以文本方式查看主题

-  计算机科学论坛  (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中,其定义如下:


//-----------------------------------------------------------------------------
// Name: class CDXUTMesh
// Desc: Class for loading and rendering file-based meshes
//-----------------------------------------------------------------------------
class CDXUTMesh
{
public:
    WCHAR                            m_strName[512];
    LPD3DXMESH                        m_pMesh;   // Managed mesh
    
    // Cache of data in m_pMesh for easy access

    IDirect3DVertexBuffer9*            m_pVB;
    IDirect3DIndexBuffer9*            m_pIB;
    IDirect3DVertexDeclaration9*    m_pDecl;
    DWORD                           m_dwNumVertices;
    DWORD                           m_dwNumFaces;
    DWORD                           m_dwBytesPerVertex;

    DWORD                           m_dwNumMaterials; // Materials for the mesh
    D3DMATERIAL9*                   m_pMaterials;
    CHAR                            (*m_strMaterials)[MAX_PATH];
    IDirect3DBaseTexture9**         m_pTextures;
    bool                            m_bUseMaterials;

public:
    // Rendering
    HRESULT Render( LPDIRECT3DDEVICE9 pd3dDevice,
                    bool bDrawOpaqueSubsets = true,
                    bool bDrawAlphaSubsets = true );

    HRESULT Render( ID3DXEffect *pEffect,
                    D3DXHANDLE hTexture = NULL,
                    D3DXHANDLE hDiffuse = NULL,
                    D3DXHANDLE hAmbient = NULL,
                    D3DXHANDLE hSpecular = NULL,
                    D3DXHANDLE hEmissive = NULL,
                    D3DXHANDLE hPower = NULL,
                    bool bDrawOpaqueSubsets = true,
                    bool bDrawAlphaSubsets = true );

    // Mesh access
    LPD3DXMESH GetMesh() { return m_pMesh; }

    // Rendering options
    void    UseMeshMaterials( bool bFlag ) { m_bUseMaterials = bFlag; }
    HRESULT SetFVF( LPDIRECT3DDEVICE9 pd3dDevice, DWORD dwFVF );
    HRESULT SetVertexDecl( LPDIRECT3DDEVICE9 pd3dDevice, const D3DVERTEXELEMENT9 *pDecl,
                           bool bAutoComputeNormals = true, bool bAutoComputeTangents = true,
                           bool bSplitVertexForOptimalTangents = false );

    // Initializing
    HRESULT RestoreDeviceObjects( LPDIRECT3DDEVICE9 pd3dDevice );
    HRESULT InvalidateDeviceObjects();

    // Creation/destruction
    HRESULT Create( LPDIRECT3DDEVICE9 pd3dDevice, LPCWSTR strFilename );
    HRESULT Create( LPDIRECT3DDEVICE9 pd3dDevice, LPD3DXFILEDATA pFileData );
    HRESULT Create(LPDIRECT3DDEVICE9 pd3dDevice, ID3DXMesh* pInMesh, D3DXMATERIAL* pd3dxMaterials, DWORD dwMaterials);

    HRESULT CreateMaterials(LPCWSTR strPath, IDirect3DDevice9 *pd3dDevice,
                            D3DXMATERIAL* d3dxMtrls, DWORD dwNumMaterials);
    HRESULT Destroy();

    CDXUTMesh( LPCWSTR strName = L"CDXUTMeshFile_Mesh" );
    virtual ~CDXUTMesh();
};

该类中包含的成员函数按其作用可分为6类。
第一类是构造和析构函数,函数CDXUTMesh()和~CDXUTMesh()分别是该类的构造函数和析构函数,其作用分别是进行一些初始化工作以及在类CDXUTMesh的对象被销毁时完成最后的销毁工作。

CDXUTMesh::CDXUTMesh( LPCWSTR strName ){    StringCchCopy( m_strName, 512, strName );
    m_pMesh              = NULL;    m_pMaterials         = NULL;    m_pTextures          = NULL;    m_bUseMaterials      = TRUE;    m_pVB                = NULL;    m_pIB                = NULL;    m_pDecl              = NULL;    m_strMaterials       = NULL;    m_dwNumMaterials     = 0;    m_dwNumVertices      = 0;    m_dwNumFaces         = 0;    m_dwBytesPerVertex   = 0;}
CDXUTMesh::~CDXUTMesh(){    Destroy();}

第二类是获取网格函数,它仅包含一个函数GetMesh(),实现也非常简单,即返回类CDXUTMesh的成员变量m_pMesh。
LPD3DXMESH GetMesh() { return m_pMesh; }

第三类是设备恢复和丢失时所采取的操作函数,这里所包含的两个成员函数RestoreDeviceObjects()和InvalidateDeviceObjects()分别是在设备恢复和丢失时调用,用于恢复和释放相应的资源。

HRESULT CDXUTMesh::RestoreDeviceObjects( LPDIRECT3DDEVICE9 pd3dDevice ){    return S_OK;}
HRESULT CDXUTMesh::InvalidateDeviceObjects(){    SAFE_RELEASE( m_pIB );    SAFE_RELEASE( m_pVB );    SAFE_RELEASE( m_pDecl );
    return S_OK;}
第四类是创建和销毁函数,这里首先重载了3个创建网格模型函数Create(),它们依次用于从指定的.x文件创建网格模型,从接口ID3DXFileData创建网格模型,从输入的网格模型中创建新的网格模型。函数CreateMaterials()用于创建网格模型中所需的材质和纹理。函数Destroy()用来在程序退出时销毁指定的资源。

来看第一个Create()函数的实现:


HRESULT CDXUTMesh::Create( LPDIRECT3DDEVICE9 pd3dDevice, LPCWSTR strFilename )
{
    WCHAR        strPath[MAX_PATH];
    LPD3DXBUFFER pAdjacencyBuffer = NULL;
    LPD3DXBUFFER pMtrlBuffer = NULL;
    HRESULT      hr;

    // Cleanup previous mesh if any
    Destroy();

    // Find the path for the file, and convert it to ANSI (for the D3DX API)
    DXUTFindDXSDKMediaFileCch( strPath, sizeof(strPath) / sizeof(WCHAR), strFilename );

    // Load the mesh
    if(FAILED(hr = D3DXLoadMeshFromXW(strPath, D3DXMESH_MANAGED, pd3dDevice, &pAdjacencyBuffer, &pMtrlBuffer, NULL,
                                      &m_dwNumMaterials, &m_pMesh)))
    {
        return hr;
    }

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

    WCHAR* pLastBSlash = wcsrchr( strPath, L'\\' );

    if( pLastBSlash )
        *(pLastBSlash + 1) = L'\0';
    else
        *strPath = L'\0';

    D3DXMATERIAL* d3dxMtrls = (D3DXMATERIAL*) pMtrlBuffer->GetBufferPointer();
    hr = CreateMaterials( strPath, pd3dDevice, d3dxMtrls, m_dwNumMaterials );

    SAFE_RELEASE( pAdjacencyBuffer );
    SAFE_RELEASE( pMtrlBuffer );

    // Extract data from m_pMesh for easy access

    D3DVERTEXELEMENT9 decl[MAX_FVF_DECL_SIZE];

    m_dwNumVertices    = m_pMesh->GetNumVertices();
    m_dwNumFaces       = m_pMesh->GetNumFaces();
    m_dwBytesPerVertex = m_pMesh->GetNumBytesPerVertex();

    m_pMesh->GetIndexBuffer( &m_pIB );
    m_pMesh->GetVertexBuffer( &m_pVB );
    m_pMesh->GetDeclaration( decl );

    pd3dDevice->CreateVertexDeclaration( decl, &m_pDecl );

    return hr;
}

函数首先销毁旧的资源,并调用DXUTFindDXSDKMediaFileCch()通过文件名查找文件所在的路径,接着调用D3DXLoadMeshFromXW()从文件中加载网格模型。
DXUTFindDXSDKMediaFileCch()的实现分析请参阅DXUT源码分析 ---- 媒体文件查找函数。

    WCHAR        strPath[MAX_PATH];    LPD3DXBUFFER pAdjacencyBuffer = NULL;    LPD3DXBUFFER pMtrlBuffer = NULL;    HRESULT      hr;
    // Cleanup previous mesh if any    Destroy();
    // Find the path for the file, and convert it to ANSI (for the D3DX API)    DXUTFindDXSDKMediaFileCch( strPath, sizeof(strPath) / sizeof(WCHAR), strFilename );
    // Load the mesh    if(FAILED(hr = D3DXLoadMeshFromXW(strPath, D3DXMESH_MANAGED, pd3dDevice, &pAdjacencyBuffer, &pMtrlBuffer, NULL,                                      &m_dwNumMaterials, &m_pMesh)))    {        return hr;    }
接着调用OptimizeInplace()对网格模型进行优化,该函数调用时第一个参数的含义如下:

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;    }
接下来,函数将模型文件所在的路径存储在strPath,如果没有路径,则strPath置为NULL。

   // Set strPath to the path of the mesh file
    WCHAR* pLastBSlash = wcsrchr( strPath, L'\\' );
    if( pLastBSlash )        *(pLastBSlash + 1) = L'\0';    else        *strPath = L'\0';
