OGRE实现鼠标选取物体

OGRE实现鼠标选取物体  

2010-01-20 23:57:19|  分类: ogre|字号 订阅

OGRE实现鼠标选取物体(精确到三角面)——附源码
以前一直以为OGRE只支持简单的AABB盒射线检测,今天才发现原来OGRE支持精确到三角面的射线检测。
有图有真相,下边上图:

只有当从鼠标位置发出的射线与模型的一个三角面相交时才表示选中该模型,此时点击鼠标就可以选中该模型,使其高亮显示。最左边模型的那一圈黄色表示已经点击鼠标选中该模型,高亮显示以区别其他模型。

上边这张图显示的是选中Y坐标轴,使其颜色改变,表示其被选中,然后在移动模型的时候只改变模型在Y轴的坐标。

源码如下:
bool PickEntity(Ogre::RaySceneQuery* mRaySceneQuery, Ogre::Ray &ray, Ogre::Entity **result, Ogre::uint32 mask ,Ogre::Vector3 &hitpoint, bool excludeInVisible,const Ogre::String& excludeobject, Ogre::Real max_distance)
{
    mRaySceneQuery->setRay(ray);
    mRaySceneQuery->setQueryMask(mask);
    mRaySceneQuery->setQueryTypeMask(Ogre::SceneManager::ENTITY_TYPE_MASK);
    mRaySceneQuery->setSortByDistance(true);

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

    // at this point we have raycast to a series of different objects bounding boxes.
    // we need to test these different objects to see which is the first polygon hit.
    // there are some minor optimizations (distance based) that mean we wont have to
    // check all of the objects most of the time, but the worst case scenario is that
    // we need to test every triangle of every object.
    Ogre::Real closest_distance = max_distance;
    Ogre::Vector3 closest_result;
    Ogre::RaySceneQueryResult &query_result = mRaySceneQuery->getLastResults();
    for (size_t qr_idx = 0; qr_idx < query_result.size(); qr_idx++)
    {
        // stop checking if we have found a raycast hit that is closer
        // than all remaining entities
        if ((closest_distance >= 0.0f) && (closest_distance < query_result[qr_idx].distance))
        {
            break;
        }

        // on ly check this result if its a hit against an entity
        if ((query_result[qr_idx].movable != NULL) && (query_result[qr_idx].movable->getMovableType().compare("Entity") == 0))
        {
            // get the entity to check
            Ogre::Entity *pentity = static_cast<Ogre::Entity*>(query_result[qr_idx].movable);

            if(excludeInVisible)
                if (!pentity->getVisible())
                    continue;
            if(pentity->getName() == excludeobject) 
                continue;

            // mesh da ta to retrieve
            size_t vertex_count;
            size_t index_count;
            Ogre::Vector3 *vertices;
            unsigned long *indices;

            // get the mesh information
            GetMeshInformationEx(pentity->getMesh(), vertex_count, vertices, index_count, indices,
                pentity->getParentNode()->_getDerivedPosition(),
                pentity->getParentNode()->_getDerivedOrientation(),
                pentity->getParentNode()->_getDerivedScale());
            
            //maybe there is a bug in GetMeshInformationEx(),when mesh is a line or a circle, the vertex_count is not multiple of 3
//            if (index_count%3 != 0)
//            {
//                index_count-=index_count%3;
//            }

            // test for hitting individual triangles on the mesh
            bool new_closest_found = false;
            for (int i = 0; i < static_cast<int>(index_count); i += 3) 
            {

                // check for a hit against this triangle
                std::pair<bool, Ogre::Real> hit = Ogre::Math::intersects(ray, vertices[indices[i]],
                    vertices[indices[i+1]], vertices[indices[i+2]], true, true);

                // if it was a hit check if its the closest
                if (hit.first)
                {
                    if ((closest_distance < 0.0f) || (hit.second < closest_distance))
                    {
                        // this is the closest so far, save it off
                        closest_distance = hit.second;
                        new_closest_found = true;
                    }
                }
            }

            // free the verticies and indicies memory
            delete[] vertices;
            delete[] indices;

            // if we found a new closest raycast for this object, update the
            // closest_result before moving on to the next object.
            if (new_closest_found)
            {
                closest_result = ray.getPoint(closest_distance);
                (*result) = pentity;
            }
        }
    }

    // return the result
    if (closest_distance != max_distance)
    {
        hitpoint = closest_result;
        return true;
    }
    else
    {
        // raycast failed
        return false;
    }
}

