Character Animation with Direct3D- P4 ppt

20 349 0
Character Animation with Direct3D- P4 ppt

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

void SkinnedMesh::Load(char fileName[]) { BoneHierarchyLoader boneHierarchy; //Load a bone hierarchy from a file D3DXLoadMeshHierarchyFromX(fileName, D3DXMESH_MANAGED, pDevice, &boneHierarchy, NULL, &m_pRootBone, NULL); //Update all Bone transformation matrices D3DXMATRIX i; D3DXMatrixIdentity(&i); UpdateMatrices((Bone*)m_pRootBone, &i); } Sometimes it can be useful to locate a specific bone in a hierarchy—for example, if you would like to find the neck bone of a character and apply a rotation transformation matrix and make the head turn. The following D3DX function is then very useful: LPD3DXFRAME D3DXFrameFind( CONST D3DXFRAME * pFrameRoot, //The root bone LPCSTR Name //Name of bone you are looking for ); This function returns a pointer to the correct bone in the hierarchy or returns NULL if the bone wasn’t found. Try to use this function in Example 3.1 to find the neck bone. Hopefully you know by now how to load a bone hierarchy by implementing the ID3DXAllocateHierarchy interface. Later on in the book, you’ll see how you can use the same interface to load several different morph targets from a single .x file rather than keeping these meshes in separate files. However, for now it is time to actually apply a mesh to the bone hierarchy. 46 Character Animation with Direct3D Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. APPLYING A MESH TO THE BONE HIERARCHY As you probably know, a mesh consists of several polygons that in turn consist of one or more triangles. Each triangle in turn is defined by three vertices—i.e., three points in 3D space. Before you look at how to skin a complex character mesh to a bone hierarchy, first just look at a single vertex. A vertex can be linked (influenced) by one or more bones in the bone hierarchy. The amount a bone influences a vertex is determined by a weight value as shown in Figure 3.4. Chapter 3 Skinned Meshes 47 EXAMPLE 3.1 Okay, I’ve think you’ve had enough theory for a while. Here’s an actual code example for you to look at. You’ll find Example 3.1 on the CD-ROM. In this example, a bone hierarchy is loaded from an .x file. An ID3DXAllocateHierarchy interface is also implemented, and you’ll find the first rough version of the SkinnedMesh class. Note that in this example there’s also a temporary function for rendering a bone hierarchy using spheres and lines. Make sure you completely understand this example, because things are about to get a lot harder. Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. It is important that the combined weights for a vertex equal 1.0 (100%). In more mathematical terms, the transformation matrix applied to a vertex is defined as follows: M Tot =(w 0 M 0 +w 1 M 1 …+W n M n ) This formula multiplies the bone weight (w x ) with the bone transformation matrix (M x ) for all influencing bones and sums up the result (M Tot ). The resulting matrix is then used to transform the vertex. In DirectX, the information about which bones influence which vertices, as well as their respective weights, etc., is stored and controlled with the ID3DXSkinInfo interface. One way of creating this interface is by using the following D3DX function: HRESULT D3DXCreateSkinInfo( DWORD NumVertices, CONST D3DVERTEXELEMENT9 * pDeclaration, DWORD NumBones, LPD3DXSKININFO * ppSkinInfo ); This function takes the amount of vertices in a mesh, their vertex declaration (i.e., what information each vertex contains), and the number of bones that will be used to skin this mesh. If you are making something in code that requires skinning, this would be the best approach. However, characters will most definitely be created and skinned in a 3D software such as 3D Studio Max, Maya, or similar. Luckily, when you export a character like this to the .x file format, the skinning information 48 Character Animation with Direct3D FIGURE 3.4 An example of how a vertex (the cross) is affected by two bones (B1 and B2) with the weights 20% and 80%, respectively. Notice how the vertex follows B2 more than B1 due to the weights. Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. is exported as well. If you look again at the CreateMeshContainer() function of the ID3DXAllocateHierarchy interface, you’ll notice that one of the parameters to this function is indeed a pointer to a ID3DXSkinInfo object. So all you need to do when reading an .x file is to store this object and use it later when you skin a character. Soon the CreateMeshContainer() function will be implemented, and through it the process of loading a character with bones and all will be complete. First, however, you need to understand the two major choices you have for how to render a skinned character. The first option is to do the skinning using the CPU— a.k.a. software skinning. The other option is to do the skinning directly with the GPU (graphics processing unit, i.e., the graphics card) as the mesh is being rendered—a.k.a. hardware skinning. (There are other variations of these two techniques, but these two are the major options). S OFTWARE SKINNING OVERVIEW With the first option, software skinning, the positions of each vertex in a mesh are calculated using the mathematical formula covered earlier. The result is stored in a temporary mesh that is then consequently rendered. Simple, straightforward, but also very slow compared to hardware skinning. So if it is so slow compared to hardware skinning, why use it? Well, the fact that the character is stored as a mesh in memory is the major upside of software skinning. With this temporary mesh, things like shadow casting, picking, etc., become a bit easier. With software skinning, it also doesn’t matter how many bones are influencing a vertex. If you were making a first-person shooter (FPS) game, you might want to test to see whether or not a bullet you fired hit one of the enemy soldiers. With software skinning, this would be easy to test using a simple mesh–ray intersection test. H ARDWARE SKINNING OVERVIEW With hardware skinning, you can, of course, also do shadow casting, picking, etc., but then it requires a little more effort to get it to work. Hardware skinning also has some limits as to how many bones can influence a vertex as well as how many bones you can have per character without having to split up the mesh into several parts. However, what you lose in functionality, you make up readily in speed. Remember to choose your skinning method based on the particular game you are making. In the following two sections, both software and hardware skinning will be looked at in more detail. For a simple mesh–ray intersection test, check out the D3DXIntersect() function in the D3DX library. See the DirectX documentation for more info. Chapter 3 Skinned Meshes 49 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. SOFTWARE SKINNING IMPLEMENTATION Let’s first look at software skinning, since this is the more straightforward and easier method to implement. Here’s a brief overview of the steps needed to render a skinned mesh with software skinning: 1. (Optional) Overload D3DXFRAME 2. (Optional) Overload D3DXMESHCONTAINER 3. Implement the ID3DXAllocateHierarchy interface 4. Load a bone hierarchy and associated meshes, skinning information, etc., with the D3DXLoadMeshHierarchyFromX() function 5. For each frame, update the skeleton pose (i.e., the SkinnedMesh:: UpdateMatrices() function) 6. Update the target mesh using ID3DXSkinInfo::UpdateSkinnedMesh() 7. Render the target mesh as a common static mesh Loading the Skinned Mesh The first step on the path of skinning a mesh is to create your own mesh container structure. You do this by overloading the D3DXMESHCONTAINER structure defined as follows: struct D3DXMESHCONTAINER { LPSTR Name; D3DXMESHDATA MeshData; LPD3DXMATERIAL pMaterials; LPD3DXEFFECTINSTANCE pEffects; DWORD NumMaterials; DWORD * pAdjacency; LPD3DXSKININFO pSkinInfo; D3DXMESHCONTAINER * pNextMeshContainer; } The D3DXMESHCONTAINER contains the mesh itself (in the D3DXMESHDATA structure) as well as all the necessary stuff needed to render the mesh (materials, textures, and shaders). The texture filenames are stored as a member of the D3DXMATERIAL structure and must be loaded separately. Another notable member of this structure is the pSkinInfo variable, which will contain skinning information for any skinned meshes loaded. There are, however, some things that we want to add to this structure to make it easier to render the mesh using software skinning. Therefore I’ve created the BoneMesh structure, defined as follows: 50 Character Animation with Direct3D Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. struct BoneMesh: public D3DXMESHCONTAINER { ID3DXMesh* OriginalMesh; //Reference mesh vector<D3DMATERIAL9> materials; //List of materials vector<IDirect3DTexture9*> textures; //List of textures DWORD NumAttributeGroups; //Number attribute groups D3DXATTRIBUTERANGE* attributeTable; //Attribute table D3DXMATRIX** boneMatrixPtrs; //Pointers to bone matrices D3DXMATRIX* boneOffsetMatrices; //Bone offset matrices D3DXMATRIX* currentBoneMatrices; //Current bone matrices }; As you can see, quite a lot of extra information has been added to the BoneMesh structure compared to what was added in the Bone structure. The first three members should be quite easy to understand; the others may seem a little bit more obscure. Table 3.1 provides more details about the members. Chapter 3 Skinned Meshes 51 TABLE 3.1 THE BONEMESH MEMBERS Member Description OriginalMesh A copy of the original mesh in the bind pose materials A vector of D3DMATERIAL9 materials (used instead of the pMaterials pointer stored in the D3DXMESHCONTAINER structure) textures A vector of textures—each texture corresponding to a material in the materials vector NumAttributeGroups The number of attribute groups in the mesh (i.e., parts of the mesh using different materials/textures, etc.) boneMatrixPtrs An array of matrix pointers, pointing to the transformation matrix of each bone in the hierarchy boneOffsetMatrices A matrix for each bone that transforms the mesh into bone space; this is retrieved from the ID3DXSkinInfo currentBoneMatrices When the character is animated, this array will contain the combined transformation matrices of all the bones Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. Now that you’ve seen the overloaded D3DXMESHCONTAINER structure, take a look at how the CreateMeshContainer() of the ID3DXAllocateHierarchy is implemented to load a mesh into a BoneMesh object. HRESULT BoneHierarchyLoader::CreateMeshContainer( LPCSTR Name, CONST D3DXMESHDATA *pMeshData, CONST D3DXMATERIAL *pMaterials, CONST D3DXEFFECTINSTANCE *pEffectInstances, DWORD NumMaterials, CONST DWORD *pAdjacency, LPD3DXSKININFO pSkinInfo, LPD3DXMESHCONTAINER *ppNewMeshContainer) { //Create new Bone Mesh BoneMesh *boneMesh = new BoneMesh; memset(boneMesh, 0, sizeof(BoneMesh)); //Get mesh data boneMesh->OriginalMesh = pMeshData->pMesh; boneMesh->MeshData.pMesh = pMeshData->pMesh; boneMesh->MeshData.Type = pMeshData->Type; //Add Reference so the mesh is not deallocated pMeshData->pMesh->AddRef(); //To be continued First, a new BoneMesh object is created and all its members are set to zero and NULL using the memset() function. Next, a reference is added to the mesh data for both the OriginalMesh and the MeshData member. You need to keep a copy of the mesh in its original form when you do software skinning (more about this when the rendering of the mesh is covered). IDirect3DDevice9 *pDevice = NULL; pMeshData->pMesh->GetDevice(&pDevice); //Get pDevice ptr //Copy materials and load textures (just like with a static mesh) for(int i=0;i<NumMaterials;i++) { D3DXMATERIAL mtrl; memcpy(&mtrl, &pMaterials[i], sizeof(D3DXMATERIAL)); boneMesh->materials.push_back(mtrl.MatD3D); IDirect3DTexture9* newTexture = NULL; 52 Character Animation with Direct3D Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. if(mtrl.pTextureFilename != NULL) { char textureFname[200]; strcpy(textureFname, "meshes/"); strcat(textureFname, mtrl.pTextureFilename); //Load texture D3DXCreateTextureFromFile(pDevice, textureFname, &newTexture); } boneMesh->textures.push_back(newTexture); } //To be continued again In this section of the CreateMeshContainer() function, you first get a pointer to the current device. After that, you copy all the materials over to the BoneMesh structure, and if necessary any textures needed are loaded with the associated materials (this is why you needed to retrieve the device pointer). Next, the skinning information sent as a parameter to the CreateMeshContainer() will have to be stored. if(pSkinInfo != NULL) { //Get Skin Info boneMesh->pSkinInfo = pSkinInfo; //Add reference so SkinInfo isn't deallocated pSkinInfo->AddRef(); //Clone mesh and store in boneMesh->MeshData.pMesh pMeshData->pMesh->CloneMeshFVF(D3DXMESH_MANAGED, pMeshData->pMesh->GetFVF(), pDevice, &boneMesh->MeshData.pMesh); //Get Attribute Table boneMesh->MeshData.pMesh->GetAttributeTable( NULL, &boneMesh->NumAttributeGroups); boneMesh->attributeTable = new D3DXATTRIBUTERANGE[boneMesh->NumAttributeGroups]; Chapter 3 Skinned Meshes 53 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. boneMesh->MeshData.pMesh->GetAttributeTable( boneMesh->attributeTable, NULL); //Create bone offset and current matrices int NumBones = pSkinInfo->GetNumBones(); boneMesh->boneOffsetMatrices = new D3DXMATRIX[NumBones]; boneMesh->currentBoneMatrices = new D3DXMATRIX[NumBones]; //Get bone offset matrices for(int i=0;i < NumBones;i++) boneMesh->boneOffsetMatrices[i] = *(boneMesh->pSkinInfo->GetBoneOffsetMatrix(i)); } //Set ppNewMeshContainer to the newly created boneMesh container *ppNewMeshContainer = boneMesh; return S_OK; } In this last part of the CreateMeshContainer() function, you check whether there’s any skinning info available. If so, make a clone of the mesh stored in the pMeshData member of your BoneMesh structure. This cloned mesh will later be the actual skinned mesh rendered. Also remember that a pointer to the original mesh ( OriginalMesh member) is stored. This mesh will be used as a reference to create the skinned mesh stored in pMeshData each frame. In this piece of code, the number of attribute groups as well as the attribute table itself is stored. Then the matrix array is created according to how many bones are defined in the skinning information (note that you copy the bone offset matrix from the ID3DXSkinInfo object). Lastly, you simply store the created BoneMesh object and return S_OK. The attribute table is stored using an array of D3DXATTRIBUTERANGE objects: struct D3DXATTRIBUTERANGE { DWORD AttribId; //which material/texture to use DWORD FaceStart; //Face start DWORD FaceCount; //Num of faces in this attribute group DWORD VertexStart; //Vertex start DWORD VertexCount; //Num of vertices in this attribute group } 54 Character Animation with Direct3D Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. Later, when you render the mesh you loop through the attribute table, get the AttribId, and use this to set which material and which texture to use to render that subset of the mesh. This basically means that you can have several combinations of materials and textures when you render a character. Rendering the Skinned Mesh with Software Skinning Now you know how to load a bone hierarchy and any meshes that are attached to a bone using the extended Bone and BoneMesh structures as well as the BoneHierarchy- Loader. Now is finally where the fun begins! Now you are finally coming to the point where you’ll see a skinned character on the screen. To render a BoneMesh we need to calculate the current matrices for all the influencing bones and store these in the BoneMesh boneMatrixPtrs array. So just after loading a mesh with the D3DXLoadMeshHierarchyFromX() function we call the following function to set up these matrix pointers: void SkinnedMesh::SetupBoneMatrixPointers(Bone *bone) { //Find all bones containing a mesh if(bone->pMeshContainer != NULL) { BoneMesh *boneMesh = (BoneMesh*)bone->pMeshContainer; //For the bones with skinned meshes, set up the pointers if(boneMesh->pSkinInfo != NULL) { //Get num bones influencing this mesh int NumBones = boneMesh->pSkinInfo->GetNumBones(); //Create an array of pointers with numBones pointers boneMesh->boneMatrixPtrs = new D3DXMATRIX*[NumBones]; //Fill array for(int i=0;i < NumBones;i++) { //Find influencing bone by name Bone *b = (Bone*)D3DXFrameFind( m_pRootBone, boneMesh->pSkinInfo->GetBoneName(i)); Chapter 3 Skinned Meshes 55 Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. [...]... supply the shader with, here are the steps required for hardware skinning ease purchase PDF Split-Merge on www.verypdf.com to remove this watermark 60 Character Animation with Direct3D 1 2 3 4 5 6 7 8 (Optional) Overload D3DXFRAME (Optional) Overload D3DXMESHCONTAINER Implement the ID3DXAllocateHierarchy interface Load a bone hierarchy and associated meshes, skinning information, etc., with the D3DXLoadMeshHierarchyFromX()... NULL, src, dest); boneMesh->MeshData.pMesh->UnlockVertexBuffer(); boneMesh->OriginalMesh->UnlockVertexBuffer(); ease purchase PDF Split-Merge on www.verypdf.com to remove this watermark 58 Character Animation with Direct3D //Render the mesh for(int i=0;i < boneMesh->NumAttributeGroups;i++) { int mtrl = boneMesh->attributeTable[i].AttribId; pDevice->SetMaterial(&(boneMesh->materials[mtrl])); pDevice->SetTexture(0,... example, a character will be loaded and rendered as explained in the previous sections HARDWARE SKINNING IMPLEMENTATION Once you have mastered (or at least somewhat understood) the process of software skinning, it’s time to take a look at its less gentle cousin: hardware skinning With hardware skinning, the mesh gets skinned on-the-fly in the GPU rather than pre-processed each frame as is the case with software...56 Character Animation with Direct3D // and store pointer to it in the array if(b != NULL) { boneMesh->boneMatrixPtrs[i] = &b->CombinedTransformationMatrix; } else { boneMesh->boneMatrixPtrs[i] = NULL; } } } } //Traverse... Palette If you have a character with more than approxNumBones, then the mesh will be split into multiple parts and rendered in several passes Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark Chapter 3 Skinned Meshes 61 Vertex Shader 2.0 supports 256 vertex constants, which in most cases is enough However, as mentioned earlier, if you have a skinned mesh with a huge amount of... remapping info //Max num bones per vertex //Num bones in combo table //Bone combo table //Output Index Blended mesh ease purchase PDF Split-Merge on www.verypdf.com to remove this watermark 62 Character Animation with Direct3D FIGURE 3.5 This figure shows how a vertex is converted to contain the necessary information for hardware skinning FIGURE 3.6 This figure shows the relationship between a single... Matrix Palette extern float4x4 MatrixPalette[35]; //This variable should be set by the application code depending ease purchase PDF Split-Merge on www.verypdf.com to remove this watermark 64 Character Animation with Direct3D //on the max number of bones used by a certain mesh extern int numBoneInfluences = 2; //Vertex Input struct VS_INPUT_SKIN { float4 position : POSITION0; float3 normal : NORMAL;... boneComboTable->Release(); Well, that was fairly simple, wasn’t it? The mesh has now been completely converted to an Index Blended Mesh with one simple function call However, it is important that you understand what this function does, because now you’ll come face to face with the actual vertex shader that performs the skinning The Skinning Vertex Shader You’ll now be introduced to the first proper shader... UpdateMatrices() function) Upload the Matrix Palette (bone matrices) to the vertex shader Render the Index Blended Mesh using the vertex shader As you can see, most of the steps here are just the same as with software skinning There are two new concepts here though: the Matrix Palette and an Index Blended Mesh Before looking into the code for creating these, next is a quick look at the theory behind them... current bone transformation matrices was used as a parameter to the ID3DXSkinInfo::UpdateSkinnedMesh() function This function used the information stored in the ID3DXSkinInfo object to match all vertices with the bones influencing them as well as their corresponding weights Now you have to take care of this calculation yourself in the vertex shader The Matrix Palette is simply another name for the array . Palette. If you have a character with more than approxNumBones, then the mesh will be split into multiple parts and rendered in several passes. 60 Character Animation with Direct3D Please purchase. format, the skinning information 48 Character Animation with Direct3D FIGURE 3.4 An example of how a vertex (the cross) is affected by two bones (B1 and B2) with the weights 20% and 80%, respectively object and use it later when you skin a character. Soon the CreateMeshContainer() function will be implemented, and through it the process of loading a character with bones and all will be complete.

Ngày đăng: 03/07/2014, 05:20

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan