以文本方式查看主题

-  计算机科学论坛  (http://bbs.xml.org.cn/index.asp)
--  『 C/C++编程思想 』  (http://bbs.xml.org.cn/list.asp?boardid=61)
----  网格模型高级技术(3)  (http://bbs.xml.org.cn/dispbbs.asp?boardid=61&rootid=&id=70890)


--  作者:卷积内核
--  发布时间:12/26/2008 11:07:00 AM

--  网格模型高级技术(3)
类CDXUTMeshFrame的详细说明请参阅DXUT源码分析 ---- 类CDXUTMeshFrame

类CDXUTMeshFile的详细说明请参阅DXUT源码分析 ---- 类CDXUTMeshFile

使用类CDXUTMeshFile进行网格模型的绘制

首先在OnCreateDevice()里创建CDXUTMeshFile对象:

g_dxut_mesh_file = new CDXUTMeshFile();
g_dxut_mesh_file->Create(pd3dDevice, L"Dwarf.x");

接着在OnFrameRender()里渲染网格模型:

// Render the sceneif( SUCCEEDED( pd3dDevice->BeginScene() ) ){        g_dxut_mesh_file->Render(pd3dDevice, &g_mat_world); RenderText(); V(g_button_dlg.OnRender(fElapsedTime));
    V( pd3dDevice->EndScene() );}
最后在OnDestroyDevice()里释放该对象所申请的资源:

g_dxut_mesh_file->Destroy();
SAFE_DELETE(g_dxut_mesh_file);

示例截图:

按此在新窗口浏览图片

主程序:


#include "dxstdafx.h"
#include "resource.h"

#pragma warning(disable : 4127 4995)

#define IDC_TOGGLE_FULLSCREEN    1
#define IDC_TOGGLE_REF            2
#define IDC_CHANGE_DEVICE        3

#define release_com(p)    do { if(p) { (p)->Release(); (p) = NULL; } } while(0)

ID3DXFont*                    g_font;
ID3DXSprite*                g_text_sprite;
bool                        g_show_help;

CDXUTDialogResourceManager    g_dlg_resource_manager;
CD3DSettingsDlg                g_settings_dlg;
CDXUTDialog                    g_button_dlg;

CDXUTMeshFile*                g_dxut_mesh_file;
D3DXMATRIX                    g_mat_world;

//--------------------------------------------------------------------------------------
// Rejects any devices that aren't acceptable by returning false
//--------------------------------------------------------------------------------------
bool CALLBACK IsDeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat,
                                  D3DFORMAT BackBufferFormat, bool bWindowed, void* pUserContext )
{
    // Typically want to skip backbuffer formats that don't support alpha blending

    IDirect3D9* pD3D = DXUTGetD3DObject();

    if( FAILED( pD3D->CheckDeviceFormat( pCaps->AdapterOrdinal, pCaps->DeviceType, AdapterFormat,
                    D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, D3DRTYPE_TEXTURE, BackBufferFormat ) ) )
        return false;

    return true;
}


//--------------------------------------------------------------------------------------
// Before a device is created, modify the device settings as needed.
//--------------------------------------------------------------------------------------
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, const D3DCAPS9* pCaps, void* pUserContext )
{
    // If video card does not support hardware vertex processing, then uses sofaware vertex processing.
    if((pCaps->DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == 0)
        pDeviceSettings->BehaviorFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;

    static bool is_first_time = true;

    if(is_first_time)
    {
        is_first_time = false;

        // if using reference device, then pop a warning message box.
        if(pDeviceSettings->DeviceType == D3DDEVTYPE_REF)
            DXUTDisplaySwitchingToREFWarning();
    }

    return true;
}

//--------------------------------------------------------------------------------------
// Remove path from fullname, and convert filename from multibyte to wchar.
//--------------------------------------------------------------------------------------
void RemovePathFromFileName(LPSTR fullname, LPWSTR wfilename)
{
    WCHAR wbuf[MAX_PATH]  = {0};
    MultiByteToWideChar(CP_ACP, 0, fullname, -1, wbuf, MAX_PATH);

    LPWSTR w_last_back_slash = wcsrchr(wbuf, '\\');

    if(w_last_back_slash)
        lstrcpy(wfilename, ++w_last_back_slash);
    else
        lstrcpy(wfilename, wbuf);
}


//--------------------------------------------------------------------------------------
// Create any D3DPOOL_MANAGED resources here
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnCreateDevice( IDirect3DDevice9* pd3dDevice,
                                 const D3DSURFACE_DESC* pBackBufferSurfaceDesc,
                                 void* pUserContext )
{
    HRESULT    hr;

    V_RETURN(g_dlg_resource_manager.OnCreateDevice(pd3dDevice));
    V_RETURN(g_settings_dlg.OnCreateDevice(pd3dDevice));

    D3DXCreateFont(pd3dDevice, 18, 0, FW_BOLD, 1, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY,
                   DEFAULT_PITCH | FF_DONTCARE, L"Arial", &g_font);
    
    g_dxut_mesh_file = new CDXUTMeshFile();
    g_dxut_mesh_file->Create(pd3dDevice, L"Dwarf.x");

    return S_OK;
}


//--------------------------------------------------------------------------------------
// Create any D3DPOOL_DEFAULT resources here
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnResetDevice( IDirect3DDevice9* pd3dDevice,
                                const D3DSURFACE_DESC* pBackBufferSurfaceDesc,
                                void* pUserContext )
{
    HRESULT hr;

    V_RETURN(g_dlg_resource_manager.OnResetDevice());
    V_RETURN(g_settings_dlg.OnResetDevice());
    V_RETURN(g_font->OnResetDevice());
    V_RETURN(D3DXCreateSprite(pd3dDevice, &g_text_sprite));

    // set dialog position and size

    g_button_dlg.SetLocation(pBackBufferSurfaceDesc->Width - 170, 0);
    g_button_dlg.SetSize(170, 170);

    // setup view matrix

    D3DXMATRIX mat_view;
    D3DXVECTOR3 eye(0.0f, 0.0f,   -5.0f);
    D3DXVECTOR3  at(0.0f, 0.0f,    0.0f);
    D3DXVECTOR3  up(0.0f, 1.0f,    0.0f);

    D3DXMatrixLookAtLH(&mat_view, &eye, &at, &up);
    pd3dDevice->SetTransform(D3DTS_VIEW, &mat_view);

    // set projection matrix
    D3DXMATRIX mat_proj;
    float aspect = (float)pBackBufferSurfaceDesc->Width / pBackBufferSurfaceDesc->Height;
    D3DXMatrixPerspectiveFovLH(&mat_proj, D3DX_PI/4, aspect, 1.0f, 100.0f);
    pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_proj);

    // setup light
    
    D3DLIGHT9 light;
    ZeroMemory(&light, sizeof(D3DLIGHT9));

    light.Type       = D3DLIGHT_DIRECTIONAL;
    light.Diffuse.r  = 1.0f;
    light.Diffuse.g  = 1.0f;
    light.Diffuse.b  = 1.0f;
    light.Range         = 1000.0f;

    D3DXVECTOR3 light_dir(0.0f, -1.0f, 1.0f);    
    D3DXVec3Normalize((D3DXVECTOR3*) &light.Direction, &light_dir);
    pd3dDevice->SetLight(0, &light);
    pd3dDevice->LightEnable(0, TRUE);
    pd3dDevice->SetRenderState(D3DRS_LIGHTING, TRUE);

    pd3dDevice->SetRenderState(D3DRS_AMBIENT, 0x0080FFFF);

    return S_OK;
}

//--------------------------------------------------------------------------------------
// Release resources created in the OnResetDevice callback here
//--------------------------------------------------------------------------------------
void CALLBACK OnLostDevice( void* pUserContext )
{
    g_dlg_resource_manager.OnLostDevice();
    g_settings_dlg.OnLostDevice();
    g_font->OnLostDevice();

    release_com(g_text_sprite);
}


//--------------------------------------------------------------------------------------
// Release resources created in the OnCreateDevice callback here
//--------------------------------------------------------------------------------------
void CALLBACK OnDestroyDevice( void* pUserContext )
{
    g_dlg_resource_manager.OnDestroyDevice();
    g_settings_dlg.OnDestroyDevice();    

    release_com(g_font);

    g_dxut_mesh_file->Destroy();
    SAFE_DELETE(g_dxut_mesh_file);
}

