I spend a few hours yesterday on getting into the animation system and looking for this. No solution yet and I have to do other stuff today, but some findings in case anyone can else takes that bug over.
First some minor modifications to the code example. It did not set the JointMode and I first suspected this to be the reason. But unfortunately this did not fix the bug. Then I added debug-output which shows that the bones used in the mesh-drawing are clearly correct. Another test with animating joints manually shows that this is also failing and also gives some further hints for the bug.
So here the new example (still using the model from the zip above):
Code: Select all
#include <irrlicht.h>
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
#ifdef _IRR_WINDOWS_
#pragma comment(lib, "Irrlicht.lib")
#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif
int main()
{
stringw debug_string;
IrrlichtDevice *device =createDevice( video::EDT_OPENGL, dimension2d<u32>(640, 480), 16,false, false, false, 0);
if (!device)
return 1;
device->setWindowCaption(L"Bone Bug");
IVideoDriver* driver = device->getVideoDriver();
ISceneManager* smgr = device->getSceneManager();
IGUIEnvironment* guienv = device->getGUIEnvironment();
IGUIStaticText* text = guienv->addStaticText(L">.<",
rect<s32>(10,10,260,64), true);
//The main model.
IAnimatedMesh* mesh = smgr->getMesh("models/block_rig.x");
if (!mesh) {
device->drop();
return 1;
}
IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode( mesh );
node->setAnimationSpeed(12);
node->setMaterialFlag(EMF_LIGHTING,false);
node->setJointMode(EJUOR_READ);
node->setDebugDataVisible(scene::EDS_SKELETON);
//node->setJointMode(EJUOR_CONTROL );
//The bone being accessed is the left hand.
IBoneSceneNode* bone = node->getJointNode("handL");
//This axe shows where the function is returning the absolute position (the handle is at that position).
IMeshSceneNode * sphere = smgr->addSphereSceneNode (.1f, 16);
sphere->getMaterial(0).Lighting = false;
smgr->addCameraSceneNode(0, vector3df(0,3,-4), vector3df(0,1,0));
while(device->run())
{
debug_string = L"Bone absolute position does not correspond to where it is drawn.";
debug_string += L"\nBone X: ";
debug_string += stringw(bone->getAbsolutePosition().X);
debug_string += L"\nBone Y: ";
debug_string += stringw(bone->getAbsolutePosition().Y);
debug_string += L"\nBone Z: ";
debug_string += stringw(bone->getAbsolutePosition().Z);
text->setText(debug_string.c_str());
driver->beginScene(true, true, SColor(255,100,101,140));
smgr->drawAll();
guienv->drawAll();
driver->endScene();
// node->animateJoints();
sphere->setPosition(bone->getAbsolutePosition());
}
device->drop();
return 0;
}
I suppose the function that fails is CSkinnedMesh::recoverJointsFromMesh. And seeing the comments in there it's something on which someone else already worked for some time. It seems to me that we have the correct matrix for relative and absolute transformations in the joint already. But we can't seem to just use them in the SceneNode because we work with euler angles there and so we try to recover the values from the relative transformation matrix in recoverJointsFromMesh and then do re-calculate the absolute matrix once more. And seeing that getScale is outcommented this might be the problem (and it's only getting more complicated from there on). I see no obvious solution for this so far, although I learned a lot more about matrix math yesterday.
Some workaround for the engine I could think of (not yet tested) would be copying the matrices of the joint (or maybe only the relative transformation matrix) directly into the bonescenenode. Maybe for read-only access, so the information can just be read by the user.
A workaround without changing the engine, but which works probably only when you don't have more than one instance of a mesh is that you can probably get the transformations for now from the mesh and it's joints directly.