Mogre RayCast problems

dannypb

04-11-2012 02:10:26

hi :)

Still working on my space shooter. I'm trying to get the nearest object in front of the ship I'm flying, and I'm having all kinds of frustration. I'm am hoping that someone can help.

1. The world is made up of a number of entities, such as the planet, space stations, jumpgates, ect. All of these entities are set up as static geometry.
2. There are also other player ships in the sector.
3. The code to check for the nearest object in front is called from the frame listener.


Mogre.Vector3 tempVector = new Mogre.Vector3(0, 0, -1000);
gCamNode.Translate(tempVector, Mogre.Node.TransformSpace.TS_LOCAL);
Mogre.Vector3 chkVector = gCamNode.Position;
tempVector = new Mogre.Vector3(0, 0, 1000);
gCamNode.Translate(tempVector, Mogre.Node.TransformSpace.TS_LOCAL);
frontDistance = prRayCast(ref frontTarget, gCamNode.Position, chkVector);


4. And here is the function that checks for the nearest entity hit.


protected float prRayCast(ref string obName, Mogre.Vector3 origPos, Mogre.Vector3 normPos)
{
Mogre.Vector3 rayHitPoint;
Single rayDistance = 0.0f;
Single lowDistance = 0.0f;
string lowName = "";
String rayHitPointName = "";

Mogre.Vector3 rayDir = normPos - origPos;
rayDir.Normalise();
Ray myRay = new Ray(origPos, rayDir);
//tarNode.Position = normPos;
RaySceneQuery raySQuery = gSceneMgr.CreateRayQuery(myRay);
RaySceneQueryResult raySQResult = raySQuery.Execute();
RaySceneQueryResultEntry raySQREntry;

for (Int16 i = 0; i < raySQResult.Count; i++) // for all hits
{
raySQREntry = raySQResult;
rayDistance = raySQREntry.distance;
rayHitPoint = myRay.Origin + (myRay.Direction * rayDistance); // calculate hit point
rayHitPointName = "";

// 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").
// There is no terrain in this case however
if (rayHitPointName.StartsWith("tile[")) // e.g. "tile[1][1,1]"
continue;

if (!rayHitPointName.ToLower().Equals("camera"))
{
if (lowDistance == 0.0f)
lowDistance = rayDistance;
if (raySQREntry.worldFragment != null)
{
if (rayDistance < lowDistance)
{
lowDistance = rayDistance;
lowName = rayHitPointName.Substring(0,5);
}
}

if (raySQREntry.movable != null)
{
if (rayDistance < lowDistance)
{
lowDistance = rayDistance;
lowName = rayHitPointName.Substring(0, 5);
}
}
}

}

if (raySQResult.IsEmpty)
{
rayHitPoint = Mogre.Vector3.ZERO;
lowDistance = 0.0f;
lowName = "Empty";
}
gSceneMgr.DestroyQuery(raySQuery);
raySQuery.Dispose();
raySQuery = null;

obName = lowName;
return lowDistance;
}


The only hits I get on it are when I face the middle of the planet. If I line up on a ship or station or a player, I get no hits. Has anyone dealt with this and figured it out?

Thanks in advance, even if you couldn't help.

Dan

dannypb

04-11-2012 19:34:35

Additional Information:

This is really strange, it actually works, but only when I am facing the general direction of vector (0,0,0), which is where the planet is. Although it only seems to score ray cast hits in about half the diameter of the planet itself. So if i get between an entity, lets say a space station and the planet and point towards the station, I get no hit. But if I'm on the other side of the station facing the middle of the planet with the station in the way, i score a hit with the ray cast on the station. I can always get the hit when facing the middle of the station. I also get hits on any number of entities in the way of the station. So I guess I am missing some sort of thing here. I'm not sure what it is though.

dannypb

04-11-2012 20:31:50

Here is an example of what im talking about.

Here in image 1 you see I am pointing past what would be the bounding box of the space ship to the planet. It correctly show a hit on the planet as the closest hit. You can see this in the lower right hand box, the middle line.



In this next image you see I am facing the ship (the tank looking thing), with the planet behind. Btw the center point of the planet is 0,0,0. it correctly shows the ship as the closest hit.



in this third image I am still facing the ship, but not pointing to the 0,0,0 ish point of the middle of the zone. You can see that it does not register the hit.



In this 4th and last picture I have moved towards the planet and am now facing away from the planet towards the ship. It show not ray cast hit.

dannypb

05-11-2012 19:12:05

OK, I found the problem. In fact I found a reference to it here: http://www.ogre3d.org/forums/viewtopic.php?f=2&t=14638&start=0 .

It seems that ray queries only work within a specific area. It seems that the area is restricted to a 10,000 unit square by default. The way to redefine it for larger worlds is a bit different in Mogre, than the solution given in Ogre, so here it is:

I added this to the end of my createScene function.