接下来,函数调用CreateMaterials()创建存储材质和纹理的内存,并释放邻接信息缓存和材质缓存。

    D3DXMATERIAL* d3dxMtrls = (D3DXMATERIAL*) pMtrlBuffer->GetBufferPointer();    hr = CreateMaterials( strPath, pd3dDevice, d3dxMtrls, m_dwNumMaterials );
    SAFE_RELEASE( pAdjacencyBuffer );    SAFE_RELEASE( pMtrlBuffer );
最后,函数获取模型的顶点数,面数,每个顶点所占的字节大小,顶点索引缓存,顶点缓存,顶点声明,以方便以后访问。

    // Extract data from m_pMesh for easy access
    m_dwNumVertices    = m_pMesh->GetNumVertices();    m_dwNumFaces       = m_pMesh->GetNumFaces();    m_dwBytesPerVertex = m_pMesh->GetNumBytesPerVertex();
    m_pMesh->GetIndexBuffer( &m_pIB );    m_pMesh->GetVertexBuffer( &m_pVB );    m_pMesh->GetDeclaration( decl );
    pd3dDevice->CreateVertexDeclaration( decl, &m_pDecl );
    return hr;


--  作者:卷积内核
--  发布时间:12/18/2008 9:53:00 AM

--  
函数CreateMaterials()用于创建网格模型中所需的材质和纹理,我们来看看CreateMaterials()的实现:


HRESULT CDXUTMesh::CreateMaterials( LPCWSTR strPath, IDirect3DDevice9 *pd3dDevice,
                                    D3DXMATERIAL* d3dxMtrls, DWORD dwNumMaterials )
{
    // Get material info for the mesh, get the array of materials out of the buffer.

    m_dwNumMaterials = dwNumMaterials;

    if( d3dxMtrls && m_dwNumMaterials > 0 )
    {
        // Allocate memory for the materials and textures

        m_pMaterials = new D3DMATERIAL9[m_dwNumMaterials];
        if( m_pMaterials == NULL )
            return E_OUTOFMEMORY;

        m_pTextures = new LPDIRECT3DBASETEXTURE9[m_dwNumMaterials];
        if( m_pTextures == NULL )
            return E_OUTOFMEMORY;

        m_strMaterials = new CHAR[m_dwNumMaterials][MAX_PATH];
        if( m_strMaterials == NULL )
            return E_OUTOFMEMORY;

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

            m_pTextures[i]          = NULL;

            // Create a texture
            if( d3dxMtrls[i].pTextureFilename )
            {
                StringCchCopyA( m_strMaterials[i], MAX_PATH, d3dxMtrls[i].pTextureFilename );

                WCHAR strTexture[MAX_PATH];
                WCHAR strTextureTemp[MAX_PATH];
                D3DXIMAGE_INFO ImgInfo;

                // 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 );
                strTextureTemp[MAX_PATH-1] = 0;

                StringCchCopy( strTexture, MAX_PATH, strPath );
                StringCchCat( strTexture, MAX_PATH, strTextureTemp );

                // 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.

                    D3DXGetImageInfoFromFile( strTexture, &ImgInfo );
                }

                // Call the appropriate loader according to the texture type.
                switch( ImgInfo.ResourceType )
                {
                    case D3DRTYPE_TEXTURE:
                    {
                        IDirect3DTexture9 *pTex;

                        if( SUCCEEDED( D3DXCreateTextureFromFileW( pd3dDevice, strTexture, &pTex ) ) )
                        {
                            // Obtain the base texture interface
                            pTex->QueryInterface( IID_IDirect3DBaseTexture9, (LPVOID*)&m_pTextures[i] );

                            // Release the specialized instance
                            pTex->Release();
                        }

                        break;
                    }

                    case D3DRTYPE_CUBETEXTURE:
                    {
                        IDirect3DCubeTexture9 *pTex;

                        if( SUCCEEDED( D3DXCreateCubeTextureFromFile( pd3dDevice, strTexture, &pTex ) ) )
                        {
                            // Obtain the base texture interface
                            pTex->QueryInterface( IID_IDirect3DBaseTexture9, (LPVOID*)&m_pTextures[i] );

                            // Release the specialized instance
                            pTex->Release();
                        }

                        break;
                    }

                    case D3DRTYPE_VOLUMETEXTURE:
                    {
                        IDirect3DVolumeTexture9 *pTex;

                        if( SUCCEEDED( D3DXCreateVolumeTextureFromFile( pd3dDevice, strTexture, &pTex ) ) )
                        {
                            // Obtain the base texture interface
                            pTex->QueryInterface( IID_IDirect3DBaseTexture9, (LPVOID*)&m_pTextures[i] );

                            // Release the specialized instance
                            pTex->Release();
                        }

                        break;
                    }
                }
            }
        }
    }

    return S_OK;
}

