diff --git a/thermion_dart/lib/thermion_dart/compatibility/native/thermion_dart.g.dart b/thermion_dart/lib/thermion_dart/compatibility/native/thermion_dart.g.dart index 59c96823..e23e1663 100644 --- a/thermion_dart/lib/thermion_dart/compatibility/native/thermion_dart.g.dart +++ b/thermion_dart/lib/thermion_dart/compatibility/native/thermion_dart.g.dart @@ -1043,6 +1043,12 @@ external int get_parent( int child, ); +@ffi.Native, EntityId)>() +external int get_ancestor( + ffi.Pointer sceneManager, + int child, +); + @ffi.Native< ffi.Void Function(ffi.Pointer, EntityId, EntityId, ffi.Bool)>() external void set_parent( diff --git a/thermion_dart/lib/thermion_dart/thermion_viewer.dart b/thermion_dart/lib/thermion_dart/thermion_viewer.dart index 1cb115a2..2bb61c00 100644 --- a/thermion_dart/lib/thermion_dart/thermion_viewer.dart +++ b/thermion_dart/lib/thermion_dart/thermion_viewer.dart @@ -773,9 +773,14 @@ abstract class ThermionViewer { PrimitiveType primitiveType = PrimitiveType.TRIANGLES}); /// - /// Gets the parent transform of [child]. + /// Gets the parent entity of [entity]. Returns null if the entity has no parent. /// - Future getParent(ThermionEntity child); + Future getParent(ThermionEntity entity); + + /// + /// Gets the ancestor (ultimate parent) entity of [entity]. Returns null if the entity has no parent. + /// + Future getAncestor(ThermionEntity entity); /// /// Sets the parent transform of [child] to [parent]. diff --git a/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart b/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart index 47f0a526..7123f0a6 100644 --- a/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart +++ b/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart @@ -1805,6 +1805,21 @@ class ThermionViewerFFI extends ThermionViewer { return parent; } + /// + /// + /// + @override + Future getAncestor(ThermionEntity child) async { + if (_sceneManager == null) { + throw Exception("Asset manager must be non-null"); + } + var parent = get_ancestor(_sceneManager!, child); + if (parent == _FILAMENT_ASSET_ERROR) { + return null; + } + return parent; + } + /// /// /// diff --git a/thermion_dart/test/integration_test.dart b/thermion_dart/test/integration_test.dart index 9889b62a..034a481e 100644 --- a/thermion_dart/test/integration_test.dart +++ b/thermion_dart/test/integration_test.dart @@ -124,7 +124,51 @@ void main() async { }); }); - group("transforms", () { + group("transforms & parenting", () { + test('getParent and getAncestor both return null when entity has no parent', () async { + var viewer = await createViewer(); + + final cube = await viewer.createGeometry(cubeVertices, cubeIndices, + primitiveType: PrimitiveType.TRIANGLES); + + expect(await viewer.getParent(cube), isNull); + expect(await viewer.getAncestor(cube), isNull); + }); + + test( + 'getParent returns the parent entity after one has been set via setParent', + () async { + var viewer = await createViewer(); + + final cube1 = await viewer.createGeometry(cubeVertices, cubeIndices, + primitiveType: PrimitiveType.TRIANGLES); + final cube2 = await viewer.createGeometry(cubeVertices, cubeIndices, + primitiveType: PrimitiveType.TRIANGLES); + + await viewer.setParent(cube1, cube2); + + final parent = await viewer.getParent(cube1); + + expect(parent, cube2); + }); + + test('getAncestor returns the ultimate parent entity', () async { + var viewer = await createViewer(); + + final grandparent = await viewer.createGeometry(cubeVertices, cubeIndices, + primitiveType: PrimitiveType.TRIANGLES); + final parent = await viewer.createGeometry(cubeVertices, cubeIndices, + primitiveType: PrimitiveType.TRIANGLES); + final child = await viewer.createGeometry(cubeVertices, cubeIndices, + primitiveType: PrimitiveType.TRIANGLES); + + await viewer.setParent(child, parent); + await viewer.setParent(parent, grandparent); + + expect(await viewer.getAncestor(child), grandparent); + + }); + test('set position based on screenspace coord', () async { var viewer = await createViewer(); print(await viewer.getCameraFov(true));