Mogre.AxisAlignedBox box = new Mogre.AxisAlignedBox();
Mogre.Vector3 max = new Mogre.Vector3(-300000, -300000, -300000);
box.SetExtents(-max, max);
gSceneMgr.SetOption("Size", max);


The 2 changes from Ogre to Mogre. You need to assign negative values to the vector3 named max. I tried to simply place the negative on the second parameter in the setExtent method, but it didn't work. At this point I'll just use it the way it works.The name of the size option in Mogre is "Size". It is "size" in Ogre.

I hope this helps someone else out.

dannypb

05-11-2012 19:56:04

Here is the proof, The debug info is now in a debug window in the upper left of the screen. The 2nd line in that box is the truncated object name and it's raw distance. The 3rd line is the ray direction.

Beauty

17-11-2012 01:28:02

Nice to see some pictures of your game :D

I like to see that you used the code example, which I added to the Ogre wiki. (So my work was useful for somebody.)
And I'm happy to see that you found a solution and posted it.

For very large worlds Ogre offers a compiler flag for "double precision".
This means to use Double values instead of Single values for positions.
The downside: For usage with Mogre you need to change the flag and build and wrap Ogre/Mogre again.
Just to let you know. Maybe you need it one day.

dannypb

17-11-2012 14:36:21

Hi Beauty :)

Your code has been VERY useful to me. I've been a programmer all my life, but I'm fairly new to 3d programming. Everything seems to be working fine now. I did play around with the poly level collision, but it is so slow. I also messed around with various ways to slide off the target upon collision. The cheapest way I found was this crappy way :) :


Mogre.Vector3 tempVector = new Mogre.Vector3(0, 0, -100);
gCamNode.Translate(tempVector, Mogre.Node.TransformSpace.TS_LOCAL);
Mogre.Vector3 chkVector = gCamNode.Position;//new Mogre.Vector3(gCamNode.Position.x, gCamNode.Position.y, gCamNode.Position.z);
tempVector = new Mogre.Vector3(0, 0, 100);
gCamNode.Translate(tempVector, Mogre.Node.TransformSpace.TS_LOCAL);
tempVector = chkVector - gCamNode.Position;
tempVector.Normalise();
frontDistance = prRayCast(ref frontTarget, tempVector);
bool hitz = frontDistance < 40 && !frontTarget.Equals("");


then later


if (hitz)
{
Mogre.Quaternion origDir = gCamNode.Orientation;
gCamNode.LookAt(dirToCollideCenter, Node.TransformSpace.TS_WORLD);
gCamNode.Translate(new Mogre.Vector3(translateVector.x, translateVector.y, translateVector.z * -1), Node.TransformSpace.TS_LOCAL);
gCamNode.Orientation = origDir;
gCamNode.Translate(translateVector, Node.TransformSpace.TS_LOCAL);

}
else
{
gCamNode.Translate(translateVector, Mogre.Node.TransformSpace.TS_LOCAL);
}


So basically, if I hit something, I remember my orientation, I point to the center of the entity I collided with, reverse my translate vector, reset to the original orientation, then translate. It has the effect of sliding off of the object. It's not perfect, but cheap. I am playing with clamping the bounding box to the object, to reduce false hits. I really want this to run on any crappy machine, so fast is important. I tried to just add the reverse vector to the collision entity center with the vector I want to travel for the frame. It worked on paper, but I couldn't quite get it to work in code. This is basically the same thing, although I'm sure it's more expensive since I'm moving the camera twice.

I was thinking of some code that would detect polygon hits at a limited distance, and drop all hits once it hits the first. Maybe that would allow cheaper poly collision.

Beauty

17-11-2012 17:51:55

I did play around with the poly level collision, but it is so slow.
[...]
I was thinking of some code that would detect polygon hits at a limited distance, and drop all hits once it hits the first. Maybe that would allow cheaper poly collision.


Which code did you use for ray queries on polygon level?

The code of the Ogre wiki (here) isn't that good.
For that reason I wrote a new polygon ray query class from the scratch.
I published it here:
http://www.ogre3d.org/forums/viewtopic. ... 83#p446083
And some notes here:
http://www.ogre3d.org/forums/viewtopic. ... 51#p448551

My class offers 2 ways of queries.
The first way is the static method DoStaticRaycast() for queries by usage of only one line of code.
For the second way you create a class instance, can set properties and execute queries multiple times.
The class contains the property MaximumDistance, which you can use to disable the detection of objects, which are far away.

Additionally the code can be improved by a 2-step check. (maybe I already implemented it??)
When the ray query results are sorted by distance, then the AABB distances and sizes could be used for first filtering.
In the second step only a sub amount of the detected objects will be analysed on polygon level. In best case it's only 1 object.
This would be useful for your game, which has large distances between objects. And it seems to have less AABB overlapping.

dannypb

18-11-2012 00:45:57

Thanks again Beauty,