函数首先根据材质数来创建相应的材质缓存、纹理缓存、纹理文件名缓存:
    m_dwNumMaterials = dwNumMaterials;
    if( d3dxMtrls && m_dwNumMaterials > 0 )    {        // Allocate memory for the materials and textures
        m_pMaterials = new D3DMATERIAL9[m_dwNumMaterials];        if( m_pMaterials == NULL )            return E_OUTOFMEMORY;
        m_pTextures = new LPDIRECT3DBASETEXTURE9[m_dwNumMaterials];        if( m_pTextures == NULL )            return E_OUTOFMEMORY;
        m_strMaterials = new CHAR[m_dwNumMaterials][MAX_PATH];        if( m_strMaterials == NULL )            return E_OUTOFMEMORY;
接下来复制材质数据:

        // 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
            m_pTextures[i]          = NULL;
如果模型中指定纹理文件名则创建纹理缓存,首先将pTextureFilename复制到m_strMaterials中,接着根据strPath和strTextureTemp生成纹理图的完整路径(可能是绝对路径也可能是相对路径):

            // Create a texture            if( d3dxMtrls[i].pTextureFilename )            {                StringCchCopyA( m_strMaterials[i], MAX_PATH, d3dxMtrls[i].pTextureFilename );
                WCHAR strTexture[MAX_PATH];                WCHAR strTextureTemp[MAX_PATH];                D3DXIMAGE_INFO ImgInfo;
                // 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 );                strTextureTemp[MAX_PATH-1] = 0;
                StringCchCopy( strTexture, MAX_PATH, strPath );                StringCchCat( strTexture, MAX_PATH, strTextureTemp );
StringCchCat()的声明如下:

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.
Syntax

HRESULT StringCchCat(          LPTSTR pszDest,    size_t cchDest,    LPCTSTR pszSrc);
Parameters

pszDest
[in, out] Pointer to a buffer containing the string to which pszSrc is concatenated, and which contains the entire resultant string. The string at pszSrc is added to the end of the string at pszDest.
cchDest
[in] Size of the destination buffer, in characters. This value must equal the length of pszSrc plus the length of pszDest plus 1 to account for both strings and the terminating null character. The maximum number of characters allowed is STRSAFE_MAX_CCH.
pszSrc
[in] Pointer to a buffer containing the source string that is concatenated to the end of pszDest. This source string must be null-terminated.
Return Value

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.
STRSAFE_E_INVALID_PARAMETER The value in cchDest is either 0 or larger than STRSAFE_MAX_CCH, or the destination buffer is already full.
STRSAFE_E_INSUFFICIENT_BUFFER The concatenation operation failed due to insufficient buffer space. The destination buffer contains a truncated, null-terminated version of the intended result. In situations where truncation is acceptable, this may not necessarily be seen as a failure condition.

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
char "string" StringCchCatA
TCHAR TEXT("string") StringCchCat
WCHAR L"string" StringCchCatW
  
StringCchCat and its ANSI and Unicode variants are replacements for these functions:

strcat
wcscat
lstrcat
strcat
StrCatBuff
_tcscat
_ftcscat
Behavior is undefined if the strings pointed to by pszSrc and pszDest overlap.

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.
                    D3DXGetImageInfoFromFile( strTexture, &ImgInfo );                }
D3DXGetImageInfoFromFile()的声明如下:

Retrieves information about a given image file.

HRESULT D3DXGetImageInfoFromFile(  LPCSTR pSrcFile,  D3DXIMAGE_INFO * pSrcInfo);
Parameters
pSrcFile
[in] File name of image to retrieve information about. If UNICODE or _UNICODE are defined, this parameter type is LPCWSTR, otherwise, the type is LPCSTR.
pSrcInfo
[in] Pointer to a D3DXIMAGE_INFO structure to be filled with the description of the data in the source file.
Return Values
If the function succeeds, the return value is D3D_OK. If the function fails, the return value can be the following: D3DERR_INVALIDCALL

Remarks
This function supports both Unicode and ANSI strings.

D3DXIMAGE_INFO
Returns a description of the original contents of an image file.

typedef struct D3DXIMAGE_INFO {    UINT Width;    UINT Height;    UINT Depth;    UINT MipLevels;    D3DFORMAT Format;    D3DRESOURCETYPE ResourceType;    D3DXIMAGE_FILEFORMAT ImageFileFormat;} D3DXIMAGE_INFO, *LPD3DXIMAGE_INFO;
Members
Width
Width of original image in pixels.
Height
Height of original image in pixels.
Depth
Depth of original image in pixels.
MipLevels
Number of mip levels in original image.
Format
A value from the D3DFORMAT enumerated type that most closely describes the data in the original image.
ResourceType
Represents the type of the texture stored in the file. It is either D3DRTYPE_TEXTURE, D3DRTYPE_VOLUMETEXTURE, or D3DRTYPE_CubeTexture.
ImageFileFormat
Represents the format of the image file.
D3DXIMAGE_FILEFORMAT
Describes the supported image file formats. See Remarks for descriptions of these formats.

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;
Constants
D3DXIFF_BMP
Windows bitmap (BMP) file format.
D3DXIFF_JPG
Joint Photographics Experts Group (JPEG) compressed file format.
D3DXIFF_TGA
Truevision (Targa, or TGA) image file format.
D3DXIFF_PNG
Portable Network Graphics (PNG) file format.
D3DXIFF_DDS
DirectDraw surface (DDS) file format.
D3DXIFF_PPM
Portable pixmap (PPM) file format.
D3DXIFF_DIB
Windows device-independent bitmap (DIB) file format.
D3DXIFF_HDR
High dynamic range (HDR) file format.
D3DXIFF_PFM
Portable float map file format.
D3DXIFF_FORCE_DWORD
Forces this enumeration to compile to 32 bits in size. Without this value, some compilers would allow this enumeration to compile to a size other than 32 bits. This value is not used.
Remarks
Functions that begin with D3DXLoadxxx support all of the formats listed. Functions that begin with D3DXSavexxx support all but the Truevision (.tga) and portable pixmap (.ppm) formats.

The following table lists the available input and output formats.

