Basic camera control API ()

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.
Defaults to `thirdperson_back` if unspecified.
* `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)`:
* Sends an already loaded mapblock to the player.
* 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();
switch(m_camera_mode) {
case CAMERA_MODE_ANY:
assert(false);
break;
case CAMERA_MODE_FIRST:
eye_offset += player->eye_offset_first;
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
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);
// Toggle the current camera mode
void toggleCameraMode() {
void toggleCameraMode()
{
if (m_camera_mode == CAMERA_MODE_FIRST)
m_camera_mode = CAMERA_MODE_THIRD;
else if (m_camera_mode == CAMERA_MODE_THIRD)
@ -185,7 +184,7 @@ public:
}
//read the current camera mode
inline CameraMode getCameraMode()
inline CameraMode getCameraMode() const
{
return m_camera_mode;
}

View file

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

View file

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

View file

@ -564,6 +564,7 @@ protected:
void updatePauseState();
void step(f32 dtime);
void processClientEvents(CameraOrientation *cam);
void updateCameraMode(); // call after changing it
void updateCameraOffset();
void updateCamera(f32 dtime);
void updateSound(f32 dtime);
@ -665,6 +666,7 @@ private:
void handleClientEvent_OverrideDayNigthRatio(ClientEvent *event,
CameraOrientation *cam);
void handleClientEvent_CloudParams(ClientEvent *event, CameraOrientation *cam);
void handleClientEvent_UpdateCamera(ClientEvent *event, CameraOrientation *cam);
void updateChat(f32 dtime);
@ -1921,6 +1923,9 @@ void Game::processKeyInput()
toggleFog();
} else if (wasKeyDown(KeyType::TOGGLE_UPDATE_CAMERA)) {
toggleUpdateCamera();
} else if (wasKeyPressed(KeyType::CAMERA_MODE)) {
camera->toggleCameraMode();
updateCameraMode();
} else if (wasKeyPressed(KeyType::TOGGLE_DEBUG)) {
toggleDebug();
} else if (wasKeyPressed(KeyType::TOGGLE_PROFILER)) {
@ -2575,6 +2580,7 @@ const ClientEventHandler Game::clientEventHandler[CLIENTEVENT_MAX] = {
{&Game::handleClientEvent_SetStars},
{&Game::handleClientEvent_OverrideDayNigthRatio},
{&Game::handleClientEvent_CloudParams},
{&Game::handleClientEvent_UpdateCamera},
};
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));
}
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)
{
while (client->hasClientEvents()) {
@ -2935,12 +2948,7 @@ void Game::updateCamera(f32 dtime)
ClientEnvironment &env = client->getEnv();
LocalPlayer *player = env.getLocalPlayer();
/*
For interaction purposes, get info about the held item
- What item is it?
- Is it a usable item?
- Can it point to liquids?
*/
// For interaction purposes, get info about the held item
ItemStack playeritem;
{
ItemStack selected, hand;
@ -2950,23 +2958,6 @@ void Game::updateCamera(f32 dtime)
ToolCapabilities playeritem_toolcap =
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 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()
{
ClientEnvironment &env = client->getEnv();
@ -3057,6 +3067,9 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud)
core::line3d<f32> shootline;
switch (camera->getCameraMode()) {
case CAMERA_MODE_ANY:
assert(false);
break;
case CAMERA_MODE_FIRST:
// Shoot from camera position, with bobbing
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_SPAWN_PARTICLE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_SpawnParticle }, // 0x46
{ "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_HUDRM", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudRemove }, // 0x4a
{ "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;
} catch (PacketError &e) {
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)

View file

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

View file

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

View file

@ -20,12 +20,6 @@
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);
#define DECL_PARAM_SRZRS(type) \
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
* 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) {
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) {
std::underlying_type_t<E> v;
deSerializeParameterValue(is, v);

View file

@ -15,6 +15,13 @@
#include "porting.h" // strlcpy
#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)
{

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;
struct HudElement;
class Environment;
@ -160,6 +171,8 @@ public:
return size;
}
CameraMode allowed_camera_mode = CAMERA_MODE_ANY;
v3f eye_offset_first;
v3f eye_offset_third;
v3f eye_offset_third_front;

View file

@ -492,6 +492,39 @@ int ObjectRef::l_get_eye_offset(lua_State *L)
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)
int ObjectRef::l_send_mapblock(lua_State *L)
{
@ -2900,6 +2933,8 @@ luaL_Reg ObjectRef::methods[] = {
luamethod(ObjectRef, respawn),
luamethod(ObjectRef, set_flags),
luamethod(ObjectRef, get_flags),
luamethod(ObjectRef, set_camera),
luamethod(ObjectRef, get_camera),
{0,0}
};

View file

@ -378,6 +378,12 @@ private:
// get_eye_offset(self)
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)
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);
}
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)
{
NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);

View file

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

View file

@ -5,6 +5,7 @@
#pragma once
#include <string_view>
#include <type_traits>
struct EnumString
{
@ -14,4 +15,13 @@ struct EnumString
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);