第一个新文件是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