File Extension Description
.bmp Windows bitmap format. Contains a header that describes the resolution of the device on which the rectangle of pixels was created, the dimensions of the rectangle, the size of the array of bits, a logical palette, and an array of bits that defines the relationship between pixels in the bitmapped image and entries in the logical palette.
.dds DirectDraw Surface file format. Stores textures, volume textures, and cubic environment maps, with or without mipmap levels, and with or without pixel compression. See DDS File Reference.
.dib Windows DIB. Contains an array of bits combined with structures that specify width and height of the bitmapped image, color format of the device where the image was created, and resolution of the device used to create that image.
.hdr HDR format. Encodes each pixel as an RGBE 32-bit color, with 8 bits of mantissa for red, green, and blue, and a shared 8-bit exponent. Each channel is separately compressed with run-length encoding (RLE).
.jpg JPEG standard. Specifies variable compression of 24-bit RGB color and 8-bit gray-scale Tagged Image File Format (TIFF) image document files.
.pfm Portable float map format. A raw floating point image format, without any compression. The file header specifies image width, height, monochrome or color, and machine word order. Pixel data is stored as 32-bit floating point values, with 3 values per pixel for color, and one value per pixel for monochrome.
.png PNG format. A non-proprietary bitmap format using lossless compression.
.ppm Portable Pixmap format. A binary or ASCII file format for color images that includes image height and width and the maximum color component value.
.tga Targa or Truevision Graphics Adapter format. Supports depths of 8, 15, 16, 24, and 32 bits, including 8-bit gray scale, and contains optional color palette data, image (x, y) origin and size data, and pixel data.

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;
                        if( SUCCEEDED( D3DXCreateTextureFromFileW( pd3dDevice, strTexture, &pTex ) ) )                        {                            // Obtain the base texture interface                            pTex->QueryInterface( IID_IDirect3DBaseTexture9, (LPVOID*)&m_pTextures[i] );
                            // Release the specialized instance                            pTex->Release();                        }
                        break;                    }
QueryInterface()的声明如下:

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);
Parameters
riid
Reference identifier of the interface being requested.
ppvObj
Address of a pointer to fill with the interface pointer if the query succeeds.
Return Values
If the method succeeds, the return value is S_OK.

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
If the application does not need to use the interface retrieved by a call to this method, it must call the IUnknown::Release method for that interface to free it. The IUnknown::QueryInterface method makes it possible to extend objects without interfering with functionality.

如果立方体纹理资源则调用D3DXCreateCubeTextureFromFile()来创建立方体纹理缓存,如果是D3DRTYPE_VOLUMETEXTURE类型的资源,则调用D3DXCreateVolumeTextureFromFile()来创建该类型的纹理资源:

                    case D3DRTYPE_CUBETEXTURE:                    {                        IDirect3DCubeTexture9 *pTex;
                        if( SUCCEEDED( D3DXCreateCubeTextureFromFile( pd3dDevice, strTexture, &pTex ) ) )                        {                            // Obtain the base texture interface                            pTex->QueryInterface( IID_IDirect3DBaseTexture9, (LPVOID*)&m_pTextures[i] );
                            // Release the specialized instance                            pTex->Release();                        }
                        break;                    }
                    case D3DRTYPE_VOLUMETEXTURE:                    {                        IDirect3DVolumeTexture9 *pTex;
                        if( SUCCEEDED( D3DXCreateVolumeTextureFromFile( pd3dDevice, strTexture, &pTex ) ) )                        {                            // Obtain the base texture interface                            pTex->QueryInterface( IID_IDirect3DBaseTexture9, (LPVOID*)&m_pTextures[i] );
                            // Release the specialized instance                            pTex->Release();                        }
                        break;                    }
D3DXCreateCubeTextureFromFile()的声明如下:

Creates a cube texture from a file.

HRESULT D3DXCreateCubeTextureFromFile(  LPDIRECT3DDEVICE9 pDevice,  LPCTSTR pSrcFile,  LPDIRECT3DCUBETEXTURE9 * ppCubeTexture);
Parameters
pDevice
[in] Pointer to an IDirect3DDevice9 interface, representing the device to be associated with the cube texture.
pSrcFile
[in] Pointer to a string that specifies the filename. If the compiler settings require Unicode, the data type LPCTSTR resolves to LPCWSTR. Otherwise, the string data type resolves to LPCSTR. See Remarks.
ppCubeTexture
[out] Address of a pointer to an IDirect3DCubeTexture9 interface, representing the created cube texture object.
Return Values
If the function succeeds, the return value is D3D_OK. If the function fails, the return value can be one of the following values: D3DERR_INVALIDCALL.

D3DERR_NOTAVAILABLED3DERR_OUTOFVIDEOMEMORYD3DXERR_INVALIDDATAE_OUTOFMEMORY
Remarks
The compiler setting also determines the function version. If Unicode is defined, the function call resolves to D3DXCreateCubeTextureFromFileW. Otherwise, the function call resolves to D3DXCreateCubeTextureFromFileA because ANSI strings are being used.

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);
Parameters
pDevice
[in] Pointer to an IDirect3DDevice9 interface, representing the device to be associated with the volume texture.
pSrcFile
[in] Pointer to a string that specifies the file name. If the compiler settings require Unicode, the data type LPCTSTR resolves to LPCWSTR. Otherwise, the string data type resolves to LPCSTR. See Remarks.
ppVolumeTexture
[out] Address of a pointer to an IDirect3DVolumeTexture9 interface representing the created texture object.
Return Values
If the function succeeds, the return value is D3D_OK. If the function fails, the return value can be one of the following:

D3DERR_NOTAVAILABLED3DERR_OUTOFVIDEOMEMORYD3DERR_INVALIDCALLD3DXERR_INVALIDDATAE_OUTOFMEMORY
Remarks
The compiler setting also determines the function version. If Unicode is defined, the function call resolves to D3DXCreateVolumeTextureFromFileW. Otherwise, the function call resolves to D3DXCreateVolumeTextureFromFileA because ANSI strings are being used.

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创建网格模型:


HRESULT CDXUTMesh::Create( LPDIRECT3DDEVICE9 pd3dDevice, LPD3DXFILEDATA pFileData )
{
    LPD3DXBUFFER pMtrlBuffer = NULL;
    LPD3DXBUFFER pAdjacencyBuffer = NULL;
    HRESULT      hr;

    // Cleanup previous mesh if any
    Destroy();

    // Load the mesh from the DXFILEDATA object
    if( FAILED( hr = D3DXLoadMeshFromXof( pFileData, D3DXMESH_MANAGED, pd3dDevice, &pAdjacencyBuffer, &pMtrlBuffer,
                                          NULL, &m_dwNumMaterials, &m_pMesh ) ) )
    {
        return hr;
    }

    // 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;
    }

    D3DXMATERIAL* d3dxMtrls = (D3DXMATERIAL*)pMtrlBuffer->GetBufferPointer();
    hr = CreateMaterials( L"", pd3dDevice, d3dxMtrls, m_dwNumMaterials );

    SAFE_RELEASE( pAdjacencyBuffer );
    SAFE_RELEASE( pMtrlBuffer );

    // Extract data from m_pMesh for easy access
    D3DVERTEXELEMENT9 decl[MAX_FVF_DECL_SIZE];

    m_dwNumVertices    = m_pMesh->GetNumVertices();
    m_dwNumFaces       = m_pMesh->GetNumFaces();
    m_dwBytesPerVertex = m_pMesh->GetNumBytesPerVertex();

    m_pMesh->GetIndexBuffer( &m_pIB );
    m_pMesh->GetVertexBuffer( &m_pVB );
    m_pMesh->GetDeclaration( decl );

    pd3dDevice->CreateVertexDeclaration( decl, &m_pDecl );

    return hr;
}

该函数与上一个Create()函数并无太大区别,只是在加载网格模型时调用D3DXLoadMeshFromXof()而不是D3DXLoadMeshFromXW()。

最后一个Create()函数从输入的网格模型中创建新的网格模型:


HRESULT CDXUTMesh::Create( LPDIRECT3DDEVICE9 pd3dDevice, ID3DXMesh* pInMesh,
                           D3DXMATERIAL* pd3dxMaterials, DWORD dwMaterials )
{
    // Cleanup previous mesh if any
    Destroy();

    // Optimize the mesh for performance

    DWORD *rgdwAdjacency = NULL;

    rgdwAdjacency = new DWORD[pInMesh->GetNumFaces() * 3];
    if( rgdwAdjacency == NULL )
        return E_OUTOFMEMORY;

    pInMesh->GenerateAdjacency(1e-6f, rgdwAdjacency);

    D3DVERTEXELEMENT9 decl[MAX_FVF_DECL_SIZE];
    pInMesh->GetDeclaration( decl );

    DWORD dwOptions = pInMesh->GetOptions();

    dwOptions &= ~(D3DXMESH_32BIT | D3DXMESH_SYSTEMMEM | D3DXMESH_WRITEONLY);
    dwOptions |= D3DXMESH_MANAGED;
    dwOptions |= D3DXMESHOPT_COMPACT | D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_VERTEXCACHE;

    ID3DXMesh* pTempMesh = NULL;

    if( FAILED( pInMesh->Optimize( dwOptions, rgdwAdjacency, NULL, NULL, NULL, &pTempMesh ) ) )
    {
        SAFE_DELETE_ARRAY( rgdwAdjacency );
        return E_FAIL;
    }

    SAFE_DELETE_ARRAY( rgdwAdjacency );
    SAFE_RELEASE( m_pMesh );

    m_pMesh = pTempMesh;

    HRESULT hr = CreateMaterials( L"", pd3dDevice, pd3dxMaterials, dwMaterials );;    

    // Extract data from m_pMesh for easy access

    m_dwNumVertices    = m_pMesh->GetNumVertices();
    m_dwNumFaces       = m_pMesh->GetNumFaces();
    m_dwBytesPerVertex = m_pMesh->GetNumBytesPerVertex();

    m_pMesh->GetIndexBuffer( &m_pIB );
    m_pMesh->GetVertexBuffer( &m_pVB );
    m_pMesh->GetDeclaration( decl );

    pd3dDevice->CreateVertexDeclaration( decl, &m_pDecl );

    return hr;
}

为了优化网格的渲染,函数首先生成网格面邻接信息缓存:
    DWORD *rgdwAdjacency = NULL;
    rgdwAdjacency = new DWORD[pInMesh->GetNumFaces() * 3];    if( rgdwAdjacency == NULL )        return E_OUTOFMEMORY;
    pInMesh->GenerateAdjacency(1e-6f, rgdwAdjacency);
GenerateAdjacency()的声明如下:

Generate a list of mesh edges, as well as a list of faces that share each edge.

HRESULT GenerateAdjacency(  FLOAT Epsilon,  DWORD * pAdjacency);
Parameters
Epsilon
[in] Specifies that vertices that differ in position by less than epsilon should be treated as coincident.
pAdjacency
[in] Pointer to an array of three DWORDs per face to be filled with the indices of adjacent faces. The number of bytes in this array must be at least 3 * ID3DXBaseMesh::GetNumFaces * sizeof(DWORD).
Return Values
If the method succeeds, the return value is D3D_OK. If the method fails, the return value can be one of the following: D3DERR_INVALIDCALL, E_OUTOFMEMORY.

Remarks
After an application generates adjacency information for a mesh, the mesh data can be optimized for better drawing performance.

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();
    dwOptions &= ~(D3DXMESH_32BIT | D3DXMESH_SYSTEMMEM | D3DXMESH_WRITEONLY);    dwOptions |= D3DXMESH_MANAGED;    dwOptions |= D3DXMESHOPT_COMPACT | D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_VERTEXCACHE;
    ID3DXMesh* pTempMesh = NULL;
    if( FAILED( pInMesh->Optimize( dwOptions, rgdwAdjacency, NULL, NULL, NULL, &pTempMesh ) ) )    {        SAFE_DELETE_ARRAY( rgdwAdjacency );        return E_FAIL;    }
接下来删除了邻接数组、网格指针,并将优化后的网格模型指针赋给m_pMesh,并调用CreateMaterials()创建材质和纹理资源:

    SAFE_DELETE_ARRAY( rgdwAdjacency );    SAFE_RELEASE( m_pMesh );
    m_pMesh = pTempMesh;
    HRESULT hr = CreateMaterials( L"", pd3dDevice, pd3dxMaterials, dwMaterials );
最后从优化后的网格模型中提取数据以方便日后访问,并调用CreateVertexDeclaration()来创建顶点声明:

   // Extract data from m_pMesh for easy access
    m_dwNumVertices    = m_pMesh->GetNumVertices();    m_dwNumFaces       = m_pMesh->GetNumFaces();    m_dwBytesPerVertex = m_pMesh->GetNumBytesPerVertex();
    m_pMesh->GetIndexBuffer( &m_pIB );    m_pMesh->GetVertexBuffer( &m_pVB );
    D3DVERTEXELEMENT9 decl[MAX_FVF_DECL_SIZE];    m_pMesh->GetDeclaration( decl );    pd3dDevice->CreateVertexDeclaration( decl, &m_pDecl );
    return hr;
CreateVertexDeclaration()的声明如下:

Create a vertex shader declaration from the device and the vertex elements.

HRESULT CreateVertexDeclaration(  CONST D3DVERTEXELEMENT9* pVertexElements,  IDirect3DVertexDeclaration9** ppDecl);
Parameters
pVertexElements
[in] An array of D3DVERTEXELEMENT9 vertex elements.
ppDecl
[out, retval] Pointer to an IDirect3DVertexDeclaration9 pointer that returns the created vertex shader declaration.
Return Values
If the method succeeds, the return value is D3D_OK. If the method fails, the return value can be D3DERR_INVALIDCALL.

Remarks
See the Vertex Declaration (Direct3D 9) page for a detailed description of how to map vertex declarations between different versions of DirectX.

函数Destroy()用来在程序退出时销毁指定的资源:

HRESULT CDXUTMesh::Destroy(){    InvalidateDeviceObjects();
    for( UINT i=0; i<m_dwNumMaterials; i++ )        SAFE_RELEASE( m_pTextures[i] );
    SAFE_DELETE_ARRAY( m_pTextures );    SAFE_DELETE_ARRAY( m_pMaterials );    SAFE_DELETE_ARRAY( m_strMaterials );
    SAFE_RELEASE( m_pMesh );
    m_dwNumMaterials = 0L;
    return S_OK;}


--  作者:卷积内核
--  发布时间: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;
    if( m_pMesh )    {        if( FAILED( m_pMesh->CloneMeshFVF( m_pMesh->GetOptions(), dwFVF, pd3dDevice, &pTempMesh ) ) )        {            SAFE_RELEASE( pTempMesh );            return E_FAIL;        }
        DWORD dwOldFVF = 0;        dwOldFVF = m_pMesh->GetFVF();
        SAFE_RELEASE( m_pMesh );        m_pMesh = pTempMesh;
        // Compute normals if they are being requested and the old mesh does not have them.        if( !(dwOldFVF & D3DFVF_NORMAL) && (dwFVF & D3DFVF_NORMAL) )                   D3DXComputeNormals( m_pMesh, NULL );           }
    return S_OK;}
代码相当简洁明了,这里只对函数D3DXComputeNormals()进行说明:

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);
Parameters
pMesh
[in, out] Pointer to an ID3DXBaseMesh interface, representing the normalized mesh object. This function may not take an ID3DXPMesh progressive mesh as input.
pAdjacency
[in] Pointer to an array of three DWORDs per face that specify the three neighbors for each face in the created progressive mesh. This parameter is optional and should be set to NULL if it is unused.
Return Values
If the function succeeds, the return value is S_OK. If the function fails, the return value can be one of the following: D3DERR_INVALIDCALL, D3DXERR_INVALIDDATA, E_OUTOFMEMORY.

Remarks
The input mesh must have the D3DFVF_NORMAL flag specified in its flexible vertex format (FVF).

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);
函数SetVertexDecl()的功能与SetFVF()类似,只是SetVertexDecl()用于可编程流水线:


//-----------------------------------------------------------------------------
// Convert the mesh to the format specified by the given vertex declarations.
//-----------------------------------------------------------------------------
HRESULT CDXUTMesh::SetVertexDecl( LPDIRECT3DDEVICE9 pd3dDevice, const D3DVERTEXELEMENT9 *pDecl,
                                  bool bAutoComputeNormals, bool bAutoComputeTangents,
                                  bool bSplitVertexForOptimalTangents )
{
    LPD3DXMESH pTempMesh = NULL;

    if( m_pMesh )
    {
        if( FAILED( m_pMesh->CloneMesh( m_pMesh->GetOptions(), pDecl, pd3dDevice, &pTempMesh ) ) )
        {
            SAFE_RELEASE( pTempMesh );
            return E_FAIL;
        }
    }

    // Check if the old declaration contains a normal.

    bool bHadNormal = false;
    bool bHadTangent = false;

    D3DVERTEXELEMENT9 aOldDecl[MAX_FVF_DECL_SIZE];

    if( m_pMesh && SUCCEEDED( m_pMesh->GetDeclaration( aOldDecl ) ) )
    {
        for( UINT index = 0; index < D3DXGetDeclLength( aOldDecl ); ++index )
        {
            if( aOldDecl[index].Usage == D3DDECLUSAGE_NORMAL )           
                bHadNormal = true;
           
            if( aOldDecl[index].Usage == D3DDECLUSAGE_TANGENT )           
                bHadTangent = true;           
        }
    }

    // Check if the new declaration contains a normal.

    bool bHaveNormalNow = false;
    bool bHaveTangentNow = false;

    D3DVERTEXELEMENT9 aNewDecl[MAX_FVF_DECL_SIZE];

    if( pTempMesh && SUCCEEDED( pTempMesh->GetDeclaration( aNewDecl ) ) )
    {
        for( UINT index = 0; index < D3DXGetDeclLength( aNewDecl ); ++index )
        {
            if( aNewDecl[index].Usage == D3DDECLUSAGE_NORMAL )
                bHaveNormalNow = true;

            if( aNewDecl[index].Usage == D3DDECLUSAGE_TANGENT )            
                bHaveTangentNow = true;            
        }
    }

    SAFE_RELEASE( m_pMesh );

    if( pTempMesh )
    {
        m_pMesh = pTempMesh;

        if( !bHadNormal && bHaveNormalNow && bAutoComputeNormals )
        {
            // Compute normals in case the meshes have them
            D3DXComputeNormals( m_pMesh, NULL );
        }

        if( bHaveNormalNow && !bHadTangent && bHaveTangentNow && bAutoComputeTangents )
        {
            ID3DXMesh* pNewMesh;
            HRESULT hr;

            DWORD *rgdwAdjacency = NULL;

            rgdwAdjacency = new DWORD[m_pMesh->GetNumFaces() * 3];
            if( rgdwAdjacency == NULL )
                return E_OUTOFMEMORY;

            V( m_pMesh->GenerateAdjacency(1e-6f, rgdwAdjacency) );

            float fPartialEdgeThreshold;
            float fSingularPointThreshold;
            float fNormalEdgeThreshold;

            if( bSplitVertexForOptimalTangents )
            {
                fPartialEdgeThreshold   = 0.01f;
                fSingularPointThreshold = 0.25f;
                fNormalEdgeThreshold    = 0.01f;
            }
            else
            {
                fPartialEdgeThreshold   = -1.01f;
                fSingularPointThreshold = 0.01f;
                fNormalEdgeThreshold    = -1.01f;
            }

            // Compute tangents, which are required for normal mapping
            hr = D3DXComputeTangentFrameEx( m_pMesh,
                                            D3DDECLUSAGE_TEXCOORD, 0,
                                            D3DDECLUSAGE_TANGENT, 0,
                                            D3DX_DEFAULT, 0,
                                            D3DDECLUSAGE_NORMAL, 0,
                                            0, rgdwAdjacency,
                                            fPartialEdgeThreshold, fSingularPointThreshold, fNormalEdgeThreshold,
                                            &pNewMesh, NULL );

            SAFE_DELETE_ARRAY( rgdwAdjacency );
            if( FAILED(hr) )
                return hr;

            SAFE_RELEASE( m_pMesh );
            m_pMesh = pNewMesh;
        }
    }

    return S_OK;
}

函数首先调用CloneMesh()以指定的顶点声明方式来克隆网格模型:
    LPD3DXMESH pTempMesh = NULL;
    if( m_pMesh )    {        if( FAILED( m_pMesh->CloneMesh( m_pMesh->GetOptions(), pDecl, pd3dDevice, &pTempMesh ) ) )        {            SAFE_RELEASE( pTempMesh );            return E_FAIL;        }    }
