Adding skeletal animation to Mesh on-the-fly (programmatically)

A place for users of OGRE to discuss ideas and experiences of utilitising OGRE in their games / demos / applications.
User avatar
only_a_ptr
Halfling
Posts: 65
Joined: Sun Apr 26, 2009 8:43 pm
x 3

Adding skeletal animation to Mesh on-the-fly (programmatically)

Post by only_a_ptr »

Hello greenskins,

I'm attempting a shenanigan: To load a mesh without blend indices/weights, add those on-the-fly, then manually create skeleton/bones and make it work together. The purpose is to replace an existing (crude and inefficient) mesh deformation system in Rigs of Rods. I'm using software skinning.

I seem to be able to augment the mesh and also get the skeleton working (no assert()s, no Exceptions). However, I get a crash within Ogre::OptimisedUtilSSE. Note I've only tried with OGRE 1.11.6 which the project is presently stuck with. I'll try to test in a OGRE14 porting branch later.

The error I get is "Exception thrown: read access violation. Ogre::TransformBase::operator[](...) returned 0xFFFFFFFFFFFFFFFF." which feels like the transform matrices aren't set up right. I noticed there's a Ogre::Mesh::prepareMatricesForVertexBlend() function, but that appears to be called by Ogre::Entity::updateAnimation() which is in my callstack, so I should be good.

This is my stack trace (a screenshot of the debugger view is attached):

Code: Select all

	OgreMain_d.dll!Ogre::SoftwareVertexSkinning_SSE_PosNorm_Separated_Packed<1,1,1,1>::apply(const float * pSrcPos, float * pDestPos, const float * pSrcNorm, float * pDestNorm, const float * pBlendWeight, const unsigned char * pBlendIndex, const Ogre::Affine3 * const * blendMatrices, unsigned __int64 blendWeightStride, unsigned __int64 blendIndexStride, unsigned __int64 numWeightsPerVertex, unsigned __int64 numIterations) Line 787	C++
 	OgreMain_d.dll!Ogre::softwareVertexSkinning_SSE_PosNorm_Separated_Packed(const float * pSrcPos, float * pDestPos, const float * pSrcNorm, float * pDestNorm, const float * pBlendWeight, const unsigned char * pBlendIndex, const Ogre::Affine3 * const * blendMatrices, unsigned __int64 blendWeightStride, unsigned __int64 blendIndexStride, unsigned __int64 numWeightsPerVertex, unsigned __int64 numIterations) Line 907	C++
 	OgreMain_d.dll!Ogre::OptimisedUtilSSE::softwareVertexSkinning(const float * pSrcPos, float * pDestPos, const float * pSrcNorm, float * pDestNorm, const float * pBlendWeight, const unsigned char * pBlendIndex, const Ogre::Affine3 * const * blendMatrices, unsigned __int64 srcPosStride, unsigned __int64 destPosStride, unsigned __int64 srcNormStride, unsigned __int64 destNormStride, unsigned __int64 blendWeightStride, unsigned __int64 blendIndexStride, unsigned __int64 numWeightsPerVertex, unsigned __int64 numVertices) Line 1189	C++
 	OgreMain_d.dll!Ogre::Mesh::softwareVertexBlend(const Ogre::VertexData * sourceVertexData, const Ogre::VertexData * targetVertexData, const Ogre::Affine3 * const * blendMatrices, unsigned __int64 numMatrices, bool blendNormals) Line 2040	C++
 	OgreMain_d.dll!Ogre::Entity::updateAnimation() Line 948	C++
 	OgreMain_d.dll!Ogre::Entity::_updateRenderQueue(Ogre::RenderQueue * queue) Line 704	C++
 	OgreMain_d.dll!Ogre::RenderQueue::processVisibleObject(Ogre::MovableObject * mo, Ogre::Camera * cam, bool onlyShadowCasters, Ogre::VisibleObjectsBoundsInfo * visibleBounds) Line 254	C++
 	OgreMain_d.dll!Ogre::SceneNode::_findVisibleObjects(Ogre::Camera * cam, Ogre::RenderQueue * queue, Ogre::VisibleObjectsBoundsInfo * visibleBounds, bool includeChildren, bool displayNodes, bool onlyShadowCasters) Line 274	C++
 	OgreMain_d.dll!Ogre::SceneNode::_findVisibleObjects(Ogre::Camera * cam, Ogre::RenderQueue * queue, Ogre::VisibleObjectsBoundsInfo * visibleBounds, bool includeChildren, bool displayNodes, bool onlyShadowCasters) Line 285	C++
 	OgreMain_d.dll!Ogre::SceneManager::_findVisibleObjects(Ogre::Camera * cam, Ogre::VisibleObjectsBoundsInfo * visibleBounds, bool onlyShadowCasters) Line 1700	C++
 	OgreMain_d.dll!Ogre::SceneManager::_renderScene(Ogre::Camera * camera, Ogre::Viewport * vp, bool includeOverlays) Line 1434	C++
 	OgreMain_d.dll!Ogre::Camera::_renderScene(Ogre::Viewport * vp, bool includeOverlays) Line 421	C++
 	OgreMain_d.dll!Ogre::Viewport::update() Line 222	C++
 	OgreMain_d.dll!Ogre::RenderTarget::_updateViewport(Ogre::Viewport * viewport, bool updateStatistics) Line 204	C++
 	RenderSystem_Direct3D9_d.dll!Ogre::D3D9RenderWindow::_updateViewport(Ogre::Viewport * viewport, bool updateStatistics) Line 852	C++
 	OgreMain_d.dll!Ogre::RenderTarget::_updateAutoUpdatedViewports(bool updateStatistics) Line 183	C++
 	OgreMain_d.dll!Ogre::RenderTarget::updateImpl() Line 159	C++
 	OgreMain_d.dll!Ogre::RenderTarget::update(bool swap) Line 574	C++
 	OgreMain_d.dll!Ogre::RenderSystem::_updateAllRenderTargets(bool swapBuffers) Line 122	C++
 	OgreMain_d.dll!Ogre::Root::_updateAllRenderTargets() Line 1297	C++
 	OgreMain_d.dll!Ogre::Root::renderOneFrame() Line 876	C++
 	RoR.exe!main(int argc, char * * argv) Line 1704	C++
 	RoR.exe!WinMain(HINSTANCE__ * hInst, HINSTANCE__ * __formal, char * strCmdLine, int __formal) Line 1735	C++

