diff --git a/thermion_dart/ffigen/native.yaml b/thermion_dart/ffigen/native.yaml index c4472fde..cb79e560 100644 --- a/thermion_dart/ffigen/native.yaml +++ b/thermion_dart/ffigen/native.yaml @@ -4,10 +4,12 @@ headers: - '../native/include/ThermionDartFFIApi.h' - '../native/include/ThermionDartApi.h' - '../native/include/ResourceBuffer.h' + - '../native/include/Aabb2.h' include-directives: - '../native/include/ThermionDartFFIApi.h' - '../native/include/ThermionDartApi.h' - '../native/include/ResourceBuffer.h' + - '../native/include/Aabb2.h' ffi-native: assetId: package:thermion_dart/thermion_dart.dart ignore-source-errors: true 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 30b657ee..b12b7d42 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 @@ -123,7 +123,7 @@ external void remove_ibl( ); @ffi.Native< - ffi.Int Function( + EntityId Function( ffi.Pointer, ffi.Uint8, ffi.Float, @@ -161,7 +161,7 @@ external int add_light( bool shadows, ); -@ffi.Native, ffi.Int)>() +@ffi.Native, EntityId)>() external void remove_light( ffi.Pointer viewer, int entityId, @@ -173,7 +173,7 @@ external void clear_lights( ); @ffi.Native< - ffi.Int Function(ffi.Pointer, ffi.Pointer, ffi.Int)>() + EntityId Function(ffi.Pointer, ffi.Pointer, ffi.Int)>() external int load_glb( ffi.Pointer sceneManager, ffi.Pointer assetPath, @@ -181,7 +181,7 @@ external int load_glb( ); @ffi.Native< - ffi.Int Function(ffi.Pointer, ffi.Pointer, ffi.Size)>() + EntityId Function(ffi.Pointer, ffi.Pointer, ffi.Size)>() external int load_glb_from_buffer( ffi.Pointer sceneManager, ffi.Pointer data, @@ -189,7 +189,7 @@ external int load_glb_from_buffer( ); @ffi.Native< - ffi.Int Function( + EntityId Function( ffi.Pointer, ffi.Pointer, ffi.Pointer)>() external int load_gltf( ffi.Pointer sceneManager, @@ -197,24 +197,24 @@ external int load_gltf( ffi.Pointer relativePath, ); -@ffi.Native, ffi.Int)>() +@ffi.Native, EntityId)>() external int create_instance( ffi.Pointer sceneManager, int id, ); -@ffi.Native, ffi.Int)>() +@ffi.Native, EntityId)>() external int get_instance_count( ffi.Pointer sceneManager, int entityId, ); @ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Int, ffi.Pointer)>() + ffi.Void Function(ffi.Pointer, EntityId, ffi.Pointer)>() external void get_instances( ffi.Pointer sceneManager, int entityId, - ffi.Pointer out, + ffi.Pointer out, ); @ffi.Native)>() @@ -222,13 +222,13 @@ external void set_main_camera( ffi.Pointer viewer, ); -@ffi.Native)>() +@ffi.Native)>() external int get_main_camera( ffi.Pointer viewer, ); @ffi.Native< - ffi.Bool Function(ffi.Pointer, ffi.Int, ffi.Pointer)>() + ffi.Bool Function(ffi.Pointer, EntityId, ffi.Pointer)>() external bool set_camera( ffi.Pointer viewer, int entity, @@ -263,6 +263,15 @@ external void render( ffi.Pointer data, ); +@ffi.Native< + ffi.Void Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer>)>() +external void capture( + ffi.Pointer viewer, + ffi.Pointer pixelBuffer, + ffi.Pointer> callback, +); + @ffi.Native< ffi.Void Function( ffi.Pointer, ffi.Pointer, ffi.Uint32, ffi.Uint32)>() @@ -335,7 +344,7 @@ external void grab_end( ); @ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Int, ffi.Pointer, + ffi.Void Function(ffi.Pointer, EntityId, ffi.Pointer, ffi.Pointer, ffi.Int)>() external void apply_weights( ffi.Pointer sceneManager, @@ -347,7 +356,7 @@ external void apply_weights( @ffi.Native< ffi.Bool Function( - ffi.Pointer, ffi.Int, ffi.Pointer, ffi.Int)>() + ffi.Pointer, EntityId, ffi.Pointer, ffi.Int)>() external bool set_morph_target_weights( ffi.Pointer sceneManager, int entity, @@ -356,7 +365,7 @@ external bool set_morph_target_weights( ); @ffi.Native< - ffi.Bool Function(ffi.Pointer, ffi.Int, ffi.Pointer, + ffi.Bool Function(ffi.Pointer, EntityId, ffi.Pointer, ffi.Pointer, ffi.Int, ffi.Int, ffi.Float)>() external bool set_morph_animation( ffi.Pointer sceneManager, @@ -368,13 +377,13 @@ external bool set_morph_animation( double frameLengthInMs, ); -@ffi.Native, ffi.Int)>() +@ffi.Native, EntityId)>() external void clear_morph_animation( ffi.Pointer sceneManager, int entity, ); -@ffi.Native, ffi.Int)>() +@ffi.Native, EntityId)>() external void reset_to_rest_pose( ffi.Pointer sceneManager, int asset, @@ -383,7 +392,7 @@ external void reset_to_rest_pose( @ffi.Native< ffi.Void Function( ffi.Pointer, - ffi.Int, + EntityId, ffi.Int, ffi.Int, ffi.Pointer, @@ -406,7 +415,8 @@ external void add_bone_animation( ); @ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Int, ffi.Pointer)>() + ffi.Void Function( + ffi.Pointer, EntityId, ffi.Pointer)>() external void get_local_transform( ffi.Pointer sceneManager, int entityId, @@ -414,7 +424,7 @@ external void get_local_transform( ); @ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Int, ffi.Int, + ffi.Void Function(ffi.Pointer, EntityId, ffi.Int, ffi.Pointer, ffi.Int)>() external void get_rest_local_transforms( ffi.Pointer sceneManager, @@ -425,7 +435,8 @@ external void get_rest_local_transforms( ); @ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Int, ffi.Pointer)>() + ffi.Void Function( + ffi.Pointer, EntityId, ffi.Pointer)>() external void get_world_transform( ffi.Pointer sceneManager, int entityId, @@ -433,7 +444,7 @@ external void get_world_transform( ); @ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Int, ffi.Int, ffi.Int, + ffi.Void Function(ffi.Pointer, EntityId, ffi.Int, ffi.Int, ffi.Pointer)>() external void get_inverse_bind_matrix( ffi.Pointer sceneManager, @@ -444,7 +455,7 @@ external void get_inverse_bind_matrix( ); @ffi.Native< - ffi.Bool Function(ffi.Pointer, ffi.Int, ffi.Int, ffi.Int, + ffi.Bool Function(ffi.Pointer, EntityId, ffi.Int, ffi.Int, ffi.Pointer)>() external bool set_bone_transform( ffi.Pointer sceneManager, @@ -455,7 +466,7 @@ external bool set_bone_transform( ); @ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Int, ffi.Int, ffi.Bool, + ffi.Void Function(ffi.Pointer, EntityId, ffi.Int, ffi.Bool, ffi.Bool, ffi.Bool, ffi.Float, ffi.Float)>() external void play_animation( ffi.Pointer sceneManager, @@ -469,7 +480,7 @@ external void play_animation( ); @ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Int, ffi.Int, ffi.Int)>() + ffi.Void Function(ffi.Pointer, EntityId, ffi.Int, ffi.Int)>() external void set_animation_frame( ffi.Pointer sceneManager, int entity, @@ -477,14 +488,14 @@ external void set_animation_frame( int animationFrame, ); -@ffi.Native, ffi.Int, ffi.Int)>() +@ffi.Native, EntityId, ffi.Int)>() external void stop_animation( ffi.Pointer sceneManager, int entity, int index, ); -@ffi.Native, ffi.Int)>() +@ffi.Native, EntityId)>() external int get_animation_count( ffi.Pointer sceneManager, int asset, @@ -492,7 +503,7 @@ external int get_animation_count( @ffi.Native< ffi.Void Function( - ffi.Pointer, ffi.Int, ffi.Pointer, ffi.Int)>() + ffi.Pointer, EntityId, ffi.Pointer, ffi.Int)>() external void get_animation_name( ffi.Pointer sceneManager, int entity, @@ -500,14 +511,14 @@ external void get_animation_name( int index, ); -@ffi.Native, ffi.Int, ffi.Int)>() +@ffi.Native, EntityId, ffi.Int)>() external double get_animation_duration( ffi.Pointer sceneManager, int entity, int index, ); -@ffi.Native, ffi.Int, ffi.Int)>() +@ffi.Native, EntityId, ffi.Int)>() external int get_bone_count( ffi.Pointer sceneManager, int assetEntity, @@ -515,7 +526,7 @@ external int get_bone_count( ); @ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Int, + ffi.Void Function(ffi.Pointer, EntityId, ffi.Pointer>, ffi.Int)>() external void get_bone_names( ffi.Pointer sceneManager, @@ -525,7 +536,7 @@ external void get_bone_names( ); @ffi.Native< - ffi.Int Function(ffi.Pointer, ffi.Int, ffi.Int, ffi.Int)>() + EntityId Function(ffi.Pointer, EntityId, ffi.Int, ffi.Int)>() external int get_bone( ffi.Pointer sceneManager, int entityId, @@ -534,21 +545,22 @@ external int get_bone( ); @ffi.Native< - ffi.Bool Function(ffi.Pointer, ffi.Int, ffi.Pointer)>() + ffi.Bool Function( + ffi.Pointer, EntityId, ffi.Pointer)>() external bool set_transform( ffi.Pointer sceneManager, int entityId, ffi.Pointer transform, ); -@ffi.Native, ffi.Int)>() +@ffi.Native, EntityId)>() external bool update_bone_matrices( ffi.Pointer sceneManager, int entityId, ); @ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Int, ffi.Int, + ffi.Void Function(ffi.Pointer, EntityId, EntityId, ffi.Pointer, ffi.Int)>() external void get_morph_target_name( ffi.Pointer sceneManager, @@ -558,14 +570,14 @@ external void get_morph_target_name( int index, ); -@ffi.Native, ffi.Int, ffi.Int)>() +@ffi.Native, EntityId, EntityId)>() external int get_morph_target_name_count( ffi.Pointer sceneManager, int assetEntity, int childEntity, ); -@ffi.Native, ffi.Int)>() +@ffi.Native, EntityId)>() external void remove_entity( ffi.Pointer viewer, int asset, @@ -577,7 +589,7 @@ external void clear_entities( ); @ffi.Native< - ffi.Bool Function(ffi.Pointer, ffi.Int, ffi.Pointer, + ffi.Bool Function(ffi.Pointer, EntityId, ffi.Pointer, ffi.Int, ffi.Float, ffi.Float, ffi.Float, ffi.Float)>() external bool set_material_color( ffi.Pointer sceneManager, @@ -590,14 +602,14 @@ external bool set_material_color( double a, ); -@ffi.Native, ffi.Int)>() +@ffi.Native, EntityId)>() external void transform_to_unit_cube( ffi.Pointer sceneManager, int asset, ); @ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Int, ffi.Float, ffi.Float, + ffi.Void Function(ffi.Pointer, EntityId, ffi.Float, ffi.Float, ffi.Float, ffi.Bool)>() external void queue_position_update( ffi.Pointer sceneManager, @@ -609,7 +621,7 @@ external void queue_position_update( ); @ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Int, ffi.Float, ffi.Float, + ffi.Void Function(ffi.Pointer, EntityId, ffi.Float, ffi.Float, ffi.Float, ffi.Float, ffi.Float, ffi.Bool)>() external void queue_rotation_update( ffi.Pointer sceneManager, @@ -624,7 +636,7 @@ external void queue_rotation_update( @ffi.Native< ffi.Void Function( - ffi.Pointer, ffi.Int, ffi.Float, ffi.Float, ffi.Float)>() + ffi.Pointer, EntityId, ffi.Float, ffi.Float, ffi.Float)>() external void set_position( ffi.Pointer sceneManager, int entity, @@ -634,7 +646,7 @@ external void set_position( ); @ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Int, ffi.Float, ffi.Float, + ffi.Void Function(ffi.Pointer, EntityId, ffi.Float, ffi.Float, ffi.Float, ffi.Float, ffi.Float)>() external void set_rotation( ffi.Pointer sceneManager, @@ -646,14 +658,14 @@ external void set_rotation( double w, ); -@ffi.Native, ffi.Int, ffi.Float)>() +@ffi.Native, EntityId, ffi.Float)>() external void set_scale( ffi.Pointer sceneManager, int entity, double scale, ); -@ffi.Native, ffi.Int)>() +@ffi.Native, EntityId)>() external void move_camera_to_asset( ffi.Pointer viewer, int asset, @@ -782,7 +794,7 @@ external void set_camera_manipulator_options( ); @ffi.Native< - ffi.Int Function(ffi.Pointer, ffi.Int, ffi.Pointer)>() + ffi.Int Function(ffi.Pointer, EntityId, ffi.Pointer)>() external int hide_mesh( ffi.Pointer sceneManager, int entity, @@ -790,7 +802,7 @@ external int hide_mesh( ); @ffi.Native< - ffi.Int Function(ffi.Pointer, ffi.Int, ffi.Pointer)>() + ffi.Int Function(ffi.Pointer, EntityId, ffi.Pointer)>() external int reveal_mesh( ffi.Pointer sceneManager, int entity, @@ -838,32 +850,32 @@ external void set_antialiasing( ffi.Int, ffi.Pointer< ffi.NativeFunction< - ffi.Void Function(ffi.Int entityId, ffi.Int x, ffi.Int y)>>)>() + ffi.Void Function(EntityId entityId, ffi.Int x, ffi.Int y)>>)>() external void filament_pick( ffi.Pointer viewer, int x, int y, ffi.Pointer< ffi.NativeFunction< - ffi.Void Function(ffi.Int entityId, ffi.Int x, ffi.Int y)>> + ffi.Void Function(EntityId entityId, ffi.Int x, ffi.Int y)>> callback, ); -@ffi.Native Function(ffi.Pointer, ffi.Int)>() +@ffi.Native Function(ffi.Pointer, EntityId)>() external ffi.Pointer get_name_for_entity( ffi.Pointer sceneManager, int entityId, ); @ffi.Native< - ffi.Int Function(ffi.Pointer, ffi.Int, ffi.Pointer)>() + EntityId Function(ffi.Pointer, EntityId, ffi.Pointer)>() external int find_child_entity_by_name( ffi.Pointer sceneManager, int parent, ffi.Pointer name, ); -@ffi.Native, ffi.Int, ffi.Bool)>() +@ffi.Native, EntityId, ffi.Bool)>() external int get_entity_count( ffi.Pointer sceneManager, int target, @@ -872,17 +884,17 @@ external int get_entity_count( @ffi.Native< ffi.Void Function( - ffi.Pointer, ffi.Int, ffi.Bool, ffi.Pointer)>() + ffi.Pointer, EntityId, ffi.Bool, ffi.Pointer)>() external void get_entities( ffi.Pointer sceneManager, int target, bool renderableOnly, - ffi.Pointer out, + ffi.Pointer out, ); @ffi.Native< ffi.Pointer Function( - ffi.Pointer, ffi.Int, ffi.Int, ffi.Bool)>() + ffi.Pointer, EntityId, ffi.Int, ffi.Bool)>() external ffi.Pointer get_entity_name_at( ffi.Pointer sceneManager, int target, @@ -913,41 +925,41 @@ external void thermion_flutter_free( @ffi.Native< ffi.Void Function( ffi.Pointer, - ffi.Int, + EntityId, ffi.Pointer< ffi.NativeFunction< - ffi.Void Function(ffi.Int entityId1, ffi.Int entityId2)>>, + ffi.Void Function(EntityId entityId1, EntityId entityId2)>>, ffi.Bool)>() external void add_collision_component( ffi.Pointer sceneManager, int entityId, ffi.Pointer< ffi.NativeFunction< - ffi.Void Function(ffi.Int entityId1, ffi.Int entityId2)>> + ffi.Void Function(EntityId entityId1, EntityId entityId2)>> callback, bool affectsCollidingTransform, ); -@ffi.Native, ffi.Int)>() +@ffi.Native, EntityId)>() external void remove_collision_component( ffi.Pointer sceneManager, int entityId, ); -@ffi.Native, ffi.Int)>() +@ffi.Native, EntityId)>() external bool add_animation_component( ffi.Pointer sceneManager, int entityId, ); -@ffi.Native, ffi.Int)>() +@ffi.Native, EntityId)>() external void remove_animation_component( ffi.Pointer sceneManager, int entityId, ); @ffi.Native< - ffi.Int Function(ffi.Pointer, ffi.Pointer, ffi.Int, + EntityId Function(ffi.Pointer, ffi.Pointer, ffi.Int, ffi.Pointer, ffi.Int, ffi.Int, ffi.Pointer)>() external int create_geometry( ffi.Pointer viewer, @@ -959,36 +971,44 @@ external int create_geometry( ffi.Pointer materialPath, ); -@ffi.Native, ffi.Int)>() +@ffi.Native, EntityId)>() external int get_parent( ffi.Pointer sceneManager, int child, ); -@ffi.Native, ffi.Int, ffi.Int)>() +@ffi.Native< + ffi.Void Function(ffi.Pointer, EntityId, EntityId, ffi.Bool)>() external void set_parent( ffi.Pointer sceneManager, int child, int parent, + bool preserveScaling, ); -@ffi.Native, ffi.Int)>() +@ffi.Native, EntityId)>() external void test_collisions( ffi.Pointer sceneManager, int entity, ); -@ffi.Native, ffi.Int, ffi.Int)>() +@ffi.Native, EntityId, ffi.Int)>() external void set_priority( ffi.Pointer sceneManager, int entityId, int priority, ); -@ffi.Native, ffi.Pointer)>() +@ffi.Native, ffi.Pointer)>() external void get_gizmo( ffi.Pointer sceneManager, - ffi.Pointer out, + ffi.Pointer out, +); + +@ffi.Native, EntityId)>() +external Aabb2 get_bounding_box( + ffi.Pointer sceneManager, + int entity, ); @ffi.Native< @@ -1499,6 +1519,27 @@ typedef LoadFilamentResourceIntoOutPointerFunction = ffi.Void Function( ffi.Pointer uri, ffi.Pointer out); typedef DartLoadFilamentResourceIntoOutPointerFunction = void Function( ffi.Pointer uri, ffi.Pointer out); + +final class Aabb2 extends ffi.Struct { + @ffi.Float() + external double minX; + + @ffi.Float() + external double minY; + + @ffi.Float() + external double maxX; + + @ffi.Float() + external double maxY; +} + +/// This header replicates most of the methods in ThermionDartApi.h. +/// It represents the interface for: +/// - invoking those methods that must be called on the main Filament engine thread +/// - setting up a render loop +typedef EntityId = ffi.Int32; +typedef DartEntityId = int; typedef _ManipulatorMode = ffi.Int32; typedef Dart_ManipulatorMode = int; typedef FilamentRenderCallback @@ -1508,13 +1549,6 @@ typedef FilamentRenderCallbackFunction = ffi.Void Function( typedef DartFilamentRenderCallbackFunction = void Function( ffi.Pointer owner); -/// This header replicates most of the methods in ThermionDartApi.h. -/// It represents the interface for: -/// - invoking those methods that must be called on the main Filament engine thread -/// - setting up a render loop -typedef EntityId = ffi.Int32; -typedef DartEntityId = int; - const int __bool_true_false_are_defined = 1; const int true1 = 1; diff --git a/thermion_dart/lib/thermion_dart/entities/abstract_gizmo.dart b/thermion_dart/lib/thermion_dart/entities/abstract_gizmo.dart index b28b04f6..30d5e5c1 100644 --- a/thermion_dart/lib/thermion_dart/entities/abstract_gizmo.dart +++ b/thermion_dart/lib/thermion_dart/entities/abstract_gizmo.dart @@ -1,3 +1,23 @@ +import 'package:thermion_dart/thermion_dart/thermion_viewer.dart'; +import 'package:vector_math/vector_math_64.dart'; + +abstract class AbstractGizmo { + bool get isActive; + + void translate(double transX, double transY); + + void reset(); + + void attach(ThermionEntity entity); + + void detach(); + + Aabb2 boundingBox = Aabb2(); + + void checkHover(double x, double y) { + if(boundingBox.containsVector2(Vector2(x, y))); + } +} diff --git a/thermion_dart/lib/thermion_dart/entities/gizmo.dart b/thermion_dart/lib/thermion_dart/entities/gizmo.dart index ccfb0019..0434fe84 100644 --- a/thermion_dart/lib/thermion_dart/entities/gizmo.dart +++ b/thermion_dart/lib/thermion_dart/entities/gizmo.dart @@ -1,16 +1,18 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; +import 'package:thermion_dart/thermion_dart/entities/abstract_gizmo.dart'; import 'package:vector_math/vector_math_64.dart'; import '../thermion_viewer.dart'; class Gizmo extends AbstractGizmo { final ThermionEntity x; - Vector3 _x = Vector3(0.1, 0, 0); final ThermionEntity y; - Vector3 _y = Vector3(0.0, 0.1, 0); final ThermionEntity z; - Vector3 _z = Vector3(0.0, 0.0, 0.1); - final ThermionViewer controller; + final ThermionEntity center; + + final ThermionViewer _viewer; ThermionEntity? _activeAxis; ThermionEntity? _activeEntity; @@ -18,29 +20,41 @@ class Gizmo extends AbstractGizmo { final Set ignore; - Gizmo(this.x, this.y, this.z, this.controller, + Aabb2 boundingBox = Aabb2(); + + Gizmo(this.x, this.y, this.z, this.center, this._viewer, {this.ignore = const {}}) { - controller.pickResult.listen(_onPickResult); + _viewer.pickResult.listen(_onPickResult); } Future _reveal() async { - await controller.reveal(x, null); - await controller.reveal(y, null); - await controller.reveal(z, null); + await _viewer.reveal(x, null); + await _viewer.reveal(y, null); + await _viewer.reveal(z, null); + await _viewer.reveal(center, null); } + final _stopwatch = Stopwatch(); + + var _translation = Vector3.zero(); + void translate(double transX, double transY) async { - late Vector3 vec; - if (_activeAxis == x) { - vec = _x; - } else if (_activeAxis == y) { - vec = _y; - } else if (_activeAxis == z) { - vec = _z; + if (!_stopwatch.isRunning) { + _stopwatch.start(); + } + if (_activeAxis == x) { + _translation += Vector3(transX, 0.0, 0.0); + } else { + _translation += Vector3(0.0, transY, 0.0); + } + + if (_stopwatch.elapsedMilliseconds > 16) { + await _viewer.queuePositionUpdate( + _activeEntity!, _translation.x, _translation.y, _translation.z, + relative: true); + _stopwatch.reset(); + _translation = Vector3.zero(); } - await controller.queuePositionUpdate( - _activeEntity!, transX * vec.x, -transY * vec.y, -transX * vec.z, - relative: true); } void reset() { @@ -63,17 +77,22 @@ class Gizmo extends AbstractGizmo { _activeAxis = null; _activeEntity = entity; await _reveal(); - await controller.setParent(x, entity); - await controller.setParent(y, entity); - await controller.setParent(z, entity); + + await _viewer.setParent(x, entity); + await _viewer.setParent(y, entity); + await _viewer.setParent(z, entity); + await _viewer.setParent(center, entity); + boundingBox = await _viewer.getBoundingBox(x); } void detach() async { - await controller.setParent(x, 0); - await controller.setParent(y, 0); - await controller.setParent(z, 0); - await controller.hide(x, null); - await controller.hide(y, null); - await controller.hide(z, null); + await _viewer.setParent(x, 0); + await _viewer.setParent(y, 0); + await _viewer.setParent(z, 0); + await _viewer.setParent(center, 0); + await _viewer.hide(x, null); + await _viewer.hide(y, null); + await _viewer.hide(z, null); + await _viewer.hide(center, null); } } diff --git a/thermion_dart/lib/thermion_dart/thermion_viewer.dart b/thermion_dart/lib/thermion_dart/thermion_viewer.dart index c3569657..14597a90 100644 --- a/thermion_dart/lib/thermion_dart/thermion_viewer.dart +++ b/thermion_dart/lib/thermion_dart/thermion_viewer.dart @@ -1,6 +1,7 @@ import 'dart:math'; import 'dart:typed_data'; +import 'package:thermion_dart/thermion_dart/entities/abstract_gizmo.dart'; import 'package:thermion_dart/thermion_dart/scene.dart'; import 'package:vector_math/vector_math_64.dart'; import 'dart:async'; @@ -55,7 +56,6 @@ class TextureDetails { } abstract class ThermionViewer { - Future get initialized; /// @@ -719,7 +719,7 @@ abstract class ThermionViewer { /// /// Sets the parent transform of [child] to [parent]. /// - Future setParent(ThermionEntity child, ThermionEntity parent); + Future setParent(ThermionEntity child, ThermionEntity parent, { bool preserveScaling }); /// /// Test all collidable entities against this entity to see if any have collided. @@ -738,7 +738,7 @@ abstract class ThermionViewer { Scene get scene; /// - /// + /// The gizmo for translating/rotating objects. Only one gizmo is present in the scene. /// AbstractGizmo? get gizmo; @@ -746,16 +746,10 @@ abstract class ThermionViewer { /// Register a callback to be invoked when this viewer is disposed. /// void onDispose(Future Function() callback); + + /// + /// Gets the 2D bounding box (in viewport coordinates) for the given entity. + /// + Future getBoundingBox(ThermionEntity entity); } -abstract class AbstractGizmo { - bool get isActive; - - void translate(double transX, double transY); - - void reset(); - - void attach(ThermionEntity entity); - - void detach(); -} diff --git a/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart b/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart index e22157a5..77a8776d 100644 --- a/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart +++ b/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart @@ -6,6 +6,7 @@ import 'package:thermion_dart/thermion_dart/compatibility/compatibility.dart'; import 'package:thermion_dart/thermion_dart/entities/gizmo.dart'; import 'package:thermion_dart/thermion_dart/scene.dart'; import 'package:vector_math/vector_math_64.dart'; +import 'package:vector_math/vector_math_64.dart' as v64; import 'thermion_viewer.dart'; import 'scene_impl.dart'; import 'package:logging/logging.dart'; @@ -67,9 +68,9 @@ class ThermionViewerFFI extends ThermionViewer { this._driver = driver ?? nullptr; this._sharedContext = sharedContext ?? nullptr; try { - _onPickResultCallable = - NativeCallable.listener( - _onPickResult); + _onPickResultCallable = NativeCallable< + Void Function( + EntityId entityId, Int x, Int y)>.listener(_onPickResult); } catch (err) { _logger.severe( "Failed to set pick result callback. This is expected if running on web/wasm"); @@ -127,9 +128,9 @@ class ThermionViewerFFI extends ThermionViewer { await setCameraManipulatorOptions(zoomSpeed: 1.0); - final out = allocator(3); + final out = allocator(4); get_gizmo(_sceneManager!, out); - _gizmo = Gizmo(out[0], out[1], out[2], this); + _gizmo = Gizmo(out[0], out[1], out[2], out[3], this); allocator.free(out); this._initialized.complete(true); @@ -408,7 +409,7 @@ class ThermionViewerFFI extends ThermionViewer { @override Future> getInstances(ThermionEntity entity) async { var count = await getInstanceCount(entity); - var out = allocator(count); + var out = allocator(count); get_instances(_sceneManager!, entity, out); var instances = []; for (int i = 0; i < count; i++) { @@ -1364,7 +1365,7 @@ class ThermionViewerFFI extends ThermionViewer { _scene!.registerSelected(entityId); } - late NativeCallable + late NativeCallable _onPickResultCallable; /// @@ -1553,7 +1554,7 @@ class ThermionViewerFFI extends ThermionViewer { Future> getChildEntities( ThermionEntity parent, bool renderableOnly) async { var count = get_entity_count(_sceneManager!, parent, renderableOnly); - var out = allocator(count); + var out = allocator(count); get_entities(_sceneManager!, parent, renderableOnly, out); var outList = List.generate(count, (index) => out[index]).cast(); @@ -1612,9 +1613,9 @@ class ThermionViewerFFI extends ThermionViewer { // ignore: sdk_version_since if (callback != null) { - var ptr = - NativeCallable.listener( - callback); + var ptr = NativeCallable< + Void Function( + EntityId entityId1, EntityId entityId2)>.listener(callback); add_collision_component( _sceneManager!, entity, ptr.nativeFunction, affectsTransform); _collisions[entity] = ptr; @@ -1699,11 +1700,11 @@ class ThermionViewerFFI extends ThermionViewer { /// /// @override - Future setParent(ThermionEntity child, ThermionEntity parent) async { + Future setParent(ThermionEntity child, ThermionEntity parent, { bool preserveScaling = false}) async { if (_sceneManager == null) { throw Exception("Asset manager must be non-null"); } - set_parent(_sceneManager!, child, parent); + set_parent(_sceneManager!, child, parent, preserveScaling); } /// @@ -1736,4 +1737,11 @@ class ThermionViewerFFI extends ThermionViewer { Future setPriority(ThermionEntity entityId, int priority) async { set_priority(_sceneManager!, entityId, priority); } + + @override + Future getBoundingBox(ThermionEntity entityId) async { + final result = get_bounding_box(_sceneManager!, entityId); + return v64.Aabb2.minMax(v64.Vector2(result.minX, result.minY), + v64.Vector2(result.maxX, result.maxY)); + } } diff --git a/thermion_dart/lib/thermion_dart/thermion_viewer_stub.dart b/thermion_dart/lib/thermion_dart/thermion_viewer_stub.dart index 9f09e2c0..ad4dc644 100644 --- a/thermion_dart/lib/thermion_dart/thermion_viewer_stub.dart +++ b/thermion_dart/lib/thermion_dart/thermion_viewer_stub.dart @@ -1,6 +1,7 @@ import 'dart:math'; import 'dart:typed_data'; +import 'package:thermion_dart/thermion_dart/entities/abstract_gizmo.dart'; import 'package:thermion_dart/thermion_dart/scene.dart'; import 'package:thermion_dart/thermion_dart/thermion_viewer.dart'; import 'package:vector_math/vector_math_64.dart'; @@ -608,7 +609,7 @@ class ThermionViewerStub extends ThermionViewer { } @override - Future setParent(ThermionEntity child, ThermionEntity parent) { + Future setParent(ThermionEntity child, ThermionEntity parent, { bool preserveScaling = false}) { // TODO: implement setParent throw UnimplementedError(); } @@ -757,4 +758,10 @@ class ThermionViewerStub extends ThermionViewer { // TODO: implement capture throw UnimplementedError(); } + + @override + Future getBoundingBox(ThermionEntity entity) { + // TODO: implement getBoundingBox + throw UnimplementedError(); + } } diff --git a/thermion_dart/native/include/Aabb2.h b/thermion_dart/native/include/Aabb2.h new file mode 100644 index 00000000..ba1196ec --- /dev/null +++ b/thermion_dart/native/include/Aabb2.h @@ -0,0 +1,18 @@ +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif +struct Aabb2 { + float minX; + float minY; + float maxX; + float maxY; +}; + +typedef struct Aabb2 Aabb2; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/thermion_dart/native/include/Gizmo.hpp b/thermion_dart/native/include/Gizmo.hpp new file mode 100644 index 00000000..b138f3ec --- /dev/null +++ b/thermion_dart/native/include/Gizmo.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "material/gizmo.h" + +#include "Aabb2.h" + +using namespace filament; +using namespace utils; + + +class Gizmo { + public: + Gizmo(Engine* const engine); + void updateTransform(); + void destroy(Engine* const engine); + Entity x() { + return _entities[0]; + }; + Entity y() { + return _entities[1]; + }; + Entity z() { + return _entities[2]; + }; + Entity center() { + return _entities[3]; + }; + private: + utils::Entity _entities[4]; + Material* _material; + MaterialInstance* _materialInstances[4]; +}; \ No newline at end of file diff --git a/thermion_dart/native/include/SceneManager.hpp b/thermion_dart/native/include/SceneManager.hpp index 4a5bdf41..70882f2c 100644 --- a/thermion_dart/native/include/SceneManager.hpp +++ b/thermion_dart/native/include/SceneManager.hpp @@ -6,6 +6,8 @@ #include #include +#include +#include #include #include @@ -17,11 +19,13 @@ #include "material/gizmo.h" #include "utils/NameComponentManager.h" +#include "Gizmo.hpp" #include "ResourceBuffer.hpp" #include "components/CollisionComponentManager.hpp" #include "components/AnimationComponentManager.hpp" #include "tsl/robin_map.h" +#include "Aabb2.h" namespace thermion_filament { @@ -37,7 +41,8 @@ namespace thermion_filament class SceneManager { public: - SceneManager(const ResourceLoaderWrapperImpl *const loader, + SceneManager(View* view, + const ResourceLoaderWrapperImpl *const loader, Engine *engine, Scene *scene, const char *uberArchivePath); @@ -146,7 +151,7 @@ namespace thermion_filament void addCollisionComponent(EntityId entity, void (*onCollisionCallback)(const EntityId entityId1, const EntityId entityId2), bool affectsCollidingTransform); void removeCollisionComponent(EntityId entityId); EntityId getParent(EntityId child); - void setParent(EntityId child, EntityId parent); + void setParent(EntityId child, EntityId parent, bool preserveScaling); bool addAnimationComponent(EntityId entity); void removeAnimationComponent(EntityId entity); @@ -165,11 +170,18 @@ namespace thermion_filament void setPriority(EntityId entity, int priority); /// @brief returns the gizmo entity, used to manipulate the global transform for entities - /// @param out a pointer to an array of three EntityId {x, y, z} + /// @param out a pointer sized large enough to hold three EntityId values (representing the x, y, and z axis of the translation gizmo). /// @return /// void getGizmo(EntityId *out); + + /// @brief returns the 2D min/max viewport coordinates of the bounding box for the specified enitty; + /// @param out a pointer large enough to store four floats (the min/max coordinates of the bounding box) + /// @return + /// + Aabb2 getBoundingBox(EntityId entity); + friend class FilamentViewer; private: @@ -177,6 +189,7 @@ namespace thermion_filament const ResourceLoaderWrapperImpl *const _resourceLoaderWrapper; Engine *_engine; Scene *_scene; + View* _view; gltfio::MaterialProvider *_ubershaderProvider = nullptr; gltfio::ResourceLoader *_gltfResourceLoader = nullptr; gltfio::TextureProvider *_stbDecoder = nullptr; @@ -203,9 +216,8 @@ namespace thermion_filament const char *entityName); EntityId addGizmo(); - utils::Entity _gizmo[3]; - Material* _gizmoMaterial; - MaterialInstance* _gizmoMaterialInstances[3]; + Gizmo* _gizmo = nullptr; + }; } diff --git a/thermion_dart/native/include/ThermionDartApi.h b/thermion_dart/native/include/ThermionDartApi.h index 4a7c4c76..c6c1226a 100644 --- a/thermion_dart/native/include/ThermionDartApi.h +++ b/thermion_dart/native/include/ThermionDartApi.h @@ -47,6 +47,7 @@ #endif #include "ResourceBuffer.hpp" +#include "Aabb2.h" typedef int32_t EntityId; typedef int32_t _ManipulatorMode; @@ -244,10 +245,11 @@ extern "C" EMSCRIPTEN_KEEPALIVE EntityId create_geometry(void *const viewer, float *vertices, int numVertices, uint16_t *indices, int numIndices, int primitiveType, const char *materialPath); EMSCRIPTEN_KEEPALIVE EntityId get_parent(void *const sceneManager, EntityId child); - EMSCRIPTEN_KEEPALIVE void set_parent(void *const sceneManager, EntityId child, EntityId parent); + EMSCRIPTEN_KEEPALIVE void set_parent(void *const sceneManager, EntityId child, EntityId parent, bool preserveScaling); EMSCRIPTEN_KEEPALIVE void test_collisions(void *const sceneManager, EntityId entity); EMSCRIPTEN_KEEPALIVE void set_priority(void *const sceneManager, EntityId entityId, int priority); EMSCRIPTEN_KEEPALIVE void get_gizmo(void *const sceneManager, EntityId *out); + EMSCRIPTEN_KEEPALIVE Aabb2 get_bounding_box(void *const sceneManager, EntityId entity); #ifdef __cplusplus } diff --git a/thermion_dart/native/src/Gizmo.cpp b/thermion_dart/native/src/Gizmo.cpp new file mode 100644 index 00000000..9544699c --- /dev/null +++ b/thermion_dart/native/src/Gizmo.cpp @@ -0,0 +1,199 @@ +#include "Gizmo.hpp" + +#include +#include +#include +#include +#include + +#include "material/gizmo.h" +#include "Log.hpp" + +Gizmo::Gizmo(Engine* const engine) +{ + _material = + Material::Builder() + .package(GIZMO_GIZMO_DATA, GIZMO_GIZMO_SIZE) + .build(*engine); + + // Line and arrow vertices + float lineLength = 0.8f; + float lineWidth = 0.005f; + float arrowLength = 0.2f; + float arrowWidth = 0.03f; + float *vertices = new float[13 * 3]{ + // Line vertices (8 vertices) + -lineWidth, -lineWidth, 0.0f, + lineWidth, -lineWidth, 0.0f, + lineWidth, lineWidth, 0.0f, + -lineWidth, lineWidth, 0.0f, + -lineWidth, -lineWidth, lineLength, + lineWidth, -lineWidth, lineLength, + lineWidth, lineWidth, lineLength, + -lineWidth, lineWidth, lineLength, + // Arrow vertices (5 vertices) + 0.0f, 0.0f, lineLength + arrowLength, // Tip of the arrow + -arrowWidth, -arrowWidth, lineLength, // Base of the arrow + arrowWidth, -arrowWidth, lineLength, + arrowWidth, arrowWidth, lineLength, + -arrowWidth, arrowWidth, lineLength}; + + // Line and arrow indices + uint16_t *indices = new uint16_t[54]{ + // Line indices (24 indices) + 0, 1, 5, 5, 4, 0, + 1, 2, 6, 6, 5, 1, + 2, 3, 7, 7, 6, 2, + 3, 0, 4, 4, 7, 3, + // Arrow indices (30 indices) + 8, 9, 10, // Front face + 8, 10, 11, // Right face + 8, 11, 12, // Back face + 8, 12, 9, // Left face + 9, 12, 11, 11, 10, 9 // Base of the arrow + }; + + auto vb = VertexBuffer::Builder() + .vertexCount(13) + .bufferCount(1) + .attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3) + .build(*engine); + + vb->setBufferAt(*engine, 0, VertexBuffer::BufferDescriptor(vertices, 13 * sizeof(filament::math::float3), [](void *buffer, size_t size, void *) + { delete[] static_cast(buffer); })); + + auto ib = IndexBuffer::Builder().indexCount(54).bufferType(IndexBuffer::IndexType::USHORT).build(*engine); + ib->setBuffer(*engine, IndexBuffer::BufferDescriptor( + indices, 54 * sizeof(uint16_t), + [](void *buffer, size_t size, void *) + { delete[] static_cast(buffer); })); + + auto &entityManager = EntityManager::get(); + + // Create the three axes + for (int i = 0; i < 3; i++) + { + _entities[i] = entityManager.create(); + _materialInstances[i] = _material->createInstance(); + + math::float3 color; + math::mat4f transform; + + switch (i) + { + case 0: // X-axis (Red) + color = {1.0f, 0.0f, 0.0f}; + transform = math::mat4f::rotation(math::F_PI_2, math::float3{0, 1, 0}); + break; + case 1: // Y-axis (Green) + color = {0.0f, 1.0f, 0.0f}; + transform = math::mat4f::rotation(-math::F_PI_2, math::float3{1, 0, 0}); + break; + case 2: // Z-axis (Blue) + color = {0.0f, 0.0f, 1.0f}; + break; + } + + _materialInstances[i]->setParameter("color", color); + + RenderableManager::Builder(1) + .boundingBox({{0, 0, (lineLength + arrowLength) / 2}, {arrowWidth / 2, arrowWidth / 2, (lineLength + arrowLength) / 2}}) + .material(0, _materialInstances[i]) + .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb, ib, 0, 54) + .culling(false) + .receiveShadows(false) + .castShadows(false) + .build(*engine, _entities[i]); + + auto &transformManager = engine->getTransformManager(); + auto instance = transformManager.getInstance(_entities[i]); + transformManager.setTransform(instance, transform); + } + + // Create the black cube (center cube) + _entities[3] = entityManager.create(); + _materialInstances[3] = _material->createInstance(); + _materialInstances[3]->setParameter("color", math::float3{0.0f, 0.0f, 0.0f}); // Black color + + // Create center cube vertices + float centerCubeSize = 0.05f; + float *centerCubeVertices = new float[8 * 3]{ + -centerCubeSize, -centerCubeSize, -centerCubeSize, + centerCubeSize, -centerCubeSize, -centerCubeSize, + centerCubeSize, centerCubeSize, -centerCubeSize, + -centerCubeSize, centerCubeSize, -centerCubeSize, + -centerCubeSize, -centerCubeSize, centerCubeSize, + centerCubeSize, -centerCubeSize, centerCubeSize, + centerCubeSize, centerCubeSize, centerCubeSize, + -centerCubeSize, centerCubeSize, centerCubeSize}; + + // Create center cube indices + uint16_t *centerCubeIndices = new uint16_t[36]{ + 0, 1, 2, 2, 3, 0, + 1, 5, 6, 6, 2, 1, + 5, 4, 7, 7, 6, 5, + 4, 0, 3, 3, 7, 4, + 3, 2, 6, 6, 7, 3, + 4, 5, 1, 1, 0, 4}; + + auto centerCubeVb = VertexBuffer::Builder() + .vertexCount(8) + .bufferCount(1) + .attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3) + .build(*engine); + + centerCubeVb->setBufferAt(*engine, 0, VertexBuffer::BufferDescriptor(centerCubeVertices, 8 * sizeof(filament::math::float3), [](void *buffer, size_t size, void *) + { delete[] static_cast(buffer); })); + + auto centerCubeIb = IndexBuffer::Builder().indexCount(36).bufferType(IndexBuffer::IndexType::USHORT).build(*engine); + centerCubeIb->setBuffer(*engine, IndexBuffer::BufferDescriptor( + centerCubeIndices, 36 * sizeof(uint16_t), + [](void *buffer, size_t size, void *) + { delete[] static_cast(buffer); })); + + RenderableManager::Builder(1) + .boundingBox({{}, {centerCubeSize, centerCubeSize, centerCubeSize}}) + .material(0, _materialInstances[3]) + .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, centerCubeVb, centerCubeIb, 0, 36) + .culling(false) + .build(*engine, _entities[3]); + + auto &rm = engine->getRenderableManager(); + for (int i = 0; i < 4; i++) + { + rm.setPriority(rm.getInstance(_entities[i]), 7); + } + +} + +void Gizmo::destroy(Engine *const engine) { + + for (int i = 0; i < 4; i++) + { + engine->destroy(_entities[i]); + engine->destroy(_materialInstances[i]); + } + + engine->destroy(_material); +} + +void Gizmo::updateTransform() +{ + Log("Updating gizmo transform"); + // // Get screen-space position of the entity + // math::float4 entityScreenPos = _view->getViewProjectionMatrix() * entityWorldPosition; + + // // Convert to NDC space + // entityScreenPos /= entityScreenPos.w; + + // // Convert to screen space + // float screenX = (entityScreenPos.x * 0.5f + 0.5f) * viewportWidth; + // float screenY = (entityScreenPos.y * 0.5f + 0.5f) * viewportHeight; + + // // Set gizmo position + // gizmo->setPosition({screenX, screenY, 0}); + + // // Scale gizmo based on viewport size + // float scale = viewportHeight * 0.1f; // 10% of screen height, for example + // gizmo->setScale({scale, scale, 1}); +} \ No newline at end of file diff --git a/thermion_dart/native/src/SceneManager.cpp b/thermion_dart/native/src/SceneManager.cpp index 7fe0eeea..1f4da40a 100644 --- a/thermion_dart/native/src/SceneManager.cpp +++ b/thermion_dart/native/src/SceneManager.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -43,11 +44,13 @@ namespace thermion_filament using namespace filament::gltfio; using std::unique_ptr; - SceneManager::SceneManager(const ResourceLoaderWrapperImpl *const resourceLoaderWrapper, + SceneManager::SceneManager(View *view, + const ResourceLoaderWrapperImpl *const resourceLoaderWrapper, Engine *engine, Scene *scene, const char *uberArchivePath) - : _resourceLoaderWrapper(resourceLoaderWrapper), + : _view(view), + _resourceLoaderWrapper(resourceLoaderWrapper), _engine(engine), _scene(scene) { @@ -76,9 +79,9 @@ namespace thermion_filament utils::EntityManager &em = utils::EntityManager::get(); _ncm = new NameComponentManager(em); - + _assetLoader = AssetLoader::create({_engine, _ubershaderProvider, _ncm, &em}); - + _gltfResourceLoader->addTextureProvider("image/ktx2", _ktxDecoder); _gltfResourceLoader->addTextureProvider("image/png", _stbDecoder); _gltfResourceLoader->addTextureProvider("image/jpeg", _stbDecoder); @@ -87,24 +90,20 @@ namespace thermion_filament _collisionComponentManager = new CollisionComponentManager(tm); _animationComponentManager = new AnimationComponentManager(tm, _engine->getRenderableManager()); - - addGizmo(); + + _gizmo = new Gizmo(_engine); } SceneManager::~SceneManager() { - + destroyAll(); - - for(int i =0; i < 3; i++) { - _engine->destroy(_gizmo[i]); - _engine->destroy(_gizmoMaterialInstances[i]); - } - - _engine->destroy(_gizmoMaterial); + + _gizmo->destroy(_engine); + _gltfResourceLoader->asyncCancelLoad(); _ubershaderProvider->destroyMaterials(); - + delete _animationComponentManager; delete _collisionComponentManager; delete _ncm; @@ -114,7 +113,6 @@ namespace thermion_filament delete _ktxDecoder; delete _ubershaderProvider; AssetLoader::destroy(&_assetLoader); - } int SceneManager::getInstanceCount(EntityId entityId) @@ -454,7 +452,8 @@ namespace thermion_filament for (auto &asset : _assets) { auto numInstances = asset.second->getAssetInstanceCount(); - for(int i = 0; i < numInstances; i++) { + for (int i = 0; i < numInstances; i++) + { auto instance = asset.second->getAssetInstances()[i]; for (int j = 0; j < instance->getEntityCount(); j++) { @@ -499,21 +498,24 @@ namespace thermion_filament return pos->second; } - math::mat4f SceneManager::getLocalTransform(EntityId entityId) { + math::mat4f SceneManager::getLocalTransform(EntityId entityId) + { auto entity = Entity::import(entityId); - auto& tm = _engine->getTransformManager(); - auto transformInstance = tm.getInstance(entity); + auto &tm = _engine->getTransformManager(); + auto transformInstance = tm.getInstance(entity); return tm.getTransform(transformInstance); } - math::mat4f SceneManager::getWorldTransform(EntityId entityId) { + math::mat4f SceneManager::getWorldTransform(EntityId entityId) + { auto entity = Entity::import(entityId); - auto& tm = _engine->getTransformManager(); - auto transformInstance = tm.getInstance(entity); + auto &tm = _engine->getTransformManager(); + auto transformInstance = tm.getInstance(entity); return tm.getWorldTransform(transformInstance); } - EntityId SceneManager::getBone(EntityId entityId, int skinIndex, int boneIndex) { + EntityId SceneManager::getBone(EntityId entityId, int skinIndex, int boneIndex) + { auto *instance = getInstanceByEntityId(entityId); if (!instance) { @@ -533,7 +535,8 @@ namespace thermion_filament return Entity::smuggle(joint); } - math::mat4f SceneManager::getInverseBindMatrix(EntityId entityId, int skinIndex, int boneIndex) { + math::mat4f SceneManager::getInverseBindMatrix(EntityId entityId, int skinIndex, int boneIndex) + { auto *instance = getInstanceByEntityId(entityId); if (!instance) { @@ -552,7 +555,6 @@ namespace thermion_filament return inverseBindMatrix; } - bool SceneManager::setBoneTransform(EntityId entityId, int32_t skinIndex, int boneIndex, math::mat4f transform) { std::lock_guard lock(_mutex); @@ -781,7 +783,7 @@ namespace thermion_filament return true; } - void SceneManager::clearMorphAnimationBuffer( + void SceneManager::clearMorphAnimationBuffer( EntityId entityId) { std::lock_guard lock(_mutex); @@ -865,12 +867,12 @@ namespace thermion_filament TransformManager &transformManager = _engine->getTransformManager(); // - // To reset the skeleton to its rest pose, we could just call animator->resetBoneMatrices(), - // which sets all bone matrices to the identity matrix. However, any subsequent calls to animator->updateBoneMatrices() - // may result in unexpected poses, because that method uses each bone's transform to calculate + // To reset the skeleton to its rest pose, we could just call animator->resetBoneMatrices(), + // which sets all bone matrices to the identity matrix. However, any subsequent calls to animator->updateBoneMatrices() + // may result in unexpected poses, because that method uses each bone's transform to calculate // the bone matrices (and resetBoneMatrices does not affect this transform). // To "fully" reset the bone, we need to set its local transform (i.e. relative to its parent) - // to its original orientation in rest pose. + // to its original orientation in rest pose. // // This can be calculated as: // @@ -879,7 +881,7 @@ namespace thermion_filament // (where bindMatrix is the inverse of the inverseBindMatrix). // // The only requirement is that parent bone transforms are reset before child bone transforms. - // glTF/Filament does not guarantee that parent bones are listed before child bones under a FilamentInstance. + // glTF/Filament does not guarantee that parent bones are listed before child bones under a FilamentInstance. // We ensure that parents are reset before children by: // - pushing all bones onto a stack // - iterate over the stack @@ -891,16 +893,16 @@ namespace thermion_filament // - pop the bone, reset its transform and mark it as completed for (int skinIndex = 0; skinIndex < skinCount; skinIndex++) { - std::unordered_set joints; - std::unordered_set completed; + std::unordered_set joints; + std::unordered_set completed; std::stack stack; auto transforms = getBoneRestTranforms(entityId, skinIndex); - + for (int i = 0; i < instance->getJointCountAt(skinIndex); i++) { auto restTransform = transforms->at(i); - const auto& joint = instance->getJointsAt(skinIndex)[i]; + const auto &joint = instance->getJointsAt(skinIndex)[i]; auto transformInstance = transformManager.getInstance(joint); transformManager.setTransform(transformInstance, restTransform); } @@ -908,7 +910,8 @@ namespace thermion_filament instance->getAnimator()->updateBoneMatrices(); } - std::unique_ptr> SceneManager::getBoneRestTranforms(EntityId entityId, int skinIndex) { + std::unique_ptr> SceneManager::getBoneRestTranforms(EntityId entityId, int skinIndex) + { auto transforms = std::make_unique>(); @@ -933,12 +936,12 @@ namespace thermion_filament transforms->resize(instance->getJointCountAt(skinIndex)); // - // To reset the skeleton to its rest pose, we could just call animator->resetBoneMatrices(), - // which sets all bone matrices to the identity matrix. However, any subsequent calls to animator->updateBoneMatrices() - // may result in unexpected poses, because that method uses each bone's transform to calculate + // To reset the skeleton to its rest pose, we could just call animator->resetBoneMatrices(), + // which sets all bone matrices to the identity matrix. However, any subsequent calls to animator->updateBoneMatrices() + // may result in unexpected poses, because that method uses each bone's transform to calculate // the bone matrices (and resetBoneMatrices does not affect this transform). // To "fully" reset the bone, we need to set its local transform (i.e. relative to its parent) - // to its original orientation in rest pose. + // to its original orientation in rest pose. // // This can be calculated as: // @@ -947,7 +950,7 @@ namespace thermion_filament // (where bindMatrix is the inverse of the inverseBindMatrix). // // The only requirement is that parent bone transforms are reset before child bone transforms. - // glTF/Filament does not guarantee that parent bones are listed before child bones under a FilamentInstance. + // glTF/Filament does not guarantee that parent bones are listed before child bones under a FilamentInstance. // We ensure that parents are reset before children by: // - pushing all bones onto a stack // - iterate over the stack @@ -958,22 +961,23 @@ namespace thermion_filament // - otherwise // - pop the bone, reset its transform and mark it as completed std::vector joints; - std::unordered_set completed; + std::unordered_set completed; std::stack stack; - + for (int i = 0; i < instance->getJointCountAt(skinIndex); i++) { - const auto& joint = instance->getJointsAt(skinIndex)[i]; + const auto &joint = instance->getJointsAt(skinIndex)[i]; joints.push_back(joint); stack.push(joint); } - while(!stack.empty()) + while (!stack.empty()) { - const auto& joint = stack.top(); + const auto &joint = stack.top(); // if we've already handled this node previously (e.g. when we encountered it as a parent), then skip - if(completed.find(joint) != completed.end()) { + if (completed.find(joint) != completed.end()) + { stack.pop(); continue; } @@ -981,23 +985,25 @@ namespace thermion_filament const auto transformInstance = transformManager.getInstance(joint); auto parent = transformManager.getParent(transformInstance); - // we need to handle parent joints before handling their children - // therefore, if this joint has a parent that hasn't been handled yet, + // we need to handle parent joints before handling their children + // therefore, if this joint has a parent that hasn't been handled yet, // push the parent to the top of the stack and start the loop again - const auto& jointIter = std::find(joints.begin(), joints.end(), joint); + const auto &jointIter = std::find(joints.begin(), joints.end(), joint); auto parentIter = std::find(joints.begin(), joints.end(), parent); - if(parentIter != joints.end() && completed.find(parent) == completed.end()) { + if (parentIter != joints.end() && completed.find(parent) == completed.end()) + { stack.push(parent); continue; } - - // otherwise let's get the inverse bind matrix for the joint + + // otherwise let's get the inverse bind matrix for the joint math::mat4f inverseBindMatrix; bool found = false; for (int i = 0; i < instance->getJointCountAt(skinIndex); i++) { - if(instance->getJointsAt(skinIndex)[i] == joint) { + if (instance->getJointsAt(skinIndex)[i] == joint) + { inverseBindMatrix = instance->getInverseBindMatricesAt(skinIndex)[i]; found = true; break; @@ -1007,7 +1013,8 @@ namespace thermion_filament // now we need to ascend back up the hierarchy to calculate the modelSpaceTransform math::mat4f modelSpaceTransform; - while(parentIter != joints.end()) { + while (parentIter != joints.end()) + { const auto transformInstance = transformManager.getInstance(parent); const auto parentIndex = distance(joints.begin(), parentIter); const auto transform = transforms->at(parentIndex); @@ -1016,9 +1023,9 @@ namespace thermion_filament parentIter = std::find(joints.begin(), joints.end(), parent); } - const auto bindMatrix = inverse(inverseBindMatrix); - - const auto inverseModelSpaceTransform = inverse(modelSpaceTransform); + const auto bindMatrix = inverse(inverseBindMatrix); + + const auto inverseModelSpaceTransform = inverse(modelSpaceTransform); const auto jointIndex = distance(joints.begin(), jointIter); transforms->at(jointIndex) = inverseModelSpaceTransform * bindMatrix; @@ -1028,7 +1035,8 @@ namespace thermion_filament return transforms; } - bool SceneManager::updateBoneMatrices(EntityId entityId) { + bool SceneManager::updateBoneMatrices(EntityId entityId) + { auto *instance = getInstanceByEntityId(entityId); if (!instance) { @@ -1046,11 +1054,13 @@ namespace thermion_filament return true; } - bool SceneManager::setTransform(EntityId entityId, math::mat4f transform) { - auto& tm = _engine->getTransformManager(); - const auto& entity = Entity::import(entityId); + bool SceneManager::setTransform(EntityId entityId, math::mat4f transform) + { + auto &tm = _engine->getTransformManager(); + const auto &entity = Entity::import(entityId); auto transformInstance = tm.getInstance(entity); - if(!transformInstance) { + if (!transformInstance) + { return false; } tm.setTransform(transformInstance, transform); @@ -1059,11 +1069,11 @@ namespace thermion_filament bool SceneManager::addBoneAnimation(EntityId parentEntity, int skinIndex, - int boneIndex, + int boneIndex, const float *const frameData, int numFrames, float frameLengthInMs, - float fadeOutInSecs, + float fadeOutInSecs, float fadeInInSecs, float maxDelta) { @@ -1122,7 +1132,8 @@ namespace thermion_filament animation.fadeInInSecs = fadeInInSecs; animation.maxDelta = maxDelta; animation.skinIndex = skinIndex; - if(!_animationComponentManager->hasComponent(instance->getRoot())) { + if (!_animationComponentManager->hasComponent(instance->getRoot())) + { Log("ERROR: specified entity is not animatable (has no animation component attached)."); return false; } @@ -1135,7 +1146,7 @@ namespace thermion_filament return true; } - + void SceneManager::playAnimation(EntityId entityId, int index, bool loop, bool reverse, bool replaceActive, float crossfade, float startOffset) { std::lock_guard lock(_mutex); @@ -1160,7 +1171,7 @@ namespace thermion_filament } } - if (!_animationComponentManager->hasComponent(instance->getRoot())) + if (!_animationComponentManager->hasComponent(instance->getRoot())) { Log("ERROR: specified entity is not animatable (has no animation component attached)."); return; @@ -1210,13 +1221,16 @@ namespace thermion_filament bool found = false; // don't play the animation if it's already running - for(int i=0; i < animationComponent.gltfAnimations.size(); i++) { - if(animationComponent.gltfAnimations[i].index == index) { + for (int i = 0; i < animationComponent.gltfAnimations.size(); i++) + { + if (animationComponent.gltfAnimations[i].index == index) + { found = true; break; } } - if(!found) { + if (!found) + { animationComponent.gltfAnimations.push_back(animation); } } @@ -1244,9 +1258,9 @@ namespace thermion_filament auto &animationComponent = _animationComponentManager->elementAt<0>(animationComponentInstance); auto erased = std::remove_if(animationComponent.gltfAnimations.begin(), - animationComponent.gltfAnimations.end(), - [=](GltfAnimation &anim) - { return anim.index == index; }); + animationComponent.gltfAnimations.end(), + [=](GltfAnimation &anim) + { return anim.index == index; }); animationComponent.gltfAnimations.erase(erased, animationComponent.gltfAnimations.end()); } @@ -1391,7 +1405,7 @@ namespace thermion_filament unique_ptr> names = std::make_unique>(); const auto *instance = getInstanceByEntityId(assetEntityId); - + if (!instance) { auto asset = getAssetByEntityId(assetEntityId); @@ -1401,7 +1415,8 @@ namespace thermion_filament return names; } instance = asset->getInstance(); - if(!instance) { + if (!instance) + { Log("Warning - failed to find instance for specified asset. This is unexpected and probably indicates you are passing the wrong entity"); return names; } @@ -1415,7 +1430,7 @@ namespace thermion_filament for (int i = 0; i < asset->getEntityCount(); i++) { - + utils::Entity e = entities[i]; if (e == target) { @@ -1431,7 +1446,8 @@ namespace thermion_filament return names; } - unique_ptr> SceneManager::getBoneNames(EntityId assetEntityId, int skinIndex) { + unique_ptr> SceneManager::getBoneNames(EntityId assetEntityId, int skinIndex) + { unique_ptr> names = std::make_unique>(); @@ -1468,7 +1484,6 @@ namespace thermion_filament return names; } - void SceneManager::transformToUnitCube(EntityId entityId) { const auto *instance = getInstanceByEntityId(entityId); @@ -1496,7 +1511,8 @@ namespace thermion_filament tm.setTransform(tm.getInstance(instance->getRoot()), transform); } - EntityId SceneManager::getParent(EntityId childEntityId) { + EntityId SceneManager::getParent(EntityId childEntityId) + { auto &tm = _engine->getTransformManager(); const auto child = Entity::import(childEntityId); const auto &childInstance = tm.getInstance(child); @@ -1504,7 +1520,7 @@ namespace thermion_filament return Entity::smuggle(parent); } - void SceneManager::setParent(EntityId childEntityId, EntityId parentEntityId) + void SceneManager::setParent(EntityId childEntityId, EntityId parentEntityId, bool preserveScaling) { auto &tm = _engine->getTransformManager(); const auto child = Entity::import(childEntityId); @@ -1512,6 +1528,31 @@ namespace thermion_filament const auto &parentInstance = tm.getInstance(parent); const auto &childInstance = tm.getInstance(child); + + if (preserveScaling) + { + auto parentTransform = tm.getWorldTransform(parentInstance); + math::float3 parentTranslation; + math::quatf parentRotation; + math::float3 parentScale; + + decomposeMatrix(parentTransform, &parentTranslation, &parentRotation, &parentScale); + + auto childTransform = tm.getTransform(childInstance); + math::float3 childTranslation; + math::quatf childRotation; + math::float3 childScale; + + decomposeMatrix(childTransform, &childTranslation, &childRotation, &childScale); + + childScale = childScale * (1 / parentScale); + + childTransform = composeMatrix(childTranslation, childRotation, childScale); + + tm.setTransform(childInstance, childTransform); + } + + // auto scale = childInstance. tm.setParent(childInstance, parentInstance); } @@ -1587,12 +1628,16 @@ namespace thermion_filament _animationComponentManager->update(); } + + void SceneManager::updateTransforms() { std::lock_guard lock(_mutex); auto &tm = _engine->getTransformManager(); + _gizmo->updateTransform(); + for (const auto &[entityId, transformUpdate] : _transformUpdates) { const auto &pos = _instances.find(entityId); @@ -1751,25 +1796,77 @@ namespace thermion_filament tm.setTransform(transformInstance, newTransform); } - void SceneManager::queuePositionUpdate(EntityId entity, float x, float y, float z, bool relative) + void SceneManager::queuePositionUpdate(EntityId entity, float x, float y, float z, bool relative) +{ + std::lock_guard lock(_mutex); + + const auto &pos = _transformUpdates.find(entity); + if (pos == _transformUpdates.end()) { - std::lock_guard lock(_mutex); - - const auto &pos = _transformUpdates.find(entity); - if (pos == _transformUpdates.end()) - { - _transformUpdates.emplace(entity, std::make_tuple(math::float3(), true, math::quatf(1.0f), true, 1.0f)); - } - auto curr = _transformUpdates[entity]; - auto &trans = std::get<0>(curr); - trans.x = x; - trans.y = y; - trans.z = z; - - auto &isRelative = std::get<1>(curr); - isRelative = relative; - _transformUpdates[entity] = curr; + _transformUpdates.emplace(entity, std::make_tuple(math::float3(), true, math::quatf(1.0f), true, 1.0f)); } + auto curr = _transformUpdates[entity]; + auto &trans = std::get<0>(curr); + + const auto &tm = _engine->getTransformManager(); + auto transformInstance = tm.getInstance(Entity::import(entity)); + auto transform = tm.getTransform(transformInstance); + math::double4 position { 0.0f, 0.0f, 0.0f, 1.0f}; + math::mat4 worldTransform = tm.getWorldTransformAccurate(transformInstance); + position = worldTransform * position; + + // Get camera's view matrix and its inverse + const Camera &camera = _view->getCamera(); + + math::mat4 viewMatrix = camera.getViewMatrix(); + math::mat4 invViewMatrix = inverse(viewMatrix); + + // Transform object position to view space + math::double4 viewSpacePos = viewMatrix * position; + + Log("viewSpacePos %f %f %f %f", viewSpacePos.x, viewSpacePos.y, viewSpacePos.z, viewSpacePos.w); + + // Calculate plane distance from camera + float planeDistance = -viewSpacePos.z; + + const auto &vp = _view->getViewport(); + + // Calculate viewport to world scale at the object's distance + float viewportToWorldScale = planeDistance * std::tan(camera.getFieldOfViewInDegrees(Camera::Fov::VERTICAL) * 0.5f * M_PI / 180.0f) * 2.0f / vp.height; + + Log("viewportToWorldScale %f", viewportToWorldScale); + + // Calculate view space delta + math::float4 viewSpaceDelta( + x * viewportToWorldScale, + -y * viewportToWorldScale, // Invert y-axis + z * viewportToWorldScale, + 0.0f); // Use 0 for the w component as it's a direction, not a position + + Log("viewSpaceDelta %f %f %f", viewSpaceDelta.x, viewSpaceDelta.y, viewSpaceDelta.z); + + // Transform delta to world space + math::float4 worldDelta = invViewMatrix * viewSpaceDelta; + + Log("worldDelta %f %f %f", worldDelta.x, worldDelta.y, worldDelta.z); + + if (relative) + { + trans.x += worldDelta.x; + trans.y += worldDelta.y; + trans.z += worldDelta.z; + } + else + { + trans.x = worldDelta.x; + trans.y = worldDelta.y; + trans.z = worldDelta.z; + } + + auto &isRelative = std::get<1>(curr); + isRelative = relative; + _transformUpdates[entity] = curr; +} void SceneManager::queueRotationUpdate(EntityId entity, float rads, float x, float y, float z, float w, bool relative) { @@ -2011,131 +2108,86 @@ namespace thermion_filament Log("Set instance renderable priority to %d", priority); } - EntityId SceneManager::addGizmo() + Aabb2 SceneManager::getBoundingBox(EntityId entityId) { - _gizmoMaterial = - Material::Builder() - .package(GIZMO_GIZMO_DATA, GIZMO_GIZMO_SIZE) - .build(*_engine); + const auto &camera = _view->getCamera(); + const auto &viewport = _view->getViewport(); - auto vertexCount = 9; + auto &tcm = _engine->getTransformManager(); + auto &rcm = _engine->getRenderableManager(); - float *vertices = new float[vertexCount * 3]{ - -0.05, 0.0f, 0.05f, - 0.05f, 0.0f, 0.05f, - 0.05f, 0.0f, -0.05f, - -0.05f, 0.0f, -0.05f, - -0.05f, 1.0f, 0.05f, - 0.05f, 1.0f, 0.05f, - 0.05f, 1.0f, -0.05f, - -0.05f, 1.0f, -0.05f, - 0.00f, 1.1f, 0.0f}; + // Get the projection and view matrices + math::mat4 projMatrix = camera.getProjectionMatrix(); + math::mat4 viewMatrix = camera.getViewMatrix(); + math::mat4 vpMatrix = projMatrix * viewMatrix; - VertexBuffer::BufferDescriptor::Callback vertexCallback = [](void *buf, size_t, - void *data) + auto entity = Entity::import(entityId); + + auto renderable = rcm.getInstance(entity); + auto worldTransform = tcm.getWorldTransform(tcm.getInstance(entity)); + + // Get the axis-aligned bounding box in model space + Box aabb = rcm.getAxisAlignedBoundingBox(renderable); + + auto min = aabb.getMin(); + auto max = aabb.getMax(); + + Log("min %f %f %f max %f %f %f", min.x, min.y, min.z, max.x, max.y, max.z); + + // Transform the 8 corners of the AABB to clip space + std::array corners = { + worldTransform * math::float4(min.x, min.y, min.z, 1.0f), + worldTransform * math::float4(max.x, min.y, min.z, 1.0f), + worldTransform * math::float4(min.x, max.y, min.z, 1.0f), + worldTransform * math::float4(max.x, max.y, min.z, 1.0f), + worldTransform * math::float4(min.x, min.y, max.z, 1.0f), + worldTransform * math::float4(max.x, min.y, max.z, 1.0f), + worldTransform * math::float4(min.x, max.y, max.z, 1.0f), + worldTransform * math::float4(max.x, max.y, max.z, 1.0f)}; + + // Project corners to clip space and convert to viewport space + float minX = std::numeric_limits::max(); + float minY = std::numeric_limits::max(); + float maxX = std::numeric_limits::lowest(); + float maxY = std::numeric_limits::lowest(); + + for (const auto &corner : corners) { - free((void *)buf); - }; - auto indexCount = 42; - uint16_t *indices = new uint16_t[indexCount]{ - // bottom quad - 0, 1, 2, - 0, 2, 3, - // top "cone" - 4, 5, 8, - 5, 6, 8, - 4, 7, 8, - 6, 7, 8, - // front - 0, 1, 4, - 1, 5, 4, - // right - 1, 2, 5, - 2, 6, 5, - // back - 2, 6, 7, - 7, 3, 2, - // left - 0, 4, 7, - 7, 3, 0 + math::float4 clipSpace = vpMatrix * corner; - }; + // Check if the point is behind the camera + if (clipSpace.w <= 0) + { + continue; // Skip this point + } - IndexBuffer::BufferDescriptor::Callback indexCallback = [](void *buf, size_t, - void *data) - { - free((void *)buf); - }; + // Perform perspective division + math::float3 ndcSpace = clipSpace.xyz / clipSpace.w; - auto vb = VertexBuffer::Builder() - .vertexCount(vertexCount) - .bufferCount(1) - .attribute( - VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3) - .build(*_engine); + // Clamp NDC coordinates to [-1, 1] range + ndcSpace.x = std::max(-1.0f, std::min(1.0f, ndcSpace.x)); + ndcSpace.y = std::max(-1.0f, std::min(1.0f, ndcSpace.y)); - vb->setBufferAt( - *_engine, - 0, - VertexBuffer::BufferDescriptor(vertices, vb->getVertexCount() * sizeof(filament::math::float3), 0, vertexCallback)); + // Convert NDC to viewport space + float viewportX = (ndcSpace.x * 0.5f + 0.5f) * viewport.width; + float viewportY = (1.0f - (ndcSpace.y * 0.5f + 0.5f)) * viewport.height; // Flip Y-axis - auto ib = IndexBuffer::Builder().indexCount(indexCount).bufferType(IndexBuffer::IndexType::USHORT).build(*_engine); - ib->setBuffer(*_engine, IndexBuffer::BufferDescriptor(indices, ib->getIndexCount() * sizeof(uint16_t), 0, indexCallback)); + minX = std::min(minX, viewportX); + minY = std::min(minY, viewportY); + maxX = std::max(maxX, viewportX); + maxY = std::max(maxY, viewportY); + } - auto &entityManager = EntityManager::get(); - - _gizmo[1] = entityManager.create(); - _gizmoMaterialInstances[1] = _gizmoMaterial->createInstance(); - _gizmoMaterialInstances[1]->setParameter("color", math::float3{1.0f, 0.0f, 0.0f}); - RenderableManager::Builder(1) - .boundingBox({{}, {1.0f, 1.0f, 1.0f}}) - .material(0, _gizmoMaterialInstances[1]) - .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb, - ib, 0, indexCount) - .culling(false) - .build(*_engine, _gizmo[1]); - - _gizmo[0] = entityManager.create(); - _gizmoMaterialInstances[0] = _gizmoMaterial->createInstance(); - _gizmoMaterialInstances[0]->setParameter("color", math::float3{0.0f, 1.0f, 0.0f}); - auto xTransform = math::mat4f::translation(math::float3{0.0f, 0.05f, -0.05f}) * math::mat4f::rotation(-math::F_PI_2, math::float3{0, 0, 1}); - auto *instanceBufferX = InstanceBuffer::Builder(1).localTransforms(&xTransform).build(*_engine); - RenderableManager::Builder(1) - .boundingBox({{}, {1.0f, 1.0f, 1.0f}}) - .instances(1, instanceBufferX) - .material(0, _gizmoMaterialInstances[0]) - .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb, - ib, 0, indexCount) - .culling(false) - .build(*_engine, _gizmo[0]); - - _gizmo[2] = entityManager.create(); - _gizmoMaterialInstances[2] = _gizmoMaterial->createInstance(); - _gizmoMaterialInstances[2]->setParameter("color", math::float3{0.0f, 0.0f, 1.0f}); - auto zTransform = math::mat4f::translation(math::float3{0.0f, 0.05f, -0.05f}) * math::mat4f::rotation(3 * math::F_PI_2, math::float3{1, 0, 0}); - auto *instanceBufferZ = InstanceBuffer::Builder(1).localTransforms(&zTransform).build(*_engine); - RenderableManager::Builder(1) - .boundingBox({{}, {1.0f, 1.0f, 1.0f}}) - .instances(1, instanceBufferZ) - .material(0, _gizmoMaterialInstances[2]) - .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb, - ib, 0, indexCount) - .culling(false) - .build(*_engine, _gizmo[2]); - - auto &rm = _engine->getRenderableManager(); - rm.setPriority(rm.getInstance(_gizmo[0]), 7); - rm.setPriority(rm.getInstance(_gizmo[1]), 7); - rm.setPriority(rm.getInstance(_gizmo[2]), 7); - return Entity::smuggle(_gizmo[0]); + return Aabb2{minX, minY, maxX, maxY}; } void SceneManager::getGizmo(EntityId *out) { - out[0] = Entity::smuggle(_gizmo[0]); - out[1] = Entity::smuggle(_gizmo[1]); - out[2] = Entity::smuggle(_gizmo[2]); + out[0] = Entity::smuggle(_gizmo->x()); + out[1] = Entity::smuggle(_gizmo->y()); + out[2] = Entity::smuggle(_gizmo->z()); + out[3] = Entity::smuggle(_gizmo->center()); } } // namespace thermion_filament diff --git a/thermion_dart/native/src/ThermionDartApi.cpp b/thermion_dart/native/src/ThermionDartApi.cpp index b72d0c25..4b6a4811 100644 --- a/thermion_dart/native/src/ThermionDartApi.cpp +++ b/thermion_dart/native/src/ThermionDartApi.cpp @@ -822,9 +822,9 @@ extern "C" return ((SceneManager *)sceneManager)->getParent(child); } - EMSCRIPTEN_KEEPALIVE void set_parent(void *const sceneManager, EntityId child, EntityId parent) + EMSCRIPTEN_KEEPALIVE void set_parent(void *const sceneManager, EntityId child, EntityId parent, bool preserveScaling) { - ((SceneManager *)sceneManager)->setParent(child, parent); + ((SceneManager *)sceneManager)->setParent(child, parent, preserveScaling); } EMSCRIPTEN_KEEPALIVE void test_collisions(void *const sceneManager, EntityId entity) @@ -841,4 +841,10 @@ extern "C" { return ((SceneManager *)sceneManager)->getGizmo(out); } + + EMSCRIPTEN_KEEPALIVE Aabb2 get_bounding_box(void *const sceneManager, EntityId entity) { + return ((SceneManager *)sceneManager)->getBoundingBox(entity); + } + + } diff --git a/thermion_dart/native/web/CMakeLists.txt b/thermion_dart/native/web/CMakeLists.txt index af98c836..1fe65068 100644 --- a/thermion_dart/native/web/CMakeLists.txt +++ b/thermion_dart/native/web/CMakeLists.txt @@ -55,6 +55,7 @@ add_executable(${MODULE_NAME} "${CMAKE_CURRENT_SOURCE_DIR}/../src/FilamentViewer.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/../src/ThermionDartApi.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/../src/ThermionDartFFIApi.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../src/Gizmo.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/../src/StreamBufferAdapter.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/../src/TimeIt.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/../src/camutils/Manipulator.cpp"