bool GraphicalWorld::pickPoint(const Ray& ray, Vector3 &hitpoint, const WS::StringVector& excludeobjects, Real max_distance/* = 9999.0f*/, uint32 mask /*= 0xFFFFFFFF*/)
{
WS_ASSERT( msCore );
//const unsigned long mask = 0xFFFFFFFF;
Ogre::Vector3 pOrigin = VEC_WS2OGRE(ray.getOrigin());
Ogre::Vector3 pDirection = VEC_WS2OGRE(ray.getDirection());
Ogre::Ray ogreRay(pOrigin, pDirection);
if (!mpRaySceneQuery)
{
mpRaySceneQuery = msCore->getSceneMgr()->createRayQuery( ogreRay, mask );
}
else
{
mpRaySceneQuery->setRay(ogreRay );
mpRaySceneQuery->setQueryMask(mask);
}
mpRaySceneQuery->setSortByDistance(true);
if (mpRaySceneQuery->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 = mpRaySceneQuery->getLastResults();
Ogre::MovableObject* movable;
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;
}
movable = query_result[qr_idx].movable;
// 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) && movable->isVisible())
{
Ogre::Entity* pOgreEntity = static_cast<Ogre::Entity*>(movable);
EntityMap::const_iterator itFind = mEntityMap.find(pOgreEntity);
if (itFind == mEntityMap.end())
continue;
// get the entity to check
graphics::IEntity *pEntity = itFind->second;
const String& pname = pEntity->getPoolableObjectID();
bool foundinlist = false;
for(unsigned int lit = 0;lit < excludeobjects.size();lit++)
{
if(excludeobjects[lit] == pname)
{
foundinlist = true;
break;
}
}
if(foundinlist) continue;
//parentOrientation * (parentScale * mPosition)
std::pair<bool, Ogre::Real> hit = HitMesh(ogreRay, pOgreEntity);
// if it was a hit check if its the closest
if (!hit.first) continue;
bool new_closest_found = false;
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;
}
// 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 = ogreRay.getPoint(closest_distance);
}
}
}
// return the result
if (closest_distance != max_distance)
{
hitpoint = VEC_OGRE2WS(closest_result);
return true;
}
else
{
return false;
}
}
拾取Mesh
std::pair<bool, Real> GraphicalWorld::HitMesh(const Ogre::Ray& ray,Ogre::Entity *entity)
{
//parentOrientation * (parentScale * mPosition)
Ogre::Vector3 pPostion = entity->getParentNode()->_getDerivedPosition();
Ogre::Vector3 pScale = entity->getParentNode()->_getDerivedScale();
Ogre::Quaternion q = entity->getParentNode()->_getDerivedOrientation();
Ogre::Ray localRay(q.Inverse() * (ray.getOrigin() - pPostion) / pScale,(q.Inverse() * ray.getDirection() / pScale).normalisedCopy());
bool added_shared = false;
size_t current_offset = 0;
size_t shared_offset = 0;
size_t next_offset = 0;
size_t index_offset = 0;
std::pair<bool, Ogre::Real> hit(false, 0);
Ogre::MeshPtr mesh = entity->getMesh();
bool useSoftwareBlendingVertices = entity->hasSkeleton();
if (useSoftwareBlendingVertices)
{
entity->_updateAnimation();
}
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;
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;
}
}
//////////////////////////////////////////////////////////////////////////Vertex
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));
//////////////////////////////////////////////////////////////////////////Index
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;
float *pReal0, *pReal1, *pReal2;
Ogre::Vector3 pVector30, pVector31,pVector32;
if (use32bitindexes)
{
for (size_t k = index_start; k < last_index;)
{
posElem->baseVertexPointerToElement(vertex + (pLong[k] + static_cast<unsigned long>( offset )) * vbuf->getVertexSize(), &pReal0);
pVector30.x = pReal0[0];
pVector30.y = pReal0[1];
pVector30.z = pReal0[2];
posElem->baseVertexPointerToElement(vertex + (pLong[k + 1] + static_cast<unsigned long>( offset )) * vbuf->getVertexSize(), &pReal1);
pVector31.x = pReal1[0];
pVector31.y = pReal1[1];
pVector31.z = pReal1[2];
posElem->baseVertexPointerToElement(vertex + (pLong[k + 2] + static_cast<unsigned long>( offset )) * vbuf->getVertexSize(), &pReal2);
pVector32.x = pReal2[0];
pVector32.y = pReal2[1];
pVector32.z = pReal2[2];
hit = Ogre::Math::intersects(localRay, pVector30,pVector31,pVector32, true, false);
if (hit.first)
{
//parentOrientation * (parentScale * mPosition);
hit = Ogre::Math::intersects(ray, q *(pVector30 * pScale) + pPostion ,q *(pVector31 * pScale) + pPostion, q *(pVector32 * pScale) + pPostion, true, false);
if (hit.first) break;
}
//
k+=3;
}
}
else
{
for (size_t k = index_start; k < last_index;)
{
posElem->baseVertexPointerToElement(vertex + (static_cast<unsigned long>( pShort[k] ) + static_cast<unsigned long>( offset )) * vbuf->getVertexSize(), &pReal0);
pVector30.x = pReal0[0];
pVector30.y = pReal0[1];
pVector30.z = pReal0[2];
posElem->baseVertexPointerToElement(vertex + (static_cast<unsigned long>( pShort[k + 1] ) + static_cast<unsigned long>( offset )) * vbuf->getVertexSize(), &pReal1);
pVector31.x = pReal1[0];
pVector31.y = pReal1[1];
pVector31.z = pReal1[2];
posElem->baseVertexPointerToElement(vertex + (static_cast<unsigned long>( pShort[k + 2] ) + static_cast<unsigned long>( offset )) * vbuf->getVertexSize(), &pReal2);
pVector32.x = pReal2[0];
pVector32.y = pReal2[1];
pVector32.z = pReal2[2];
hit = Ogre::Math::intersects(localRay, pVector30,pVector31,pVector32, true, false);
if (hit.first)
{
hit = Ogre::Math::intersects(ray, q *(pVector30 * pScale) + pPostion ,q *(pVector31 * pScale) + pPostion, q *(pVector32 * pScale) + pPostion, true, false);
if (hit.first) break;
}
k+=3;
}
}
ibuf->unlock();
vbuf->unlock();
}
return hit;
}