Cocos2d-x教程(34)-三维物体OBB碰撞检測算法


欢迎增加Cocos2d-x 交流群:193411763


转载时请注明原文出处 :http://blog.csdn.net/u012945598/article/details/39665911



----------------------------------------------------------------------------------------------------------------------------------------

       在上一篇文章中解说了AABB包围盒碰撞检測的原理,并在文章末尾提到了Cocos2d-x 3.3beta0版本号中小乌龟碰撞检測的样例。这个样例使用的并不是是AABB碰撞检測,而是比AABB包围盒更加精确的OBB包围盒的碰撞检測方法,本篇文章将对OBB包围盒及其碰撞检測方法进行介绍。


1. OBB包围盒

       OBB(Oriented Bounding Box)包围盒也被称作有向包围盒或定向包围盒,它会随着物体的移动、缩放、旋转。

简单来说,它就是一个能够旋转的AABB包围盒。在Cocos2d-x中使用过物理引擎的开发人员一定见过当我们在物理世界中创建一个物体并开启调试模式时。这个物体会被红色的矩形包围,当物体做平移或旋转时,这个红色矩形也会做相同的操作,这个红色矩形正是该物体的OBB包围盒。

如图1-1所看到的:


      
图1-1
       
       相对于AABB包围盒来讲,OBB在碰撞精度上要高于AABB,可是准确度的提高同一时候带来的就是效率的降低,OBB的算法无疑是要比AABB复杂的,相同内存消耗也会更大。这个问题我们能够从OBB的表达式来得到结论。

      OBB包围盒的表达式:


       表达一个AABB包围盒或OBB包围盒都有若干种方式,可是无疑须要选中里面最优的一种。对于AABB来说,通常仅仅要使用两个顶点的坐标信息标识一个AABB包围盒就能够了,其它的顶点信息都能够通过计算得出。可是对于OBB包围盒的表达方式仅仅有两点信息显然是不够的。

       想要唯一标识一个OBB包围盒我们大概会想到,使用8个顶点的集合、6个面的集合、1个顶点和3各彼此正交的边向量。又或者是1个中心点、1个旋转矩阵和3个1/2边长(注:一个旋转矩阵包括了三个旋转轴,若是二维的OBB包围盒则是一个中心点,两个旋转轴,两个1/2边长)

      上述最后一种方法就是最经常使用的方法。下面来看一段Cocos2d-x 3.3beta0中CCOBB.h中的代码:
       
    Vec3 _center;   // 中心点
    /*
      下面三个变量为正交单位向量。
      定义了当前OBB包围盒的x,y,z轴
      用于计算矢量投影
     */
    Vec3 _xAxis;    // 包围盒x轴方向单位矢量
    Vec3 _yAxis;    // 包围盒y轴方向单位矢量
    Vec3 _zAxis;    // 包围盒z轴方向单位矢量
    Vec3 _extents;  // 3个1/2边长,半长、半宽、半高

       Cocos2d-x 3.0beta0在CCOBB.h中定义了五个成员变量,每个数据类型都是一个三维向量。包括了3个浮点数。

也就是说,表达一个OBB包围盒须要15个float类型的变量。占用60个字节,然而表示一个AABB包围盒仅须要两个顶点,24个字节。从这一点上来说。OBB的内存消耗算非常高了。

       一种降低开销的方案是:仅仅存储旋转矩阵的两个轴。仅仅是在測试时利用叉积计算第三个轴,这样能够降低CPU操作开销并节省3个浮点数分量,降低20%内存消耗。


      OBB包围盒的创建:

       怎样用一个高效的算法构建一个紧密包围的OBB包围盒或是AABB包围盒是一个非常复杂的问题,因为不同的形状物体的包围是不同的,这里我们不去探讨构建OBB包围盒的原理,仅仅了解一下Cocos2d-x 3.3beta0 中为我们提供的构建方法。

       在Cocos2d-x 中使用了两种方法去计算OBB。第一种方法是一种简化的OBB构建算法。由一个AABB包围盒来确定终于OBB包围盒,第二种方法是通过协方差矩阵来确定一个方向包围盒(实际上不管是AABB包围盒还是OBB包围盒。真正的难点便在于包围盒的构建上)。

       在Cocos2d-x中第一种方法用起来更为简单一些,比如:
 AABB aabb = _sprite->getAABB();//获取一个Sprite3D对象的aabb包围盒
 OBB  _obbt = OBB(aabb);//创建obb包围盒


