Raycasting to the polygon level (Mogre)

From Ogre Wiki

Jump to: navigation, search

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. Image:Rays_and_BoundingBox_1a.gif


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 
Personal tools
administration