How to detect a on-clicked node from the scene graph?

Hi there,

after a lot of time in experimenting and researching, I still can’t find a good solution to the following problem:

Assume your scene has two different transform nodes which share the same shape node (and same geometry node, let it be a 2d rectangle). If both rectangles are shown in the viewport and I click on one of these, I can’t figure out which is the underlying (parent) transform node. From the viewport’s triangle hit test function I get the triangle and the associated TShape structure and the TShapeNode. As they hold both transform nodes as parents, I do not know how to proceed to find the actual transform node?

The code shows the event evaluation:

``````if Event.IsMouseButton(buttonLeft) then
begin
Triangle := Viewport.TriangleHit;
if (Triangle <> nil) then
begin
if  Triangle^.ShapeNode.ParentFieldsCount > 0 then
begin
for I := 0 to Triangle^.ShapeNode.ParentFieldsCount-1 do
begin
if Triangle^.ShapeNode.ParentFieldsNode[I] is TTransformNode then
begin
// Do something to determine the transform node here?
end
end;
end;
end;
end;
``````

I wonder, if the approach is completely wrong and I should rather use the bounding box method?

Any hint is welcome
Kobalt

Ah, the `TShapeNode` references in this case are indeed not unique enough. Both cases will share the same `TShapeNode` reference.

1. You can look at `Triangle^.Shape` (instance of `TShape`, not to be mistaken with `TShapeNode`). Different instance of the node will have a different `TShape` reference, even though they share equal `TShapeNode`.

And `Triangle^.Shape` has `GeometryParentNodeName`, `GeometryGrandParentNodeName`, `GeometryGrandGrandParentNodeName` (see Castle Game Engine: CastleShapes: Class TShape ) . `GeometryParentNodeName` is probably not very useful (the “parent” is the `TShapeNode` in this case) but `GeometryGrandParentNodeName` and `GeometryGrandGrandParentNodeName` may be useful.

I’m afraid these isn’t more information that could help you. `TShape` could but doesn’t store more information about parents (like all references to X3D parent nodes). It’s a very rare use-case that you need to differentiate between clicks on 2 reused shapes.

2. A complicated solution: You could derive your own `TMyCastleScene` (descendant of `TCastleScene`), and `TMyShape` (your own `TGLShape` descendant). In `TMyCastleScene` you would override `CreateShape` to create instance of `TMyShape`. And then you can override `TMyShape.Create` constructor, that gets `ParentInfo` with detailed links to all parents:

``````constructor TShape.Create(const AParentScene: TX3DEventsEngine;
const AOriginalGeometry: TAbstractGeometryNode;
const AOriginalState: TX3DGraphTraverseState;
const ParentInfo: PTraversingInfo);
``````
1. Depending on your use-case, but the simplest solution (if AD 1 is not enough and AD 2 seems to complicated) may be to just avoid such constructions in your files.

Depends on what you need. If all you want is a bounding box, then sure, just use `TShape.BoundingBox`.

To elaborate on my project to understand the question: I’m working on a tile board converter. The created x3d scene contains a lot of transform nodes (one for each tile), but for efficiency (see reply to AD 3 below), tile nodes share the same shape node if they a look alike.

Unfortunately the name strings are empty.

Unfortunately this is, as you stated, a little too complicated for my use case. I am just looking for a simple way to detect the clicked-on tile-transform node for a short demo program. Please see also answer to AD 3.

I wonder about this statement: Lets assume you have 100 transform nodes, shouldn’t it be much more memory efficient to have them share 1 shape node (if they all look alike) instead of creating 100 shape nodes which are configured identically. - If this is not true, it would be even simpler solution to just create an individual shape for each transform node, although they are configured identically.

Best regards

.

In such case your optimization makes total sense. Keep them shared, this is better, allows to reuse all GPU resources on the engine side (without any extra work to “detect if some shapes are equal” – we don’t need to detect it when we know they have equal references).

Hm, so let’s try to make a solution that is simple but also uses your data.

Would changing existing node names (strings):

``````    property GeometryParentNodeName: string read FGeometryParentNodeName;
``````

into references to nodes, like this:

``````    property GeometryParentNode: TX3DNode ...;
property GeometryGrandParentNode: TX3DNode ...;
property GeometryGrandGrandParentNode: TX3DNode ...;
``````

I would then make `XxxNodeName` a deprecated functions that just access `XxxNode.X3DName`.

Just dropping 2 cents (and maybe that’s not a good idea, because it’s less generic), but if aiming at efficiency I’d avoid raycasting against multiple colliders/bounding boxes. I’d rather create a (maybe invisible) container plane (which would correspond to span of the map), detect point of ray collision only with this plane and based on collision coordinates convert those to 2D coordinates on this plane and eventually calculate the tile that was clicked.

This method has it’s limitations (e.g. if the tiles can be elevated or have variable height + viewed on oblique angle), but for most usecases I’m aware of it may be more efficient to do coordinates conversion twice-thrice, than enumerate all the objects in the scene.

If the `Geometry(Grand)(Grand)ParentNode` functions will just return the one grand parent node considering the transformation state, this would be perfectly sufficient.

I like the idea. Though, with the demo program I just want to demonstrate the possibility to load and use the scene in the most basic way. If you plan on developing a real project from this, there could be more efficient implementations (with regard of your concern about the sensor detection efficiency) to use the tile map scene depending on your use-case (as the one proposed by you).

Yes exactly. Done in TShape.GeometryParentNode, GeometryGrandParentNode, · castle-engine/[email protected] · GitHub , check it out

Thank you! Works like a charm now! - With minimal effort the underlying transform node is found now.

1 Like