Getting Object Data From RayCasts

Discuss any questions about BEPUphysics or problems encountered.
Post Reply
Cheeser12
Posts: 8
Joined: Sat Sep 17, 2011 10:10 pm

Getting Object Data From RayCasts

Post by Cheeser12 »

Hi,

I was wondering if anyone could give me some information about how to use the HitObject gained from a RayCast. My code looks like this so far:

Code: Select all

space.RayCast(new Ray(character.Camera.Position + character.Camera.Direction * 10f, character.Camera.Direction), 10f, out result);
                if (result.HitObject != null)
                {
                    
                    
                }
So I know that I can use HitObject to get some information about the object that was hit by the ray. What I would like to know is how to actually access that object using HitObject. For example, I have a Door class which inherits from the Box class, and I would like to call the Door's Open function when it gets hit by the ray. However, I can't find any way to actually cast the HitObject to get to the Box class, let alone my class. Is there anyway I can use this data to convert the HitObject to the Door it hit? Or is there anyway to compare information so I can at least check to see whether the Door and HitObject correspond?

Also, as a sidenote: I'd like to store a string about the collision entity in its tag. I was able to get this to work for a static mesh, but in my Door class (which inherits from a
class I wrote for drawing a box which subsequently inherits from the Box class itself) the data doesn't seem to be getting through. Does anyone have any thoughts as to why this doesn't work?

EDIT: A second side question. Is the help file on the Bepu codeplex page broken? When I try to view anything in it the link is broken.

I would really appreciate any help here. :) I imagine there has to be some way to check which Door was hit, if it's not possible to access the Door itself.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Getting Object Data From RayCasts

Post by Norbo »

For example, I have a Door class which inherits from the Box class
By the way, it's generally best to structure game logic such that your game objects have physical representations (an Entity or some other types like StaticMesh) rather than have game objects that are physical objects. This helps avoids awkward coupling between rendering, game logic, and physics architecture.

That said, to use the hit object in a ray cast or collision event, it helps to understand the flow of ownership around Collidables and Entities.

-The Entity supports motion and can collide. If dynamic, it's 'normally physical' and will respond to collisions in the expected way. Kinematic entities effectively have infinite mass and inertia, so they do not respond to collisions, but their velocity can be changed manually. Dynamic entities that a kinematic entity runs into will be pushed out of the way or crushed; the kinematic will not slow down or change direction due to the collision. Two kinematics will simply go through each other.

-The Entity.CollisionInformation property returns an EntityCollidable that acts as the entity's collision proxy in the space. This object inherits from Collidable, which in turn inherits from BroadPhaseEntry. BroadPhaseEntry objects live in the collision detection pipeline starting in the BroadPhase, detecting collision pairs for further analysis. Collidable is the subclass of BroadPhaseEntry that is capable of generating contacts with other Collidables. Those contacts are the primary output of the narrow phase collision detection.

