43. GameProject8 + 碰撞检测

第一个新文件是BoundingGeometry.h头文件。该头文件包含了3个类和两个函数。第一个类是边界几何图形基类,创建其他边界几何图形时可以从该类派生。第二个类是边界框类,第三个是边界球类。头文件中的两个函数分别是BoxToBoxIntersect()和SphereToSphereIntersect()。这两个函数分别用于检测两个方框和两个球体之间的碰撞。

每个类中的成员函数分别是CreateFromPoints()、isPointInside()、两个Intersect()、GetPlanes()和isRayInside()。在创建涉及的边界几何图形时,会用到CreateFromPoints()函数。isPointInside()函数和本章演示程序中创建的OnCollision()函数很相似,用于测试一个点是否和一个边界几何图形发生碰撞。从根本上讲,这两个Intersect()函数就是OnCollision()函数,但它们是通过射线检测的。GetPlanes()函数只有在边界框类中才用到,它计算每个框边外的平面(6个平面)。最后一个函数isRayInside()用于查看射线是否真的在边界几何图形中。要记住:射线是无界的,为了和有界物体发生碰撞,射线不必在该对象内部。只要在沿着射线方向的某个位置上射线碰撞到物体,就表示射线与物体发生了碰撞。

BoundingGeometry.h头文件的完整代码

#ifndef _UGP_BOUNDINGGEOMETRY_H_
#define _UGP_BOUNDINGGEOMETRY_H_


#include
"MathLibrary.h"



// 边界几何图形基类
class CBoundingBase
{
public:
CBoundingBase(){}
virtual ~CBoundingBase() {}

virtual void CreateFromPoints(CVector3 *pointList, int numPoints) = 0;

// 测试一个点是否和一个边界几何图形发生碰撞
virtual bool isPointInside(CVector3 &v) = 0;

virtual bool Intersect(CRay ray, float *dist) = 0;
virtual bool Intersect(CRay ray, float length, float *dist) = 0;

// 计算每个框边外的平面(6个平面)
virtual void GetPlanes(CPlane *planes) = 0;

// 查看射线是否真的在边界几何图形中
virtual bool isRayInside(CRay &ray, float length) = 0;
};



// 边界框类
class CBoundingBox : public CBoundingBase
{
public:
CBoundingBox() {}
~CBoundingBox() {}

void CreateFromPoints(CVector3 *pointList, int numPoints);
bool isPointInside(CVector3 &v);

bool Intersect(CRay ray, float *dist);
bool Intersect(CRay ray, float length, float *dist);

void GetPlanes(CPlane *planes);
bool isRayInside(CRay &ray, float length);

CVector3 m_min, m_max;
};



// 边界球
class CBoundingSphere : public CBoundingBase
{
public:
CBoundingSphere() : m_radius(
0) {}
~CBoundingSphere() {}

void CreateFromPoints(CVector3 *pointList, int numPoints);
bool isPointInside(CVector3 &v);

bool Intersect(CRay ray, float *dist);
bool Intersect(CRay ray, float length, float *dist);

void GetPlanes(CPlane *planes) {}
bool isRayInside(CRay &ray, float length);

CVector3 m_center;
float m_radius;
};


// 检测两个方框之间的碰撞
bool BoxToBoxIntersect(CBoundingBox &bb1, CBoundingBox &bb2);

// 检测两个球体之间的碰撞
bool SphereToSphereIntersect(CBoundingSphere &bs1, CBoundingSphere &bs2);

#endif

  

BoundingGeometry.cpp

/*
Demo Name: Game Project 8
Author: Allen Sherrod
Chapter: Chapter 9
*/


#include
"BoundingGeometry.h"


void CBoundingBox::CreateFromPoints(CVector3 *pointList, int numPoints)
{
// Loop through all of the points to find the min/max values.
for(int i = 0; i < numPoints; i++)
{
if(pointList[i].x < m_min.x) m_min.x = pointList[i].x;
if(pointList[i].x > m_max.x) m_max.x = pointList[i].x;

if(pointList[i].y < m_min.y) m_min.y = pointList[i].y;
if(pointList[i].y > m_max.y) m_max.y = pointList[i].y;

if(pointList[i].z < m_min.z) m_min.z = pointList[i].z;
if(pointList[i].z > m_max.z) m_max.z = pointList[i].z;
}
}


