在3D虚拟世界中检测碰撞?如果世界是由多个动态物体构成的,充满了成万或成千万的三角形,那么如何用一种快速有效的方式检测和避免可能发生的碰撞?要解决这个问题其实并不简单。使用数学运算检查两个三角形之间是否存在碰撞,非常消耗CPU,同样对于射线到三角形的测试也是如此。假想一下,如果一个三角形需要很大的CPU开支,那么几千个或几百万个三角形将会立刻摧毁所有现代处理器的性能。
在游戏开发中实现某些事物的最快方法是采用捷径,并在必要时使用近似值。如果问题可以简化为一个更容易处理、计算更快的形式,那么有可能实现某一种技术而不会影响程序性能,而这是在长时间做某件事情时才会出现的情况。然而这样做会产生新问题:使用捷径和近似值会强迫放弃某些精度。但幸运的是,问题并不总是那么糟糕,它取决于正在讨论的程序。大部分时间可以使用近似值和估计值侥幸取得成功,因为如果经历很长时间进行计算的话,用户也许不会注意到将要发生的事物和事物的表现形式出现的差异。这种观察是真实的,它不仅仅针对碰撞,对物理学、图形学或动画显示也是如此。大部分情况下,开发人员为了实现真实感,没必要得到十全十美的精确度。这在开发人员需要提高程序中影响性能的那片区域的速度时非常方便。如果游戏下降的精度不影响玩游戏的体验,并且如果正在讨论的问题严重影响性能,那最好的解决办法就是简化,并尽可能地使用提供的多种快捷方法。
边界体是一个环绕一个或一组几何图形(如球、盒子、椭圆球等)的形状。本章将介绍两类边界体——边界框和边界球,它们可以简化碰撞检测和加快处理速度,因为形状不像原始模型那样复杂。每个边界形状都有自己的优缺点,但它们具有的共同点是它们要比其他可选方案要快得多。
碰撞介绍
当一个几何图形占据了另一个几何图形的相同虚拟空间时就会发生碰撞。因为发生碰撞的两个以上物体要在时空上的某些点相会,这很有意义。当谈到物体间发生碰撞需要检测时,将碰撞从数学角度转换为真实反应时,会为程序增加真实感。当然,这样做主要是为了检查一个物体是否和另一个物体发生碰撞,这需要根据物体2中的每个其他多边形来测试物体1中的每个多边形。这是一个代价昂贵的方法,所以不可取。解决该问题的一个方法是对简化过的物体进行碰撞检测。例如,假定有一个由10000个多边形构成的模型,将其简化到1000个多边形。这样,在使用该场景中的物体时,速度要快很多,因为它只有真实情况下的场景中物体尺寸的10%。但是问题依然存在,因为三角形测试非常慢,几千个即使是只有几百个对象也足以摧毁程序的性能。最好的解决办法是为了确定是否可能出现碰撞,仅需几个满足条件的形状即可。而且,如果有满足这个目的的方法,最好能将昂贵的三角形测试一起去掉,这样就不会影响程序的性能。下面要介绍的边界框和边界球信息就能派得上用场。
边界框
边界框是一个围绕在物体周围的方框。它用于实现许多不同的技术,诸如可见度判断,以及包含碰撞检测的方法。边界框是一个简单的结构,它由x、y、z矢量的最大值和最小值构成。物体的边界框尽可能地靠和物体的极点围绕物体。
边界框中有许多浪费的空间。出现这种情况是因为框中的某些区域不包含任何几何图形,只是为了让方框保证它的样子,才导致出现浪费的空间。这样在碰撞检测中会导致精确度降低,因为在两个物体实际上没有发生碰撞时,测试结果也会显示为真,即这两个物体发生了碰撞。这意味着在碰撞检测中使用了快捷的方法,而不具有正确的精确度。
一种将问题最小化的方法是创建一个大的围绕物体的边界框,以及一些较小的围绕每个主题部分的边界框。一旦根据大的边界框进行的测试通过的话,那么就开始进行较小的边界框测试。如果大的边界框测试结果为真,那么同样可以认为较小的边界框测试也可能为真。如果大的边界框测试没有通过,那么就没有必要测试模型中较小的边界框了。虽然这样做可以得到更好的精确度,但是在使用边界框时却存在浪费的空间,不过,至少这种方法要比只使用一个大的边界框所浪费的空间要少一些。
通过循环所有的对象顶点并确定x、y、z的最大值和最小值就可以简单地计算出边界框。这如同使用两个3D矢量一样简单,一个3D矢量代表最小值,另一个代表最大值,使用if语句可以检查每个三角形中每个点的每个轴要么比最小值小,要么比最大值大。如果是这样的情况,那么更新那些变量,并移动到下一个顶点。当循环完顶点列表中所有的顶点时,就可以得到最大值和最小值。
所要做的全部工作是将顶点位置循环一遍,并确定最小轴和最大轴。可以是使用该信息构造完全适合对象的方形盒子,该盒子是函数创建边界框时要用到的对象。
使用边界框测试碰撞
测试点是否在盒子内是很简单的事情。如果点的x、y、z坐标位置比边界框的最小轴大,比最大轴要小,那么可认为存在碰撞。有两种方法可用于测试一个盒子是否在另一个盒子内部。首先,要计算盒子的4个角的坐标位置,并对每个角进行点/盒子测试。如果任意一个角的测试结果为真,那么可以认为这两个盒子之间发生碰撞。另一个更好的办法是确保第一个盒子的最小值要比第二个盒子的最大值小,而它的最大坐标轴要比第二个盒子的最小坐标轴要大。
bool isPointInside(CVector3 &v, CVector3 min, CVector3 max)
{
if (max.x <= v.x) return false;
if (min.x >= v.x) return false;
if (max.y <= v.y) return false;
if (min.y >= v.y) return false;
if (max.z <= v.z) return false;
if (min.z >= v.z) return false;
return true;
}
bool OnCollision(stBoundingBox &bb1, stBoundingBox &bb2)
{
if ( (bb1.min[0] > bb2.max[0]) || (bb2.min[0] > bb1.max[0]) )
return false;
if ( (bb1.min[1] > bb2.max[1]) || (bb2.min[1] > bb1.max[1]) )
return false;
if ( (bb1.min[2] > bb2.max[2]) || (bb2.min[2] > bb1.max[2]) )
return false;
return true;
}
并不是所有的边框结构都是由最小和最大矢量构成的,与3D打交道时,这个矢量可以封装6个浮点值。边界框可以由4个浮点数构成,其中3个代表了盒子的中心位置,而第四个代表了盒子的尺寸。对结构而言,对边界框使用4个浮点数需要的空间较少,而这是较合理的空间坐标。主要缺陷在于盒子变成了立方体。这样在尝试封装物体时会浪费更多的空间,因为需要为深度、宽度和高度使用最大的尺寸值,而不是使用每个方向上所必须的扩展值。程序清单9.3给出了一个使用4个浮点数以及一个最大、最小数值对来定义和创建边框的方法。
struct stBoundingBox
{
CVector3 min, max;
};
struct stBoundingBox
{
CVector3 center;
float size;
};
边界球
边界球完全围绕在一些几何图形周围,它除了不用方框之外,和边界框结构非常相似。边界球结构可以使用4个浮点值确定。前3个浮点值代表了边界球的中心位置,第四个浮点数代表了边界球的半径。和边界框相比较而言,边界球在处理碰撞检测和可见度时速度更快。使用边界球存在的主要问题是它们环绕造成的空间浪费要比边界框多。
计算边界球像计算边界框一样容易。为了计算边界球,需要对被考虑的几何图形的各个轴循环计算,并确定顶点的最大值和最小值。用最大结果值的平方根减去最小结果值的平方根就会得到球的半径。边界球的中心坐标就是差值除以2的结果。程序清单9.4给出了使用一列顶点定义和计算边界球的方法。
struct stBoundingSphere
{
CVector3 center;
double radius;
};
stBoundingSphere CalculateBoundingSphere(CVector3 *vList, int totalVerts)
{
float d, maxDistance = 0.0f;
stBoundingSphere sphere;
for(int i = 0; i < totalVerts; i++)
{
if(vList[i].x < min[0]) min[0] = vList[i].x;
if(vList[i].x > max[0]) max[0] = vList[i].x;
if(vList[i].y < min[1]) min[1] = vList[i].y;
if(vList[i].y > max[1]) max[1] = vList[i].y;
if(vList[i].z < min[2]) min[2] = vList[i].z;
if(vList[i].z > max[2]) max[2] = vList[i].z;
}
CVector3 center = (max + min) * 0.5f;
for(i = 0; i < totalVerts; i++)
{
d = ((vList[i].x - center.x) * (vList[i].x - center.x)) +
((vList[i].y - center.y) * (vList[i].y - center.y)) +
((vList[i].z - center.z) * (vList[i].z - center.z));
if(d > maxDistance)
maxDistance = d;
}
sphere.radius = sqrt(maxDistance);
sphere.center = center;
return sphere;
}
如前所述,边界球要比边界框更容易处理(碰撞、能见度等),因为对球所做的操作都是比较便宜的CPU相关的。这样在想要快速确定一个物体是否和其他物体发生碰撞或是那个物体是否可见的情况下,就最有可能选择使用边界球。某些混合法包括先使用边界球快速拒绝几何图形,然后使用边界框进行更准确的测试。如果这两个测试都通过了,那么接下来的操作要么是接受物体测试成功,要么是将物体分解成更小的块,然后分别对围绕这些块的边界框进行测试。当然,使用的方法很大程度上取决于程序的要求。取代使用大的球,然后是大的边界框,可以先使用大的边界球,然后再直接使用小一些的边界框。无论如何,最初使用边界球进行测试经过证明十分有用,因为进行碰撞和可见度测试使用的大量时间对包含大量物体的场景而言都是不够的。程序停止碰撞检测的速度越快,性能就越好。程序清单9.5给出了使用球体对球体碰撞测试法进行边界球碰撞测试的方法。
bool OnCollision(stBoundingSphere bs1, stBoundingSphere bs2)
{
// The distance between the two spheres.
CVector3 Intersect;
// Get difference.
Intersect = bs1.center - bs2.center;
// Test for collision.
if(sqrt(intersect.x * intersect.x +
intersect.y * intersect.y +
intersect.z * intersect.z) < bs1.radius + bs2.radius)
return true;
return false;
}
平面碰撞
如同在第8章介绍的一样,平面是3D场景中的无穷网格。通过使用一点平面数学知识,就可以很容易地确定某个顶点位于平面的哪一侧。同样也可以判断出点是否位于平面边界上。一种用于平面碰撞测试的方法是根据点的最后位置测试当前位置。例如若最后的位置在平面前面,而当前位置在平面上或平面后,那么就发生了碰撞。使用一系列平面将使程序可以确定体内是否有东西存在。例如,6个平面可以用于环绕摄象机视角。如果对象在任何一个平面内部(或外部),那么就可以确定对象是否可见,从而在渲染过程中忽略。该技术被称为平截头体选择(frustum
culling),因为平截头体是围绕摄象机视角创建的。如果在平截头体外存在对象的话,那么在渲染过程中就会忽略该对象。
就碰撞检测而言,如果对象边界体的任何一部分跨越出平面,就意味着物体的一部分在平面前,另一部分在平面后,那么这时就发生了碰撞。通过查看方形盒子角上的点是否跨越出了平面,可以测试是否发生了碰撞。即轴的最大值和最小值是否在平面的同一侧。如果不在同一侧,测试结束,开始运行响应碰撞的代码
边界框碰撞代码:
#include<d3d9.h>
#include<d3dx9.h>
#include<stdio.h>
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
#define WINDOW_CLASS "UGPDX"
#define WINDOW_NAME "Bounding Box"
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
// Function Prototypes...
bool InitializeD3D(HWND hWnd, bool fullscreen);
bool InitializeObjects();
void RenderScene();
void Shutdown();
// Direct3D object and device.
LPDIRECT3D9 g_D3D = NULL;
LPDIRECT3DDEVICE9 g_D3DDevice = NULL;
// Matrices.
D3DXMATRIX g_projection;
D3DXMATRIX g_worldMatrix;
D3DXMATRIX g_ViewMatrix;
struct stBoundingBox
{
stBoundingBox () { min[0] = min[1] = min[2] =
max[0] = max[1] = max[2] = 0; }
float min[3];
float max[3];
};
stBoundingBox g_boundingBox;
// Amounts to move each object.
float g_obj1XPos = -1;
float g_obj1MoveAmt = 0;
float g_obj1Dir = -0.02f;
float g_obj2XPos = 1;
float g_obj2MoveAmt = 0;
float g_obj2Dir = 0.02f;
// True if collision occurred, false if not.
bool g_collision = false;
// DirectX font object.
LPD3DXFONT g_Font = NULL;
// Vertex buffer to hold the geometry.
LPDIRECT3DVERTEXBUFFER9 g_VertexBuffer = NULL;
// A structure for our custom vertex type
struct stD3DVertex
{
float x, y, z;
unsigned long color;
};
// Our custom FVF, which describes our custom vertex structure
#define D3DFVF_VERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE)
void CalculateBoundingBox(stD3DVertex *vList, int totalVerts, stBoundingBox *bb)
{
if(!vList || totalVerts <= 0 || !bb) return;
for(int i = 0; i < totalVerts; i++)
{
if(vList[i].x < bb->min[0]) bb->min[0] = vList[i].x;
if(vList[i].x > bb->max[0]) bb->max[0] = vList[i].x;
if(vList[i].y < bb->min[1]) bb->min[1] = vList[i].y;
if(vList[i].y > bb->max[1]) bb->max[1] = vList[i].y;
if(vList[i].z < bb->min[2]) bb->min[2] = vList[i].z;
if(vList[i].z > bb->max[2]) bb->max[2] = vList[i].z;
}
}
// Point inside box.
bool OnCollision(stBoundingBox bb, float x, float y, float z)
{
if(bb.max[0] <= x) return false;
if(bb.min[0] >= x) return false;
if(bb.max[1] <= y) return false;
if(bb.min[1] >= y) return false;
if(bb.max[2] <= z) return false;
if(bb.min[2] >= z) return false;
// Return true then we have collision.
return true;
}
// Box on box.
bool OnCollision(stBoundingBox &bb1, stBoundingBox &bb2)
{
if((bb1.min[0] > bb2.max[0]) || (bb2.min[0] > bb1.max[0]))
return false;
// 因为物体只在x轴上运动,其实可以去掉在y,z轴上的判断
if((bb1.min[1] > bb2.max[1]) || (bb2.min[1] > bb1.max[1]))
return false;
if((bb1.min[2] > bb2.max[2]) || (bb2.min[2] > bb1.max[2]))
return false;
//
return true;
}
LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
case WM_KEYUP:
if(wParam == VK_ESCAPE)
PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE prevhInst, LPSTR cmdLine, int show)
{
// Register the window class
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
WINDOW_CLASS, NULL };
RegisterClassEx(&wc);
// Create the application's window
HWND hWnd = CreateWindow(WINDOW_CLASS, WINDOW_NAME, WS_OVERLAPPEDWINDOW,
100, 100, WINDOW_WIDTH, WINDOW_HEIGHT,
GetDesktopWindow(), NULL, wc.hInstance, NULL);
// Initialize Direct3D
if(InitializeD3D(hWnd, false))
{
// Show the window
ShowWindow(hWnd, SW_SHOWDEFAULT);
UpdateWindow(hWnd);
// Enter the message loop
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
RenderScene();
}
}
// Release any and all resources.
Shutdown();
// Unregister our window.
UnregisterClass(WINDOW_CLASS, wc.hInstance);
return 0;
}
bool InitializeD3D(HWND hWnd, bool fullscreen)
{
D3DDISPLAYMODE displayMode;
// Create the D3D object.
g_D3D = Direct3DCreate9(D3D_SDK_VERSION);
if(g_D3D == NULL) return false;
// Get the desktop display mode.
if(FAILED(g_D3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode)))
return false;
// Set up the structure used to create the D3DDevice
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
if(fullscreen)
{
d3dpp.Windowed = FALSE;
d3dpp.BackBufferWidth = WINDOW_WIDTH;
d3dpp.BackBufferHeight = WINDOW_HEIGHT;
}
else
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = displayMode.Format;
d3dpp.BackBufferCount = 1;
// Create the D3DDevice
if(FAILED(g_D3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE,
&d3dpp, &g_D3DDevice))) return false;
// Initialize any objects we will be displaying.
if(!InitializeObjects()) return false;
return true;
}
bool InitializeObjects()
{
// Create the font.
if(FAILED(D3DXCreateFont(g_D3DDevice, 18, 0, 0, 1, 0,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE, "Arial",
&g_Font))) return false;
// Fill in our structure to draw an object.
// x, y, z, color.
stD3DVertex objData[] =
{
{-0.3f, -0.4f, 0, D3DCOLOR_XRGB(255,255,255)},
{0.3f, -0.4f, 0, D3DCOLOR_XRGB(255,255,255)},
{0.3f, 0.4f, 0, D3DCOLOR_XRGB(255,255,255)},
{0.3f, 0.4f, 0, D3DCOLOR_XRGB(255,255,255)},
{-0.3f, 0.4f, 0, D3DCOLOR_XRGB(255,255,255)},
{-0.3f, -0.4f, 0, D3DCOLOR_XRGB(255,255,255)}
};
/* 算出边界框 */
CalculateBoundingBox(objData, 6, &g_boundingBox);
// Create the vertex buffer.
if(FAILED(g_D3DDevice->CreateVertexBuffer(sizeof(objData), 0,
D3DFVF_VERTEX, D3DPOOL_DEFAULT,
&g_VertexBuffer, NULL))) return false;
// Fill the vertex buffer.
void *ptr;
if(FAILED(g_VertexBuffer->Lock(0, sizeof(objData),
(void**)&ptr, 0))) return false;
memcpy(ptr, objData, sizeof(objData));
g_VertexBuffer->Unlock();
// Set default rendering states.
g_D3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
g_D3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
// Set the projection matrix.
D3DXMatrixPerspectiveFovLH(&g_projection, D3DX_PI / 4,
WINDOW_WIDTH/WINDOW_HEIGHT, 0.1f, 1000.0f);
g_D3DDevice->SetTransform(D3DTS_PROJECTION, &g_projection);
// Define camera information.
D3DXVECTOR3 cameraPos(0.0f, 0.0f, -5.0f);
D3DXVECTOR3 lookAtPos(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 upDir(0.0f, 1.0f, 0.0f);
// Build view matrix.
D3DXMatrixLookAtLH(&g_ViewMatrix, &cameraPos,
&lookAtPos, &upDir);
return true;
}
void RenderScene()
{
// RECT used to position the font and a string.
RECT fontPos = {0, 125, WINDOW_WIDTH, WINDOW_HEIGHT};
char str[64] = {0};
// Move objects. If position limit is hit, switch directions.
g_obj1MoveAmt += g_obj1Dir;
if(g_obj1MoveAmt > 2)
g_obj1Dir *= -1;
if(g_obj1MoveAmt < -2)
g_obj1Dir *= -1;
g_obj2MoveAmt += g_obj2Dir;
if(g_obj2MoveAmt > 2)
g_obj2Dir *= -1;
if(g_obj2MoveAmt < -2)
g_obj2Dir *= -1;
// Get bounding box for each object.
stBoundingBox newBB1, newBB2;
memcpy(&newBB1, &g_boundingBox, sizeof(stBoundingBox));
memcpy(&newBB2, &g_boundingBox, sizeof(stBoundingBox));
// Move boxes for each object along since they have moved.
// 只改变x方向
newBB1.min[0] = g_boundingBox.min[0] + (g_obj1XPos + g_obj1MoveAmt);
newBB1.max[0] = g_boundingBox.max[0] + (g_obj1XPos + g_obj1MoveAmt);
newBB2.min[0] = g_boundingBox.min[0] + (g_obj2XPos + g_obj2MoveAmt);
newBB2.max[0] = g_boundingBox.max[0] + (g_obj2XPos + g_obj2MoveAmt);
// Test for collision and record results.
g_collision = OnCollision(newBB1, newBB2);
// Create string.
if(g_collision)
sprintf(str, "Collision: TRUE");
else sprintf(str, "Collision: FALSE");
// Clear the backbuffer.
g_D3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f, 0);
// Begin the scene. Start rendering.
g_D3DDevice->BeginScene();
// Display if collision occurred.
g_Font->DrawText(NULL, str, -1, &fontPos, DT_CENTER,
D3DCOLOR_XRGB(255,255,255));
// Apply the view (camera).
g_D3DDevice->SetTransform(D3DTS_VIEW, &g_ViewMatrix);
// Set stream.
g_D3DDevice->SetStreamSource(0, g_VertexBuffer,
0, sizeof(stD3DVertex));
//两个矩形用的是同一套顶点列表,只是变换其在世界坐标系中的位置
// 这样就可以绘制出2个矩形来
// Draw square 1. 先画第一个矩形
D3DXMatrixTranslation(&g_worldMatrix, g_obj1XPos + g_obj1MoveAmt, 0, 0);
g_D3DDevice->SetTransform(D3DTS_WORLD, &g_worldMatrix);
g_D3DDevice->SetFVF(D3DFVF_VERTEX);
g_D3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);
// Draw square 2. 再画第二个矩形
D3DXMatrixTranslation(&g_worldMatrix, g_obj2XPos + g_obj2MoveAmt, 0, 0);
g_D3DDevice->SetTransform(D3DTS_WORLD, &g_worldMatrix);
g_D3DDevice->SetFVF(D3DFVF_VERTEX);
g_D3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);
// End the scene. Stop rendering.
g_D3DDevice->EndScene();
// Display the scene.
g_D3DDevice->Present(NULL, NULL, NULL, NULL);
}
void Shutdown()
{
if(g_D3DDevice != NULL)
g_D3DDevice->Release();
g_D3DDevice = NULL;
if(g_D3D != NULL)
g_D3D->Release();
g_D3D = NULL;
if(g_VertexBuffer != NULL)
g_VertexBuffer->Release();
g_VertexBuffer = NULL;
if(g_Font != NULL)
g_Font->Release();
g_Font = NULL;
}
边界球碰撞检测代码:
#include<d3d9.h>
#include<d3dx9.h>
#include<stdio.h>
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
#define WINDOW_CLASS "UGPDX"
#define WINDOW_NAME "Bounding Sphere"
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
// Function Prototypes...
bool InitializeD3D(HWND hWnd, bool fullscreen);
bool InitializeObjects();
void RenderScene();
void Shutdown();
// Direct3D object and device.
LPDIRECT3D9 g_D3D = NULL;
LPDIRECT3DDEVICE9 g_D3DDevice = NULL;
// Matrices.
D3DXMATRIX g_projection;
D3DXMATRIX g_worldMatrix;
D3DXMATRIX g_ViewMatrix;
struct stBoundingSphere
{
stBoundingSphere () { center[0] = center[1] =
center[2] = radius = 0; }
float center[3];
float radius;
};
stBoundingSphere g_boundingSphere;
// Amounts to move each object.
float g_obj1XPos = -1;
float g_obj1MoveAmt = 0;
float g_obj1Dir = -0.02f;
float g_obj2XPos = 1;
float g_obj2MoveAmt = 0;
float g_obj2Dir = 0.02f;
// True if collision occurred, false if not.
bool g_collision = false;
// DirectX font object.
LPD3DXFONT g_Font = NULL;
// Vertex buffer to hold the geometry.
LPDIRECT3DVERTEXBUFFER9 g_VertexBuffer = NULL;
// A structure for our custom vertex type
struct stD3DVertex
{
float x, y, z;
unsigned long color;
};
// Our custom FVF, which describes our custom vertex structure
#define D3DFVF_VERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE)
void CalculateBoundingSphere(stD3DVertex *vList, int totalVerts,
stBoundingSphere *bs)
{
if(!vList || totalVerts <= 0 || !bs) return;
float min[3] = {0}, max[3] = {0};
float dist = 0, maxDistance = 0.0f;
// Get min and max values.
for(int i = 0; i < totalVerts; i++)
{
if(vList[i].x < min[0]) min[0] = vList[i].x;
if(vList[i].x > max[0]) max[0] = vList[i].x;
if(vList[i].y < min[1]) min[1] = vList[i].y;
if(vList[i].y > max[1]) max[1] = vList[i].y;
if(vList[i].z < min[2]) min[2] = vList[i].z;
if(vList[i].z > max[2]) max[2] = vList[i].z;
}
// Calculate center.
bs->center[0] = (max[0] + min[0]) * 0.5f;
bs->center[1] = (max[1] + min[1]) * 0.5f;
bs->center[2] = (max[2] + min[2]) * 0.5f;
// Find max distance.
// 找四边形上的点与球心之间的距离的最大值就是半径
for(int i = 0; i < totalVerts; i++)
{
dist = ((vList[i].x - bs->center[0]) * (vList[i].x - bs->center[0])) +
((vList[i].y - bs->center[1]) * (vList[i].y - bs->center[1])) +
((vList[i].z - bs->center[2]) * (vList[i].z - bs->center[2]));
if(dist > maxDistance)
maxDistance = dist;
}
// Calculate radius.
bs->radius = sqrt(maxDistance);
}
// Sphere on Sphere.
bool OnCollision(stBoundingSphere &bb1, stBoundingSphere &bb2)
{
// The distance between the two spheres.
float intersect[3];
intersect[0] = bb1.center[0] - bb2.center[0];
intersect[1] = bb1.center[1] - bb2.center[1];
intersect[2] = bb1.center[2] - bb2.center[2];
// Test for collision.
if(sqrt(intersect[0] * intersect[0] +
intersect[1] * intersect[1] +
intersect[2] * intersect[2]) < bb1.radius + bb2.radius)
return true;
return false;
}
// Point inside sphere.
bool OnCollision(stBoundingSphere bs, float x, float y, float z)
{
// 将一个点看成半径为0的球体
stBoundingSphere bs2;
bs2.radius = 0;
bs2.center[0] = x;
bs2.center[1] = y;
bs2.center[2] = z;
return OnCollision(bs, bs2);
}
LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
case WM_KEYUP:
if(wParam == VK_ESCAPE) PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE prevhInst, LPSTR cmdLine, int show)
{
// Register the window class
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
WINDOW_CLASS, NULL };
RegisterClassEx(&wc);
// Create the application's window
HWND hWnd = CreateWindow(WINDOW_CLASS, WINDOW_NAME, WS_OVERLAPPEDWINDOW,
100, 100, WINDOW_WIDTH, WINDOW_HEIGHT,
GetDesktopWindow(), NULL, wc.hInstance, NULL);
// Initialize Direct3D
if(InitializeD3D(hWnd, false))
{
// Show the window
ShowWindow(hWnd, SW_SHOWDEFAULT);
UpdateWindow(hWnd);
// Enter the message loop
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
RenderScene();
}
}
// Release any and all resources.
Shutdown();
// Unregister our window.
UnregisterClass(WINDOW_CLASS, wc.hInstance);
return 0;
}
bool InitializeD3D(HWND hWnd, bool fullscreen)
{
D3DDISPLAYMODE displayMode;
// Create the D3D object.
g_D3D = Direct3DCreate9(D3D_SDK_VERSION);
if(g_D3D == NULL) return false;
// Get the desktop display mode.
if(FAILED(g_D3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode)))
return false;
// Set up the structure used to create the D3DDevice
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
if(fullscreen)
{
d3dpp.Windowed = FALSE;
d3dpp.BackBufferWidth = WINDOW_WIDTH;
d3dpp.BackBufferHeight = WINDOW_HEIGHT;
}
else
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = displayMode.Format;
d3dpp.BackBufferCount = 1;
// Create the D3DDevice
if(FAILED(g_D3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE,
&d3dpp, &g_D3DDevice))) return false;
// Initialize any objects we will be displaying.
if(!InitializeObjects()) return false;
return true;
}
bool InitializeObjects()
{
// Create the font.
if(FAILED(D3DXCreateFont(g_D3DDevice, 18, 0, 0, 1, 0,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE, "Arial",
&g_Font))) return false;
// Fill in our structure to draw an object.
// x, y, z, color, texture coords.
stD3DVertex objData[] =
{
{-0.3f, -0.4f, 0, D3DCOLOR_XRGB(255,255,255)},
{0.3f, -0.4f, 0, D3DCOLOR_XRGB(255,255,255)},
{0.3f, 0.4f, 0, D3DCOLOR_XRGB(255,255,255)},
{0.3f, 0.4f, 0, D3DCOLOR_XRGB(255,255,255)},
{-0.3f, 0.4f, 0, D3DCOLOR_XRGB(255,255,255)},
{-0.3f, -0.4f, 0, D3DCOLOR_XRGB(255,255,255)}
};
// Calculate bounding sphere data for the mesh.
CalculateBoundingSphere(objData, 6, &g_boundingSphere);
// Create the vertex buffer.
if(FAILED(g_D3DDevice->CreateVertexBuffer(sizeof(objData), 0,
D3DFVF_VERTEX, D3DPOOL_DEFAULT,
&g_VertexBuffer, NULL))) return false;
// Fill the vertex buffer.
void *ptr;
if(FAILED(g_VertexBuffer->Lock(0, sizeof(objData),
(void**)&ptr, 0))) return false;
memcpy(ptr, objData, sizeof(objData));
g_VertexBuffer->Unlock();
// Set default rendering states.
g_D3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
g_D3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
// Set the projection matrix.
D3DXMatrixPerspectiveFovLH(&g_projection, D3DX_PI / 4,
WINDOW_WIDTH/WINDOW_HEIGHT, 0.1f, 1000.0f);
g_D3DDevice->SetTransform(D3DTS_PROJECTION, &g_projection);
// Define camera information.
D3DXVECTOR3 cameraPos(0.0f, 0.0f, -5.0f);
D3DXVECTOR3 lookAtPos(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 upDir(0.0f, 1.0f, 0.0f);
// Build view matrix.
D3DXMatrixLookAtLH(&g_ViewMatrix, &cameraPos,
&lookAtPos, &upDir);
return true;
}
void RenderScene()
{
// RECT used to position the font and a string.
RECT fontPos = {0, 125, WINDOW_WIDTH, WINDOW_HEIGHT};
char str[64] = {0};
// Move objects. If position limit is hit, switch directions.
g_obj1MoveAmt += g_obj1Dir;
if(g_obj1MoveAmt > 2) g_obj1Dir *= -1;
if(g_obj1MoveAmt < -2) g_obj1Dir *= -1;
g_obj2MoveAmt += g_obj2Dir;
if(g_obj2MoveAmt > 2) g_obj2Dir *= -1;
if(g_obj2MoveAmt < -2) g_obj2Dir *= -1;
// Get bounding sphere for each object.
stBoundingSphere newBS1, newBS2;
memcpy(&newBS1, &g_boundingSphere, sizeof(stBoundingSphere));
memcpy(&newBS2, &g_boundingSphere, sizeof(stBoundingSphere));
// Move spheres for each object along since they have moved.
newBS1.center[0] = g_boundingSphere.center[0] +
(g_obj1XPos + g_obj1MoveAmt);
newBS2.center[0] = g_boundingSphere.center[0] +
(g_obj2XPos + g_obj2MoveAmt);
// Test for collision and record results.
g_collision = OnCollision(newBS1, newBS2);
// Create string.
if(g_collision) sprintf(str, "Collision: TRUE");
else sprintf(str, "Collision: FALSE");
// Clear the backbuffer.
g_D3DDevice->Clear(0, NULL, D3DCLEAR_TARGET,
D3DCOLOR_XRGB(0,0,0), 1.0f, 0);
// Begin the scene. Start rendering.
g_D3DDevice->BeginScene();
// Display if collision occurred.
g_Font->DrawText(NULL, str, -1, &fontPos, DT_CENTER,
D3DCOLOR_XRGB(255,255,255));
// Apply the view (camera).
g_D3DDevice->SetTransform(D3DTS_VIEW, &g_ViewMatrix);
// Set stream.
g_D3DDevice->SetStreamSource(0, g_VertexBuffer,
0, sizeof(stD3DVertex));
// Draw square 1.
D3DXMatrixTranslation(&g_worldMatrix, g_obj1XPos + g_obj1MoveAmt, 0, 0);
g_D3DDevice->SetTransform(D3DTS_WORLD, &g_worldMatrix);
g_D3DDevice->SetFVF(D3DFVF_VERTEX);
g_D3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);
// Draw square 2.
D3DXMatrixTranslation(&g_worldMatrix, g_obj2XPos + g_obj2MoveAmt, 0, 0);
g_D3DDevice->SetTransform(D3DTS_WORLD, &g_worldMatrix);
g_D3DDevice->SetFVF(D3DFVF_VERTEX);
g_D3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);
// End the scene. Stop rendering.
g_D3DDevice->EndScene();
// Display the scene.
g_D3DDevice->Present(NULL, NULL, NULL, NULL);
}
void Shutdown()
{
if(g_D3DDevice != NULL)
g_D3DDevice->Release();
g_D3DDevice = NULL;
if(g_D3D != NULL)
g_D3D->Release();
g_D3D = NULL;
if(g_VertexBuffer != NULL)
g_VertexBuffer->Release();
g_VertexBuffer = NULL;
if(g_Font != NULL)
g_Font->Release();
g_Font = NULL;
}
平面碰撞检测代码:
#include<d3d9.h>
#include<d3dx9.h>
#include<stdio.h>
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
#define WINDOW_CLASS "UGPDX"
#define WINDOW_NAME "Plane/Line Collision Detection"
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
// Function Prototypes...
bool InitializeD3D(HWND hWnd, bool fullscreen);
bool InitializeObjects();
void RenderScene();
void Shutdown();
// Direct3D object and device.
LPDIRECT3D9 g_D3D = NULL;
LPDIRECT3DDEVICE9 g_D3DDevice = NULL;
// Matrices.
D3DXMATRIX g_projection;
D3DXMATRIX g_worldMatrix;
D3DXMATRIX g_ViewMatrix;
// DirectX font object.
LPD3DXFONT g_Font = NULL;
// Vertex buffers to hold the geometry.
LPDIRECT3DVERTEXBUFFER9 g_squareBuffer = NULL;
LPDIRECT3DVERTEXBUFFER9 g_lineBuffer = NULL;
// A structure for our custom vertex type
struct stD3DVertex
{
float x, y, z;
unsigned long color;
};
// Our custom FVF, which describes our custom vertex structure
#define D3DFVF_VERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE)
#define UGP_FRONT 0
#define UGP_BACK 1
#define UGP_ON_PLANE 2
class CPlane
{
public:
CPlane() { a = b = c = d = 0; }
void NormalizeVec(stD3DVertex &v)
{
// Normalize normal.
float lenght = (float)sqrt((v.x * v.x +
v.y * v.y +
v.z * v.z));
if(lenght == 0.0f) lenght = 1.0f;
v.x /= lenght;
v.y /= lenght;
v.z /= lenght;
}
void CreatePlaneFromTri(stD3DVertex &v1, stD3DVertex &v2, stD3DVertex &v3)
{
// Get triangle normal.
stD3DVertex normal, e1, e2;
// Get edge 1.
e1.x = v3.x - v1.x;
e1.y = v3.y - v1.y;
e1.z = v3.z - v1.z;
NormalizeVec(e1);
// Get edge 2.
e2.x = v2.x - v1.x;
e2.y = v2.y - v1.y;
e2.z = v2.z - v1.z;
NormalizeVec(e2);
// Get cross product of the edges.
normal.x = ((e1.y * e2.z) - (e1.z * e2.y));
normal.y = ((e1.z * e2.x) - (e1.x * e2.z));
normal.z = ((e1.x * e2.y) - (e1.y * e2.x));
NormalizeVec(normal);
// Save normal and calculate d.
a = normal.x;
b = normal.y;
c = normal.z;
d = - (a * v1.x + b * v1.y + c * v1.z);
}
int ClassifyPoint(float x, float y, float z)
{
float distance = a * x + b * y + c * z + d;
if(distance > 0.001) return UGP_FRONT;
if(distance < -0.001) return UGP_BACK;
return UGP_ON_PLANE;
}
float a, b, c, d;
};
CPlane g_plane;
// Start and end Z positions and amounts to move each point.
float g_lineStartZPos = -1;
float g_lineEndZPos = 1;
float g_lineZMoveAmt = 0;
float g_lineZDir = -0.02f;
// True if collision occurred, false if not.
bool g_collision = false;
LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
case WM_KEYUP:
if(wParam == VK_ESCAPE) PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE prevhInst, LPSTR cmdLine, int show)
{
// Register the window class
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
WINDOW_CLASS, NULL };
RegisterClassEx(&wc);
// Create the application's window
HWND hWnd = CreateWindow(WINDOW_CLASS, WINDOW_NAME, WS_OVERLAPPEDWINDOW,
100, 100, WINDOW_WIDTH, WINDOW_HEIGHT,
GetDesktopWindow(), NULL, wc.hInstance, NULL);
// Initialize Direct3D
if(InitializeD3D(hWnd, false))
{
// Show the window
ShowWindow(hWnd, SW_SHOWDEFAULT);
UpdateWindow(hWnd);
// Enter the message loop
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
RenderScene();
}
}
// Release any and all resources.
Shutdown();
// Unregister our window.
UnregisterClass(WINDOW_CLASS, wc.hInstance);
return 0;
}
bool InitializeD3D(HWND hWnd, bool fullscreen)
{
D3DDISPLAYMODE displayMode;
// Create the D3D object.
g_D3D = Direct3DCreate9(D3D_SDK_VERSION);
if(g_D3D == NULL) return false;
// Get the desktop display mode.
if(FAILED(g_D3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode)))
return false;
// Set up the structure used to create the D3DDevice
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
if(fullscreen)
{
d3dpp.Windowed = FALSE;
d3dpp.BackBufferWidth = WINDOW_WIDTH;
d3dpp.BackBufferHeight = WINDOW_HEIGHT;
}
else
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = displayMode.Format;
d3dpp.BackBufferCount = 1;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
// Create the D3DDevice
if(FAILED(g_D3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE,
&d3dpp, &g_D3DDevice))) return false;
// Initialize any objects we will be displaying.
if(!InitializeObjects()) return false;
return true;
}
bool InitializeObjects()
{
// Create the font.
if(FAILED(D3DXCreateFont(g_D3DDevice, 18, 0, 0, 1, 0,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE, "Arial",
&g_Font))) return false;
// Create our objects.
stD3DVertex squareData[] =
{
{-0.3f, -0.4f, 0, D3DCOLOR_XRGB(255,255,255)},
{0.3f, -0.4f, 0, D3DCOLOR_XRGB(255,255,255)},
{0.3f, 0.4f, 0, D3DCOLOR_XRGB(255,255,255)},
{0.3f, 0.4f, 0, D3DCOLOR_XRGB(255,255,255)},
{-0.3f, 0.4f, 0, D3DCOLOR_XRGB(255,255,255)},
{-0.3f, -0.4f, 0, D3DCOLOR_XRGB(255,255,255)}
};
stD3DVertex lineData[] =
{
{0, 0, g_lineStartZPos, D3DCOLOR_XRGB(0,255,0)},
{0, 0, g_lineEndZPos, D3DCOLOR_XRGB(0,255,0)},
};
// Get plane from the object. Only need one triangle since
// it is a even square (plane will be the same for both tris).
g_plane.CreatePlaneFromTri(squareData[0], squareData[1], squareData[2]);
// Create square vertex buffer.
if(FAILED(g_D3DDevice->CreateVertexBuffer(sizeof(squareData), 0,
D3DFVF_VERTEX, D3DPOOL_DEFAULT,
&g_squareBuffer, NULL))) return false;
void *ptr;
// Fill the vertex buffer.
if(FAILED(g_squareBuffer->Lock(0, sizeof(squareData),
(void**)&ptr, 0))) return false;
memcpy(ptr, squareData, sizeof(squareData));
g_squareBuffer->Unlock();
// Create line vertex buffer.
if(FAILED(g_D3DDevice->CreateVertexBuffer(sizeof(lineData), 0,
D3DFVF_VERTEX, D3DPOOL_DEFAULT,
&g_lineBuffer, NULL))) return false;
// Fill the vertex buffer.
if(FAILED(g_lineBuffer->Lock(0, sizeof(lineData),
(void**)&ptr, 0))) return false;
memcpy(ptr, lineData, sizeof(lineData));
g_lineBuffer->Unlock();
// Set default rendering states.
g_D3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
g_D3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
g_D3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
// Set the projection matrix.
D3DXMatrixPerspectiveFovLH(&g_projection, D3DX_PI / 4,
WINDOW_WIDTH/WINDOW_HEIGHT, 0.1f, 1000.0f);
g_D3DDevice->SetTransform(D3DTS_PROJECTION, &g_projection);
// Define camera information.
D3DXVECTOR3 cameraPos(5.0f, 0.0f, -5.0f);
D3DXVECTOR3 lookAtPos(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 upDir(0.0f, 1.0f, 0.0f);
// Build view matrix.
D3DXMatrixLookAtLH(&g_ViewMatrix, &cameraPos,
&lookAtPos, &upDir);
return true;
}
void RenderScene()
{
// RECT used to position the font and a string.
RECT fontPos = {0, 125, WINDOW_WIDTH, WINDOW_HEIGHT};
char str[64] = {0};
// Move line. If position limit is hit, switch directions.
g_lineZMoveAmt += g_lineZDir;
if(g_lineZMoveAmt > 2) g_lineZDir *= -1;
if(g_lineZMoveAmt < -2) g_lineZDir *= -1;
// Test which side of the plane both points are.
int result1 = g_plane.ClassifyPoint(0, 0, g_lineStartZPos + g_lineZMoveAmt);
int result2 = g_plane.ClassifyPoint(0, 0, g_lineEndZPos + g_lineZMoveAmt);
// Test for collision. True if points are on different ends.
g_collision = (result1 != result2);
// Create string.
if(g_collision)
sprintf(str, "Collision: TRUE");
else
sprintf(str, "Collision: FALSE");
// Clear the backbuffer.
g_D3DDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
D3DCOLOR_XRGB(0,0,0), 1.0f, 0);
// Begin the scene. Start rendering.
g_D3DDevice->BeginScene();
// Display if collision occurred.
g_Font->DrawText(NULL, str, -1, &fontPos, DT_CENTER,
D3DCOLOR_XRGB(255,255,255));
// Apply the view (camera).
g_D3DDevice->SetTransform(D3DTS_VIEW, &g_ViewMatrix);
// Draw line.
g_D3DDevice->SetStreamSource(0, g_lineBuffer, 0, sizeof(stD3DVertex));
D3DXMatrixTranslation(&g_worldMatrix, 0, 0, g_lineZMoveAmt);
g_D3DDevice->SetTransform(D3DTS_WORLD, &g_worldMatrix);
g_D3DDevice->SetFVF(D3DFVF_VERTEX);
g_D3DDevice->DrawPrimitive(D3DPT_LINELIST, 0, 1);
// Draw square.
g_D3DDevice->SetStreamSource(0, g_squareBuffer, 0, sizeof(stD3DVertex));
D3DXMatrixIdentity(&g_worldMatrix);
g_D3DDevice->SetTransform(D3DTS_WORLD, &g_worldMatrix);
g_D3DDevice->SetFVF(D3DFVF_VERTEX);
g_D3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);
// End the scene. Stop rendering.
g_D3DDevice->EndScene();
// Display the scene.
g_D3DDevice->Present(NULL, NULL, NULL, NULL);
}
void Shutdown()
{
if(g_D3DDevice != NULL) g_D3DDevice->Release();
g_D3DDevice = NULL;
if(g_D3D != NULL) g_D3D->Release();
g_D3D = NULL;
if(g_squareBuffer != NULL) g_squareBuffer->Release();
g_squareBuffer = NULL;
if(g_lineBuffer != NULL) g_lineBuffer->Release();
g_lineBuffer = NULL;
if(g_Font != NULL) g_Font->Release();
g_Font = NULL;
}