//--------------------------------------------------------------------------------------
// Handle updates to the scene
//--------------------------------------------------------------------------------------
void CALLBACK OnFrameMove( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
{
    D3DXMATRIX mat_trans, mat_rot;
    D3DXMatrixTranslation(&mat_trans, 0.0f, -0.7f, 0.0f);
    D3DXMatrixRotationY(&mat_rot, timeGetTime() / 1000.0f);
    
    g_mat_world = mat_trans * mat_rot;
}

//--------------------------------------------------------------------------------------
// Render the helper information
//--------------------------------------------------------------------------------------
void RenderText()
{
    CDXUTTextHelper text_helper(g_font, g_text_sprite, 20);
    
    text_helper.Begin();

    // show frame and device states
    text_helper.SetInsertionPos(5, 5);
    text_helper.SetForegroundColor( D3DXCOLOR(1.0f, 0.475f, 0.0f, 1.0f) );
    text_helper.DrawTextLine( DXUTGetFrameStats(true) );
    text_helper.DrawTextLine( DXUTGetDeviceStats() );

    // show helper information
    
    const D3DSURFACE_DESC* surface_desc = DXUTGetBackBufferSurfaceDesc();

    if(g_show_help)
    {
        text_helper.SetInsertionPos(10, surface_desc->Height - 15 * 6);
        text_helper.SetForegroundColor( D3DXCOLOR(1.0f, 0.475f, 0.0f, 1.0f) );
        text_helper.DrawTextLine(L"Controls (F1 to hide):");
        
        text_helper.SetInsertionPos(40, surface_desc->Height - 15 * 4);
        text_helper.DrawTextLine(L"Quit: ESC");
    }
    else
    {
        text_helper.SetInsertionPos(10, surface_desc->Height - 15 * 4);
        text_helper.SetForegroundColor( D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f) );
        text_helper.DrawTextLine(L"Press F1 for help");
    }

    text_helper.End();
}

//--------------------------------------------------------------------------------------
// Render the scene
//--------------------------------------------------------------------------------------
void CALLBACK OnFrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
{
    HRESULT hr;

    if(g_settings_dlg.IsActive())
    {
        g_settings_dlg.OnRender(fElapsedTime);
        return;
    }

    // Clear the render target and the zbuffer
    V( pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_ARGB(0, 0, 0, 0), 1.0f, 0) );

    // Render the scene
    if( SUCCEEDED( pd3dDevice->BeginScene() ) )
    {                            
        g_dxut_mesh_file->Render(pd3dDevice, &g_mat_world);
        RenderText();
        V(g_button_dlg.OnRender(fElapsedTime));

        V( pd3dDevice->EndScene() );
    }
}


//--------------------------------------------------------------------------------------
// Handle messages to the application
//--------------------------------------------------------------------------------------
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
                          bool* pbNoFurtherProcessing, void* pUserContext )
{
    *pbNoFurtherProcessing = g_dlg_resource_manager.MsgProc(hWnd, uMsg, wParam, lParam);
    if(*pbNoFurtherProcessing)
        return 0;

    if(g_settings_dlg.IsActive())
    {
        g_settings_dlg.MsgProc(hWnd, uMsg, wParam, lParam);
        return 0;
    }

    *pbNoFurtherProcessing = g_button_dlg.MsgProc(hWnd, uMsg, wParam, lParam);
    if(*pbNoFurtherProcessing)
        return 0;

    return 0;
}


//--------------------------------------------------------------------------------------
// Handle keybaord event
//--------------------------------------------------------------------------------------
void CALLBACK OnKeyboardProc(UINT charater, bool is_key_down, bool is_alt_down, void* user_context)
{
    if(is_key_down)
    {
        switch(charater)
        {
        case VK_F1:
            g_show_help = !g_show_help;
            break;
        }
    }
}

//--------------------------------------------------------------------------------------
// Handle events for controls
//--------------------------------------------------------------------------------------
void CALLBACK OnGUIEvent(UINT event, int control_id, CDXUTControl* control, void* user_context)
{
    switch(control_id)
    {
    case IDC_TOGGLE_FULLSCREEN:
        DXUTToggleFullScreen();
        break;

    case IDC_TOGGLE_REF:
        DXUTToggleREF();
        break;

    case IDC_CHANGE_DEVICE:
        g_settings_dlg.SetActive(true);
        break;
    }
}

//--------------------------------------------------------------------------------------
// Initialize dialogs
//--------------------------------------------------------------------------------------
void InitDialogs()
{
    g_settings_dlg.Init(&g_dlg_resource_manager);
    g_button_dlg.Init(&g_dlg_resource_manager);

    g_button_dlg.SetCallback(OnGUIEvent);

    int x = 35, y = 10, width = 125, height = 22;

    g_button_dlg.AddButton(IDC_TOGGLE_FULLSCREEN, L"Toggle full screen", x, y,         width, height);
    g_button_dlg.AddButton(IDC_TOGGLE_REF,          L"Toggle REF (F3)",     x, y += 24, width, height);
    g_button_dlg.AddButton(IDC_CHANGE_DEVICE,      L"Change device (F2)", x, y += 24, width, height, VK_F2);    
}

//--------------------------------------------------------------------------------------
// Initialize everything and go into a render loop
//--------------------------------------------------------------------------------------
INT WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
{
    // Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif

    // Set the callback functions
    DXUTSetCallbackDeviceCreated( OnCreateDevice );
    DXUTSetCallbackDeviceReset( OnResetDevice );
    DXUTSetCallbackDeviceLost( OnLostDevice );
    DXUTSetCallbackDeviceDestroyed( OnDestroyDevice );
    DXUTSetCallbackMsgProc( MsgProc );
    DXUTSetCallbackFrameRender( OnFrameRender );
    DXUTSetCallbackFrameMove( OnFrameMove );
    DXUTSetCallbackKeyboard(OnKeyboardProc);
   
    // TODO: Perform any application-level initialization here
    InitDialogs();

    // Initialize DXUT and create the desired Win32 window and Direct3D device for the application
    DXUTInit( true, true, true ); // Parse the command line, handle the default hotkeys, and show msgboxes
    DXUTSetCursorSettings( true, true ); // Show the cursor and clip it when in full screen
    DXUTCreateWindow( L"Use DXUT Mesh File Class" );
    DXUTCreateDevice( D3DADAPTER_DEFAULT, true, 640, 480, IsDeviceAcceptable, ModifyDeviceSettings );

    // Start the render loop
    DXUTMainLoop();

    // TODO: Perform any application-level cleanup here

    return DXUTGetExitCode();
}

下载示例工程(不包含资源)


--  作者:卷积内核
--  发布时间:12/26/2008 11:08:00 AM

--  
网格模型动画一般有两种:一种是渐变动画;另一种是骨骼动画,这种动画包含在网格模型之中,通过网格模型不同部分之间的相对运动来实现动画。

骨骼动画基本原理

骨骼动画是目前最流行也最复杂的角色动画,它包含以下几个重要元素:骨骼、动画。骨骼动画思想的起源很简单,自然界中的大多数动物都拥有一套骨骼,身体的皮毛血肉都依附于骨骼,当骨骼开始运动的时候,依附于对应骨骼的皮毛血肉都随骨骼一起运动。在三维图形编程领域,角色的躯体是由网格模型来表示的,网格模型通常由大量三角形图元组成,而三角形又是由顶点组成的。为了模仿现实世界中角色自身的动作,就需要为角色网格模型添加一套骨骼,同时需要确定哪些顶点依附于哪块骨骼,这样当骨骼运动时就能牵引依附骨骼的顶点一起运动,这就是骨骼动画的基本原理。

骨骼动画模型的骨骼是以树状层次结构组织起来的,整个骨骼结构中有一块根骨骼,其他的骨骼都直接或间接连接到根骨骼上,形成角色模型的整个骨骼框架。

一般每块骨骼都带有两个矩阵,一个是初始变换矩阵(LocalTransformMatrix),表示骨骼的初始位置;另一个是组合变换矩阵(CombinedTransformMaitrx),用于对骨骼进行各种变换,从而实现角色动画。在每次渲染角色模型前,需要更新整个骨骼层次结构,组合每个连续的变换,将上层骨骼的运动传递到下层骨骼,这个原理可表示为:

(子骨骼的)CombinedTransformMaitrx = (子骨骼的)LocalTransformMaitrx x (父骨骼的)CombinedTransformMaitrx

骨骼的组合变换矩阵是随动画的播放不断变化的,而它的初始变换矩阵一般是不改变的,正是所有骨骼的这些矩阵相互作用才牵引着顶点的变化,从而实现了骨骼动画。因为一次变换只能将骨骼变换到一个特定位置,要形成连续的动画就需要一帧一帧地连续改变骨骼的位置,每次改变骨骼的位置都需要一个骨骼变换矩阵,在网格模型中不可能保存任意时刻骨骼的变换矩阵,通常是保存关键时间点骨骼的变换矩阵(即关键桢),然后在播放角色动画时,根据播放时间进行插值得到任意时刻骨骼的变换矩阵,从而形成连续的角色动画。

