[2.1] execute scene query for manual mesh?

Problems building or running the engine, queries about how to use features etc.
Post Reply
rrlangly
Gnoblar
Posts: 19
Joined: Sat Jan 24, 2009 8:27 pm

[2.1] execute scene query for manual mesh?

Post by rrlangly »

In this link ... http://wiki.ogre3d.org/Raycasting+to+the+polygon+level ... there's the following code.

Code: Select all

    // 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);
        }
    }
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. It appears to be trying to hit an objects "bounding box", and I'm using 2.1 and noticed that SceneNode's do not have the showBoundingBox(true) function like in the 1.x stuff. And besides, the objects I'm trying to pick are manual objects, not the bounding box. Actually, I'm trying to select a single triangle on the face of my manual objects.

So I'm wondering if I'm going about this correctly. Can anyone comment on this?
rpgplayerrobin
Gnoll
Posts: 619
Joined: Wed Mar 18, 2009 3:03 am
x 353

Re: [2.1] execute scene query for manual mesh?

Post by rpgplayerrobin »

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.
Post Reply