bool CBoundingBox::isPointInside(CVector3 &v)
{
if(m_max.x <= v.x) return false;
if(m_min.x >= v.x) return false;
if(m_max.y <= v.y) return false;
if(m_min.y >= v.y) return false;
if(m_max.z <= v.z) return false;
if(m_min.z >= v.z) return false;

return true;
}


bool CBoundingBox::Intersect(CRay ray, float *dist)
{
float t0, t1, temp;
float min = -999999.9f;
float max = 999999.9f;

// 若射线在y,z轴组成的平面上,则只需判断x坐标
if(fabs(ray.m_direction.x) < 0.00001f)
{
if((ray.m_origin.x < m_min.x) || (ray.m_origin.x > m_max.x))
return false;
}

// 检查x轴方向上是否满足相交的条件
t0 = (m_min.x - ray.m_origin.x) / ray.m_direction.x;
t1
= (m_max.x - ray.m_origin.x) / ray.m_direction.x;

if(t0 > t1) { temp = t0; t0 = t1; t1 = temp; }
if(t0 > min) min = t0;
if(t1 < max) max = t1;
if(min > max) return false;
if(max < 0) return false;


//// 若射线在x,z轴组成的平面上,则只需判断y坐标
if(fabs(ray.m_direction.y) < 0.00001f)
{
if((ray.m_origin.y < m_min.y) ||
(ray.m_origin.y
> m_max.y)) return false;
}

t0
= (m_min.y - ray.m_origin.y) / ray.m_direction.y;
t1
= (m_max.y - ray.m_origin.y) / ray.m_direction.y;

if(t0 > t1) { temp = t0; t0 = t1; t1 = temp; }
if(t0 > min) min = t0;
if(t1 < max) max = t1;
if(min > max) return false;
if(max < 0) return false;


//// 若射线在x,y轴组成的平面上,则只需判断z坐标
if(fabs(ray.m_direction.z) < 0.00001f)
{
if((ray.m_origin.z < m_min.z) ||
(ray.m_origin.z
> m_max.z)) return false;
}

t0
= (m_min.z - ray.m_origin.z) / ray.m_direction.z;
t1
= (m_max.z - ray.m_origin.z) / ray.m_direction.z;

if(t0 > t1) { temp = t0; t0 = t1; t1 = temp; }
if(t0 > min) min = t0;
if(t1 < max) max = t1;
if(min > max) return false;
if(max < 0) return false;


if(min > 0)
if(dist)
*dist = min;
else if(dist)
*dist = max;

return true;
}


bool CBoundingBox::Intersect(CRay ray, float length, float *dist)
{
float t0, t1, temp;
float min = -999999.9f;
float max = 999999.9f;
float d = 0;

if(fabs(ray.m_direction.x) < 0.00001f)
{
if((ray.m_origin.x < m_min.x) ||
(ray.m_origin.x
> m_max.x)) return false;
}

t0
= (m_min.x - ray.m_origin.x) / ray.m_direction.x;
t1
= (m_max.x - ray.m_origin.x) / ray.m_direction.x;

if(t0 > t1) { temp = t0; t0 = t1; t1 = temp; }
if(t0 > min) min = t0;
if(t1 < max) max = t1;
if(min > max) return false;
if(max < 0) return false;


if(fabs(ray.m_direction.y) < 0.00001f)
{
if((ray.m_origin.y < m_min.y) ||
(ray.m_origin.y
> m_max.y)) return false;
}

t0
= (m_min.y - ray.m_origin.y) / ray.m_direction.y;
t1
= (m_max.y - ray.m_origin.y) / ray.m_direction.y;

if(t0 > t1) { temp = t0; t0 = t1; t1 = temp; }
if(t0 > min) min = t0;
if(t1 < max) max = t1;
if(min > max) return false;
if(max < 0) return false;


if(fabs(ray.m_direction.z) < 0.00001f)
{
if((ray.m_origin.z < m_min.z) ||
(ray.m_origin.z
> m_max.z)) return false;
}

t0
= (m_min.z - ray.m_origin.z) / ray.m_direction.z;
t1
= (m_max.z - ray.m_origin.z) / ray.m_direction.z;

if(t0 > t1) { temp = t0; t0 = t1; t1 = temp; }
if(t0 > min) min = t0;
if(t1 < max) max = t1;
if(min > max) return false;
if(max < 0) return false;

if(min > 0) d = min;
else d = max;

if(d > length) return false;
if(dist) *dist = d;

return true;
}


void CBoundingBox::GetPlanes(CPlane *planes)
{
// Right.
planes[0].a = 1.0f; planes[0].b = 0.0f; planes[0].c = 0.0f;
planes[
0].d = -(1 * m_max.x + 0 * m_max.y + 0 * m_max.z);

// Left.
planes[1].a = -1.0f; planes[1].b = 0.0f; planes[1].c = 0.0f;
planes[
1].d = -(-1 * m_min.x + 0 * m_min.y + 0 * m_min.z);

// Front.
planes[2].a = 0.0f; planes[2].b = 0.0f; planes[2].c = -1.0f;
planes[
2].d = -(0 * m_min.x + 0 * m_min.y + -1 * m_min.z);

// Back.
planes[3].a = 0.0f; planes[3].b = 0.0f; planes[3].c = 1.0f;
planes[
3].d = -(0 * m_max.x + 0 * m_max.y + 1 * m_max.z);

// Top.
planes[4].a = 0.0f; planes[4].b = 1.0f; planes[4].c = 0.0f;
planes[
4].d = -(0 * m_max.x + 1 * m_max.y + 0 * m_max.z);

// Bottom.
planes[5].a = 0.0f; planes[5].b = -1.0f; planes[5].c = 0.0f;
planes[
5].d = -(0 * m_min.x + -1 * m_min.y + 0 * m_min.z);
}


bool CBoundingBox::isRayInside(CRay &ray, float length)
{
CVector3 endPos
= ray.m_origin + (ray.m_direction * length);
return (isPointInside(ray.m_origin) && isPointInside(endPos));
}


void CBoundingSphere::CreateFromPoints(CVector3 *pointList, int numPoints)
{
CVector3 min, max;
float dist = 0, maxDistance = 0.0f;

// Loop through all of the points to find the min/max values.
for(int i = 0; i < numPoints; i++)
{
if(pointList[i].x < min.x) min.x = pointList[i].x;
if(pointList[i].x > max.x) max.x = pointList[i].x;

if(pointList[i].y < min.y) min.y = pointList[i].y;
if(pointList[i].y > max.y) max.y = pointList[i].y;

if(pointList[i].z < min.z) min.z = pointList[i].z;
if(pointList[i].z > max.z) max.z = pointList[i].z;
}

m_center
= (max + min) * 0.5f;

// Find max distance.
for(i = 0; i < numPoints; i++)
{
dist
= ((pointList[i].x - m_center.x) * (pointList[i].x - m_center.x)) +
((pointList[i].y
- m_center.y) * (pointList[i].y - m_center.y)) +
((pointList[i].z
- m_center.z) * (pointList[i].z - m_center.z));

if(dist > maxDistance)
maxDistance
= dist;
}

// Calculate radius.
m_radius = sqrt(maxDistance);
}


bool CBoundingSphere::isPointInside(CVector3 &v)
{
// The distance between the two spheres.
CVector3 intersect = m_center - v;

// Test for collision.
if(sqrt(intersect.x * intersect.x + intersect.y * intersect.y +
intersect.z
* intersect.z) < m_radius)
return true;

return false;
}


