Skip to main content

As developed by gerds in this forum page.

Mogre (.NET) users can use a C# port: Raycasting to the polygon level - Mogre

Introduction

An Ogre ray query generally only detect bounding boxes (-AABBs), like you see on this image. With this code you can avoid this disadvantage.
Rays_and_BoundingBox_1a.gif

Important note

This code only returns correct results for polygons defined by rendertype OT_TRIANGLE_LIST. If a mesh contains other rendertypes (different triangle definition), lines or points, you will get wrong results or even a crash. For more details and improvement suggestions look here.

Code for initialization

Copy to clipboard
// create the ray scene query object m_pray_scene_query = m_pscene_manager->createRayQuery(Ogre::Ray(), Ogre::SceneManager::WORLD_GEOMETRY_TYPE_MASK); if (NULL == m_pray_scene_query) { LOG_ERROR << "Failed to create Ogre::RaySceneQuery instance" << ENDLOG; return (false); } m_pray_scene_query->setSortByDistance(true);

Method for raycast

Copy to clipboard
// raycast from a point in to the scene. // returns success or failure. // on success the point is returned in the result. bool OgreVisionEngine::RaycastFromPoint(const Vector3 &point, const Vector3 &normal, Vector3 &result) { // create the ray to test Ogre::Ray ray(Ogre::Vector3(point.x, point.y, point.z), Ogre::Vector3(normal.x, normal.y, normal.z)); // check we are initialised if (m_pray_scene_query != NULL) { // create a query object m_pray_scene_query->setRay(ray); // execute the query, returns a vector of hits if (m_pray_scene_query->execute().size() <= 0) { // raycast did not hit an objects bounding box return (false); } } else { LOG_ERROR << "Cannot raycast without RaySceneQuery instance" << ENDLOG; 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 = -1.0f; Ogre::Vector3 closest_result; Ogre::RaySceneQueryResult &query_result = m_pray_scene_query->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; } // only 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); // mesh data to retrieve size_t vertex_count; size_t index_count; Ogre::Vector3 *vertices; unsigned long *indices; // get the mesh information OgreVE::GetMeshInformation(pentity->getMesh(), vertex_count, vertices, index_count, indices, pentity->getParentNode()->getWorldPosition(), pentity->getParentNode()->getWorldOrientation(), pentity->getParentNode()->_getDerivedScale()); // 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, false); // 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); } } } // return the result if (closest_distance >= 0.0f) { // raycast success result = closest_result; return (true); } else { // raycast failed return (false); } }

GetMeshInformation

This code is a copy of RetrieveVertexData.(Optimized version)

Original version

Copy to clipboard
// Get the mesh information for the given mesh. // Code found in Wiki: www.ogre3d.org/wiki/index.php/RetrieveVertexData void OgreVE::GetMeshInformation(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 only need to add the shared vertices once 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 data into the arrays for ( unsigned short i = 0; i < mesh->getNumSubMeshes(); ++i) { Ogre::SubMesh* submesh = mesh->getSubMesh(i); Ogre::VertexData* vertex_data = 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_data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION); Ogre::HardwareVertexBufferSharedPtr vbuf = vertex_data->vertexBufferBinding->getBuffer(posElem->getSource()); unsigned char* vertex = static_cast<unsigned char*>(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY)); // 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_data->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_data->vertexCount; } Ogre::IndexData* index_data = submesh->indexData; size_t numTris = index_data->indexCount / 3; Ogre::HardwareIndexBufferSharedPtr ibuf = index_data->indexBuffer; if( ibuf.isNull() ) continue; // need to check if index buffer is valid (which will be not if the mesh doesn't have triangles like a pointcloud) bool use32bitindexes = (ibuf->getType() == Ogre::HardwareIndexBuffer::IT_32BIT); unsigned long* pLong = static_cast<unsigned long*>(ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY)); unsigned short* pShort = reinterpret_cast<unsigned short*>(pLong); size_t offset = (submesh->useSharedVertices)? shared_offset : current_offset; size_t index_start = index_data->indexStart; size_t last_index = numTris*3 + index_start; if (use32bitindexes) for (size_t k = index_start; k < last_index; ++k) { indices[index_offset++] = pLong[k] + static_cast<unsigned long>( offset ); } else for (size_t k = index_start; k < last_index; ++k) { indices[ index_offset++ ] = static_cast<unsigned long>( pShort[k] ) + static_cast<unsigned long>( offset ); } ibuf->unlock(); current_offset = next_offset; } }