2. OBB包围盒的碰撞检測方法

       OBB包围盒的碰撞检測方法常採用的是分离轴定理,首先简单说明一下分离轴定理(separating axis theorem),通过分离轴定理我们能够得到例如以下结论:
      假设能找到一个轴,两个凸形状在该轴上的投影不重叠。则这两个形状不相交。假设这个轴不存在,而且那些形状是凸形的,则能够确定两个形状相交(凹形不适用,比方月牙形状,即使找不到分离轴。两个月牙形也可能不相交)。
      这个定理也能够这样理解。假设能找到一条直线,令包围盒A全然在直线的一边,包围盒B全然在还有一边,则两包围盒不重叠。

而这条直线便成为分离线(在三维世界中被称为分离面),而且一定垂直于分离轴。


      这里我们还是先来用二维世界中的OBB来进行演示,明确当中原理之后三维物体的OBB便十分easy理解了,如图2-1所看到的:
      
图2-1

       在图2-1中,针对某一分离轴L,假设包围盒A与包围盒B在轴L上的投影的半径和小于包围盒中心点间距在L的投影距离,那么包围盒A与包围盒B处于分离状态。

使用运算式来表达就是:|T * L|>rA + rB。在Cocos2d-x 3.3beta0中验证相交时採用的并不是这种方法。Cocos2d-x选用的方法是,取物体A及物体B的最大点和最小点的投影坐标,然后比較A的最大点与B的最小点,B的最小点与A的最大点。注意,虽然这是一个二维图像,可是实际上三维的图面体在分离轴上的投影跟二维的原理是一样的,都是一个线段。

      
       如今知道了分离轴定理的原理。下面则是怎样选取分离轴。因为潜在的分离轴可能存在无数种,所以我们并不是去要逐个验证,而是选取几个可測试的轴就可以。比如Cocos2d-x中所选用的OBB包围盒的凸面体均为长方体,我们就以长方体的碰撞为例,两个长方体的碰撞能够将其归结为下面几种组合:面-面碰撞、面-边碰撞、边-边碰撞(顶点视为边的一部分)。

所以实际上在取分离轴的时候,仅仅须要分别取第一个包围盒的3个坐标轴,第二个包围盒的3个坐标轴。以及垂直于某一轴的9个轴(其它的分离轴都是跟这15个分离轴中的某一个轴平行的轴,投影所得线段值都一样,无需再验证)。


       这里再解释下面什么是垂直于某一轴的9个轴,详细的做法就是。首先取包围盒A的x轴方向的某一边矢量,再取包围盒B的x轴方向的某一边矢量,对两个矢量做叉积,求出一个垂直于A的矢量与B的矢量的方向矢量,这个结果就是须要使用的分离轴。依照这样的做法,取A的x轴方向的边矢量分别与B的三个轴方向的边矢量做叉积,再取A的y轴方向与B的三个轴方向的边矢量做叉积,再取A的z轴方向的边矢量与B的三个轴方向的边矢量做叉积,结果一共3*3个分离轴。

如图2-2所看到的:


图2-2

      图中的顶点顺序是依照Cocos2d-x中定义的OBB包围盒的顶点顺序定义的。

图中的x代表的就是包围盒的x轴方向边的矢量(注意这里写的是矢量,不是x,y,z坐标轴),当然这个方向的矢量有无数个,我们仅仅需取一个代表性的用来计算叉积就可以。


       最后在Cocos2d-x 3.3beta0版本号中的OBB碰撞检測函数 bool OBB::intersects(const OBB& box) const 中遇到了一点小问题。在最后的3*3边矢量求分离轴的时候2d-x中貌似调用错了方法。仍然你调用的是取三个坐标轴做分离轴。笔者下面做了一些改动,最后附上源代码。

      
       CCOBB.h代码例如以下:
