Sign in to follow this  

[D3DX] Skinning a mesh with multiple subsets?

This topic is 3039 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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;
}

Share this post


Link to post
Share on other sites
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), [I guess this is the answer you're looking for] that's what creating an indexed mesh does.

If you create an indexed mesh with just one subset, you'll still to access the bone attrib table, but it'll just have an index of 0.

EDIT: Actually, it applies to just a blended mesh (indexed or not). Sorry about that.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Remember that an indexed vertex can be used in several different triangles. A triangle to one side of the vertex may be in a different subset (different color or texture) than a triangle on the other side using the same vertex. For "smooth" skinning, you want those triangles to appear continuous.

If the same indexed vertex is influenced by different sets of bones at the same time, then that same vertex would get drawn in two different positions. There would be a "break" in the mesh between the subsets.

I suppose it's possible to change the bone tables to change a vertex's influences for each subset. But (right now) I can't think of a good reason for the tables to be set up that way, other than it allows breaks in the mesh between subsets. A robot's head gets blown off?? You move that subset away from the body and then quit rendering that subset? Probably easier to do with separate models for the head and the body.

Share this post


Link to post
Share on other sites
Right, I didn't think it's useful either, but then once again, the bone combo table doesn't make sense. I don't see any purpose to it other than making life more difficult.

Returning to my above example, suppose that the vertex I mentioned has a bone index of 5 and it gets mapped to 7 in all combo tables. Maybe it's just me, but I think the natural question to ask here is:

Why wasn't the index 7 to begin with?!?!?

Share this post


Link to post
Share on other sites
Quote:
suppose that the vertex I mentioned has a bone index of 5 and it gets mapped to 7 in all combo tables

Uh.. it would only get mapped that way if you did it yourself. So, I give up. Why would you do that? [WINK]

Share this post


Link to post
Share on other sites
I'd expect that the main reason it's done that way is to minimize the number of bone matrix shader constants you need to set for each subset.

It also means that if the whole mesh has too many bones to fit in the available shader constants the mesh can be split into two or more subsets to enable you to draw it (assuming the exporter knows what that limit is). To test this try making a model with 86 or more bones - those won't fit in the standard 256 vertex shader constants, so to render it you'd want it split into at least two subsets.

Share this post


Link to post
Share on other sites
Quote:
Original post by Adam_42
I'd expect that the main reason it's done that way is to minimize the number of bone matrix shader constants you need to set for each subset.

It also means that if the whole mesh has too many bones to fit in the available shader constants the mesh can be split into two or more subsets to enable you to draw it (assuming the exporter knows what that limit is). To test this try making a model with 86 or more bones - those won't fit in the standard 256 vertex shader constants, so to render it you'd want it split into at least two subsets.


That makes perfect sense!

So suppose I don't care about all that. I can just preprocess my vertices and use the table to map the bone indices to the real bone indices, and then at runtime just forget about the table, right?

Also, if I have a lot of constant registers, doing this could actually hurt performance because it would increase the number of draw calls, right?

Thanks for the clarification!

Share this post


Link to post
Share on other sites
It's true that subsets can be chosen to ensure the number of bones per subset doesn't exceed the maximum.

I thought your question was about how the tables were setup to access the matrices. Glad you got satisfaction!

Quote:
doing this could actually hurt performance because it would increase the number of draw calls, right?

You still have to avoid exceeding the max matrices for the shader for each subset. If you have a lot of bones, then, yes, you'll have to create subsets to limit the number of matrices per draw call.

Share this post


Link to post
Share on other sites

This topic is 3039 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this