[D3DX] Skinning a mesh with multiple subsets?

Started by
14 comments, last by Buckeye 14 years, 8 months ago
I have an x file of a dwarf holding an axe. The dwarf and the axe are in different subsets (and those are the only two subsets). I also have a demo that does skinning (though it only plays the first animation in the file). It uses a vertex shader to do the skinning. When I run the demo with this model, the dwarf is running and swinging his hands, but the axe does not follow his hand. It does move back and forth a little, and the movement looks like it's in sync with the dwarf, but still doesn't look right at all. I got this model from a game that came with full source code. In that game the animation looks fine, but it uses ID3DXSkinInfo::UpdateSkinnedMesh() to do the skinning. Now, I know that there could be a lot of things that are causing this, and I haven't provided a lot of information (like code or a screenshot), but first I would like to ask just this: is there any reason why code that works correctly with single-subset meshes shouldn't work correctly with multi-subset meshes? Thanks in advance.
Advertisement
Quote:but it uses ID3DXSkinInfo::UpdateSkinnedMesh() to do the skinning

Why do you say "but.."? With respect to your multi-subset question, that's immaterial. That moves the bones. Subset information is used for changing textures, etc. Is the sword actually a subset, or is it a separate mesh? If it's a separate mesh, it needs an animation, also.
Quote:why code that works correctly with single-subset meshes shouldn't work correctly with multi-subset meshes?

The obvious answer is "yes," if the code only renders subset(0), for example. Assuming that you know that, maybe your should post your mesh rendering loop.

It should look something like:
mesh->MoveBones();for(int attrib=0; attrib < mesh->numAttribs; attrib++) {   // whatever function you use to set the attributes, etc.   DrawSubset(attrib, mesh,mesh->attribs[attrib], etc.);}


EDIT: if the sword is textured properly, it's lack of movement probably isn't a subset problem unless it has a different shader, etc.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

My code draws all the subsets. When I draw only subset 0, the dwarf is drawn, but the axe is not.

My code looks like this:

mesh->MoveBones();for(int attrib=0; attrib < mesh->numAttribs; attrib++) {   mesh->DrawSubset(attrib);}

That is, all my DrawSubset() calls are done in a row. Does that matter?

Also, any chance this might have something to do with the bone combination table? (returned by ConvertToIndexedBlendedMesh()). Basically, for each subset, it gives me an array with the indices of the bones used by vertices in that subset. But I don't understand how that information can be useful for anything.

Quote:all my DrawSubset() calls are done in a row. Does that matter?

It shouldn't.
Quote:it gives me an array with the indices of the bones used by vertices in that subset

That's what's used to access the offset matrices for the bones affecting the subset[ATTENTION]
Take a look at the SkinnedMesh example in the SDK.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Thanks, looks like that's it. Looking at the SDK sample, there's some code in CAnimInstance::DrawMeshFrame() that I guess I should add.

But why is this needed? Why do vertices in different subsets need to access the matrix palette in different ways? I thought the only thing that separates vertices in different subsets is that they use different textures or material properties. What am I missing?

BTW, do I also need to do this for meshes with just one subset?
Is the sword a different mesh than the dwarf? And which D3DXFRAME are the vertices of the sword under? Is that frame directly animated by any of the keyframe animations in the X file? If not, maybe the frame is "attached" to another D3DXFRAME that is directly animated?

For example, there might be a L_HAND or R_HAND frame that is directly animated by the file's keyframes. The sword might actually be under an Editable_Mesh frame that isn't directly connected to either L_HAND or R_HAND. In some games, I've actually seen the following kind of frame hierarchy for items:

Scene_Root
Item_on_Ground transformation matrix frame
Item_on_Back transformation matrix frame
Item_held transformation matrix frame
Item_on_Belt transformation matrix frame
Editable_Mesh transformation matrix frame
Item Mesh Frame

In this case, the complete transform to put a held version of the item in object space requires selecting three transform matrices, like so: Scene_Root * Item_Held * Editable_Mesh. This would orient the axe for being held, but doesn't yet attach it or animate it. Without you selecting the correct transform sequence, DirectX would likely select Scene_Root * Editable_Mesh (in the example above).

Next you would *ATTACH* the Scene_Root frame of the Axe onto the Dwarf's R_HAND frame so that the axe gets animated correctly as the R_HAND bone moves.

To attach one D3DXFRAME to another here is an example from my own C++ project (I actually overloaded the D3DXMESHCONTAINER/D3DXFRAME classes to have some additional members which I think are showing here). My item models didn't come with any SkinWeights, so I had to create a single fake bone and attach all their vertices to it with 100% weight (that's not shown here):

//-----------------------------------------------------------------------------
// Name: CActor::AttachModel()
// Desc: Finds the frame with the given name and adds the provided one
// to it's mesh container list. Used for attaching weapons, shields
// and other equipment. "frameName" is the name of the frame in the
// base model where "objMesh" should be attached. "objRoot" is the root frame
// of the object model.
//
// GetRootFrame() is an internal method that retrieves the root frame of
// the base model.
//
// CreateMeshContainer() allocates/creates a mesh container with the specified
// mesh data, adjacency and materials.
//
// The D3DXMESHCONTAINER structure is augmented per Microsoft's Multi-Animation
// sample to have some bone matrices and a SetupBonePtrs() method. Refer to
// their sample for more information.
//-----------------------------------------------------------------------------
bool CActor::AttachModel( char *frameName,
D3DXMESHCONTAINER *objMesh,
D3DXFRAME *objRoot )
{
if (!mesh) return false;

HRESULT hr = S_OK;

//
// Find the frame in the base model to attach this
// object to.
//
D3DXFRAME *pFrame = D3DXFrameFind( GetRootFrame(), frameName );
if( pFrame != NULL )
{
//
// Create a new mesh container to hold this object
// and attach it either as this frame's pMeshContainer
// or if there already is one there, as
// pMeshContainer->pNextMeshContainer.
//
D3DXMESHCONTAINER *newObjMesh = NULL;

CreateMeshContainer( objMesh->Name, &objMesh->MeshData,
objMesh->pMaterials, objMesh->pEffects,
objMesh->NumMaterials, objMesh->pAdjacency,
objMesh->pSkinInfo, &newObjMesh );

D3DXMESHCONTAINER *pMC = pFrame->pMeshContainer;
while (pMC)
pMC = pMC->pNextMeshContainer;
pMC = newObjMesh;

//
// Fix-up bone pointers and set transformation matrices
//
newObjMesh->SetupBonePtrs( objRoot );

D3DXMATRIX mx;
D3DXFRAME *pHeld = D3DXFrameFind( objRoot, "HELD" );
D3DXFRAME *pMesh = D3DXFrameFind( objRoot, "Editable_Mesh" );
if (pHeld && pMesh)
{
mx = pHeld->TransformationMatrix * pMesh->TransformationMatrix;
}
newObjMesh->m_amxBoneOffsets[ 0 ] = mx;
newObjMesh->m_apmxBonePointers[ 0 ] = &pFrame->TransformationMatrix;

return true;
e_Exit:
return false;
}
Quote:I thought the only thing that separates vertices in different subsets is that they use different textures or material properties.


When you're rendering the subset (i.e., drawing skinned vertices), you have to generate a matrix for the vertices based on the bones for those vertices. [You already know that, I know] Because the vertices are indexed, the same vertex may be used in more than one subset. The bone indices table in question determines which bones affect the vertices in that subset. While it's true that you could use one table (as for a single subset), that's what creating an indexed mesh does.<br><br>If you create an indexed mesh with just &#111;ne subset, you'll still to access the bone attrib table, but it'll just have an index of 0.<br><br>EDIT: Actually, it applies to just a blended mesh (indexed or not). Sorry about that.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Quote:Original post by Buckeye
Because the vertices are indexed, the same vertex may be used in more than one subset.


Hmm, I didn't think a vertex can be in more than one subset. So what you're saying is that the bones indexed by a vertex in one subset are not necessarily the same bones indexed by the same vertex in another subset.

Given that, the bone combination table makes sense, but I don't understand why this is useful. Why would you want to have a vertex in two different subsets, indexing two different sets of bones?

BTW, I changed my demo to use the bone combination table and now the animation looks correct. The axe is following the hand like it should. So, that was indeed the problem.
I didn't say that a vertex could be influenced by different bones in different subsets. Actually, I think that's impossible.

But an indexed vertex can certainly be in different subsets.

As I said, the real reason why you have to use bone combo tables is because that's how the software is implemented! [SMILE]

Glad you've got the model working! Congrats.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Quote:Original post by Buckeye
I didn't say that a vertex could be influenced by different bones in different subsets. Actually, I think that's impossible.


I thought this is exactly what the combo table allows. For example, suppose you have a mesh with one bone influence (so one bone index per vertex and no weights), and suppose you have a vertex in two different subsets, and its bone index is 5. If the bone combo table for the first subset maps that index to 7, and the bone combo table for the second subset maps that index to 10, then that vertex indexes two different bones in two different subsets.

Or did I misunderstand how the bone combo table is used?

Quote:As I said, the real reason why you have to use bone combo tables is because that's how the software is implemented! [smile]


Yes, I understand that. What I don't understand is why is it implemented like that?

I realize that only the people who wrote the software know for sure, but I was hoping you (or someone else) could make an educated guess.

Quote:Glad you've got the model working! Congrats.


Thanks, and thanks again for pointing me in the right direction.

This topic is closed to new replies.

Advertisement