[DX9] Implementation of Meshcontainers and Frames

Started by
15 comments, last by Tispe 12 years, 4 months ago
hmmm.

Lets say a hand-mesh in centered in hand-space at the origin xyz=(0, 0, 0) and the hand-bone is at xyz=(50,100,20) in model-space. Each vertex is then multiplied by the Offset Matrix to align the hand-mesh to maybe xyz=(5,6,8), but it is not transformed to any specefic bone location yet. Now, why are there no intermediate transform between doing the Offset transform and vertex blending? I would think that if we went straight away to vertex blending after Offset the hand would only move its fingers at location xyz=(5,6,8). Or does the transform to xyz=(50,100,20) occur simultaneously when applying the four weighted matrices to deform the hand-mesh?
Advertisement

hmmm.

Lets say a hand-mesh in centered in hand-space at the origin xyz=(0, 0, 0) and the hand-bone is at xyz=(50,100,20) in model-space. Each vertex is then multiplied by the Offset Matrix to align the hand-mesh to maybe xyz=(5,6,8), but it is not transformed to any specefic bone location yet. Now, why are there no intermediate transform between doing the Offset transform and vertex blending? I would think that if we went straight away to vertex blending after Offset the hand would only move its fingers at location xyz=(5,6,8). Or does the transform to xyz=(50,100,20) occur simultaneously when applying the four weighted matrices to deform the hand-mesh?



The time to consider the bone offset matrix is when your C++ program is setting up the "workingPalette" before sending it into the GPU as a 4x4 matrix array for the vertex shader to use during the skinning step.

The MultiAnimation sample calls an array of transform matrices used by a particular skin partition a "workingPalette". D3DX calls skin partitions ID3DXSkinInfo. A skin partition contains the minimum set of bones that are accessed by any vertex in this skin's mesh during the skinning step.

Typically if you want to have a very customisable player model, you define several different skin partitions (head, arms, body, legs, boots, etc) and attach them all to the root bone. The working palette for each skin partition gives a list of the bones that influence the vertices of this partition.

Right before you render a skin partition, you have a working palette of 4x4 matrices numbered from 0 .. W-1 and a complete frame hierarchy of bone matrices which you can flatten into an array of 0 .. B-1 bones. Note that W need not equal B but W <= B. Each vertex of the skin has a bone index in the 0 .. W-1 space (with a corresponding weight).

Now you fill in each matrix of the working palette by multiplying the bone offset transform[ 0 .. W-1 ] with the bone transform[ 0 .. W-1 ]. You must first map the range 0 .. W-1 back to 0 .. B-1 because the bone offset transform and the bone transforms are actually stored in the complete list of skeleton bones, the working palette for a skin partition just has the product of them.

Now upload the working palette to the shader and you can access the working palette using the index found in each vertices array of blend indices. Let me again state the blend index from a vertex is not necessarily the bone index in the master skeleton but the working palette index.

For a simple skinned mesh you might just have one single skin partition and would therefore have a 1:1 mapping between the working palette and the master skeleton list.

For a more complicated character, let's imagine he has an astounding 120 bones in his main skeleton. He has 5 skin partitions (head, body, arms, legs, gloves, boots). The head skin partition has a working palette with 10 of the 120 bones. The body skin partition has a working palette with 35 of the 120 bones (maybe some of the same as the head even), etc, etc. The sum of the sizes of the working palettes can be larger than the total number of bones in the character.

Breaking a character up into skin partitions like this helps because there is a limit on how many constants registers you can upload to a vertex shader and each matrix takes 4 float4 vectors (you can be tricky and store a transform as a quaternion and a translation and get down to half the size). So to draw any piece of the character above, the most you would have to upload is 35 4x4 matrices rather than 120 (assuming the body skin partition was the largest).

Typically if you want to have a very customisable player model, you define several different skin partitions (head, arms, body, legs, boots, etc) and attach them all to the root bone.


Thank you for reading but I don't think you awnsered my question. What I am confused about is this:
Given a "boots" mesh container, this mesh is passed unaltered to the shader and each vertex is only transformed ONCE by the workingPalette right (4 bone weights etc)?

When you say "attach them all to the root bone", does this mean the root Frame D3DXFRAME:pMeshContainer and its D3DXFRAME:pNextMeshContainer are chained for all these partitions, all other frames have D3DXFRAME:pMeshContainer = NULL?

I don't see in the code how a partition is attached mathematically to any frame.