骨骼动画是通过骨骼变换矩阵实现的,在网格模型中保存的也是关键时间点骨骼的变换矩阵,因此插值就是针对这些关键时间点上的骨骼变换矩阵进行的。假设在s1时刻骨骼变换矩阵是mat1,在s2时刻骨骼变换矩阵是mat2,在s1和s2之间的任意时刻s,其骨骼变换矩阵mat为:

mat = (1-w) * mat1 + w * mat2

其中w是权值,通过这个权值来调节在s时刻骨骼变换矩阵中mat1和mat2所占的比重,对骨骼变换矩阵进行插值最简单的方法是线性插值,这时w = (s-s1) / (s2-s1)

骨骼动画类的设计与实现

我们需要将骨骼动画网格模型的相关操作封装到一组类和结构中,这一组类和结构可以看成一套完整的骨骼动画网格模型接口,它们之间的关系如下图所示:

按此在新窗口浏览图片

其中,D3DXFRAME和D3DXMESHCONTAINER是Direct3D提供的两个结构,D3DXMESHCONTAINER结构用于保存模型的网格数据,D3DXFRAME用于保存模型的骨骼框架。结构D3DXMESHCONTAINER_DERIVED继承自Direct3D提供的结构D3DXMESHCONTAINER,结构D3DXFRAME_DERIVED继承自Direct3D提供的结构D3DXFRAME,分别进行了相应的扩充,使其能够保存所需要的其他数据。

cAllocateHierarchy类负责从动画网格模型文件加载各种数据,该类继承自Direct3D中的ID3DXAllocateHierarchy接口。

cAnimMesh类是唯一对外开放的类,它通过cAllocateHierarchy类的对象从模型文件中加载所需的数据,并负责处理骨骼动画信息以及网格模型的渲染。

继承并扩展结构体D3DXFRAME

为了在渲染网格模型的同时播放包含在网格模型的动画,需要处理两个单独的实体:骨骼结构(即框架结构)和网格模型。框架结构和网格模型的相关数据分别使用D3DXFRAME_DERIVED和D3DXMESHCONTAINER_DERIVED结构保存。需要指出的是.x文件中的一个网格模型可以由多个框架和多个网格组成,但具体到某一个框架时,它一般只有一个网格,当然它也可以有多个网格。

为了方便加载骨骼动画网格模型,Direct3D提供了两个重要的结构体:D3DXFRAME和D3DXMESHCONTAINER,其中D3DXFRAME用来加载框架,其定义如下:

Encapsulates a transform frame in a transformation frame hierarchy.

typedef struct D3DXFRAME {    LPSTR Name;    D3DXMATRIX TransformationMatrix;    LPD3DXMESHCONTAINER pMeshContainer;    D3DXFRAME * pFrameSibling;    D3DXFRAME * pFrameFirstChild;} D3DXFRAME, *LPD3DXFRAME;
Members
Name
Name of the frame.
TransformationMatrix
Transformation matrix.
pMeshContainer
Pointer to the mesh container.
pFrameSibling
Pointer to a sibling frame.
pFrameFirstChild
Pointer to a child frame.
Remarks
An application can derive from this structure to add other data.

显然在实现动画网格模型的绘制前,不仅要得到每个框架的初始变换矩阵,同时还要得到从该框架的所有父节点到本级框架的组合变换矩阵,这是因为任何一个父框架的位置改变都会影响该框架自身位置的变化,所以在此将结构D3DXFRAME扩展为D3DXFRAME_DERIVED,在D3DXFRAME_DERIVED中添加一个成员变量CombinedTransformMaitrx,用TransformationMatrix记录在任何动画数据未加载前框架的初始变换矩阵,也就是该框架的初始位置,用CombinedTransformMaitrx来记录从所有的父框架到该框架自身所积累起来的组合变换矩阵,这样就将整个网格模型很方便地组织起来了。

结构D3DXFRAME_DERIVED的定义如下:

struct D3DXFRAME_DERIVED : public D3DXFRAME{ D3DXMATRIX CombinedTransformMatrix;};

继承并扩展结构体D3DXMESHCONTAINER

结构体D3DXMESHCONTAINER用来加载每个具体网格模型的数据,其定义如下:

Encapsulates a mesh object in a transformation frame hierarchy.

typedef struct D3DXMESHCONTAINER {    LPSTR Name;    D3DXMESHDATA MeshData;    LPD3DXMATERIAL pMaterials;    LPD3DXEFFECTINSTANCE pEffects;    DWORD NumMaterials;    DWORD * pAdjacency;    LPD3DXSKININFO pSkinInfo;    D3DXMESHCONTAINER * pNextMeshContainer;} D3DXMESHCONTAINER, *LPD3DXMESHCONTAINER;
Members
Name
Mesh name.
MeshData
Type of data in the mesh.
pMaterials
Array of mesh materials.
pEffects
Pointer to a set of default effect parameters.
NumMaterials
Number of materials in the mesh.
pAdjacency
Pointer to an array of three DWORDs per triangle of the mesh that contains adjacency information.
pSkinInfo
Pointer to the skin information interface.
pNextMeshContainer
Pointer to the next mesh container.

结构体D3DXMESHCONTAINER中没有记录网格模型的纹理信息,所以将该结构体扩展为D3DXMESHCONTAINER_DERIVED,定义如下:

struct D3DXMESHCONTAINER_DERIVED : public D3DXMESHCONTAINER{ IDirect3DTexture9** ppTextures;};
其中ppTextures用来存储网格模型的纹理对象。


--  作者:卷积内核
--  发布时间:12/26/2008 11:10:00 AM

--  
为了方便加载.x文件中的框架和网格模型数据,Direct3D提供了一个ID3DXAllocateHierarchy接口,该接口中有4个纯虚函数:用来创建框架的CreateFrame(),创建网格容器的CreateMeshContainer(),销毁框架的DestroyFrame(),销毁网格容器的DestroyMeshContainer()。应用程序会在相应的时机自动调用这些对应的函数,以构建或者销毁对应的框架或网格模型。

This interface is implemented by the application to allocate or free frame and mesh container objects. Methods on this are called during loading and destroying frame hierarchies.

Method Description
ID3DXAllocateHierarchy::CreateFrame Requests allocation of a frame object.
ID3DXAllocateHierarchy::CreateMeshContainer Requests allocation of a mesh container object.
ID3DXAllocateHierarchy::DestroyFrame Requests deallocation of a frame object.
ID3DXAllocateHierarchy::DestroyMeshContainer Requests deallocation of a mesh container object.

ID3DXAllocateHierarchy::CreateFrame
Requests allocation of a frame object.

HRESULT CreateFrame(  LPCSTR Name,  LPD3DXFRAME * ppNewFrame);
Parameters
Name
[in] Name of the frame to be created.
ppNewFrame
[out, retval] Returns the created frame object.
Return Values
The return values of this method are implemented by an application programmer. In general, if no error occurs, program the method to return D3D_OK. Otherwise, program the method to return an appropriate error message from D3DERR or D3DXERR, as this will cause D3DXLoadMeshHierarchyFromX to fail also, and return the error.

ID3DXAllocateHierarchy::CreateMeshContainer
Requests allocation of a mesh container object.

HRESULT CreateMeshContainer(  LPCSTR Name,  CONST D3DXMESHDATA * pMeshData,  CONST D3DXMATERIAL * pMaterials,  CONST D3DXEFFECTINSTANCE * pEffectInstances,  DWORD NumMaterials,  CONST DWORD * pAdjacency,  LPD3DXSKININFO pSkinInfo,  LPD3DXMESHCONTAINER * ppNewMeshContainer);
Parameters
Name
[in] Name of the mesh.
pMeshData
[in] Pointer to the mesh data structure.
pMaterials
[in] Array of materials used in the mesh.
pEffectInstances
[in] Array of effect instances used in the mesh.  
NumMaterials
[in] Number of materials in the materials array.
pAdjacency
[in] Adjacency array for the mesh.
pSkinInfo
[in] Pointer to the skin mesh object if skin data is found.
ppNewMeshContainer
[out, retval] Returns the created mesh container.
Return Values
The return values of this method are implemented by an application programmer. In general, if no error occurs, program the method to return D3D_OK. Otherwise, program the method to return an appropriate error message from D3DERR or D3DXERR, as this will cause D3DXLoadMeshHierarchyFromX to fail also, and return the error.