#ifndef __CC_OBB_H__
#define __CC_OBB_H__

#include "CCAABB.h"
#include "3d/3dExport.h"

NS_CC_BEGIN

class CC_3D_DLL OBB
{
public:
    OBB();//默认构造函数

    /*
     * 构造函数 依据一个AABB包围盒初始化一个OBB包围盒
     */
    OBB(const AABB& aabb);
    
    /*
     * 构造函数 依据点信息初始化一个OBB包围盒
     */
    OBB(const Vec3* verts, int num);
    
    /*
     * 推断点是否在一个OBB包围盒内
     */
    bool containPoint(const Vec3& point) const;

    /*
     * 指定OBB各成员变量的值
     */
    void set(const Vec3& center, const Vec3& _xAxis, const Vec3& _yAxis, const Vec3& _zAxis, const Vec3& _extents);
    
    /*
     * 复位函数 将当前OBB对象所占用的内存块置零
     */ 
    void reset();

    /* 面向包围盒z轴负方向
     * verts[0] : 左上点坐标
     * verts[1] : 左下点坐标
     * verts[2] : 右下点坐标
     * verts[3] : 右上点坐标
     *
     * 面向包围盒z轴正方向
     * verts[4] : 右上点坐标
     * verts[5] : 右下点坐标
     * verts[6] : 左下点坐标
     * verts[7] : 左上点坐标
     */
    void getCorners(Vec3* verts) const;
    
    /*
     *检測是否和其它OBB盒碰撞
     */
    bool intersects(const OBB& box) const;
    
	/**
     * 由给定的变换矩阵变换OBB包围盒
     */
    void transform(const Mat4& mat);
    
protected:
    /*
     * 将点投影到目标轴
     */
    float projectPoint(const Vec3& point, const Vec3& axis) const;
    
    /*
     * 计算最大和最小投影值
     */
    void getInterval(const OBB& box, const Vec3& axis, float &min, float &max) const;
    
    /*
     * 取边的矢量 获取分离轴使用
     */
    Vec3 getEdgeDirection(int index) const;
    
    /*
     * 取面的方向矢量 获取分离轴时使用
     */
    Vec3 getFaceDirection(int index) const;

public:
    Vec3 _center;   // 中心点
    /*
      下面三个变量为正交单位向量,
      定义了当前OBB包围盒的x,y,z轴
      用于计算矢量投影
     */
    Vec3 _xAxis;    // 包围盒x轴方向单位矢量
    Vec3 _yAxis;    // 包围盒y轴方向单位矢量
    Vec3 _zAxis;    // 包围盒z轴方向单位矢量
    Vec3 _extents;  // 3个1/2边长,半长、半宽、半高
};

NS_CC_END

#endif

       CCOBB.cpp代码例如以下:
#include "3d/CCOBB.h"

NS_CC_BEGIN

#define ROTATE(a,i,j,k,l) g=a.m[i + 4 * j]; h=a.m[k + 4 * l]; a.m[i + 4 * j]=(float)(g-s*(h+g*tau)); a.m[k + 4 * l]=(float)(h+s*(g-h*tau));

