Add First Person Attachments (#10360)

Fixes some other third person camera specific attachments.
Implements a single new flag for entities to be forced visible in first person mode.
Old mods do not need to be updated to use the new flag and are fully backwards compatible.
This commit is contained in:
Jordan Snelling 2020-10-04 14:10:34 +01:00 committed by GitHub
parent 0f98b54aa4
commit 3068853e8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 101 additions and 35 deletions

@ -6167,12 +6167,14 @@ object you are working with still exists.
`frame_loop`. `frame_loop`.
* `set_animation_frame_speed(frame_speed)` * `set_animation_frame_speed(frame_speed)`
* `frame_speed`: number, default: `15.0` * `frame_speed`: number, default: `15.0`
* `set_attach(parent, bone, position, rotation)` * `set_attach(parent, bone, position, rotation, forced_visible)`
* `bone`: string * `bone`: string
* `position`: `{x=num, y=num, z=num}` (relative) * `position`: `{x=num, y=num, z=num}` (relative)
* `rotation`: `{x=num, y=num, z=num}` = Rotation on each axis, in degrees * `rotation`: `{x=num, y=num, z=num}` = Rotation on each axis, in degrees
* `get_attach()`: returns parent, bone, position, rotation or nil if it isn't * `forced_visible`: Boolean to control whether the attached entity
attached. should appear in first person.
* `get_attach()`: returns parent, bone, position, rotation, forced_visible,
or nil if it isn't attached.
* `set_detach()` * `set_detach()`
* `set_bone_position(bone, position, rotation)` * `set_bone_position(bone, position, rotation)`
* `bone`: string * `bone`: string

@ -120,9 +120,9 @@ public:
virtual void setAttachment(int parent_id, const std::string &bone, v3f position, virtual void setAttachment(int parent_id, const std::string &bone, v3f position,
v3f rotation) {} v3f rotation, bool force_visible) {}
virtual void getAttachment(int *parent_id, std::string *bone, v3f *position, virtual void getAttachment(int *parent_id, std::string *bone, v3f *position,
v3f *rotation) const {} v3f *rotation, bool *force_visible) const {}
virtual void clearChildAttachments() {} virtual void clearChildAttachments() {}
virtual void clearParentAttachment() {} virtual void clearParentAttachment() {}
virtual void addAttachmentChild(int child_id) {} virtual void addAttachmentChild(int child_id) {}

@ -456,7 +456,8 @@ void GenericCAO::setChildrenVisible(bool toset)
for (u16 cao_id : m_attachment_child_ids) { for (u16 cao_id : m_attachment_child_ids) {
GenericCAO *obj = m_env->getGenericCAO(cao_id); GenericCAO *obj = m_env->getGenericCAO(cao_id);
if (obj) { if (obj) {
obj->setVisible(toset); // Check if the entity is forced to appear in first person.
obj->setVisible(obj->isForcedVisible() ? true : toset);
} }
} }
} }
@ -477,8 +478,6 @@ void GenericCAO::setAttachment(int parent_id, const std::string &bone, v3f posit
if (parent) if (parent)
parent->addAttachmentChild(m_id); parent->addAttachmentChild(m_id);
} }
updateAttachments(); updateAttachments();
} }
@ -498,7 +497,7 @@ void GenericCAO::clearChildAttachments()
int child_id = *m_attachment_child_ids.begin(); int child_id = *m_attachment_child_ids.begin();
if (ClientActiveObject *child = m_env->getActiveObject(child_id)) if (ClientActiveObject *child = m_env->getActiveObject(child_id))
child->setAttachment(0, "", v3f(), v3f()); child->setAttachment(0, "", v3f(), v3f(), false);
removeAttachmentChild(child_id); removeAttachmentChild(child_id);
} }
@ -800,6 +799,7 @@ void GenericCAO::addToScene(ITextureSource *tsrc)
updateBonePosition(); updateBonePosition();
updateAttachments(); updateAttachments();
setNodeLight(m_last_light); setNodeLight(m_last_light);
updateMeshCulling();
} }
void GenericCAO::updateLight(u32 day_night_ratio) void GenericCAO::updateLight(u32 day_night_ratio)
@ -1411,6 +1411,9 @@ void GenericCAO::updateTextures(std::string mod)
setMeshColor(mesh, m_prop.colors[0]); setMeshColor(mesh, m_prop.colors[0]);
} }
} }
// Prevent showing the player after changing texture
if (m_is_local_player)
updateMeshCulling();
} }
void GenericCAO::updateAnimation() void GenericCAO::updateAnimation()
@ -1739,12 +1742,25 @@ void GenericCAO::processMessage(const std::string &data)
std::string bone = deSerializeString16(is); std::string bone = deSerializeString16(is);
v3f position = readV3F32(is); v3f position = readV3F32(is);
v3f rotation = readV3F32(is); v3f rotation = readV3F32(is);
m_force_visible = readU8(is); // Returns false for EOF
setAttachment(parent_id, bone, position, rotation); setAttachment(parent_id, bone, position, rotation);
// Forcibly show attachments if required by set_attach
if (m_force_visible)
m_is_visible = true;
// localplayer itself can't be attached to localplayer // localplayer itself can't be attached to localplayer
if (!m_is_local_player) else if (!m_is_local_player) {
m_is_visible = !m_attached_to_local; // Objects attached to the local player should be hidden in first
// person provided the forced boolean isn't set.
m_is_visible = !m_attached_to_local ||
m_client->getCamera()->getCameraMode() != CAMERA_MODE_FIRST;
m_force_visible = false;
} else {
// Local players need to have this set,
// otherwise first person attachments fail.
m_is_visible = true;
}
} else if (cmd == AO_CMD_PUNCHED) { } else if (cmd == AO_CMD_PUNCHED) {
u16 result_hp = readU16(is); u16 result_hp = readU16(is);
@ -1858,5 +1874,33 @@ std::string GenericCAO::debugInfoText()
return os.str(); return os.str();
} }
void GenericCAO::updateMeshCulling()
{
if (!m_is_local_player)
return;
// Grab the active player scene node so we know there's
// at least a mesh to occlude from the camera.
irr::scene::ISceneNode *node = getSceneNode();
if (!node)
return;
if (m_client->getCamera()->getCameraMode() == CAMERA_MODE_FIRST) {
// Hide the mesh by culling both front and
// back faces. Serious hackyness but it works for our
// purposes. This also preserves the skeletal armature.
node->setMaterialFlag(video::EMF_BACK_FACE_CULLING,
true);
node->setMaterialFlag(video::EMF_FRONT_FACE_CULLING,
true);
} else {
// Restore mesh visibility.
node->setMaterialFlag(video::EMF_BACK_FACE_CULLING,
m_prop.backface_culling);
node->setMaterialFlag(video::EMF_FRONT_FACE_CULLING,
false);
}
}
// Prototype // Prototype
GenericCAO proto_GenericCAO(NULL, NULL); GenericCAO proto_GenericCAO(NULL, NULL);

@ -124,6 +124,7 @@ private:
float m_step_distance_counter = 0.0f; float m_step_distance_counter = 0.0f;
u8 m_last_light = 255; u8 m_last_light = 255;
bool m_is_visible = false; bool m_is_visible = false;
bool m_force_visible = false;
s8 m_glow = 0; s8 m_glow = 0;
// Material // Material
video::E_MATERIAL_TYPE m_material_type; video::E_MATERIAL_TYPE m_material_type;
@ -215,6 +216,11 @@ public:
m_is_visible = toset; m_is_visible = toset;
} }
inline bool isForcedVisible() const
{
return m_force_visible;
}
void setChildrenVisible(bool toset); void setChildrenVisible(bool toset);
void setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation); void setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation);
void getAttachment(int *parent_id, std::string *bone, v3f *position, void getAttachment(int *parent_id, std::string *bone, v3f *position,
@ -275,4 +281,6 @@ public:
{ {
return m_prop.infotext; return m_prop.infotext;
} }
void updateMeshCulling();
}; };

@ -2950,7 +2950,8 @@ void Game::updateCamera(u32 busy_time, f32 dtime)
camera->toggleCameraMode(); camera->toggleCameraMode();
playercao->setVisible(camera->getCameraMode() > CAMERA_MODE_FIRST); // Make the player visible depending on camera mode.
playercao->updateMeshCulling();
playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST); playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
} }

@ -80,10 +80,11 @@ LuaEntitySAO *getAttachedObject(PlayerSAO *sao, ServerEnvironment *env)
int id; int id;
std::string bone; std::string bone;
v3f dummy; v3f dummy;
sao->getAttachment(&id, &bone, &dummy, &dummy); bool force_visible;
sao->getAttachment(&id, &bone, &dummy, &dummy, &force_visible);
ServerActiveObject *ao = env->getActiveObject(id); ServerActiveObject *ao = env->getActiveObject(id);
while (id && ao) { while (id && ao) {
ao->getAttachment(&id, &bone, &dummy, &dummy); ao->getAttachment(&id, &bone, &dummy, &dummy, &force_visible);
if (id) if (id)
ao = env->getActiveObject(id); ao = env->getActiveObject(id);
} }

@ -63,7 +63,8 @@ int LuaCamera::l_set_camera_mode(lua_State *L)
return 0; return 0;
camera->setCameraMode((CameraMode)((int)lua_tonumber(L, 2))); camera->setCameraMode((CameraMode)((int)lua_tonumber(L, 2)));
playercao->setVisible(camera->getCameraMode() > CAMERA_MODE_FIRST); // Make the player visible depending on camera mode.
playercao->updateMeshCulling();
playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST); playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
return 0; return 0;
} }

