Jump to content

  • Log In with Google      Sign In   
  • Create Account

Awesome job so far everyone! Please give us your feedback on how our article efforts are going. We still need more finished articles for our May contest theme: Remake the Classics

Digitalfragment

Member Since 29 Aug 2002
Offline Last Active Oct 21 2012 09:36 PM
*****

Posts I've Made

In Topic: Proper order of operations for skinning

02 October 2012 - 07:44 PM

Export the inverse of the worldspace bind pose for each joint.

Is the worldspace bind pose the same as the bind pose used to transform the vertices to get the "post-transformed vertex data"? i.e. The matrix obtained by rootMatrix*modelMatrix? If so the bind pose is the same for each joint... so I don't understand.

The worldspace bindpose matrix for a bone is the the world space matrix for that bone itself in blender. If you only have local space matrices for the bones in blender, then you have to work up the tree from the root node to generate them.

So you're saying the animation matrix should be created like the following?

Matrix4f animationTransform = new Quaternion(rotation).toMatrix();
   animationTransform.m00 *= scale.x;
   animationTransform.m01 *= scale.x;
   animationTransform.m02 *= scale.x;
  
   animationTransform.m10 *= scale.y;
   animationTransform.m11 *= scale.y;
   animationTransform.m12 *= scale.y;
  
   animationTransform.m20 *= scale.z;
   animationTransform.m21 *= scale.z;
   animationTransform.m22 *= scale.z;
  
   animationTransform.m30 = position.x;
   animationTransform.m31 = position.y;
   animationTransform.m32 = position.z;


Where does the armatureMatrix and parent animation transforms come into play?

That animationTransform matrix is the local-space matrix generated by a single frame of animation, relative to the space of the parent.
The parent animation transforms come into play when generating the world transform for the parent, as its the world transform of the parent you need to pass down to the children in the tree.

The armatureMatrix in your case might be a pre-concatenation matrix for the animation transform?
I've never needed anything beyond: parentBoneWorldspace * childAnimatedLocalspace * inverseBindPose

I know that

   Matrix4f.mul(transformMatrix, boneMatrix, transformMatrix);
   Matrix4f.mul(transformMatrix, skinMatrix, transformMatrix);

Results in the identity matrix. But if I multiply in the animationTransform from above, things get wonky. I made an animation that's just the bind pose. But the animationTransform is not the identity matrix when the model is in the bind pose, so what's the deal?

animationTransform is a local space matrix. When your animationTransforms are converted to world space by walking the tree and concatenating the transforms, the resulting matricies should look like the boneMatrix values. Unless your boneMatrix (and corresponding skinMatrix) are in a different coordinate space.

I suggest writing some debug rendering code that draws the matrices as axis-markers connected by lines showing the hierarchy.

In Topic: Proper order of operations for skinning

01 October 2012 - 05:45 PM

Edit as I missed a section of your post and ended up misreading the question as a result.

I'm not sure why you have a seperate "skinMatrix" and "transformMatrix" being applied to the animation:

Export your vertex data post-transformed, so that the model looks correct when skinned with identity matricies.

Export the inverse of the worldspace bind pose for each joint.
This matrix is used to bring the vertex back into local space as necessary.

At runtime, the skinning matrix is the animated worldspace matrix multiplied by the inverse-bind-pose matrix.
If the worldspace matrices match the original bind pose, you get identity matrices and so your model is correct. If you move the joints around, you will see the mesh move correctly.


The local-space animation matrix is built by composing a rotation matrix from the quaternion (which should just end up being the top left 3x3), multiplying the topleft 3x3 by the scale components (no need to multiply the bottom row or right column) then setting the translation row (or column depending on whether your matrices are transposed) You are better off writing the matrix manually instead of multiplying in the data:



On the note of inverse bind pose matrices, the big reason to stick with storing the inverse bind pose and leaving the mesh data in transformed space on export is for multiple influences per vertex. If you were to transform the mesh into local space, it would be a unique local space per bone influence.

When dealing with multiple influences you can either run the vertex through each of the matrices separately, then blend the results, or blend the matrices together then multiply the vertex through it. There are other ways of doing this blending that give nicer results, less 'candy wrapping' etc, and are worth looking at once you have your core steps up and running.

In Topic: Direct3D 11 question about loading .OBJ models

30 August 2012 - 11:03 PM

faces are polygons in the obj format, there can be any number of points listed. Standard approach is to split them like a triangle fan when converting to triangles.

In Topic: Q: Fast and simple way to store/manipulate pixels?

10 August 2012 - 01:25 AM

Hodgman: Thanks I think I get it. I'm unfamiliar with static_cast but it looks like you can read a section of memory as a different data type. I'm going to check it out.


Memory is just blocks of bytes, nothing more. You can read it however you want, but to prevent people from shooting themselves in the foot somewhat, C based languages are type-strict - you have to be explicit when you want to treat memory as different types.

In C++ there are 4 types of casts. static_cast, const_cast, dynamic_cast & reinterpret_cast.

static_cast is the same as doing a cast in C, like (Uint32*)Screen; It will take the pointer and give the pointer to the memory of a new type, assuming that type is 'castable' from the previous type.

For times where the types aren't castable (for example, casting Cheese* to Monkey*) you can call reinterpret_cast, which is like casting C see with a void* layer in between

dynamic_cast is the akin to the "as" cast in C#

const_cast will cast a const pointer to a non const pointer.

In Topic: Loading a specific part of the model?

05 August 2012 - 08:30 PM

Any reason you don't just load the entire model and grab the ModelMeshParts out of the Model and draw them as needed?

PARTNERS