1. Trang chủ
  2. » Công Nghệ Thông Tin

Character Animation with Direct3D- P4 ppt

20 349 0
Tài liệu đã được kiểm tra trùng lặp

Đ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

Thông tin cơ bản

Tiêu đề Character Animation with Direct3D
Trường học University of XYZ
Chuyên ngành Computer Science
Thể loại Bài luận
Năm xuất bản 2023
Thành phố Hanoi
Định dạng
Số trang 20
Dung lượng 293,98 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

void SkinnedMesh::Loadchar fileName[] { BoneHierarchyLoader boneHierarchy; //Load a bone hierarchy from a file D3DXLoadMeshHierarchyFromXfileName, D3DXMESH_MANAGED, pDevice, &boneHiera

Trang 1

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

NULLif 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

ID3DXAllocateHierarchyinterface 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

Trang 2

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

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.

Trang 3

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 (wx) with the bone transformation matrix (Mx) for all influencing bones and sums up the result (MTot) 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 ID3DXSkinInfointerface 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

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.

Trang 4

is exported as well If you look again at the CreateMeshContainer()function of the

ID3DXAllocateHierarchyinterface, you’ll notice that one of the parameters to this function is indeed a pointer to a ID3DXSkinInfoobject 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 S KINNING O VERVIEW

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 S KINNING O VERVIEW

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.

Trang 5

S OFTWARE S KINNING I MPLEMENTATION

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 ID3DXAllocateHierarchyinterface

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 D3DXMESHCONTAINERstructure defined as follows:

struct D3DXMESHCONTAINER { LPSTR Name;

D3DXMESHDATA MeshData;

LPD3DXMATERIAL pMaterials;

LPD3DXEFFECTINSTANCE pEffects;

DWORD NumMaterials;

DWORD * pAdjacency;

LPD3DXSKININFO pSkinInfo;

D3DXMESHCONTAINER * pNextMeshContainer;

}

The D3DXMESHCONTAINERcontains the mesh itself (in the D3DXMESHDATAstructure)

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 D3DXMATERIALstructure and must be loaded separately Another notable member of this structure is the

pSkinInfovariable, 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

BoneMeshstructure, defined as follows:

Trang 6

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 Bonestructure 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

TABLE 3.1 THE BONEMESH MEMBERS

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

Trang 7

Now that you’ve seen the overloaded D3DXMESHCONTAINERstructure, take a look

at how the CreateMeshContainer()of the ID3DXAllocateHierarchyis implemented

to load a mesh into a BoneMeshobject

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 BoneMeshobject is created and all its members are set to zero and

NULLusing the memset()function Next, a reference is added to the mesh data for both the OriginalMeshand the MeshDatamember 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;

Trang 8

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];

Trang 9

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

pMeshDatamember of your BoneMeshstructure This cloned mesh will later be the actual skinned mesh rendered Also remember that a pointer to the original mesh (OriginalMeshmember) is stored This mesh will be used as a reference to create the skinned mesh stored in pMeshDataeach 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 ID3DXSkinInfoobject) Lastly, you simply store the created BoneMeshobject and return S_OK

The attribute table is stored using an array of D3DXATTRIBUTERANGEobjects:

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 }

Trang 10

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 Boneand BoneMeshstructures 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 boneMatrixPtrsarray 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));

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

TỪ KHÓA LIÊN QUAN