bool CBoundingSphere::Intersect(CRay ray, float *dist)
{
CVector3 RayToSphereDir;

float RayToSphereLength = 0.0f;
float IntersectPoint = 0.0f;
float SquaredPoint = 0.0f;

// Get the direction of the ray to the object.
RayToSphereDir = m_center - ray.m_origin;

// Dot product the direction of the ray to current sphere to get the length.
RayToSphereLength = RayToSphereDir.DotProduct3(RayToSphereDir);

// Find intersect distance.
IntersectPoint = RayToSphereDir.DotProduct3(ray.m_direction);

// If true, no collision.
if(IntersectPoint < 0 ) return false;

// Get the squared sphere intersect distance.
SquaredPoint = (m_radius * m_radius) - RayToSphereLength +
(IntersectPoint
* IntersectPoint);

// If true, no collision.
if(SquaredPoint < 0) return false;

// Else it does hit the sphere and we record the results.
if(dist) *dist = IntersectPoint - (float)sqrt(SquaredPoint);

return true;
}


bool CBoundingSphere::Intersect(CRay ray, float length, float *dist)
{
CVector3 RayToSphereDir;

float RayToSphereLength = 0.0f;
float IntersectPoint = 0.0f;
float SquaredPoint = 0.0f;

// Get the direction of the ray to the object.
RayToSphereDir = m_center - ray.m_origin;

// Dot product the direction of the ray to current sphere to get the length.
RayToSphereLength = RayToSphereDir.DotProduct3(RayToSphereDir);

// Find intersect distance.
IntersectPoint = RayToSphereDir.DotProduct3(ray.m_direction);

// If true, no collision.
if(IntersectPoint < 0 ) return false;

// Get the squared sphere intersect distance.
SquaredPoint = (m_radius * m_radius) - RayToSphereLength +
(IntersectPoint
* IntersectPoint);

// If true, no collision.
if(SquaredPoint < 0) return false;

// Calculate intersection distance.
float d = IntersectPoint - (float)sqrt(SquaredPoint);

// If distance is > than the length of the ray return false.
if(d > length) return false;

// Else it does hit the sphere and we record the results.
if(dist) *dist = d;

return true;
}


bool CBoundingSphere::isRayInside(CRay &ray, float length)
{
CVector3 endPos
= ray.m_origin + (ray.m_direction * length);
return (isPointInside(ray.m_origin) && isPointInside(endPos));
}


bool BoxToBoxIntersect(CBoundingBox &bb1, CBoundingBox &bb2)
{
if((bb1.m_min.x > bb2.m_max.x) || (bb2.m_min.x > bb1.m_max.x))
return false;
if((bb1.m_min.y > bb2.m_max.y) || (bb2.m_min.y > bb1.m_max.y))
return false;
if((bb1.m_min.z > bb2.m_max.z) || (bb2.m_min.z > bb1.m_max.z))
return false;

return true;
}


bool SphereToSphereIntersect(CBoundingSphere &bs1, CBoundingSphere &bs2)
{
// The distance between the two spheres.
CVector3 intersect = bs1.m_center - bs2.m_center;

// Test for collision.
if(sqrt(intersect.x * intersect.x + intersect.y * intersect.y +
intersect.z
* intersect.z) < bs1.m_radius + bs2.m_radius)
return true;

return false;
}

  

游戏项目中的最后一个改动是在MathLibrary.h头文件中添加边界几何图形类和头文件。将信息添加到该头文件中,这样就可以访问整个游戏引擎和游戏组件。

#ifndef _UGP_MATH_LIBRARY_H_
#define _UGP_MATH_LIBRARY_H_


#include
"Vector.h" // 向量相关
#include"Matrix.h" // 矩阵相关
#include"Quaternion.h" // 四元组相关
#include"Physics.h" // 物理学相关

class CRay; // 射线类
class CBoundingBox; // 边界框类
class CBoundingSphere; // 边界球类
class CPlane; // 平面类
class CPolygon; // 多边形类

#include
"Ray.h"
#include
"BoundingGeometry.h"
#include
"Plane.h"
#include
"Polygon.h"
#include
"MathDefines.h"


#endif

  

posted @ 2011-09-14 23:00  小 楼 一 夜 听 春 雨  阅读(379)  评论(0编辑  收藏  举报