void GetMeshInformationEx(const Ogre::MeshPtr mesh,
                          size_t &vertex_count,
                          Ogre::Vector3* &vertices,
                          size_t &index_count,
                          unsigned long* &indices,
                          const Ogre::Vector3 &position,
                          const Ogre::Quaternion &orient,
                          const Ogre::Vector3 &scale)
{
    bool added_shared = false;
    size_t current_offset = 0;
    size_t shared_offset = 0;
    size_t next_offset = 0;
    size_t index_offset = 0;

    vertex_count = index_count = 0;

    // Calculate how many vertices and indices we're going to need
    for (unsigned short i = 0; i < mesh->getNumSubMeshes(); ++i)
    {
        Ogre::SubMesh* submesh = mesh->getSubMesh( i );

        // We on ly need to add the shared vertices on ce
        if(submesh->useSharedVertices)
        {
            if( !added_shared )
            {
                vertex_count += mesh->sharedVertexData->vertexCount;
                added_shared = true;
            }
        }
        else
        {
            vertex_count += submesh->vertexData->vertexCount;
        }

        // Add the indices
        index_count += submesh->indexData->indexCount;
    }


    // Allocate space for the vertices and indices
    vertices = new Ogre::Vector3[vertex_count];
    indices = new unsigned long[index_count];

    added_shared = false;

    // Run through the submeshes again, adding the da ta into the arrays
    for ( unsigned short i = 0; i < mesh->getNumSubMeshes(); ++i)
    {
        Ogre::SubMesh* submesh = mesh->getSubMesh(i);

        Ogre::VertexData* vertex_da ta = submesh->useSharedVertices ? mesh->sharedVertexData : submesh->vertexData;

        if((!submesh->useSharedVertices)||(submesh->useSharedVertices && !added_shared))
        {
            if(submesh->useSharedVertices)
            {
                added_shared = true;
                shared_offset = current_offset;
            }

            const Ogre::VertexElement* posElem = vertex_da ta->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION);

            Ogre::HardwareVertexBufferSharedPtr vbuf = vertex_da ta->vertexBufferBinding->getBuffer(posElem->getSource());

            unsigned char* vertex = static_cast<unsigned char*>(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ON LY));

            // There is _no_ baseVertexPointerToElement() which takes an Ogre::Real or a double
            //  as second argument. So make it float, to avoid trouble when Ogre::Real will
            //  be comiled/typedefed as double:
            //      Ogre::Real* pReal;
            float* pReal;

            for( size_t j = 0; j < vertex_da ta->vertexCount; ++j, vertex += vbuf->getVertexSize())
            {
                posElem->baseVertexPointerToElement(vertex, &pReal);

                Ogre::Vector3 pt(pReal[0], pReal[1], pReal[2]);

                vertices[current_offset + j] = (orient * (pt * scale)) + position;
            }

            vbuf->unlock();
            next_offset += vertex_da ta->vertexCount;
        }


        Ogre::IndexData* index_da ta = submesh->indexData;
        size_t numTris = index_da ta->indexCount / 3;
        Ogre::HardwareIndexBufferSharedPtr ibuf = index_da ta->indexBuffer;

        bool use32bitindexes = (ibuf->getType() == Ogre::HardwareIndexBuffer::IT_32BIT);

        unsigned long*  pLong = static_cast<unsigned long*>(ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ON LY));
        unsigned short* pShort = reinterpret_cast<unsigned short*>(pLong);

        size_t offset = (submesh->useSharedVertices)? shared_offset : current_offset;

        if ( use32bitindexes )
        {
            for ( size_t k = 0; k < numTris*3; ++k)
            {
                indices[index_offset++] = pLong[k] + static_cast<unsigned long>(offset);
            }
        }
        else
        {
            for ( size_t k = 0; k < numTris*3; ++k)
            {
                indices[index_offset++] = static_cast<unsigned long>(pShort[k]) + static_cast<unsigned long>(offset);
            }
        }

        ibuf->unlock();
        current_offset = next_offset;
    }
}

使用时用如下代码即可(可能需要根据实际情况有所改动):
Entity* rayResult=NULL;
Vector3 hitPoint;
CEGUI::Point mousePos= CEGUI::MouseCursor::getSingleton().getPosition();
Ray mouseRay = mCamera->getCameraToViewportRay(mousePos.d_x/float(e.state.width),mousePos.d_y/float(e.state.height));
RaySceneQuery* rayQuery=mSceneMgr->createRayQuery(Ray());
if (PickEntity(rayQuery,mouseRay,&rayResult,QF_moveable_object,hitPoint,true))
          rayResult->getParentSceneNode(); //find a entity
以上源码参考Ogitor,为了配合自己的程序,稍有改动。
为了方便大家,所以贴出来,建议大家直接看Ogitor源码。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值