http://blog.csdn.net/wkc123456789/article/details/8424938
最近在做有关kinect的应用。想利用Kinect使玩家可以用肢体控制虚拟人物,并和电脑AI控制的NPC做搏击,该系统基于Ogre,Kinect sdk 1.0,QT。目前已经完成了Kinect对骨骼蒙皮人物的驱动,最近在做加入NPC后,两个人物之间的碰撞检测,由于游戏中对实时性的要求比较高,不可能进行三角面片级的碰撞检测,因此我使用了包围盒来替代人体躯干。但由于Ogre似乎仅支持AABB这种简单的碰撞检测,而人物肢体这样的类似于细长的长方体AABB包围盒不够紧凑,不能很好的模拟人物之间的碰撞,因此我选用OBB来进行骨骼人物的碰撞检测。
大致的过程是首先第一步要从Ogre中获取人物的骨骼关节点所对应的点集。然后对每个点集求得OBB的各个参数。最后即可进行OBB与OBB之间的碰撞检测,当然在创建各个关节点的OBB后,可以进一步创建层次包围盒或者为每个OBB在创建一个包围球来进一步优化碰撞效率。
对于如何从Ogre中获取各个骨骼关节点对应的点集,由于我也是Ogre新手,可能方法还有点问题吧,贴上代码供参考:
- void SkeletonModelOBB::SetupData()
- {
- Ogre::MeshPtr msh=playerModel->getMesh();
- bool addShared=false;
- Ogre::Vector3 *sharedVertexArray;
- Ogre::Vector3 *vertexArray;
- Ogre::Vector3 *tempArray;
- for (int i=0;i<msh->getNumSubMeshes();i++)
- {
- Ogre::SubMesh *submsh=msh->getSubMesh(i);
- Ogre::VertexData *vertex;
- if (!addShared&&submsh->useSharedVertices)
- {
- vertex=msh->sharedVertexData;
- }
- else vertex=submsh->vertexData;
- int vertexCount=vertex->vertexCount;
- if (!submsh->useSharedVertices||(!addShared&&submsh->useSharedVertices))
- {
- if (submsh->useSharedVertices)
- {
- addShared=true;
- sharedVertexArray=new Ogre::Vector3[vertexCount];
- tempArray=sharedVertexArray;
- }
- else
- {
- vertexArray=new Ogre::Vector3[vertexCount];
- tempArray=vertexArray;
- }
- const Ogre::VertexElement *PosElement=vertex->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION);
- const Ogre::HardwareVertexBufferSharedPtr hb=vertex->vertexBufferBinding->getBuffer(PosElement->getSource());
- unsigned char *vertexBuffer=static_cast<unsigned char*>(hb->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
- float *data;
- for (int j=0;j<vertex->vertexCount;j++)
- {
- PosElement->baseVertexPointerToElement(vertexBuffer,&data);
- Ogre::Vector3 temp(data[0],data[1],data[2]);
- tempArray[j]=temp;
- vertexBuffer+=hb->getVertexSize();
- }
- hb->unlock();
- }
- if (submsh->useSharedVertices)
- {
- tempArray=sharedVertexArray;
- }
- else tempArray=vertexArray;
- Ogre::SubMesh::VertexBoneAssignmentList VBAssignList=submsh->getBoneAssignments();
- for (Ogre::SubMesh::VertexBoneAssignmentList::iterator i=VBAssignList.begin();i!=VBAssignList.end();i++)
- {
- if (i->second.weight>=0.5)
- {
- Ogre::Vector3 temp=tempArray[i->second.vertexIndex];
- BoneVertexList[i->second.boneIndex].push_back(tempArray[i->second.vertexIndex]);
- }
- }
- }
- }
获得关节点对应的顶点集后,即可使用相关算法为每个关节生成OBB。一般方法为先生成点集的凸包,然后求的凸包的协方差矩阵,并根据解得的特征向量,得到OBB盒的三个轴,再将点集投影到轴上,得到OBB的各轴上的最大最小值,由此便得到OBB。
关于凸包的生成,我采用了简单快捷而又便于理解的QuikHull算法。基本思想是先在点集中寻找极值点,可以先找x,y,z方向的最大最小点,这样可得到6个点,而由于可能存在极值点重合的情况,结果可能是2-6个点,我的目标是初始化一个四面体,需要四个点,因此若不重复极值点大于等于四则任意取四个点作为四面体的顶点,若不重复极值点有三个,则通过搜索离该三个点组成的三角面最远的顶点作为四面体的另一个初始点,若不重复极值点仅为2个,则先找到离该线段最远的点后,组成三角面,再同样根据三角面找到离开该面最远的点组成四面体。在拥有了初始的四面体后,利用四面体的各个面作为点集的分割面再由四面体的任意一个内部点进行点的划分,划分得到的点与内部点在分割面的不同侧,然后在划分得到的点集中找到距离分割面最远的那个点与分割面再次组成一个四面体,递归划分,划分的结束条件为分割面另一侧没有其他点存在。2D算法示意图如下:
关于生成OBB的类的声明:
- class OBB
- {
- public:
- Ogre::Vector3 center;
- Ogre::Vector3 axis[3];
- Ogre::Vector3 length;
- std::vector<Ogre::Vector3>HullTriangleList;
- OBB()
- {
- }
- OBB(std::list<Ogre::Vector3> &VertexBoneList)
- {
- std::list<Ogre::Vector3> VertexBone(VertexBoneList);
- if(VertexBone.size()<4)
- {
- length=Ogre::Vector3(0,0,0);
- std::cout<<"wrong";
- }
- else
- {
- computConvex(VertexBone);
- Ogre::Real EigenValue[3];
- Ogre::Vector3 EigenVector[3];
- Computing(EigenValue,EigenVector);
- ComputeBoxParamter(EigenVector);
- std::cout<<"中心"<<this->center<<" u轴"<<this->axis[0]<<" 长度"<<this->length[0]<<" v轴"<<axis[1]<<"长度"<<this->length[1]<<" w轴"<<axis[2]<<"长度"<<this->length[1]<<" ";
- }
- }
- ~OBB()
- {
- }
- virtual void Add()
- {
- }
- virtual void Remove()
- {
- }
- /************************************************************************/
- /* 计算骨骼关联的点集的凸包 */
- /************************************************************************/
- void computConvex(std::list<Ogre::Vector3> &VertexBone);
- /************************************************************************/
- /* 寻找组成体空间中的任意一个内部点 */
- /************************************************************************/
- inline Ogre::Vector3 fineInnerPoint(std::vector<Ogre::Vector3>vertex);
- /************************************************************************/
- /* 根据分割面,将点集中位于分割面一侧的点从点集中取出 */
- /************************************************************************/
- void FindVertexSet(std::list<Ogre::Vector3>&vertexBone,
- std::list<Ogre::Vector3>&result,
- Ogre::Vector3 *plane,
- Ogre::Vector3 innerPoint);
- /************************************************************************/
- /* QuickHull算法 */
- /************************************************************************/
- void QuickHull(std::list<Ogre::Vector3>&vertexSet,
- Ogre::Vector3 *triangle,
- Ogre::Vector3 innerPoint);
- /************************************************************************/
- /* 根据凸体计算协方差矩阵,求的其特征向量作为OBB的新的维度 */
- /************************************************************************/
- void Computing(Ogre::Real EigenValue[3],
- Ogre::Vector3 EigenVector[3]);
- void ComputeBoxParamter(Ogre::Vector3 *EigenVector);
- //使用分离轴定理进行碰撞检测
- virtual bool OBB2OBBIntersect(OBB *box);
- //void upDate();
- };
在得到OBB的凸包后,然后计算凸包的协方差矩阵,求的矩阵的特征向量即为OBB的三个轴,关于为什么协方差矩阵的特征向量即为OBB的三个轴,可百度PCA的原理了解。
未完待续