Where the code executes the query, I see the comment "raycast did not hit an objects bounding box" which causes me concern in why I may not be able to select anything.
The raycast is first checking against all objects bounding boxes to see if they can be viable targets. All objects have a bounding box even if the function to show them is removed (you can render them yourself if you make a manager for it though).
It seems the code you posted do this step first, and then check per triangle for both normal meshes and manual objects. So it should work, as manual objects also have bounding boxes (if they don't, make sure they do, there is a function called setBoundingBox for ManualObject).
If you however find that the raySceneQuery is not working for you, in my code I skip that completely and have made my own picker so I have 100% control over what happens.
I am using Ogre 1.12 but I think the same approach would be ok.
I hope my code below works, I copy pasted a lot since that class is a lot bigger than this for many other purposes.
This code sorts the position hit so the first possible hit will be returned instead of a hit of a triangle further away.
It also works with scale, orientation and position of the mesh correctly.
Why I use this code instead of a code in the Ogre source is because I have full control over everything this way. I only pick against objects I want to pick against, nothing else.
I never use "raySceneQuery" in my game, I handle all objects that can be picked against myself instead. First I do a simple AABB ray intersection and just get them into a list, then I do the below code for each object.
You could easily add specific pickings to this and have a manager that can handle all sorts of picking that Ogre could never do, you could probably also add manual object picking with this if you alter it a bit.
Using the code below should be something like:
Code: Select all
CMeshInformation info = new CMeshInformation(meshName);
int triangleIndex = -1; // You don't have to use this if you do not want the triangle index that was hit returned
Vector3 hitPos = info->GetTriangleIntersection(node, ray, &triangleIndex);
if(hitPos != INVALID_POSITION)
{
// Target was hit!
}
delete info;
INVALID_POSITION is defined as Vector3(10000, 10000, 10000) and can be checked for if it didn't hit, define it somewhere.
In my game I cache the CMeshInformation in a manager instead of having to recreate it for each raycast.
Code:
.h file:
Code: Select all
class CMeshInformation
{
public:
CMeshInformation(std::string meshName);
~CMeshInformation();
std::string m_meshName;
size_t m_vertex_count;
Ogre::Vector3* m_vertices;
size_t m_index_count;
unsigned long* m_indices;
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 Vector3 GetTriangleIntersection(SceneNode* node, Ray ray, Vector3* vertices, size_t index_count, unsigned long* indices, int* triangleIndex = NULL);
Vector3 GetTriangleIntersection(SceneNode* node, Ray ray, int* triangleIndex = NULL);
};
.cpp file:
Code: Select all
// Constructor for the CMeshInformation
CMeshInformation::CMeshInformation(std::string meshName)
{
// Get the mesh information
m_meshName = meshName;
MeshPtr tmpMeshPtr = MeshManager::getSingletonPtr()->getByName(meshName.m_string);
if( tmpMeshPtr )
GetMeshInformation(tmpMeshPtr, m_vertex_count, m_vertices, m_index_count, m_indices, Vector3::ZERO, Quaternion::IDENTITY, Vector3(1.0f, 1.0f, 1.0f));
else
CGeneric::Error("CNavMeshBase::CNavMeshBase::CMeshInformation, unable to find mesh: " + meshName.m_string);
}
// Destructor for the CMeshInformation
CMeshInformation::~CMeshInformation()
{
delete[] m_vertices;
delete[] m_indices;
}
// Get the mesh information for the given mesh.
// Code found in Wiki: www.ogre3d.org/wiki/index.php/RetrieveVertexData
void CMeshInformation::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 ) 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;
}
}
// Intersects with the mesh information with a ray
Vector3 CMeshInformation::GetTriangleIntersection(SceneNode* node, Ray ray, Vector3* vertices, size_t index_count,
unsigned long* indices, int* triangleIndex)
{
// Loop through all triangles
Quaternion tmpNodeOrientation = node->_getDerivedOrientation();
Vector3 tmpNodeScale = node->getScale();
Vector3 tmpNodePosition = node->_getDerivedPosition();
Vector3 tmpRet = INVALID_POSITION;
int i = 0;
float tmpBestDistance = FLT_MAX;
for(; i < static_cast<int>(index_count); i += 3)
{
// Calculate the current triangles vertexes from the node specified
Vector3 tmpVertex0 = vertices[indices[i]];
Vector3 tmpVertex1 = vertices[indices[i+1]];
Vector3 tmpVertex2 = vertices[indices[i+2]];
tmpVertex0 = (tmpNodeOrientation * (tmpVertex0 * tmpNodeScale)) + tmpNodePosition;
tmpVertex1 = (tmpNodeOrientation * (tmpVertex1 * tmpNodeScale)) + tmpNodePosition;
tmpVertex2 = (tmpNodeOrientation * (tmpVertex2 * tmpNodeScale)) + tmpNodePosition;
// Check if we intersected with the triangle
std::pair<bool, Ogre::Real> tmpIntersectionData = Ogre::Math::intersects(ray, tmpVertex0, tmpVertex1, tmpVertex2, true, true);
if( tmpIntersectionData.first )
{
// Check if this is the closest intersection so far
float tmpDistance = (ray.getPoint(tmpIntersectionData.second) - ray.getOrigin()).length();
if(tmpDistance < tmpBestDistance)
{
// Set the current intersection to be the best intersection
tmpBestDistance = tmpDistance;
tmpRet = ray.getPoint(tmpIntersectionData.second);
if(triangleIndex)
(*triangleIndex) = i;
}
}
}
// Return the intersection point (if any)
return tmpRet;
}
// Intersects with the mesh information with a ray
Vector3 CMeshInformation::GetTriangleIntersection(SceneNode* node, Ray ray, int* triangleIndex)
{
// Call and return the base
return GetTriangleIntersection(node, ray, m_vertices, m_index_count, m_indices, triangleIndex);
}
For a manual object, you could just use the code from the link you posted to get the data, then use that here as well.