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


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

    (2010-06-11 19:14:27)
    标签:

    杂谈

    分类: MSN搬家
        我们课程的期末项目是做一个蚊子吸血的三维游戏。由于蚊子的体积很小,并且蚊子需要在三维场景中穿梭飞行。因此常规的模型包围盒检测便显得有些不足。例如,场景中有一个台灯,从模型的包围盒看,台灯是一个长方体,如果按照这样来做碰撞检测,那么蚊子便无法从台灯的长长的弧形躯干形成的拱桥洞中飞过去,尽管看起来蚊子确实没有撞到台灯。这在现实中是让人无法接受的。
        我曾经尝试了几种方法,一种是用从蚊子出发的指向各个方向的几条射线来检测碰撞,另一种是用一个球体包围蚊子,然后检测球体中的物体个数来检测碰撞。但最终都失败了,其本质的原因还是因为,这些碰撞只能检测到和模型包围盒的交点。
        看来,必须要寻找更好的解决方案。
        很幸运,我找到一个第三方的开源库,叫做  Minimal Ogre Collision  。可以在http://www.ogre3d.org/wiki/index.php/Minimal_Ogre_Collision上找到它。
        我决定把其中最核心的一段代码挑出来,详细解读。
    //ray,求交射线;result,与模型面片的交点;target,相交物体;closest_distance,距离最近交点的距离;queryMask,碰撞检测掩码
    bool CollisionTools::raycast(const Ogre::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 without RaySceneQuery instance" << ENDLOG;
            return (false);
        }
        // 注意哦,到这里我们已经得到一系列按照包围盒检测到的模型了.
        // 我们要找到第一个相交的物体.
        // 这就意味着我们不必去检测后面的物体了,这样大大节省了时间
        // 但是很遗憾,我们不得不遍历每一个物体的三角面,听起来是多么痛苦,必须得忍
        // 
        //初始化最小距离为-1
     closest_distance = -1.0f;
        Ogre::Vector3 closest_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);
        }
    }
  • 相关阅读:
    UITextView自适应高度解决方法
    UITextView自适应高度出现的问题
    UITextView出现的一些问题
    服务器终于好了!
    Update语句
    VS.NET经验与技巧
    唯一约束
    由C#风潮想起的-给初学编程者的忠告
    location.search在客户端获取Url参数的方法
    Web Services 入门概念
  • 原文地址:https://www.cnblogs.com/lizhengjin/p/1914145.html
Copyright © 2020-2023  润新知