Skip to main content

History: Ray query with MOGRE

Preview of version: 9

The Ray Query class members in Mogre looks a little bit different to the Ogre API. Don't be confused by that. Mogre offers the same functionality.

An example for ray usage:
A character should walk on terrain. So you need to clamp it to the terrain surface.
After each movement, you perform a ray query straight downwards and read out the distance to the terrain. As result you know how to change the heigth value of the characters position.

Notes:

  • Only the bounding boxes (AABB) will be hit, not the object itself (see picture).
    • If you need distances on polygon level, look here.
  • Unvisible objects can be hit, too.
  • At least one frame must be rendered to get a result.
  • For rays starting from (or pointing to) a SceneNode you should call myNode._getDerivedPosition() and myNode._getDerivedOrientation() to get the wanted values for Ray.Origin and Ray.Direction.
  • If you use the Terrain Scene Manager (outdated, removed in Ogre 1.8), be aware that the distance is not very precise.
    • The returned distance is too long (up to 1 Ogre length unit). A demonstration screenshot is here.
    • An easy to use workaround for precise terrain distances was published here.
    • I repeat again: This issue is only related to the Terrain Scene Manager (and forks). The new Ogre Terrain Component works well by default.


Rays and BoundingBox 1a.gif

Code snippet

Copy to clipboard
//-- set params -- Vector3 startPosition = new Vector3(0, 0, 0); Vector3 rayDirection = new Vector3(0, -1, 0); // here: ray into depth // OR use position and orientation of a SceneNode //SceneNode someNode = mScene.Smgr.GetSceneNode("name_of_node"); //Vector3 startPosition = someNode._getDerivedPosition; //Vector3 rayDirection = someNode._getDerivedOrientation.XAxis; // in this case: base quaternion direction is along XAxis Ray myRay = new Ray(startPosition, rayDirection); RaySceneQuery raySQuery = mScene.Smgr.CreateRayQuery(myRay); // optionally sort the results raySQuery.SetSortByDistance(true); // second possible parameter: maxResults

Copy to clipboard
//-- calculate -- Vector3 rayHitPoint; Single rayDistance; String rayHitPointName = ""; RaySceneQueryResult raySQResult = raySQuery.Execute(); RaySceneQueryResultEntry raySQREntry; for (Int16 i = 0; i < raySQResult.Count; i++) // for all hits { raySQREntry = raySQResult[i]; rayDistance = raySQREntry.distance; rayHitPoint = myRay.Origin + (myRay.Direction * rayDistance); // calculate hit point // save names of objects (or world fragment) that was hit if (raySQREntry.worldFragment != null) rayHitPointName = raySQREntry.worldFragment.ToString(); if (raySQREntry.movable != null) rayHitPointName = raySQREntry.movable.Name; // Also bounding boxes of terrains can be hit. // Skip them to prevent hits on strange places ("just in the air"). if (rayHitPointName.StartsWith("tile[")) // e.g. "tile[1][1,1]" continue; // ... do something with the hit ... if (raySQREntry.worldFragment != null) { // ... only do something with hits to terrain } if (raySQREntry.movable != null) { // ... only do something with hits to other objects } // If you just want to find a special hit, you can leave the for() loop. // The results will be stored in the variables (rayHitPoint, raySQREntry etc.) if (this_is_your_hit) break; } // for

Copy to clipboard
//-- if nothing was hit -- if (raySQResult.IsEmpty) { // ... do something ... // e.g. prevent null values rayHitPoint = Vector3.ZERO; rayDistance = 0; rayHitPointName = "no_hitPoint"; }

Query flags

Query flags are a simple way to create a filter for ray queries.
For this you can define "masks" (flags) to several scene objects (e.g. for enemies, walls, etc.) and for your ray queries you tell Ogre, which objects you want to get or ignore.


Here is a pseudo code example:

Copy to clipboard
(put this enum in a shared class) [FlagsAttribute] // optional, but it makes clear, that this is a flag enum. enum QueryMasks : uint //Depends on your needs, but I need a lot of flags and bit shifting needs big numbers (int = 32bit = 32 flags, byte = 8 bit = 8 flags, ...). { Ground = 1<<0, //bit shift required (2,4,8,16,32,64,128,256,...) Wall = 1<<1 [...] } (somewhere in your code, e.g. in CreateScene()) Entity Groundent = (...); Groundent.QueryFlags = QueryMask.Ground; //assign a custom query flag (Somewhere in FrameStarted or similar) static CollisionTools ct = new CollisionTools(MySceneMgr); //C# does not support static variables in methods (VB.Net does ;) ), make a global var instead var Result = ct.RaycastFromCamera(RenderWindow, Camera, ScreenPosition, QueryMask.Ground); //search for a specific query flag if (Result != null) { //success, this has to be an entity with the Mask "Ground". }


For more details about query flags read the Intermediate Tutorial 3.

 Alternatives
There are of course helper classes to make raycasting easier.

Here are some:
MMOC (C# port of MOC) (used in example above)
Beauty's class (Preview release)
Both support ray casting to polygon level (very accurate but slow)
Of course, you could just query all AABBs: RaySceneQuery API
That is of course the fastest method but very inaccurate.

 Performance
I never did speed tests but all raycasting to polygone level are brute force attacks. All mentioned classes just iterate through all triangles of the meshes returned by a RayQuery. That's not soooo slow but just use this technique wise: Do you really need to do 20 raycasts to poly level (per frame)? Maybe just do it once and cache the result. Or prefilter the objects (that may need so tweaking of the helper classes), e.g. store all objects in a certain distance to your player and skip the rest.

An real alternative could be the "Selection buffer" (only c++): It colors all visible meshes in the scene for one frame in a (random but unique) color and returns the mesh at a specific screen cooridinate. This method (of course) only works for raycasting from the camera...

See also

History

Information Version
Sat 04 of Aug, 2012 14:04 GMT-0000 Tubulii Added alternative. 10
Wed 18 of Jul, 2012 10:53 GMT-0000 Tubulii Extended 'Query flags' section with additional code and comments. 9
Tue 17 of Jul, 2012 23:20 GMT-0000 Beauty Improvements for my text, which I wrote some years ago 8
Tue 17 of Jul, 2012 22:51 GMT-0000 Beauty added section "Query flags" 7
Thu 29 of Jul, 2010 09:42 GMT-0000 Beauty corrected links 6
Thu 29 of Jul, 2010 09:40 GMT-0000 Beauty added note for WorldPosition, WorldOrientation ... unfortunately this properties were removed from Ogre /-: 5
Wed 30 of Dec, 2009 07:38 GMT-0000 jacmoe 4
Sun 27 of Dec, 2009 19:04 GMT-0000 jacmoe 3
Sun 27 of Dec, 2009 19:03 GMT-0000 jacmoe 2
Sun 01 of Mar, 2009 16:15 GMT-0000 Smiley80 1