1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-06-27 16:36:03 +00:00

Basic camera control API (#15796)

This commit is contained in:
sfan5 2025-02-19 18:45:45 +01:00 committed by GitHub
parent 50819ace8f
commit ba62808fe8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 162 additions and 42 deletions

View file

@ -8827,6 +8827,14 @@ child will follow movement and rotation of that bone.
Same limits as for `thirdperson_back` apply. Same limits as for `thirdperson_back` apply.
Defaults to `thirdperson_back` if unspecified. Defaults to `thirdperson_back` if unspecified.
* `get_eye_offset()`: Returns camera offset vectors as set via `set_eye_offset`. * `get_eye_offset()`: Returns camera offset vectors as set via `set_eye_offset`.
* `set_camera(params)`: Sets camera parameters.
* `mode`: Defines the camera mode used
- `any`: free choice between all modes (default)
- `first`: first-person camera
- `third`: third-person camera
- `third_front`: third-person camera, looking opposite of movement direction
* Supported by client since 5.12.0.
* `get_camera()`: Returns the camera parameters as a table as above.
* `send_mapblock(blockpos)`: * `send_mapblock(blockpos)`:
* Sends an already loaded mapblock to the player. * Sends an already loaded mapblock to the player.
* Returns `false` if nothing was sent (note that this can also mean that * Returns `false` if nothing was sent (note that this can also mean that

View file

@ -375,6 +375,9 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 tool_reload_ratio)
{ {
v3f eye_offset = player->getEyeOffset(); v3f eye_offset = player->getEyeOffset();
switch(m_camera_mode) { switch(m_camera_mode) {
case CAMERA_MODE_ANY:
assert(false);
break;
case CAMERA_MODE_FIRST: case CAMERA_MODE_FIRST:
eye_offset += player->eye_offset_first; eye_offset += player->eye_offset_first;
break; break;

View file

@ -57,8 +57,6 @@ struct Nametag
} }
}; };
enum CameraMode {CAMERA_MODE_FIRST, CAMERA_MODE_THIRD, CAMERA_MODE_THIRD_FRONT};
/* /*
Client camera class, manages the player and camera scene nodes, the viewing distance Client camera class, manages the player and camera scene nodes, the viewing distance
and performs view bobbing etc. It also displays the wielded tool in front of the and performs view bobbing etc. It also displays the wielded tool in front of the
@ -169,7 +167,8 @@ public:
void drawWieldedTool(irr::core::matrix4* translation=NULL); void drawWieldedTool(irr::core::matrix4* translation=NULL);
// Toggle the current camera mode // Toggle the current camera mode
void toggleCameraMode() { void toggleCameraMode()
{
if (m_camera_mode == CAMERA_MODE_FIRST) if (m_camera_mode == CAMERA_MODE_FIRST)
m_camera_mode = CAMERA_MODE_THIRD; m_camera_mode = CAMERA_MODE_THIRD;
else if (m_camera_mode == CAMERA_MODE_THIRD) else if (m_camera_mode == CAMERA_MODE_THIRD)
@ -185,7 +184,7 @@ public:
} }
//read the current camera mode //read the current camera mode
inline CameraMode getCameraMode() inline CameraMode getCameraMode() const
{ {
return m_camera_mode; return m_camera_mode;
} }

View file

@ -217,6 +217,7 @@ public:
void handleCommand_MediaPush(NetworkPacket *pkt); void handleCommand_MediaPush(NetworkPacket *pkt);
void handleCommand_MinimapModes(NetworkPacket *pkt); void handleCommand_MinimapModes(NetworkPacket *pkt);
void handleCommand_SetLighting(NetworkPacket *pkt); void handleCommand_SetLighting(NetworkPacket *pkt);
void handleCommand_Camera(NetworkPacket* pkt);
void ProcessData(NetworkPacket *pkt); void ProcessData(NetworkPacket *pkt);

View file

@ -36,6 +36,7 @@ enum ClientEventType : u8
CE_SET_STARS, CE_SET_STARS,
CE_OVERRIDE_DAY_NIGHT_RATIO, CE_OVERRIDE_DAY_NIGHT_RATIO,
CE_CLOUD_PARAMS, CE_CLOUD_PARAMS,
CE_UPDATE_CAMERA,
CLIENTEVENT_MAX, CLIENTEVENT_MAX,
}; };
@ -66,11 +67,14 @@ struct ClientEventHudChange
struct ClientEvent struct ClientEvent
{ {
// TODO: should get rid of this ctor
ClientEvent() : type(CE_NONE) {}
ClientEvent(ClientEventType type) : type(type) {}
ClientEventType type; ClientEventType type;
union union
{ {
// struct{
//} none;
struct struct
{ {
u16 amount; u16 amount;
@ -86,8 +90,6 @@ struct ClientEvent
std::string *formspec; std::string *formspec;
std::string *formname; std::string *formname;
} show_formspec; } show_formspec;
// struct{
//} textures_updated;
ParticleParameters *spawn_particle; ParticleParameters *spawn_particle;
struct struct
{ {

View file

@ -564,6 +564,7 @@ protected:
void updatePauseState(); void updatePauseState();
void step(f32 dtime); void step(f32 dtime);
void processClientEvents(CameraOrientation *cam); void processClientEvents(CameraOrientation *cam);
void updateCameraMode(); // call after changing it
void updateCameraOffset(); void updateCameraOffset();
void updateCamera(f32 dtime); void updateCamera(f32 dtime);
void updateSound(f32 dtime); void updateSound(f32 dtime);
@ -665,6 +666,7 @@ private:
void handleClientEvent_OverrideDayNigthRatio(ClientEvent *event, void handleClientEvent_OverrideDayNigthRatio(ClientEvent *event,
CameraOrientation *cam); CameraOrientation *cam);
void handleClientEvent_CloudParams(ClientEvent *event, CameraOrientation *cam); void handleClientEvent_CloudParams(ClientEvent *event, CameraOrientation *cam);
void handleClientEvent_UpdateCamera(ClientEvent *event, CameraOrientation *cam);
void updateChat(f32 dtime); void updateChat(f32 dtime);
@ -1921,6 +1923,9 @@ void Game::processKeyInput()
toggleFog(); toggleFog();
} else if (wasKeyDown(KeyType::TOGGLE_UPDATE_CAMERA)) { } else if (wasKeyDown(KeyType::TOGGLE_UPDATE_CAMERA)) {
toggleUpdateCamera(); toggleUpdateCamera();
} else if (wasKeyPressed(KeyType::CAMERA_MODE)) {
camera->toggleCameraMode();
updateCameraMode();
} else if (wasKeyPressed(KeyType::TOGGLE_DEBUG)) { } else if (wasKeyPressed(KeyType::TOGGLE_DEBUG)) {
toggleDebug(); toggleDebug();
} else if (wasKeyPressed(KeyType::TOGGLE_PROFILER)) { } else if (wasKeyPressed(KeyType::TOGGLE_PROFILER)) {
@ -2575,6 +2580,7 @@ const ClientEventHandler Game::clientEventHandler[CLIENTEVENT_MAX] = {
{&Game::handleClientEvent_SetStars}, {&Game::handleClientEvent_SetStars},
{&Game::handleClientEvent_OverrideDayNigthRatio}, {&Game::handleClientEvent_OverrideDayNigthRatio},
{&Game::handleClientEvent_CloudParams}, {&Game::handleClientEvent_CloudParams},
{&Game::handleClientEvent_UpdateCamera},
}; };
void Game::handleClientEvent_None(ClientEvent *event, CameraOrientation *cam) void Game::handleClientEvent_None(ClientEvent *event, CameraOrientation *cam)
@ -2879,6 +2885,13 @@ void Game::handleClientEvent_CloudParams(ClientEvent *event, CameraOrientation *
clouds->setSpeed(v2f(event->cloud_params.speed_x, event->cloud_params.speed_y)); clouds->setSpeed(v2f(event->cloud_params.speed_x, event->cloud_params.speed_y));
} }
void Game::handleClientEvent_UpdateCamera(ClientEvent *event, CameraOrientation *cam)
{
// no parameters to update here, this just makes sure the camera is in the
// state it should be after something was changed.
updateCameraMode();
}
void Game::processClientEvents(CameraOrientation *cam) void Game::processClientEvents(CameraOrientation *cam)
{ {
while (client->hasClientEvents()) { while (client->hasClientEvents()) {
@ -2935,12 +2948,7 @@ void Game::updateCamera(f32 dtime)
ClientEnvironment &env = client->getEnv(); ClientEnvironment &env = client->getEnv();
LocalPlayer *player = env.getLocalPlayer(); LocalPlayer *player = env.getLocalPlayer();
/* // For interaction purposes, get info about the held item
For interaction purposes, get info about the held item
- What item is it?
- Is it a usable item?
- Can it point to liquids?
*/
ItemStack playeritem; ItemStack playeritem;
{ {
ItemStack selected, hand; ItemStack selected, hand;
@ -2950,23 +2958,6 @@ void Game::updateCamera(f32 dtime)
ToolCapabilities playeritem_toolcap = ToolCapabilities playeritem_toolcap =
playeritem.getToolCapabilities(itemdef_manager); playeritem.getToolCapabilities(itemdef_manager);
if (wasKeyPressed(KeyType::CAMERA_MODE)) {
GenericCAO *playercao = player->getCAO();
// If playercao not loaded, don't change camera
if (!playercao)
return;
camera->toggleCameraMode();
if (g_touchcontrols)
g_touchcontrols->setUseCrosshair(!isTouchCrosshairDisabled());
// Make the player visible depending on camera mode.
playercao->updateMeshCulling();
playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
}
float full_punch_interval = playeritem_toolcap.full_punch_interval; float full_punch_interval = playeritem_toolcap.full_punch_interval;
float tool_reload_ratio = runData.time_from_last_punch / full_punch_interval; float tool_reload_ratio = runData.time_from_last_punch / full_punch_interval;
@ -2981,6 +2972,25 @@ void Game::updateCamera(f32 dtime)
} }
} }
void Game::updateCameraMode()
{
LocalPlayer *player = client->getEnv().getLocalPlayer();
// Obey server choice
if (player->allowed_camera_mode != CAMERA_MODE_ANY)
camera->setCameraMode(player->allowed_camera_mode);
if (g_touchcontrols)
g_touchcontrols->setUseCrosshair(!isTouchCrosshairDisabled());
GenericCAO *playercao = player->getCAO();
if (playercao) {
// Make the player visible depending on camera mode.
playercao->updateMeshCulling();
playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
}
}
void Game::updateCameraOffset() void Game::updateCameraOffset()
{ {
ClientEnvironment &env = client->getEnv(); ClientEnvironment &env = client->getEnv();
@ -3057,6 +3067,9 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud)
core::line3d<f32> shootline; core::line3d<f32> shootline;
switch (camera->getCameraMode()) { switch (camera->getCameraMode()) {
case CAMERA_MODE_ANY:
assert(false);
break;
case CAMERA_MODE_FIRST: case CAMERA_MODE_FIRST:
// Shoot from camera position, with bobbing // Shoot from camera position, with bobbing
shootline.start = camera->getPosition(); shootline.start = camera->getPosition();

View file

@ -83,7 +83,7 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] =
{ "TOCLIENT_MOVEMENT", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Movement }, // 0x45 { "TOCLIENT_MOVEMENT", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Movement }, // 0x45
{ "TOCLIENT_SPAWN_PARTICLE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_SpawnParticle }, // 0x46 { "TOCLIENT_SPAWN_PARTICLE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_SpawnParticle }, // 0x46
{ "TOCLIENT_ADD_PARTICLESPAWNER", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_AddParticleSpawner }, // 0x47 { "TOCLIENT_ADD_PARTICLESPAWNER", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_AddParticleSpawner }, // 0x47
null_command_handler, { "TOCLIENT_CAMERA", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Camera }, // 0x48
{ "TOCLIENT_HUDADD", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudAdd }, // 0x49 { "TOCLIENT_HUDADD", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudAdd }, // 0x49
{ "TOCLIENT_HUDRM", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudRemove }, // 0x4a { "TOCLIENT_HUDRM", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudRemove }, // 0x4a
{ "TOCLIENT_HUDCHANGE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudChange }, // 0x4b { "TOCLIENT_HUDCHANGE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudChange }, // 0x4b

View file

@ -1530,7 +1530,19 @@ void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
*pkt >> player->eye_offset_third_front; *pkt >> player->eye_offset_third_front;
} catch (PacketError &e) { } catch (PacketError &e) {
player->eye_offset_third_front = player->eye_offset_third; player->eye_offset_third_front = player->eye_offset_third;
}; }
}
void Client::handleCommand_Camera(NetworkPacket* pkt)
{
LocalPlayer *player = m_env.getLocalPlayer();
assert(player);
u8 tmp;
*pkt >> tmp;
player->allowed_camera_mode = static_cast<CameraMode>(tmp);
m_client_event_queue.push(new ClientEvent(CE_UPDATE_CAMERA));
} }
void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt) void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)

