WorldRay not finding my roads

I have a function that uses WorldRay to determine if there is a road at the given position. If there is, it returns the Y of the worldray vector intersection with the road. Otherwise it returns the height from the terrain. It ignores other objects.

But WorldRay isn’t finding my roads. It may find the vehicle at that position, it may find the terrain tile. But it isn’t finding the road. The road has collide=true and exists=true and pickable=true. I think WorldRay is supposed to return everything that intersects this ray. What might be going wrong?

In this case, it is getting heights for the dump truck, so using its position to generate the worldray. But the road segment under the truck is not in the resulting RayCollision list.

function htfromRoad( MainViewport : TCastleViewport;
                       Terrain : TMyTerrain;
                       Pos : TVector3 ) : single;
    var RayCollision: TRayCollision;

    procedure findroad( out roadheight : single );
     var i : integer;
         item : TCastleTransform;
     begin
       for i := RayCollision.Count - 1 downto 0 do
        begin
          item := RayCollision[i].Item;
          if ( Item is TTerrainTile ) then
             break
          else
          if ( Item is TRoadGraphic ) or ( Item is TRoadNodeGraphic ) then
           begin
             roadheight := RayCollision[i].Point.Y;
             exit;
           end;
        end;
       Terrain.heightAboveTerrain( Pos, roadheight );
     end;
   begin
     Pos.Y := 1000;
    // gmarkuplayer.addmark(vector2(pos.x, pos.z), 0.1, whiteRGB);
     RayCollision := MainViewport.Items.WorldRay(Pos, Vector3(0, -1, 0));
     if assigned( RayCollision ) then
      begin
        findroad( Result );
        FreeAndNil(RayCollision)
      end;
   end;                                          

MouseRayHit does find the roads. Not sure how it differs from WorldRay.

I have also a problem with WorldRay. I use it in the Start procedure on terrain and it works fine. If I use the same function in the Update procedure it always returns 0.

After more testing, WorldRay and MouseRay don’t return everything that intersects. I will have to keep stepping.

Sorry, I found the error in my code. WorldRay works also in the Update procedure.

Indeed, the ray collision routines return the first thing that intersects with the ray. They are internally optimized for this too.

It may be a bit confusing, because TRayCollision is a list (of TRayCollisionNode), but it is just a list of all parents of the object being hit. It’s a path within the TCastleTransform tree, to the root of that tree. So it all describes just the one collision found.

Indeed “stepping” (repeating the ray cast, with new ray position equal to “detected collision point + ray direction * epsilon”) is the way to solve this.

Thanks for the clarification. The docs are a little fuzzy on this. I inferred it would get all objects on the ray. Thanks.

" This checks collisions with world (everything inside this TCastleAbstractRootTransform)." made me think it would return a list of everything too.

A world xray function would be cool:)

Good point, it seems docs never said this (“the ray collision finds the first hit”) explicitly. I improved them: Document that various ray-casting routines return the *first* object … · castle-engine/castle-engine@500660e · GitHub

As for adding an alternative routine to CGE that returns everything: In general we want to leverage in the future the physics engine (currently Kraft, we plan to also add PhysX or Bullet in the future, see Roadmap | Manual | Castle Game Engine ) for such features. And not implement things like “ray cast” in CGE at all, i.e. we will expose simple methods in CGE naturally but they will just use existing functionality from a physics engine. And looking at PhysX possibilities…

… they actually pack a lot of “power”, including “max hits” and “sweeps”, both of which could be used to get “more than only a first hit” from collision routines. So this is cool, and once we jump into PhysX we should explore this.

1 Like

So the method of looking up terrain height at a given point in the callback function that is used by Wyrdforest will return the height of the top of an object that is at that location, instead of the terrain. This explains difficulties I was having before I changed to getting the terrain height direct from the data instead of using the worldray. But now I do want the height of things like roads in certain cases.

Hmm. If I was to make the vehicle become the child of any road segment that it is on, instead of siblings in the main scene… would I get both the vehicle and the road in the results of the vertical worldray? That may be a solution, and it could have some other advantages too (like road segment immediately knowing what vehicles are on it).

The WorldRay queries all objects, indeed, not only terrain.

You can flip the Exists flag to false on some objects, before doing the raycast, to avoid hitting them. Routines like TCastleTransform.RayCast do this for you, i.e. they just do

function TCastleTransform.RayCast(const RayOrigin, RayDirection: TVector3): TCastleTransform;
var
  RayOriginWorld, RayDirectionWorld: TVector3;
  SavedExists: Boolean;
begin
  RayOriginWorld := Parent.LocalToWorld(RayOrigin);
  RayDirectionWorld := Parent.LocalToWorldDirection(RayDirection);
  SavedExists := Exists;
  Exists := false;
  try
    Result := World.WorldRayCast(RayOriginWorld, RayDirectionWorld);
  finally Exists := SavedExists end;
end;

You could also query using PhysicsRayCast which can take a parameter with physics layers ( Physics | Manual | Castle Game Engine ) to select what is hit. Ah, but this is only available in PR for now, not yet in CGE master.

The ray collision routine doesn’t care about the hierarchy. We return the first TCastleScene hit by the ray. It doesn’t matter whether it is a child of another TCastleTransform (which can be another TCastleScene). That is, the TRayCollision list will return all the parents of the hit scene, but the exact collision was just found at the first item there.

IOW, yes, but I’m not sure if this is useful to you :slight_smile: You don’t really change what collides with what by changing the hierarchy arrangement. But you can surely change the hierarchy and the TRayCollision list wil reflect that.