History: Raycasting to the polygon level - Mogre
Source of version: 15 (current)
Copy to clipboard
This is a Mogre version of ((Raycasting to the polygon level)) as ported by Funguine (ketarax on #ogre3d) & Antont.
Here is the [http://www.ogre3d.org/phpBB2/viewtopic.php?t=23440|forum thread] of the original code (C++).
!!Introducion
An Ogre ray query generally only detect bounding boxes (((-AABB))s), like you see on this image. With this code you can avoid this disadvantage.
{img src="img/wiki_up/Rays_and_BoundingBox_1a.gif" alt="Rays_and_BoundingBox_1a.gif"}
!!Important note
This code only returns correct results for polygons defined by rendertype ((ManualObject|#Rendering_Types|OT_TRIANGLE_LIST)). If a mesh contains other rendertypes (different triangle definition), lines or points, you will get wrong results or even a crash. Terrain and ManualObject aren't supported. Also it's not recommend for heavy usage (multiple queries per second). For details [http://www.ogre3d.org/forums/viewtopic.php?f=1&t=67355|look here].
!!An improved alternative
Because of disadvantages with the code on this wiki page, user ((User:Beauty|Beauty)) wrotes a new polygon raycasting class from scratch.
In general the code is ready to use. Only some helper classes (e.g. for debugging) are added quick and dirty to a second code file. In the future Beauty wants to polish up the code and publish it in a common way later (not only as attachment of a forum post).
The (alternative) polygon raycasting code you find [http://www.ogre3d.org/forums/viewtopic.php?p=446083#p446083|here] in the forum. Notes about you find [http://www.ogre3d.org/forums/viewtopic.php?p=448551#p448551|here].
For questions about Beautys code, please us the same forum topic.
Note: The code on this wiki page (below this section) is the old and "primitive" polygon raycasting version.
!!Code for initialization
Define this field (variable) in your class
{CODE(wrap="1", colors="c++")}RaySceneQuery m_pray_scene_query;{CODE}
Initialize it, for example in CreateScene()
{CODE(wrap="1", colors="c++")}
// create the ray scene query object
m_pray_scene_query = sceneMgr.CreateRayQuery(new Ray(), SceneManager.WORLD_GEOMETRY_TYPE_MASK);
if (null == m_pray_scene_query)
{
return false;
}
m_pray_scene_query.SetSortByDistance(true);{CODE}
!!Method for raycast
{CODE(wrap="1", colors="c++")}// raycast from a point in to the scene.
// returns success or failure.
// on success the point is returned in the result.
public bool RaycastFromPoint(Vector3 point, Vector3 normal, ref Vector3 result,
ref Vector3 resNormal)
{
// create the ray to test
Ray ray = new Ray(point, normal);
// check we are initialised
if (m_pray_scene_query != null)
{
// create a query object
m_pray_scene_query.Ray = ray;
// execute the query, returns a vector of hits
RaySceneQueryResult rayresult = m_pray_scene_query.Execute();
if (rayresult.Count <= 0)
{
// raycast did not hit an objects bounding box
return false;
}
}
else
{
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.
float closest_distance = -1.0f;
Vector3 closest_result = Vector3.ZERO;
Vector3 vNormal = Vector3.ZERO;
RaySceneQueryResult query_result = m_pray_scene_query.GetLastResults();
foreach (RaySceneQueryResultEntry this_result in query_result)
{
// stop checking if we have found a raycast hit that is closer
// than all remaining entities
if ((closest_distance >= 0.0f) &&
(closest_distance < this_result.distance))
{
break;
}
// only check this result if its a hit against an entity
if ((this_result.movable != null) &&
(this_result.movable.MovableType == "Entity"))
{
// get the entity to check
Entity pentity = (Entity)this_result.movable;
// mesh data to retrieve
uint vertex_count = 0;
uint index_count = 0;
Vector3[] vertices = new Vector3[0];
UInt64[] indices = new UInt64[0];
// get the mesh information
GetMeshInformation(pentity.GetMesh(),
ref vertex_count, ref vertices, ref index_count, ref indices,
pentity.ParentNode._getDerivedPosition(), // WorldPosition
pentity.ParentNode._getDerivedOrientation(), // WorldOrientation
pentity.ParentNode.GetScale());
int ncf = -1; // new_closest_found
// test for hitting individual triangles on the mesh
for (int i = 0; i < (int)index_count; i += 3)
{
// check for a hit against this triangle
Pair<bool, float> hit = Mogre.Math.Intersects(ray, 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 ((closest_distance < 0.0f) ||
(hit.second < closest_distance))
{
// this is the closest so far, save it off
closest_distance = hit.second;
ncf = i;
}
}
}
if (ncf > -1)
{
closest_result = ray.GetPoint(closest_distance);
// if you don't need the normal, comment this out; you'll save some CPU cycles.
Vector3 v1 = vertices[indices[ncf]] - vertices[indices[ncf + 1]];
Vector3 v2 = vertices[indices[ncf + 2]] - vertices[indices[ncf + 1]];
vNormal = v1.CrossProduct(v2);
}
// free the verticies and indicies memory
vertices = null;
indices = null;
}
}
// if we found a new closest raycast for this object, update the
// closest_result before moving on to the next object.
if (closest_distance >= 0.0f)
{
result = new Vector3(closest_result.x, closest_result.y, closest_result.z);
resNormal = vNormal / vNormal.Normalise();
/*
// this visualizes the 'result' position
if (!sceneMgr.HasSceneNode("marker"))
{
SceneNode node = sceneMgr.CreateSceneNode("marker");
Entity ent = sceneMgr.CreateEntity("marker", "Cube.mesh");
node.AttachObject(ent);
node.Position = result;
node.Scale(0.25f, 0.25f, 0.25f);
sceneMgr.RootSceneNode.AddChild(node);
}
else
{
sceneMgr.GetSceneNode("marker").Position = result;
}
*/
// raycast success
return true;
}
else
{
// raycast failed
return false;
}
} // RayCastFromPoint{CODE}
!!GetMeshInformation
This code is a port from ((RetrieveVertexData)).
There are ((Raycasting to the polygon level|#Adapted_version|two alternative versions)) of ''GetMeshInformation()''. One takes into account the animation state of an entity and the other one solves compiling errors for 64bit using GCC.
{CODE(wrap="1", colors="c++")}// Get the mesh information for the given mesh.
// Code found in Wiki: www.ogre3d.org/wiki/index.php/RetrieveVertexData
public unsafe void GetMeshInformation(MeshPtr mesh,
ref uint vertex_count,
ref Vector3[] vertices,
ref uint index_count,
ref UInt64[] indices,
Vector3 position,
Quaternion orientation,
Vector3 scale)
{
bool added_shared = false;
uint current_offset = 0;
uint shared_offset = 0;
uint next_offset = 0;
uint index_offset = 0;
vertex_count = index_count = 0;
// Calculate how many vertices and indices we're going to need
for (ushort i = 0; i < mesh.NumSubMeshes; ++i)
{
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 Vector3[vertex_count];
indices = new UInt64[index_count];
added_shared = false;
// Run through the submeshes again, adding the data into the arrays
for (ushort i = 0; i < mesh.NumSubMeshes; ++i)
{
SubMesh submesh = mesh.GetSubMesh(i);
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;
}
VertexElement posElem =
vertex_data.vertexDeclaration.FindElementBySemantic(VertexElementSemantic.VES_POSITION);
HardwareVertexBufferSharedPtr vbuf =
vertex_data.vertexBufferBinding.GetBuffer(posElem.Source);
byte* vertex = (byte*)vbuf.Lock(HardwareBuffer.LockOptions.HBL_READ_ONLY);
float* pReal;
// 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;
for (int j = 0; j < vertex_data.vertexCount; ++j, vertex += vbuf.VertexSize)
{
posElem.BaseVertexPointerToElement(vertex, &pReal);
Vector3 pt = new Vector3(pReal[0], pReal[1], pReal[2]);
vertices[current_offset + j] = (orientation * (pt * scale)) + position;
}
// |!| Important: VertexBuffer Unlock() + Dispose() avoids memory corruption
vbuf.Unlock();
vbuf.Dispose();
next_offset += vertex_data.vertexCount;
}
IndexData index_data = submesh.indexData;
uint numTris = index_data.indexCount / 3;
HardwareIndexBufferSharedPtr ibuf = index_data.indexBuffer;
// UNPORTED line of C++ code (because ibuf.IsNull() doesn't exist in C#)
// if( ibuf.isNull() ) 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.Type == HardwareIndexBuffer.IndexType.IT_32BIT);
uint* pLong = (uint*)ibuf.Lock(HardwareBuffer.LockOptions.HBL_READ_ONLY);
ushort* pShort = (ushort*)pLong;
uint offset = submesh.useSharedVertices ? shared_offset : current_offset;
if (use32bitindexes)
{
for (int k = 0; k < index_data.indexCount; ++k)
{
indices[index_offset++] = (UInt64)pLong[k] + (UInt64)offset;
}
}
else
{
for (int k = 0; k < index_data.indexCount; ++k)
{
indices[index_offset++] = (UInt64)pShort[k] + (UInt64)offset;
}
}
// |!| Important: IndexBuffer Unlock() + Dispose() avoids memory corruption
ibuf.Unlock();
ibuf.Dispose();
current_offset = next_offset;
}
// |!| Important: MeshPtr Dispose() avoids memory corruption
mesh.Dispose(); // This dispose the MeshPtr, not the Mesh
} // GetMeshInformation {CODE}
---
Alias: (alias(Raycasting_to_the_polygon_level_-_Mogre))
Alias: (alias(Raycasting_to_the_polygon_level_%28Mogre%29))