View file

@ -442,6 +442,11 @@ enum ToClientCommand : u16
*/ */
TOCLIENT_CAMERA = 0x48,
/*
u8 allowed_camera_mode
*/
TOCLIENT_HUDADD = 0x49, TOCLIENT_HUDADD = 0x49,
/* /*
u32 id u32 id

View file

@ -183,7 +183,7 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] =
{ "TOCLIENT_MOVEMENT", 0, true }, // 0x45 { "TOCLIENT_MOVEMENT", 0, true }, // 0x45
{ "TOCLIENT_SPAWN_PARTICLE", 0, true }, // 0x46 { "TOCLIENT_SPAWN_PARTICLE", 0, true }, // 0x46
{ "TOCLIENT_ADD_PARTICLESPAWNER", 0, true }, // 0x47 { "TOCLIENT_ADD_PARTICLESPAWNER", 0, true }, // 0x47
null_command_factory, // 0x48 { "TOCLIENT_CAMERA", 0, true }, // 0x48
{ "TOCLIENT_HUDADD", 1, true }, // 0x49 { "TOCLIENT_HUDADD", 1, true }, // 0x49
{ "TOCLIENT_HUDRM", 1, true }, // 0x4a { "TOCLIENT_HUDRM", 1, true }, // 0x4a
{ "TOCLIENT_HUDCHANGE", 1, true }, // 0x4b { "TOCLIENT_HUDCHANGE", 1, true }, // 0x4b

View file

@ -20,12 +20,6 @@
namespace ParticleParamTypes namespace ParticleParamTypes
{ {
template <bool cond, typename T>
using enableIf = typename std::enable_if<cond, T>::type;
// std::enable_if_t does not appear to be present in GCC????
// std::is_enum_v also missing. wtf. these are supposed to be
// present as of c++14
template<typename T> using BlendFunction = T(float,T,T); template<typename T> using BlendFunction = T(float,T,T);
#define DECL_PARAM_SRZRS(type) \ #define DECL_PARAM_SRZRS(type) \
void serializeParameterValue (std::ostream& os, type v); \ void serializeParameterValue (std::ostream& os, type v); \
@ -57,12 +51,12 @@ namespace ParticleParamTypes
* that's hideous and unintuitive. instead, we supply the following functions to * that's hideous and unintuitive. instead, we supply the following functions to
* transparently map enumeration types to their underlying values. */ * transparently map enumeration types to their underlying values. */
template <typename E, enableIf<std::is_enum<E>::value, bool> = true> template <typename E, std::enable_if_t<std::is_enum_v<E>, bool> = true>
void serializeParameterValue(std::ostream& os, E k) { void serializeParameterValue(std::ostream& os, E k) {
serializeParameterValue(os, (std::underlying_type_t<E>)k); serializeParameterValue(os, (std::underlying_type_t<E>)k);
} }
template <typename E, enableIf<std::is_enum<E>::value, bool> = true> template <typename E, std::enable_if_t<std::is_enum_v<E>, bool> = true>
void deSerializeParameterValue(std::istream& is, E& k) { void deSerializeParameterValue(std::istream& is, E& k) {
std::underlying_type_t<E> v; std::underlying_type_t<E> v;
deSerializeParameterValue(is, v); deSerializeParameterValue(is, v);

View file

@ -15,6 +15,13 @@
#include "porting.h" // strlcpy #include "porting.h" // strlcpy
#include <tuple> #include <tuple>
const struct EnumString es_CameraMode[] = {
{CAMERA_MODE_ANY, "any"},
{CAMERA_MODE_FIRST, "first"},
{CAMERA_MODE_THIRD, "third"},
{CAMERA_MODE_THIRD_FRONT, "third_front"},
{0, nullptr}
};
bool is_valid_player_name(std::string_view name) bool is_valid_player_name(std::string_view name)
{ {

View file

@ -126,6 +126,17 @@ struct PlayerPhysicsOverride
} }
}; };
/// @note numeric values are part of network protocol
enum CameraMode {
// not a mode. indicates that any may be used.
CAMERA_MODE_ANY = 0,
CAMERA_MODE_FIRST,
CAMERA_MODE_THIRD,
CAMERA_MODE_THIRD_FRONT
};
extern const struct EnumString es_CameraMode[];
class Map; class Map;
struct HudElement; struct HudElement;
class Environment; class Environment;
@ -160,6 +171,8 @@ public:
return size; return size;
} }
CameraMode allowed_camera_mode = CAMERA_MODE_ANY;
v3f eye_offset_first; v3f eye_offset_first;
v3f eye_offset_third; v3f eye_offset_third;
v3f eye_offset_third_front; v3f eye_offset_third_front;

View file

@ -492,6 +492,39 @@ int ObjectRef::l_get_eye_offset(lua_State *L)
return 3; return 3;
} }
int ObjectRef::l_set_camera(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkObject<ObjectRef>(L, 1);
RemotePlayer *player = getplayer(ref);
if (player == nullptr)
return 0;
luaL_checktype(L, 2, LUA_TTABLE);
lua_getfield(L, -1, "mode");
if (lua_isstring(L, -1))
string_to_enum(es_CameraMode, player->allowed_camera_mode, lua_tostring(L, -1));
lua_pop(L, 1);
getServer(L)->SendCamera(player->getPeerId(), player);
return 0;
}
int ObjectRef::l_get_camera(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkObject<ObjectRef>(L, 1);
RemotePlayer *player = getplayer(ref);
if (player == nullptr)
return 0;
lua_newtable(L);
setstringfield(L, -1, "mode", enum_to_string(es_CameraMode, player->allowed_camera_mode));
return 1;
}
// send_mapblock(self, pos) // send_mapblock(self, pos)
int ObjectRef::l_send_mapblock(lua_State *L) int ObjectRef::l_send_mapblock(lua_State *L)
{ {
@ -2900,6 +2933,8 @@ luaL_Reg ObjectRef::methods[] = {
luamethod(ObjectRef, respawn), luamethod(ObjectRef, respawn),
luamethod(ObjectRef, set_flags), luamethod(ObjectRef, set_flags),
luamethod(ObjectRef, get_flags), luamethod(ObjectRef, get_flags),
luamethod(ObjectRef, set_camera),
luamethod(ObjectRef, get_camera),
{0,0} {0,0}
}; };

View file

@ -378,6 +378,12 @@ private:
// get_eye_offset(self) // get_eye_offset(self)
static int l_get_eye_offset(lua_State *L); static int l_get_eye_offset(lua_State *L);
// set_camera(self, {params})
static int l_set_camera(lua_State *L);
// get_camera(self)
static int l_get_camera(lua_State *L);
// set_nametag_attributes(self, attributes) // set_nametag_attributes(self, attributes)
static int l_set_nametag_attributes(lua_State *L); static int l_set_nametag_attributes(lua_State *L);

View file

@ -1944,6 +1944,15 @@ void Server::SendSetLighting(session_t peer_id, const Lighting &lighting)
Send(&pkt); Send(&pkt);
} }
void Server::SendCamera(session_t peer_id, Player *player)
{
NetworkPacket pkt(TOCLIENT_CAMERA, 1, peer_id);
pkt << static_cast<u8>(player->allowed_camera_mode);
Send(&pkt);
}
void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed) void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
{ {
NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id); NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);