-There exist Collidables which are not EntityCollidables and so are not used by any Entity but can exist alone in the Space. These are the StaticMesh, InstancedMesh, and Terrain (and other custom types; it's extensible). These are completely incapable of continuous motion; they cannot have velocity. These are typically used for static level geometry. StaticMesh and its peers don't have a CollisionInformation property because they are the collision information themselves and have no other abilities.

So how is this relevant?

The space ray cast is tested against the BroadPhase's acceleration structure. That acceleration structure holds only references to BroadPhaseEntry objects. You can access the acceleration structure queries directly through the Space.BroadPhase.QueryAccelerator to see how it works. The Space.RayCast takes the list of BroadPhaseEntry objects found by the acceleration structure and performs a detailed ray cast against the individual objects. The logic for the detailed ray cast is defined by the BroadPhaseEntry object itself. That detailed ray cast provides the hit location, time, and normal. So, the result of a Space ray cast is a broad phase entry, the location, T, and normal (or a list of them).

There's a few ways you can get from the BroadPhaseEntry to the data you want.

In your case, where you have extended the Entity type, you'll need to try to cast the entry to EntityCollidable, since that's what Entities have. If the cast succeeds, you can use the EntityCollidable's Entity property to get the entity. You can then attempt to cast that Entity to a Door. If that succeeds, you can call its Open method.

Rather than doing casting around the physics object hierarchy, it's usually more convenient to make use of tags. The BroadPhaseEntry class has its own Tag property. The Entity class has its own separate Tag property. Setting an Entity.Tag does not set the Entity.CollisionInformation.Tag. Because the BroadPhaseEntry.Tag is visible directly from the ray cast result (or collision event), it can be convenient to store a game object in the tag to help manage logic.

For example, if you split up the architecture such that there was a clear separation between physics, rendering, and game logic with some game object that is aware of all the pieces that go into itself and handles the 'glue' between them, you could put that game object into the Tag property. Then, when a ray cast hits a BroadPhaseEntry, you could attempt to cast the Tag to your game object and then fire your RayHit event or directly call some logic or whatever else.
Also, as a sidenote: I'd like to store a string about the collision entity in its tag. I was able to get this to work for a static mesh, but in my Door class (which inherits from a class I wrote for drawing a box which subsequently inherits from the Box class itself) the data doesn't seem to be getting through. Does anyone have any thoughts as to why this doesn't work?
This is probably just the separation between BroadPhaseEntry.Tag and Entity.Tag mentioned above. They are separate properties, so setting one does not set the other automatically.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Getting Object Data From RayCasts

Post by Norbo »

EDIT: A second side question. Is the help file on the Bepu codeplex page broken? When I try to view anything in it the link is broken.
This is probably just the security measure that blocks .chm files that are downloaded from remote sources. Try right clicking, going to properties, and at the bottom click Unblock.
Cheeser12
Posts: 8
Joined: Sat Sep 17, 2011 10:10 pm

Re: Getting Object Data From RayCasts

Post by Cheeser12 »

Thanks so much for that informative post! :)

It looks like I have some work to do then....I had my suspicions that combining the drawable box to the physical one was probably not the best idea. I'll separate those.
And since they would be separated, that does make using the Tag property to store the Door object itself probably the best course of action.

Thanks for your help! :D
Cheeser12
Posts: 8
Joined: Sat Sep 17, 2011 10:10 pm

Re: Getting Object Data From RayCasts

Post by Cheeser12 »

I separated the physics and graphics part of the box as you suggested. However, I'm still having problems accessing the tag.

When I set the data for the tag to the Door itself:

Code: Select all

FrontDoor.PhysicsBox.CollisionInformation.Tag = FrontDoor;
I'm not getting the correct data for a hit (I toggle whether the sky is drawn to test):

Code: Select all

RayCastResult result;
                space.RayCast(new Ray(character.Camera.Position + 5f * character.Camera.Direction, character.Camera.Direction), out result);

                if (result.HitObject != null)
                {          
                    if (result.HitObject.Tag as Door != null)
                    {
                        // Not set!
                        drawSky = false;
                        
                    }
                }
When I tried this with a string, it returned true when I checked if the tag was a string. When I tried to do an equality test for the value, however, (is the string = "Door") it didn't work however. Is there anything I'm doing wrong here?
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Getting Object Data From RayCasts

Post by Norbo »

When I tried this with a string, it returned true when I checked if the tag was a string. When I tried to do an equality test for the value, however, (is the string = "Door") it didn't work however.
Comparing reference equality between a string that has a value of "door" and the string literal "door" can return false because they are not necessarily the same reference. Depending on how the equality was tested, this might have been related.
Is there anything I'm doing wrong here?
Not obviously in the given code. I would recommend putting a break point in there, manually examining the values, and comparing them against your expectations.

Nothing in the engine sets or interferes with the Tag property, so all the relevant logic should be within external code somewhere.
Cheeser12
Posts: 8
Joined: Sat Sep 17, 2011 10:10 pm

Re: Getting Object Data From RayCasts

Post by Cheeser12 »

Fixed it! :)

The problem was that the ray I was casting had a tail that was actually past the door I was looking at. I had to set a displacement from the camera position because otherwise the ray was hitting the character's collision object from the inside. When I went through with breakpoints as you suggested, I found that the ray's displacement was causing it to hit the wall behind the door instead of the door itself. Shortening the displacement fixed this.

Thanks for all the help! :D
Post Reply