11. Mesh Part Two
This article translated from "Introduction to 3D Game Programming With DirectX 9.0" Chapter 11 "Mesh Part Two", please take ax.
This chapter describes the interface, structure, and functions related to Mesh provided by the D3DX library. Through the learning of this chapter, the complex 3D model will be loaded to control the fineness of the Mesh object. The goal to achieve this chapter:
l Learn to load .x file
l Understand the benefits of using progressive mesh (Progressive Mesh) and learning how to use the progressive Mesh interface ID3DXPMESH. Translate Progressive Mesh in the original text into progressive grids, I don't know if it is appropriate
l Learn Bounding Volume, and how to create a boundary range using D3DX functions
11.1. About ID3DXBUFFER
This interface runs through the entire D3DX library, which requires a substantial understanding of the interface. ID3DXBuffer is the structure of D3DX to manage continuous memory blocks, he has only two ways:
l LPVOID getBufferPointer (); - Return to the first address of the data block
l DWORD getBuffersize (); - Returns the size of the buffer, in bytes
For example, the D3DXLoadMeshFromx function returns the neighboring information of the MESH object using ID3dxBuffer. Since the adjacency information is a DWORD array, the type conversion is required. Such as:
DWORD * INFO = (DWORD *) AdjacencyInfo-> getBufferPointer (); D3DXMATERIAL * MTRLS = (D3DXMATERIAL *) mtrlbuffer-> getBufferPointer ();
Also because id3dxbuffer is a COM object, so after it is used, it needs to be released.
AdjacencyInfo-> Release (); mtrlbuffer-> release ();
You can also create an empty ID3DXBuffer object using the following functions:
HRESULT WINAPI D3DXCREATEBUFFER (DWORD NUMBYTES, LPD3DXBUFFER * PPBuffer);
The meaning of the parameters is obvious. For example, create a buffer containing four integer:
ID3DXBUFFER * Buffer = 0; D3DXCREATEBUFFER (4 * sizeof (int), & buffer);
11.2. X file
Use the D3DxCreate * function, you can create some simple geometry, such as balls, cylinders, cubes, etc. If you want to create a more complicated 3D object by manually set the vertex, you will find that this is too much trouble, it is simply unable to do it! Now, many 3D modeling tool software can be used to complete this boring work, such as 3DS Max, LightWave 3D, Maya, etc. With such modeling tools, you can design complex, realistic models in visualization, interactive environments, and there are extensive tools to make the entire modeling process is quite simple. The simple is the simpler in the "manual setting of the vertex in the program", in fact, these modeling tools are quite complicated, and they want to use their hands. It is not a good job in the first day.
These modeling tools can save data (geometric information, materials, animation, etc.) of the established model to the file. We need to analyze the data required from the file and then apply to your own 3D program. There is a common file format, XFile, its extension is .X, simpler, Direct3D defined file format, D3DX library provides complete support to meet the general needs. 11.2.1. Load a .x file
Use the following functions to load the Mesh data stored in the .x file. It creates an ID3DXMESH object and then reads the geometric information of the Mesh from the .x file.
HRESULT WINAPI D3DXLoadMeshFromX (LPCTSTR pFilename, DWORD Options, LPDIRECT3DDEVICE9 pD3DDevice, LPD3DXBUFFER * ppAdjacency, LPD3DXBUFFER * ppMaterials, LPD3DXBUFFER * ppEffectInstances, DWORD * pNumMaterials, LPD3DXMESH * ppMesh);
l PfileName -.x file name
l Options - Create a sign of a mesh. For details, refer to the D3DxMesh enumeration type in the SDK document. Several symbols commonly used are as follows:
N D3DXMESH_32BIT - Use 32-bit vertex index, default is 16 bits
N D3DXMESH_MANAGED - Using a controlled memory buffer pool
n D3DXMESH_WRITEONLY - Buffer only writes
N D3DXMESH_DYNAMIC - Using the Dynamic Source Buffer Pool
l pd3ddevice -d3d device pointer
l Ppadjacency - Returns the neighbor information of Mesh using ID3DXBuffer, this is a DWORD array
l PPMATERIALS - Returns the material data of Mesh using ID3dxBuffer, this is a D3DXMATERIAL type array
l PPEFFECTINSTANCES - Return to a D3DxEffectInstance structure array using ID3DXBuffer
l PNumMaterials - Returns the number of materials that Mesh objects, which is the number of elements of the D3DXMATERIAL array returned by ppmaterials
l ppmesh - return ID3DXMESH object
11.2.2. Xfile material
The seventh parameter of the function D3DXLoadMeshFromx returns the material number of the MESH object, the fifth parameter is the array of D3DXMATERIAL, including the material data of MESH. The D3DXMATERIAL structure is defined as follows:
Typedef struct d3dxmaterial {d3dmaterial9 matd3d; lpstr pTextureFileName;} D3DXMATERIAL
This structure is simple, including a D3DMATERIAL9 structure and a pointer to a string ended with 0 characters, indicates the associated texture file. The x file does not contain textured data, only the file name of the texture file. After loading the .x file with this function, you also need to manually load the texture according to the file name of the texture file.
The D3DXMATERIAL array returned by the function D3DXLoadMeshFromx is correct to the subset of the Mesh object. That is, the material texture information of the i-th subset is stored in ppmaterials [i].
11.2.3. Application example of x file
This example is quite simple, it loads a Bigship1.x file, which is a file in the DirectX SDK. There are only the main framework of the code here. ID3DXMESH * MESH = 0; Vector } Else {// no texture for the ith subset text;}}}}}}} Release Device-> Clear (0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xFFFFFFF, 1.0F, 0); device-> beginscene (); for (int i = 0; i 11.2.4. Method for creating vertices Sometimes the .x file does not contain a normal view of the vertex, at this time, if you use light, you need to manually calculate the method of the vertex. For interface ID3dxmesh and its parent interface ID3DXBASEMESH, you can use the method of calculating the vertex by using the following function: HRESULT WINAPI D3DXComputenormals (LPD3DXBASEMESH PMESH, Const DWORD * Padjacency); This function will use the average of the average of the average as the method of vertices. If the neighboring information of the Mesh object is provided, the duplicate vertex will be ignored; if there is no adjacency information, the repeated vertex will be repeated. Another point is more important, and the vertex format of the Mesh object that needs to calculate the method must contain the D3DFVF_NORMAL flag. If there is no normal data in the .x file, the vertex format of the ID3DXMESH object created by the D3DXLoadMeshFromx function does not contain the D3DFVF_NORMAL flag. Therefore, the D3DFVF_NORMAL flag must be replicated with the D3DFVF_NORMAL flag before the calculation method. // does the mesh harve a d3dfvf_normal in its Vertex Format? IF (! (pmesh-> getfvf () & d3dfvf_normal)) {// No, SO Clone a new mesh and add d3dfvf_normal to it its format: id3dxmesh * ptempmesh = 0; pMesh-> CloneMeshFVF (D3DXMESH_MANAGED, pMesh-> GetFVF () | D3DFVF_NORMAL, // add it here Device, & pTempMesh); // compute the normals: D3DXComputeNormals (pTempMesh, 0); pMesh-> Release (); // get rid Of The Old Mesh Pmesh = Ptempmesh; // Save The New Mesh with Normals} 11.3. Advantage Mesh Gradual Mesh is an object of the ID3DXPMESH interface that simplifies the edge reduction conversion (ECT)). Each ECT will reduce a vertex and one or two faces. Since the ECT process is reversible (his inverse process is called vertex split), the MESH can be restored to the original state by the reverse process. Of course, we can't get a more fine-fined Mesh object than the original state, you can only return it to the original state. The MIPMAP in Progressive Mesh and textures is very similar. Using high resolution textures on smaller and long-distance objects is purely wasteful, because the details of the texture are not shown. The same is true for the Mesh object, and the smaller distance is much more than a triangle, which is more wasteful. Therefore, when rendering, there is no need to waste time in these fundamental places. One way is to adjust its fine level (LOD, Level of Detail) based on the distance of the viewpoint from the Mesh object. When the distance is increased, the LOD can be lowered; then, the LOD is increased. Only the usage of the ID3DXPMESH interface is discussed here without discussing its implementation details. If you are interested, you can refer to other information. 11.3.1. Generate a gradual mesh Create an ID3DXPMESH object using the following function: HRESULT WINAPI D3DXGeneratePMesh (LPD3DXMESH pMesh, const DWORD * pAdjacency, const D3DXATTRIBUTEWEIGHTS * pVertexAttributeWeights, const FLOAT * pVertexWeights, DWORD MinValue, DWORD Options, LPD3DXPMESH * ppPMesh); l Pmesh - Enter ordinary Mesh object l Padjacency-Mesh object neighboring information, this is a DWORD array l PvertexattributeWeights - Architecture D3DXATTRIBUTEGHTS, the number of elements is pmesh-> getnumvertices (), indicating the right of the attribute of the vertex. When simplifies the Mesh object, the weight determines the possibility of a vertex to be deleted. This parameter can be set to NULL, which uses the default weight using the vertex. l Pvertexweights - The right to the vertex is a float array, the number of elements is pmesh-> getnumvertices (), which is used to determine the size of the possibility that the vertex is deleted when it is simplified. This parameter can also be set to NULL, at this time, the default weight of the vertex is 1.0F. l MINVALUE - Minimum number of values in the vertex or triangle when simplified Mesh. This parameter is necessary, and has a relationship with the values of the vertex weights and vertex attributes, which may not meet this value. l Options - You can only take a value in the D3DxMeshsimp enumeration type: N D3DXMESHSIMP_VERTEX - Previous parameter minvalue pointing N D3DXMESHSIMP_FACE - Previous parameter minvalue refers to the number of triangles l pppmesh - return to the generated gradual mesh 11.3.2. Attribute right of the vertex typedef struct _D3DXATTRIBUTEWEIGHTS {FLOAT Position; FLOAT Boundary; FLOAT Normal; FLOAT Diffuse; FLOAT Specular; FLOAT Texcoord [8]; FLOAT Tangent; FLOAT Binormal;} D3DXATTRIBUTEWEIGHTS, * LPD3DXATTRIBUTEWEIGHTS; With this structure, you can specify a weight for each attribute of the vertex, and 0.0 indicates that the attribute is not right. The higher the weight, the less easily deleted when simplified. The default weight is as follows: D3DXATTRIBUTEWEIGHTS AttributeWeights; AttributeWeights.Position = 1.0; AttributeWeights.Boundary = 1.0; AttributeWeights.Normal = 1.0; AttributeWeights.Diffuse = 0.0; AttributeWeights.Specular = 0.0; AttributeWeights.Tex [8] = {0.0, 0.0, 0.0, 0.0, 0.0 , 0.0, 0.0, 0.0}; In general, it is recommended to use the default weight unless you think it is necessary to use different weights. 11.3.3. Method of ID3DXPMESH Interface ID3DXPMESH inherits from ID3DXBASEMESH, which describes some common methods. l DWORD getMaxFaces (void); - Return to Mesh's maximum triangle l DWORD getMaxvertices (void); - Return to Mesh's largest vertex number l DWORD getMinfaces (void); - Return to Mesh minimum triangle l DWord getminvertices (void); - Return to Mesh's minimum vertex number l HRESULT SETNUMFCES (DWORD Faces); - Set the number of triangles of the MESH. For example, assuming that Mesh now has 50 triangles, and wants to simplify it into 30 triangles, call Pmesh-> SetNumFaces (30). Adjusting triangular numbers may not be the number of we set, because Pmesh's triangular figures have the greatest and minimal restrictions. l HRESULT SETNUMVERTICES (DWORD VERTICES); - Set the number of vertices of Pmesh. For example, suppose now Pmesh has 20 top points, and in order to increase its extent to 40, simply call PMESH-> SetNumvertices (40). As with the number of triangles, the final result may not be the value we specified, and there is also a maximum limit of the minimum number. l HRESULT TRIMBYFACES DWord NewFacesmin, DWORD NewFaceSmax, DWORD * RGIFACEREMAP, DWORD * RGIVERTREMAP ); The method sets the maximum minimum number of PMESH triangularities. The new maximum minimum must be between the current maximum minimum, that is, in [Getminfaces (), getMaxFaces ()]. At the same time, the method will also return the reloading information of the triangle and the vertex. l HRESULT TRIMBYVERTICES DWORD Newverticesmin, DWORD Newverticessmax, DWORD * RGIFACEREMAP, DWORD * RGIVERTREMAP ); The method is similar to the above method. 11.3.4. Application example: Progressive Mesh This example is similar to the previous XFile example, just in which the ID3DXPMESH interface is used. Similar to the previous example, we use the following global variables: ID3DXMESH * SOURCEMESH = 0; ID3DXPMESH * PMESH = 0; // Progressive Mesh Vector Before creating the Progressive Mesh, you need to load the .x file: HRESULT HR = 0; // ... load Xfile Data INTO SOUREMESH SNIPPED. // ... Extracting Materials and textures Snipped. ////////// HR = D3DXGeneratepmesh (Sourcemesh, (DWORD * ) adjBuffer-> GetBufferPointer (), // adjacency 0, // default vertex attribute weights 0, // default vertex weights 1, // simplify as low as possible D3DXMESHSIMP_FACE, // simplify by face count & PMesh); Release Now, gradual mesh has been generated, but if directly rendered, the resolution of MESH is at the lowest. If you want to render all resolution Pmesh, you first need to set the number of triangles: // set to Original (Full) Detail Dword Maxfaces = Pmesh-> getMaxFaces (); pmesh-> setnumfaces (maxfaces); When rendering the PMESH, we use the keyboard input to control its resolution: A key will increase the resolution, and the S button will reduce the resolution. // Get the current Number of Faces The Pmesh Has. Int Numfaces = Pmesh-> getnumface (); // add a face, note the setnumface () Will AutomaticL // Clamp The Specified Value IT Goes Out of Bounds. IF ( :: GetAsyncKeyState ( 'A') & 0x8000f) {// Sometimes we must add more than one face to invert // an edge collapse transformation because of the internal // implementation details of the ID3DXPMesh interface. In // other words, adding One Face May Possibly Result In A // Mesh with the Same Number of Faces As Before. Thus to // Increase The Face Count We May Sometimes Have To Add // Two Faces At Celetes (Numfaces 1); IF (pmesh-> getnumface () == Numfaces) Pmesh-> setNumfaces (Numfaces 2);} // Remove a face, Note the setnumface () Will AutomaticLi IT Out Out of Bounds. if it goes out of baounds. if (: GetasynckeyState ('s') & 0x8000F) Pmesh-> setNumfaces (Numfaces - 1); the above method is straightforward, just increasing the number of triangles, sometimes adding two to meet the needs of ECT. Finally, rendering ID3DXPMESH using and rendering ID3DXMESH. In addition, in order to more intuitive observation of the change in the Trimorial number of PMESH, the triangle of the Mesh is rendered under the wireframe mode. Device-> Clear (0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xFFFFFFF, 1.0F, 0); device-> beginscene (); for (int i = 0; i Sometimes it is necessary to calculate the boundary range of the Mesh object, there are two types commonly used: cubes and balls. There are also other methods, such as cylinders, ellipsoids, diamonds, space compartments, and the like. Here, we only discuss two border forms of cubes and spheres. Boundary boxes or boundaries are often used to accelerate multi-object interval testing, collision detection, etc. If a Mesh's boundary box / ball is not visible, you can think that Mesh is also invisible. Detecting whether the boundary box / ball is visible than the detection of all triangles in Mesh, it is convenient to be more convenient. In collision detection, if a missile is fired, we need to test whether he hits the goal in the same scene. Since these objects are all a large triangular configuration, we can detect each triangle of each object to detect whether the missile (the rays in the mathematical model) hit these triangles. This method requires the operation of multiple ray / triangular intersections. A preferred method is to use a boundary box or boundary ball, calculate the intersection of the boundary box / boundary ball of the rays and the scene in the scene. If the ray intersects the boundary range of the object, it can be considered that the object is hit. This is a fair approximation method. If you need higher precision, you can use the boundary range to first remove those objects that are significantly not collusive, and then detect the most likely collision with more accurate way. If the boundary range detection finds collision, the object is likely to collide. The D3DX library provides a function of calculating the Mesh object boundary box / ball. These functions use vertex arrays as input calculation boundary boxes / balls, you can use a variety of vertex formats: HRESULT WINAPI D3DXComputeBoundingsphere (Const D3DxVector3 * PfirstPosition, DWORD NUMVERTICES, DWORD DWSTRIDE, D3DXVECTOR3 * PCENTER, FLOAT * PRADIUS); l pfirstposition - the address of the vertex array, the first vector of the vertex needs to be the position coordinate of the vertex l Numvertices - Number of vertices l DWSTRIDE - The vertex size is in bytes. Because there are many additional data in the vertex, such as normal vector, texture coordinate, etc., calculate the boundary range does not need this data, so you need to know how much data can be skipped to find the coordinates of the next vertex. l pCenter - Return to the center of boundaries l Pradius - Return the radius of the boundary ball HRESULT WINAPI D3DXComputeBoundingBox (const D3DXVECTOR3 * pFirstPosition, DWORD NumVertices, DWORD dwStride, D3DXVECTOR3 * pMin, D3DXVECTOR3 * pMax); The first three parameters are the same function calculates the boundary of the ball; the latter two parameters returns the minimum and maximum points of the bounding box. 11.4.1. Border detection type In order to make the boundary detection easy to use, we implements several auxiliary data structures: struct BoundingBox {BoundingBox (); bool isPointInside (D3DXVECTOR3 & p); D3DXVECTOR3 _min; D3DXVECTOR3 _max;}; struct BoundingSphere {BoundingSphere (); D3DXVECTOR3 _center; float _radius;}; BoundingBox :: BoundingBox () {// infinite small bounding box _min.x = flt_max; _min.z = flt_max; _max.x = -flt_max; _max.y = -flt_max; _max.z = -flt_max;} Bool BoundingBox :: ispointinside (D3DXVector3 & P) { // is the point inside the bunking box? if (px> = _min.x && py> = _min.y && pz> = _MAX.X && PX <= _max.y && pz <= _max.z) {return true;}}} boveingsphere :: boveingsphere () {_radius = 0.0f;} 11.4.2. Example of boundary range application This example demonstrates the use of D3DxComputeBoundingsphere and D3DxComputeBoundingbox functions. The program first loads a .x file and calculates the bound box / ball of the MESH. Create two ID3dxmesh objects in the code, using boundary boxes and border balls, respectively. Finally, rendering them separately. This example is very simple, here only give the code about boundary range: bool ComputeBoundingSphere (ID3DXMesh * mesh, // mesh to compute bounding sphere for BoundingSphere * sphere) // return bounding sphere {HRESULT hr = 0; BYTE * v = 0; mesh-> LockVertexBuffer (0, (void **) & v) ; hr = D3DXComputeBoundingSphere ((D3DXVECTOR3 *) v, mesh-> GetNumVertices (), D3DXGetFVFVertexSize (mesh-> GetFVF ()), & sphere -> _ center, & sphere -> _ radius); mesh-> UnlockVertexBuffer (); if (FAILED ( hr)) return false; return true;} bool ComputeBoundingBox (ID3DXMesh * mesh, // mesh to compute bounding box for BoundingBox * box) // return bounding box {HRESULT hr = 0; BYTE * v = 0; mesh-> LockVertexBuffer (0, (void **) & v); hr = d3dxcomputeboundingbox (D3DXVector3 *) v, Mesh-> getnumvertices (), d3dxgetfvfvertexsize (Mesh-> getfvf ()), & box -> _ min, & box -> _ max); mesh -> unlockvertexbuffer (); if (failed (hr)) Return False; Return True;} Type conversion (D3DXVE CTOR3 *) V assumes that the vertex coordinates at the beginning of the vertex structure, generally. Summary l Now, we can build complex Mesh objects with 3D modeling software. x files. Use the D3DXLoadMeshFromx function to get the ID3dxmesh object, you can use it freely in your own application. l The gradual Mesh represented using the ID3DXPMESH interface can control its fineness. The extent of the PMESH can be adjusted according to the protruding degree of the object in the scene. l We can use the D3DxComputeBoundingsphere and the D3DxComputeBoundingBox function to calculate the boundaries of the Mesh object. The boundary range is useful, and its approach the real boundary, accelerates the calculation of collision detection.