@ -664,7 +664,7 @@ int ObjectRef::l_get_bone_position(lua_State *L)
return 2; return 2;
} }
// set_attach(self, parent, bone, position, rotation) // set_attach(self, parent, bone, position, rotation, force_visible)
int ObjectRef::l_set_attach(lua_State *L) int ObjectRef::l_set_attach(lua_State *L)
{ {
GET_ENV_PTR; GET_ENV_PTR;
@ -687,7 +687,8 @@ int ObjectRef::l_set_attach(lua_State *L)
std::string bone; std::string bone;
v3f position = v3f(0, 0, 0); v3f position = v3f(0, 0, 0);
v3f rotation = v3f(0, 0, 0); v3f rotation = v3f(0, 0, 0);
co->getAttachment(&parent_id, &bone, &position, &rotation); bool force_visible;
co->getAttachment(&parent_id, &bone, &position, &rotation, &force_visible);
if (parent_id) { if (parent_id) {
ServerActiveObject *old_parent = env->getActiveObject(parent_id); ServerActiveObject *old_parent = env->getActiveObject(parent_id);
old_parent->removeAttachmentChild(co->getId()); old_parent->removeAttachmentChild(co->getId());
@ -702,7 +703,8 @@ int ObjectRef::l_set_attach(lua_State *L)
rotation = v3f(0, 0, 0); rotation = v3f(0, 0, 0);
if (!lua_isnil(L, 5)) if (!lua_isnil(L, 5))
rotation = read_v3f(L, 5); rotation = read_v3f(L, 5);
co->setAttachment(parent->getId(), bone, position, rotation); force_visible = readParam<bool>(L, 6, false);
co->setAttachment(parent->getId(), bone, position, rotation, force_visible);
parent->addAttachmentChild(co->getId()); parent->addAttachmentChild(co->getId());
return 0; return 0;
} }
@ -722,7 +724,8 @@ int ObjectRef::l_get_attach(lua_State *L)
std::string bone; std::string bone;
v3f position = v3f(0, 0, 0); v3f position = v3f(0, 0, 0);
v3f rotation = v3f(0, 0, 0); v3f rotation = v3f(0, 0, 0);
co->getAttachment(&parent_id, &bone, &position, &rotation); bool force_visible;
co->getAttachment(&parent_id, &bone, &position, &rotation, &force_visible);
if (!parent_id) if (!parent_id)
return 0; return 0;
ServerActiveObject *parent = env->getActiveObject(parent_id); ServerActiveObject *parent = env->getActiveObject(parent_id);
@ -731,7 +734,8 @@ int ObjectRef::l_get_attach(lua_State *L)
lua_pushlstring(L, bone.c_str(), bone.size()); lua_pushlstring(L, bone.c_str(), bone.size());
push_v3f(L, position); push_v3f(L, position);
push_v3f(L, rotation); push_v3f(L, rotation);
return 4; lua_pushboolean(L, force_visible);
return 5;
} }
// set_detach(self) // set_detach(self)

