# Character Animation with Direct3D- P18

Chia sẻ: Cong Thanh | Ngày: | Loại File: PDF | Số trang:20

0
32
lượt xem
4

## Character Animation with Direct3D- P18

Mô tả tài liệu

Character Animation with Direct3D- P18:This book is primarily aimed at teaching indie and hobby game developers how to create character animation with Direct3D. Also, the seasoned professional game developer may find some interesting things in this book. You will need a solid understanding of the C++ programming language as well as general object-oriented programming skills.

Chủ đề:

Bình luận(0)

Lưu

## Nội dung Text: Character Animation with Direct3D- P18

1. 326 Character Animation with Direct3D hit position with a radius of the decal size). The problem with this approach, however, is quite obvious: not only will it be quite slow (especially for characters with tens of thousands of triangles), but it also has the potential to include lots of unnecessary triangles, as shown in Figure 14.6. FIGURE 14.6 The problem with brute force selection of the decal mesh. As you can see in Figure 14.6, the ray hits the front of the character’s leg. Since the decal size is larger than the leg itself, it will end up selecting polygons on the backside of the leg, which is something you’d like to avoid. This will happen as long as the decal size is larger than the thickness of the body part being tested (and note that the polygons don’t even have to be adjacent when selected with this method). So when a decal is added to the front of the character’s torso, there might be some polygons added on the back as well. So, even though the brute force method would work, let’s try a better one. It is quite easy to calculate the adjacency information of a mesh (information of which triangles share which edges). The adjacency information of a mesh can be calculated using the following D3DX library function defined in the ID3DXBaseMesh class: Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
2. Chapter 14 Character Decals 327 HRESULT GenerateAdjacency( FLOAT Epsilon, DWORD * pAdjacency ); This function returns a list of indices containing the information of which faces are adjacent to each other. Any vertices closer to each other than the Epsilon variable will be treated as coincident. The resulting adjacency data will be written to the pAdjacency pointer. An example mesh with its calculated adjacency information is shown in Figure 14.7. FIGURE 14.7 Four example triangles. In Figure 14.7 there’s an example mesh consisting of four triangles (numbered 1 to 4). The adjacency information consists of double words (DWORD). If a certain edge of a triangle doesn’t have a neighboring triangle, this slot will be filled with the value 0xffffffff. The neighbors can then be extracted in code like this: ease purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
4. Chapter 14 Character Decals 329 A more detailed hit position (in the un-skinned mesh local space) can be calcu- lated using the UV barycentric coordinates given to you from the D3DXIntersect() function. Consider the triangle in Figure 14.8. FIGURE 14.8 An example triangle with corners A, B, and C. Given any arbitrary barycentric coordinates (u and v), any point (p) on this triangle (A, B, C) can be described as: p = A + u(B A)+ v(C A) Try, in your head, to calculate the barycentric coordinate of a few points on the triangle in Figure 14.8. After just trying a few points you’ll realize the truth that any given point on a triangle can be described using only two barycentric coordinates, regardless of the shape of the triangle. The simple formula above is also implemented by the following D3DX library function: D3DXVECTOR3 * D3DXVec3BaryCentric( D3DXVECTOR3 * pOut, //Resulting point on triangle CONST D3DXVECTOR3 * pV1, //Corner 1 (A) CONST D3DXVECTOR3 * pV2, //Corner 2 (B) CONST D3DXVECTOR3 * pV3, //Corner 3 (C) FLOAT f, //Barycentric U coordinate FLOAT g //Barycentric V coordinate ); ease purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
5. 330 Character Animation with Direct3D With the D3DXINTERSECTINFO object returned from the GetFace() function created in the previous example, you can easily extract the vertices of the triangle hit by the ray. Then, feed them into the D3DXVec3BaryCentric() function together with the barycentric coordinates of the hit. Out comes the exact hit position of the ray in the un-skinned character’s local space (which is the space in which all the vertex positions are stored). SELECTING TRIANGLES FOR THE DECAL MESH Before I cover the code on how to create the decal mesh there is one more issue to discuss. When you create the decal mesh you must ensure that all affected triangles are added to the decal mesh. The idea is to do a flood-fill of triangles out from the triangle that was hit by the ray and stop once you’ve selected a large enough submesh to “house” the decal. With each triangle you test you can extract the three corners (i.e., mesh vertices) and test against the decal testing volume. However, this brings us to the following problem described in Figure 14.9. FIGURE 14.9 A simplified sphere-triangle test. Here, the problem becomes apparent if you only test the vertices of the trian- gle against the bounding sphere. As you can see, the bounding sphere intersects the triangle but misses all of the triangle vertices. This will lead to the triangle not being included in the decal mesh, which in turn can give your decal a visual artifact Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
6. Chapter 14 Character Decals 331 (making it look like a piece of the decal is missing). Again, there are many different ways to handle this. One easy way to solve this would be to calculate the bounding sphere of the triangle itself and use a sphere–sphere intersection test to determine whether the triangle should be included. Alas, this approach often results in too many triangles being selected, with large triangles having a much larger chance of being selected. So the simple solution is just to increase the testing radius of the decal bounding sphere. This will, of course, have to be increased with a magical number differing from character to character. In most cases a simple 10%–20% increase should do it. Again, there are more accurate ways to go about this, but in the end, we’re not calculating the trajectories of a ballistic missile here. Feel free to spend time on perfecting the solution to this problem though. COPYING THE SKINNING INFORMATION Finally, you’ve come to the point where you need to create the actual decal mesh using the subset of triangles selected using the techniques covered in the previous sections. However, I’ve been talking a lot about the original mesh of the character and how you need to select the triangles to use for the decal mesh from this origi- nal mesh. Now that the time has come to actually copy the selected submesh and create the new decal mesh, there is one more important thing to consider—namely, that of the skinning information. Since one of the basic problems you were faced with when wanting to create decals for characters was that they are dynamic and animated meshes, you need to make sure that your decals are also dynamic. The decal needs to map exactly to the underlying character and follow the character’s every move. For this to happen, the decal needs to also copy the skin information of the skinned character (for the affected triangles only). I must admit that the first time I approached this problem I took the long way around. I created a new ID3DXSkinInfo object and manually copied over the skin- ning data from the skin info object stored in the BoneMesh class to the new skin info object of the decal mesh. Although this worked (and was a great exercise), it was also completely unnecessary. However, if you’re ever faced with the prospect of copying skinning information from one character to another, here’s the basic outline on how to proceed: 1. Create a new ID3DXSkinInfo object using the D3DXCreateSkinInfo() or D3DX- CreateSkinInfoFVF() function. 2. Use the ID3DXSkinInfo::GetBoneInfluence() function to retrieve the vertices and weights from the source character. 3. Map bone influences to actual vertices (these are not the same) using the ID3DXSkinInfo::GetBoneVertexInfluence() function. 4. Finally, write the newly created vertices and weights to the target skin info ob- ject using the ID3DXSkinInfo::SetBoneInfluence() function. ease purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
8. Chapter 14 Character Decals 333 Here now follows the giant code snippet that has been explained in this and the previous sections. The upcoming CreateDecalMesh() function has been added to the BoneMesh class to calculate a skinned decal mesh taking a ray origin, ray direction, and decal size as parameters: ID3DXMesh* BoneMesh::CreateDecalMesh( D3DXVECTOR3 &rayOrg, D3DXVECTOR3 &rayDir, float decalSize) { //Only supports skinned meshes for now if(pSkinInfo == NULL) return NULL; D3DXINTERSECTINFO hitInfo = GetFace(rayOrg, rayDir); //No face was hit if(hitInfo.FaceIndex == 0xffffffff) return NULL; //Generate adjacency lookup table DWORD* adj = new DWORD[OriginalMesh->GetNumFaces() * 3]; OriginalMesh->GenerateAdjacency(0.001f, adj); //Get vertex and index buffer of temp mesh Vertex *v = NULL; WORD *i = NULL; OriginalMesh->LockVertexBuffer(D3DLOCK_READONLY, (VOID**)&v); OriginalMesh->LockIndexBuffer(D3DLOCK_READONLY, (VOID**)&i); //Calculate hit position on original mesh WORD i1 = i[hitInfo.FaceIndex * 3 + 0]; WORD i2 = i[hitInfo.FaceIndex * 3 + 1]; WORD i3 = i[hitInfo.FaceIndex * 3 + 2]; D3DXVECTOR3 hitPos; D3DXVec3BaryCentric(&hitPos, &v[i1].position, &v[i2].position, &v[i3].position, hitInfo.U, hitInfo.V); ease purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
9. 334 Character Animation with Direct3D //Find adjacent faces within range of hit location queue openFaces; map decalFaces; //Add first face openFaces.push((WORD)hitInfo.FaceIndex); while(!openFaces.empty()) { //Get first face WORD face = openFaces.front(); openFaces.pop(); //Get triangle data for open face WORD i1 = i[face * 3 + 0]; WORD i2 = i[face * 3 + 1]; WORD i3 = i[face * 3 + 2]; D3DXVECTOR3 &v1 = v[i1].position; D3DXVECTOR3 &v2 = v[i2].position; D3DXVECTOR3 &v3 = v[i3].position; float testSize = max(decalSize, 0.1f); //Should this face be added? if(D3DXVec3Length(&(hitPos - v1)) < testSize || D3DXVec3Length(&(hitPos - v2)) < testSize || D3DXVec3Length(&(hitPos - v3)) < testSize || decalFaces.empty()) { decalFaces[face] = true; //Add adjacent faces to open queue for(int a=0; a
10. Chapter 14 Character Decals 335 } } } } OriginalMesh->UnlockIndexBuffer(); OriginalMesh->UnlockVertexBuffer(); //Create decal mesh ID3DXMesh* decalMesh = NULL; //No faces to create decal with if(decalFaces.empty()) return NULL; //Create a new mesh from selected faces D3DVERTEXELEMENT9 decl[MAX_FVF_DECL_SIZE]; MeshData.pMesh->GetDeclaration(decl); D3DXCreateMesh((int)decalFaces.size(), (int)decalFaces.size() * 3, D3DXMESH_MANAGED, decl, g_pDevice, &decalMesh); //Lock dest & src buffers DecalVertex* vDest = NULL; WORD* iDest = NULL; DecalVertex* vSrc = NULL; WORD* iSrc = NULL; decalMesh->LockVertexBuffer(0, (VOID**)&vDest); decalMesh->LockIndexBuffer(0, (VOID**)&iDest); MeshData.pMesh->LockVertexBuffer(D3DLOCK_READONLY, (VOID**)&vSrc); MeshData.pMesh->LockIndexBuffer(D3DLOCK_READONLY, (VOID**)&iSrc); //Iterate through all faces in the decalFaces map map::iterator f; int index = 0; for(f=decalFaces.begin(); f!=decalFaces.end(); f++) { WORD faceIndex = (*f).first; ease purchase PDF Split-Merge on www.verypdf.com to remove this watermark.