ID3DXAllocateHierarchy::DestroyFrame
Requests deallocation of a frame object.

HRESULT DestroyFrame(  LPD3DXFRAME pFrameToFree);
Parameters
pFrameToFree
[in] Pointer to the frame to be deallocated.
Return Values
The return values of this method are implemented by an application programmer. In general, if no error occurs, program the method to return D3D_OK. Otherwise, program the method to return an appropriate error message from D3DERR or D3DXERR, as this will cause D3DXLoadMeshHierarchyFromX to fail also, and return the error.

ID3DXAllocateHierarchy::DestroyMeshContainer
Requests deallocation of a mesh container object.

HRESULT DestroyMeshContainer(  LPD3DXMESHCONTAINER pMeshContainerToFree);
Parameters
pMeshContainerToFree
[in] Pointer to the mesh container object to be deallocated.
Return Values
The return values of this method are implemented by an application programmer. In general, if no error occurs, program the method to return D3D_OK. Otherwise, program the method to return an appropriate error message from D3DERR or D3DXERR, as this will cause D3DXLoadMeshHierarchyFromX to fail also, and return the error.

cAllocateHierarchy类继承自ID3DXAllocateHierarchy接口,在cAllocateHierarchy类需要重载这4个纯虚函数以实现动画网格模型数据的加载和释放。

该类的定义如下:

class cAllocateHierarchy : public ID3DXAllocateHierarchy{private: HRESULT AllocateName(LPCSTR name, LPSTR* ret_name);
public: STDMETHOD(CreateFrame)(LPCSTR name, LPD3DXFRAME* ret_frame);
 STDMETHOD(CreateMeshContainer)(LPCSTR name,                                    CONST D3DXMESHDATA* mesh_data,                                   CONST D3DXMATERIAL* xmaterials,                                    CONST D3DXEFFECTINSTANCE* effect_instances,                                    DWORD num_materials,                                    CONST DWORD* adjacency,                                    LPD3DXSKININFO skin_info,                                    LPD3DXMESHCONTAINER* ret_mesh_container);    
    STDMETHOD(DestroyFrame)(THIS_ LPD3DXFRAME frame_to_free);    STDMETHOD(DestroyMeshContainer)(THIS_ LPD3DXMESHCONTAINER base_mesh_container);};

函数AllocateFrame()用来为框架生成一个名称:

HRESULT cAllocateHierarchy::AllocateName(LPCSTR name, LPSTR* ret_name){ if(name != NULL) {  UINT length = (UINT)strlen(name) + 1;
  *ret_name = new CHAR[length];
  if(*ret_name == NULL)   return E_OUTOFMEMORY;
  memcpy(*ret_name, name, length * sizeof(CHAR)); } else {  *ret_name = NULL; }
 return S_OK;}

函数CreateFrame()的作用在于生成一个新的扩展框架,并按照指定的参数为该框架命名:

HRESULT cAllocateHierarchy::CreateFrame(LPCSTR name, LPD3DXFRAME* ret_frame){ *ret_frame = NULL;
 D3DXFRAME_DERIVED* new_frame = new D3DXFRAME_DERIVED;
 if(new_frame == NULL)  return E_OUTOFMEMORY;
 HRESULT hr = AllocateName(name, &new_frame->Name);
 if(FAILED(hr)) {  delete new_frame;  return hr; }
 D3DXMatrixIdentity(&new_frame->TransformationMatrix); D3DXMatrixIdentity(&new_frame->CombinedTransformMatrix);
 new_frame->pMeshContainer   = NULL; new_frame->pFrameSibling    = NULL; new_frame->pFrameFirstChild = NULL;
 *ret_frame = new_frame;
 return S_OK;}


--  作者:卷积内核
--  发布时间:12/26/2008 11:10:00 AM

--  
在一个框架创建好后,需要创建该框架的网格容器,这通过函数CreateMeshContainer()来实现:

HRESULT cAllocateHierarchy::CreateMeshContainer(LPCSTR name,                                                 CONST D3DXMESHDATA* mesh_data,                                                CONST D3DXMATERIAL* xmaterials,                                                 CONST D3DXEFFECTINSTANCE* effect_instances,                                                 DWORD num_materials,                                                 CONST DWORD* adjacency,                                                 LPD3DXSKININFO skin_info,                                                 LPD3DXMESHCONTAINER* ret_mesh_container) { *ret_mesh_container = NULL;
 if(mesh_data->Type != D3DXMESHTYPE_MESH)  return E_FAIL;
 ID3DXMesh* mesh_ptr = mesh_data->pMesh; DWORD fvf = mesh_ptr->GetFVF();
 if(fvf == 0)  return E_FAIL;
 // create a mesh container and zero it
 D3DXMESHCONTAINER_DERIVED* new_mesh_container = new D3DXMESHCONTAINER_DERIVED;
 if(new_mesh_container == NULL)  return E_OUTOFMEMORY;
 memset(new_mesh_container, 0, sizeof(D3DXMESHCONTAINER_DERIVED));
 // copy mesh name
 HRESULT hr = AllocateName(name, &new_mesh_container->Name);
 if(FAILED(hr)) {  DestroyMeshContainer(new_mesh_container);  return hr; }
 IDirect3DDevice9* device; mesh_ptr->GetDevice(&device);
 new_mesh_container->MeshData.Type = D3DXMESHTYPE_MESH;
 // be sure mesh contain normal if(!(fvf & D3DFVF_NORMAL)) {  hr = mesh_ptr->CloneMeshFVF(mesh_ptr->GetOptions(), fvf | D3DFVF_NORMAL, device,          &new_mesh_container->MeshData.pMesh);
  if(FAILED(hr))  {   release_com(device);   DestroyMeshContainer(new_mesh_container);   return hr;  }
  mesh_ptr = new_mesh_container->MeshData.pMesh;  D3DXComputeNormals(mesh_ptr, NULL); } else {  new_mesh_container->MeshData.pMesh = mesh_ptr;  mesh_ptr->AddRef(); // !! important, so DestroyMeshContainer() will not crash. }
 // load materials and textures
 UINT num_faces = mesh_ptr->GetNumFaces();
 new_mesh_container->NumMaterials = max(1, num_materials); new_mesh_container->pMaterials  = new D3DXMATERIAL[new_mesh_container->NumMaterials]; new_mesh_container->ppTextures  = new LPDIRECT3DTEXTURE9[new_mesh_container->NumMaterials]; new_mesh_container->pAdjacency  = new DWORD[num_faces * 3];
 if((new_mesh_container->pAdjacency == NULL) || (new_mesh_container->pMaterials == NULL) ||    (new_mesh_container->ppTextures == NULL)) {  release_com(device);  DestroyMeshContainer(new_mesh_container);  return E_OUTOFMEMORY; }
 memcpy(new_mesh_container->pAdjacency, adjacency, sizeof(DWORD) * num_faces * 3); memset(new_mesh_container->ppTextures, 0, sizeof(LPDIRECT3DTEXTURE9) * new_mesh_container->NumMaterials);
 D3DXMATERIAL* xmaterials_ptr = new_mesh_container->pMaterials;
 if(num_materials > 0) {  memcpy(xmaterials_ptr, xmaterials, sizeof(D3DXMATERIAL) * num_materials);  xmaterials_ptr->MatD3D.Ambient = xmaterials_ptr->MatD3D.Diffuse;
  for(UINT i = 0; i < num_materials; i++)  {   if(xmaterials_ptr[i].pTextureFilename != NULL)   {    WCHAR w_texture_path[MAX_PATH];    WCHAR w_filename[MAX_PATH];
    RemovePathFromFileName(xmaterials_ptr[i].pTextureFilename, w_filename);    DXUTFindDXSDKMediaFileCch(w_texture_path, MAX_PATH, w_filename);
    if(FAILED( D3DXCreateTextureFromFileW(device, w_texture_path, &new_mesh_container->ppTextures[i]) ))     new_mesh_container->ppTextures[i] = NULL;   }  } } else {  xmaterials_ptr[0].pTextureFilename = NULL;  memset(&xmaterials_ptr[0].MatD3D, 0, sizeof(D3DMATERIAL9));
  xmaterials_ptr[0].MatD3D.Diffuse.r = 0.5f;  xmaterials_ptr[0].MatD3D.Diffuse.r = 0.5f;  xmaterials_ptr[0].MatD3D.Diffuse.b = 0.5f;  xmaterials_ptr[0].MatD3D.Specular  = xmaterials_ptr[0].MatD3D.Diffuse;  xmaterials_ptr[0].MatD3D.Ambient   = xmaterials_ptr[0].MatD3D.Diffuse; }
 release_com(device);
 *ret_mesh_container = new_mesh_container;
 return S_OK;}
