您的位置:首页 > 其它

Ogre碰撞检测,精确到物体网格三角面

2013-06-05 10:26 906 查看
我们课程的期末项目是做一个蚊子吸血的三维游戏。由于蚊子的体积很小,并且蚊子需要在三维场景中穿梭飞行。因此常规的模型包围盒检测便显得有些不足。例如,场景中有一个台灯,从模型的包围盒看,台灯是一个长方体,如果按照这样来做碰撞检测,那么蚊子便无法从台灯的长长的弧形躯干形成的拱桥洞中飞过去,尽管看起来蚊子确实没有撞到台灯。这在现实中是让人无法接受的。
我曾经尝试了几种方法,一种是用从蚊子出发的指向各个方向的几条射线来检测碰撞,另一种是用一个球体包围蚊子,然后检测球体中的物体个数来检测碰撞。但最终都失败了,其本质的原因还是因为,这些碰撞只能检测到和模型包围盒的交点。
看来,必须要寻找更好的解决方案。
很幸运,我找到一个第三方的开源库,叫做 Minimal OgreCollision 。可以在http://www.ogre3d.org/wiki/index.php/Minimal_Ogre_Collision上找到它。
我决定把其中最核心的一段代码挑出来,详细解读。
//ray,求交射线;result,与模型面片的交点;target,相交物体;closest_distance,距离最近交点的距离;queryMask,碰撞检测掩码
boolCollisionTools::raycast(constOgre::Ray &ray,Ogre::Vector3&result,Ogre::MovableObject*&target,float &closest_distance,const Ogre::uint32 queryMask)

{

target = NULL;
// 测试射线是否有效

if (mRaySceneQuery != NULL)

{

// 创建一个射线查询
mRaySceneQuery->setRay(ray);

mRaySceneQuery->setSortByDistance(true);//查询结果按距离排序

mRaySceneQuery->setQueryMask(queryMask);//设置掩码

// 执行查询

if(mRaySceneQuery->execute().size() <=0)

{
return (false);

}

}

else

{

//LOG_ERROR<< "Cannot raycast withoutRaySceneQuery instance" <<ENDLOG;

return (false);

}
// 注意哦,到这里我们已经得到一系列按照包围盒检测到的模型了.

// 我们要找到第一个相交的物体.

// 这就意味着我们不必去检测后面的物体了,这样大大节省了时间

// 但是很遗憾,我们不得不遍历每一个物体的三角面,听起来是多么痛苦,必须得忍

//

//初始化最小距离为-1

closest_distance = -1.0f;

Ogre::Vector3closest_result;

Ogre::RaySceneQueryResult&query_result =mRaySceneQuery->getLastResults();//取回刚才查询的结果,因为之前并没有保存

for (size_t qr_idx = 0; qr_idx <query_result.size(); qr_idx++)

{

// 如果下一个碰撞物体比这个还远,当然要无视啦
if((closest_distance >= 0.0f)&&

(closest_distance <query_result[qr_idx].distance))

{

break;

}
// 我们只关心碰撞的东西是个物体
if((query_result[qr_idx].movable != NULL) &&

(query_result[qr_idx].movable->getMovableType().compare("Entity")== 0))

{

// 取得被碰撞的物体
Ogre::MovableObject *pentity =static_cast<Ogre::MovableObject*>(query_result[qr_idx].movable);
// 顶点是顶点,索引是索引,不着急,往下看

size_t vertex_count;

size_t index_count;

Ogre::Vector3 *vertices;

Ogre::uint32 *indices;
// 下面的函数得到模型的详细信息

GetMeshInformation(((Ogre::Entity*)pentity)->getMesh(),vertex_count, vertices, index_count, indices,

pentity->getParentNode()->_getDerivedPosition(),

pentity->getParentNode()->_getDerivedOrientation(),

pentity->getParentNode()->_getDerivedScale());
// 再次注意了,下面求每一个三角面的交点,同样记录最近点
bool new_closest_found = false;

for (size_t i = 0; i < index_count; i += 3)

{

// 下面的函数求一条射线与三角面的交点,返回一个pair,《是否相交,距离交点距离》

std::pair<bool,Ogre::Real> hit =Ogre::Math::intersects(ray,vertices[indices[i]],

vertices[indices[i+1]], vertices[indices[i+2]], true,false);//知道索引干嘛用的了吧,索引乃顶点之索引也
// 如果碰撞,检查是否是当前最小距离

if (hit.first)

{

if ((closest_distance < 0.0f) ||

(hit.second < closest_distance))

{

// 如果是则更新

closest_distance = hit.second;

new_closest_found = true;

}

}

}
// 释放刚才申请的内存,这种东西当然是在写申请的时候就要成对编写的了,正如同时写下{}
delete[] vertices;

delete[] indices;
//如果找到新的点,不要忘了更新相应信息

if (new_closest_found)

{

target = pentity;

closest_result =ray.getPoint(closest_distance);//最近点的计算,简单的线性方程

}

}

}
// 返回结果
if (closest_distance >=0.0f)

{

// 成功了
result =closest_result;

return (true);

}

else

{

//失败了
return(false);

}

}

精细的碰撞检测,到这里便可以完成了。剩下的内容自由发挥吧。看来也不是那么复杂啊。下面上一张游戏截图。
仔细看,蚊子从物体中间的孔洞穿过去了




转载自:http://blog.sina.com.cn/s/blog_6e75913c0100mhxt.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: