[C++, DX9] GPU skinning problem (VS messes up vertexes)

Started by
29 comments, last by Steve_Segreto 13 years, 9 months ago
Honestly it wasn't right even with "tiny.x", she was oddly rotated to start with. Your view matrix isn't a good one - just start with an identity matrix for the character's world matrix and get the view matrix positioned correctly.

With an identity world matrix, the character will be drawn wherever their scene root matrix dictates, so they may be translated off screen unless you "look-at" one of their bones using the view matrix.

You can use this to get the largest sphere that covers all the vertices in the model before it animates - and also return the center of the model (again before it animates).

//
// Get bounding radius for entire hierarchy
//
hr = D3DXFrameCalculateBoundingSphere( m_pFrameRoot, & m_vObjectCenter, & m_fObjectRadius );

The models root matrix also dictates the "up-vector" for your look-at matrix. For instance, for tiny.x it was { 0, 0, 1 } but it looked like { 0, 1, 0 } for your body.x file.

If you just use the TransformationMatrix of a particular bone after using D3DXFrameFind, then you get the "live" animated position of the bone, which is good for keeping your camera locked on a character's head (or any other bone).

Tiny.x is actually anything but tiny ;) She is like 1000.0f floats tall ;)

Your other model is quite a bit smaller.
Advertisement
Well since rotation and position (probably scaling too) stacks each frame, how should I make my world matrix? All I think think of, is to calculate difference between last frame and current, to prevent stacking, but I'm quite sure there is better way.
You'll need to give some thought on what type of camera you want and how you want to view your model.

If you just want to view it for the sake of viewing it, then an identity world matrix is fine and a quat-ball view matrix is appropriate.

If you want a 3rd person camera to follow the character as they walk around a level (a flat plane for instance - like the multianimation sample), then the player's world matrix only rotates around the up-vector and translates along the other two vectors. Google 3rd-person cameras for insight on how to build your view matrix.

If you want a first person camera, then google how to create such a camera as well.

If you want a side-scrolling or isometric top-down camera - again google is your friend.

Also decide if your players can fly and/or swim and jump because all of these affect the possible rotations and translations of your model's world matrix.
One thing I still don't understand. Why with tiny_4anim.x world matrix doesn't add/stack, but with mine body.x it does?
Dunno - maybe her root bone animation resets near the end of an animation cycle and yours just leaves the root bone rotated differently at the end of your model's animation cycle.

BTW doesn't cost much to reset all your bone matrices to bind before calling advanceTime on the animation controller.

This X-file requires it in fact:

http://dl.dropbox.com/u/8974528/Briton_Bandit_1.x

It also shows a good example of properly formatting an X file...
I've solved speed and other problems for now. But I came up with old question: how to add overlays? I have this body.x and shirt.x models. body.x has single mesh, and animated (nearly identical to one in earlier posts), while shirt.x is only mesh, without bones or animations. How would I connect it to my animated model, so it gets animated together?

Thank you in advance.



Sorry if I'm asking too much, but I'm not very smart. If you could, please show code how it should be done, it'll make more sense to me than telling what to set or what functions to use.

Thanks again.
Its hard to give you a specific answer for overlays because your code is missing some important convenience pieces.

Here's one (non-optimal) thing you could do just to get started:

1. In your modelling program, create a dummy bone in the overlay file and name this dummy bone the same as one of your actual skeleton bones. For the shirt, you could create a dummy bone called "Bip01" if this is the middle bone of your skeleton.

2. In your modelling program, place the vertices of the overlay mesh under the dummy bone, so the mesh gets exported as a mesh container of that bone.

3. In your C++ program, you need to enhance AllocHierarchy::CreateMeshContainer with the following type of code (adapt for your needs):

//// If this is a static mesh, mock up a skinned mesh from it.//if( pSkinInfo == NULL ){    wasStatic = true;    //    // Mock up a skin covering all vertices    //    D3DVERTEXELEMENT9 pDecl[ MAX_FVF_DECL_SIZE ];    hr = pMeshData->pMesh->GetDeclaration( pDecl ); // 2 declarations: type 2 (float3), 1 (float2); usage 0 (position), 5 (texture coord)    if (hr != S_OK) goto e_Exit;    hr = D3DXCreateSkinInfo (pMeshData->pMesh->GetNumVertices(),			     pDecl,			     1, // 1 bone			     &pSkinInfo);    if (hr != S_OK) goto e_Exit;    //    // Attach all the vertices to this single bone with 100% weight.    //    DWORD *verts = new DWORD[ pMeshData->pMesh->GetNumVertices() ];    for (unsigned int i = 0; i < pMeshData->pMesh->GetNumVertices(); i++)	verts = i;<br>    <span class="cpp-keyword">FLOAT</span> *weights = <span class="cpp-keyword">new</span> <span class="cpp-keyword">FLOAT</span>[ pMeshData-&gt;pMesh-&gt;GetNumVertices() ];<br>    <span class="cpp-keyword">for</span> (<span class="cpp-keyword">unsigned</span> <span class="cpp-keyword">int</span> i = <span class="cpp-number">0</span>; i &lt; pMeshData-&gt;pMesh-&gt;GetNumVertices(); i++)<br> 	weights = <span class="cpp-number">1</span>.0f;<br>    hr = pSkinInfo-&gt;SetBoneInfluence( <span class="cpp-number">0</span>,<br>	                              <span class="cpp-number">1</span>,<br> 				      verts,<br>        		  	      weights );<br>    <span class="cpp-keyword">if</span> (hr != S_OK) <span class="cpp-keyword">goto</span> e_Exit;<br>    <span class="cpp-keyword">delete</span> [] verts;<br>    <span class="cpp-keyword">delete</span> [] weights;<br>    pSkinInfo-&gt;SetBoneName( <span class="cpp-number">0</span>, g_lastFrameName );<br>    D3DXMATRIX boneTransform;<br>    D3DXMatrixIdentity( &amp;boneTransform );<br>    pSkinInfo-&gt;SetBoneOffsetMatrix( <span class="cpp-number">0</span>, &amp;boneTransform );<br>}<br><br><br><br></pre></div><!–ENDSCRIPT–><br><br>This creates a dummy "skin partition" for the overlay mesh, that uses &#111;ne single bone and attaches all the vertices of the overlay mesh to that bone with 100% weight. Which bone does it attach the overlay too? g_lastFrameName, which is a global variable you will need to create and track. Do something like this in your AllocHierarchy::CreateFrame() routine:<br><br><!–STARTSCRIPT–><!–source lang="cpp"–><div class="source"><pre><br><span class="cpp-keyword">char</span> *g_lastFrameName = <span class="cpp-number">0</span>;<br>HRESULT CXFileAdapter::CreateFrame( THIS_ LPCSTR Name, LPD3DXFRAME * ppFrame )<br>{<br>    <span class="cpp-keyword">static</span> <span class="cpp-keyword">unsigned</span> <span class="cpp-keyword">long</span> frameCnt = <span class="cpp-number">0</span>;<br>    assert( m_pSceneGraph );<br><br>    * ppFrame = NULL;<br><br>    SceneGraphFrame * pFrame = <span class="cpp-keyword">new</span> SceneGraphFrame;<br>    <span class="cpp-keyword">if</span>( pFrame == NULL )<br>    {<br>        DestroyFrame( pFrame );<br>        <span class="cpp-keyword">return</span> E_OUTOFMEMORY;<br>    }<br><br>    ZeroMemory( pFrame, <span class="cpp-keyword">sizeof</span>( SceneGraphFrame ) );<br><br>    <span class="cpp-keyword">if</span>( Name )<br>        pFrame-&gt;Name = (<span class="cpp-keyword">CHAR</span> *) HeapCopy( (<span class="cpp-keyword">CHAR</span> *) Name );<br>    <span class="cpp-keyword">else</span><br>    {<br>        <span class="cpp-comment">// Add a counter to append to the string below</span><br>        <span class="cpp-comment">// so that we are using a different name for</span><br>        <span class="cpp-comment">// each bone.</span><br>	<span class="cpp-keyword">char</span> s[ <span class="cpp-number">256</span> ];<br>	sprintf (s, <span class="cpp-literal">"&lt;no_name%ld&gt;"</span>, frameCnt++);<br>        pFrame-&gt;Name = (<span class="cpp-keyword">CHAR</span> *) HeapCopy( s );<br>    }<br><br>    <span class="cpp-keyword">if</span>( pFrame-&gt;Name == NULL )<br>    {<br>        DestroyFrame( pFrame );<br>        <span class="cpp-keyword">return</span> E_OUTOFMEMORY;<br>    }<br><br>    * ppFrame = pFrame;<br>    g_lastFrameName = pFrame-&gt;Name;<br><br>    <span class="cpp-comment">//</span><br>    <span class="cpp-comment">// Insert into a flat list</span><br>    <span class="cpp-comment">//</span><br>    m_pSceneGraph-&gt;m_boneList.push_back( (SceneGraphFrame *)* ppFrame );<br><br>    <span class="cpp-keyword">return</span> S_OK;<br>}<br><br><br><br></pre></div><!–ENDSCRIPT–><br><br>4. You also may need to handle the -&gt;Next field in your DrawMeshContainer and CreateMeshContainer routines in case you end up adding two or more meshes to the same frame.<br><br>5. Render the mesh as normal - the overlay should render using the same vertex shader as the skinned mesh and at the same position as the bone its attached to. You may need to include an additional frame to orient the overlay properly. Some games will have various frames for a weapon mesh that are called "Held", "OnGround", "OnBelt", "OnBack" that provide the correct orientation for that particular weapon in each of those situations.<br><br>If this mesh is truly an overlay in the sense that it occupies the same approximate z-depth as the body mesh, you may need to change the Z-fail condition to LessEqual.<br><br>Sorry that's as much code as can be provided - you will need to figure out the rest for yourself with a particular static mesh.
Looking at this I wonder about 2 things:

1. Do all game developers do this in all games?
2. Would I gain any real FPS using this way to render shirt, rather than simply rendering extra skinned model?
I sincerely doubt other developers even use D3DX's ID3DXAnimationController for animated models. I also doubt they create all their static models as skinned models with 1 bone and all vertices attached to that one bone at 100% weight.

You don't really have any subsystems in place to allow you other choices - so this would be my best recommendation if you just wanted to immediately get this working.

Professional games do use the different orientation frames like "Held", "OnBack", "OnBelt", "OnGround" to orient their weapons and items, I've observed that for a fact after reverse engineering the art resources from about 6 games.

Professional games also tend to use different body meshes to represent player with shirt, player with armor, player with bulky armor, etc - rather than "overlays" (as you call them).

What about second question? =p

Quote:Original post by Ripiz
2. Would I gain any real FPS using this way to render shirt, rather than simply rendering extra skinned model?

This topic is closed to new replies.

Advertisement