13.6例子程序: Terrain
该例子是用一个包含高度信息的RAW文件创建一个地形,纹理和光源。用方向键在地形上行走。
fps.h:
/*********************************************************************************
PURPOISE:
Wraps the code to compute and display the frames rendered per second.
*********************************************************************************/
#ifndef FPS_H
#define FPS_H
#include <d3dx9.h>
class cFpsCounter
{
private:
IDirect3DDevice9* m_device;
ID3DXFont* m_font;
DWORD m_frame_count;
float m_time_elapsed;
float m_fps;
char m_fps_string[9];
public:
cFpsCounter(IDirect3DDevice9* device);
~cFpsCounter();
void render(D3DCOLOR color, float time_delta,
int window_width, int window_height);
};
#endif
fps.cpp:
/*********************************************************************************
PURPOISE:
Wraps the code to compute and display the frames rendered per second.
*********************************************************************************/
#include <cstdio>
#include "d3dUtility.h"
#include "fps.h"
#pragma warning(disable : 4996)
cFpsCounter::cFpsCounter(IDirect3DDevice9* device)
{
m_device = device;
D3DXFONT_DESC font_desc;
ZeroMemory(&font_desc, sizeof(font_desc));
font_desc.Height = 25; // in logical units
font_desc.Width = 12; // in logical units
font_desc.Weight = 500; // boldness, range 0(light) - 1000(bold)
font_desc.Italic = FALSE;
font_desc.CharSet = DEFAULT_CHARSET;
font_desc.OutputPrecision = 0;
font_desc.Quality = 0;
font_desc.PitchAndFamily = 0;
strcpy(font_desc.FaceName, "Times New Roman");
D3DXCreateFontIndirect(device, &font_desc, &m_font);
m_frame_count = 0;
m_time_elapsed = 0.0f;
m_fps = 0.0f;
m_fps_string[0] = '\0';
}
cFpsCounter::~cFpsCounter()
{
safe_release<ID3DXFont*>(m_font);
}
void cFpsCounter::render(D3DCOLOR color, float time_delta,
int window_width, int window_height)
{
if(m_font == NULL)
return;
m_frame_count++;
m_time_elapsed += time_delta;
if(m_time_elapsed >= 1.0f)
{
m_fps = m_frame_count / m_time_elapsed;
sprintf(m_fps_string, "%f", m_fps);
m_fps_string[8] = '\0'; // mark end of string
m_time_elapsed = 0.0f;
m_frame_count = 0;
}
RECT rect = {0, 0, window_width, window_height};
m_font->DrawText(NULL, m_fps_string, -1, &rect, DT_TOP | DT_LEFT, color);
}
TerrainApp.cpp:
/**************************************************************************************
Renders a terrain and allows you to walk around it.
**************************************************************************************/
#include "d3dUtility.h"
#include "camera.h"
#include "terrain.h"
#include "fps.h"
#pragma warning(disable : 4100)
const int WIDTH = 640;
const int HEIGHT = 480;
IDirect3DDevice9* g_device;
cTerrain* g_terrain;
cCamera g_camera(LAND_OBJECT);
cFpsCounter* g_fps_counter;
bool g_draw_triangle;
////////////////////////////////////////////////////////////////////////////////////////////////////
bool setup()
{
// create the terrain
D3DXVECTOR3 dir_to_light(0.0f, 1.0f, 0.0f);
g_terrain = new cTerrain(g_device, "coastMountain64.raw", 64, 64, 10, 0.5f);
g_terrain->generate_texture(&dir_to_light);
g_fps_counter = new cFpsCounter(g_device);
// set texture filters
g_device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
g_device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
g_device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
// set the projection matrix
D3DXMATRIX proj;
D3DXMatrixPerspectiveFovLH(&proj, D3DX_PI/2.0f, (float)WIDTH/HEIGHT, 1.0f, 1000.0f);
g_device->SetTransform(D3DTS_PROJECTION, &proj);
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
void cleanup()
{
delete g_terrain;
delete g_fps_counter;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
bool display(float time_delta)
{
// update the camera
if(GetAsyncKeyState(VK_UP) & 0x80000f)
g_camera.walk(100.0f * time_delta);
if( GetAsyncKeyState(VK_DOWN) & 0x8000f )
g_camera.walk(-100.0f * time_delta);
if( GetAsyncKeyState(VK_LEFT) & 0x8000f )
g_camera.yaw(-1.0f * time_delta);
if( GetAsyncKeyState(VK_RIGHT) & 0x8000f )
g_camera.yaw(1.0f * time_delta);
if( GetAsyncKeyState('N') & 0x8000f )
g_camera.strafe(-100.0f * time_delta);
if( GetAsyncKeyState('M') & 0x8000f )
g_camera.strafe(100.0f * time_delta);
if(GetAsyncKeyState('W') & 0x8000f)
g_camera.pitch(1.0f * time_delta);
if(GetAsyncKeyState('S') & 0x8000f)
g_camera.pitch(-1.0f * time_delta);
// set camera height
float height = g_terrain->get_height(g_camera.m_pos.x, g_camera.m_pos.z);
g_camera.m_pos.y = height + 5.0f; // add height because we're standing up
// update the view matrix representing the camera's new position/orientation
D3DXMATRIX view_matrix;
g_camera.get_view_matrix(&view_matrix);
g_device->SetTransform(D3DTS_VIEW, &view_matrix);
// render now
g_device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0);
g_device->BeginScene();
D3DXMATRIX identity_matrix;
D3DXMatrixIdentity(&identity_matrix);
g_terrain->draw(&identity_matrix, g_draw_triangle);
g_fps_counter->render(0xffffffff, time_delta, WIDTH, HEIGHT);
g_device->EndScene();
g_device->Present(NULL, NULL, NULL, NULL);
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK wnd_proc(HWND hwnd, UINT msg, WPARAM word_param, LPARAM long_param)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
if(word_param == VK_ESCAPE)
DestroyWindow(hwnd);
if(word_param == VK_SPACE)
g_draw_triangle = !g_draw_triangle;
break;
}
return DefWindowProc(hwnd, msg, word_param, long_param);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE inst, HINSTANCE, PSTR cmd_line, int cmd_show)
{
if(! init_d3d(inst, WIDTH, HEIGHT, true, D3DDEVTYPE_HAL, &g_device))
{
MessageBox(NULL, "init_d3d() - failed.", 0, MB_OK);
return 0;
}
if(! setup())
{
MessageBox(NULL, "Steup() - failed.", 0, MB_OK);
return 0;
}
enter_msg_loop(display);
cleanup();
g_device->Release();
return 0;
}
下载源程序
Terrain读取顶点数据到一个很大的缓存,在多重的顶点缓存中划分地形结构,在速度和可测量性方面都十分有利。为我们提出一个问题:顶点缓存最大支持多大?回答是,这依赖于你的硬件。所以你必须先检测。
将地图划分为许多小的顶点缓存是重要的练习,然后将类似矩阵的数据结构编入索引,并且管理数据,这不需要引入新的概念。我们不必详细讨论它。简单的说,你基本上站在地形中一个我们叫做“blocks”的矩阵上,每个block是地形的一个矩形区域。另外,每个block区域(在它自己的顶点索引缓存中)的下方包含地形中的几何信息,为了画它在地形中的位置。
另外,你可以读取地形到一个很大的ID3DXMesh接口。使用D3D函数D3DXSplitMesh划分地形为许多小的Mesh,以下是D3DXSplitMesh函数原型:
void D3DXSplitMesh(
const LPD3DXMESH pMeshIn,
const DWORD *pAdjacencyIn,
const DWORD MaxSize,
const DWORD Options,
DWORD *pMeshesOut,
LPD3DXBUFFER *ppMeshArrayOut,
LPD3DXBUFFER *ppAdjacencyArrayOut,
LPD3DXBUFFER *ppFaceRemapArrayOut,
LPD3DXBUFFER *ppVertRemapArrayOut
);
|
这个函数将一个源Mesh划分多个小的Mesh,,pMeshIn参数是一个指针,指向想划分的Mesh,pAdjacencyIn指向一个邻接数组,MaxSize参数指定作为结果返回的最大顶点数,为返回的Meshe使用指定的创建标记,pMeshesOut参数返回ppMeshArrayOut数组中的Mesh数量,最后3个参数是可选的(可以指定为null),返回邻接信息的数组。