在此实现的骨骼动画网格模型中没有涉及到蒙皮信息,所以在CreateMeshContainer()函数中没有处理参数skin_info指向的蒙皮信息。

函数DestroyFrame()只有一个参数指向准备释放的框架对象:

HRESULT cAllocateHierarchy::DestroyFrame(LPD3DXFRAME frame_to_free) { SAFE_DELETE_ARRAY(frame_to_free->Name); SAFE_DELETE(frame_to_free);
 return S_OK;}

函数DestroyMeshContainer()也只有一个参数指向将要释放的网格容器对象:

HRESULT cAllocateHierarchy::DestroyMeshContainer(LPD3DXMESHCONTAINER base_mesh_container){ if(base_mesh_container == NULL)  return S_OK;
 D3DXMESHCONTAINER_DERIVED* mesh_container = (D3DXMESHCONTAINER_DERIVED*) base_mesh_container;
 SAFE_DELETE_ARRAY(mesh_container->Name); SAFE_DELETE_ARRAY(mesh_container->pAdjacency); SAFE_DELETE_ARRAY(mesh_container->pMaterials);
 if(mesh_container->ppTextures != NULL) {  for(UINT i = 0; i < mesh_container->NumMaterials; i++)   release_com(mesh_container->ppTextures[i]); }
 SAFE_DELETE_ARRAY(mesh_container->ppTextures);
 SAFE_RELEASE(mesh_container->MeshData.pMesh); SAFE_RELEASE(mesh_container->pSkinInfo);
 SAFE_DELETE(mesh_container);
 return S_OK;}


--  作者:卷积内核
--  发布时间:12/26/2008 11:10:00 AM

--  
类cAnimMesh是最关键的一个类,所有与骨骼动画相关的具体实现细节都封装在该类中,该类还定义了类cAllocateHierarchy的一个对象m_alloc_hierarchy,该对象完成从文件中加载动画网格模型的骨骼层次结构、动画数据以及其他用于绘制模型的几何数据。


类cAnimMesh的定义如下所示:

class cAnimMesh{private: cAllocateHierarchy*   m_alloc_hierarchy; IDirect3DDevice9*   m_device; D3DXFRAME*   m_root_frame;
public: D3DXVECTOR3  m_object_center; float   m_object_radius; bool   m_is_play_anim; ID3DXAnimationController* m_anim_controller;
private: HRESULT load_from_xfile(CONST WCHAR* wfilename); void update_frame_matrices(D3DXFRAME* base_frame, CONST D3DXMATRIX* parent_matrix); void draw_frame(CONST D3DXFRAME* frame); void draw_mesh_container(CONST D3DXMESHCONTAINER* base_mesh_container, CONST D3DXFRAME* base_frame);
public: HRESULT create(IDirect3DDevice9* device, CONST WCHAR* wfilename); void render(CONST D3DXMATRIX* mat_world, double app_elapsed_time); void destroy();
public: cAnimMesh(); virtual ~cAnimMesh();};

构造函数负责分配资源和初始化成员变量,析构函数负责释放资源:

cAnimMesh::cAnimMesh(){ m_is_play_anim   = true; m_device    = NULL; m_anim_controller      = NULL; m_root_frame   = NULL;
 m_alloc_hierarchy = new cAllocateHierarchy();}
cAnimMesh::~cAnimMesh(){ D3DXFrameDestroy(m_root_frame, m_alloc_hierarchy); release_com(m_anim_controller);
 delete m_alloc_hierarchy;}

函数load_from_xfile()的主要任务是调用函数D3DXLoadMeshHierarchyFromX()从.x文件中加载动画模型,其实现如下:

HRESULT cAnimMesh::load_from_xfile(CONST WCHAR* wfilename){ HRESULT hr;
 WCHAR wpath[MAX_PATH]; DXUTFindDXSDKMediaFileCch(wpath, sizeof(wpath) / sizeof(WCHAR), wfilename);
 V_RETURN(D3DXLoadMeshHierarchyFromXW(wpath, D3DXMESH_MANAGED, m_device, m_alloc_hierarchy, NULL,          &m_root_frame, &m_anim_controller));
 V_RETURN(D3DXFrameCalculateBoundingSphere(m_root_frame, &m_object_center, &m_object_radius));
 return S_OK;}
虽然该函数的实现代码非常简单,但其内部过程却是很复杂的,关键是要弄清除D3DXLoadMeshHierarchyFromX()函数中m_alloc_hierarchy参数的作用。D3DXLoadMeshHierarchyFromX()函数在内部隐式地通过m_alloc_hierarchy调用加载网格模型具体数据的函数(即上面提到的cAllocateHeirarchy中的CreateFrame()和CreateMeshContainer()函数),这些函数是由用户编写的,但却是由Direct3D在内部于适当机制调用。

来看看D3DXLoadMeshHierarchyFromX()的具体使用说明:

Loads the first frame hierarchy from a .x file.

