Raycasting to the polygon level (Mogre)
From Ogre Wiki
This is a Mogre version of Raycasting to the polygon level as ported by Funguine (ketarax on #ogre3d) & Antont.
Here is the forum thread of the original code (C++).
Contents |
Introducion
An Ogre ray query generally only detect bounding boxes (AABBs), like you see on this image. With this code you can avoid this disadvantage.
Code for initialization
// Initialize, for example in CreateScene()
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);
Method for raycast
// 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)
{
Ray ray = new Ray(point, normal);
if (null != m_pray_scene_query)
{
m_pray_scene_query.Ray = ray;
RaySceneQueryResult rayresult = m_pray_scene_query.Execute();
if (rayresult.Count <= 0)
{
return false;
}
}
else
{
return false;
}
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)
{
if ((closest_distance >= 0.0f) &&
(closest_distance < this_result.distance))
{
break;
}
if ((this_result.movable != null) &&
(this_result.movable.MovableType.CompareTo("Entity") == 0))
{
Entity pentity = (Entity)this_result.movable;
uint vertex_count = 0;
uint index_count = 0;
Vector3[] vertices = new Vector3[0];
UInt64[] indices = new UInt64[0];
GetMeshInformation(pentity.GetMesh(),
ref vertex_count, ref vertices, ref index_count, ref indices,
pentity.ParentNode.WorldPosition,
pentity.ParentNode.WorldOrientation,
pentity.ParentNode.GetScale());
int ncf = -1; // new_closest_found
for (int i = 0; i < (int)index_count; i += 3)
{
Pair<bool, float> hit = Mogre.Math.Intersects(ray, vertices[indices[i]],
vertices[indices[i + 1]], vertices[indices[i + 2]], true, false);
if (hit.first)
{
if ((closest_distance < 0.0f) ||
(hit.second < closest_distance))
{
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);
}
vertices = null;
indices = null;
}
}
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;
}
*/
return true;
}
else
{
return false;
}
} // RayCastFromPoint
GetMeshInformation
Code is from RetrieveVertexData (C++ ... from the first or the optimized version?)
// Get the mesh information for the given mesh.
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;
for (ushort i = 0; i < mesh.NumSubMeshes; ++i)
{
SubMesh submesh = mesh.GetSubMesh(i);
if (submesh.useSharedVertices)
{
if (!added_shared)
{
vertex_count += mesh.sharedVertexData.vertexCount;
added_shared = true;
}
}
else
{
vertex_count += submesh.vertexData.vertexCount;
}
index_count += submesh.indexData.indexCount;
}
vertices = new Vector3[vertex_count];
indices = new UInt64[index_count];
added_shared = false;
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;
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;
}
vbuf.Unlock();
next_offset += vertex_data.vertexCount;
}
IndexData index_data = submesh.indexData;
uint numTris = index_data.indexCount / 3;
HardwareIndexBufferSharedPtr ibuf = index_data.indexBuffer;
bool use32bitindexes = (ibuf.Type == HardwareIndexBuffer.IndexType.IT_32BIT);
ulong* pLong = (ulong*)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;
}
}
ibuf.Unlock();
current_offset = next_offset;
}
} // GetMeshInformation

