以文本方式查看主题 - 计算机科学论坛 (http://bbs.xml.org.cn/index.asp) -- 『 C/C++编程思想 』 (http://bbs.xml.org.cn/list.asp?boardid=61) ---- DXUT源码分析 (http://bbs.xml.org.cn/dispbbs.asp?boardid=61&rootid=&id=70614) |
-- 作者:卷积内核 -- 发布时间:12/18/2008 9:52:00 AM -- DXUT源码分析 DXUT源码分析 ---- 类CDXUTMesh(1) 类CDXUTMesh主要用于从一个指定的网格模型中加载数据、渲染模型以及销毁网格模型,它将整个网格模型作为一个整体进行操作,没有考虑网格模型内部的框架层次,对于不包含动画信息的网格模型,使用该类是一个比较好的选择。 这个类的定义和实现分别位于DXUTMesh.h和DXUTMesh.cpp中,其定义如下: IDirect3DVertexBuffer9* m_pVB; DWORD m_dwNumMaterials; // Materials for the mesh public: HRESULT Render( ID3DXEffect *pEffect, // Mesh access // Rendering options // Initializing // Creation/destruction HRESULT CreateMaterials(LPCWSTR strPath, IDirect3DDevice9 *pd3dDevice, CDXUTMesh( LPCWSTR strName = L"CDXUTMeshFile_Mesh" );
该类中包含的成员函数按其作用可分为6类。 CDXUTMesh::CDXUTMesh( LPCWSTR strName ){ StringCchCopy( m_strName, 512, strName ); 第二类是获取网格函数,它仅包含一个函数GetMesh(),实现也非常简单,即返回类CDXUTMesh的成员变量m_pMesh。 第三类是设备恢复和丢失时所采取的操作函数,这里所包含的两个成员函数RestoreDeviceObjects()和InvalidateDeviceObjects()分别是在设备恢复和丢失时调用,用于恢复和释放相应的资源。 HRESULT CDXUTMesh::RestoreDeviceObjects( LPDIRECT3DDEVICE9 pd3dDevice ){ return S_OK;} 来看第一个Create()函数的实现: // Cleanup previous mesh if any // Find the path for the file, and convert it to ANSI (for the D3DX API) // Load the mesh // Optimize the mesh for performance // Set strPath to the path of the mesh file WCHAR* pLastBSlash = wcsrchr( strPath, L'\\' ); if( pLastBSlash ) D3DXMATERIAL* d3dxMtrls = (D3DXMATERIAL*) pMtrlBuffer->GetBufferPointer(); SAFE_RELEASE( pAdjacencyBuffer ); // Extract data from m_pMesh for easy access D3DVERTEXELEMENT9 decl[MAX_FVF_DECL_SIZE]; m_dwNumVertices = m_pMesh->GetNumVertices(); m_pMesh->GetIndexBuffer( &m_pIB ); pd3dDevice->CreateVertexDeclaration( decl, &m_pDecl ); return hr;
函数首先销毁旧的资源,并调用DXUTFindDXSDKMediaFileCch()通过文件名查找文件所在的路径,接着调用D3DXLoadMeshFromXW()从文件中加载网格模型。 WCHAR strPath[MAX_PATH]; LPD3DXBUFFER pAdjacencyBuffer = NULL; LPD3DXBUFFER pMtrlBuffer = NULL; HRESULT hr; D3DXMESHOPT_COMPACT — 从mesh中移除没有用的顶点和索引项。 D3DXMESHOPT_ATTRSORT — 根据属性给三角形排序并调整属性表,这将使DrawSubset执行更有效。 D3DXMESHOPT_VERTEXCACHE — 增加顶点缓存的命中率。 // Optimize the mesh for performance if( FAILED( hr = m_pMesh->OptimizeInplace( D3DXMESHOPT_COMPACT | D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_VERTEXCACHE, (DWORD*) pAdjacencyBuffer->GetBufferPointer(), NULL, NULL, NULL))) { SAFE_RELEASE( pAdjacencyBuffer ); SAFE_RELEASE( pMtrlBuffer ); return hr; } // Set strPath to the path of the mesh file D3DXMATERIAL* d3dxMtrls = (D3DXMATERIAL*) pMtrlBuffer->GetBufferPointer(); hr = CreateMaterials( strPath, pd3dDevice, d3dxMtrls, m_dwNumMaterials ); // Extract data from m_pMesh for easy access |
-- 作者:卷积内核 -- 发布时间:12/18/2008 9:53:00 AM -- 函数CreateMaterials()用于创建网格模型中所需的材质和纹理,我们来看看CreateMaterials()的实现: m_dwNumMaterials = dwNumMaterials; if( d3dxMtrls && m_dwNumMaterials > 0 ) m_pMaterials = new D3DMATERIAL9[m_dwNumMaterials]; m_pTextures = new LPDIRECT3DBASETEXTURE9[m_dwNumMaterials]; m_strMaterials = new CHAR[m_dwNumMaterials][MAX_PATH]; // Copy each material and create its texture m_pTextures[i] = NULL; // Create a texture WCHAR strTexture[MAX_PATH]; // First attempt to look for texture in the same folder as the input folder. MultiByteToWideChar( CP_ACP, 0, d3dxMtrls[i].pTextureFilename, -1, strTextureTemp, MAX_PATH ); StringCchCopy( strTexture, MAX_PATH, strPath ); // Inspect the texture file to determine the texture type. D3DXGetImageInfoFromFile( strTexture, &ImgInfo ); // Call the appropriate loader according to the texture type. if( SUCCEEDED( D3DXCreateTextureFromFileW( pd3dDevice, strTexture, &pTex ) ) ) // Release the specialized instance break; case D3DRTYPE_CUBETEXTURE: if( SUCCEEDED( D3DXCreateCubeTextureFromFile( pd3dDevice, strTexture, &pTex ) ) ) // Release the specialized instance break; case D3DRTYPE_VOLUMETEXTURE: if( SUCCEEDED( D3DXCreateVolumeTextureFromFile( pd3dDevice, strTexture, &pTex ) ) ) // Release the specialized instance break; return S_OK;
函数首先根据材质数来创建相应的材质缓存、纹理缓存、纹理文件名缓存: // Copy each material and create its texture for( DWORD i=0; i<m_dwNumMaterials; i++ ) { // Copy the material m_pMaterials[i] = d3dxMtrls[i].MatD3D; m_pMaterials[i].Ambient = m_pMaterials[i].Diffuse; // add by me // Create a texture if( d3dxMtrls[i].pTextureFilename ) { StringCchCopyA( m_strMaterials[i], MAX_PATH, d3dxMtrls[i].pTextureFilename ); StringCchCat is a replacement for strcat. The size, in characters, of the destination buffer is provided to the function to ensure that StringCchCat does not write past the end of this buffer. HRESULT StringCchCat( LPTSTR pszDest, size_t cchDest, LPCTSTR pszSrc); pszDest Note that this function returns an HRESULT as opposed to strcat, which returns a pointer. It is strongly recommended that you use the SUCCEEDED and FAILED macros to test the return value of this function. S_OK Source data was present, the strings were fully concatenated without truncation, and the resultant destination buffer is null-terminated. Remarks StringCchCat provides additional processing for proper buffer handling in your code. Poor buffer handling is implicated in many security issues that involve buffer overruns. StringCchCat always null-terminates a non-zero-length destination buffer. StringCchCat can be used in its generic form, or specifically as StringCchCatA (for ANSI strings) or StringCchCatW (for Unicode strings). The form to use is determined by your data. String Data Type String Literal Function strcat Neither pszSrc nor pszDest should be NULL. See StringCchCatEx if you require the handling of null string pointer values.
接下来调用D3DXGetImageInfoFromFile()从文件中获取图像数据,如果获取失败,则在搜索路径中增加一层目录media来搜索: // Inspect the texture file to determine the texture type. if( FAILED( D3DXGetImageInfoFromFile( strTexture, &ImgInfo ) ) ) { // Search the media folder if( FAILED( DXUTFindDXSDKMediaFileCch( strTexture, MAX_PATH, strTextureTemp ) ) ) continue; // Can't find. Skip. Retrieves information about a given image file. HRESULT D3DXGetImageInfoFromFile( LPCSTR pSrcFile, D3DXIMAGE_INFO * pSrcInfo); Remarks D3DXIMAGE_INFO typedef struct D3DXIMAGE_INFO { UINT Width; UINT Height; UINT Depth; UINT MipLevels; D3DFORMAT Format; D3DRESOURCETYPE ResourceType; D3DXIMAGE_FILEFORMAT ImageFileFormat;} D3DXIMAGE_INFO, *LPD3DXIMAGE_INFO; typedef enum D3DXIMAGE_FILEFORMAT{ D3DXIFF_BMP = 0, D3DXIFF_JPG = 1, D3DXIFF_TGA = 2, D3DXIFF_PNG = 3, D3DXIFF_DDS = 4, D3DXIFF_PPM = 5, D3DXIFF_DIB = 6, D3DXIFF_HDR = 7, D3DXIFF_PFM = 8, D3DXIFF_FORCE_DWORD = 0x7fffffff,} D3DXIMAGE_FILEFORMAT, *LPD3DXIMAGE_FILEFORMAT; The following table lists the available input and output formats. File Extension Description See Types of Bitmaps (GDI+) for more information on some of these formats. 接下来根据图像的资源类型来创建纹理,如果是纹理资源,则调用D3DXCreateTextureFromFile()来创建纹理,并调用QueryInterface()来请求该类型的接口: // Call the appropriate loader according to the texture type. switch( ImgInfo.ResourceType ) { case D3DRTYPE_TEXTURE: { IDirect3DTexture9 *pTex; Determines whether the object supports a particular COM interface. If it does, the system increases the object's reference count, and the application can use that interface immediately. HRESULT QueryInterface( REFIID riid, LPVOID * ppvObj); If the method fails, the return value may be E_NOINTERFACE. Some components also have their own definitions of these error values in their header files. Remarks 如果立方体纹理资源则调用D3DXCreateCubeTextureFromFile()来创建立方体纹理缓存,如果是D3DRTYPE_VOLUMETEXTURE类型的资源,则调用D3DXCreateVolumeTextureFromFile()来创建该类型的纹理资源: case D3DRTYPE_CUBETEXTURE: { IDirect3DCubeTexture9 *pTex; Creates a cube texture from a file. HRESULT D3DXCreateCubeTextureFromFile( LPDIRECT3DDEVICE9 pDevice, LPCTSTR pSrcFile, LPDIRECT3DCUBETEXTURE9 * ppCubeTexture); D3DERR_NOTAVAILABLED3DERR_OUTOFVIDEOMEMORYD3DXERR_INVALIDDATAE_OUTOFMEMORY The function is equivalent to D3DXCreateCubeTextureFromFileEx(pDevice, pSrcFile, D3DX_DEFAULT, D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, ppCubeTexture). This function supports the following file formats: .bmp, .dds, .dib, .hdr, .jpg, .pfm, .png, .ppm, and .tga. See D3DXIMAGE_FILEFORMAT. Note that a resource created with this function will be placed in the memory class denoted by D3DPOOL_MANAGED. Filtering is automatically applied to a texture created using this method. The filtering is equivalent to D3DX_FILTER_TRIANGLE | D3DX_FILTER_DITHER in D3DX_FILTER. D3DXCreateCubeTextureFromFile uses the DirectDraw surface (DDS) file format. The DirectX Texture Editor (dxtex.exe) enables you to generate a cube map from other file formats and save it in the DDS file format. D3DXCreateVolumeTextureFromFile()的声明如下: Creates a volume texture from a file. HRESULT D3DXCreateVolumeTextureFromFile( LPDIRECT3DDEVICE9 pDevice, LPCTSTR pSrcFile, LPDIRECT3DVOLUMETEXTURE9 * ppVolumeTexture); D3DERR_NOTAVAILABLED3DERR_OUTOFVIDEOMEMORYD3DERR_INVALIDCALLD3DXERR_INVALIDDATAE_OUTOFMEMORY The function is equivalent to D3DXCreateVolumeTextureFromFileEx(pDevice, pSrcFile, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, ppVolumeTexture). This function supports the following file formats: .bmp, .dds, .dib, .hdr, .jpg, .pfm, .png, .ppm, and .tga. See D3DXIMAGE_FILEFORMAT. Mipmapped textures automatically have each level filled with the loaded texture. When loading images into mipmapped textures, some devices are unable to go to a 1x1 image and this function will fail. If this happens, then the images need to be loaded manually. Note that a resource created with this function will be placed in the memory class denoted by D3DPOOL_MANAGED. Filtering is automatically applied to a texture created using this method. The filtering is equivalent to D3DX_FILTER_TRIANGLE | D3DX_FILTER_DITHER in D3DX_FILTER |
-- 作者:卷积内核 -- 发布时间:12/18/2008 9:53:00 AM -- 接下来的Create()函数从接口ID3DXFileData创建网格模型: // Cleanup previous mesh if any // Load the mesh from the DXFILEDATA object // Optimize the mesh for performance D3DXMATERIAL* d3dxMtrls = (D3DXMATERIAL*)pMtrlBuffer->GetBufferPointer(); SAFE_RELEASE( pAdjacencyBuffer ); // Extract data from m_pMesh for easy access m_dwNumVertices = m_pMesh->GetNumVertices(); m_pMesh->GetIndexBuffer( &m_pIB ); pd3dDevice->CreateVertexDeclaration( decl, &m_pDecl ); return hr;
该函数与上一个Create()函数并无太大区别,只是在加载网格模型时调用D3DXLoadMeshFromXof()而不是D3DXLoadMeshFromXW()。 最后一个Create()函数从输入的网格模型中创建新的网格模型: // Optimize the mesh for performance DWORD *rgdwAdjacency = NULL; rgdwAdjacency = new DWORD[pInMesh->GetNumFaces() * 3]; pInMesh->GenerateAdjacency(1e-6f, rgdwAdjacency); D3DVERTEXELEMENT9 decl[MAX_FVF_DECL_SIZE]; DWORD dwOptions = pInMesh->GetOptions(); dwOptions &= ~(D3DXMESH_32BIT | D3DXMESH_SYSTEMMEM | D3DXMESH_WRITEONLY); ID3DXMesh* pTempMesh = NULL; if( FAILED( pInMesh->Optimize( dwOptions, rgdwAdjacency, NULL, NULL, NULL, &pTempMesh ) ) ) SAFE_DELETE_ARRAY( rgdwAdjacency ); m_pMesh = pTempMesh; HRESULT hr = CreateMaterials( L"", pd3dDevice, pd3dxMaterials, dwMaterials );; // Extract data from m_pMesh for easy access m_dwNumVertices = m_pMesh->GetNumVertices(); m_pMesh->GetIndexBuffer( &m_pIB ); pd3dDevice->CreateVertexDeclaration( decl, &m_pDecl ); return hr;
为了优化网格的渲染,函数首先生成网格面邻接信息缓存: Generate a list of mesh edges, as well as a list of faces that share each edge. HRESULT GenerateAdjacency( FLOAT Epsilon, DWORD * pAdjacency); Remarks The order of the entries in the adjacency buffer is determined by the order of the vertex indices in the index buffer. The adjacent triangle 0 always corresponds to the edge between the indices of the corners 0 and 1. The adjacent triangle 1 always corresponds to the edge between the indices of the corners 1 and 2 while the adjacent triangle 2 corresponds to the edge between the indices of the corners 2 and 0. 为了优化网格的渲染,函数去除了32位索引、系统内存的使用、只写访问,增加了托管内存的使用,以及去除了无用的顶点和索引项、根据属性给三角形排序并调整属性表,增加了顶点缓存的命中率,并调用Optimize()对网格模型进行优化,优化后的网格模型存储在pTempMesh中: DWORD dwOptions = pInMesh->GetOptions(); SAFE_DELETE_ARRAY( rgdwAdjacency ); SAFE_RELEASE( m_pMesh ); // Extract data from m_pMesh for easy access Create a vertex shader declaration from the device and the vertex elements. HRESULT CreateVertexDeclaration( CONST D3DVERTEXELEMENT9* pVertexElements, IDirect3DVertexDeclaration9** ppDecl); Remarks
函数Destroy()用来在程序退出时销毁指定的资源: HRESULT CDXUTMesh::Destroy(){ InvalidateDeviceObjects(); |
-- 作者:卷积内核 -- 发布时间:12/18/2008 9:54:00 AM -- 第三类是设置渲染选项函数。其中UseMeshMaterials()函数用于设置是否使用网格模型自身的材质和纹理进行渲染,如果调用该函数并将参数m_bUseMaterials设置为TRUE,则在渲染时使用网格模型自身的材质和纹理,这也是默认资源,如果将参数m_bUseMaterials设置为FALSE,则在渲染时不使用网格模型自身的材质和纹理,自然就使用当前为Direct3D设置的材质和纹理渲染网格模型。 void UseMeshMaterials( bool bFlag ) { m_bUseMaterials = bFlag; }
SetFVF()函数将网格模型克隆为参数dwFVF指定的灵活顶点格式: HRESULT CDXUTMesh::SetFVF( LPDIRECT3DDEVICE9 pd3dDevice, DWORD dwFVF ){ LPD3DXMESH pTempMesh = NULL; Computes unit normals for each vertex in a mesh. Provided to support legacy applications. Use D3DXComputeTangentFrameEx for better results. HRESULT D3DXComputeNormals( LPD3DXBASEMESH pMesh, CONST DWORD * pAdjacency); Remarks A normal for a vertex is generated by averaging the normals of all faces that share that vertex. If adjacency is provided, replicated vertices are ignored and "smoothed" over. If adjacency is not provided, replicated vertices will have normals averaged in from only the faces explicitly referencing them. This function simply calls D3DXComputeTangentFrameEx with the following input parameters: D3DXComputeTangentFrameEx( pMesh, D3DX_DEFAULT, 0, D3DX_DEFAULT, 0, D3DX_DEFAULT, 0, D3DDECLUSAGE_NORMAL, 0, D3DXTANGENT_GENERATE_IN_PLACE | D3DXTANGENT_CALCULATE_NORMALS, pAdjacency, -1.01f, -0.01f, -1.01f, NULL, NULL); if( m_pMesh ) // Check if the old declaration contains a normal. bool bHadNormal = false; D3DVERTEXELEMENT9 aOldDecl[MAX_FVF_DECL_SIZE]; if( m_pMesh && SUCCEEDED( m_pMesh->GetDeclaration( aOldDecl ) ) ) // Check if the new declaration contains a normal. bool bHaveNormalNow = false; D3DVERTEXELEMENT9 aNewDecl[MAX_FVF_DECL_SIZE]; if( pTempMesh && SUCCEEDED( pTempMesh->GetDeclaration( aNewDecl ) ) ) if( aNewDecl[index].Usage == D3DDECLUSAGE_TANGENT ) SAFE_RELEASE( m_pMesh ); if( pTempMesh ) if( !bHadNormal && bHaveNormalNow && bAutoComputeNormals ) if( bHaveNormalNow && !bHadTangent && bHaveTangentNow && bAutoComputeTangents ) DWORD *rgdwAdjacency = NULL; rgdwAdjacency = new DWORD[m_pMesh->GetNumFaces() * 3]; V( m_pMesh->GenerateAdjacency(1e-6f, rgdwAdjacency) ); float fPartialEdgeThreshold; if( bSplitVertexForOptimalTangents ) // Compute tangents, which are required for normal mapping SAFE_DELETE_ARRAY( rgdwAdjacency ); SAFE_RELEASE( m_pMesh ); return S_OK;
函数首先调用CloneMesh()以指定的顶点声明方式来克隆网格模型: // Check if the old declaration contains a normal. // Check if the new declaration contains a normal. SAFE_RELEASE( m_pMesh ); if( bHaveNormalNow && !bHadTangent && bHaveTangentNow && bAutoComputeTangents ) { ID3DXMesh* pNewMesh; HRESULT hr; D3DXComputeTangentFrameEx()的声明如下: Performs tangent frame computations on a mesh. Tangent, binormal, and optionally normal vectors are generated. Singularities are handled as required by grouping edges and splitting vertices. HRESULT D3DXComputeTangentFrameEx( ID3DXMesh * pMesh, DWORD dwTextureInSemantic, DWORD dwTextureInIndex, DWORD dwUPartialOutSemantic, DWORD dwUPartialOutIndex, DWORD dwVPartialOutSemantic, DWORD dwVPartialOutIndex, DWORD dwNormalOutSemantic, DWORD dwNormalOutIndex, DWORD dwOptions, CONST DWORD * pdwAdjacency, FLOAT fPartialEdgeThreshold, FLOAT fSingularPointThreshold, FLOAT fNormalEdgeThreshold, ID3DXMesh ** ppMeshOut, ID3DXBuffer ** ppVertexMapping); Description D3DXTANGENT Flag Value pdwAdjacency Remarks The computed normal vector at each vertex is always normalized to have unit length. The most robust solution for computing orthogonal Cartesian coordinates is to not set flags D3DXTANGENT_ORTHOGONALIZE_FROM_U and D3DXTANGENT_ORTHOGONALIZE_FROM_V, so that orthogonal coordinates are computed from both texture coordinates u and v. However, in this case, if either u or v is zero, then the function will compute orthogonal coordinates using D3DXTANGENT_ORTHOGONALIZE_FROM_V or D3DXTANGENT_ORTHOGONALIZE_FROM_U, respectively.
|
-- 作者:卷积内核 -- 发布时间:12/18/2008 9:54:00 AM -- 最后一类是渲染函数,类CDXUTMesh重载了两个渲染函数Render(),其作用都是用来渲染当前的网格模型。所不同的是,第一个函数用在固定函数流水线中,第二个函数用在可编程流水线技术中,这两个函数的最后两个参数用于指定是否渲染网格模型中的不透明和半透明部分。 首先来看第一个Render()函数: // Frist, draw the subsets without alpha pd3dDevice->SetMaterial( &m_pMaterials[i] ); m_pMesh->DrawSubset( i ); // Then, draw the subsets with alpha // Set the material and texture return S_OK;
代码简洁明了,首先绘制不透明的网格(alpha == 1.0f),接着绘制半透明的网格(alpha != 1.0f)。 接着来看第二个Render()函数: UINT cPasses; // Frist, draw the subsets without alpha for( UINT p = 0; p < cPasses; ++p ) for( DWORD i=0; i<m_dwNumMaterials; i++ ) if( hTexture ) // D3DCOLORVALUE and D3DXVECTOR4 are data-wise identical. if( hDiffuse ) if( hAmbient ) if( hSpecular ) if( hEmissive ) if( hPower ) pEffect->CommitChanges(); m_pMesh->DrawSubset( i ); pEffect->EndPass(); pEffect->End(); // Then, draw the subsets with alpha for( UINT p = 0; p < cPasses; ++p ) for( DWORD i=0; i<m_dwNumMaterials; i++ ) if( hTexture ) // D3DCOLORVALUE and D3DXVECTOR4 are data-wise identical. if( hDiffuse ) if( hAmbient ) if( hSpecular ) if( hEmissive ) if( hPower ) pEffect->CommitChanges(); m_pMesh->DrawSubset( i ); pEffect->EndPass(); pEffect->End(); return S_OK;
代码也是相当简洁明了,首先绘制不透明网格,接着绘制半透明网格。在绘制时遍历所有的通道,并设置纹理,材质的漫反射光、环境光、镜面反射光、自发光、镜面反射光指数。 |
W 3 C h i n a ( since 2003 ) 旗 下 站 点 苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》 |
515.625ms |