summaryrefslogtreecommitdiff
path: root/src/content_cao.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/content_cao.cpp')
-rw-r--r--src/content_cao.cpp309
1 files changed, 211 insertions, 98 deletions
diff --git a/src/content_cao.cpp b/src/content_cao.cpp
index deb9aa903..c2cce3d58 100644
--- a/src/content_cao.cpp
+++ b/src/content_cao.cpp
@@ -51,6 +51,8 @@ struct ToolCapabilities;
core::map<u16, ClientActiveObject::Factory> ClientActiveObject::m_types;
+std::vector<core::vector2d<int> > attachment_list; // X is child ID, Y is parent ID
+
/*
SmoothTranslator
*/
@@ -580,8 +582,7 @@ private:
v2f m_frames;
int m_frame_speed;
int m_frame_blend;
- std::map<std::string, core::vector2d<v3f> > m_bone_posrot;
- ClientActiveObject* m_attachment_parent;
+ std::map<std::string, core::vector2d<v3f> > m_bone_posrot; // stores position and rotation for each bone name
std::string m_attachment_bone;
v3f m_attachment_position;
v3f m_attachment_rotation;
@@ -624,7 +625,6 @@ public:
m_frame_speed(15),
m_frame_blend(0),
// Nothing to do for m_bone_posrot
- m_attachment_parent(NULL),
m_attachment_bone(""),
m_attachment_position(v3f(0,0,0)),
m_attachment_rotation(v3f(0,0,0)),
@@ -693,28 +693,43 @@ public:
}
core::aabbox3d<f32>* getSelectionBox()
{
- if(!m_prop.is_visible || !m_is_visible || m_is_local_player)
+ if(!m_prop.is_visible || !m_is_visible || m_is_local_player || getParent() != NULL)
return NULL;
return &m_selection_box;
}
v3f getPosition()
{
+ if(getParent() != NULL){
+ if(m_meshnode)
+ return m_meshnode->getAbsolutePosition();
+ if(m_animated_meshnode)
+ return m_animated_meshnode->getAbsolutePosition();
+ if(m_spritenode)
+ return m_spritenode->getAbsolutePosition();
+ return v3f(0,0,0); // Just in case
+ }
return pos_translator.vect_show;
}
scene::IMeshSceneNode *getMeshSceneNode()
{
- return m_meshnode;
+ if(m_meshnode)
+ return m_meshnode;
+ return NULL;
}
scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode()
{
- return m_animated_meshnode;
+ if(m_animated_meshnode)
+ return m_animated_meshnode;
+ return NULL;
}
scene::IBillboardSceneNode *getSpriteSceneNode()
{
- return m_spritenode;
+ if(m_spritenode)
+ return m_spritenode;
+ return NULL;
}
bool isPlayer()
@@ -727,8 +742,80 @@ public:
return m_is_local_player;
}
- void removeFromScene()
+ void updateParent()
+ {
+ updateAttachments();
+ }
+
+ ClientActiveObject *getParent()
{
+ ClientActiveObject *obj = NULL;
+ for(std::vector<core::vector2d<int> >::const_iterator cii = attachment_list.begin(); cii != attachment_list.end(); cii++)
+ {
+ if(cii->X == this->getId()){ // This ID is our child
+ if(cii->Y > 0){ // A parent ID exists for our child
+ if(cii->X != cii->Y){ // The parent and child ID are not the same
+ obj = m_env->getActiveObject(cii->Y);
+ }
+ }
+ break;
+ }
+ }
+ if(obj)
+ return obj;
+ return NULL;
+ }
+
+ void removeFromScene(bool permanent)
+ {
+ // bool permanent should be true when removing the object permanently and false when it's only refreshed (and comes back in a few frames)
+
+ // If this object is being permanently removed, delete it from the attachments list
+ if(permanent)
+ {
+ for(std::vector<core::vector2d<int> >::iterator ii = attachment_list.begin(); ii != attachment_list.end(); ii++)
+ {
+ if(ii->X == this->getId()) // This is the ID of our object
+ {
+ attachment_list.erase(ii);
+ break;
+ }
+ }
+ }
+
+ // If this object is being removed, either permanently or just to refresh it, then all
+ // objects attached to it must be unparented else Irrlicht causes a segmentation fault.
+ for(std::vector<core::vector2d<int> >::iterator ii = attachment_list.begin(); ii != attachment_list.end(); ii++)
+ {
+ if(ii->Y == this->getId()) // This is a child of our parent
+ {
+ ClientActiveObject *obj = m_env->getActiveObject(ii->X); // Get the object of the child
+ if(obj)
+ {
+ if(permanent)
+ {
+ // The parent is being permanently removed, so the child stays detached
+ ii->Y = 0;
+ obj->updateParent();
+ }
+ else
+ {
+ // The parent is being refreshed, detach our child enough to avoid bad memory reads
+ // This only stays into effect for a few frames, as addToScene will parent its children back
+ scene::IMeshSceneNode *m_child_meshnode = obj->getMeshSceneNode();
+ scene::IAnimatedMeshSceneNode *m_child_animated_meshnode = obj->getAnimatedMeshSceneNode();
+ scene::IBillboardSceneNode *m_child_spritenode = obj->getSpriteSceneNode();
+ if(m_child_meshnode)
+ m_child_meshnode->setParent(m_smgr->getRootSceneNode());
+ if(m_child_animated_meshnode)
+ m_child_animated_meshnode->setParent(m_smgr->getRootSceneNode());
+ if(m_child_spritenode)
+ m_child_spritenode->setParent(m_smgr->getRootSceneNode());
+ }
+ }
+ }
+ }
+
if(m_meshnode){
m_meshnode->remove();
m_meshnode = NULL;
@@ -749,6 +836,18 @@ public:
m_smgr = smgr;
m_irr = irr;
+ // If this object has attachments and is being re-added after having been refreshed, parent its children back.
+ // The parent ID for this child hasn't been changed in attachment_list, so just update its attachments.
+ for(std::vector<core::vector2d<int> >::iterator ii = attachment_list.begin(); ii != attachment_list.end(); ii++)
+ {
+ if(ii->Y == this->getId()) // This is a child of our parent
+ {
+ ClientActiveObject *obj = m_env->getActiveObject(ii->X); // Get the object of the child
+ if(obj)
+ obj->updateParent();
+ }
+ }
+
if(m_meshnode != NULL || m_animated_meshnode != NULL || m_spritenode != NULL)
return;
@@ -828,7 +927,7 @@ public:
mesh->addMeshBuffer(buf);
buf->drop();
}
- m_meshnode = smgr->addMeshSceneNode(mesh, NULL);
+ m_meshnode = smgr->addMeshSceneNode(mesh, m_smgr->getRootSceneNode());
mesh->drop();
// Set it to use the materials of the meshbuffers directly.
// This is needed for changing the texture in the future
@@ -837,7 +936,7 @@ public:
else if(m_prop.visual == "cube"){
infostream<<"GenericCAO::addToScene(): cube"<<std::endl;
scene::IMesh *mesh = createCubeMesh(v3f(BS,BS,BS));
- m_meshnode = smgr->addMeshSceneNode(mesh, NULL);
+ m_meshnode = smgr->addMeshSceneNode(mesh, m_smgr->getRootSceneNode());
mesh->drop();
m_meshnode->setScale(v3f(m_prop.visual_size.X,
@@ -851,7 +950,7 @@ public:
scene::IAnimatedMesh *mesh = smgr->getMesh(m_prop.mesh.c_str());
if(mesh)
{
- m_animated_meshnode = smgr->addAnimatedMeshSceneNode(mesh, NULL);
+ m_animated_meshnode = smgr->addAnimatedMeshSceneNode(mesh, m_smgr->getRootSceneNode());
m_animated_meshnode->animateJoints(); // Needed for some animations
m_animated_meshnode->setScale(v3f(m_prop.visual_size.X,
m_prop.visual_size.Y,
@@ -876,7 +975,7 @@ public:
irr->getVideoDriver()->getMeshManipulator();
scene::IMesh *mesh = manip->createMeshUniquePrimitives(item_mesh);
- m_meshnode = smgr->addMeshSceneNode(mesh, NULL);
+ m_meshnode = smgr->addMeshSceneNode(mesh, m_smgr->getRootSceneNode());
mesh->drop();
m_meshnode->setScale(v3f(m_prop.visual_size.X/2,
@@ -918,7 +1017,7 @@ public:
void updateLight(u8 light_at_pos)
{
// Objects attached to the local player should always be hidden
- if(m_attachment_parent != NULL && m_attachment_parent->isLocalPlayer())
+ if(getParent() != NULL && getParent()->isLocalPlayer())
m_is_visible = false;
else
m_is_visible = (m_hp != 0);
@@ -949,7 +1048,7 @@ public:
void updateNodePos()
{
- if(m_attachment_parent != NULL)
+ if(getParent() != NULL)
return;
if(m_meshnode){
@@ -975,14 +1074,27 @@ public:
if(m_visuals_expired && m_smgr && m_irr){
m_visuals_expired = false;
- removeFromScene();
+ removeFromScene(false);
addToScene(m_smgr, m_gamedef->tsrc(), m_irr);
updateAnimations();
updateBonePosRot();
updateAttachments();
+ return;
}
- if(m_attachment_parent == NULL) // Attachments should be glued to their parent by Irrlicht
+ if(getParent() != NULL) // Attachments should be glued to their parent by Irrlicht
+ {
+ // Set these for later
+ if(m_meshnode)
+ m_position = m_meshnode->getAbsolutePosition();
+ if(m_animated_meshnode)
+ m_position = m_animated_meshnode->getAbsolutePosition();
+ if(m_spritenode)
+ m_position = m_spritenode->getAbsolutePosition();
+ m_velocity = v3f(0,0,0);
+ m_acceleration = v3f(0,0,0);
+ }
+ else
{
if(m_prop.physical){
core::aabbox3d<f32> box = m_prop.collisionbox;
@@ -1047,17 +1159,10 @@ public:
updateTextures("");
}
}
- if(fabs(m_prop.automatic_rotate) > 0.001){
+ if(getParent() == NULL && fabs(m_prop.automatic_rotate) > 0.001){
m_yaw += dtime * m_prop.automatic_rotate * 180 / M_PI;
updateNodePos();
}
-
- // REMAINING ATTACHMENT ISSUES:
- // Absolute Position of attachments is printed differently here than what it's set to in the SetAttachment function.
- // Apparently here it prints the origin of the parent, but ignores the offset it was actually set to.
-
- //if(m_animated_meshnode != NULL && m_attachment_parent != NULL)
- // errorstream<<"Attachment position, step: "<<m_animated_meshnode->getAbsolutePosition().X<<","<<m_animated_meshnode->getAbsolutePosition().Y<<","<<m_animated_meshnode->getAbsolutePosition().Z<<std::endl;
}
void updateTexturePos()
@@ -1123,9 +1228,15 @@ public:
m_spritenode->setMaterialTexture(0,
tsrc->getTextureRaw(texturestring));
- // Does not work yet with the current lighting settings
- m_meshnode->getMaterial(0).AmbientColor = m_prop.colors[0];
- m_meshnode->getMaterial(0).DiffuseColor = m_prop.colors[0];
+ // This allows setting per-material colors. However, until a real lighting
+ // system is added, the code below will have no effect. Once MineTest
+ // has directional lighting, it should work automatically.
+ if(m_prop.colors.size() >= 1)
+ {
+ m_meshnode->getMaterial(0).AmbientColor = m_prop.colors[0];
+ m_meshnode->getMaterial(0).DiffuseColor = m_prop.colors[0];
+ m_meshnode->getMaterial(0).SpecularColor = m_prop.colors[0];
+ }
}
}
if(m_animated_meshnode)
@@ -1153,9 +1264,12 @@ public:
}
for (u32 i = 0; i < m_prop.colors.size(); ++i)
{
- // Does not work yet with the current lighting settings
+ // This allows setting per-material colors. However, until a real lighting
+ // system is added, the code below will have no effect. Once MineTest
+ // has directional lighting, it should work automatically.
m_animated_meshnode->getMaterial(i).AmbientColor = m_prop.colors[i];
m_animated_meshnode->getMaterial(i).DiffuseColor = m_prop.colors[i];
+ m_animated_meshnode->getMaterial(i).SpecularColor = m_prop.colors[i];
}
}
}
@@ -1184,9 +1298,15 @@ public:
material.getTextureMatrix(0).setTextureTranslate(pos.X, pos.Y);
material.getTextureMatrix(0).setTextureScale(size.X, size.Y);
- // Does not work yet with the current lighting settings
- m_meshnode->getMaterial(i).AmbientColor = m_prop.colors[i];
- m_meshnode->getMaterial(i).DiffuseColor = m_prop.colors[i];
+ // This allows setting per-material colors. However, until a real lighting
+ // system is added, the code below will have no effect. Once MineTest
+ // has directional lighting, it should work automatically.
+ if(m_prop.colors.size() > i)
+ {
+ m_meshnode->getMaterial(i).AmbientColor = m_prop.colors[i];
+ m_meshnode->getMaterial(i).DiffuseColor = m_prop.colors[i];
+ m_meshnode->getMaterial(i).SpecularColor = m_prop.colors[i];
+ }
}
}
else if(m_prop.visual == "upright_sprite")
@@ -1201,9 +1321,15 @@ public:
buf->getMaterial().setTexture(0,
tsrc->getTextureRaw(tname));
- // Does not work yet with the current lighting settings
- m_meshnode->getMaterial(0).AmbientColor = m_prop.colors[0];
- m_meshnode->getMaterial(0).DiffuseColor = m_prop.colors[0];
+ // This allows setting per-material colors. However, until a real lighting
+ // system is added, the code below will have no effect. Once MineTest
+ // has directional lighting, it should work automatically.
+ if(m_prop.colors.size() >= 1)
+ {
+ buf->getMaterial().AmbientColor = m_prop.colors[0];
+ buf->getMaterial().DiffuseColor = m_prop.colors[0];
+ buf->getMaterial().SpecularColor = m_prop.colors[0];
+ }
}
{
std::string tname = "unknown_object.png";
@@ -1216,9 +1342,21 @@ public:
buf->getMaterial().setTexture(0,
tsrc->getTextureRaw(tname));
- // Does not work yet with the current lighting settings
- m_meshnode->getMaterial(1).AmbientColor = m_prop.colors[1];
- m_meshnode->getMaterial(1).DiffuseColor = m_prop.colors[1];
+ // This allows setting per-material colors. However, until a real lighting
+ // system is added, the code below will have no effect. Once MineTest
+ // has directional lighting, it should work automatically.
+ if(m_prop.colors.size() >= 2)
+ {
+ buf->getMaterial().AmbientColor = m_prop.colors[1];
+ buf->getMaterial().DiffuseColor = m_prop.colors[1];
+ buf->getMaterial().SpecularColor = m_prop.colors[1];
+ }
+ else if(m_prop.colors.size() >= 1)
+ {
+ buf->getMaterial().AmbientColor = m_prop.colors[0];
+ buf->getMaterial().DiffuseColor = m_prop.colors[0];
+ buf->getMaterial().SpecularColor = m_prop.colors[0];
+ }
}
}
}
@@ -1226,7 +1364,7 @@ public:
void updateAnimations()
{
- if(!m_animated_meshnode)
+ if(m_animated_meshnode == NULL)
return;
m_animated_meshnode->setFrameLoop((int)m_frames.X, (int)m_frames.Y);
@@ -1236,14 +1374,17 @@ public:
void updateBonePosRot()
{
- if(m_bone_posrot.size() > 0)
- {
- m_animated_meshnode->setJointMode(irr::scene::EJUOR_CONTROL); // To write positions to the mesh on render
- for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_posrot.begin(); ii != m_bone_posrot.end(); ++ii){
- std::string bone_name = (*ii).first;
- v3f bone_pos = (*ii).second.X;
- v3f bone_rot = (*ii).second.Y;
- irr::scene::IBoneSceneNode* bone = m_animated_meshnode->getJointNode(bone_name.c_str());
+ if(!m_bone_posrot.size() || m_animated_meshnode == NULL)
+ return;
+
+ m_animated_meshnode->setJointMode(irr::scene::EJUOR_CONTROL); // To write positions to the mesh on render
+ for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_posrot.begin(); ii != m_bone_posrot.end(); ++ii){
+ std::string bone_name = (*ii).first;
+ v3f bone_pos = (*ii).second.X;
+ v3f bone_rot = (*ii).second.Y;
+ irr::scene::IBoneSceneNode* bone = m_animated_meshnode->getJointNode(bone_name.c_str());
+ if(bone)
+ {
bone->setPosition(bone_pos);
bone->setRotation(bone_rot);
}
@@ -1252,38 +1393,7 @@ public:
void updateAttachments()
{
- // REMAINING ATTACHMENT ISSUES:
- // We get to this function when the object is an attachment that needs to
- // be attached to its parent. If a bone is set we attach it to that skeletal
- // bone, otherwise just to the object's origin. Attachments should not copy parent
- // position as that's laggy... instead the Irrlicht function(s) to attach should
- // be used. If the parent object is NULL that means this object should be detached.
- // This function is only called whenever a GENERIC_CMD_SET_ATTACHMENT message is received.
-
- // We already attach our entity on the server too (copy position). Reason we attach
- // to the client as well is first of all lag. The server sends the position
- // of the child separately than that of the parent, so even on localhost
- // you'd see the child lagging behind. Models are also client-side, so this is
- // needed to read bone data and attach to joints.
-
- // Functions:
- // - m_attachment_parent is ClientActiveObject* for the parent entity.
- // - m_attachment_bone is std::string of the bone, "" means none.
- // - m_attachment_position is v3f and represents the position offset of the attachment.
- // - m_attachment_rotation is v3f and represents the rotation offset of the attachment.
-
- // Implementation information:
- // From what I know, we need to get the AnimatedMeshSceneNode of m_attachment_parent then
- // use parent_node->addChild(m_animated_meshnode) for position attachment. For skeletal
- // attachment I don't know yet. Same must be used to detach when a NULL parent is received.
-
- //Useful links:
- // http://irrlicht.sourceforge.net/forum/viewtopic.php?t=7514
- // http://www.irrlicht3d.org/wiki/index.php?n=Main.HowToUseTheNewAnimationSystem
- // http://gamedev.stackexchange.com/questions/27363/finding-the-endpoint-of-a-named-bone-in-irrlicht
- // Irrlicht documentation: http://irrlicht.sourceforge.net/docu/
-
- if(m_attachment_parent == NULL || m_attachment_parent->isLocalPlayer()) // Detach
+ if(getParent() == NULL || getParent()->isLocalPlayer()) // Detach
{
if(m_meshnode)
{
@@ -1315,25 +1425,21 @@ public:
}
else // Attach
{
- // REMAINING ATTACHMENT ISSUES:
- // The code below should cause the child to get attached, but for some reason it's not working
- // A debug print confirms both position and absolute position are set accordingly, but the object still doesn't show
- // Position and Absolute Position were tested to be set properly here
-
scene::IMeshSceneNode *parent_mesh = NULL;
- if(m_attachment_parent->getMeshSceneNode())
- parent_mesh = m_attachment_parent->getMeshSceneNode();
+ if(getParent()->getMeshSceneNode())
+ parent_mesh = getParent()->getMeshSceneNode();
scene::IAnimatedMeshSceneNode *parent_animated_mesh = NULL;
- if(m_attachment_parent->getAnimatedMeshSceneNode())
- parent_animated_mesh = m_attachment_parent->getAnimatedMeshSceneNode();
+ if(getParent()->getAnimatedMeshSceneNode())
+ parent_animated_mesh = getParent()->getAnimatedMeshSceneNode();
scene::IBillboardSceneNode *parent_sprite = NULL;
- if(m_attachment_parent->getSpriteSceneNode())
- parent_sprite = m_attachment_parent->getSpriteSceneNode();
+ if(getParent()->getSpriteSceneNode())
+ parent_sprite = getParent()->getSpriteSceneNode();
scene::IBoneSceneNode *parent_bone = NULL;
if(parent_animated_mesh && m_attachment_bone != "")
parent_bone = parent_animated_mesh->getJointNode(m_attachment_bone.c_str());
+ // The spaghetti code below makes sure attaching works if either the parent or child is a spritenode, meshnode, or animatedmeshnode
// TODO: Perhaps use polymorphism here to save code duplication
if(m_meshnode){
if(parent_bone){
@@ -1452,7 +1558,6 @@ public:
else if(cmd == GENERIC_CMD_UPDATE_POSITION)
{
// Not sent by the server if the object is an attachment
-
m_position = readV3F1000(is);
m_velocity = readV3F1000(is);
m_acceleration = readV3F1000(is);
@@ -1462,6 +1567,9 @@ public:
bool is_end_position = readU8(is);
float update_interval = readF1000(is);
+ if(getParent() != NULL) // Just in case
+ return;
+
// Place us a bit higher if we're physical, to not sink into
// the ground due to sucky collision detection...
if(m_prop.physical)
@@ -1500,7 +1608,7 @@ public:
m_frame_speed = readF1000(is);
m_frame_blend = readF1000(is);
- expireVisuals(); // Automatically calls the proper function next
+ updateAnimations();
}
else if(cmd == GENERIC_CMD_SET_BONE_POSROT)
{
@@ -1509,20 +1617,25 @@ public:
v3f rotation = readV3F1000(is);
m_bone_posrot[bone] = core::vector2d<v3f>(position, rotation);
- expireVisuals(); // Automatically calls the proper function next
+ updateBonePosRot();
}
else if(cmd == GENERIC_CMD_SET_ATTACHMENT)
{
- int parent_id = readS16(is);
- ClientActiveObject *obj = m_env->getActiveObject(parent_id);
- if(!parent_id || !obj)
- obj = NULL;
- m_attachment_parent = obj;
+ // If an entry already exists for this object, delete it first to avoid duplicates
+ for(std::vector<core::vector2d<int> >::iterator ii = attachment_list.begin(); ii != attachment_list.end(); ii++)
+ {
+ if(ii->X == this->getId()) // This is the ID of our object
+ {
+ attachment_list.erase(ii);
+ break;
+ }
+ }
+ attachment_list.push_back(core::vector2d<int>(this->getId(), readS16(is)));
m_attachment_bone = deSerializeString(is);
m_attachment_position = readV3F1000(is);
m_attachment_rotation = readV3F1000(is);
- expireVisuals(); // Automatically calls the proper function next
+ updateAttachments();
}
else if(cmd == GENERIC_CMD_PUNCHED)
{