This is my code (abridged):

Code: Select all

m_skeleton = Ogre::SkeletonManager::getSingleton().create(skeleton_name, GetResourceGroup());
assert(m_skeleton->getGroup() == m_mesh_clone->getGroup());
m_mesh_clone->_notifySkeleton(m_skeleton); // Cannot use `setSkeletonName()` because that attempts to load _directly_ from file.

Ogre::Bone* root_bone = m_skeleton->createBone("root", 0);
root_bone->setManuallyControlled(true);
for (...)
{
    Ogre::Bone* bone = m_skeleton->createBone(fmt::format("bone{}", bone_index), bone_index);
    bone->setManuallyControlled(true);
    root_bone->addChild(bone);
    bone->setPosition(...);

for (...)
{
        mesh->addBoneAssignment(vba); // 1-based boneIndex (0 is the root bone)
}
}

m_skeleton->setBindingPose();
mesh->_compileBoneAssignments();

// create entity...

My questions are:

  • Can I somehow disable the SSE-optimized vertex blending? I'm assuming there's some simple blending implementation that may give me more insight.
  • Is there something I'm doing apparently wrong in the code above?

Thanks.

You do not have the required permissions to view the files attached to this post.
Rigs of Rods is alive and kicking!
User avatar
only_a_ptr
Halfling
Posts: 65
Joined: Sun Apr 26, 2009 8:43 pm
x 3

Re: Adding skeletal animation to Mesh on-the-fly (programmatically)

Post by only_a_ptr »

Bump!

I found the culprit - a bug in my bone generation code:

Code: Select all

00:15:44: [ROR|FlexBody] DBG creating bones
00:15:44: [ROR|FlexBody]     DBG Bone 'bone1'(handle 1) assigned 808 vertices

Before I found this, I tried serializing the skeleton to file; it went without exceptions/asserts, but the file was apparently corrupted, as loading it ended with exception InvalidParametersException: Couldn't read 16 bit header value from input stream..

Rigs of Rods is alive and kicking!
User avatar
only_a_ptr
Halfling
Posts: 65
Joined: Sun Apr 26, 2009 8:43 pm
x 3

Re: Adding skeletal animation to Mesh on-the-fly (programmatically)

Post by only_a_ptr »

Bump2

I tried serializing the modified mesh/skeleton files and loading them back, to see what happens. The export goes well (no exceptions/asserts) but loading back the .skeleton fails with an error InvalidParametersException: Couldn't read 16 bit header value from input stream. in Serializer::determineEndianness ....

I looked at the file in hex viewer and it seems OK, at least the header is identical with another .skeleton file that works OK. See attachment. I left endianness setting at the default value (NATIVE).

Excerpt from my Ogre.log:

Code: Select all

