天行健 君子当自强而不息

Working with skeletal animation(8)

As shown in Figure 4.5, the SkeletalAnim mesh demonstrates what you learned in this chapter by loading a skinned mesh (the Tiny.x mesh provided in the DirectX SDK samples) and rendering it to the display.

Figure 4.5: Meet Tiny, Microsoft's woman of skeletal meshes. She is constructed from a single mesh and an underlying hierarchy of invisible bones.

source added into Direct3D.cpp:

//-------------------------------------------------------------------------------------------
// Declare an internal .X file parser class for loading meshes and frames.
//-------------------------------------------------------------------------------------------
class cXInternalParser
{
public:
    
// information passed from calling function
    IDirect3DDevice9*        m_device;
    
const char*                m_texture_path;
    DWORD                    m_new_fvf;
    DWORD                    m_load_flags;

    DWORD                    m_flags;    
// flags for which data to load: 1 = mesh, 2 = frames, 3 = both.

    
// hierarchies used during loading
    D3DXMESHCONTAINER_EX*    m_root_mesh_container;
    D3DXFRAME_EX
*            m_root_frame;

protected:
    
void parse_object(ID3DXFileData* xfile_data,
                      ID3DXFileData
* parent_xfile_data,
                      DWORD   depth,
                      
void**  data,
                      
bool      force_ref);

    
void parse_child_object(ID3DXFileData* xfile_data, DWORD depth, void** data, bool force_ref);

public:
    cXInternalParser();
    
~cXInternalParser();

    
bool  parse(const char* filename, void** data);
    
char* get_name(ID3DXFileData* xfile_data);
};

//==============================================================================================
// Generic .X parser class code
//==============================================================================================

cXInternalParser::cXInternalParser()
{
    ZeroMemory(
thissizeof(*this));
}

cXInternalParser::
~cXInternalParser()
{
    delete m_root_mesh_container;    m_root_mesh_container 
= NULL;
    delete m_root_frame;            m_root_frame 
= NULL;
}

///////////////////////////////////////////////////////////////////////////////////////////////////

bool cXInternalParser::parse(const char* filename, void** data)
{
    
if(filename == NULL)
        
return false;

    ID3DXFile
* xfile;

    
if(FAILED(D3DXFileCreate(&xfile)))
        
return false;

    
// register standard templates
    if(FAILED(xfile->RegisterTemplates((LPVOID) D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES)))
    {
        xfile
->Release();
        
return false;
    }

    ID3DXFileEnumObject
* xfile_enum;

    
if(FAILED(xfile->CreateEnumObject(filename, DXFILELOAD_FROMFILE, &xfile_enum)))
    {
        xfile
->Release();
        
return false;
    }

    SIZE_T num_child;
    xfile_enum
->GetChildren(&num_child);

    
// loop through all top-level objects, breaking on errors.
    for(SIZE_T i = 0; i < num_child; i++)
    {
        ID3DXFileData
* xfile_data;
        xfile_enum
->GetChild(i, &xfile_data);

        parse_object(xfile_data, NULL, 
0, data, false);
        release_com(xfile_data);
    }

    release_com(xfile_enum);
    release_com(xfile);

    
return true;
}

///////////////////////////////////////////////////////////////////////////////////////////////////