接下来检查原网格中是否包含法线和切线信息:

    // Check if the old declaration contains a normal.
    bool bHadNormal = false;    bool bHadTangent = false;
    D3DVERTEXELEMENT9 aOldDecl[MAX_FVF_DECL_SIZE];
    if( m_pMesh && SUCCEEDED( m_pMesh->GetDeclaration( aOldDecl ) ) )    {        for( UINT index = 0; index < D3DXGetDeclLength( aOldDecl ); ++index )        {            if( aOldDecl[index].Usage == D3DDECLUSAGE_NORMAL )                           bHadNormal = true;           
            if( aOldDecl[index].Usage == D3DDECLUSAGE_TANGENT )                           bHadTangent = true;                   }    }
再接下来检查克隆后的网格模型中是否包含法线和切线信息:

    // Check if the new declaration contains a normal.
    bool bHaveNormalNow = false;    bool bHaveTangentNow = false;
    D3DVERTEXELEMENT9 aNewDecl[MAX_FVF_DECL_SIZE];
    if( pTempMesh && SUCCEEDED( pTempMesh->GetDeclaration( aNewDecl ) ) )    {        for( UINT index = 0; index < D3DXGetDeclLength( aNewDecl ); ++index )        {            if( aNewDecl[index].Usage == D3DDECLUSAGE_NORMAL )                bHaveNormalNow = true;
            if( aNewDecl[index].Usage == D3DDECLUSAGE_TANGENT )                            bHaveTangentNow = true;                    }    }