HRESULT D3DXLoadMeshHierarchyFromX(  LPCSTR Filename,  DWORD MeshOptions,  LPDIRECT3DDEVICE9 pDevice,  LPD3DXALLOCATEHIERARCHY pAlloc,  LPD3DXLOADUSERDATA pUserDataLoader,  LPD3DXFRAME* ppFrameHierarchy,  LPD3DXANIMATIONCONTROLLER* ppAnimController);
Parameters
Filename
[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.
MeshOptions
[in] Combination of one or more flags from the D3DXMESH enumeration that specify creation options for the mesh.
pDevice
[in] Pointer to an IDirect3DDevice9 interface, the device object associated with the mesh.
pAlloc
[in] Pointer to an ID3DXAllocateHierarchy interface.
pUserDataLoader
[in] Application provided interface that allows loading of user data.
ppFrameHierarchy
[out, retval] Returns a pointer to the loaded frame hierarchy.
ppAnimController
[out, retval] Returns a pointer to the animation controller corresponding to animation in the .x file. This is created with default tracks and events.
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, E_OUTOFMEMORY.

Remarks
The compiler setting also determines the function version. If Unicode is defined, the function call resolves to D3DXLoadMeshHierarchyFromXW. Otherwise, the function call resolves to D3DXLoadMeshHierarchyFromXA.

All the meshes in the file will be collapsed into one output mesh. If the file contains a frame hierarchy, all the transformations will be applied to the mesh.

D3DXLoadMeshHierarchyFromX loads the animation data and frame hierarchy from a .x file. It scans the .x file and builds a frame hierarchy and animation controller according to the ID3DXAllocateHierarchy-derived object passed to it through pAlloc. Loading the data requires several steps as follows:

Derive ID3DXAllocateHierarchy, implementing each method. This controls how frames and meshes are allocated and freed.
Derive ID3DXLoadUserData, implementing each method. If your .x file has no embedded user-defined data, or if you do not need it, you can skip this part.
Create an object of your ID3DXAllocateHierarchy class, and optionally of your LoadUserData class. You do not need to call any methods of these objects yourself.
Call D3DXLoadMeshHierarchyFromX, passing in your ID3DXAllocateHierarchy object and your ID3DXLoadUserData object (or NULL) to create the frame hierarchy and animation controller. All the animation sets and frames are automatically registered to the animation controller.
During the load, ID3DXAllocateHierarchy::CreateFrame and ID3DXLoadUserData::LoadFrameChildData are called back on each frame to control loading and allocation of the frame. The application defines these methods to control how frames are stored. ID3DXAllocateHierarchy::CreateMeshContainer and ID3DXLoadUserData::LoadMeshChildData are called back on each mesh object to control loading and allocation of mesh objects. ID3DXLoadUserData::LoadTopLevelData is called back for each top level object that doesn't get loaded by the other methods.

To free this data, call ID3DXAnimationController::Release to free the animation sets, and D3DXFRAMEDestroy, passing in the root node of the frame hierarchy and an object of your derived ID3DXAllocateHierarchy class. ID3DXAllocateHierarchy::DestroyFrame and ID3DXAllocateHierarchy::DestroyMeshContainer will each be called for every frame and mesh object in the frame hierarchy. Your implementation of ID3DXAllocateHierarchy::DestroyFrame should release everything allocated by ID3DXAllocateHierarchy::CreateFrame, and likewise for the mesh container methods.

因为在每次渲染网格模型前,只有知道每个框架的确切位置,才能在正确的位置上绘制出该框架包含的具体网格模型,所以需要计算得到各级框架的组合变换矩阵,函数update_frame_matrices()采用递归的方法计算各级框架的组合变换矩阵,具体实现代码如下:

void cAnimMesh::update_frame_matrices(D3DXFRAME* base_frame, CONST D3DXMATRIX* parent_matrix){ D3DXFRAME_DERIVED* frame = (D3DXFRAME_DERIVED*) base_frame;
 if(parent_matrix != NULL)  D3DXMatrixMultiply(&frame->CombinedTransformMatrix, &frame->TransformationMatrix, parent_matrix); else  frame->CombinedTransformMatrix = frame->TransformationMatrix;
 if(frame->pFrameSibling != NULL)  update_frame_matrices(frame->pFrameSibling, parent_matrix);
 if(frame->pFrameFirstChild != NULL)  update_frame_matrices(frame->pFrameFirstChild, &frame->CombinedTransformMatrix);}

因为骨骼动画网格模型是通过框架按照树状结构组织起来的,而网格模型又包含在框架之中,所以在为了渲染网格模型的同时能将其中的动画播放出来,就需要逐个框架逐个网格模型地进行渲染,其中draw_mesh_container()负责渲染框架中包含的具体网格模型:

void cAnimMesh::draw_mesh_container(CONST D3DXMESHCONTAINER* base_mesh_container, CONST D3DXFRAME* base_frame){ D3DXMESHCONTAINER_DERIVED* mesh_container = (D3DXMESHCONTAINER_DERIVED*) base_mesh_container; D3DXFRAME_DERIVED* frame = (D3DXFRAME_DERIVED*) base_frame;
 m_device->SetTransform(D3DTS_WORLD, &frame->CombinedTransformMatrix);
 for(UINT i = 0; i < mesh_container->NumMaterials; i++) {  m_device->SetMaterial(&mesh_container->pMaterials[i].MatD3D);  m_device->SetTexture(0, mesh_container->ppTextures[i]);
  mesh_container->MeshData.pMesh->DrawSubset(i); }}
该函数的实现比较简单,在渲染每个网格之前,首先调用函数SetTransform(),根据该网格在框架的组合变换矩阵,将网格中所包含的网格模型移动到正确的位置后,再设置材质、纹理,最后进行绘制。

函数draw_frame()以draw_mesh_container()为基础,采用递归的方法,将整个网格模型绘制出来:

void cAnimMesh::draw_frame(CONST D3DXFRAME* frame){ D3DXMESHCONTAINER* mesh_container = frame->pMeshContainer;
 while(mesh_container != NULL) {  draw_mesh_container(mesh_container, frame);  mesh_container = mesh_container->pNextMeshContainer; }
 if(frame->pFrameSibling != NULL)  draw_frame(frame->pFrameSibling);
 if(frame->pFrameFirstChild != NULL)  draw_frame(frame->pFrameFirstChild);}
在调用该函数时,只需将参数frame设置为网格模型的根节点就可以绘制出整个网格模型。

函数render()通过draw_frame()完成整个网格模型的渲染,其实现如下:

void cAnimMesh::render(CONST D3DXMATRIX* mat_world, double app_elapsed_time){ if(0.0f == app_elapsed_time)  return;
 if(m_is_play_anim && m_anim_controller != NULL) m_anim_controller->AdvanceTime(app_elapsed_time, NULL);
 update_frame_matrices(m_root_frame, mat_world); draw_frame(m_root_frame);}
在渲染网格模型之前,首先使用动画控制器m_anim_controller调用函数AdvanceTime()将网格模型动画向前推进,然后调用函数update_frame_matrices(),根据当前网格模型的世界矩阵mat_world更新整个网格模型的层次,即计算每个框架的组合变换矩阵,最后调用draw_frame()函数渲染出整个网格模型。

create()函数用于根据参数指定的网格模型文件名创建骨骼动画网格模型:

HRESULT cAnimMesh::create(IDirect3DDevice9* device, CONST WCHAR* wfilename){ m_device = device;
 HRESULT hr; V_RETURN(load_from_xfile(wfilename));
 return S_OK;}
函数destroy()只负责销毁对象:

void cAnimMesh::destroy(){ delete this;}


--  作者:卷积内核
--  发布时间:12/26/2008 11:11:00 AM

--  
使用cAnimMesh类包含3个步骤,首先在回调函数OnCreateDevice()中创建cAnimMesh类的实例:

g_anim_mesh = new cAnimMesh();
V_RETURN(g_anim_mesh->create(pd3dDevice, L"emr.x"));

接着在回调函数OnFrameRender()中渲染网格模型:

g_anim_mesh->render(&g_mat_world, fElapsedTime * 0.001);

最后在回调函数OnDestroy()中释放网格模型:


g_anim_mesh->destroy();

运行截图:

按此在新窗口浏览图片


--  作者:卷积内核
--  发布时间:12/26/2008 11:11:00 AM

--  
主程序:

#include "dxstdafx.h"
#include "resource.h"
#include "AnimMesh.h"

#pragma warning(disable : 4127 4995)

#define  IDC_TOGGLE_FULLSCREEN    1
#define  IDC_TOGGLE_REF            2
#define  IDC_CHANGE_DEVICE        3

#define  release_com(p)    do { if(p) { (p)->Release(); (p) = NULL; } } while(0)

ID3DXFont*                    g_font;
ID3DXSprite*                g_text_sprite;
bool                         g_show_help;

CDXUTDialogResourceManager    g_dlg_resource_manager;
CD3DSettingsDlg                g_settings_dlg;
CDXUTDialog                    g_button_dlg;

cAnimMesh*                    g_anim_mesh;
D3DXMATRIX                    g_mat_world;

//--------------------------------------------------------------------------------------
// Rejects any devices that aren't acceptable by returning false
//--------------------------------------------------------------------------------------
bool  CALLBACK IsDeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat,
                                  D3DFORMAT BackBufferFormat,  bool  bWindowed,  void * pUserContext )
{
     // Typically want to skip backbuffer formats that don't support alpha blending

    IDirect3D9* pD3D = DXUTGetD3DObject();

     if ( FAILED( pD3D->CheckDeviceFormat( pCaps->AdapterOrdinal, pCaps->DeviceType, AdapterFormat,
                    D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING, D3DRTYPE_TEXTURE, BackBufferFormat ) ) )
         return   false ;

     return   true ;
}


//--------------------------------------------------------------------------------------
// Before a device is created, modify the device settings as needed.
//--------------------------------------------------------------------------------------
bool  CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings,  const  D3DCAPS9* pCaps,  void * pUserContext )
{
     // If video card does not support hardware vertex processing, then uses sofaware vertex processing.
     if ((pCaps->DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == 0)
        pDeviceSettings->BehaviorFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;

     static   bool  is_first_time =  true ;

     if (is_first_time)
    {
        is_first_time =  false ;

         // if using reference device, then pop a warning message box.
         if (pDeviceSettings->DeviceType == D3DDEVTYPE_REF)
            DXUTDisplaySwitchingToREFWarning();
    }

     return   true ;
}


//--------------------------------------------------------------------------------------
// Create any D3DPOOL_MANAGED resources here
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnCreateDevice( IDirect3DDevice9* pd3dDevice,
                                  const  D3DSURFACE_DESC* pBackBufferSurfaceDesc,
                                  void * pUserContext )
{
    HRESULT    hr;

    V_RETURN(g_dlg_resource_manager.OnCreateDevice(pd3dDevice));
    V_RETURN(g_settings_dlg.OnCreateDevice(pd3dDevice));

    D3DXCreateFont(pd3dDevice, 18, 0, FW_BOLD, 1, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY,
                   DEFAULT_PITCH | FF_DONTCARE, L"Arial", &g_font);
    
    g_anim_mesh =  new  cAnimMesh();
    V_RETURN(g_anim_mesh->create(pd3dDevice, L"emr.x"));

     return  S_OK;
}


//--------------------------------------------------------------------------------------
// Create any D3DPOOL_DEFAULT resources here
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnResetDevice( IDirect3DDevice9* pd3dDevice,
                                 const  D3DSURFACE_DESC* pBackBufferSurfaceDesc,
                                 void * pUserContext )
{
    HRESULT hr;

    V_RETURN(g_dlg_resource_manager.OnResetDevice());
    V_RETURN(g_settings_dlg.OnResetDevice());
    V_RETURN(g_font->OnResetDevice());
    V_RETURN(D3DXCreateSprite(pd3dDevice, &g_text_sprite));

     // set dialog position and size

    g_button_dlg.SetLocation(pBackBufferSurfaceDesc->Width - 170, 0);
    g_button_dlg.SetSize(170, 170);

     // setup view matrix

    D3DXMATRIX mat_view;
    D3DXVECTOR3 eye(0.0f, 0.0f,   -50.0f);
    D3DXVECTOR3  at(0.0f, 0.0f,    0.0f);
    D3DXVECTOR3  up(0.0f, 1.0f,    0.0f);

    D3DXMatrixLookAtLH(&mat_view, &eye, &at, &up);
    pd3dDevice->SetTransform(D3DTS_VIEW, &mat_view);

     // set projection matrix
    D3DXMATRIX mat_proj;    
    D3DXMatrixPerspectiveFovLH(&mat_proj, D3DX_PI/4, 1.0f, 1.0f, 100.0f);
    pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_proj);

     // setup light
    
    D3DLIGHT9 light;
    ZeroMemory(&light,  sizeof (D3DLIGHT9));

    light.Type       = D3DLIGHT_DIRECTIONAL;
    light.Diffuse.r  = 1.0f;
    light.Diffuse.g  = 1.0f;
    light.Diffuse.b  = 1.0f;
    light.Range         = 1000.0f;

    D3DXVECTOR3 light_dir(0.0f, -1.0f, 1.0f);    
    D3DXVec3Normalize((D3DXVECTOR3*) &light.Direction, &light_dir);
    pd3dDevice->SetLight(0, &light);
    pd3dDevice->LightEnable(0, TRUE);
    pd3dDevice->SetRenderState(D3DRS_LIGHTING, TRUE);

    pd3dDevice->SetRenderState(D3DRS_AMBIENT, 0x00FFFFFF);

     return  S_OK;
}

