本篇是创建3D图形引擎(3)【OO改良版】的续篇,以创建游戏内核【OO改良版】中编写的代码为基础进行开发,细节说明请参阅创建3D图形引擎(4)。
下载源码和工程
接口:
/*************************************************************************
PURPOSE:
Interface for sky box.
*************************************************************************/
#ifndef _SKY_BOX_H
#define _SKY_BOX_H
#include "core_common.h"
enum SKY_BOX_SIDES { TOP = 0, BOTTOM, LEFT, RIGHT, FRONT, BACK };
//=====================================================================================
// This calss encapsulate how to make sky box.
//=====================================================================================
typedef class SKY_BOX
{
private:
typedef struct SKY_BOX_VERTEX
{
float x, y, z;
float u, v;
} *SKY_BOX_VERTEX_PTR;
#define SKY_BOX_FVF (D3DFVF_XYZ | D3DFVF_TEX1)
private:
TEXTURE m_textures[6]; // face texture (0 - 5)
VERTEX_BUFFER m_vertex_buffer; // mesh vertex buffer
WORLD_POSITION m_pos; // sky box position
public:
SKY_BOX();
~SKY_BOX();
BOOL create();
void free();
void set_default_state();
BOOL load_texture(short side, pcstr filename, D3DCOLOR transparent, D3DFORMAT format);
void rotate(float x_rot, float y_rot, float z_rot);
void rotate_rel(float x_rot, float y_rot, float z_rot);
BOOL render(CAMERA_PTR camera, BOOL alpha_blend);
} *SKY_BOX_PTR;
#endif
实现:
/*************************************************************************
PURPOSE:
Implement for sky box.
*************************************************************************/
#include "core_common.h"
#include "core_graphics.h"
#include "sky_box.h"
//----------------------------------------------------------------------------------
// Constructor, initialize member data.
//----------------------------------------------------------------------------------
SKY_BOX::SKY_BOX()
{
}
//----------------------------------------------------------------------------------
// Destructor, release allocated resource.
//----------------------------------------------------------------------------------
SKY_BOX::~SKY_BOX()
{
free();
}
//----------------------------------------------------------------------------------
// Release allocated resource.
//----------------------------------------------------------------------------------
void SKY_BOX::free()
{
for(short i = 0; i < 6; i++)
m_textures[i].free();
m_vertex_buffer.free();
}
//----------------------------------------------------------------------------------
// Set default state for sky box.
//----------------------------------------------------------------------------------
void SKY_BOX::set_default_state()
{
m_pos.set_default_state();
}
//----------------------------------------------------------------------------------
// Create a sky box class object.
//----------------------------------------------------------------------------------
BOOL SKY_BOX::create()
{
SKY_BOX_VERTEX verts[24] = {
{ -10.0f, 10.0f, -10.0f, 0.0f, 0.0f }, // Top
{ 10.0f, 10.0f, -10.0f, 1.0f, 0.0f },
{ -10.0f, 10.0f, 10.0f, 0.0f, 1.0f },
{ 10.0f, 10.0f, 10.0f, 1.0f, 1.0f },
{ -10.0f, -10.0f, 10.0f, 0.0f, 0.0f }, // Bottom
{ 10.0f, -10.0f, 10.0f, 1.0f, 0.0f },
{ -10.0f, -10.0f, -10.0f, 0.0f, 1.0f },
{ 10.0f, -10.0f, -10.0f, 1.0f, 1.0f },
{ -10.0f, 10.0f, -10.0f, 0.0f, 0.0f }, // Left
{ -10.0f, 10.0f, 10.0f, 1.0f, 0.0f },
{ -10.0f, -10.0f, -10.0f, 0.0f, 1.0f },
{ -10.0f, -10.0f, 10.0f, 1.0f, 1.0f },
{ 10.0f, 10.0f, 10.0f, 0.0f, 0.0f }, // Right
{ 10.0f, 10.0f, -10.0f, 1.0f, 0.0f },
{ 10.0f, -10.0f, 10.0f, 0.0f, 1.0f },
{ 10.0f, -10.0f, -10.0f, 1.0f, 1.0f },
{ -10.0f, 10.0f, 10.0f, 0.0f, 0.0f }, // Front
{ 10.0f, 10.0f, 10.0f, 1.0f, 0.0f },
{ -10.0f, -10.0f, 10.0f, 0.0f, 1.0f },
{ 10.0f, -10.0f, 10.0f, 1.0f, 1.0f },
{ 10.0f, 10.0f, -10.0f, 0.0f, 0.0f }, // Back
{ -10.0f, 10.0f, -10.0f, 1.0f, 0.0f },
{ 10.0f, -10.0f, -10.0f, 0.0f, 1.0f },
{ -10.0f, -10.0f, -10.0f, 1.0f, 1.0f },
};
free(); // free a prior sky box
// error checking
if(g_d3d_device == NULL)
return FALSE;
// create the vertex buffer (and copy over sky box vertices)
if(m_vertex_buffer.create(24, sizeof(SKY_BOX_VERTEX), SKY_BOX_FVF))
m_vertex_buffer.fill_in(0, 24, (void*)verts);
// rotate the sky box into default orientation
rotate(0.0f, 0.0f, 0.0f);
return TRUE;
}
//----------------------------------------------------------------------------------
// Set a specific side's texture map, allow for transparent and storage format changes.
//----------------------------------------------------------------------------------
BOOL SKY_BOX::load_texture(short side, pcstr filename, D3DCOLOR transparent, D3DFORMAT format)
{
// error checking
if(g_d3d_device == NULL || side < 0 || side > 5)
return FALSE;
m_textures[side].free(); // free prior texture
return m_textures[side].load(filename, transparent, format);
}
//----------------------------------------------------------------------------------
// Rotate box to an absolute rotation.
//----------------------------------------------------------------------------------
void SKY_BOX::rotate(float x_rot, float y_rot, float z_rot)
{
m_pos.rotate(x_rot, y_rot, z_rot);
}
//----------------------------------------------------------------------------------
// Rotate box to an relative rotation.
//----------------------------------------------------------------------------------
void SKY_BOX::rotate_rel(float x_rot, float y_rot, float z_rot)
{
m_pos.rotate_rel(x_rot, y_rot, z_rot);
}
//----------------------------------------------------------------------------------
// Render the sky box (using optional alpha-blending) and using current view
// transformation from camera.
//----------------------------------------------------------------------------------
BOOL SKY_BOX::render(CAMERA_PTR camera, BOOL alpha_blend)
{
// error checking
if(g_d3d_device == NULL || camera == NULL)
return FALSE;
// position sky box around viewer
m_pos.move(camera->get_x_pos(), camera->get_y_pos(), camera->get_z_pos());
g_d3d_device->SetTransform(D3DTS_WORLD, m_pos.get_world_matrix());
// enable alpha testing and alpha blending
enable_alpha_testing();
if(alpha_blend)
enable_alpha_blending(D3DBLEND_SRCCOLOR, D3DBLEND_DESTCOLOR);
// draw each layer
for(short i = 0; i < 6; i++)
{
if(m_textures[i].is_loaded())
{
g_d3d_device->SetTexture(0, m_textures[i].get_d3d_texture());
m_vertex_buffer.render(i * 4, 2, D3DPT_TRIANGLESTRIP);
}
}
// disable alpha testing and alpha blending
disable_alpha_testing();
if(alpha_blend)
disable_alpha_blending();
return TRUE;
}
测试代码:
/************************************************************************************
PURPOSE:
node tree mesh test.
************************************************************************************/
#include "core_common.h"
#include "core_framework.h"
#include "core_graphics.h"
#include "core_input.h"
#include "core_sound.h"
#include "frustum.h"
#include "node_tree_mesh.h"
#include "sky_box.h"
class APP : public FRAMEWORK
{
public:
BOOL init()
{
if(! create_display(g_hwnd, get_client_width(g_hwnd), get_client_height(g_hwnd), 16, TRUE, TRUE))
return FALSE;
set_perspective(D3DX_PI / 4, 1.3333f, 1.0f, 10000.0f);
ShowCursor(TRUE);
// enable lighting and setup light
g_d3d_device->SetRenderState(D3DRS_LIGHTING, TRUE);
set_ambient_light(24, 24, 24);
g_d3d_device->LightEnable(0, TRUE);
m_light.set_default_state();
m_light.set_attenuation_0(0.4f);
m_light.set_range(1000.0f);
m_camera.set_default_state();
// initialize input and input device
m_input.create(g_hwnd, get_window_inst());
m_keyboard.create_keyboard(&m_input);
m_mouse.create_mouse(&m_input, TRUE);
// load the mesh and create a nodetree mesh from it
if(! m_mesh.load("..\\Data\\Level.x", "..\\Data\\"))
return FALSE;
m_node_tree_mesh.create(&m_mesh, QUADTREE, 256.0f, 32);
// position view at origin
m_x_pos = m_y_pos = m_z_pos = 0.0f;
// setup sky box
m_sky_box.create();
m_sky_box.set_default_state();
for(short i = 0; i < 6; i++)
m_sky_box.load_texture(i, "..\\data\\stars.bmp", 0, D3DFMT_UNKNOWN);
// initialize the sound system to play with
m_sound.init(g_hwnd, 22050, 1, 16, DSSCL_PRIORITY);
m_sound_data.load_wav("..\\data\\cricket.wav");
for(short i = 0; i < 3; i++)
m_sound_channel[i].create(&m_sound, 22050, 1, 16);
return TRUE;
}
BOOL frame()
{
static DWORD time_now = timeGetTime();
// play a random cricket sound
for(short i = 0; i< 3; i++)
{
if(!m_sound_channel[i].is_playing() && rand()%256 < 16)
m_sound_channel[i].play(&m_sound_data, 10, 1);
}
// calculate elapsed time (plus speed boost)
ulong time_elapsed = timeGetTime() - time_now;
time_now = timeGetTime();
// read keyboard and mouse data
m_keyboard.read();
m_mouse.read();
// process input and update everything, ESC quits program.
if(m_keyboard.get_key_state(KEY_ESC))
return FALSE;
float x_move, z_move;
// process movement
x_move = z_move = 0.0f;
if(m_keyboard.get_key_state(KEY_UP) || m_keyboard.get_key_state(KEY_W))
{
x_move = (float) sin(m_camera.get_y_rotation()) * time_elapsed;
z_move = (float) cos(m_camera.get_y_rotation()) * time_elapsed;
}
if(m_keyboard.get_key_state(KEY_DOWN) || m_keyboard.get_key_state(KEY_S))
{
x_move = (float) -sin(m_camera.get_y_rotation()) * time_elapsed;
z_move = (float) -cos(m_camera.get_y_rotation()) * time_elapsed;
}
if(m_keyboard.get_key_state(KEY_LEFT) || m_keyboard.get_key_state(KEY_A))
{
x_move = (float) sin(m_camera.get_y_rotation() - 1.57f) * time_elapsed;
z_move = (float) cos(m_camera.get_y_rotation() - 1.57f) * time_elapsed;
}
if(m_keyboard.get_key_state(KEY_RIGHT) || m_keyboard.get_key_state(KEY_D))
{
x_move = (float) sin(m_camera.get_y_rotation() + 1.57f) * time_elapsed;
z_move = (float) cos(m_camera.get_y_rotation() + 1.57f) * time_elapsed;
}
// check for height changes (can step up to 64 units)
float height = m_node_tree_mesh.closest_height_below_object(m_x_pos, m_y_pos + m_above_floor, m_z_pos);
if(m_y_pos > height)
{
// dropping
if((m_y_pos -= (float)time_elapsed) < height)
m_y_pos = height;
else
x_move = z_move = 0.0f;
}
else
{
// climbing
m_y_pos = height;
}
float dist;
// check for movement collision - can not walk past anything blocking path.
if(m_node_tree_mesh.is_ray_intersect_mesh(m_x_pos, m_y_pos + m_above_floor, m_z_pos,
m_x_pos + x_move, m_y_pos + m_above_floor, m_z_pos + z_move,
&dist))
{
// adjust coordinates to be exactly 2.5 units away from target
float diff = dist - 2.5f;
D3DXVECTOR2 dir;
D3DXVec2Normalize(&dir, &D3DXVECTOR2(x_move, z_move));
dir *= diff;
x_move = dir.x;
z_move = dir.y;
}
// update view coordinats
m_x_pos += x_move;
m_z_pos += z_move;
// position camera and rotate based on mouse position
m_camera.move(m_x_pos, m_y_pos + 50.0f, m_z_pos);
// m_mouse.get_y_delta():
// get mouse's relative x movement coordinate.
//
// m_mouse.get_x_delta():
// get mouse's relative y movement coordinate.
m_camera.rotate_rel((float) m_mouse.get_y_delta() / 200.0f, (float) m_mouse.get_x_delta() / 200.0f, 0.0f);
// position
m_light.move(m_x_pos, m_y_pos + 60.0f, m_z_pos);
g_d3d_device->SetLight(0, m_light.get_d3d_light());
FRUSTUM frustum;
// set camera and calculate frustum
g_d3d_device->SetTransform(D3DTS_VIEW, m_camera.get_view_matrix());
frustum.create(0.0f);
// render everything
clear_display_zbuffer(1.0f);
//clear_display(0, 1.0f);
// begin render now
if(SUCCEEDED(g_d3d_device->BeginScene()))
{
g_d3d_device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
g_d3d_device->SetRenderState(D3DRS_LIGHTING, FALSE);
m_sky_box.render(&m_camera, FALSE);
g_d3d_device->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
g_d3d_device->SetRenderState(D3DRS_LIGHTING, TRUE);
m_node_tree_mesh.render(&frustum, 0.0f);
g_d3d_device->EndScene();
}
present_display();
return TRUE;
}
BOOL shutdown()
{
return TRUE;
}
private:
CAMERA m_camera;
LIGHT m_light;
SOUND m_sound;
SOUND_DATA m_sound_data;
SOUND_CHANNEL m_sound_channel[3];
SKY_BOX m_sky_box;
INPUT m_input;
INPUT_DEVICE m_keyboard;
INPUT_DEVICE m_mouse;
MESH m_mesh;
NODE_TREE_MESH m_node_tree_mesh;
float m_x_pos, m_y_pos, m_z_pos;
static const float m_above_floor;
};
const float APP::m_above_floor = 64.0f;
int WINAPI WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line, int cmd_show)
{
DWORD client_width = 640;
DWORD client_height = 480;
DWORD x_pos = (get_screen_width() - client_width) / 2;
DWORD y_pos = (get_screen_height() - client_height) / 4;
if(! build_window(inst, "node_tree_mesh_class", "node tree mesh test",
WS_BORDER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU,
x_pos, y_pos, client_width, client_height))
{
return -1;
}
APP app;
app.run();
return 0;
}