如果原网格中不包含法线信息,同时克隆后的网格模型中要求包含法线信息,且参数bAutoComputeNormals被设置为true(即要求自动计算法线),则调用D3DXComputeNormals()计算法线:

    SAFE_RELEASE( m_pMesh );
    if( pTempMesh )    {        m_pMesh = pTempMesh;
        if( !bHadNormal && bHaveNormalNow && bAutoComputeNormals )        {            // Compute normals in case the meshes have them            D3DXComputeNormals( m_pMesh, NULL );        }
如果原网格中不包含切线信息,同时克隆后的网格模型中要求包含切线信息,且参数bAutoComputeTangents被设置为true(即要求自动计算切线),则首先调用GenerateAdjacency()生成网格面邻接信息,并调用D3DXComputeTangentFrameEx()来计算网格面切线:

        if( bHaveNormalNow && !bHadTangent && bHaveTangentNow && bAutoComputeTangents )        {            ID3DXMesh* pNewMesh;            HRESULT hr;
            DWORD *rgdwAdjacency = NULL;
            rgdwAdjacency = new DWORD[m_pMesh->GetNumFaces() * 3];            if( rgdwAdjacency == NULL )                return E_OUTOFMEMORY;
            V( m_pMesh->GenerateAdjacency(1e-6f, rgdwAdjacency) );
            float fPartialEdgeThreshold;            float fSingularPointThreshold;            float fNormalEdgeThreshold;
            if( bSplitVertexForOptimalTangents )            {                fPartialEdgeThreshold   = 0.01f;                fSingularPointThreshold = 0.25f;                fNormalEdgeThreshold    = 0.01f;            }            else            {                fPartialEdgeThreshold   = -1.01f;                fSingularPointThreshold = 0.01f;                fNormalEdgeThreshold    = -1.01f;            }
            // Compute tangents, which are required for normal mapping            hr = D3DXComputeTangentFrameEx( m_pMesh,                                             D3DDECLUSAGE_TEXCOORD, 0,                                             D3DDECLUSAGE_TANGENT, 0,                                            D3DX_DEFAULT, 0,                                             D3DDECLUSAGE_NORMAL, 0,                                            0, rgdwAdjacency,                                             fPartialEdgeThreshold, fSingularPointThreshold, fNormalEdgeThreshold,                                             &pNewMesh, NULL );
            SAFE_DELETE_ARRAY( rgdwAdjacency );            if( FAILED(hr) )                return hr;
            SAFE_RELEASE( m_pMesh );            m_pMesh = pNewMesh;        }

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);
Parameters
pMesh
[in] Pointer to an input ID3DXMesh mesh object.
dwTextureInSemantic
[in] Specifies the texture coordinate input semantic. If D3DX_DEFAULT, the function assumes that there are no texture coordinates, and the function will fail unless normal vector calculation is specified.
dwTextureInIndex
[in] If a mesh has multiple texture coordinates, specifies the texture coordinate to use for the tangent frame computations. If zero, the mesh has only a single texture coordinate.
dwUPartialOutSemantic
[in] Specifies the output semantic for the type, typically D3DDECLUSAGE_TANGENT, that describes where the partial derivative with respect to the U texture coordinate will be stored. If D3DX_DEFAULT, then this partial derivative will not be stored.
dwUPartialOutIndex
[in] Specifies the semantic index at which to store the partial derivative with respect to the U texture coordinate.
dwVPartialOutSemantic
[in] Specifies the D3DDECLUSAGE type, typically D3DDECLUSAGE_BINORMAL, that describes where the partial derivative with respect to the V texture coordinate will be stored. If D3DX_DEFAULT, then this partial derivative will not be stored.
dwVPartialOutIndex
[in] Specifies the semantic index at which to store the partial derivative with respect to the V texture coordinate.
dwNormalOutSemantic
[in] Specifies the output normal semantic, typically D3DDECLUSAGE_NORMAL, that describes where the normal vector at each vertex will be stored. If D3DX_DEFAULT, then this normal vector will not be stored.
dwNormalOutIndex
[in] Specifies the semantic index at which to store the normal vector at each vertex.
dwOptions
Combination of one or more D3DXTANGENT flags that specify tangent frame computation options. If NULL, the following options will be specified:

Description D3DXTANGENT Flag Value
Weight the normal vector length by the angle, in radians, subtended by the two edges leaving the vertex. & !( D3DXTANGENT_WEIGHT_BY_AREA | D3DXTANGENT_WEIGHT_EQUAL )
Compute orthogonal Cartesian coordinates from texture coordinates (u, v). See Remarks. & !( D3DXTANGENT_ORTHOGONALIZE_FROM_U | D3DXTANGENT_ORTHOGONALIZE_FROM_V )
Textures are not wrapped in either u or v directions & !( D3DXTANGENT_WRAP_UV )
Partial derivatives with respect to texture coordinates are normalized. & !( D3DXTANGENT_DONT_NORMALIZE_PARTIALS )
Vertices are ordered in a counterclockwise direction around each triangle. & !( D3DXTANGENT_WIND_CW )
Use per-vertex normal vectors already present in the input mesh. & !( D3DXTANGENT_CALCULATE_NORMALS )

[in] If D3DXTANGENT_GENERATE_IN_PLACE is not set, the input mesh is cloned. The original mesh must therefore have sufficient space to store the computed normal vector and partial derivative data.
  

pdwAdjacency
[in] Pointer to an array of three DWORDs per face that specify the three neighbors for each face in the mesh. The number of bytes in this array must be at least 3 * ID3DXBaseMesh::GetNumFaces * sizeof(DWORD).
fPartialEdgeThreshold
[in] Specifies the maximum cosine of the angle at which two partial derivatives are deemed to be incompatible with each other. If the dot product of the direction of the two partial derivatives in adjacent triangles is less than or equal to this threshold, then the vertices shared between these triangles will be split.
fSingularPointThreshold
[in] Specifies the maximum magnitude of a partial derivative at which a vertex will be deemed singular. As multiple triangles are incident on a point that have nearby tangent frames, but altogether cancel each other out (such as at the top of a sphere), the magnitude of the partial derivative will decrease. If the magnitude is less than or equal to this threshold, then the vertex will be split for every triangle that contains it.
fNormalEdgeThreshold
[in] Similar to fPartialEdgeThreshold, specifies the maximum cosine of the angle between two normals that is a threshold beyond which vertices shared between triangles will be split. If the dot product of the two normals is less than the threshold, the shared vertices will be split, forming a hard edge between neighboring triangles. If the dot product is more than the threshold, neighboring triangles will have their normals interpolated.
ppMeshOut
[out] Address of a pointer to an output ID3DXMesh mesh object that receives the computed tangent, binormal, and normal vector data.
ppVertexMapping
[out] Address of a pointer to an output ID3DXBuffer buffer object that receives a mapping of new vertices computed by this method to the original vertices. The buffer is an array of DWORDs, with the array size defined as the number of vertices in ppMeshOut.
Return Values
If the function succeeds, the return value is S_OK. If the function fails, the return value can be one of the following: D3DERR_INVALIDCALL, D3DXERR_INVALIDDATA, E_OUTOFMEMORY.

Remarks
A simplified version of this function is available as D3DXComputeTangentFrame.

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()函数:


HRESULT CDXUTMesh::Render( LPDIRECT3DDEVICE9 pd3dDevice, bool bDrawOpaqueSubsets, bool bDrawAlphaSubsets )
{
    if( NULL == m_pMesh )
        return E_FAIL;

    // Frist, draw the subsets without alpha
    if( bDrawOpaqueSubsets )
    {
        for( DWORD i=0; i<m_dwNumMaterials; i++ )
        {
            if( m_bUseMaterials )
            {
                if( m_pMaterials[i].Diffuse.a < 1.0f )
                    continue;

                pd3dDevice->SetMaterial( &m_pMaterials[i] );
                pd3dDevice->SetTexture( 0, m_pTextures[i] );
            }

            m_pMesh->DrawSubset( i );
        }
    }

    // Then, draw the subsets with alpha
    if( bDrawAlphaSubsets && m_bUseMaterials )
    {
        for( DWORD i=0; i<m_dwNumMaterials; i++ )
        {
            if( m_pMaterials[i].Diffuse.a == 1.0f )
                continue;

            // Set the material and texture
            pd3dDevice->SetMaterial( &m_pMaterials[i] );
            pd3dDevice->SetTexture( 0, m_pTextures[i] );
            m_pMesh->DrawSubset( i );
        }
    }

    return S_OK;
}

代码简洁明了,首先绘制不透明的网格(alpha == 1.0f),接着绘制半透明的网格(alpha != 1.0f)。

接着来看第二个Render()函数:


HRESULT CDXUTMesh::Render( ID3DXEffect *pEffect,
                           D3DXHANDLE hTexture,  D3DXHANDLE hDiffuse,  D3DXHANDLE hAmbient,
                           D3DXHANDLE hSpecular, D3DXHANDLE hEmissive, D3DXHANDLE hPower,
                           bool bDrawOpaqueSubsets, bool bDrawAlphaSubsets )
{
    if( NULL == m_pMesh )
        return E_FAIL;

    UINT cPasses;

    // Frist, draw the subsets without alpha
    if( bDrawOpaqueSubsets )
    {
        pEffect->Begin( &cPasses, 0 );

        for( UINT p = 0; p < cPasses; ++p )
        {
            pEffect->BeginPass( p );

            for( DWORD i=0; i<m_dwNumMaterials; i++ )
            {
                if( m_bUseMaterials )
                {
                    if( m_pMaterials[i].Diffuse.a < 1.0f )
                        continue;

                    if( hTexture )
                        pEffect->SetTexture( hTexture, m_pTextures[i] );

                    // D3DCOLORVALUE and D3DXVECTOR4 are data-wise identical.
                    // No conversion is needed.

                    if( hDiffuse )
                        pEffect->SetVector( hDiffuse, (D3DXVECTOR4*)&m_pMaterials[i].Diffuse );

                    if( hAmbient )
                        pEffect->SetVector( hAmbient, (D3DXVECTOR4*)&m_pMaterials[i].Ambient );

                    if( hSpecular )
                        pEffect->SetVector( hSpecular, (D3DXVECTOR4*)&m_pMaterials[i].Specular );

                    if( hEmissive )
                        pEffect->SetVector( hEmissive, (D3DXVECTOR4*)&m_pMaterials[i].Emissive );

                    if( hPower )
                        pEffect->SetFloat( hPower, m_pMaterials[i].Power );

                    pEffect->CommitChanges();
                }

                m_pMesh->DrawSubset( i );
            }

            pEffect->EndPass();
        }

        pEffect->End();
    }

    // Then, draw the subsets with alpha
    if( bDrawAlphaSubsets )
    {
        pEffect->Begin( &cPasses, 0 );

        for( UINT p = 0; p < cPasses; ++p )
        {
            pEffect->BeginPass( p );

            for( DWORD i=0; i<m_dwNumMaterials; i++ )
            {
                if( m_bUseMaterials )
                {
                    if( m_pMaterials[i].Diffuse.a == 1.0f )
                        continue;

                    if( hTexture )
                        pEffect->SetTexture( hTexture, m_pTextures[i] );

                    // D3DCOLORVALUE and D3DXVECTOR4 are data-wise identical.
                    // No conversion is needed.

                    if( hDiffuse )
                        pEffect->SetVector( hDiffuse, (D3DXVECTOR4*)&m_pMaterials[i].Diffuse );

                    if( hAmbient )
                        pEffect->SetVector( hAmbient, (D3DXVECTOR4*)&m_pMaterials[i].Ambient );

                    if( hSpecular )
                        pEffect->SetVector( hSpecular, (D3DXVECTOR4*)&m_pMaterials[i].Specular );

                    if( hEmissive )
                        pEffect->SetVector( hEmissive, (D3DXVECTOR4*)&m_pMaterials[i].Emissive );

                    if( hPower )
                        pEffect->SetFloat( hPower, m_pMaterials[i].Power );

                    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