[2.1] Terra - ray scene query

Problems building or running the engine, queries about how to use features etc.
Post Reply
fenriss
Gold Sponsor
Gold Sponsor
Posts: 25
Joined: Mon Oct 03, 2016 7:36 pm
x 2

[2.1] Terra - ray scene query

Post by fenriss »

I think at the moment it's not possible to execute a ray scene query or an equivalent with the new 2.1pso Terra so i wanted to ask if this is true or if i have overlooked something.
Finding some statements in the old terrain howto and while thinking about it i believe it's not the easiest thing to implement. However, i wanted to ask if something is planned, if existing shader code could be re/abused or generally if someone can point me into a direction on how to best approach it before i try tinkering myself.

Edit: I modified https://github.com/jeremynewlin/Accel a little and can successfully use it to calc the query on the cpu. Although it's enough for tinkering around, a gpu implementation would be really nice but i don't know if this is easily possible with Ogre.
Lax
Hobgoblin
Posts: 583
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 50

Re: [2.1] Terra - ray scene query

Post by Lax »

Hi,

I wrote my own ray cast function, it works but not perfectly. Here is the code. I would be happy for any improvements.
You can adapt it, as it checks for different types and for gizmo.

Code: Select all

bool getRaycastFromPoint(Ogre::RaySceneQuery* raySceneQuery, Ogre::Camera* camera, Ogre::Vector3& resultPositionOnModel, unsigned long& targetEntity, float& closestDistance,
	Ogre::Vector3& normalOnModel, std::vector<Ogre::v1::Entity*>* excludeEntities, bool forGizmo)
{
	bool foundGizmo = false;
	targetEntity = 0;
	// check we are initialised
	if (raySceneQuery != nullptr)
	{
		if (true == forGizmo)
		{
			raySceneQuery->setSortByDistance(false);
		}
		// execute the query, returns a vector of hits
		if (raySceneQuery->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::Polygon polygon;

	closestDistance = -1.0f;
	Ogre::Vector3 closestResult;
	Ogre::RaySceneQueryResult& queryResult = raySceneQuery->getLastResults();
	for (size_t qrIdx = 0; qrIdx < queryResult.size(); qrIdx++)
	{
		// stop checking if we have found a raycast hit that is closer
		// than all remaining entities
		if ((closestDistance >= 0.0f) && (closestDistance < queryResult[qrIdx].distance) && false == forGizmo)
		{
			break;
		}

		// only check this result if its a hit against an entity
		Ogre::String type = queryResult[qrIdx].movable->getMovableType();

		bool foundType = false;

		if ((queryResult[qrIdx].movable != nullptr)/* && (type.compare("Entity") == 0 || type.compare("Terra") == 0)*/)
		{
			bool newClosestFound = false;

			// get the entity to check
			Ogre::v1::Entity* entity = dynamic_cast<Ogre::v1::Entity*>(queryResult[qrIdx].movable);
			if (nullptr != entity)
			{
				foundType = true;
				// If its the exclude entity, continue the loop with a different ones
				bool foundExcludedOne = false;
				Ogre::String entityName = entity->getName();
				if (true == forGizmo)
				{
					// Only check for gizmo, no matter how far away it is, or if there are some objects covering the gizmo!
					if ("XArrowGizmoEntity" != entityName && "YArrowGizmoEntity" != entityName && "ZArrowGizmoEntity" != entityName && "SphereGizmoEntity" != entityName)
						continue;
					else
						foundGizmo = true;
				}

				if (nullptr != excludeEntities)
				{
					for (size_t i = 0; i < excludeEntities->size(); i++)
					{
						if (entity == excludeEntities->at(i))
						{
							foundExcludedOne = true;
							continue;
						}
					}

					if (true == foundExcludedOne)
					{
						continue;
					}
				}

				// mesh data to retrieve
				size_t vertexCount;
				size_t indexCount;
				Ogre::Vector3 *vertices = nullptr;
				unsigned long *indices = nullptr;

				// get the mesh information
				this->getMeshInformation(entity->getMesh(), vertexCount, vertices,
					indexCount, indices, entity->getParentNode()->_getDerivedPositionUpdated(),
					entity->getParentNode()->_getDerivedOrientationUpdated(),
					entity->getParentNode()->getScale());

				// test for hitting individual triangles on the mesh
				for (int i = 0; i < static_cast<int> (indexCount); i += 3)
				{
					// check for a hit against this triangle
					std::pair<bool, Ogre::Real> hit = Ogre::Math::intersects(raySceneQuery->getRay(),
						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 ((closestDistance < 0.0f) || (hit.second < closestDistance) || forGizmo)
						{
							// this is the closest so far, save it off
							closestDistance = hit.second;
							newClosestFound = true;

							// Gizmo found, do not check further
							if (true == foundGizmo)
								break;

							// Get the normal from the vertices
							Ogre::Vector3 v1 = vertices[indices[i]] - vertices[indices[i + 1]];
							Ogre::Vector3 v2 = vertices[indices[i + 2]] - vertices[indices[i + 1]];
							normalOnModel = v1.crossProduct(v2);
							normalOnModel.normalise();
						}
					}
				}

				// free the verticies and indicies memory
				OGRE_FREE(vertices, Ogre::MEMCATEGORY_GEOMETRY);
				OGRE_FREE(indices, Ogre::MEMCATEGORY_GEOMETRY);

				// if we found a new closest raycast for this object, update the
				// closestResult before moving on to the next object.
				if (newClosestFound)
				{
					targetEntity = (unsigned long)entity;
					closestResult = raySceneQuery->getRay().getPoint(closestDistance);
					// Gizmo found, do not check further
					if (true == foundGizmo)
						return true;
				}
			}
			
			if (false == foundType)
			{
				// Terra
				Ogre::Terra* terra = dynamic_cast<Ogre::Terra*>(queryResult[qrIdx].movable);
				if (nullptr != terra)
				{
					foundType = true;
					// - 0 later position of terra?

					Ogre::Vector3 direction = camera->getDerivedPosition() - raySceneQuery->getRay().getOrigin();
					bool res = terra->getHeightAt(direction);

					Ogre::Real distance = (direction).length();

					Ogre::Vector3 pos1(raySceneQuery->getRay().getPoint(distance));
					res = terra->getHeightAt(pos1);
					targetEntity = (unsigned long)terra;

					Ogre::Vector3 pos2(raySceneQuery->getRay().getPoint(distance));
					pos2 += Ogre::Vector3(0.1f, 0.0f, 0.0f);
					res = terra->getHeightAt(pos2);

					Ogre::Vector3 pos3(raySceneQuery->getRay().getPoint(distance));
					pos3 += Ogre::Vector3(0.0f, 0.0f, 0.1f);
					res = terra->getHeightAt(pos3);

					Ogre::Plane plane = Ogre::Plane(pos1, pos2, pos3);

					// check for a hit against this triangle
					std::pair<bool, Ogre::Real> hit = Ogre::Math::intersects(raySceneQuery->getRay(), plane);

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

							closestResult = raySceneQuery->getRay().getPoint(closestDistance);
							pos1 = closestResult;
							pos2 = closestResult;
							pos3 = closestResult;
							Ogre::Vector3 validPos = Ogre::Vector3::ZERO;
							bool res = terra->getHeightAt(closestResult);

							res = terra->getHeightAt(pos1);
							
							pos2 += Ogre::Vector3(0.1f, 0.0f, 0.0f);
							res = terra->getHeightAt(pos2);
							
							pos3 += Ogre::Vector3(0.0f, 0.0f, 0.1f);
							res = terra->getHeightAt(pos3);

							// Calculate the normal
							Ogre::Vector3 v1 = pos1 - pos2;
							Ogre::Vector3 v2 = pos3 - pos2;
							normalOnModel = v1.crossProduct(v2) * -1.0f;
							normalOnModel.normalise();
						}
					}
				}
			}
			
			if (false == foundType)
			{
				// ManualObject
				Ogre::ManualObject* manualObject = dynamic_cast<Ogre::ManualObject*>(queryResult[qrIdx].movable);
				if (nullptr != manualObject)
				{
					foundType = true;

					// mesh data to retrieve
					size_t vertexCount;
					size_t indexCount;
					Ogre::Vector3 *vertices = nullptr;
					unsigned long *indices = nullptr;

					// get the manual mesh information
					this->getManualMeshInformation2(manualObject, vertexCount, vertices,
						indexCount, indices, manualObject->getParentNode()->_getDerivedPositionUpdated(),
						manualObject->getParentNode()->_getDerivedOrientationUpdated(),
						manualObject->getParentNode()->getScale());

					// test for hitting individual triangles on the mesh
					for (int i = 0; i < static_cast<int> (indexCount); i += 3)
					{
						// check for a hit against this triangle
						std::pair<bool, Ogre::Real> hit = Ogre::Math::intersects(raySceneQuery->getRay(),
							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 ((closestDistance < 0.0f) || (hit.second < closestDistance))
							{
								// this is the closest so far, save it off
								closestDistance = hit.second;
								newClosestFound = true;

								// Get the normal from the vertices
								Ogre::Vector3 v1 = vertices[indices[i]] - vertices[indices[i + 1]];
								Ogre::Vector3 v2 = vertices[indices[i + 2]] - vertices[indices[i + 1]];
								normalOnModel = v1.crossProduct(v2);
								normalOnModel.normalise();
							}
						}
					}

					// free the verticies and indicies memory
					OGRE_FREE(vertices, Ogre::MEMCATEGORY_GEOMETRY);
					OGRE_FREE(indices, Ogre::MEMCATEGORY_GEOMETRY);

					// if we found a new closest raycast for this object, update the
					// closestResult before moving on to the next object.
					if (newClosestFound)
					{
						targetEntity = (unsigned long)manualObject;
						closestResult = raySceneQuery->getRay().getPoint(closestDistance);
					}
				}
			}
		}
	}

	// return the result
	if (closestDistance >= 0.0f)
	{
		// raycast success
		resultPositionOnModel = closestResult;
		return true;
	}
	else
	{
		// raycast failed
		return false;
	}
}
Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

Post Reply