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.
[2.1] Terra - ray scene query
-
- Gold Sponsor
- Posts: 25
- Joined: Mon Oct 03, 2016 7:36 pm
- x 2
-
- Hobgoblin
- Posts: 583
- Joined: Mon Aug 06, 2007 12:53 pm
- Location: Saarland, Germany
- x 50
Re: [2.1] Terra - ray scene query
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.
Best Regards
Lax
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;
}
}
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