View file

@ -45,6 +45,7 @@ class BanManager;
class Inventory; class Inventory;
class ModChannelMgr; class ModChannelMgr;
class RemotePlayer; class RemotePlayer;
class Player;
class PlayerSAO; class PlayerSAO;
struct PlayerHPChangeReason; struct PlayerHPChangeReason;
class IRollbackManager; class IRollbackManager;
@ -409,6 +410,7 @@ public:
void SendMovePlayerRel(session_t peer_id, const v3f &added_pos); void SendMovePlayerRel(session_t peer_id, const v3f &added_pos);
void SendPlayerSpeed(session_t peer_id, const v3f &added_vel); void SendPlayerSpeed(session_t peer_id, const v3f &added_vel);
void SendPlayerFov(session_t peer_id); void SendPlayerFov(session_t peer_id);
void SendCamera(session_t peer_id, Player *player);
void SendMinimapModes(session_t peer_id, void SendMinimapModes(session_t peer_id,
std::vector<MinimapMode> &modes, std::vector<MinimapMode> &modes,
@ -546,6 +548,7 @@ private:
void SendCloudParams(session_t peer_id, const CloudParams &params); void SendCloudParams(session_t peer_id, const CloudParams &params);
void SendOverrideDayNightRatio(session_t peer_id, bool do_override, float ratio); void SendOverrideDayNightRatio(session_t peer_id, bool do_override, float ratio);
void SendSetLighting(session_t peer_id, const Lighting &lighting); void SendSetLighting(session_t peer_id, const Lighting &lighting);
void broadcastModChannelMessage(const std::string &channel, void broadcastModChannelMessage(const std::string &channel,
const std::string &message, session_t from_peer); const std::string &message, session_t from_peer);

View file

@ -5,6 +5,7 @@
#pragma once #pragma once
#include <string_view> #include <string_view>
#include <type_traits>
struct EnumString struct EnumString
{ {
@ -14,4 +15,13 @@ struct EnumString
bool string_to_enum(const EnumString *spec, int &result, std::string_view str); bool string_to_enum(const EnumString *spec, int &result, std::string_view str);
template <typename T, std::enable_if_t<std::is_enum_v<T>, bool> = true>
bool string_to_enum(const EnumString *spec, T &result, std::string_view str)
{
int result_int = result;
bool ret = string_to_enum(spec, result_int, str);
result = static_cast<T>(result_int);
return ret;
}
const char *enum_to_string(const EnumString *spec, int num); const char *enum_to_string(const EnumString *spec, int num);