What I think happens:
Each mesh partition is seperate in their own meshcontainers. And each partition is in ther own space centered at the origin xyz=(0, 0, 0).
If all meshcontainers where to be drawn without beeing transformed, they would all appear ontop of eachother at the origin.
Each partition is therefore moved to their correct position, this is where the "attached" to a frame comes to my mind. If a meshcontainer is attached to a frame using D3DXFRAME:pMeshContainer then HOW is that meshcontainer transformed to the correct frame in model space?

What determines if the "arm mesh" is positioned at the arm bone? All I see is the workingPallete influencing the "arm mesh" vertices locally to deform the skin, not move it around in model space.

If you could try to explain in the following order:
1. What position is a vertex at when it is loaded from file
2. What position has a vertex as it sits in the MeshContainer
3. Is the Vertex position still the same when the MeshContainer is attached to a frame (loading process)
4. Is vertex still around the origin (as the mesh is modeled in its own space) when it is passed to the vertex shader?
5. Does the four influencing weighted combinedTransformMatrices*OffsetMatrices moving the Vertex from the "origin" position to its correct position on the model?
A partition would be attached mathematically to a frame if you render the meshcontainer while recursing the frames.

1. Usually specified relative to (0,0,0)
2. Same as in file
3. Yes still the same vertex position
4. Yes
5. Yes

Since you like DirectX '.X' files have a look here: ModelViewer.zip

It's a zipped file (50 MB dowload) that has a library of X files and textures converted from the characters and monsters of WildTangent's FATE game. I do not own the rights to the artwork or models, but I have converted the information from the MDL/SMA/SMS file formats natively used by the game to the X file format.

Included is a small model viewer app - the binary for it is in bin\ModelViewer.exe. If you associate files of type 'X' with the application than you can double-click to examine each character and monster file in the library under "bin\FateCombined"

The controls for the model viewer are:

'1' - Cycle to next animation
'W' - Wireframe
'S' - Solid
'V' - Show bones
'X' - Toggle skin drawing on/off
'SPACE' Single step through keyframes
'ENTER' Stop single step

Additionally you can open any of the X fies using notepad.exe and view them as text files. They are well formatted with regard to whitespace (unlike Tiny.x) and examining the contents of them should give you a better idea of how skinned meshes work.
Thanks

When you say "attach them all to the root bone", does this mean the root Frame D3DXFRAME:pMeshContainer and its D3DXFRAME:pNextMeshContainer are chained for all these partitions, all other frames have D3DXFRAME:pMeshContainer = NULL?

Also: is the MDL/SMA/SMS file formats open? If not what mothod did you use to figure out file layout?

Thanks

When you say "attach them all to the root bone", does this mean the root Frame D3DXFRAME:pMeshContainer and its D3DXFRAME:pNextMeshContainer are chained for all these partitions, all other frames have D3DXFRAME:pMeshContainer = NULL?

Also: is the MDL/SMA/SMS file formats open? If not what mothod did you use to figure out file layout?



Yeah that's usually how people do it, a linked list of skin partitions all attached under the root bone. But your mileage might vary.

I used a hex editor and Luna's paper to figure out the formats. There are lots of identity matrices in frame hierarchies so once you know what an IEEE float looks like in hex, you can easily identify the identity matrix in the data file. From that point on you just play sudoko with the file until you know what all the bytes mean.

You can do it to any file, like I did it for Mortal Kombat SSF files, Zelda BMD files, DAOC/Oblivion NIF files, etc. Somebody had already published the format of the NIF file, so I didn't have to do too much for that one.

And the funny thing was after examining all those games I realized that they were all pretty much doing skinned meshes the same. They used skinning to get meshes to deform as the skeleton animated and they used attachment to get a mesh to follow one particular bone (usually rigidly, but some of the games attached one skinned mesh to the bone of another one - like an animated character holding an animated whip in their hand and cracking it).

Examining the large number of assets these games have also convinced me of the drawbacks of the D3DX animation system, though it is sufficient for example and learning purposes, which is likely what it was intended for.
Thanks for that information.

What paper are you refering to?

Once I figure out the file format for the game I wish to borrow assets from, it is just a matter of writing my own D3DXLoadMeshHierarchyFromX() function specefic for that file format. On that topic, what else behind the scenes like populating the pFirstSibling, pFirstChild, pMeshContainer and adding animation sets to the AnimationController would I need to do in such a function?

This topic is closed to new replies.

Advertisement