层次细节网格模型(progress mesh)是一种特殊的网格模型,它的顶点数据以树状形式组织,可以随意增加或降低模型的复杂程度,从而比普通的网格模型具有更大的灵活性,层次细节网格模型对于层次细节场景的渲染非常理想。当模型距离观察者较远时可以降低模型的复杂程度,提高渲染速度。而当模型距离观察者较近时可以使用复杂的模型,从而提高视觉效果。Direct3D用ID3DXPMesh来表示层次细节网格模型对象,而不是ID3DXMesh。
层次细节网格模型是根据原始普通网格模型生成的,在生成层次细节网格模型之前,首先需要对原始网格模型进行相关处理,包括整理、顶点融合、检查3个步骤。
在调用函数D3DXLoadMeshFromX()生成网格模型后,调用Direct3D功能函数D3DXCleanMesh()对初始网格模型进行整理,其声明如下:
Cleans a mesh, preparing it for simplification.
HRESULT D3DXCleanMesh(
D3DXCLEANTYPE CleanType,
LPD3DXMESH pMeshIn,
CONST DWORD * pAdjacencyIn,
LPD3DXMESH * ppMeshOut,
DWORD * pAdjacencyOut,
LPD3DXBUFFER * ppErrorsAndWarnings
);
Parameters
- CleanType
- [in] Vertex operations to perform in preparation
for mesh cleaning.
- pMeshIn
- [in] Pointer to an ID3DXMesh interface,
representing the mesh to be cleaned.
- pAdjacencyIn
- [in] Pointer to an array of three DWORDs per face
that specify the three neighbors for each face in the mesh to be cleaned.
- ppMeshOut
- [out] Address of a pointer to an ID3DXMesh
interface, representing the returned cleaned mesh. The same mesh is returned
that was passed in if no cleaning was necessary.
- pAdjacencyOut
- [out] Pointer to an array of three DWORDs per face
that specify the three neighbors for each face in the output mesh.
- ppErrorsAndWarnings
- [out] Returns a buffer containing a string of
errors and warnings, which explain the problems found in the mesh.
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_INVALIDCALL, E_OUTOFMEMORY.
Remarks
This function cleans a mesh using the cleaning method
and options specified in the CleanType parameter. See the D3DXCLEANTYPE
enumeration for a description of the available options.
枚举常量D3DXCLEANTYPE的定义如下:
Defines operations to perform on vertices in
preparation for mesh cleaning.
typedef enum D3DXCLEANTYPE
{
D3DXCLEAN_BACKFACING = 1,
D3DXCLEAN_BOWTIES = 2,
D3DXCLEAN_SKINNING = D3DXCLEAN_BACKFACING,
D3DXCLEAN_OPTIMIZATION = D3DXCLEAN_BACKFACING,
D3DXCLEAN_SIMPLIFICATION = D3DXCLEAN_BACKFACING | D3DXCLEAN_BOWTIES,
} D3DXCLEANTYPE, *LPD3DXCLEANTYPE;
Constants
- D3DXCLEAN_BACKFACING
- Merge triangles that share the same vertex indices
but have face normals pointing in opposite directions (back-facing
triangles). Unless the triangles are not split by adding a replicated
vertex, mesh adjacency data from the two triangles may conflict.
- D3DXCLEAN_BOWTIES
- If a vertex is the apex of two triangle fans (a
bowtie) and mesh operations will affect one of the fans, then split the
shared vertex into two new vertices. Bowties can cause problems for
operations such as mesh simplification that remove vertices, because
removing one vertex affects two distinct sets of triangles.
- D3DXCLEAN_SKINNING
- Use this flag to prevent infinite loops during
skinning setup mesh operations.
- D3DXCLEAN_OPTIMIZATION
- Use this flag to prevent infinite loops during
mesh optimization operations.
- D3DXCLEAN_SIMPLIFICATION
- Use this flag to prevent infinite loops during
mesh simplification operations.
通常选择D3DXCLEAN_SIMPLIFICATION,这样既可以将共享相同顶点且面法向量不在同一方向的三角形进行合并,同时还可以避免在简化网格的操作中陷入死循环。
在整理好网格模型后,调用函数D3DXWeldVertices()将网格模型中属性相同的重复顶点溶合到一起,从而简化网格模型,其声明如下:
Welds together replicated vertices that have equal
attributes. This method uses specified epsilon values for equality comparisons.
HRESULT D3DXWeldVertices(
LPD3DXMESH pMesh,
DWORD Flags,
CONST D3DXWeldEpsilons * pEpsilons,
CONST DWORD * pAdjacencyIn,
DWORD * pAdjacencyOut,
DWORD * pFaceRemap,
LPD3DXBUFFER * ppVertexRemap
);
Parameters
- pMesh
- [in] Pointer to an ID3DXMesh object, the mesh from
which to weld vertices.
- Flags
- [in] Combination of one or more flags from
D3DXWeldEpsilonsFLAGS.
- pEpsilons
- [in] Pointer to a D3DXWeldEpsilons structure,
specifying the epsilon values to be used for this method. Use NULL to
initialize all structure members to a default value of 1.0e-6f.
- pAdjacencyIn
- [in] Pointer to an array of three DWORDs per face
that specify the three neighbors for each face in the source mesh. If the
edge has no adjacent faces, the value is 0xffffffff. If this parameter is
set to NULL, ID3DXBaseMesh::GenerateAdjacency will be called to create
logical adjacency information.
- pAdjacencyOut
- [in, out] Pointer to an array of three DWORDs per
face that specify the three neighbors for each face in the optimized mesh.
If the edge has no adjacent faces, the value is 0xffffffff.
- pFaceRemap
- [out] An array of DWORDs, one per face, that
identifies the original mesh face that corresponds to each face in the
welded mesh.
- ppVertexRemap
- [out] Address of a pointer to an ID3DXBuffer
interface, which contains a DWORD for each vertex that specifies how the new
vertices map to the old vertices. This remap is useful if you need to alter
external data based on the new vertex mapping.
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_INVALIDCALL, E_OUTOFMEMORY.
Remarks
This function uses supplied adjacency information to
determine the points that are replicated. Vertices are merged based on an
epsilon comparison. Vertices with equal position must already have been
calculated and represented by point-representative data.
This function combines logically-welded vertices that
have similar components, such as normals or texture coordinates within
pEpsilons.
The following example code calls this function with
welding enabled. Vertices are compared using epsilon values for normal vector
and vertex position. A pointer is returned to a face remapping array
(pFaceRemap).
TCHAR strMediaPath[512]; // X-file path
LPD3DXBUFFER pAdjacencyBuffer = NULL; // adjacency data buffer
LPD3DXBUFFER pD3DXMtrlBuffer = NULL; // material buffer
LPD3DXMESH pMesh = NULL; // mesh object
DWORD m_dwNumMaterials; // number of materials
D3DXWELDEPSILONS Epsilons; // structure with epsilon values
DWORD *pFaceRemap[65536]; // face remapping array
DWORD i; // internal variable
// Load the mesh from the specified file
hr = D3DXLoadMeshFromX ( strMediaPath,
D3DXMESH_MANAGED,
m_pd3dDevice,
&pAdjacencyBuffer,
&pD3DXMtrlBuffer,
NULL,
&m_dwNumMaterials,
&pMesh ) )
if( FAILED( hr ) )
goto End; // Go to error handling
// Set epsilon values
Epsilons.Normal = 0.001;
Epsilons.Position = 0.1;
// Weld the vertices
for( i=0; i < 65536; i++ )
{
pFaceRemap[i] = 0;
}
hr = D3DXWeldVertices ( pMesh,
D3DXWELDEPSILONS_WELDPARTIALMATCHES,
&Epsilons,
(DWORD*)pAdjacencyBuffer->GetBufferPointer(),
(DWORD*)pAdjacencyBuffer->GetBufferPointer(),
(DWORD*)pFaceRemap,
NULL )
if( FAILED( hr ) )
goto End; // Go to error handling
D3DXWeldEpsilonsFLAGS的定义如下:
Options for welding together vertices.
typedef enum D3DXWeldEpsilonsFLAGS
{
D3DXWeldEpsilons_WELDALL = 1,
D3DXWeldEpsilons_WELDPARTIALMATCHES = 2,
D3DXWeldEpsilons_DONOTREMOVEVERTICES = 4,
D3DXWeldEpsilons_DONOTSPLIT = 8,
} D3DXWeldEpsilonsFLAGS, *LPD3DXWeldEpsilonsFLAGS;
Constants
- D3DXWeldEpsilons_WELDALL
- Weld together all vertices that are at the same
location. Using this flag avoids an epsilon comparison between vertex
components.
- D3DXWeldEpsilons_WELDPARTIALMATCHES
- If a given vertex component is within epsilon,
modify partially matched vertices so that both components are identical. If
all components are equal, remove one of the vertices.
- D3DXWeldEpsilons_DONOTREMOVEVERTICES
- Instructs the weld to allow only modifications to
vertices and not removal. This flag is valid only if
D3DXWeldEpsilons_WELDPARTIALMATCHES is set. It is useful to modify vertices
to be equal, but not to allow vertices to be removed.
- D3DXWeldEpsilons_DONOTSPLIT
- Instructs the weld not to split vertices that are
in separate attribute groups. When the ID3DXMesh::Optimize,
ID3DXPMesh::Optimize, or ID3DXPMesh::OptimizeBaseLOD methods are called with
the D3DXMESHOPT_ATTRSORT flag, then the D3DXMESHOPT_DONOTSPLIT flag will
also be set. Setting this flag can slow down software vertex processing.
D3DXWeldEpsilons的定义如下:
Specifies tolerance values for each vertex component
when comparing vertices to determine if they are similar enough to be welded
together.
typedef struct D3DXWeldEpsilons {
FLOAT Position;
FLOAT BlendWeights;
FLOAT Normal;
FLOAT PSize;
FLOAT Specular;
FLOAT Diffuse;
FLOAT Texcoord[8];
FLOAT Tangent;
FLOAT Binormal;
FLOAT Tess Factor;
} D3DXWeldEpsilons, *LPD3DXWeldEpsilons;
Members
- Position
- Position
- BlendWeights
- Blend weight
- Normal
- Normal
- PSize
- Point size value
- Specular
- Specular lighting value
- Diffuse
- Diffuse lighting value
- Texcoord
- Eight texture coordinates
- Tangent
- Tangent
- Binormal
- Binormal
- Tess Factor
- Tessellation factor
Remarks
The LPD3DXWeldEpsilons type is defined as a pointer to
the D3DXWeldEpsilons structure.
typedef D3DXWELDEPSILONS *LPD3DXWELDEPSILONS;
对原始网格模型进行整理、简化之后,还需要检查处理后的网格模型是否有效,检查工作由函数D3DXValidMesh()完成,其声明如下:
Validates a mesh.
HRESULT D3DXValidMesh(
LPD3DXMESH pMeshIn,
CONST DWORD * pAdjacency,
LPD3DXBUFFER * ppErrorsAndWarnings
);
Parameters
- pMeshIn
- [in] Pointer to an ID3DXMesh interface,
representing the mesh to be tested.
- pAdjacency
- [in] Pointer to an array of three DWORDs per face
that specify the three neighbors for each face in the mesh to be tested.
- ppErrorsAndWarnings
- [out] Returns a buffer containing a string of
errors and warnings, which explain the problems found in the mesh.
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:
D3DXERR_INVALIDMESH, D3DERR_INVALIDCALL, E_OUTOFMEMORY.
Remarks
This method validates the mesh by checking for invalid
indices. Error information is available from the debugger output.
在上面的准备工作完成后,可以调用函数D3DXGeneratePMesh()根据初始网格模型生成层次细节网格模型,其声明如下:
Generates a progressive mesh.
HRESULT D3DXGeneratePMesh(
LPD3DXMESH pMesh,
CONST DWORD * pAdjacency,
CONST D3DXATTRIBUTEWEIGHTS * pVertexAttributeWeights,
CONST FLOAT * pVertexWeights,
DWORD MinValue,
DWORD Options,
LPD3DXPMESH * ppPMesh
);
Parameters
- pMesh
- [in] Pointer to an ID3DXMesh interface,
representing the source mesh.
- pAdjacency
- [in] Pointer to an array of three DWORDs per face
that specify the three neighbors for each face in the created progressive
mesh.
- pVertexAttributeWeights
- [in] Pointer to a D3DXATTRIBUTEWEIGHTS structure,
containing the weight for each vertex component. If this parameter is set to
NULL, a default structure is used. See Remarks.
- pVertexWeights
- [in] Pointer to an array of vertex weights. If
this parameter is set to NULL, all vertex weights are set to 1.0. Note that
the higher the vertex weight for a given vertex, the less likely it is to be
simplified away.
- MinValue
- [in] Number of vertices or faces, depending on the
flag set in the Options parameter, by which to simplify the source mesh.
- Options
- [in] Specifies simplification options for the
mesh. One flag from the D3DXMESHSIMP enumeration can be set.
- ppPMesh
- [out] Address of a pointer to an ID3DXPMesh
interface, representing the created progressive mesh.
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:
D3DXERR_CANNOTATTRSORT, D3DERR_INVALIDCALL, E_OUTOFMEMORY.
Remarks
This function generates a mesh where the level of
detail (LOD) can be adjusted from the current value to the MinValue.
If the simplification process cannot reduce the mesh to
MinValue, the call still succeeds because MinValue is a desired minimum, not an
absolute minimum.
If pVertexAttributeWeights is set to NULL, the
following values are assigned to the default D3DXATTRIBUTEWEIGHTS
structure.
D3DXATTRIBUTEWEIGHTS AttributeWeights;
AttributeWeights.Position = 1.0;
AttributeWeights.Boundary = 1.0;
AttributeWeights.Normal = 1.0;
AttributeWeights.Diffuse = 0.0;
AttributeWeights.Specular = 0.0;
AttributeWeights.Tex[8] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
This default structure is what most applications should
use because it considers only geometric and normal adjustment. Only in special
cases will the other member fields need to be modified.
D3DXMESHSIMP的定义如下:
Specifies simplification options for a mesh.
typedef enum D3DXMESHSIMP
{
D3DXMESHSIMP_VERTEX = 1,
D3DXMESHSIMP_FACE = 2,
} D3DXMESHSIMP, *LPD3DXMESHSIMP;
Constants
- D3DXMESHSIMP_VERTEX
- The mesh will be simplified by the number of
vertices specified in the MinValue parameter.
- D3DXMESHSIMP_FACE
- The mesh will be simplified by the number of faces
specified in the MinValue parameter.
参数MinValue设置的最小顶点数只是一个期望值,实际生成的层次细节网格模型的最小顶点数量可能大于设置的最小顶点数量。
示例程序ProgressMesh的具体实现
示例程序演示了层次细节网格模型的生成和渲染,其中加载原始网格模型、简化熔合原始网格并生成层次细节网格模型的代码如下:
ID3DXBuffer* material_buffer;
V_RETURN(D3DXLoadMeshFromXW(L"Dwarf.x", D3DXMESH_MANAGED, pd3dDevice, &g_adj_buffer, &material_buffer, NULL,
&g_num_materials, &g_mesh));
D3DXMATERIAL* xmaterials = (D3DXMATERIAL*) material_buffer->GetBufferPointer();
g_mesh_materials = new D3DMATERIAL9[g_num_materials];
g_mesh_textures = new IDirect3DTexture9*[g_num_materials];
for(DWORD i = 0; i < g_num_materials; i++)
{
g_mesh_materials[i] = xmaterials[i].MatD3D;
g_mesh_materials[i].Ambient = g_mesh_materials[i].Diffuse;
WCHAR wfilename[256];
RemovePathFromFileName(xmaterials[i].pTextureFilename, wfilename);
g_mesh_textures[i] = NULL;
if(xmaterials[i].pTextureFilename != NULL && lstrlen(wfilename) > 0)
{
V_RETURN(D3DXCreateTextureFromFileW(pd3dDevice, wfilename, &g_mesh_textures[i]));
}
}
material_buffer->Release();
ID3DXMesh* cleaned_mesh;
DWORD* adj = (DWORD*) g_adj_buffer->GetBufferPointer();
V_RETURN(D3DXCleanMesh(D3DXCLEAN_SIMPLIFICATION, g_mesh, adj, &cleaned_mesh, adj, NULL));
release_com(g_mesh);
g_mesh = cleaned_mesh;
D3DXWELDEPSILONS weld_epsilons;
ZeroMemory(&weld_epsilons, sizeof(D3DXWELDEPSILONS));
V_RETURN(D3DXWeldVertices(g_mesh, D3DXWELDEPSILONS_WELDPARTIALMATCHES, &weld_epsilons, adj, adj, NULL, NULL));
V_RETURN(D3DXValidMesh(g_mesh, adj, NULL));
V_RETURN(D3DXGeneratePMesh(g_mesh, adj, NULL, NULL, 1, D3DXMESHSIMP_VERTEX, &g_progress_mesh));
运行效果图:
主程序:
#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 IDC_DETAIL 4
#define release_com(p) do { if(p) { (p)->Release(); (p) = NULL; } } while(0)
ID3DXFont* g_font;
ID3DXSprite* g_text_sprite;
bool g_show_help = true;
CDXUTDialogResourceManager g_dlg_resource_manager;
CD3DSettingsDlg g_settings_dlg;
CDXUTDialog g_button_dlg;
CDXUTDialog g_ui_dlg;
ID3DXMesh* g_mesh;
D3DMATERIAL9* g_mesh_materials;
IDirect3DTexture9** g_mesh_textures;
DWORD g_num_materials;
ID3DXBuffer* g_adj_buffer;
ID3DXPMesh* g_progress_mesh;
//--------------------------------------------------------------------------------------
// 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);
ID3DXBuffer* material_buffer;
V_RETURN(D3DXLoadMeshFromXW(L"Dwarf.x", D3DXMESH_MANAGED, pd3dDevice, &g_adj_buffer, &material_buffer, NULL,
&g_num_materials, &g_mesh));
D3DXMATERIAL* xmaterials = (D3DXMATERIAL*) material_buffer->GetBufferPointer();
g_mesh_materials = new D3DMATERIAL9[g_num_materials];
g_mesh_textures = new IDirect3DTexture9*[g_num_materials];
for(DWORD i = 0; i < g_num_materials; i++)
{
g_mesh_materials[i] = xmaterials[i].MatD3D;
g_mesh_materials[i].Ambient = g_mesh_materials[i].Diffuse;
WCHAR wfilename[256];
RemovePathFromFileName(xmaterials[i].pTextureFilename, wfilename);
g_mesh_textures[i] = NULL;
if(xmaterials[i].pTextureFilename != NULL && lstrlen(wfilename) > 0)
{
V_RETURN(D3DXCreateTextureFromFileW(pd3dDevice, wfilename, &g_mesh_textures[i]));
}
}
material_buffer->Release();
ID3DXMesh* cleaned_mesh;
DWORD* adj = (DWORD*) g_adj_buffer->GetBufferPointer();
V_RETURN(D3DXCleanMesh(D3DXCLEAN_SIMPLIFICATION, g_mesh, adj, &cleaned_mesh, adj, NULL));
release_com(g_mesh);
g_mesh = cleaned_mesh;
D3DXWELDEPSILONS weld_epsilons;
ZeroMemory(&weld_epsilons, sizeof(D3DXWELDEPSILONS));
V_RETURN(D3DXWeldVertices(g_mesh, D3DXWELDEPSILONS_WELDPARTIALMATCHES, &weld_epsilons, adj, adj, NULL, NULL));
V_RETURN(D3DXValidMesh(g_mesh, adj, NULL));
V_RETURN(D3DXGeneratePMesh(g_mesh, adj, NULL, NULL, 1, D3DXMESHSIMP_VERTEX, &g_progress_mesh));
DWORD min_vertices = g_progress_mesh->GetMinVertices();
DWORD max_vertices = g_progress_mesh->GetMaxVertices();
g_ui_dlg.GetSlider(IDC_DETAIL)->SetRange(min_vertices, max_vertices);
g_ui_dlg.GetSlider(IDC_DETAIL)->SetValue(max_vertices);
g_progress_mesh->SetNumVertices(max_vertices);
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);
g_ui_dlg.SetLocation(0, pBackBufferSurfaceDesc->Height - 60);
g_ui_dlg.SetSize(pBackBufferSurfaceDesc->Width, 50);
g_ui_dlg.GetControl(IDC_DETAIL)->SetSize(pBackBufferSurfaceDesc->Width - 20, 16);
// setup view matrix
D3DXMATRIX mat_view;
D3DXVECTOR3 eye(0.0f, 0.0f, -4.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);
pd3dDevice->SetRenderState(D3DRS_AMBIENT, 0xFFFFFFFF);
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();
delete[] g_mesh_materials;
g_mesh_materials = NULL;
if(g_mesh_textures)
{
for(DWORD i = 0; i < g_num_materials; i++)
release_com(g_mesh_textures[i]);
delete[] g_mesh_textures;
g_mesh_textures = NULL;
}
release_com(g_font);
release_com(g_adj_buffer);
release_com(g_mesh);
release_com(g_progress_mesh);
}
//--------------------------------------------------------------------------------------
// Handle updates to the scene
//--------------------------------------------------------------------------------------
void CALLBACK OnFrameMove( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
{
D3DXMATRIX mat_world, mat_translation, mat_rotation;
D3DXMatrixTranslation(&mat_translation, 0, -0.7f, 0);
D3DXMatrixRotationY(&mat_rotation, timeGetTime() / 1000.0f);
mat_world = mat_translation * mat_rotation;
pd3dDevice->SetTransform(D3DTS_WORLD, &mat_world);
}
//--------------------------------------------------------------------------------------
// 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 other simple information
text_helper.SetForegroundColor( D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f) );
text_helper.DrawFormattedTextLine(L"Vertices Range: %u ~ %u\nCurrent Vertices Number: %u\n",
g_progress_mesh->GetMinVertices(),
g_progress_mesh->GetMaxVertices(),
g_progress_mesh->GetNumVertices());
// 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() ) )
{
for(DWORD i = 0; i < g_num_materials; i++)
{
pd3dDevice->SetMaterial(&g_mesh_materials[i]);
pd3dDevice->SetTexture(0, g_mesh_textures[i]);
g_progress_mesh->DrawSubset(i);
}
RenderText();
V(g_button_dlg.OnRender(fElapsedTime));
V(g_ui_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;
*pbNoFurtherProcessing = g_ui_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;
case IDC_DETAIL:
g_progress_mesh->SetNumVertices( ((CDXUTSlider*) control)->GetValue() );
}
}
//--------------------------------------------------------------------------------------
// Initialize dialogs
//--------------------------------------------------------------------------------------
void InitDialogs()
{
g_settings_dlg.Init(&g_dlg_resource_manager);
g_button_dlg.Init(&g_dlg_resource_manager);
g_ui_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);
g_ui_dlg.SetCallback(OnGUIEvent);
g_ui_dlg.AddSlider(IDC_DETAIL, 10, 36, 200, 16, 4, 4, 4);
}
//--------------------------------------------------------------------------------------
// 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"ProgressMesh" );
DXUTCreateDevice( D3DADAPTER_DEFAULT, true, 640, 480, IsDeviceAcceptable, ModifyDeviceSettings );
// Start the render loop
DXUTMainLoop();
// TODO: Perform any application-level cleanup here
return DXUTGetExitCode();
}
下载示例工程(仅包含工程文件和源码,不包含资源文件,资源请从示例程序OptimizedMesh中提取)