void cXInternalParser::parse_object(ID3DXFileData* xfile_data, 
                                    ID3DXFileData
* parent_xfile_data, 
                                    DWORD depth, 
void** data, bool force_ref)
{
    GUID type;
    xfile_data
->GetType(&type);

    
// process templates based on their type
    if(type == TID_D3DRMFrame && (m_flags & PARSE_FRAME) && force_ref == false)
    {
        D3DXFRAME_EX
* frame = new D3DXFRAME_EX;
        frame
->Name = get_name(xfile_data);

        
// link frame into hierarchy
        if(data == NULL)
        {
            
// link as sibling of root
            frame->pFrameSibling = m_root_frame;
            m_root_frame 
= frame;
            frame 
= NULL;

            data 
= (void**&m_root_frame;
        }
        
else
        {
            
// link as child of supplied frame
            D3DXFRAME_EX* frame_ptr        = (D3DXFRAME_EX*)*data;
            frame
->pFrameSibling        = frame_ptr->pFrameFirstChild;
            frame_ptr
->pFrameFirstChild = frame;
            frame 
= NULL;

            data 
= (void**)&frame_ptr->pFrameFirstChild;
        }
    }
    
else if(type == TID_D3DRMFrameTransformMatrix && (m_flags & PARSE_FRAME) && data && force_ref == false)
    {
        D3DXFRAME_EX
* frame = (D3DXFRAME_EX*)*data;

        
if(frame)
        {
            SIZE_T size;
            
const void* tran_matrix;

            xfile_data
->Lock(&size, &tran_matrix);

            frame
->TransformationMatrix = *((const D3DXMATRIX*) tran_matrix);
            frame
->mat_original            = frame->TransformationMatrix;

            xfile_data
->Unlock();
        }
    }
    
else if(type == TID_D3DRMMesh && (m_flags & PARSE_MESH))    // load a mesh (skinned or regular)
    {
        
if(force_ref == false)
        {
            D3DXMESHCONTAINER_EX
* mesh_container;
            load_mesh(
&mesh_container, m_device, xfile_data, m_texture_path, m_new_fvf, m_load_flags);

            
// link mesh to head of list of meshes0
            if(mesh_container)
            {
                mesh_container
->pNextMeshContainer = m_root_mesh_container;
                m_root_mesh_container 
= mesh_container;
                mesh_container 
= NULL;

                
// link mesh to frame if needed
                if(data)
                {
                    D3DXFRAME_EX
* frame = (D3DXFRAME_EX**data;

                    
if((m_flags & PARSE_FRAME) && frame)
                        frame
->pMeshContainer = m_root_mesh_container;
                }
            }
        }
        
else    // referenced, then check if wanting to link to frame.
        {
            
if(data)
            {
                D3DXFRAME_EX
* frame = (D3DXFRAME_EX**data;

                
if((m_flags & PARSE_FRAME) && m_root_mesh_container && frame)
                {
                    
char* name = get_name(xfile_data);

                    
if(name)
                    {
                        frame
->pMeshContainer = m_root_mesh_container->find(name);

                        delete[] name;
                        name 
= NULL;
                    }
                }
            }
        }
    }
    
    parse_child_object(xfile_data, depth, data, force_ref);
}

///////////////////////////////////////////////////////////////////////////////////////////////////

void cXInternalParser::parse_child_object(ID3DXFileData* xfile_data, 
                                          DWORD depth, 
void** data, bool force_ref)
{
    SIZE_T num_child;
    xfile_data
->GetChildren(&num_child);

    
// scan for embedded templates
    for(SIZE_T i = 0; i < num_child; i++)
    {
        ID3DXFileData
* child_xfile_data;
        xfile_data
->GetChild(i, &child_xfile_data);

        
if(child_xfile_data->IsReference())
            force_ref 
= true;

        parse_object(child_xfile_data, xfile_data, depth
+1, data, force_ref);
        release_com(child_xfile_data);
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////

char* cXInternalParser::get_name(ID3DXFileData* xfile_data)
{
    
if(xfile_data == NULL)
        
return NULL;

    DWORD size;

    
if(FAILED(xfile_data->GetName(NULL, &size)))
        
return NULL;

    
char* name = NULL;

    
if(size > 1)
    {
        name 
= new char[size];
        xfile_data
->GetName(name, &size);
    }

    
return name;
}

///////////////////////////////////////////////////////////////////////////////////////////////////

HRESULT load_mesh(D3DXMESHCONTAINER_EX
** ret_mesh_container,
                  D3DXFRAME_EX
** ret_frame,
                  IDirect3DDevice9
* device,
                  
const char* filename,
                  
const char* texture_path,
                  DWORD new_fvf,
                  DWORD load_flags)
{
    
// error checking
    if(device == NULL || filename == NULL || texture_path == NULL)
        
return E_FAIL;

    cXInternalParser parser;

    
// set parser data
    parser.m_device            = device;
    parser.m_texture_path    
= texture_path;
    parser.m_new_fvf        
= new_fvf;
    parser.m_load_flags        
= load_flags;
    parser.m_flags            
= ((ret_mesh_container == NULL) ? PARSE_NONE : PARSE_MESH) |
                              ((ret_frame 
== NULL) ? PARSE_NONE : PARSE_FRAME);

    
// clear mesh and frame pointers
    parser.m_root_frame = NULL;
    parser.m_root_mesh_container 
= NULL;

    
if(! parser.parse(filename, NULL))
        
return E_FAIL;

    
// Map the matrices to the frames and create an array of bone matrices,
    
// but only if user passed pointers to receive and the loader found some meshes and frames.
    if(ret_mesh_container && ret_frame && parser.m_root_mesh_container && parser.m_root_frame)
    {
        
// scan through all meshes

        D3DXMESHCONTAINER_EX
* mesh_container = parser.m_root_mesh_container;

        
while(mesh_container)
        {
            
// does this mesh use skinning?
            if(mesh_container->pSkinInfo)
            {
                DWORD num_bones 
= mesh_container->pSkinInfo->GetNumBones();

                
// allocate the matrix pointers and bone matrices
                mesh_container->frame_combined_matrices = new D3DXMATRIX*[num_bones];
                mesh_container
->bone_trans_matrices        = new D3DXMATRIX[num_bones];

                
// match matrix poiners to frames
                for(DWORD i = 0; i < num_bones; i++)
                {
                    
const char* bone_name = mesh_container->pSkinInfo->GetBoneName(i);
                    D3DXFRAME_EX
* frame   = parser.m_root_frame->find(bone_name);

                    
// match frame to bone
                    if(frame)
                        mesh_container
->frame_combined_matrices[i] = &frame->mat_combined;
                    
else
                        mesh_container
->frame_combined_matrices[i] = NULL;
                }
            }

            
// go to next mesh
            mesh_container = (D3DXMESHCONTAINER_EX*) mesh_container->pNextMeshContainer;
        }
    }

    
if(ret_mesh_container)
    {
        
// copy the pointer into passed variables
        *ret_mesh_container = parser.m_root_mesh_container;
        parser.m_root_mesh_container 
= NULL;
    }
    
else
    {
        
// delete list of meshes in case any were not needed.
        delete parser.m_root_mesh_container;
        parser.m_root_mesh_container 
= NULL;
    }

    
if(ret_frame)
    {
        
// assign frame hierarchy pointer
        *ret_frame = parser.m_root_frame;
        parser.m_root_frame 
= NULL;
    }
    
else
    {
        
// delete frame hierarchy in case it was loaded and it was not needed.
        delete parser.m_root_frame;
        parser.m_root_frame 
= NULL;
    }

    
return S_OK;
}

///////////////////////////////////////////////////////////////////////////////////////////////////

HRESULT update_skin_mesh(D3DXMESHCONTAINER_EX
* mesh_container)
{
    
if(mesh_container == NULL)
        
return E_FAIL;

    
if(mesh_container->MeshData.pMesh == NULL || mesh_container->skin_mesh == NULL || mesh_container->pSkinInfo == NULL)
        
return E_FAIL;

    
if(mesh_container->bone_trans_matrices == NULL || mesh_container->frame_combined_matrices == NULL)
        
return E_FAIL;

    
// copy the bone matrices over (must have been combined before call draw_mesh)
    for(DWORD i = 0; i < mesh_container->pSkinInfo->GetNumBones(); i++)
    {
        
// start with bone offset matrix
        mesh_container->bone_trans_matrices[i] = *(mesh_container->pSkinInfo->GetBoneOffsetMatrix(i));

        
// apply frame transformation
        if(mesh_container->frame_combined_matrices[i])
            mesh_container
->bone_trans_matrices[i] *= (*mesh_container->frame_combined_matrices[i]);
    }

    
void* src_vertices;
    
void* dest_vertices;

    mesh_container
->MeshData.pMesh->LockVertexBuffer(D3DLOCK_READONLY, (void**)&src_vertices);
    mesh_container
->skin_mesh->LockVertexBuffer(0, (void**)&dest_vertices);

    
// update the skinned mesh using provided transformations
    mesh_container->pSkinInfo->UpdateSkinnedMesh(mesh_container->bone_trans_matrices, NULL, src_vertices, dest_vertices);

    mesh_container
->MeshData.pMesh->UnlockVertexBuffer();
    mesh_container
->skin_mesh->UnlockVertexBuffer();    

    
return S_OK;
}

 

WinMain.cpp:

#include <windows.h>
#include 
<d3d9.h>
#include 
<d3dx9.h>
#include 
"Direct3D.h"

IDirect3D9
*                g_d3d;
IDirect3DDevice9
*        g_device;
D3DXMESHCONTAINER_EX
*    g_mesh_container;
D3DXFRAME_EX
*            g_frame;

float g_mesh_radius = 0.0f;    // bounding radius of mesh

const char CLASS_NAME[] = "SkeletalClass";
const char CAPTION[]    = "Skeletal Demo";

////////////////////////////////////////////////////////////////////////////////////////////////

LRESULT FAR PASCAL window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

bool do_init(HWND hwnd);
void do_shutdown();
void do_frame();


//////////////////////////////////////////////////////////////////////////////////////////////

int PASCAL WinMain(HINSTANCE inst, HINSTANCE, LPSTR, int cmd_show)
{      
    CoInitialize(NULL);    
// Initialize the COM system

    
// Create the window class here and register it

    WNDCLASSEX win_class;  

    win_class.cbSize        
= sizeof(win_class);
    win_class.style         
= CS_CLASSDC;
    win_class.lpfnWndProc   
= window_proc;
    win_class.cbClsExtra    
= 0;
    win_class.cbWndExtra    
= 0;
    win_class.hInstance     
= inst;
    win_class.hIcon         
= LoadIcon(NULL, IDI_APPLICATION);
    win_class.hCursor       
= LoadCursor(NULL, IDC_ARROW);
    win_class.hbrBackground 
= NULL;
    win_class.lpszMenuName  
= NULL;
    win_class.lpszClassName 
= CLASS_NAME;
    win_class.hIconSm       
= LoadIcon(NULL, IDI_APPLICATION);

    
if(!RegisterClassEx(&win_class))
        
return -1;

    
// Create the main window
    HWND hwnd = CreateWindow(CLASS_NAME, CAPTION, WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
                             
00640480, NULL, NULL, inst, NULL);

    
if(hwnd == NULL)
        
return -1;

    ShowWindow(hwnd, cmd_show);
    UpdateWindow(hwnd);

    
// Call init function and enter message pump
    if(do_init(hwnd)) 
    {
        MSG msg;    
        ZeroMemory(
&msg, sizeof(MSG));

        
// Start message pump, waiting for user to exit
        while(msg.message != WM_QUIT) 
        {
            
if(PeekMessage(&msg, NULL, 00, PM_REMOVE)) 
            {
                TranslateMessage(
&msg);
                DispatchMessage(
&msg);
            }
            
            do_frame();    
// Render a single frame
        }
    }
  
    do_shutdown();
    UnregisterClass(CLASS_NAME, inst);
    CoUninitialize();

    
return 0;
}

LRESULT FAR PASCAL window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    
// Only handle window destruction messages
    switch(msg) 
    {
    
case WM_DESTROY:
        PostQuitMessage(
0);
        
break;

    
case WM_KEYDOWN:
        
if(wParam == VK_ESCAPE)
            DestroyWindow(hwnd);

        
break;
    }

    
return DefWindowProc(hwnd, msg, wParam, lParam);
}

bool do_init(HWND hwnd)
{
    init_d3d(
&g_d3d, &g_device, hwnd, falsefalse);

    
if(FAILED(load_mesh(&g_mesh_container, &g_frame, g_device, "..\\Data\\tiny.x""..\\Data\\"00)))
        
return false;

    
// get the bounding radius of the object

    g_mesh_radius 
= 0.0f;

    D3DXMESHCONTAINER_EX
* mesh_container = g_mesh_container;

    
while(mesh_container)
    {
        ID3DXMesh
* mesh = mesh_container->MeshData.pMesh;

        
if(mesh)
        {
            
// lock the vertex buffer, get its radius, and unlock buffer.

            D3DXVECTOR3
* vertices;
            D3DXVECTOR3  center;
            
float         radius;

            mesh
->LockVertexBuffer(D3DLOCK_READONLY, (void**)&vertices);

            D3DXComputeBoundingSphere(vertices, mesh
->GetNumVertices(), D3DXGetFVFVertexSize(mesh->GetFVF()),
                                      
&center, &radius);
            
            mesh
->UnlockVertexBuffer();

            
// update radius
            if(radius > g_mesh_radius)
                g_mesh_radius 
= radius;
        }

        
// goto next mesh
        mesh_container = (D3DXMESHCONTAINER_EX*) mesh_container->pNextMeshContainer;
    }

    
return true;
}

void do_shutdown()
{
    
// free mesh data
    delete g_mesh_container;    g_mesh_container = NULL;
    delete g_frame;                g_frame 
= NULL;
    
    
// release D3D objects
    release_com(g_device);
    release_com(g_d3d);
}

void do_frame()
{
    
// calculate a view transformation matrix using the mesh's bounding radius to position the viewer

    
float distance = g_mesh_radius * 3.0f;
    
float angle       = timeGetTime() / 2000.0f;        

    D3DXMATRIX  mat_view;
    D3DXVECTOR3 eye(cos(angle) 
* distance, g_mesh_radius, sin(angle) * distance);
    D3DXVECTOR3 at(
0.0f0.0f0.0f);
    D3DXVECTOR3 up(
0.0f1.0f0.0f);

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

    
// rebuild the frame hierarchy transformations
    if(g_frame)
        g_frame
->update_hierarchy(NULL);

    
// rebuild the mesh
    update_skin_mesh(g_mesh_container);

    
// clear the device and start drawing the scene

    g_device
->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_RGBA(0064255), 1.0f0);

    g_device
->BeginScene();

    D3DXMATRIX mat_world;
    D3DXMatrixIdentity(
&mat_world);
    g_device
->SetTransform(D3DTS_WORLD, &mat_world);

    draw_mesh(g_mesh_container);

    g_device
->EndScene();

    g_device
->Present(NULL, NULL, NULL, NULL);
}

 

download source file


posted on 2008-04-23 20:24 lovedday 阅读(644) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论