I had used the one from the wiki, and also MMOC. They are both slow. I'll check out your class.

Beauty

18-11-2012 01:46:12

Additionally the code can be improved by a 2-step check. (maybe I already implemented it??)
When the ray query results are sorted by distance, then the AABB distances and sizes could be used for first filtering.
In the second step only a sub amount of the detected objects will be analysed on polygon level. In best case it's only 1 object.

I looked into my code now.
There I saw that I already implemented a similar way of checking.

If an objects AABB is more far away than the (previously detected) closest distance then there is no need for further checking on polygon level.

I'll check out your class.
Please note, my code was a pre-release. A few lines of code has to be modified. (The compiler tell you the lines.)
If you have questions or problems, just ask.

I used it for detection of ManualObjects. Sometimes I got crashes, but never found the source of the problem. Even not by deep debugging inside of the Ogre sources. It was a very strange thing. Details I wrote here.
I hope the problems only happens with ManualObjects and detection of Meshes cause no problems.

When you try my code, I would be happy about feedback.

dannypb

18-11-2012 12:48:04

Thanks Beauty. I'm away from home on vacation for a week (in Massachusetts and New Hampshire in the US), so I may not be able to give you any feedback for a bit.

Also I was thinking of another possible optimization. The original problem I had was the default size of the space ray queries were active. From what I remember it's supposed to work within the bounding box of the terrain, or a -10k to 10k box. My space zones are very large, and have no terrain so they use the default. I wonder if I could clamp the ray query zone to the player ship? then I could reduce the area of a ray query and further optimize my code.

Beauty

18-11-2012 13:27:47

Don't worry, time doesn't care so much.

I wonder if I could clamp the ray query zone to the player ship?
I suppose the common ray query (AABB check) can't be reduced by distance, but it's an easy calculation, which doesn't need much time.
My improvement (check for hits on polygon level) can be limited to a maximum distance.
The distance is compared to the ray origin. In the case the origin is on your ship, then it should work as you expected.

Aside this the speed could be increased (very much??) by usage of GPU for triangle check calculations. Unfortunately I have no idea about GPU programming.

dannypb

27-11-2012 19:00:54

I added some code to the prRayCast code above...


gSceneMgr.DestroyQuery(raySQuery);
raySQuery.Dispose();
raySQuery = null;


was added to the end of the function. This closes a memory leak.

Also, I played around with your code Beauty, and it is very good. It is still slower than I want, so I think I will create a collision radius for entities. This way I can get a little closer to the mesh than the bounding box or bounding radius, but still run very fast. Maybe I'll implement an array of collision points and collision radii for each node. That way I can sort of create a primitive collision type for unusually shaped objects, and still do the check fast. This would be faster because I wouldn't have to get and use the mesh information at all. It's not a best case scenario, but it will work for what I want to do. Thanks for all your help :)

I may still use your code on the ground, or in stations, where more precision is needed. Plus in these scenarios I'm not dealing with a super large world, and ray queries run a lot smoother.

Beauty

01-12-2012 20:27:57

I added some code to the prRayCast code above...
Good idea!
(Sometimes I forget disposing, because often it's not needed in the C# world.)

Also, I played around with your code Beauty, and it is very good.
I'm happy to hear that.
When I find some time, I want to improve the "plain helper code", publish the code in a repository and write a tiny demo application. So it's more easy for others and also maintaining is better.

It is still slower than I want
First way of speed-up:
Use a shader application for the triangle-ray-checks and perform the calculations multithreaded on the GPU.
Benefits would be:
* No need to copy the vertex information from the graphic card to the RAM of the PC.
* Parallel calculations on GPU can be much more quickly.

Unfortunately I have no experience with GPU programming.


I also have an other idea. First I write about a feature of the Newton library:
When you create a ConvexHull of a 3D object (entity / ManualObject) then an algorithm (inside of the Newton library) calculates all vertices, which are needed to create the convex hull. So all inner vertices are removed. Additionally Newton offers to remove vertices with a similar position. By this method, complex objects can be simplified very much. The downside is a kind of imprecision.
The algorithm is somewhere in the published Newton source. You also can ask Julio Jerez (autor of Newton) in the Newton forum. He can tell you where to finde the code and maybe the "official name" of the algorithm for further searches in the web.
Back to RayCast:
You could embedd this algorithm to your game. So your game can create "simplified clones" of your space ships. By usage of Ogre BitMasks it's very easy to apply polygon ray detection only to the simplified clones.
The clones you include to the scene, too. Keep them invisible. So you don't see them and there is no lack of the FPS rate. The Ogre ray query will still detect them.
Just an idea ...

collision radius for entities
From a very old game development book I know that it was common to use multiple bounding boxes to define a simplified hull of objects. Intersection checks should be very quick, but it can get time consuming to define all the edge positions manually. ... or you try to create the boxes by a 3D modelling tool.