Adapted version

The following is an adapted version of GetMeshInformation() that takes into account the entity in its currently animated state.
It's a modified version from OgreOpcode and it has worked flawlessly since many years.
(Thanks to jacmoe, Baxissimo and KingPrawnVindaloo)


Note:
This code can cause problems, if the animated model uses hardware skinning.
Look to this forum topic for details.

Copy to clipboard
void GetMeshInformation(const Entity *entity, 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; Ogre::MeshPtr mesh = entity->getMesh(); bool useSoftwareBlendingVertices = entity->hasSkeleton(); if (useSoftwareBlendingVertices) { entity->_updateAnimation(); } // 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 only need to add the shared vertices once 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 data into the arrays for ( unsigned short i = 0; i < mesh->getNumSubMeshes(); ++i) { Ogre::SubMesh* submesh = mesh->getSubMesh(i); //---------------------------------------------------------------- // GET VERTEXDATA //---------------------------------------------------------------- //Ogre::VertexData* vertex_data = submesh->useSharedVertices ? mesh->sharedVertexData : submesh->vertexData; Ogre::VertexData* vertex_data; //When there is animation: if(useSoftwareBlendingVertices) vertex_data = submesh->useSharedVertices ? entity->_getSkelAnimVertexData() : entity->getSubEntity(i)->_getSkelAnimVertexData(); else vertex_data = 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_data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION); Ogre::HardwareVertexBufferSharedPtr vbuf = vertex_data->vertexBufferBinding->getBuffer(posElem->getSource()); unsigned char* vertex = static_cast<unsigned char*>(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY)); // 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_data->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_data->vertexCount; } Ogre::IndexData* index_data = submesh->indexData; size_t numTris = index_data->indexCount / 3; Ogre::HardwareIndexBufferSharedPtr ibuf = index_data->indexBuffer; bool use32bitindexes = (ibuf->getType() == Ogre::HardwareIndexBuffer::IT_32BIT); unsigned long* pLong = static_cast<unsigned long*>(ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY)); unsigned short* pShort = reinterpret_cast<unsigned short*>(pLong); size_t offset = (submesh->useSharedVertices)? shared_offset : current_offset; size_t index_start = index_data->indexStart; size_t last_index = numTris*3 + index_start; if (use32bitindexes) for (size_t k = index_start; k < last_index; ++k) { indices[index_offset++] = pLong[k] + static_cast<unsigned long>( offset ); } else for (size_t k = index_start; k < last_index; ++k) { indices[ index_offset++ ] = static_cast<unsigned long>( pShort[k] ) + static_cast<unsigned long>( offset ); } ibuf->unlock(); current_offset = next_offset; } }


Adapted version 2

The versions of GetMeshInformation() fails on 64 bit machines using GCC. The following version, adapted from Rumi's version, solves this issue for me:

Difference:

  • Changed type of "indices" from long (uint64) to uint32

Important:

  • Every sub mesh can contain up to 2^32 indices.
  • If the whole mesh has more than 2^32 indices, the code will crash at: indices = new Ogre::uint32[index_count]
  • In this code is no security check or special treatment for "uint32 overflow".
  • Suggestion: Use this modified code only if really needed.


Copy to clipboard
void RayCastUtils::GetMeshInformation(Entity *entity, size_t &vertex_count, Ogre::Vector3* &vertices, size_t &index_count, Ogre::uint32* &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; Ogre::MeshPtr mesh = entity->getMesh(); bool useSoftwareBlendingVertices = entity->hasSkeleton(); if (useSoftwareBlendingVertices) { entity->_updateAnimation(); } // 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 only need to add the shared vertices once 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 Ogre::uint32[index_count]; added_shared = false; // Run through the submeshes again, adding the data into the arrays for ( unsigned short i = 0; i < mesh->getNumSubMeshes(); ++i) { Ogre::SubMesh* submesh = mesh->getSubMesh(i); //---------------------------------------------------------------- // GET VERTEXDATA //---------------------------------------------------------------- Ogre::VertexData* vertex_data; //When there is animation: if(useSoftwareBlendingVertices) vertex_data = submesh->useSharedVertices ? entity->_getSkelAnimVertexData() : entity->getSubEntity(i)->_getSkelAnimVertexData(); else vertex_data = 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_data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION); Ogre::HardwareVertexBufferSharedPtr vbuf = vertex_data->vertexBufferBinding->getBuffer(posElem->getSource()); unsigned char* vertex = static_cast<unsigned char*>(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY)); // 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_data->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_data->vertexCount; } Ogre::IndexData* index_data = submesh->indexData; size_t numTris = index_data->indexCount / 3; Ogre::HardwareIndexBufferSharedPtr ibuf = index_data->indexBuffer; bool use32bitindexes = (ibuf->getType() == Ogre::HardwareIndexBuffer::IT_32BIT); void* hwBuf = ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY); size_t offset = (submesh->useSharedVertices)? shared_offset : current_offset; size_t index_start = index_data->indexStart; size_t last_index = numTris*3 + index_start; if (use32bitindexes) { Ogre::uint32* hwBuf32 = static_cast<Ogre::uint32*>(hwBuf); for (size_t k = index_start; k < last_index; ++k) { indices[index_offset++] = hwBuf32[k] + static_cast<Ogre::uint32>( offset ); } } else { Ogre::uint16* hwBuf16 = static_cast<Ogre::uint16*>(hwBuf); for (size_t k = index_start; k < last_index; ++k) { indices[ index_offset++ ] = static_cast<Ogre::uint32>( hwBuf16[k] ) + static_cast<Ogre::uint32>( offset ); } } ibuf->unlock(); current_offset = next_offset; } }


If you're using a newer version of Ogre some of the function names have changed. You'll want to change the call to GetMeshInformation() to this:

Copy to clipboard
GetMeshInformation( pentity, vertex_count, vertices, index_count, indices, pentity->getParentNode()->_getDerivedPosition(), pentity->getParentNode()->_getDerivedOrientation(), pentity->getParentNode()->_getDerivedScale() );


ManualObject GetMeshInformation()

You can use this to raycast into ManualObjects too. Half-Written by PickleNinja, Help from Beauty's C# stuff here on getting triangle data from a ManualObject.

Copy to clipboard
void getMeshInformation(Ogre::ManualObject *manual, 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) { std::vector<Ogre::Vector3> returnVertices; std::vector<unsigned long> returnIndices; unsigned long thisSectionStart = 0; for (int i=0; i<manual->getNumSections(); i++) { Ogre::ManualObject::ManualObjectSection * section = manual->getSection(i); Ogre::RenderOperation * renderOp = section->getRenderOperation(); std::vector<Ogre::Vector3> pushVertices; //Collect the vertices { const Ogre::VertexElement * vertexElement = renderOp->vertexData->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION); Ogre::HardwareVertexBufferSharedPtr vertexBuffer = renderOp->vertexData->vertexBufferBinding->getBuffer(vertexElement->getSource()); char * verticesBuffer = (char*)vertexBuffer->lock(Ogre::HardwareBuffer::HBL_READ_ONLY); float * positionArrayHolder; thisSectionStart = returnVertices.size() + pushVertices.size(); pushVertices.reserve(renderOp->vertexData->vertexCount); for (unsigned int j=0; j<renderOp->vertexData->vertexCount; j++) { vertexElement->baseVertexPointerToElement(verticesBuffer + j * vertexBuffer->getVertexSize(), &positionArrayHolder); Ogre::Vector3 vertexPos = Ogre::Vector3(positionArrayHolder[0], positionArrayHolder[1], positionArrayHolder[2]); vertexPos = (orient * (vertexPos * scale)) + position; pushVertices.push_back(vertexPos); } vertexBuffer->unlock(); } //Collect the indices { if (renderOp->useIndexes) { Ogre::HardwareIndexBufferSharedPtr indexBuffer = renderOp->indexData->indexBuffer; if (indexBuffer.isNull() || renderOp->operationType != Ogre::RenderOperation::OT_TRIANGLE_LIST) { //No triangles here, so we just drop the collected vertices and move along to the next section. continue; } else { returnVertices.reserve(returnVertices.size() + pushVertices.size()); returnVertices.insert(returnVertices.end(), pushVertices.begin(), pushVertices.end()); } unsigned int * pLong = (unsigned int*)indexBuffer->lock(Ogre::HardwareBuffer::HBL_READ_ONLY); unsigned short * pShort = (unsigned short*)pLong; returnIndices.reserve(returnIndices.size() + renderOp->indexData->indexCount); for (int j = 0; j < renderOp->indexData->indexCount; j++) { unsigned long index; //We also have got to remember that for a multi section object, each section has //different vertices, so the indices will not be correct. To correct this, we //have to add the position of the first vertex in this section to the index //(At least I think so...) if (indexBuffer->getType() == Ogre::HardwareIndexBuffer::IT_32BIT) index = (unsigned long)pLong[j] + thisSectionStart; else index = (unsigned long)pShort[j] + thisSectionStart; returnIndices.push_back(index); } indexBuffer->unlock(); } } } //Now we simply return the data. index_count = returnIndices.size(); vertex_count = returnVertices.size(); vertices = new Ogre::Vector3[vertex_count]; for (unsigned long i = 0; i<vertex_count; i++) { vertices[i] = returnVertices[i]; } indices = new unsigned long[index_count]; for (unsigned long i = 0; i<index_count; i++) { indices[i] = returnIndices[i]; } //All done. return; }

Working version for Ogre v1.7.3: Entity, ManualObject

Copy to clipboard
/***************************************************************************//*! * @brief Ray Collision Detection * Usage: (Example with FPS view & cursor style (minecraft, cs, ...)) * @code * //... * Ogre::SceneManager* sceneMgr = ... * //... * OgreRay ray(sceneMgr); * //... * * Ogre::Vector3 result; * if( ray.RaycastFromPoint(m_camera->getPosition(), Ogre::Vector3 direction = m_camera->getDirection(), result) ){ * printf("Your mouse is over the position %f,%f,%f (witch is textured)\n", result.x, result.y, result.z); * }else{ * printf("No mouse collision\n Are you looking the sky ?\n"); * } * @endcode */ class OgreRay { private: Ogre::RaySceneQuery* m_raySceneQuery;//!< Ray query public: OgreRay( Ogre::SceneManager* sceneMgr ); bool RaycastFromPoint( const Ogre::Vector3& point, const Ogre::Vector3& normal, Ogre::Vector3& result ); private: static void GetMeshInformation( 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 ); static void GetMeshInformation( const Ogre::ManualObject* manual, 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 ); static void GetMeshInformation( const Ogre::Entity* entity, 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 ); }; /***************************************************************************//*! * @brief Init * @param[in] sceneMgr Scene manager * @return[NONE] */ OgreRay::OgreRay( Ogre::SceneManager* sceneMgr ) { m_raySceneQuery = sceneMgr->createRayQuery(Ogre::Ray(), Ogre::SceneManager::WORLD_GEOMETRY_TYPE_MASK); if( !m_raySceneQuery ) printf("["__FILE__"::%u] Failed to create Ogre::RaySceneQuery instance\n", __LINE__); m_raySceneQuery->setSortByDistance(true); } /***************************************************************************//*! * @brief Raycast from a point in to the scene. * @param[in] point Point to analyse * @param[in] normal Direction * @param[out] result Result ( ONLY if return TRUE ) * @return TRUE if somethings found => {result} NOT EMPTY */ bool OgreRay::RaycastFromPoint( const Ogre::Vector3& point, const Ogre::Vector3& normal, Ogre::Vector3& result ) { // create the ray to test Ogre::Ray ray(point,normal); if( !m_raySceneQuery ) return false; // create a query object m_raySceneQuery->setRay(ray); // execute the query, returns a vector of hits if( m_raySceneQuery->execute().size() <= 0 ) // raycast did not hit an objects bounding box 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 = -1.0f; Ogre::Vector3 closest_result; Ogre::RaySceneQueryResult& query_result = m_raySceneQuery->getLastResults(); for( size_t qr_idx=0, size=query_result.size(); qr_idx<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; // only check this result if its a hit against an entity if( query_result[qr_idx].movable ) { const std::string& movableType = query_result[qr_idx].movable->getMovableType(); // mesh data to retrieve size_t vertex_count; size_t index_count; Ogre::Vector3* vertices; unsigned long* indices; if( movableType == "ManualObject" ){ // get the entity to check Ogre::ManualObject* pentity = static_cast<Ogre::ManualObject*>(query_result[qr_idx].movable); // get the mesh information GetMeshInformation( pentity, vertex_count, vertices, index_count, indices, pentity->getParentNode()->_getDerivedPosition(), pentity->getParentNode()->_getDerivedOrientation(), pentity->getParentNode()->_getDerivedScale() ); }else if( movableType == "Entity" ){ // get the entity to check Ogre::Entity *pentity = static_cast<Ogre::Entity*>(query_result[qr_idx].movable); // get the mesh information GetMeshInformation( pentity, vertex_count, vertices, index_count, indices, pentity->getParentNode()->_getDerivedPosition(), pentity->getParentNode()->_getDerivedOrientation(), pentity->getParentNode()->_getDerivedScale() ); }else{ continue; } // 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, false); // if it was a hit check if its the closest if( hit.first && (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); } } // return the result if( closest_distance >= 0.0f ){ // raycast success result = closest_result; return true; } // raycast failed return false; } void OgreRay::GetMeshInformation( const Ogre::Entity* entity, 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; Ogre::MeshPtr mesh = entity->getMesh(); bool useSoftwareBlendingVertices = entity->hasSkeleton(); if( useSoftwareBlendingVertices ) const_cast<Ogre::Entity*>(entity)->_updateAnimation(); // Calculate how many vertices and indices we're going to need for( unsigned short i=0,size=mesh->getNumSubMeshes(); i<size; ++i ) { Ogre::SubMesh* submesh = mesh->getSubMesh(i); // We only need to add the shared vertices once 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 data into the arrays for( unsigned short i=0, size=mesh->getNumSubMeshes(); i<size; ++i) { Ogre::SubMesh* submesh = mesh->getSubMesh(i); //---------------------------------------------------------------- // GET VERTEXDATA //---------------------------------------------------------------- //Ogre::VertexData* vertex_data = submesh->useSharedVertices ? mesh->sharedVertexData : submesh->vertexData; Ogre::VertexData* vertex_data; //When there is animation: if( useSoftwareBlendingVertices ) vertex_data = submesh->useSharedVertices ? entity->_getSkelAnimVertexData() : entity->getSubEntity(i)->_getSkelAnimVertexData(); else vertex_data = 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_data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION); Ogre::HardwareVertexBufferSharedPtr vbuf = vertex_data->vertexBufferBinding->getBuffer(posElem->getSource()); unsigned char* vertex = static_cast<unsigned char*>(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY)); // 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 = 0; for( size_t j=0; j<vertex_data->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_data->vertexCount; } Ogre::IndexData* index_data = submesh->indexData; size_t numTris = index_data->indexCount / 3; Ogre::HardwareIndexBufferSharedPtr ibuf = index_data->indexBuffer; bool use32bitindexes = (ibuf->getType() == Ogre::HardwareIndexBuffer::IT_32BIT); unsigned long* pLong = static_cast<unsigned long*>(ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY)); unsigned short* pShort = reinterpret_cast<unsigned short*>(pLong); size_t offset = (submesh->useSharedVertices)? shared_offset : current_offset; size_t index_start = index_data->indexStart; size_t last_index = numTris*3 + index_start; if (use32bitindexes){ for( size_t k = index_start; k<last_index; ++k ) indices[index_offset++] = pLong[k] + static_cast<unsigned long>( offset ); }else{ for( size_t k=index_start; k<last_index; ++k ) indices[ index_offset++ ] = static_cast<unsigned long>( pShort[k] ) + static_cast<unsigned long>( offset ); } ibuf->unlock(); current_offset = next_offset; } } // Get the mesh information for the given mesh. // Code found in Wiki: www.ogre3d.org/wiki/index.php/RetrieveVertexData void OgreRay::GetMeshInformation( 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, size=mesh->getNumSubMeshes(); i<size; ++i ) { Ogre::SubMesh* submesh = mesh->getSubMesh( i ); // We only need to add the shared vertices once 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 data into the arrays for( unsigned short i=0, size=mesh->getNumSubMeshes(); i<size; ++i ) { Ogre::SubMesh* submesh = mesh->getSubMesh(i); Ogre::VertexData* vertex_data = 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_data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION); Ogre::HardwareVertexBufferSharedPtr vbuf = vertex_data->vertexBufferBinding->getBuffer(posElem->getSource()); unsigned char* vertex = static_cast<unsigned char*>(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY)); // 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_data->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_data->vertexCount; } Ogre::IndexData* index_data = submesh->indexData; size_t numTris = index_data->indexCount / 3; Ogre::HardwareIndexBufferSharedPtr ibuf = index_data->indexBuffer; if( ibuf.isNull() ) continue; // need to check if index buffer is valid (which will be not if the mesh doesn't have triangles like a pointcloud) bool use32bitindexes = (ibuf->getType() == Ogre::HardwareIndexBuffer::IT_32BIT); unsigned long* pLong = static_cast<unsigned long*>(ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY)); unsigned short* pShort = reinterpret_cast<unsigned short*>(pLong); size_t offset = (submesh->useSharedVertices)? shared_offset : current_offset; size_t index_start = index_data->indexStart; size_t last_index = numTris*3 + index_start; if( use32bitindexes ){ for( size_t k=index_start; k<last_index; ++k ) { indices[index_offset++] = pLong[k] + static_cast<unsigned long>( offset ); } }else{ for( size_t k=index_start; k<last_index; ++k ) { indices[ index_offset++ ] = static_cast<unsigned long>( pShort[k] ) + static_cast<unsigned long>( offset ); } } ibuf->unlock(); current_offset = next_offset; } } void OgreRay::GetMeshInformation( const Ogre::ManualObject* manual, 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 ) { std::vector<Ogre::Vector3> returnVertices; std::vector<unsigned long> returnIndices; unsigned long thisSectionStart = 0; for ( unsigned int i=0,size=manual->getNumSections(); i<size; ++i ) { Ogre::ManualObject::ManualObjectSection* section = manual->getSection(i); Ogre::RenderOperation* renderOp = section->getRenderOperation(); std::vector<Ogre::Vector3> pushVertices; //Collect the vertices { const Ogre::VertexElement* vertexElement = renderOp->vertexData->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION); Ogre::HardwareVertexBufferSharedPtr vertexBuffer = renderOp->vertexData->vertexBufferBinding->getBuffer(vertexElement->getSource()); char* verticesBuffer = static_cast<char*>(vertexBuffer->lock(Ogre::HardwareBuffer::HBL_READ_ONLY)); float* positionArrayHolder; thisSectionStart = returnVertices.size() + pushVertices.size(); pushVertices.reserve(renderOp->vertexData->vertexCount); for( unsigned int j=0; j<renderOp->vertexData->vertexCount; ++j ) { vertexElement->baseVertexPointerToElement(verticesBuffer + j * vertexBuffer->getVertexSize(), &positionArrayHolder); Ogre::Vector3 vertexPos = Ogre::Vector3(positionArrayHolder[0],positionArrayHolder[1],positionArrayHolder[2]); vertexPos = (orient * (vertexPos * scale)) + position; pushVertices.push_back(vertexPos); } vertexBuffer->unlock(); } //Collect the indices { if( renderOp->useIndexes ){ Ogre::HardwareIndexBufferSharedPtr indexBuffer = renderOp->indexData->indexBuffer; if( indexBuffer.isNull() || renderOp->operationType != Ogre::RenderOperation::OT_TRIANGLE_LIST ){ //No triangles here, so we just drop the collected vertices and move along to the next section. continue; }else{ returnVertices.reserve(returnVertices.size() + pushVertices.size()); returnVertices.insert(returnVertices.end(), pushVertices.begin(), pushVertices.end()); } unsigned int* pLong = static_cast<unsigned int*>(indexBuffer->lock(Ogre::HardwareBuffer::HBL_READ_ONLY)); unsigned short* pShort = reinterpret_cast<unsigned short*>(pLong); returnIndices.reserve(returnIndices.size() + renderOp->indexData->indexCount); for( size_t j=0; j<renderOp->indexData->indexCount; ++j ) { unsigned long index; //We also have got to remember that for a multi section object, each section has //different vertices, so the indices will not be correct. To correct this, we //have to add the position of the first vertex in this section to the index //(At least I think so...) if( indexBuffer->getType() == Ogre::HardwareIndexBuffer::IT_32BIT ) index = static_cast<unsigned long>(pLong[j]) + thisSectionStart; else index = static_cast<unsigned long>(pShort[j]) + thisSectionStart; returnIndices.push_back(index); } indexBuffer->unlock(); } } } //Now we simply return the data. index_count = returnIndices.size(); vertex_count = returnVertices.size(); vertices = new Ogre::Vector3[vertex_count]; for( unsigned long i = 0; i<vertex_count; ++i ) vertices[i] = returnVertices[i]; indices = new unsigned long[index_count]; for( unsigned long i = 0; i<index_count; ++i ) indices[i] = returnIndices[i]; //All done. return; }

Alias: Raycasting_to_the_polygon_level