Hello, is there any way, I could get whole list of items, when I do RayCast on the viewport items? I need to pick geometry on click and currently MouseRayHit or RayCast, WorldRayCast, etc., returns just the first item it hits with it. Is there any way in engine to pick all the items, also underneath the first item picked by RayCast? Thank you.
All the ray-casting methods are indeed designed to only return first hit object. There are specific optimizations inside for this use-case, since it is the most common need.
To query everything, you can repeat the query, shifting
RayOrigin (position at which the ray starts) each time. This is similar to what I described in this post: Convenient way to determine exact point under mouse - #4 by michalis , albeit that was for a bit different task (to avoid detecting hits on transparent pixels).
MyViewport.CameraRayCollision(RayOrigin, RayDirection). It returns
TRayCollision, just like
MouseRayHit(so it can be processed in the same way as above to detect the particular point). It takes as input ray position and direction.
Use it in a loop.
RayDirectioncan come from the mouse position – use
Make a call to
MyViewport.CameraRayCollision(RayOrigin, RayDirection). If something was hit, add it to the list, and make another call to
MyViewport.CameraRayCollision(NewRayOrigin, RayDirection). The
NewRayOrigincan be calculated like
NewRayOrigin := HitPosition + RayDirection.AdjustToLength(SomeEpsilon)
Hello, thank you, it worked nicely, with just one thing - Pickable property needs to be set to false while raycasting, to prevent picking object multiple times depending on shape. But I found one issue, with TCastleAbstractPrimitives, whenever I create for example a TCastleBox in editor and load it into runtime app, this does not work. Box is registered correctly by raycast, but no Name given event if I had named it, and also another raycast for object behind or under it does not work either. For loaded TCastleScene models or created geometry via X3D it works perfect. Is there anything specific why it would not work on primitives like box, plane etc.? Thanks.
Indeed. If you don’t want multiple hits on the same object, you can toggle
Exsists on the object.
Ray collisions with primitives (TCastleAbstractPrimitive) should be detected OK.
But note that
MyViewport.MouseRayHit will then be more complicated: the first item on
MyViewport.MouseRayHit will be the internal
TCastleScene instance (each TCastleAbstractPrimitives actually creates an internal
TCastleScene that it manages). The 2nd item will be a reference to the actual TCastleAbstractPrimitive instance.
It that is the case, it is easiest to just use
MyViewport.TransformUnderMouse. Or look at
MyViewport.MouseRayHit <> nil, if not nil then look at
MyViewport.MouseRayHit.Transform. Both these methods will return the first transform that is not internal. (they detect it by looking at
csTransient in Transform.ComponentStyle).
It is possible we’ll change
MouseRayHit at some point to avoid showing these internal instances at all. I know they can be confusing. These are also discussed in Tiled maps | Manual | Castle Game Engine .
Let me know if this fixes your problem with TCastleAbstractPrimitive. If the problem will remain, please submit a testcase to reproduce the problem.
Hello, so it looks like I did not thoroughly tested, because I have found a strange issue. It does not work completely I figured now, but I do not understand why. Basically I have saved user interface file which I edit in the editor where I do have viewport as root, some background and then in the Items, I do have Camera, DirLight and all models as TCastleScene. One of them I do have generated as custom geometry through X3DNodes and is saved as X3D and loaded into scene (there I need to differentiate per shape with help of triangle, so I need extended collision result). The thing is, that when I move around and get mouse cursor over object like e.g. building there, and box behind it, it only registers the building and then proceeds to return nil as WorldRay result even if I previously make the building Pickable property false. But when there is that custom geometry behind building (but should be irelevant as it is loaded as X3D model), it does register both objects correctly. Where could possibly be a problem hidden here? Here is the code for method which I call to get all geometry in Viewport’s OnMotion procedure.
function CGEForm.AllItemsHitByRay(pos: TVector2; depth: integer): TList<TPair<TShape, TCastleTransform>>; var RayHit: TRayCollision; RayOrigin, RayDirection: TVector3; Item: TPair<TShape, TCastleTransform>; Shape: TShape; rayColNode: TRayCollisionNode; rayColPos: Integer; begin Result := nil; depth := Max(depth, 1); fCGEViewport.PositionToRay(pos, true, RayOrigin, RayDirection); RayHit := fCGEViewport.Items.WorldRay(RayOrigin, RayDirection); if RayHit <> nil then begin rayColPos := GetCorrectRayCollisionNodePos(rayHit); Result := TList<TPair<TShape, TCastleTransform>>.Create(); while (RayHit <> nil) AND (rayColPos > -1) AND (depth > 0) do begin shape := nil; rayColNode := RayHit.Items[rayColPos]; if (rayColNode.Item is TCastleScene) AND (rayColNode.Triangle <> nil) AND (rayColNode.Triangle.Shape <> nil) then begin if TCastleScene(rayColNode.Item).VerticesCount <> rayColNode.Triangle.Shape.VerticesCount then shape := rayColNode.Triangle.Shape; end; Result.Add(TPair<TShape, TCastleTransform>.Create(shape, rayColNode.Item)); rayColNode.Item.Pickable := false; RayOrigin := rayColNode.Point + RayDirection.AdjustToLength(SingleEqualityEpsilon); FreeAndNil(RayHit); RayHit := fCGEViewport.Items.WorldRay(RayOrigin, RayDirection); rayColPos := GetCorrectRayCollisionNodePos(RayHit); Dec(depth); end; if RayHit <> nil then FreeAndNil(RayHit); for Item in Result do begin Item.Value.Pickable := true; end; end; end;
Thanks for any further advice.
Hmm, I don’t know the solution from a first glance. Your code looks OK (in particular you restore the
...Pickable := true reliably, which was my first suspicion). I will need to have a complete testcase (code that compiles + data) to experiment and advise more on this. Can you post it (on this forum, or as GitHub issue Issues · castle-engine/castle-engine · GitHub ) ?
Hello, sorry for the inconvenience, I just found out that I changed a logic for printing out the names of objects under pick slightly and also quite poorly debugged probably, because now I have noticed the cause and the solution seems to work flawless as it is there. Also there is probably no need to computing vector from hitPoint when disabling pickable, thus saving a line and small operation cost maybe. Thanks for help.