//--------------------------------------------------------------------------------------
// Release resources created in the OnResetDevice callback here
//--------------------------------------------------------------------------------------
void  CALLBACK OnLostDevice(  void * pUserContext )
{
    g_dlg_resource_manager.OnLostDevice();
    g_settings_dlg.OnLostDevice();
    g_font->OnLostDevice();

    release_com(g_text_sprite);
}


//--------------------------------------------------------------------------------------
// Release resources created in the OnCreateDevice callback here
//--------------------------------------------------------------------------------------
void  CALLBACK OnDestroyDevice(  void * pUserContext )
{
    g_dlg_resource_manager.OnDestroyDevice();
    g_settings_dlg.OnDestroyDevice();    

    release_com(g_font);

    g_anim_mesh->destroy();
}

//--------------------------------------------------------------------------------------
// Handle updates to the scene
//--------------------------------------------------------------------------------------
void  CALLBACK OnFrameMove( IDirect3DDevice9* pd3dDevice,  double  fTime,  float  fElapsedTime,  void * pUserContext )
{
    D3DXMATRIX mat_trans, mat_rot_x, mat_rot_y;

    D3DXMatrixRotationX(&mat_rot_x, -D3DX_PI / 2);
    D3DXMatrixRotationY(&mat_rot_y, timeGetTime() / 1000.0f);
    D3DXMatrixTranslation(&mat_trans, 0.0f, -8.0f, 0.0f);
        
    g_mat_world = mat_rot_x * mat_rot_y * mat_trans;
}

//--------------------------------------------------------------------------------------
// Render the helper information
//--------------------------------------------------------------------------------------
void  RenderText()
{
    CDXUTTextHelper text_helper(g_font, g_text_sprite, 20);
    
    text_helper.Begin();

     // show frame and device states
    text_helper.SetInsertionPos(5, 5);
    text_helper.SetForegroundColor( D3DXCOLOR(1.0f, 0.475f, 0.0f, 1.0f) );
    text_helper.DrawTextLine( DXUTGetFrameStats( true ) );
    text_helper.DrawTextLine( DXUTGetDeviceStats() );

     // show helper information
    
     const  D3DSURFACE_DESC* surface_desc = DXUTGetBackBufferSurfaceDesc();

     if (g_show_help)
    {
        text_helper.SetInsertionPos(10, surface_desc->Height - 15 * 6);
        text_helper.SetForegroundColor( D3DXCOLOR(1.0f, 0.475f, 0.0f, 1.0f) );
        text_helper.DrawTextLine(L"Controls (F1 to hide):");
        
        text_helper.SetInsertionPos(40, surface_desc->Height - 15 * 4);
        text_helper.DrawTextLine(L"Quit: ESC");
    }
     else
    {
        text_helper.SetInsertionPos(10, surface_desc->Height - 15 * 4);
        text_helper.SetForegroundColor( D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f) );
        text_helper.DrawTextLine(L"Press F1 for help");
    }

    text_helper.End();
}

//--------------------------------------------------------------------------------------
// Render the scene
//--------------------------------------------------------------------------------------
void  CALLBACK OnFrameRender( IDirect3DDevice9* pd3dDevice,  double  fTime,  float  fElapsedTime,  void * pUserContext )
{
    HRESULT hr;

     if (g_settings_dlg.IsActive())
    {
        g_settings_dlg.OnRender(fElapsedTime);
         return ;
    }

     // Clear the render target and the zbuffer
    V( pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_ARGB(0, 64, 16, 16), 1.0f, 0) );

     // Render the scene
     if ( SUCCEEDED( pd3dDevice->BeginScene() ) )
    {                            
        g_anim_mesh->render(&g_mat_world, fElapsedTime * 0.001);
        RenderText();
        V(g_button_dlg.OnRender(fElapsedTime));

        V( pd3dDevice->EndScene() );
    }
}


//--------------------------------------------------------------------------------------
// Handle messages to the application
//--------------------------------------------------------------------------------------
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
                           bool * pbNoFurtherProcessing,  void * pUserContext )
{
    *pbNoFurtherProcessing = g_dlg_resource_manager.MsgProc(hWnd, uMsg, wParam, lParam);
     if (*pbNoFurtherProcessing)
         return  0;

     if (g_settings_dlg.IsActive())
    {
        g_settings_dlg.MsgProc(hWnd, uMsg, wParam, lParam);
         return  0;
    }

    *pbNoFurtherProcessing = g_button_dlg.MsgProc(hWnd, uMsg, wParam, lParam);
     if (*pbNoFurtherProcessing)
         return  0;

     return  0;
}


//--------------------------------------------------------------------------------------
// Handle keybaord event
//--------------------------------------------------------------------------------------
void  CALLBACK OnKeyboardProc(UINT charater,  bool  is_key_down,  bool  is_alt_down,  void * user_context)
{
     if (is_key_down)
    {
         switch (charater)
        {
         case  VK_F1:
            g_show_help = !g_show_help;
             break ;
        }
    }
}

//--------------------------------------------------------------------------------------
// Handle events for controls
//--------------------------------------------------------------------------------------
void  CALLBACK OnGUIEvent(UINT  event ,  int  control_id, CDXUTControl* control,  void * user_context)
{
     switch (control_id)
    {
     case  IDC_TOGGLE_FULLSCREEN:
        DXUTToggleFullScreen();
         break ;

     case  IDC_TOGGLE_REF:
        DXUTToggleREF();
         break ;

     case  IDC_CHANGE_DEVICE:
        g_settings_dlg.SetActive( true );
         break ;
    }
}

//--------------------------------------------------------------------------------------
// Initialize dialogs
//--------------------------------------------------------------------------------------
void  InitDialogs()
{
    g_settings_dlg.Init(&g_dlg_resource_manager);
    g_button_dlg.Init(&g_dlg_resource_manager);

    g_button_dlg.SetCallback(OnGUIEvent);

     int  x = 35, y = 10, width = 125, height = 22;

    g_button_dlg.AddButton(IDC_TOGGLE_FULLSCREEN, L"Toggle full screen", x, y,         width, height);
    g_button_dlg.AddButton(IDC_TOGGLE_REF,          L"Toggle REF (F3)",     x, y += 24, width, height);
    g_button_dlg.AddButton(IDC_CHANGE_DEVICE,      L"Change device (F2)", x, y += 24, width, height, VK_F2);    
}