//生成协方差矩阵
static Mat4 _getConvarianceMatrix(const Vec3* vertPos, int vertCount)
{
    int i;
    Mat4 Cov;

    double S1[3];
    double S2[3][3];

    S1[0] = S1[1] = S1[2] = 0.0;
    S2[0][0] = S2[1][0] = S2[2][0] = 0.0;
    S2[0][1] = S2[1][1] = S2[2][1] = 0.0;
    S2[0][2] = S2[1][2] = S2[2][2] = 0.0;

    // get center of mass
    for(i=0; i<vertCount; i++)
    {
        S1[0] += vertPos[i].x;
        S1[1] += vertPos[i].y;
        S1[2] += vertPos[i].z;

        S2[0][0] += vertPos[i].x * vertPos[i].x;
        S2[1][1] += vertPos[i].y * vertPos[i].y;
        S2[2][2] += vertPos[i].z * vertPos[i].z;
        S2[0][1] += vertPos[i].x * vertPos[i].y;
        S2[0][2] += vertPos[i].x * vertPos[i].z;
        S2[1][2] += vertPos[i].y * vertPos[i].z;
    }

    float n = (float)vertCount;
    // now get covariances
    Cov.m[0] = (float)(S2[0][0] - S1[0]*S1[0] / n) / n;
    Cov.m[5] = (float)(S2[1][1] - S1[1]*S1[1] / n) / n;
    Cov.m[10] = (float)(S2[2][2] - S1[2]*S1[2] / n) / n;
    Cov.m[4] = (float)(S2[0][1] - S1[0]*S1[1] / n) / n;
    Cov.m[9] = (float)(S2[1][2] - S1[1]*S1[2] / n) / n;
    Cov.m[8] = (float)(S2[0][2] - S1[0]*S1[2] / n) / n;
    Cov.m[1] = Cov.m[4];
    Cov.m[2] = Cov.m[8];
    Cov.m[6] = Cov.m[9];

    return Cov;
}
//获取一个矢量在某个轴的分量
static float& _getElement( Vec3& point, int index)
{
    if (index == 0)
        return point.x;
    if (index == 1)
        return point.y;
    if (index == 2)
        return point.z;

    CC_ASSERT(0);
    return point.x;
}
//获取特征向量
static void _getEigenVectors(Mat4* vout, Vec3* dout, Mat4 a)
{
    int n = 3;
    int j,iq,ip,i;
    double tresh, theta, tau, t, sm, s, h, g, c;
    int nrot;
    Vec3 b;
    Vec3 z;
    Mat4 v;
    Vec3 d;

    v = Mat4::IDENTITY;
    for(ip = 0; ip < n; ip++)
    {
        _getElement(b, ip) = a.m[ip + 4 * ip];
        _getElement(d, ip) = a.m[ip + 4 * ip];
        _getElement(z, ip) = 0.0;
    }

    nrot = 0;

    for(i = 0; i < 50; i++)
    {
        sm = 0.0;
        for(ip = 0; ip < n; ip++) for(iq = ip+1; iq < n; iq++) sm += fabs(a.m[ip + 4 * iq]);
        if( fabs(sm) < FLT_EPSILON )
        {
            v.transpose();
            *vout = v;
            *dout = d;
            return;
        }

        if (i < 3)
            tresh = 0.2 * sm / (n*n);
        else 
            tresh = 0.0;

        for(ip = 0; ip < n; ip++)
        {
            for(iq = ip + 1; iq < n; iq++)
            {
                g = 100.0 * fabs(a.m[ip + iq * 4]);
                float dmip = _getElement(d, ip);
                float dmiq = _getElement(d, iq);

                if( i>3 && fabs(dmip) + g == fabs(dmip) && fabs(dmiq) + g == fabs(dmiq) )
                {
                    a.m[ip + 4 * iq] = 0.0;
                }
                else if (fabs(a.m[ip + 4 * iq]) > tresh)
                {
                    h = dmiq - dmip;
                    if (fabs(h) + g == fabs(h))
                    {
                        t=(a.m[ip + 4 * iq])/h;
                    }
                    else
                    {
                        theta = 0.5 * h / (a.m[ip + 4 * iq]);
                        t=1.0 / (fabs(theta) + sqrt(1.0 + theta * theta));
                        if (theta < 0.0) t = -t;
                    }
                    c = 1.0 / sqrt(1+t*t);
                    s = t*c;
                    tau = s / (1.0+c);
                    h = t * a.m[ip + 4 * iq];
                    _getElement(z, ip) -= (float)h;
                    _getElement(z, iq) += (float)h;
                    _getElement(d, ip) -= (float)h;
                    _getElement(d, iq) += (float)h;
                    a.m[ip + 4 * iq]=0.0;
                    for(j = 0; j < ip; j++) { ROTATE(a,j,ip,j,iq); }
                    for(j = ip + 1; j < iq; j++) { ROTATE(a,ip,j,j,iq); }
                    for(j = iq + 1; j < n; j++) { ROTATE(a,ip,j,iq,j); }
                    for(j = 0; j < n; j++) { ROTATE(v,j,ip,j,iq); }
                    nrot++;
                }
            }
        }

        for(ip = 0; ip < n; ip++)
        {
            _getElement(b, ip) += _getElement(z, ip);
            _getElement(d, ip) = _getElement(b, ip);
            _getElement(z, ip) = 0.0f;
        }
    }

    v.transpose();
    *vout = v;
    *dout = d;
    return;
}
//创建OBB包围盒取向矩阵
static Mat4 _getOBBOrientation(const Vec3* vertPos, int num)
{
    Mat4 Cov; //创建一个 4*4 矩阵

    if (num <= 0)
        return Mat4::IDENTITY; //返回单位矩阵

    Cov = _getConvarianceMatrix(vertPos, num); //创建协方差矩阵

    // now get eigenvectors
    Mat4 Evecs;
    Vec3 Evals;
    _getEigenVectors(&Evecs, &Evals, Cov); //求特征向量

    Evecs.transpose(); //转置

    return Evecs;
}
//默认构造函数
OBB::OBB()
{
    //数据复位
    reset();
}
//由一个AABB包围盒生成OBB包围盒
OBB::OBB(const AABB& aabb)
{
    //数据复位
    reset();
    
    //中心点
    _center = (aabb._min + aabb._max);
    _center.scale(0.5f);
    
    //各轴旋转矩阵的单位矩阵
    _xAxis = Vec3(1.0f, 0.0f, 0.0f);
    _yAxis = Vec3(0.0f, 1.0f, 0.0f);
    _zAxis = Vec3(0.0f, 0.0f, 1.0f);
    
    //半尺存 半长 半宽 半高
    _extents = aabb._max - aabb._min;
    _extents.scale(0.5f);
}
//构造函数 依据点信息初始化一个OBB包围盒
OBB::OBB(const Vec3* verts, int num)
{
    if (!verts) return; //假设verts不存在 返回
    
    reset(); //数据复位
    
    Mat4 matTransform = _getOBBOrientation(verts, num); //创建包围盒取向矩阵
    
    /*	
        matTransform是一个正交矩阵,所以它的逆矩阵就是它的转置;
        AA'=E(E为单位矩阵。A'表示“矩阵A的转置矩阵”) A称为正交矩阵
    */
    matTransform.transpose(); //计算matTransform矩阵的转置(此处相当于求逆矩)
    
    Vec3 vecMax = matTransform * Vec3(verts[0].x, verts[0].y, verts[0].z);
    
    Vec3 vecMin = vecMax;
    
    for (int i = 1; i < num; i++)
    {
        Vec3 vect = matTransform * Vec3(verts[i].x, verts[i].y, verts[i].z);
        
        vecMax.x = vecMax.x > vect.x ? vecMax.x : vect.x;
        vecMax.y = vecMax.y > vect.y ? vecMax.y : vect.y;
        vecMax.z = vecMax.z > vect.z ? vecMax.z : vect.z;
        
        vecMin.x = vecMin.x < vect.x ? vecMin.x : vect.x;
        vecMin.y = vecMin.y < vect.y ? vecMin.y : vect.y;
        vecMin.z = vecMin.z < vect.z ? vecMin.z : vect.z;
    }
    
    matTransform.transpose();
    
    _xAxis = Vec3(matTransform.m[0], matTransform.m[1], matTransform.m[2]);
    _yAxis = Vec3(matTransform.m[4], matTransform.m[5], matTransform.m[6]);
    _zAxis = Vec3(matTransform.m[8], matTransform.m[9], matTransform.m[10]);
    
    _center	= 0.5f * (vecMax + vecMin);
    _center *= matTransform;
    
    _xAxis.normalize();
    _yAxis.normalize();
    _zAxis.normalize();
    
    _extents = 0.5f * (vecMax - vecMin);
}
//推断一点是否在OBB包围盒内
bool OBB::containPoint(const Vec3& point) const
{
    //相当于将点坐标从世界坐标系中转换到了OBB包围盒的物体坐标系中
    Vec3 vd = point - _center;
    
    /*
      dot方法为求点积
      因为_xAxis为单位矢量
      vd与_xAxis的点击即为在_xAxis方向的投影
    */
    float d = vd.dot(_xAxis); //计算x方向投影d
    
    //推断投影是否大于x正方向的半长或小于x负方向半长
    if (d > _extents.x || d < -_extents.x)
        return false;//满足条件说明不在包围盒内

    d = vd.dot(_yAxis); //计算y方向投影
    //同理
    if (d > _extents.y || d < -_extents.y)
        return false;

    d = vd.dot(_zAxis);//计算z方向投影
    if (d > _extents.z || d < -_extents.z)
        return false;

    return true;
}
//指定OBB包围盒的变量值
void OBB::set(const Vec3& center, const Vec3& xAxis, const Vec3& yAxis, const Vec3& zAxis, const Vec3& extents)
{
    _center = center;
    _xAxis = xAxis;
    _yAxis = yAxis;
    _zAxis = zAxis;
    _extents = extents;
}
//复位
void OBB::reset()
{
    memset(this, 0, sizeof(OBB)); //将OBB所在内存块置零
}
//获取顶点信息
void OBB::getCorners(Vec3* verts) const
{
    Vec3 extX = _xAxis * _extents.x;  //x方向分量
    Vec3 extY = _yAxis * _extents.y;  //y方向分量
    Vec3 extZ = _zAxis * _extents.z;  //z方向分量
    
    //z轴正方向的面
    verts[0] = _center - extX + extY + extZ;     // 左上顶点坐标
    verts[1] = _center - extX - extY + extZ;     // 左下顶点坐标
    verts[2] = _center + extX - extY + extZ;     // 右下顶点坐标
    verts[3] = _center + extX + extY + extZ;     // 右上顶点坐标
    
    //z轴负方向的面
    verts[4] = _center + extX + extY - extZ;     // 右上顶点坐标
    verts[5] = _center + extX - extY - extZ;     // 右下顶点坐标
    verts[6] = _center - extX - extY - extZ;     // 左下顶点坐标
    verts[7] = _center - extX + extY - extZ;     // 左上顶点坐标
}
//将点投影到坐标轴
float OBB::projectPoint(const Vec3& point, const Vec3& axis)const
{
    float dot = axis.dot(point); //点积
    float ret = dot * point.length();
    return ret;
}
//计算最大最小投影值
void OBB::getInterval(const OBB& box, const Vec3& axis, float &min, float &max)const
{
    Vec3 corners[8];
    box.getCorners(corners);//获取包围盒顶点信息
    float value;
    //分别投影八个点。取最大和最小值
    min = max = projectPoint(axis, corners[0]);
    for(int i = 1; i < 8; i++)
    {
        value = projectPoint(axis, corners[i]);
        min = MIN(min, value);
        max = MAX(max, value);
    }
}
//取边的矢量
Vec3 OBB::getEdgeDirection(int index)const
{
    Vec3 corners[8];
    getCorners(corners); //获取八个顶点信息
    
    Vec3 tmpLine;
    switch(index)
    {
        case 0:// x轴方向
            tmpLine = corners[5] - corners[6];
            tmpLine.normalize(); //归一化
            break;
        case 1:// y轴方向
            tmpLine = corners[7] - corners[6];
            tmpLine.normalize();
            break;
        case 2:// z轴方向
            tmpLine = corners[1] - corners[6];
            tmpLine.normalize();
            break;
        default:
            CCASSERT(0, "Invalid index!");
            break;
    }
    return tmpLine;
}
//取面的方向矢量
Vec3 OBB::getFaceDirection(int index) const
{
    Vec3 corners[8];
    getCorners(corners); //获取八个顶点信息
    
    Vec3 faceDirection, v0, v1;
    switch(index)
    {
        case 0://前/后 计算结果为一个与z轴平行的矢量
            v0 = corners[2] - corners[1]; //朝向+z的面 左下点->右下点的矢量
            v1 = corners[0] - corners[1]; //          左下点->左上点的矢量
            /*
               两个矢量的叉积得到的结果
               是垂直于原来两个相乘矢量的矢量
             */
            Vec3::cross(v0, v1, &faceDirection); //计算v0,v1的叉积 结果存储到faceDirection
            /*
                归一化
                此处相当于求x,y轴所在平面的法矢量
             */
            faceDirection.normalize();
            break;
        case 1:// 左/右 计算结果为一个与x轴平行的矢量
            v0 = corners[5] - corners[2];
            v1 = corners[3] - corners[2];
            Vec3::cross(v0, v1, &faceDirection);
            faceDirection.normalize();
            break;
        case 2:// 上/下 计算结果为一个与y轴平行的矢量
            v0 = corners[1] - corners[2];
            v1 = corners[5] - corners[2];
            Vec3::cross(v0, v1, &faceDirection);
            faceDirection.normalize();
            break;
        default:
            CCASSERT(0, "Invalid index!");
            break;
    }
    return faceDirection; //返回方向矢量
}
//检測两个OBB包围盒是否重合
bool OBB::intersects(const OBB& box) const
{
    float min1, max1, min2, max2;
    //当前包围盒的三个面方向 相当于取包围盒的三个坐标轴为分离轴并计算投影作比較
    for (int i = 0; i < 3; i++)
    {
        getInterval(*this, getFaceDirection(i), min1, max1);//计算当前包围盒在某轴上的最大最小投影值
        getInterval(box, getFaceDirection(i), min2, max2);//计算还有一个包围盒在某轴上的最大最小投影值
        if (max1 < min2 || max2 < min1) return false; //推断分离轴上投影是否重合
    }
    //box包围盒的三个面方向
    for (int i = 0; i < 3; i++)
    {
        getInterval(*this, box.getFaceDirection(i), min1, max1);
        getInterval(box, box.getFaceDirection(i), min2, max2);
        if (max1 < min2 || max2 < min1) return false;
    }
    
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            Vec3 axis;
            //Vec3::cross(getFaceDirection(i), box.getFaceDirection(j), &axis); //2d-x源代码
            Vec3::cross(getEdgeDirection(i), box.getEdgeDirection(j), &axis); //改动,这里应该边的矢量并做叉积
            getInterval(*this, axis, min1, max1);
            getInterval(box, axis, min2, max2);
            if (max1 < min2 || max2 < min1) return false;
        }
    }
    
    return true;
}

//由一个给定矩阵对OBB包围盒进行变换
void OBB::transform(const Mat4& mat)
{
    // 新的中心点
    Vec4 newcenter = mat * Vec4(_center.x, _center.y, _center.z, 1.0f);
    _center.x = newcenter.x;
    _center.y = newcenter.y;
    _center.z = newcenter.z;

    //变换向量
    _xAxis = mat * _xAxis;
    _yAxis = mat * _yAxis;
    _zAxis = mat * _zAxis;

    _xAxis.normalize();  //归一化
    _yAxis.normalize();
    _zAxis.normalize();

    Vec3 scale, trans; 
    Quaternion quat;  //四元数 单位长度的四元数能够表示三维旋转
    mat.decompose(&scale, &quat, &trans);

    //半长 半宽 半高 
    _extents.x *= scale.x;
    _extents.y *= scale.y;
    _extents.z *= scale.z;
}

NS_CC_END



      
posted @ 2017-07-10 08:51  mfmdaoyou  阅读(629)  评论(0编辑  收藏  举报