@ -238,9 +238,9 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
msg_os << serializeString32(generateUpdateAnimationCommand()); // 3 msg_os << serializeString32(generateUpdateAnimationCommand()); // 3
for (const auto &bone_pos : m_bone_position) { for (const auto &bone_pos : m_bone_position) {
msg_os << serializeString32(generateUpdateBonePositionCommand( msg_os << serializeString32(generateUpdateBonePositionCommand(
bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // m_bone_position.size bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // 3 + N
} }
msg_os << serializeString32(generateUpdateAttachmentCommand()); // 4 msg_os << serializeString32(generateUpdateAttachmentCommand()); // 4 + m_bone_position.size
int message_count = 4 + m_bone_position.size(); int message_count = 4 + m_bone_position.size();

@ -122,10 +122,10 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
msg_os << serializeString32(generateUpdateAnimationCommand()); // 3 msg_os << serializeString32(generateUpdateAnimationCommand()); // 3
for (const auto &bone_pos : m_bone_position) { for (const auto &bone_pos : m_bone_position) {
msg_os << serializeString32(generateUpdateBonePositionCommand( msg_os << serializeString32(generateUpdateBonePositionCommand(
bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // m_bone_position.size bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // 3 + N
} }
msg_os << serializeString32(generateUpdateAttachmentCommand()); // 4 msg_os << serializeString32(generateUpdateAttachmentCommand()); // 4 + m_bone_position.size
msg_os << serializeString32(generateUpdatePhysicsOverrideCommand()); // 5 msg_os << serializeString32(generateUpdatePhysicsOverrideCommand()); // 5 + m_bone_position.size
int message_count = 5 + m_bone_position.size(); int message_count = 5 + m_bone_position.size();
@ -569,7 +569,8 @@ bool PlayerSAO::checkMovementCheat()
int parent_id; int parent_id;
std::string bone; std::string bone;
v3f attachment_rot; v3f attachment_rot;
getAttachment(&parent_id, &bone, &attachment_pos, &attachment_rot); bool force_visible;
getAttachment(&parent_id, &bone, &attachment_pos, &attachment_rot, &force_visible);
} }
v3f parent_pos = parent->getBasePosition(); v3f parent_pos = parent->getBasePosition();

@ -121,8 +121,8 @@ void UnitSAO::sendOutdatedData()
} }
// clang-format on // clang-format on
void UnitSAO::setAttachment( void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position,
int parent_id, const std::string &bone, v3f position, v3f rotation) v3f rotation, bool force_visible)
{ {
// Attachments need to be handled on both the server and client. // Attachments need to be handled on both the server and client.
// If we just attach on the server, we can only copy the position of the parent. // If we just attach on the server, we can only copy the position of the parent.
@ -137,6 +137,7 @@ void UnitSAO::setAttachment(
m_attachment_bone = bone; m_attachment_bone = bone;
m_attachment_position = position; m_attachment_position = position;
m_attachment_rotation = rotation; m_attachment_rotation = rotation;
m_force_visible = force_visible;
m_attachment_sent = false; m_attachment_sent = false;
if (parent_id != old_parent) { if (parent_id != old_parent) {
@ -145,13 +146,14 @@ void UnitSAO::setAttachment(
} }
} }
void UnitSAO::getAttachment( void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
int *parent_id, std::string *bone, v3f *position, v3f *rotation) const v3f *rotation, bool *force_visible) const
{ {
*parent_id = m_attachment_parent_id; *parent_id = m_attachment_parent_id;
*bone = m_attachment_bone; *bone = m_attachment_bone;
*position = m_attachment_position; *position = m_attachment_position;
*rotation = m_attachment_rotation; *rotation = m_attachment_rotation;
*force_visible = m_force_visible;
} }
void UnitSAO::clearChildAttachments() void UnitSAO::clearChildAttachments()
@ -159,7 +161,7 @@ void UnitSAO::clearChildAttachments()
for (int child_id : m_attachment_child_ids) { for (int child_id : m_attachment_child_ids) {
// Child can be NULL if it was deleted earlier // Child can be NULL if it was deleted earlier
if (ServerActiveObject *child = m_env->getActiveObject(child_id)) if (ServerActiveObject *child = m_env->getActiveObject(child_id))
child->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0)); child->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0), false);
} }
m_attachment_child_ids.clear(); m_attachment_child_ids.clear();
} }
@ -169,9 +171,9 @@ void UnitSAO::clearParentAttachment()
ServerActiveObject *parent = nullptr; ServerActiveObject *parent = nullptr;
if (m_attachment_parent_id) { if (m_attachment_parent_id) {
parent = m_env->getActiveObject(m_attachment_parent_id); parent = m_env->getActiveObject(m_attachment_parent_id);
setAttachment(0, "", m_attachment_position, m_attachment_rotation); setAttachment(0, "", m_attachment_position, m_attachment_rotation, false);
} else { } else {
setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0)); setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0), false);
} }
// Do it // Do it
if (parent) if (parent)
@ -245,6 +247,7 @@ std::string UnitSAO::generateUpdateAttachmentCommand() const
os << serializeString16(m_attachment_bone); os << serializeString16(m_attachment_bone);
writeV3F32(os, m_attachment_position); writeV3F32(os, m_attachment_position);
writeV3F32(os, m_attachment_rotation); writeV3F32(os, m_attachment_rotation);
writeU8(os, m_force_visible);
return os.str(); return os.str();
} }

@ -64,9 +64,9 @@ public:
ServerActiveObject *getParent() const; ServerActiveObject *getParent() const;
inline bool isAttached() const { return getParent(); } inline bool isAttached() const { return getParent(); }
void setAttachment(int parent_id, const std::string &bone, v3f position, void setAttachment(int parent_id, const std::string &bone, v3f position,
v3f rotation); v3f rotation, bool force_visible);
void getAttachment(int *parent_id, std::string *bone, v3f *position, void getAttachment(int *parent_id, std::string *bone, v3f *position,
v3f *rotation) const; v3f *rotation, bool *force_visible) const;
void clearChildAttachments(); void clearChildAttachments();
void clearParentAttachment(); void clearParentAttachment();
void addAttachmentChild(int child_id); void addAttachmentChild(int child_id);
@ -133,4 +133,5 @@ private:
v3f m_attachment_position; v3f m_attachment_position;
v3f m_attachment_rotation; v3f m_attachment_rotation;
bool m_attachment_sent = false; bool m_attachment_sent = false;
bool m_force_visible = false;
}; };