02:10:12: [ROR|FlexBody] DBG creating bones
02:10:12: [ROR|FlexBody]     DBG Bone 'bone01' (handle=1) assigned 131 vertices
02:10:12: [ROR|FlexBody]     DBG Bone 'bone02' (handle=2) assigned 25 vertices
02:10:12: [ROR|FlexBody]     DBG Bone 'bone03' (handle=3) assigned 31 vertices
02:10:12: [ROR|FlexBody]     DBG Bone 'bone04' (handle=4) assigned 63 vertices
02:10:12: [ROR|FlexBody]     DBG Bone 'bone05' (handle=5) assigned 56 vertices
02:10:12: [ROR|FlexBody]     DBG Bone 'bone06' (handle=6) assigned 10 vertices
02:10:12: [ROR|FlexBody]     DBG Bone 'bone07' (handle=7) assigned 14 vertices
02:10:12: [ROR|FlexBody]     DBG Bone 'bone08' (handle=8) assigned 75 vertices
02:10:12: [ROR|FlexBody]     DBG Bone 'bone09' (handle=9) assigned 59 vertices
02:10:12: [ROR|FlexBody]     DBG Bone 'bone10' (handle=10) assigned 8 vertices
02:10:12: [ROR|FlexBody] DBG Exporting mesh...`
02:10:12: MeshSerializer writing mesh data to stream gavril23.zip_gavrilrally.truck-FlexBody00(gavrilRheadlight_r).mesh...
02:10:12: File header written.
02:10:12: Writing mesh data...
02:10:12: Writing submesh...
02:10:12: Exporting submesh texture aliases...
02:10:12: Submesh texture aliases exported.
02:10:12: Submesh exported.
02:10:12: Exporting skeleton link...
02:10:12: Skeleton link exported.
02:10:12: Exporting shared geometry bone assignments...
02:10:12: Shared geometry bone assignments exported.
02:10:12: Exporting bounds information....
02:10:12: Bounds information exported.
02:10:12: Exporting submesh name table...
02:10:12: Submesh name table exported.
02:10:12: Exporting edge lists...
02:10:12: Edge lists exported
02:10:12: Mesh data exported.
02:10:12: MeshSerializer export successful.
02:10:12: [ROR|FlexBody] DBG Exporting skeleton (11 bones, 0 animations)`
02:10:12: Exporting bones..
02:10:12: Bones exported.
02:10:12: Exporting animations, count=0
02:10:12: [ROR|FlexBody] DBG Loading back the exported mesh (skeleton should load automatically)
02:10:12: Mesh: Loading gavril23.zip_gavrilrally.truck-FlexBody00(gavrilRheadlight_r).mesh.
02:10:12: Skeleton: Loading gavril23.zip_gavrilrally.truck-FlexBody00(gavrilRheadlight_r).skeleton
02:10:12: Ogre::InvalidParametersException::InvalidParametersException: Couldn't read 16 bit header value from input stream. in Serializer::determineEndianness at C:\Users\Petr\.conan2\p\b\ogre3ff3740bbb9785\b\OgreMain\src\OgreSerializer.cpp (line 68)
02:10:12: Error: Unable to load skeleton 'gavril23.zip_gavrilrally.truck-FlexBody00(gavrilRheadlight_r).skeleton' for Mesh 'gavril23.zip_gavrilrally.truck-FlexBody00(gavrilRheadlight_r).mesh'. This Mesh will not be animated. You can ignore this message if you are using an offline tool.

The code which produced that log:

Code: Select all

    // Serialize the modified mesh + skeleton to cache
    LOG("[ROR|FlexBody] DBG Exporting mesh...`");
    Ogre::MeshSerializer mesh_serializer;
    mesh_serializer.exportMesh(mesh.get(),
        Ogre::ResourceGroupManager::getSingleton().createResource(cache_meshname, RGN_CACHE, /*overwrite:*/true));

LOG(fmt::format("[ROR|FlexBody] DBG Exporting skeleton ({} bones, {} animations)`", skeleton->getNumBones(), skeleton->getNumAnimations()));
Ogre::SkeletonSerializer skeleton_serializer;
skeleton_serializer.exportSkeleton(skeleton.get(),
    Ogre::ResourceGroupManager::getSingleton().createResource(cache_skeletonname, RGN_CACHE, /*overwrite:*/true));

// Unload the original mesh and skeleton
Ogre::MeshManager::getSingleton().unload(mesh->getHandle());
Ogre::MeshManager::getSingleton().remove(mesh->getHandle());
mesh.setNull();

Ogre::SkeletonManager::getSingleton().unload(skeleton->getHandle());
Ogre::SkeletonManager::getSingleton().remove(skeleton->getHandle());
skeleton.setNull();

// Load the serialized mesh (skeleton should load automatically)
LOG("[ROR|FlexBody] DBG Loading back the exported mesh (skeleton should load automatically)");
m_animated_mesh = Ogre::MeshManager::getSingleton().load(cache_meshname, RGN_CACHE);
m_skeleton = m_animated_mesh->getSkeleton();
You do not have the required permissions to view the files attached to this post.
Rigs of Rods is alive and kicking!
paroj
OGRE Team Member
OGRE Team Member
Posts: 2106
Joined: Sun Mar 30, 2014 2:51 pm
x 1132

Re: Adding skeletal animation to Mesh on-the-fly (programmatically)

Post by paroj »

pretty sure you managed to corrupt the memory somehow and the written file is mostly fine. Try to inspect the variables of the Serializer class in the debugger whether they make sense.