//--------------------------------------------------------------------------------------
// Initialize everything and go into a render loop
//--------------------------------------------------------------------------------------
INT WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR,  int  )
{
     // Enable run-time memory check for debug builds.
#if  defined(DEBUG) | defined(_DEBUG)
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif

     // Set the callback functions
    DXUTSetCallbackDeviceCreated( OnCreateDevice );
    DXUTSetCallbackDeviceReset( OnResetDevice );
    DXUTSetCallbackDeviceLost( OnLostDevice );
    DXUTSetCallbackDeviceDestroyed( OnDestroyDevice );
    DXUTSetCallbackMsgProc( MsgProc );
    DXUTSetCallbackFrameRender( OnFrameRender );
    DXUTSetCallbackFrameMove( OnFrameMove );
    DXUTSetCallbackKeyboard(OnKeyboardProc);
   
     // TODO: Perform any application-level initialization here
    InitDialogs();

     // Initialize DXUT and create the desired Win32 window and Direct3D device for the application
    DXUTInit(  true ,  true ,  true  );  // Parse the command line, handle the default hotkeys, and show msgboxes
    DXUTSetCursorSettings(  true ,  true  );  // Show the cursor and clip it when in full screen
    DXUTCreateWindow( L"Use DXUT Mesh File Class" );
    DXUTCreateDevice( D3DADAPTER_DEFAULT,  true , 640, 480, IsDeviceAcceptable, ModifyDeviceSettings );

     // Start the render loop
    DXUTMainLoop();

     // TODO: Perform any application-level cleanup here

     return  DXUTGetExitCode();
}   
[B][URL=http://www.cppblog.com/Files/changingnow/UseAnimMesh.rar]下载示例工程[/URL][/B]


--  作者:卷积内核
--  发布时间:12/26/2008 11:12:00 AM

--  
在骨骼动画网格模型中,骨骼的变动导致躯体网格模型的改变,于是就有了骨骼动画。网格模型是由一个个顶点构成的,如果任意一个顶点只受一块骨骼影响,那么网格模型在运动时很容易出现裂缝,特别是在一些结合部位,如肩关节、肘关节等。尽管可以使用缝合技术缩小这些裂痕,但当关节的旋转比较极端时,例如前臂绕肘关节旋转120度时,就会在肘关节处出现一个较大的剪缺。这是因为只有一个多边形跨越在上臂和前臂之间的整个间隙,间隙越大,裂缝就越大,最终的渲染效果也越差。

采用蒙皮骨骼动画网格模型则可以很好地解决这一问题,蒙皮骨骼动画网格模型假定其中的一个顶点可以受多块骨骼影响,网格中的每个顶点包含一个影响它的骨骼列表。骨骼列表提供了影响该顶点的每个骨骼的权重信息,这些权重信息表示具体的每块骨骼将对该顶点产生多大的影响,任何一个顶点的所有骨骼权重之和是1.0。对于蒙皮网格模型,Direct3D首先会根据骨骼权重和骨骼矩阵对顶点数据(包括顶点位置和顶点法向量)进行混合,然后进行渲染。

这项技术称为图形混合(或称为顶点混合)。假设某个顶点平均地附属到两块骨骼上,这意味着两块骨骼对于该顶点的权重分量都是0.5,即该顶点将从每块骨骼上各继承50%的运动,所以这项技术也称为"骨骼蒙皮调色",权重一般是根据顶点所附属的份数和从顶点到每块骨骼的距离进行划分的,不过这些工作在大多数三维建模软件中已经事先计算好了,程序员需要做的工作只是加载保存在网格模型中的蒙皮信息,即骨骼矩阵和相应的权重,并将其应用到网格模型各部分的变换以及最终的渲染。

图形混合

Direct3D提供了图形混合技术,即蒙皮技术,实现了具有平滑关节的效果,特别是角色模型的渲染,从而提高了场景的真实感。

下图演示了图形混合的基本原理,它对最初的图形进行了两次变换,第一次没有对顶点进行变换,相当于对顶点数据乘以一个单位矩阵,第二次对图形进行了简单的旋转变换,相当于对顶点数据乘以一个旋转矩阵,将两次变换后的顶点数据进行混合,然后渲染就得到了最终显示的图形。

按此在新窗口浏览图片


通常调用IDirect3DDevice9::SetTransform()函数设置世界变换矩阵时将第一个参数设置为D3DTS_WORLD,但要实现上图所示的图形混合效果,需要使用两个世界坐标变换矩阵,这时需要将第一个参数设置为D3DTS_WORLDMATRIX宏,第二个参数设置为相应的混合变换矩阵。

在对顶点位置和法线进行混合时,每个混合矩阵对最终的顶点位置和法线的影响大小由混合权重来决定。混合权重是0.0 ~ 1.0之间的浮点数,0.0表示该权重对应的混合矩阵对顶点位置和法线向量没有影响,1.0表示顶点的位置和法线完全通过该权重对应的混合矩阵进行变换,权重也是顶点数据的一部分。

Direct3D根据混合矩阵和相应的权重对顶点位置和法线进行现行插值,公式如下:

Vblend = V * (M0 * W0 + M1 * W1 + ... + Mn * Wn)

其中,Vblend表示混合后的顶点,V表示混合前的顶点,M0、M1、...,Mn表示混合矩阵,W0、W1、...,Wn表示混合权重。

因为所有混合权重之和为1.0,所以在顶点数据中只需保存前n-1个混合权重,第n个混合权重为1.0减去前n-1个混合权重之和。在Direct3D引擎中,影响每个顶点的混合矩阵最大数量限定为4,所以在Direct3D中顶点混合就有三种情况:

Vblend = V * (M0 * W0 + M1 * (1 - W0))

Vblend = V * (M0 * W0 + M1 * W1 + M2 * (1 - (W0 + W1)))

Vblend = V * (M0 * W0 + M1 * W1 + M2 * W2 + M3 * (1 - (W0 + W1 + W2)))

如果使用图形混合,则在顶点数据中必须包含相应的混合权重数据,尽管在灵活顶点格式中使用D3DFVF_XYZB1 ~ D3DFVF_XYZB5指定顶点数据中最多可以包含5个混合权重,但目前的Direct3D版本最多只使用前面三个混合权重。在具体渲染图形时,Direct3D使用几个混合矩阵和相应的混合权重对顶点进行混合,由渲染状态D3DVERTEXBLEND控制,该渲染状态的取值为枚举类型D3DVERTEXBLENDFLAGS。

D3DRS_VERTEXBLEND
Number of matrices to use to perform geometry blending, if any. Valid values are members of the D3DVERTEXBLENDFLAGS enumerated type. The default value is D3DVBF_DISABLE.
D3DVERTEXBLENDFLAGS
Defines flags used to control the number or matrices that the system applies when performing multimatrix vertex blending.

    typedef enum D3DVERTEXBLENDFLAGS{    D3DVBF_DISABLE = 0,    D3DVBF_1WEIGHTS = 1,    D3DVBF_2WEIGHTS = 2,    D3DVBF_3WEIGHTS = 3,    D3DVBF_TWEENING = 255,    D3DVBF_0WEIGHTS = 256,} D3DVERTEXBLENDFLAGS, *LPD3DVERTEXBLENDFLAGS;

Constants
D3DVBF_DISABLE
Disable vertex blending; apply only the world matrix set by the D3DTS_WORLDMATRIX macro, where the index value for the transformation state is 0.
D3DVBF_1WEIGHTS
Enable vertex blending between the two matrices set by the D3DTS_WORLDMATRIX macro, where the index value for the transformation states are 0 and 1.
D3DVBF_2WEIGHTS
Enable vertex blending between the three matrices set by the D3DTS_WORLDMATRIX macro, where the index value for the transformation states are 0, 1, and 2.
D3DVBF_3WEIGHTS
Enable vertex blending between the four matrices set by the D3DTS_WORLDMATRIX macro, where the index value for the transformation states are 0, 1, 2, and 3.
D3DVBF_TWEENING
Vertex blending is done by using the value assigned to D3DRS_TWEENFACTOR.
D3DVBF_0WEIGHTS
Use a single matrix with a weight of 1.0.
Remarks
Members of this type are used with the D3DRS_VERTEXBLEND render state.

Geometry blending (multimatrix vertex blending) requires that your application use a vertex format that has blending (beta) weights for each vertex.

在默认情况下,顶点混合是禁用的。要启用图形混合功能,只需调用IDirect3DDevice9::SetRenderState()函数,将渲染状态D3DVERTEXBLEND设置为D3DVERTEXBLENDFLAGS枚举值中除D3DVBF_DISABLE外的其他值。如果启用了图形混合功能,Direct3D就认为在顶点数据中包含了合适数量的混合权重,程序员的工作就是为图形渲染提供包含所需数量混合权重的顶点数据和相应的顶点格式。


--  作者:秋十三
--  发布时间:1/2/2009 8:16:00 PM

--  

我全收藏了
W 3 C h i n a ( since 2003 ) 旗 下 站 点
苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
6,566.406ms