diff --git a/thermion_dart/lib/thermion_dart/compatibility/compatibility.dart b/thermion_dart/lib/thermion_dart/compatibility/compatibility.dart deleted file mode 100644 index a8435873..00000000 --- a/thermion_dart/lib/thermion_dart/compatibility/compatibility.dart +++ /dev/null @@ -1 +0,0 @@ -export 'native/compatibility.dart'; diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/ffi/allocator.dart b/thermion_dart/lib/thermion_dart/compatibility/web/ffi/allocator.dart deleted file mode 100644 index 41d8f6d8..00000000 --- a/thermion_dart/lib/thermion_dart/compatibility/web/ffi/allocator.dart +++ /dev/null @@ -1,238 +0,0 @@ -import 'dart:ffi'; -export "allocator.dart"; -export "thermion_dart.g.dart"; - -import 'dart:convert'; -import 'dart:ffi' as ffi hide Uint8Pointer, FloatPointer; -import 'dart:typed_data'; - -import 'package:thermion_dart/thermion_dart/compatibility/web/ffi/thermion_dart.g.dart'; - -import 'package:ffi/ffi.dart'; -export 'package:ffi/ffi.dart' hide StringUtf8Pointer, Utf8Pointer; -export 'dart:ffi' - hide - Uint8Pointer, - FloatPointer, - DoublePointer, - Int32Pointer, - Int64Pointer, - PointerPointer, - Allocator; - -class Allocator implements ffi.Allocator { - const Allocator(); - @override - ffi.Pointer allocate(int byteCount, - {int? alignment}) { - return thermion_flutter_web_allocate(byteCount).cast(); - } - - @override - void free(ffi.Pointer pointer) { - thermion_flutter_web_free(pointer.cast()); - } -} - -extension CharPointer on ffi.Pointer { - int get value { - return thermion_flutter_web_get(this, 0); - } - - set value(int value) { - thermion_flutter_web_set(this, 0, value); - } - - void operator []=(int index, int value) { - this.elementAt(index).value = value; - } - - ffi.Pointer elementAt(int index) => - ffi.Pointer.fromAddress(address + ffi.sizeOf() * index); -} - -extension IntPointer on ffi.Pointer { - int get value { - return thermion_flutter_web_get_int32(this.cast(), 0); - } - - set value(int value) { - thermion_flutter_web_set_int32(this.cast(), 0, value); - } - - void operator []=(int index, int value) { - this.elementAt(index).value = value; - } - - int operator [](int index) { - return this.elementAt(index).value; - } - - ffi.Pointer elementAt(int index) => - ffi.Pointer.fromAddress(address + ffi.sizeOf() * index); -} - -extension Int32Pointer on ffi.Pointer { - int get value { - return thermion_flutter_web_get_int32(this, 0); - } - - set value(int value) { - thermion_flutter_web_set_int32(this, 0, value); - } - - void operator []=(int index, int value) { - this.elementAt(index).value = value; - } - - int operator [](int index) { - return this.elementAt(index).value; - } - - ffi.Pointer elementAt(int index) => - ffi.Pointer.fromAddress(address + ffi.sizeOf() * index); -} - -extension UInt8Pointer on ffi.Pointer { - int get value { - return thermion_flutter_web_get(this.cast(), 0); - } - - set value(int value) { - thermion_flutter_web_set(this.cast(), 0, value); - } - - void operator []=(int index, int value) { - this.elementAt(index).value = value; - } - - int operator [](int index) { - return this.elementAt(index).value; - } - - ffi.Pointer elementAt(int index) => - ffi.Pointer.fromAddress(address + ffi.sizeOf() * index); -} - -extension PointerPointer - on ffi.Pointer> { - ffi.Pointer get value { - return thermion_flutter_web_get_pointer(cast>(), 0) - .cast(); - } - - set value(ffi.Pointer value) { - thermion_flutter_web_set_pointer( - cast>(), 0, value.cast()); - } - - - ffi.Pointer operator [](int index) { - return this.elementAt(index).value; - } - - void operator []=(int index, ffi.Pointer value) { - this.elementAt(index).value = value; - } - - ffi.Pointer> elementAt(int index) => - ffi.Pointer.fromAddress(address + ffi.sizeOf() * index); -} - -extension FloatPointer on ffi.Pointer { - double get value { - return thermion_flutter_web_get_float(this, 0); - } - - set value(double value) { - thermion_flutter_web_set_float(this, 0, value); - } - - double operator [](int index) { - return this.elementAt(index).value; - } - - void operator []=(int index, double value) { - this.elementAt(index).value = value; - } - - ffi.Pointer elementAt(int index) => - ffi.Pointer.fromAddress(address + ffi.sizeOf() * index); - - Float32List asTypedList(int length) { - var list = Float32List(length); - - for (int i = 0; i < length; i++) { - list[i] = this[i]; - } - return list; - } -} - -extension StringConversion on String { - ffi.Pointer toNativeUtf8({ffi.Allocator? allocator}) { - final units = utf8.encode(this); - final ffi.Pointer result = - allocator!(units.length + 1); - for (int i = 0; i < units.length; i++) { - result.elementAt(i).value = units[i]; - } - result.elementAt(units.length).value = 0; - return result.cast(); - } -} - -extension StringUtf8Pointer on ffi.Pointer { - static int _length(ffi.Pointer codeUnits) { - var length = 0; - while (codeUnits[length] != 0) { - length++; - } - return length; - } - - String toDartString({int? length}) { - final codeUnits = this.cast(); - final list = []; - - if (length != null) { - RangeError.checkNotNegative(length, 'length'); - } else { - length = _length(codeUnits); - } - for (int i = 0; i < length; i++) { - list.add(codeUnits.elementAt(i).value); - } - return utf8.decode(list); - } -} - -extension DoublePointer on ffi.Pointer { - double get value { - return thermion_flutter_web_get_double(this, 0); - } - - set value(double value) { - return thermion_flutter_web_set_double(this, 0, value); - } - - Float64List asTypedList(int length) { - var list = Float64List(length); - - for (int i = 0; i < length; i++) { - list[i] = elementAt(i).value; - } - return list; - } - - double operator [](int index) { - return elementAt(index).value; - } - - void operator []=(int index, double value) { - elementAt(index).value = value; - } - - ffi.Pointer elementAt(int index) => - ffi.Pointer.fromAddress(address + ffi.sizeOf() * index); -} diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/ffi/compatibility_ffi.dart b/thermion_dart/lib/thermion_dart/compatibility/web/ffi/compatibility_ffi.dart deleted file mode 100644 index e9242cc2..00000000 --- a/thermion_dart/lib/thermion_dart/compatibility/web/ffi/compatibility_ffi.dart +++ /dev/null @@ -1,118 +0,0 @@ -import 'dart:async'; -import 'dart:js_interop'; -import 'package:thermion_dart/thermion_dart/compatibility/web/ffi/interop.dart'; - -import "allocator.dart"; - -export "allocator.dart"; -export "thermion_dart.g.dart"; - -export 'package:ffi/ffi.dart' hide StringUtf8Pointer, Utf8Pointer; -export 'dart:ffi' - hide - Uint8Pointer, - FloatPointer, - DoublePointer, - Int32Pointer, - Int64Pointer, - PointerPointer, - Allocator; - -const allocator = Allocator(); - -@AbiSpecificIntegerMapping({ - Abi.androidArm: Uint8(), - Abi.androidArm64: Uint8(), - Abi.androidIA32: Int8(), - Abi.androidX64: Int8(), - Abi.androidRiscv64: Uint8(), - Abi.fuchsiaArm64: Uint8(), - Abi.fuchsiaX64: Int8(), - Abi.fuchsiaRiscv64: Uint8(), - Abi.iosArm: Int8(), - Abi.iosArm64: Int8(), - Abi.iosX64: Int8(), - Abi.linuxArm: Uint8(), - Abi.linuxArm64: Uint8(), - Abi.linuxIA32: Int8(), - Abi.linuxX64: Int8(), - Abi.linuxRiscv32: Uint8(), - Abi.linuxRiscv64: Uint8(), - Abi.macosArm64: Int8(), - Abi.macosX64: Int8(), - Abi.windowsArm64: Int8(), - Abi.windowsIA32: Int8(), - Abi.windowsX64: Int8(), -}) -final class FooChar extends AbiSpecificInteger { - const FooChar(); -} - -class Compatibility { - final _foo = FooChar(); -} - -Future withVoidCallback( - Function(Pointer>) func) async { - JSArray retVal = createVoidCallback(); - var promise = retVal.toDart[0] as JSPromise; - var fnPtrAddress = retVal.toDart[1] as JSNumber; - var fnPtr = Pointer>.fromAddress( - fnPtrAddress.toDartInt); - func(fnPtr); - await promise.toDart; -} - -Future withVoidPointerCallback( - void Function(Pointer)>>) - func) async { - JSArray retVal = createVoidPointerCallback(); - var promise = retVal.toDart[0] as JSPromise; - - var fnPtrAddress = retVal.toDart[1] as JSNumber; - var fnPtr = Pointer)>>.fromAddress( - fnPtrAddress.toDartInt); - func(fnPtr); - final addr = await promise.toDart; - return addr.toDartInt; -} - -Future withBoolCallback( - Function(Pointer>) func) async { - JSArray retVal = createBoolCallback(); - var promise = retVal.toDart[0] as JSPromise; - - var fnPtrAddress = retVal.toDart[1] as JSNumber; - var fnPtr = Pointer>.fromAddress( - fnPtrAddress.toDartInt); - func(fnPtr); - final addr = await promise.toDart; - return addr.toDart; -} - -Future withIntCallback( - Function(Pointer>) func) async { - JSArray retVal = createBoolCallback(); - var promise = retVal.toDart[0] as JSPromise; - - var fnPtrAddress = retVal.toDart[1] as JSNumber; - var fnPtr = Pointer>.fromAddress( - fnPtrAddress.toDartInt); - func(fnPtr); - final addr = await promise.toDart; - return addr.toDartInt; -} - -Future withCharPtrCallback( - Function(Pointer)>>) - func) async { - JSArray retVal = createVoidPointerCallback(); - var promise = retVal.toDart[0] as JSPromise; - - var fnPtrAddress = retVal.toDart[1] as JSNumber; - var fnPtr = Pointer)>>.fromAddress( - fnPtrAddress.toDartInt); - func(fnPtr); - final addr = await promise.toDart; - return Pointer.fromAddress(addr.toDartInt).toDartString(); -} diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/ffi/interop.dart b/thermion_dart/lib/thermion_dart/compatibility/web/ffi/interop.dart deleted file mode 100644 index 4773d252..00000000 --- a/thermion_dart/lib/thermion_dart/compatibility/web/ffi/interop.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'dart:js_interop'; - -@JS() -external JSArray createIntCallback(); - -@JS() -external JSArray createBoolCallback(); - -@JS() -external JSArray createVoidPointerCallback(); - -@JS() -external JSArray createVoidCallback(); - - - diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/ffi/thermion_dart.g.dart b/thermion_dart/lib/thermion_dart/compatibility/web/ffi/thermion_dart.g.dart deleted file mode 100644 index c1bf6de3..00000000 --- a/thermion_dart/lib/thermion_dart/compatibility/web/ffi/thermion_dart.g.dart +++ /dev/null @@ -1,1792 +0,0 @@ -// AUTO GENERATED FILE, DO NOT EDIT. -// -// Generated by `package:ffigen`. -// ignore_for_file: type=lint -import 'dart:ffi' as ffi; - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, ffi.Int32, ffi.Pointer)>( - symbol: '_thermion_flutter_web_load_resource_callback', - assetId: 'thermion_dart') -external void thermion_flutter_web_load_resource_callback( - ffi.Pointer data, - int length, - ffi.Pointer context, -); - -@ffi.Native, ffi.Int32)>( - symbol: '_thermion_flutter_web_get', assetId: 'thermion_dart') -external int thermion_flutter_web_get( - ffi.Pointer ptr, - int offset, -); - -@ffi.Native, ffi.Int32)>( - symbol: '_thermion_flutter_web_get_float', assetId: 'thermion_dart') -external double thermion_flutter_web_get_float( - ffi.Pointer ptr, - int offset, -); - -@ffi.Native, ffi.Int32)>( - symbol: '_thermion_flutter_web_get_double', assetId: 'thermion_dart') -external double thermion_flutter_web_get_double( - ffi.Pointer ptr, - int offset, -); - -@ffi.Native< - ffi.Pointer Function( - ffi.Pointer>, ffi.Int32)>( - symbol: '_thermion_flutter_web_get_pointer', assetId: 'thermion_dart') -external ffi.Pointer thermion_flutter_web_get_pointer( - ffi.Pointer> ptr, - int offset, -); - -@ffi.Native, ffi.Int32, ffi.Int32)>( - symbol: '_thermion_flutter_web_set', assetId: 'thermion_dart') -external void thermion_flutter_web_set( - ffi.Pointer ptr, - int offset, - int val, -); - -@ffi.Native, ffi.Int32, ffi.Float)>( - symbol: '_thermion_flutter_web_set_float', assetId: 'thermion_dart') -external void thermion_flutter_web_set_float( - ffi.Pointer ptr, - int offset, - double val, -); - -@ffi.Native, ffi.Int32, ffi.Double)>( - symbol: '_thermion_flutter_web_set_double', assetId: 'thermion_dart') -external void thermion_flutter_web_set_double( - ffi.Pointer ptr, - int offset, - double val, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer>, ffi.Int32, - ffi.Pointer)>( - symbol: '_thermion_flutter_web_set_pointer', assetId: 'thermion_dart') -external void thermion_flutter_web_set_pointer( - ffi.Pointer> ptr, - int offset, - ffi.Pointer val, -); - -@ffi.Native, ffi.Int32)>( - symbol: '_thermion_flutter_web_get_int32', assetId: 'thermion_dart') -external int thermion_flutter_web_get_int32( - ffi.Pointer ptr, - int offset, -); - -@ffi.Native, ffi.Int32, ffi.Int32)>( - symbol: '_thermion_flutter_web_set_int32', assetId: 'thermion_dart') -external void thermion_flutter_web_set_int32( - ffi.Pointer ptr, - int offset, - int value, -); - -@ffi.Native>)>( - symbol: '_thermion_flutter_web_get_address', assetId: 'thermion_dart') -external int thermion_flutter_web_get_address( - ffi.Pointer> out, -); - -@ffi.Native Function(ffi.Int32)>( - symbol: '_thermion_flutter_web_allocate', assetId: 'thermion_dart') -external ffi.Pointer thermion_flutter_web_allocate( - int size, -); - -@ffi.Native)>( - symbol: '_thermion_flutter_web_free', assetId: 'thermion_dart') -external void thermion_flutter_web_free( - ffi.Pointer ptr, -); - -@ffi.Native( - symbol: '_thermion_dart_web_create_gl_context', assetId: 'thermion_dart') -external int thermion_dart_web_create_gl_context(); - -@ffi.Native Function()>( - symbol: '_thermion_dart_web_get_resource_loader_wrapper', - assetId: 'thermion_dart') -external ffi.Pointer thermion_dart_web_get_resource_loader_wrapper(); - -@ffi.Native< - ffi.Pointer Function(LoadFilamentResourceFromOwner, - FreeFilamentResourceFromOwner, ffi.Pointer)>( - symbol: '_make_resource_loader', assetId: 'thermion_dart') -external ffi.Pointer make_resource_loader( - LoadFilamentResourceFromOwner loadFn, - FreeFilamentResourceFromOwner freeFn, - ffi.Pointer owner, -); - -@ffi.Native< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer)>( - symbol: '_create_filament_viewer', assetId: 'thermion_dart') -external ffi.Pointer create_filament_viewer( - ffi.Pointer context, - ffi.Pointer loader, - ffi.Pointer platform, - ffi.Pointer uberArchivePath, -); - -@ffi.Native)>( - symbol: '_destroy_filament_viewer', assetId: 'thermion_dart') -external void destroy_filament_viewer( - ffi.Pointer viewer, -); - -@ffi.Native Function(ffi.Pointer)>( - symbol: '_get_scene_manager', assetId: 'thermion_dart') -external ffi.Pointer get_scene_manager( - ffi.Pointer viewer, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.IntPtr, ffi.Uint32, - ffi.Uint32)>(symbol: '_create_render_target', assetId: 'thermion_dart') -external void create_render_target( - ffi.Pointer viewer, - int texture, - int width, - int height, -); - -@ffi.Native)>( - symbol: '_clear_background_image', assetId: 'thermion_dart') -external void clear_background_image( - ffi.Pointer viewer, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Pointer, - ffi.Bool)>(symbol: '_set_background_image', assetId: 'thermion_dart') -external void set_background_image( - ffi.Pointer viewer, - ffi.Pointer path, - bool fillHeight, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, ffi.Float, ffi.Float, ffi.Bool)>( - symbol: '_set_background_image_position', assetId: 'thermion_dart') -external void set_background_image_position( - ffi.Pointer viewer, - double x, - double y, - bool clamp, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Float, ffi.Float, ffi.Float, - ffi.Float)>(symbol: '_set_background_color', assetId: 'thermion_dart') -external void set_background_color( - ffi.Pointer viewer, - double r, - double g, - double b, - double a, -); - -@ffi.Native, ffi.Int)>( - symbol: '_set_tone_mapping', assetId: 'thermion_dart') -external void set_tone_mapping( - ffi.Pointer viewer, - int toneMapping, -); - -@ffi.Native, ffi.Float)>( - symbol: '_set_bloom', assetId: 'thermion_dart') -external void set_bloom( - ffi.Pointer viewer, - double strength, -); - -@ffi.Native, ffi.Pointer)>( - symbol: '_load_skybox', assetId: 'thermion_dart') -external void load_skybox( - ffi.Pointer viewer, - ffi.Pointer skyboxPath, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Pointer, - ffi.Float)>(symbol: '_load_ibl', assetId: 'thermion_dart') -external void load_ibl( - ffi.Pointer viewer, - ffi.Pointer iblPath, - double intensity, -); - -@ffi.Native, ffi.Pointer)>( - symbol: '_rotate_ibl', assetId: 'thermion_dart') -external void rotate_ibl( - ffi.Pointer viewer, - ffi.Pointer rotationMatrix, -); - -@ffi.Native)>( - symbol: '_remove_skybox', assetId: 'thermion_dart') -external void remove_skybox( - ffi.Pointer viewer, -); - -@ffi.Native)>( - symbol: '_remove_ibl', assetId: 'thermion_dart') -external void remove_ibl( - ffi.Pointer viewer, -); - -@ffi.Native< - EntityId Function( - ffi.Pointer, - ffi.Uint8, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Bool)>(symbol: '_add_light', assetId: 'thermion_dart') -external int add_light( - ffi.Pointer viewer, - int type, - double colour, - double intensity, - double posX, - double posY, - double posZ, - double dirX, - double dirY, - double dirZ, - double falloffRadius, - double spotLightConeInner, - double spotLightConeOuter, - double sunAngularRadius, - double sunHaloSize, - double sunHaloFallof, - bool shadows, -); - -@ffi.Native, EntityId)>( - symbol: '_remove_light', assetId: 'thermion_dart') -external void remove_light( - ffi.Pointer viewer, - int entityId, -); - -@ffi.Native)>( - symbol: '_clear_lights', assetId: 'thermion_dart') -external void clear_lights( - ffi.Pointer viewer, -); - -@ffi.Native< - EntityId Function(ffi.Pointer, ffi.Pointer, - ffi.Int)>(symbol: '_load_glb', assetId: 'thermion_dart') -external int load_glb( - ffi.Pointer sceneManager, - ffi.Pointer assetPath, - int numInstances, -); - -@ffi.Native< - EntityId Function(ffi.Pointer, ffi.Pointer, - ffi.Size)>(symbol: '_load_glb_from_buffer', assetId: 'thermion_dart') -external int load_glb_from_buffer( - ffi.Pointer sceneManager, - ffi.Pointer data, - int length, -); - -@ffi.Native< - EntityId Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer)>(symbol: '_load_gltf', assetId: 'thermion_dart') -external int load_gltf( - ffi.Pointer sceneManager, - ffi.Pointer assetPath, - ffi.Pointer relativePath, -); - -@ffi.Native, EntityId)>( - symbol: '_create_instance', assetId: 'thermion_dart') -external int create_instance( - ffi.Pointer sceneManager, - int id, -); - -@ffi.Native, EntityId)>( - symbol: '_get_instance_count', assetId: 'thermion_dart') -external int get_instance_count( - ffi.Pointer sceneManager, - int entityId, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, EntityId, ffi.Pointer)>( - symbol: '_get_instances', assetId: 'thermion_dart') -external void get_instances( - ffi.Pointer sceneManager, - int entityId, - ffi.Pointer out, -); - -@ffi.Native)>( - symbol: '_set_main_camera', assetId: 'thermion_dart') -external void set_main_camera( - ffi.Pointer viewer, -); - -@ffi.Native)>( - symbol: '_get_main_camera', assetId: 'thermion_dart') -external int get_main_camera( - ffi.Pointer viewer, -); - -@ffi.Native< - ffi.Bool Function(ffi.Pointer, EntityId, - ffi.Pointer)>(symbol: '_set_camera', assetId: 'thermion_dart') -external bool set_camera( - ffi.Pointer viewer, - int entity, - ffi.Pointer nodeName, -); - -@ffi.Native, ffi.Bool)>( - symbol: '_set_view_frustum_culling', assetId: 'thermion_dart') -external void set_view_frustum_culling( - ffi.Pointer viewer, - bool enabled, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - ffi.Uint64, - ffi.Pointer, - ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function(ffi.Pointer buf, ffi.Size size, - ffi.Pointer data)>>, - ffi.Pointer)>(symbol: '_render', assetId: 'thermion_dart') -external void render( - ffi.Pointer viewer, - int frameTimeInNanos, - ffi.Pointer pixelBuffer, - ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function(ffi.Pointer buf, ffi.Size size, - ffi.Pointer data)>> - callback, - ffi.Pointer data, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Pointer, ffi.Uint32, - ffi.Uint32)>(symbol: '_create_swap_chain', assetId: 'thermion_dart') -external void create_swap_chain( - ffi.Pointer viewer, - ffi.Pointer window, - int width, - int height, -); - -@ffi.Native)>( - symbol: '_destroy_swap_chain', assetId: 'thermion_dart') -external void destroy_swap_chain( - ffi.Pointer viewer, -); - -@ffi.Native, ffi.Float)>( - symbol: '_set_frame_interval', assetId: 'thermion_dart') -external void set_frame_interval( - ffi.Pointer viewer, - double interval, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, ffi.Uint32, ffi.Uint32, ffi.Float)>( - symbol: '_update_viewport_and_camera_projection', assetId: 'thermion_dart') -external void update_viewport_and_camera_projection( - ffi.Pointer viewer, - int width, - int height, - double scaleFactor, -); - -@ffi.Native)>( - symbol: '_scroll_begin', assetId: 'thermion_dart') -external void scroll_begin( - ffi.Pointer viewer, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Float, ffi.Float, - ffi.Float)>(symbol: '_scroll_update', assetId: 'thermion_dart') -external void scroll_update( - ffi.Pointer viewer, - double x, - double y, - double z, -); - -@ffi.Native)>( - symbol: '_scroll_end', assetId: 'thermion_dart') -external void scroll_end( - ffi.Pointer viewer, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Float, ffi.Float, - ffi.Bool)>(symbol: '_grab_begin', assetId: 'thermion_dart') -external void grab_begin( - ffi.Pointer viewer, - double x, - double y, - bool pan, -); - -@ffi.Native, ffi.Float, ffi.Float)>( - symbol: '_grab_update', assetId: 'thermion_dart') -external void grab_update( - ffi.Pointer viewer, - double x, - double y, -); - -@ffi.Native)>( - symbol: '_grab_end', assetId: 'thermion_dart') -external void grab_end( - ffi.Pointer viewer, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - EntityId, - ffi.Pointer, - ffi.Pointer, - ffi.Int)>(symbol: '_apply_weights', assetId: 'thermion_dart') -external void apply_weights( - ffi.Pointer sceneManager, - int entity, - ffi.Pointer entityName, - ffi.Pointer weights, - int count, -); - -@ffi.Native< - ffi.Bool Function(ffi.Pointer, EntityId, ffi.Pointer, - ffi.Int)>(symbol: '_set_morph_target_weights', assetId: 'thermion_dart') -external bool set_morph_target_weights( - ffi.Pointer sceneManager, - int entity, - ffi.Pointer morphData, - int numWeights, -); - -@ffi.Native< - ffi.Bool Function( - ffi.Pointer, - EntityId, - ffi.Pointer, - ffi.Pointer, - ffi.Int, - ffi.Int, - ffi.Float)>(symbol: '_set_morph_animation', assetId: 'thermion_dart') -external bool set_morph_animation( - ffi.Pointer sceneManager, - int entity, - ffi.Pointer morphData, - ffi.Pointer morphIndices, - int numMorphTargets, - int numFrames, - double frameLengthInMs, -); - -@ffi.Native, EntityId)>( - symbol: '_reset_to_rest_pose', assetId: 'thermion_dart') -external void reset_to_rest_pose( - ffi.Pointer sceneManager, - int asset, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - EntityId, - ffi.Int, - ffi.Int, - ffi.Pointer, - ffi.Int, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float)>(symbol: '_add_bone_animation', assetId: 'thermion_dart') -external void add_bone_animation( - ffi.Pointer sceneManager, - int entity, - int skinIndex, - int boneIndex, - ffi.Pointer frameData, - int numFrames, - double frameLengthInMs, - double fadeOutInSecs, - double fadeInInSecs, - double maxDelta, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, EntityId, ffi.Pointer)>( - symbol: '_get_local_transform', assetId: 'thermion_dart') -external void get_local_transform( - ffi.Pointer sceneManager, - int entityId, - ffi.Pointer arg2, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - EntityId, - ffi.Int, - ffi.Pointer, - ffi.Int)>(symbol: '_get_rest_local_transforms', assetId: 'thermion_dart') -external void get_rest_local_transforms( - ffi.Pointer sceneManager, - int entityId, - int skinIndex, - ffi.Pointer out, - int numBones, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, EntityId, ffi.Pointer)>( - symbol: '_get_world_transform', assetId: 'thermion_dart') -external void get_world_transform( - ffi.Pointer sceneManager, - int entityId, - ffi.Pointer arg2, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, EntityId, ffi.Int, ffi.Int, - ffi.Pointer)>( - symbol: '_get_inverse_bind_matrix', assetId: 'thermion_dart') -external void get_inverse_bind_matrix( - ffi.Pointer sceneManager, - int entityId, - int skinIndex, - int boneIndex, - ffi.Pointer arg4, -); - -@ffi.Native< - ffi.Bool Function(ffi.Pointer, EntityId, ffi.Int, ffi.Int, - ffi.Pointer)>( - symbol: '_set_bone_transform', assetId: 'thermion_dart') -external bool set_bone_transform( - ffi.Pointer sceneManager, - int entity, - int skinIndex, - int boneIndex, - ffi.Pointer transform, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - EntityId, - ffi.Int, - ffi.Bool, - ffi.Bool, - ffi.Bool, - ffi.Float)>(symbol: '_play_animation', assetId: 'thermion_dart') -external void play_animation( - ffi.Pointer sceneManager, - int entity, - int index, - bool loop, - bool reverse, - bool replaceActive, - double crossfade, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, EntityId, ffi.Int, ffi.Int)>( - symbol: '_set_animation_frame', assetId: 'thermion_dart') -external void set_animation_frame( - ffi.Pointer sceneManager, - int entity, - int animationIndex, - int animationFrame, -); - -@ffi.Native, EntityId, ffi.Int)>( - symbol: '_stop_animation', assetId: 'thermion_dart') -external void stop_animation( - ffi.Pointer sceneManager, - int entity, - int index, -); - -@ffi.Native, EntityId)>( - symbol: '_get_animation_count', assetId: 'thermion_dart') -external int get_animation_count( - ffi.Pointer sceneManager, - int asset, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, EntityId, ffi.Pointer, - ffi.Int)>(symbol: '_get_animation_name', assetId: 'thermion_dart') -external void get_animation_name( - ffi.Pointer sceneManager, - int entity, - ffi.Pointer outPtr, - int index, -); - -@ffi.Native, EntityId, ffi.Int)>( - symbol: '_get_animation_duration', assetId: 'thermion_dart') -external double get_animation_duration( - ffi.Pointer sceneManager, - int entity, - int index, -); - -@ffi.Native, EntityId, ffi.Int)>( - symbol: '_get_bone_count', assetId: 'thermion_dart') -external int get_bone_count( - ffi.Pointer sceneManager, - int assetEntity, - int skinIndex, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - EntityId, - ffi.Pointer>, - ffi.Int)>(symbol: '_get_bone_names', assetId: 'thermion_dart') -external void get_bone_names( - ffi.Pointer sceneManager, - int assetEntity, - ffi.Pointer> outPtr, - int skinIndex, -); - -@ffi.Native< - EntityId Function(ffi.Pointer, EntityId, ffi.Int, ffi.Int)>( - symbol: '_get_bone', assetId: 'thermion_dart') -external int get_bone( - ffi.Pointer sceneManager, - int entityId, - int skinIndex, - int boneIndex, -); - -@ffi.Native< - ffi.Bool Function( - ffi.Pointer, EntityId, ffi.Pointer)>( - symbol: '_set_transform', assetId: 'thermion_dart') -external bool set_transform( - ffi.Pointer sceneManager, - int entityId, - ffi.Pointer transform, -); - -@ffi.Native, EntityId)>( - symbol: '_update_bone_matrices', assetId: 'thermion_dart') -external bool update_bone_matrices( - ffi.Pointer sceneManager, - int entityId, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - EntityId, - EntityId, - ffi.Pointer, - ffi.Int)>(symbol: '_get_morph_target_name', assetId: 'thermion_dart') -external void get_morph_target_name( - ffi.Pointer sceneManager, - int assetEntity, - int childEntity, - ffi.Pointer outPtr, - int index, -); - -@ffi.Native, EntityId, EntityId)>( - symbol: '_get_morph_target_name_count', assetId: 'thermion_dart') -external int get_morph_target_name_count( - ffi.Pointer sceneManager, - int assetEntity, - int childEntity, -); - -@ffi.Native, EntityId)>( - symbol: '_remove_entity', assetId: 'thermion_dart') -external void remove_entity( - ffi.Pointer viewer, - int asset, -); - -@ffi.Native)>( - symbol: '_clear_entities', assetId: 'thermion_dart') -external void clear_entities( - ffi.Pointer viewer, -); - -@ffi.Native< - ffi.Bool Function( - ffi.Pointer, - EntityId, - ffi.Pointer, - ffi.Int, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float)>(symbol: '_set_material_color', assetId: 'thermion_dart') -external bool set_material_color( - ffi.Pointer sceneManager, - int entity, - ffi.Pointer meshName, - int materialIndex, - double r, - double g, - double b, - double a, -); - -@ffi.Native, EntityId)>( - symbol: '_transform_to_unit_cube', assetId: 'thermion_dart') -external void transform_to_unit_cube( - ffi.Pointer sceneManager, - int asset, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - EntityId, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Bool)>(symbol: '_queue_position_update', assetId: 'thermion_dart') -external void queue_position_update( - ffi.Pointer sceneManager, - int entity, - double x, - double y, - double z, - bool relative, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - EntityId, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Bool)>(symbol: '_queue_rotation_update', assetId: 'thermion_dart') -external void queue_rotation_update( - ffi.Pointer sceneManager, - int entity, - double rads, - double x, - double y, - double z, - double w, - bool relative, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, EntityId, ffi.Float, ffi.Float, - ffi.Float)>(symbol: '_set_position', assetId: 'thermion_dart') -external void set_position( - ffi.Pointer sceneManager, - int entity, - double x, - double y, - double z, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - EntityId, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float)>(symbol: '_set_rotation', assetId: 'thermion_dart') -external void set_rotation( - ffi.Pointer sceneManager, - int entity, - double rads, - double x, - double y, - double z, - double w, -); - -@ffi.Native, EntityId, ffi.Float)>( - symbol: '_set_scale', assetId: 'thermion_dart') -external void set_scale( - ffi.Pointer sceneManager, - int entity, - double scale, -); - -@ffi.Native, EntityId)>( - symbol: '_move_camera_to_asset', assetId: 'thermion_dart') -external void move_camera_to_asset( - ffi.Pointer viewer, - int asset, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Float, ffi.Float, - ffi.Float)>(symbol: '_set_camera_exposure', assetId: 'thermion_dart') -external void set_camera_exposure( - ffi.Pointer viewer, - double aperture, - double shutterSpeed, - double sensitivity, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Float, ffi.Float, - ffi.Float)>(symbol: '_set_camera_position', assetId: 'thermion_dart') -external void set_camera_position( - ffi.Pointer viewer, - double x, - double y, - double z, -); - -@ffi.Native)>( - symbol: '_get_camera_position', assetId: 'thermion_dart') -external void get_camera_position( - ffi.Pointer viewer, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Float, ffi.Float, ffi.Float, - ffi.Float)>(symbol: '_set_camera_rotation', assetId: 'thermion_dart') -external void set_camera_rotation( - ffi.Pointer viewer, - double w, - double x, - double y, - double z, -); - -@ffi.Native, ffi.Pointer)>( - symbol: '_set_camera_model_matrix', assetId: 'thermion_dart') -external void set_camera_model_matrix( - ffi.Pointer viewer, - ffi.Pointer matrix, -); - -@ffi.Native Function(ffi.Pointer)>( - symbol: '_get_camera_model_matrix', assetId: 'thermion_dart') -external ffi.Pointer get_camera_model_matrix( - ffi.Pointer viewer, -); - -@ffi.Native Function(ffi.Pointer)>( - symbol: '_get_camera_view_matrix', assetId: 'thermion_dart') -external ffi.Pointer get_camera_view_matrix( - ffi.Pointer viewer, -); - -@ffi.Native Function(ffi.Pointer)>( - symbol: '_get_camera_projection_matrix', assetId: 'thermion_dart') -external ffi.Pointer get_camera_projection_matrix( - ffi.Pointer viewer, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Pointer, - ffi.Double, ffi.Double)>( - symbol: '_set_camera_projection_matrix', assetId: 'thermion_dart') -external void set_camera_projection_matrix( - ffi.Pointer viewer, - ffi.Pointer matrix, - double near, - double far, -); - -@ffi.Native, ffi.Double, ffi.Double)>( - symbol: '_set_camera_culling', assetId: 'thermion_dart') -external void set_camera_culling( - ffi.Pointer viewer, - double near, - double far, -); - -@ffi.Native)>( - symbol: '_get_camera_culling_near', assetId: 'thermion_dart') -external double get_camera_culling_near( - ffi.Pointer viewer, -); - -@ffi.Native)>( - symbol: '_get_camera_culling_far', assetId: 'thermion_dart') -external double get_camera_culling_far( - ffi.Pointer viewer, -); - -@ffi.Native Function(ffi.Pointer)>( - symbol: '_get_camera_culling_projection_matrix', assetId: 'thermion_dart') -external ffi.Pointer get_camera_culling_projection_matrix( - ffi.Pointer viewer, -); - -@ffi.Native Function(ffi.Pointer)>( - symbol: '_get_camera_frustum', assetId: 'thermion_dart') -external ffi.Pointer get_camera_frustum( - ffi.Pointer viewer, -); - -@ffi.Native, ffi.Float, ffi.Float)>( - symbol: '_set_camera_fov', assetId: 'thermion_dart') -external void set_camera_fov( - ffi.Pointer viewer, - double fovInDegrees, - double aspect, -); - -@ffi.Native, ffi.Float)>( - symbol: '_set_camera_focal_length', assetId: 'thermion_dart') -external void set_camera_focal_length( - ffi.Pointer viewer, - double focalLength, -); - -@ffi.Native, ffi.Float)>( - symbol: '_set_camera_focus_distance', assetId: 'thermion_dart') -external void set_camera_focus_distance( - ffi.Pointer viewer, - double focusDistance, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, _ManipulatorMode, ffi.Double, - ffi.Double, ffi.Double)>( - symbol: '_set_camera_manipulator_options', assetId: 'thermion_dart') -external void set_camera_manipulator_options( - ffi.Pointer viewer, - int mode, - double orbitSpeedX, - double orbitSpeedY, - double zoomSpeed, -); - -@ffi.Native< - ffi.Int Function(ffi.Pointer, EntityId, - ffi.Pointer)>(symbol: '_hide_mesh', assetId: 'thermion_dart') -external int hide_mesh( - ffi.Pointer sceneManager, - int entity, - ffi.Pointer meshName, -); - -@ffi.Native< - ffi.Int Function(ffi.Pointer, EntityId, - ffi.Pointer)>(symbol: '_reveal_mesh', assetId: 'thermion_dart') -external int reveal_mesh( - ffi.Pointer sceneManager, - int entity, - ffi.Pointer meshName, -); - -@ffi.Native, ffi.Bool)>( - symbol: '_set_post_processing', assetId: 'thermion_dart') -external void set_post_processing( - ffi.Pointer viewer, - bool enabled, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Bool, ffi.Bool, ffi.Bool)>( - symbol: '_set_antialiasing', assetId: 'thermion_dart') -external void set_antialiasing( - ffi.Pointer viewer, - bool msaa, - bool fxaa, - bool taa, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - ffi.Int, - ffi.Int, - ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function( - EntityId entityId, ffi.Int x, ffi.Int y)>>)>( - symbol: '_filament_pick', assetId: 'thermion_dart') -external void filament_pick( - ffi.Pointer viewer, - int x, - int y, - ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function(EntityId entityId, ffi.Int x, ffi.Int y)>> - callback, -); - -@ffi.Native Function(ffi.Pointer, EntityId)>( - symbol: '_get_name_for_entity', assetId: 'thermion_dart') -external ffi.Pointer get_name_for_entity( - ffi.Pointer sceneManager, - int entityId, -); - -@ffi.Native< - EntityId Function( - ffi.Pointer, EntityId, ffi.Pointer)>( - symbol: '_find_child_entity_by_name', assetId: 'thermion_dart') -external int find_child_entity_by_name( - ffi.Pointer sceneManager, - int parent, - ffi.Pointer name, -); - -@ffi.Native, EntityId, ffi.Bool)>( - symbol: '_get_entity_count', assetId: 'thermion_dart') -external int get_entity_count( - ffi.Pointer sceneManager, - int target, - bool renderableOnly, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, EntityId, ffi.Bool, ffi.Pointer)>( - symbol: '_get_entities', assetId: 'thermion_dart') -external void get_entities( - ffi.Pointer sceneManager, - int target, - bool renderableOnly, - ffi.Pointer out, -); - -@ffi.Native< - ffi.Pointer Function(ffi.Pointer, EntityId, ffi.Int, - ffi.Bool)>(symbol: '_get_entity_name_at', assetId: 'thermion_dart') -external ffi.Pointer get_entity_name_at( - ffi.Pointer sceneManager, - int target, - int index, - bool renderableOnly, -); - -@ffi.Native, ffi.Bool)>( - symbol: '_set_recording', assetId: 'thermion_dart') -external void set_recording( - ffi.Pointer viewer, - bool recording, -); - -@ffi.Native, ffi.Pointer)>( - symbol: '_set_recording_output_directory', assetId: 'thermion_dart') -external void set_recording_output_directory( - ffi.Pointer viewer, - ffi.Pointer outputDirectory, -); - -@ffi.Native(symbol: '_ios_dummy', assetId: 'thermion_dart') -external void ios_dummy(); - -@ffi.Native)>( - symbol: '_thermion_flutter_free', assetId: 'thermion_dart') -external void thermion_flutter_free( - ffi.Pointer ptr, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - EntityId, - ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function(EntityId entityId1, EntityId entityId2)>>, - ffi.Bool)>(symbol: '_add_collision_component', assetId: 'thermion_dart') -external void add_collision_component( - ffi.Pointer sceneManager, - int entityId, - ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function(EntityId entityId1, EntityId entityId2)>> - callback, - bool affectsCollidingTransform, -); - -@ffi.Native, EntityId)>( - symbol: '_remove_collision_component', assetId: 'thermion_dart') -external void remove_collision_component( - ffi.Pointer sceneManager, - int entityId, -); - -@ffi.Native, EntityId)>( - symbol: '_add_animation_component', assetId: 'thermion_dart') -external bool add_animation_component( - ffi.Pointer sceneManager, - int entityId, -); - -@ffi.Native, EntityId)>( - symbol: '_remove_animation_component', assetId: 'thermion_dart') -external void remove_animation_component( - ffi.Pointer sceneManager, - int entityId, -); - -@ffi.Native< - EntityId Function( - ffi.Pointer, - ffi.Pointer, - ffi.Int, - ffi.Pointer, - ffi.Int, - ffi.Int, - ffi.Pointer)>( - symbol: '_create_geometry', assetId: 'thermion_dart') -external int create_geometry( - ffi.Pointer viewer, - ffi.Pointer vertices, - int numVertices, - ffi.Pointer indices, - int numIndices, - int primitiveType, - ffi.Pointer materialPath, -); - -@ffi.Native, EntityId)>( - symbol: '_get_parent', assetId: 'thermion_dart') -external int get_parent( - ffi.Pointer sceneManager, - int child, -); - -@ffi.Native, EntityId, EntityId)>( - symbol: '_set_parent', assetId: 'thermion_dart') -external void set_parent( - ffi.Pointer sceneManager, - int child, - int parent, -); - -@ffi.Native, EntityId)>( - symbol: '_test_collisions', assetId: 'thermion_dart') -external void test_collisions( - ffi.Pointer sceneManager, - int entity, -); - -@ffi.Native, EntityId, ffi.Int)>( - symbol: '_set_priority', assetId: 'thermion_dart') -external void set_priority( - ffi.Pointer sceneManager, - int entityId, - int priority, -); - -@ffi.Native, ffi.Pointer)>( - symbol: '_get_gizmo', assetId: 'thermion_dart') -external void get_gizmo( - ffi.Pointer sceneManager, - ffi.Pointer out, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi - .Pointer< - ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer renderCallbackOwner)>>, - ffi.Pointer, - ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function(ffi.Pointer viewer)>>)>( - symbol: '_create_filament_viewer_ffi', assetId: 'thermion_dart') -external void create_filament_viewer_ffi( - ffi.Pointer context, - ffi.Pointer platform, - ffi.Pointer uberArchivePath, - ffi.Pointer loader, - ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function(ffi.Pointer renderCallbackOwner)>> - renderCallback, - ffi.Pointer renderCallbackOwner, - ffi.Pointer< - ffi.NativeFunction viewer)>> - callback, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Uint32, - ffi.Uint32, - ffi.Pointer>)>( - symbol: '_create_swap_chain_ffi', assetId: 'thermion_dart') -external void create_swap_chain_ffi( - ffi.Pointer viewer, - ffi.Pointer surface, - int width, - int height, - ffi.Pointer> onComplete, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, - ffi.Pointer>)>( - symbol: '_destroy_swap_chain_ffi', assetId: 'thermion_dart') -external void destroy_swap_chain_ffi( - ffi.Pointer viewer, - ffi.Pointer> onComplete, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.IntPtr, ffi.Uint32, - ffi.Uint32, ffi.Pointer>)>( - symbol: '_create_render_target_ffi', assetId: 'thermion_dart') -external void create_render_target_ffi( - ffi.Pointer viewer, - int nativeTextureId, - int width, - int height, - ffi.Pointer> onComplete, -); - -@ffi.Native)>( - symbol: '_destroy_filament_viewer_ffi', assetId: 'thermion_dart') -external void destroy_filament_viewer_ffi( - ffi.Pointer viewer, -); - -@ffi.Native)>( - symbol: '_render_ffi', assetId: 'thermion_dart') -external void render_ffi( - ffi.Pointer viewer, -); - -@ffi.Native( - symbol: '_make_render_callback_fn_pointer', assetId: 'thermion_dart') -external FilamentRenderCallback make_render_callback_fn_pointer( - FilamentRenderCallback arg0, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Bool, - ffi.Pointer>)>( - symbol: '_set_rendering_ffi', assetId: 'thermion_dart') -external void set_rendering_ffi( - ffi.Pointer viewer, - bool rendering, - ffi.Pointer> onComplete, -); - -@ffi.Native, ffi.Float)>( - symbol: '_set_frame_interval_ffi', assetId: 'thermion_dart') -external void set_frame_interval_ffi( - ffi.Pointer viewer, - double frameInterval, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Uint32, ffi.Uint32, - ffi.Float, ffi.Pointer>)>( - symbol: '_update_viewport_and_camera_projection_ffi', - assetId: 'thermion_dart') -external void update_viewport_and_camera_projection_ffi( - ffi.Pointer viewer, - int width, - int height, - double scaleFactor, - ffi.Pointer> onComplete, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, ffi.Float, ffi.Float, ffi.Float, ffi.Float)>( - symbol: '_set_background_color_ffi', assetId: 'thermion_dart') -external void set_background_color_ffi( - ffi.Pointer viewer, - double r, - double g, - double b, - double a, -); - -@ffi.Native)>( - symbol: '_clear_background_image_ffi', assetId: 'thermion_dart') -external void clear_background_image_ffi( - ffi.Pointer viewer, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Pointer, - ffi.Bool, ffi.Pointer>)>( - symbol: '_set_background_image_ffi', assetId: 'thermion_dart') -external void set_background_image_ffi( - ffi.Pointer viewer, - ffi.Pointer path, - bool fillHeight, - ffi.Pointer> onComplete, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, ffi.Float, ffi.Float, ffi.Bool)>( - symbol: '_set_background_image_position_ffi', assetId: 'thermion_dart') -external void set_background_image_position_ffi( - ffi.Pointer viewer, - double x, - double y, - bool clamp, -); - -@ffi.Native, ffi.Int)>( - symbol: '_set_tone_mapping_ffi', assetId: 'thermion_dart') -external void set_tone_mapping_ffi( - ffi.Pointer viewer, - int toneMapping, -); - -@ffi.Native, ffi.Float)>( - symbol: '_set_bloom_ffi', assetId: 'thermion_dart') -external void set_bloom_ffi( - ffi.Pointer viewer, - double strength, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer>)>( - symbol: '_load_skybox_ffi', assetId: 'thermion_dart') -external void load_skybox_ffi( - ffi.Pointer viewer, - ffi.Pointer skyboxPath, - ffi.Pointer> onComplete, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Pointer, - ffi.Float)>(symbol: '_load_ibl_ffi', assetId: 'thermion_dart') -external void load_ibl_ffi( - ffi.Pointer viewer, - ffi.Pointer iblPath, - double intensity, -); - -@ffi.Native)>( - symbol: '_remove_skybox_ffi', assetId: 'thermion_dart') -external void remove_skybox_ffi( - ffi.Pointer viewer, -); - -@ffi.Native)>( - symbol: '_remove_ibl_ffi', assetId: 'thermion_dart') -external void remove_ibl_ffi( - ffi.Pointer viewer, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - ffi.Uint8, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Bool, - ffi.Pointer>)>( - symbol: '_add_light_ffi', assetId: 'thermion_dart') -external void add_light_ffi( - ffi.Pointer viewer, - int type, - double colour, - double intensity, - double posX, - double posY, - double posZ, - double dirX, - double dirY, - double dirZ, - double falloffRadius, - double spotLightConeInner, - double spotLightConeOuter, - double sunAngularRadius, - double sunHaloSize, - double sunHaloFallof, - bool shadows, - ffi.Pointer> callback, -); - -@ffi.Native, EntityId)>( - symbol: '_remove_light_ffi', assetId: 'thermion_dart') -external void remove_light_ffi( - ffi.Pointer viewer, - int entityId, -); - -@ffi.Native)>( - symbol: '_clear_lights_ffi', assetId: 'thermion_dart') -external void clear_lights_ffi( - ffi.Pointer viewer, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Pointer, ffi.Int, - ffi.Pointer>)>( - symbol: '_load_glb_ffi', assetId: 'thermion_dart') -external void load_glb_ffi( - ffi.Pointer sceneManager, - ffi.Pointer assetPath, - int numInstances, - ffi.Pointer> callback, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Size, - ffi.Int, - ffi.Pointer>)>( - symbol: '_load_glb_from_buffer_ffi', assetId: 'thermion_dart') -external void load_glb_from_buffer_ffi( - ffi.Pointer sceneManager, - ffi.Pointer data, - int length, - int numInstances, - ffi.Pointer> callback, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Pointer>)>( - symbol: '_load_gltf_ffi', assetId: 'thermion_dart') -external void load_gltf_ffi( - ffi.Pointer sceneManager, - ffi.Pointer assetPath, - ffi.Pointer relativePath, - ffi.Pointer> callback, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, EntityId, - ffi.Pointer>)>( - symbol: '_create_instance_ffi', assetId: 'thermion_dart') -external void create_instance_ffi( - ffi.Pointer sceneManager, - int entityId, - ffi.Pointer> callback, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, EntityId, - ffi.Pointer>)>( - symbol: '_remove_entity_ffi', assetId: 'thermion_dart') -external void remove_entity_ffi( - ffi.Pointer viewer, - int asset, - ffi.Pointer> callback, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, - ffi.Pointer>)>( - symbol: '_clear_entities_ffi', assetId: 'thermion_dart') -external void clear_entities_ffi( - ffi.Pointer viewer, - ffi.Pointer> callback, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - EntityId, - ffi.Pointer, - ffi.Pointer>)>( - symbol: '_set_camera_ffi', assetId: 'thermion_dart') -external void set_camera_ffi( - ffi.Pointer viewer, - int asset, - ffi.Pointer nodeName, - ffi.Pointer> callback, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - EntityId, - ffi.Pointer, - ffi.Pointer, - ffi.Int)>(symbol: '_apply_weights_ffi', assetId: 'thermion_dart') -external void apply_weights_ffi( - ffi.Pointer sceneManager, - int asset, - ffi.Pointer entityName, - ffi.Pointer weights, - int count, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, EntityId, ffi.Int, ffi.Int)>( - symbol: '_set_animation_frame_ffi', assetId: 'thermion_dart') -external void set_animation_frame_ffi( - ffi.Pointer sceneManager, - int asset, - int animationIndex, - int animationFrame, -); - -@ffi.Native, EntityId, ffi.Int)>( - symbol: '_stop_animation_ffi', assetId: 'thermion_dart') -external void stop_animation_ffi( - ffi.Pointer sceneManager, - int asset, - int index, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, EntityId, - ffi.Pointer>)>( - symbol: '_get_animation_count_ffi', assetId: 'thermion_dart') -external void get_animation_count_ffi( - ffi.Pointer sceneManager, - int asset, - ffi.Pointer> callback, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - EntityId, - ffi.Pointer, - ffi.Int, - ffi.Pointer>)>( - symbol: '_get_animation_name_ffi', assetId: 'thermion_dart') -external void get_animation_name_ffi( - ffi.Pointer sceneManager, - int asset, - ffi.Pointer outPtr, - int index, - ffi.Pointer> callback, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - EntityId, - EntityId, - ffi.Pointer, - ffi.Int, - ffi.Pointer>)>( - symbol: '_get_morph_target_name_ffi', assetId: 'thermion_dart') -external void get_morph_target_name_ffi( - ffi.Pointer sceneManager, - int assetEntity, - int childEntity, - ffi.Pointer outPtr, - int index, - ffi.Pointer> callback, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, EntityId, EntityId, - ffi.Pointer>)>( - symbol: '_get_morph_target_name_count_ffi', assetId: 'thermion_dart') -external void get_morph_target_name_count_ffi( - ffi.Pointer sceneManager, - int asset, - int childEntity, - ffi.Pointer> callback, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - EntityId, - ffi.Pointer, - ffi.Int, - ffi.Pointer>)>( - symbol: '_set_morph_target_weights_ffi', assetId: 'thermion_dart') -external void set_morph_target_weights_ffi( - ffi.Pointer sceneManager, - int asset, - ffi.Pointer morphData, - int numWeights, - ffi.Pointer> callback, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, EntityId, - ffi.Pointer>)>( - symbol: '_update_bone_matrices_ffi', assetId: 'thermion_dart') -external void update_bone_matrices_ffi( - ffi.Pointer sceneManager, - int asset, - ffi.Pointer> callback, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - EntityId, - ffi.Int, - ffi.Int, - ffi.Pointer, - ffi.Pointer>)>( - symbol: '_set_bone_transform_ffi', assetId: 'thermion_dart') -external void set_bone_transform_ffi( - ffi.Pointer sceneManager, - int asset, - int skinIndex, - int boneIndex, - ffi.Pointer transform, - ffi.Pointer> callback, -); - -@ffi.Native, ffi.Bool)>( - symbol: '_set_post_processing_ffi', assetId: 'thermion_dart') -external void set_post_processing_ffi( - ffi.Pointer viewer, - bool enabled, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, EntityId, - ffi.Pointer>)>( - symbol: '_reset_to_rest_pose_ffi', assetId: 'thermion_dart') -external void reset_to_rest_pose_ffi( - ffi.Pointer sceneManager, - int entityId, - ffi.Pointer> callback, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Int, - ffi.Pointer, - ffi.Int, - ffi.Int, - ffi.Pointer, - ffi.Pointer>)>( - symbol: '_create_geometry_ffi', assetId: 'thermion_dart') -external void create_geometry_ffi( - ffi.Pointer viewer, - ffi.Pointer vertices, - int numVertices, - ffi.Pointer indices, - int numIndices, - int primitiveType, - ffi.Pointer materialPath, - ffi.Pointer> callback, -); - -typedef LoadFilamentResourceFromOwner - = ffi.Pointer>; -typedef LoadFilamentResourceFromOwnerFunction = ResourceBuffer Function( - ffi.Pointer, ffi.Pointer); - -final class ResourceBuffer extends ffi.Struct { - external ffi.Pointer data; - - @ffi.Int32() - external int size; - - @ffi.Int32() - external int id; -} - -typedef FreeFilamentResourceFromOwner - = ffi.Pointer>; -typedef FreeFilamentResourceFromOwnerFunction = ffi.Void Function( - ResourceBuffer, ffi.Pointer); -typedef DartFreeFilamentResourceFromOwnerFunction = void Function( - ResourceBuffer, ffi.Pointer); - -/// 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 - = ffi.Pointer>; -typedef FilamentRenderCallbackFunction = ffi.Void Function( - ffi.Pointer owner); -typedef ThermionDartRenderCallbackFunction = void Function( - ffi.Pointer owner); - -const int __bool_true_false_are_defined = 1; - -const int true1 = 1; - -const int false1 = 0; diff --git a/thermion_dart/lib/thermion_dart/entities/gizmo.dart b/thermion_dart/lib/thermion_dart/entities/gizmo.dart index 29998407..2cde276e 100644 --- a/thermion_dart/lib/thermion_dart/entities/gizmo.dart +++ b/thermion_dart/lib/thermion_dart/entities/gizmo.dart @@ -86,7 +86,6 @@ class Gizmo extends AbstractGizmo { } Future attach(ThermionEntity entity) async { - print("Attaching"); _activeAxis = null; if (entity == _activeEntity) { return; @@ -102,7 +101,7 @@ class Gizmo extends AbstractGizmo { } _activeEntity = entity; await _viewer.setGizmoVisibility(true); - await _viewer.setParent(center, entity, preserveScaling: true); + await _viewer.setParent(center, entity, preserveScaling: false); _boundingBoxController.sink.add(await _viewer.getViewportBoundingBox(x)); } diff --git a/thermion_dart/lib/thermion_dart/scene.dart b/thermion_dart/lib/thermion_dart/scene.dart index 76d86d43..12f1b330 100644 --- a/thermion_dart/lib/thermion_dart/scene.dart +++ b/thermion_dart/lib/thermion_dart/scene.dart @@ -1,48 +1,233 @@ +import 'dart:convert'; import 'package:thermion_dart/thermion_dart/thermion_viewer.dart'; -import 'dart:async'; +import 'package:vector_math/vector_math_64.dart'; -/// -/// For now, this class just holds the entities that have been loaded (though not necessarily visible in the Filament Scene). -/// -abstract class Scene { - /// - /// The last entity clicked/tapped in the viewport (internally, the result of calling pick); - ThermionEntity? selected; +class SceneV2 { + final Map assets; + final List lights; + List cameras; + final List entities; + EnvironmentInfo? environment; - /// - /// A Stream updated whenever an entity is added/removed from the scene. - /// - Stream get onUpdated; + SceneV2({ + Map? assets, + List? lights, + List? cameras, + List? entities, + this.environment, + }) : assets = assets ?? {}, + lights = lights ?? [], + cameras = cameras ?? [], + entities = entities ?? []; - /// - /// A Stream containing every ThermionEntity added to the scene (i.e. via [loadGlb], [loadGltf] or [addLight]). - /// This is provided for convenience so you can set listeners in front-end widgets that can respond to entity loads without manually passing around the ThermionEntity returned from those methods. - /// - Stream get onLoad; + void addAsset(String uri, AssetType type) { + assets[uri] = AssetInfo(uri: uri, type: type); + } - /// - /// A Stream containing every ThermionEntity removed from the scene (i.e. via [removeEntity], [clearEntities], [removeLight] or [clearLights]). + void addLight(LightInfo light) { + lights.add(light); + } - Stream get onUnload; + void clearAssets() { + assets.clear(); + } - /// - /// Lists all light entities currently loaded (not necessarily active in the scene). Does not account for instances. - /// - Iterable listLights(); + void clearLights() { + lights.clear(); + } - /// - /// Lists all entities currently loaded (not necessarily active in the scene). Does not account for instances. - /// - Iterable listEntities(); + void setCamera(Matrix4 modelMatrix, Matrix4 projectionMatrix) { + var camera = cameras.firstWhere((cam) => cam.isActive); + camera.modelMatrix = modelMatrix; + camera.projectionMatrix = projectionMatrix; + } - /// - /// Attach the gizmo to the specified entity. - /// - void select(ThermionEntity entity); + void addEntity(String assetUri, Matrix4 transform) { + if (assets.containsKey(assetUri)) { + entities.add(EntityInfo(assetUri: assetUri, transform: transform)); + } else { + throw Exception('Asset not found: $assetUri'); + } + } - /// - /// - /// - void registerEntity(ThermionEntity entity); + void setEnvironment(String? skyboxUri, String? iblUri) { + environment = EnvironmentInfo(skyboxUri: skyboxUri, iblUri: iblUri); + } -} \ No newline at end of file + Map toJson() => { + 'assets': assets.map((key, value) => MapEntry(key, value.toJson())), + 'lights': lights.map((light) => light.toJson()).toList(), + 'cameras': cameras.map((camera) => camera.toJson()), + 'entities': entities.map((entity) => entity.toJson()).toList(), + 'environment': environment?.toJson(), + }; + + String toJsonString() => jsonEncode(toJson()); + + static SceneV2 fromJson(Map json) { + return SceneV2( + assets: (json['assets'] as Map).map( + (key, value) => MapEntry(key, AssetInfo.fromJson(value)), + ), + lights: (json['lights'] as List) + .map((light) => LightInfo.fromJson(light)) + .toList(), + cameras: json['cameras'].map((camera) => CameraInfo.fromJson), + entities: (json['entities'] as List) + .map((entity) => EntityInfo.fromJson(entity)) + .toList(), + environment: json['environment'] != null + ? EnvironmentInfo.fromJson(json['environment']) + : null, + ); + } + + static SceneV2 fromJsonString(String jsonString) => + fromJson(jsonDecode(jsonString)); +} + +class AssetInfo { + final String uri; + final AssetType type; + + AssetInfo({required this.uri, required this.type}); + + Map toJson() => { + 'uri': uri, + 'type': type.toString().split('.').last, + }; + + static AssetInfo fromJson(Map json) { + return AssetInfo( + uri: json['uri'], + type: AssetType.values.firstWhere( + (e) => e.toString().split('.').last == json['type'], + orElse: () => AssetType.glb), + ); + } +} + +enum AssetType { glb, gltf, geometryPrimitive } + +class LightInfo { + final LightType type; + final Vector3 position; + final Vector3 direction; + final Color color; + final double intensity; + + LightInfo({ + required this.type, + required this.position, + required this.direction, + required this.color, + required this.intensity, + }); + + Map toJson() => { + 'type': type.toString().split('.').last, + 'position': [position.x, position.y, position.z], + 'direction': [direction.x, direction.y, direction.z], + 'color': color.toJson(), + 'intensity': intensity, + }; + + static LightInfo fromJson(Map json) { + return LightInfo( + type: LightType.values.firstWhere((e) => e.name == json['type']), + position: Vector3.array(json['position'].cast()), + direction: Vector3.array(json['direction'].cast()), + color: Color.fromJson(json['color']), + intensity: json['intensity'], + ); + } +} + +class CameraInfo { + final bool isActive; + Matrix4 modelMatrix; + Matrix4 projectionMatrix; + + CameraInfo( + {required this.isActive, + required this.modelMatrix, + required this.projectionMatrix}); + + Map toJson() => { + 'modelMatrix': modelMatrix.storage, + 'projectionMatrix': projectionMatrix.storage, + 'isActive': isActive, + }; + + static CameraInfo fromJson(Map json) { + return CameraInfo( + modelMatrix: + Matrix4.fromFloat64List(json['modelMatrix'].cast()), + projectionMatrix: + Matrix4.fromFloat64List(json['modelMatrix'].cast()), + isActive: json["isActive"]); + } +} + +class EntityInfo { + final String assetUri; + final Matrix4 transform; + + EntityInfo({required this.assetUri, required this.transform}); + + Map toJson() => { + 'assetUri': assetUri, + 'transform': transform.storage, + }; + + static EntityInfo fromJson(Map json) { + return EntityInfo( + assetUri: json['assetUri'], + transform: Matrix4.fromList(List.from(json['transform'])), + ); + } +} + +class EnvironmentInfo { + final String? skyboxUri; + final String? iblUri; + + EnvironmentInfo({this.skyboxUri, this.iblUri}); + + Map toJson() => { + 'skyboxUri': skyboxUri, + 'iblUri': iblUri, + }; + + static EnvironmentInfo fromJson(Map json) { + return EnvironmentInfo( + skyboxUri: json['skyboxUri'], + iblUri: json['iblUri'], + ); + } +} + +class Color { + final double r; + final double g; + final double b; + final double a; + + Color({required this.r, required this.g, required this.b, this.a = 1.0}); + + Map toJson() => { + 'r': r, + 'g': g, + 'b': b, + 'a': a, + }; + + static Color fromJson(Map json) { + return Color( + r: json['r'], + g: json['g'], + b: json['b'], + a: json['a'], + ); + } +} diff --git a/thermion_dart/lib/thermion_dart/scene_impl.dart b/thermion_dart/lib/thermion_dart/scene_impl.dart deleted file mode 100644 index a31c8848..00000000 --- a/thermion_dart/lib/thermion_dart/scene_impl.dart +++ /dev/null @@ -1,125 +0,0 @@ -import 'dart:async'; -import 'package:thermion_dart/thermion_dart/scene.dart'; -import 'thermion_viewer.dart'; - -/// -/// For now, this class just holds the entities that have been loaded (though not necessarily visible in the Filament Scene). -/// -class SceneImpl extends Scene { - ThermionViewer controller; - - SceneImpl(this.controller); - - @override - ThermionEntity? selected; - - final _onUpdatedController = StreamController.broadcast(); - @override - Stream get onUpdated => _onUpdatedController.stream; - - final _onLoadController = StreamController.broadcast(); - @override - Stream get onLoad => _onLoadController.stream; - - final _onUnloadController = StreamController.broadcast(); - @override - Stream get onUnload => _onUnloadController.stream; - - final _lights = {}; - final _entities = {}; - - void registerLight(ThermionEntity entity) { - _lights.add(entity); - _onLoadController.sink.add(entity); - _onUpdatedController.add(true); - } - - void unregisterLight(ThermionEntity entity) async { - var children = await controller.getChildEntities(entity, true); - if (selected == entity || children.contains(selected)) { - selected = null; - controller.gizmo?.detach(); - } - _lights.remove(entity); - _onUnloadController.add(entity); - _onUpdatedController.add(true); - } - - void unregisterEntity(ThermionEntity entity) async { - var children = await controller.getChildEntities(entity, true); - if (selected == entity || children.contains(selected)) { - selected = null; - - controller.gizmo?.detach(); - } - _entities.remove(entity); - _onUnloadController.add(entity); - _onUpdatedController.add(true); - } - - void registerEntity(ThermionEntity entity) { - _entities.add(entity); - _onLoadController.sink.add(entity); - _onUpdatedController.add(true); - } - - void clearLights() { - for (final light in _lights) { - if (selected == light) { - selected = null; - controller.gizmo?.detach(); - } - _onUnloadController.add(light); - } - - _lights.clear(); - _onUpdatedController.add(true); - } - - void clearEntities() { - for (final entity in _entities) { - if (selected == entity) { - selected = null; - controller.gizmo?.detach(); - } - _onUnloadController.add(entity); - } - _entities.clear(); - _onUpdatedController.add(true); - } - - /// - /// Lists all entities currently loaded (not necessarily active in the scene). - /// - Iterable listLights() { - return _lights; - } - - @override - Iterable listEntities() { - return _entities; - } - - void registerSelected(ThermionEntity entity) { - selected = entity; - _onUpdatedController.add(true); - } - - void unregisterSelected() { - selected = null; - _onUpdatedController.add(true); - } - - @override - void select(ThermionEntity entity) { - selected = entity; - controller.gizmo?.attach(entity); - _onUpdatedController.add(true); - } - - Future dispose() async { - await _onLoadController.close(); - await _onUnloadController.close(); - await _onUpdatedController.close(); - } -} diff --git a/thermion_dart/lib/thermion_dart/thermion_viewer.dart b/thermion_dart/lib/thermion_dart/thermion_viewer.dart index 24be69a7..48a17df7 100644 --- a/thermion_dart/lib/thermion_dart/thermion_viewer.dart +++ b/thermion_dart/lib/thermion_dart/thermion_viewer.dart @@ -1,885 +1,6 @@ -import 'dart:math'; -import 'dart:typed_data'; +library thermion_viewer; -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'; -import 'package:animation_tools_dart/animation_tools_dart.dart'; - -// a handle that can be safely passed back to the rendering layer to manipulate an Entity -typedef ThermionEntity = int; - -// "picking" means clicking/tapping on the viewport, and unprojecting the X/Y coordinate to determine whether any renderable entities were present at those coordinates. -typedef FilamentPickResult = ({ThermionEntity entity, double x, double y}); - -enum LightType { - SUN, //!< Directional light that also draws a sun's disk in the sky. - DIRECTIONAL, //!< Directional light, emits light in a given direction. - POINT, //!< Point light, emits light from a position, in all directions. - FOCUSED_SPOT, //!< Physically correct spot light. - SPOT, -} - -enum ShadowType { - PCF, //!< percentage-closer filtered shadows (default) - VSM, //!< variance shadows - DPCF, //!< PCF with contact hardening simulation - PCSS, //!< PCF with soft shadows and contact hardening -} - -// copied from filament/backened/DriverEnums.h -enum PrimitiveType { - // don't change the enums values (made to match GL) - POINTS, //!< points - LINES, //!< lines - UNUSED1, - LINE_STRIP, //!< line strip - TRIANGLES, //!< triangles - TRIANGLE_STRIP, //!< triangle strip -} - -enum ToneMapper { ACES, FILMIC, LINEAR } - -// see filament Manipulator.h for more details -enum ManipulatorMode { ORBIT, MAP, FREE_FLIGHT } - -class TextureDetails { - final int textureId; - - // both width and height are in physical, not logical pixels - final int width; - final int height; - - TextureDetails( - {required this.textureId, required this.width, required this.height}); -} - -abstract class ThermionViewer { - Future get initialized; - - /// - /// The current dimensions of the viewport (in physical pixels). - /// - var viewportDimensions = (0.0,0.0); - - /// - /// The current ratio of logical to physical pixels. - /// - late double pixelRatio; - - /// - /// The result(s) of calling [pick] (see below). - /// This may be a broadcast stream, so you should ensure you have subscribed to this stream before calling [pick]. - /// If [pick] is called without an active subscription to this stream, the results will be silently discarded. - /// - Stream get pickResult; - - /// - /// The result(s) of calling [pickGizmo] (see below). - /// - Stream get gizmoPickResult; - - /// - /// Whether the controller is currently rendering at [framerate]. - /// - bool get rendering; - - /// - /// Set to true to continuously render the scene at the framerate specified by [setFrameRate] (60 fps by default). - /// - Future setRendering(bool render); - - /// - /// Render a single frame. - /// - Future render(); - - /// - /// Render a single frame to the viewport and copy the pixel buffer to [out]. - /// - Future capture(); - - /// - /// Sets the framerate for continuous rendering when [setRendering] is enabled. - /// - Future setFrameRate(int framerate); - - /// - /// Destroys/disposes the viewer (including the entire scene). You cannot use the viewer after calling this method. - /// - Future dispose(); - - /// - /// Set the background image to [path] (which should have a file extension .png, .jpg, or .ktx). - /// This will be rendered at the maximum depth (i.e. behind all other objects including the skybox). - /// If [fillHeight] is false, the image will be rendered at its original size. Note this may cause issues with pixel density so be sure to specify the correct resolution - /// If [fillHeight] is true, the image will be stretched/compressed to fit the height of the viewport. - /// - Future setBackgroundImage(String path, {bool fillHeight = false}); - - /// - /// Moves the background image to the relative offset from the origin (bottom-left) specified by [x] and [y]. - /// If [clamp] is true, the image cannot be positioned outside the bounds of the viewport. - /// - Future setBackgroundImagePosition(double x, double y, {bool clamp = false}); - - /// - /// Removes the background image. - /// - Future clearBackgroundImage(); - - /// - /// Sets the color for the background plane (positioned at the maximum depth, i.e. behind all other objects including the skybox). - /// - Future setBackgroundColor(double r, double g, double b, double alpha); - - /// - /// Load a skybox from [skyboxPath] (which must be a .ktx file) - /// - Future loadSkybox(String skyboxPath); - - /// - /// Removes the skybox from the scene. - /// - Future removeSkybox(); - - /// - /// Creates an indirect light by loading the reflections/irradiance from the KTX file. - /// Only one indirect light can be active at any given time; if an indirect light has already been loaded, it will be replaced. - /// - Future loadIbl(String lightingPath, {double intensity = 30000}); - - /// - /// Creates a indirect light with the given color. - /// Only one indirect light can be active at any given time; if an indirect light has already been loaded, it will be replaced. - /// - Future createIbl(double r, double g, double b, double intensity); - - /// - /// Rotates the IBL & skybox. - /// - Future rotateIbl(Matrix3 rotation); - - /// - /// Removes the image-based light from the scene. - /// - Future removeIbl(); - - /// - /// Add a light to the scene. - /// See LightManager.h for details - /// Note that [sunAngularRadius] is in degrees, - /// whereas [spotLightConeInner] and [spotLightConeOuter] are in radians - /// - Future addLight( - LightType type, - double colour, - double intensity, - double posX, - double posY, - double posZ, - double dirX, - double dirY, - double dirZ, - {double falloffRadius = 1.0, - double spotLightConeInner = pi / 8, - double spotLightConeOuter = pi / 4, - double sunAngularRadius = 0.545, - double sunHaloSize = 10.0, - double sunHaloFallof = 80.0, - bool castShadows = true}); - - Future removeLight(ThermionEntity light); - - /// - /// Remove all lights (excluding IBL) from the scene. - /// - Future clearLights(); - - /// - /// Load the .glb asset at the given path and insert into the scene. - /// Specify [numInstances] to create multiple instances (this is more efficient than dynamically instantating at a later time). You can then retrieve the created instances with [getInstances]. - /// If you want to be able to call [createInstance] at a later time, you must pass true for [keepData]. - /// If [keepData] is false, the source glTF data will be released and [createInstance] will throw an exception. - /// - Future loadGlb(String path, - {int numInstances = 1, bool keepData = false}); - - /// - /// Load the .glb asset from the specified buffer and insert into the scene. - /// Specify [numInstances] to create multiple instances (this is more efficient than dynamically instantating at a later time). You can then retrieve the created instances with [getInstances]. - /// If you want to be able to call [createInstance] at a later time, you must pass true for [keepData]. - /// If [keepData] is false, the source glTF data will be released and [createInstance] will throw an exception. - /// - Future loadGlbFromBuffer(Uint8List data, - {int numInstances = 1, bool keepData = false}); - - /// - /// Create a new instance of [entity]. - /// - Future createInstance(ThermionEntity entity); - - /// - /// Returns the number of instances of the asset associated with [entity]. - /// - Future getInstanceCount(ThermionEntity entity); - - /// - /// Returns all instances of [entity]. - /// - Future> getInstances(ThermionEntity entity); - - /// - /// Load the .gltf asset at the given path and insert into the scene. - /// [relativeResourcePath] is the folder path where the glTF resources are stored; - /// this is usually the parent directory of the .gltf file itself. - /// - /// See [loadGlb] for an explanation of [keepData]. - /// - Future loadGltf(String path, String relativeResourcePath, - {bool keepData = false}); - - /// - /// Called by `FilamentGestureDetector`. You probably don't want to call this yourself. - /// - Future panStart(double x, double y); - - /// - /// Called by `FilamentGestureDetector`. You probably don't want to call this yourself. - /// - Future panUpdate(double x, double y); - - /// - /// Called by `FilamentGestureDetector`. You probably don't want to call this yourself. - /// - Future panEnd(); - - /// - /// Called by `FilamentGestureDetector`. You probably don't want to call this yourself. - /// - Future rotateStart(double x, double y); - - /// - /// Called by `FilamentGestureDetector`. You probably don't want to call this yourself. - /// - Future rotateUpdate(double x, double y); - - /// - /// Called by `FilamentGestureDetector`. You probably don't want to call this yourself. - /// - Future rotateEnd(); - - /// - /// Set the weights for all morph targets in [entity] to [weights]. - /// Note that [weights] must contain values for ALL morph targets, but no exception will be thrown if you don't do so (you'll just get incorrect results). - /// If you only want to set one value, set all others to zero (check [getMorphTargetNames] if you need the get a list of all morph targets). - /// IMPORTANT - this accepts the actual ThermionEntity with the relevant morph targets (unlike [getMorphTargetNames], which uses the parent entity and the child mesh name). - /// Use [getChildEntityByName] if you are setting the weights for a child mesh. - /// - Future setMorphTargetWeights(ThermionEntity entity, List weights); - - /// - /// Gets the names of all morph targets for the child renderable [childEntity] under [entity]. - /// - Future> getMorphTargetNames( - ThermionEntity entity, ThermionEntity childEntity); - - /// - /// Gets the names of all bones for the armature at [skinIndex] under the specified [entity]. - /// - Future> getBoneNames(ThermionEntity entity, {int skinIndex = 0}); - - /// - /// Gets the names of all glTF animations embedded in the specified entity. - /// - Future> getAnimationNames(ThermionEntity entity); - - /// - /// Returns the length (in seconds) of the animation at the given index. - /// - Future getAnimationDuration( - ThermionEntity entity, int animationIndex); - - /// - /// Animate the morph targets in [entity]. See [MorphTargetAnimation] for an explanation as to how to construct the animation frame data. - /// This method will check the morph target names specified in [animation] against the morph target names that actually exist exist under [meshName] in [entity], - /// throwing an exception if any cannot be found. - /// It is permissible for [animation] to omit any targets that do exist under [meshName]; these simply won't be animated. - /// - Future setMorphAnimationData( - ThermionEntity entity, MorphAnimationData animation, - {List? targetMeshNames}); - - /// - /// Clear all current morph animations for [entity]. - /// - Future clearMorphAnimationData(ThermionEntity entity); - - /// - /// Resets all bones in the given entity to their rest pose. - /// This should be done before every call to addBoneAnimation. - /// - Future resetBones(ThermionEntity entity); - - /// - /// Enqueues and plays the [animation] for the specified bone(s). - /// By default, frame data is interpreted as being in *parent* bone space; - /// a 45 degree around Y means the bone will rotate 45 degrees around the - /// Y axis of the parent bone *in its current orientation*. - /// (i.e NOT the parent bone's rest position!). - /// Currently, only [Space.ParentBone] and [Space.Model] are supported; if you want - /// to transform to another space, you will need to do so manually. - /// - /// [fadeInInSecs]/[fadeOutInSecs]/[maxDelta] are used to cross-fade between - /// the current active glTF animation ("animation1") and the animation you - /// set via this method ("animation2"). The bone orientations will be - /// linearly interpolated between animation1 and animation2; at time 0, - /// the orientation will be 100% animation1, at time [fadeInInSecs], the - /// animation will be ((1 - maxDelta) * animation1) + (maxDelta * animation2). - /// This will be applied in reverse after [fadeOutInSecs]. - /// - /// - Future addBoneAnimation(ThermionEntity entity, BoneAnimationData animation, - {int skinIndex = 0, - double fadeInInSecs = 0.0, - double fadeOutInSecs = 0.0, - double maxDelta = 1.0}); - - /// - /// Gets the entity representing the bone at [boneIndex]/[skinIndex]. - /// The returned entity is only intended for use with [getWorldTransform]. - /// - Future getBone(ThermionEntity parent, int boneIndex, - {int skinIndex = 0}); - - /// - /// Gets the local (relative to parent) transform for [entity]. - /// - Future getLocalTransform(ThermionEntity entity); - - /// - /// Gets the world transform for [entity]. - /// - Future getWorldTransform(ThermionEntity entity); - - /// - /// Gets the inverse bind (pose) matrix for the bone. - /// Note that [parent] must be the ThermionEntity returned by [loadGlb/loadGltf], not any other method ([getChildEntity] etc). - /// This is because all joint information is internally stored with the parent entity. - /// - Future getInverseBindMatrix(ThermionEntity parent, int boneIndex, - {int skinIndex = 0}); - - /// - /// Sets the transform (relative to its parent) for [entity]. - /// - Future setTransform(ThermionEntity entity, Matrix4 transform); - - /// - /// Updates the bone matrices for [entity] (which must be the ThermionEntity - /// returned by [loadGlb/loadGltf]). - /// Under the hood, this just calls [updateBoneMatrices] on the Animator - /// instance of the relevant FilamentInstance (which uses the local - /// bone transform and the inverse bind matrix to set the bone matrix). - /// - Future updateBoneMatrices(ThermionEntity entity); - - /// - /// Directly set the bone matrix for the bone at the given index. - /// Don't call this manually unless you know what you're doing. - /// - Future setBoneTransform( - ThermionEntity entity, int boneIndex, Matrix4 transform, - {int skinIndex = 0}); - - /// - /// Removes/destroys the specified entity from the scene. - /// [entity] will no longer be a valid handle after this method is called; ensure you immediately discard all references once this method is complete. - /// - Future removeEntity(ThermionEntity entity); - - /// - /// Removes/destroys all renderable entities from the scene (including cameras). - /// All [ThermionEntity] handles will no longer be valid after this method is called; ensure you immediately discard all references to all entities once this method is complete. - /// - Future clearEntities(); - - /// - /// Called by `FilamentGestureDetector`. You probably don't want to call this yourself. - /// - Future zoomBegin(); - - /// - /// Called by `FilamentGestureDetector`. You probably don't want to call this yourself. - /// - Future zoomUpdate(double x, double y, double z); - - /// - /// Called by `FilamentGestureDetector`. You probably don't want to call this yourself. - /// - Future zoomEnd(); - - /// - /// Schedules the glTF animation at [index] in [entity] to start playing on the next frame. - /// - Future playAnimation(ThermionEntity entity, int index, - {bool loop = false, - bool reverse = false, - bool replaceActive = true, - double crossfade = 0.0, - double startOffset = 0.0}); - - /// - /// Schedules the glTF animation at [index] in [entity] to start playing on the next frame. - /// - Future playAnimationByName(ThermionEntity entity, String name, - {bool loop = false, - bool reverse = false, - bool replaceActive = true, - double crossfade = 0.0}); - - Future setAnimationFrame( - ThermionEntity entity, int index, int animationFrame); - - Future stopAnimation(ThermionEntity entity, int animationIndex); - Future stopAnimationByName(ThermionEntity entity, String name); - - /// - /// Sets the current scene camera to the glTF camera under [name] in [entity]. - /// - Future setCamera(ThermionEntity entity, String? name); - - /// - /// Sets the current scene camera to the main camera (which is always available and added to every scene by default). - /// - Future setMainCamera(); - - /// - /// Returns the entity associated with the main camera. - /// - Future getMainCamera(); - - /// - /// Sets the horizontal field of view (if [horizontal] is true) or vertical field of view for the currently active camera to [degrees]. - /// The aspect ratio of the current viewport is used. - /// - Future setCameraFov(double degrees, {bool horizontal = true}); - - /// - /// Gets the field of view (in degrees). - /// - Future getCameraFov(bool horizontal); - - /// - /// Sets the tone mapping (requires postprocessing). - /// - Future setToneMapping(ToneMapper mapper); - - /// - /// Sets the strength of the bloom. - /// - Future setBloom(double bloom); - - /// - /// Sets the focal length of the camera. Default value is 28.0. - /// - Future setCameraFocalLength(double focalLength); - - /// - /// Sets the distance (in world units) to the near/far planes for the active camera. Default values are 0.05/1000.0. See Camera.h for details. - /// - Future setCameraCulling(double near, double far); - - /// - /// Get the distance (in world units) to the near plane for the active camera. - /// - @Deprecated("Use getCameraNear") - Future getCameraCullingNear(); - - /// - /// Get the distance (in world units) to the near plane for the active camera. - /// - Future getCameraNear(); - - /// - /// Get the distance (in world units) to the far culling plane for the active camera. - /// - Future getCameraCullingFar(); - - /// - /// - /// - Future setCameraLensProjection(double near, double far, double aspect, double focalLength); - - /// - /// Sets the focus distance for the camera. - /// - Future setCameraFocusDistance(double focusDistance); - - /// - /// Get the camera position in world space. - /// - Future getCameraPosition(); - - /// - /// Get the camera's model matrix. - /// - Future getCameraModelMatrix(); - - /// - /// Get the camera's view matrix. See Camera.h for more details. - /// - Future getCameraViewMatrix(); - - /// - /// Get the camera's projection matrix. See Camera.h for more details. - /// - Future getCameraProjectionMatrix(); - - /// - /// Get the camera's culling projection matrix. See Camera.h for more details. - /// - Future getCameraCullingProjectionMatrix(); - - /// - /// Get the camera's culling frustum in world space. Returns a (vector_math) [Frustum] instance where plane0-plane6 define the left, right, bottom, top, far and near planes respectively. - /// See Camera.h and (filament) Frustum.h for more details. - /// - Future getCameraFrustum(); - - /// - /// Set the camera position in world space. Note this is not persistent - any viewport navigation will reset the camera transform. - /// - Future setCameraPosition(double x, double y, double z); - - /// - /// Get the camera rotation matrix. - /// - Future getCameraRotation(); - - /// - /// Repositions the camera to the last vertex of the bounding box of [entity], looking at the penultimate vertex. - /// - Future moveCameraToAsset(ThermionEntity entity); - - /// - /// Enables/disables frustum culling. Currently we don't expose a method for manipulating the camera projection/culling matrices so this is your only option to deal with unwanted near/far clipping. - /// - Future setViewFrustumCulling(bool enabled); - - /// - /// Sets the camera exposure. - /// - Future setCameraExposure( - double aperture, double shutterSpeed, double sensitivity); - - /// - /// Rotate the camera by [rads] around the given axis. - /// - Future setCameraRotation(Quaternion quaternion); - - /// - /// Sets the camera model matrix. - /// - @Deprecated("Will be superseded by setCameraModelMatrix4") - Future setCameraModelMatrix(List matrix); - - /// - /// Sets the camera model matrix. - /// - Future setCameraModelMatrix4(Matrix4 matrix); - - /// - /// Sets the `baseColorFactor` property for the material at index [materialIndex] in [entity] under node [meshName] to [color]. - /// - @Deprecated("Use setMaterialPropertyFloat4 instead") - Future setMaterialColor(ThermionEntity entity, String meshName, - int materialIndex, double r, double g, double b, double a); - - /// - /// Sets the material property [propertyName] under material [materialIndex] for [entity] to [value]. - /// [entity] must have a Renderable attached. - /// - Future setMaterialPropertyFloat4(ThermionEntity entity, String propertyName, - int materialIndex, double f1, double f2, double f3, double f4); - - /// - /// Sets the material property [propertyName] under material [materialIndex] for [entity] to [value]. - /// [entity] must have a Renderable attached. - /// - Future setMaterialPropertyFloat(ThermionEntity entity, String propertyName, int materialIndex, double value); - - /// - /// Scale [entity] to fit within the unit cube. - /// - Future transformToUnitCube(ThermionEntity entity); - - /// - /// Directly sets the world space position for [entity] to the given coordinates. - /// - Future setPosition(ThermionEntity entity, double x, double y, double z); - - /// - /// Set the world space position for [lightEntity] to the given coordinates. - /// - Future setLightPosition( - ThermionEntity lightEntity, double x, double y, double z); - - /// - /// Sets the world space direction for [lightEntity] to the given vector. - /// - Future setLightDirection(ThermionEntity lightEntity, Vector3 direction); - - /// - /// Directly sets the scale for [entity], skipping all collision detection. - /// - Future setScale(ThermionEntity entity, double scale); - - /// - /// Directly sets the rotation for [entity] to [rads] around the axis {x,y,z}, skipping all collision detection. - /// - Future setRotation( - ThermionEntity entity, double rads, double x, double y, double z); - - /// - /// Queues an update to the worldspace position for [entity] to {x,y,z}. - /// The actual update will occur on the next frame, and will be subject to collision detection. - /// - Future queuePositionUpdate( - ThermionEntity entity, double x, double y, double z, - {bool relative = false}); - - /// - /// TODO - /// - Future queuePositionUpdateFromViewportCoords( - ThermionEntity entity, double x, double y); - - /// - /// TODO - /// - Future queueRelativePositionUpdateWorldAxis(ThermionEntity entity, - double viewportX, double viewportY, double x, double y, double z); - - /// - /// Queues an update to the worldspace rotation for [entity]. - /// The actual update will occur on the next frame, and will be subject to collision detection. - /// - Future queueRotationUpdate( - ThermionEntity entity, double rads, double x, double y, double z, - {bool relative = false}); - - /// - /// Same as [queueRotationUpdate]. - /// - Future queueRotationUpdateQuat(ThermionEntity entity, Quaternion quat, - {bool relative = false}); - - /// - /// Enable/disable postprocessing (disabled by default). - /// - Future setPostProcessing(bool enabled); - - /// - /// Enable/disable shadows (disabled by default). - /// - Future setShadowsEnabled(bool enabled); - - /// - /// Set shadow type. - /// - Future setShadowType(ShadowType shadowType); - - /// - /// Set soft shadow options (ShadowType DPCF and PCSS) - /// - Future setSoftShadowOptions(double penumbraScale, double penumbraRatioScale); - - /// - /// Set antialiasing options. - /// - Future setAntiAliasing(bool msaa, bool fxaa, bool taa); - - /// - /// Sets the rotation for [entity] to the specified quaternion. - /// - Future setRotationQuat(ThermionEntity entity, Quaternion rotation); - - /// - /// Reveal the node [meshName] under [entity]. Only applicable if [hide] had previously been called; this is a no-op otherwise. - /// - Future reveal(ThermionEntity entity, String? meshName); - - /// - /// If [meshName] is provided, hide the node [meshName] under [entity], otherwise hide the root node for [entity]. - /// The entity still exists in memory, but is no longer being rendered into the scene. Call [reveal] to re-commence rendering. - /// - Future hide(ThermionEntity entity, String? meshName); - - /// - /// Used to select the entity in the scene at the given viewport coordinates. - /// Called by `FilamentGestureDetector` on a mouse/finger down event. You probably don't want to call this yourself. - /// This is asynchronous and will require 2-3 frames to complete - subscribe to the [pickResult] stream to receive the results of this method. - /// [x] and [y] must be in local logical coordinates (i.e. where 0,0 is at top-left of the ThermionWidget). - /// - void pick(int x, int y); - - /// - /// Used to test whether a Gizmo is at the given viewport coordinates. - /// Called by `FilamentGestureDetector` on a mouse/finger down event. You probably don't want to call this yourself. - /// This is asynchronous and will require 2-3 frames to complete - subscribe to the [gizmoPickResult] stream to receive the results of this method. - /// [x] and [y] must be in local logical coordinates (i.e. where 0,0 is at top-left of the ThermionWidget). - /// - void pickGizmo(int x, int y); - - /// - /// Retrieves the name assigned to the given ThermionEntity (usually corresponds to the glTF mesh name). - /// - String? getNameForEntity(ThermionEntity entity); - - /// - /// Sets the options for manipulating the camera via the viewport. - /// ManipulatorMode.FREE_FLIGHT and ManipulatorMode.MAP are currently unsupported and will throw an exception. - /// - @Deprecated("Use ThermionGestureHandler instead") - Future setCameraManipulatorOptions( - {ManipulatorMode mode = ManipulatorMode.ORBIT, - double orbitSpeedX = 0.01, - double orbitSpeedY = 0.01, - double zoomSpeed = 0.01}); - - /// - /// Returns all child entities under [parent]. - /// - Future> getChildEntities( - ThermionEntity parent, bool renderableOnly); - - /// - /// Finds the child entity named [childName] associated with the given parent. - /// Usually, [parent] will be the return value from [loadGlb]/[loadGltf] and [childName] will be the name of a node/mesh. - /// - Future getChildEntity( - ThermionEntity parent, String childName); - - /// - /// List the name of all child entities under the given entity. - /// - Future> getChildEntityNames(ThermionEntity entity, - {bool renderableOnly = true}); - - /// - /// If [recording] is set to true, each frame the framebuffer/texture will be written to /tmp/output_*.png. - /// This will impact performance; handle with care. - /// - Future setRecording(bool recording); - - /// - /// Sets the output directory where recorded PNGs will be placed. - /// - Future setRecordingOutputDirectory(String outputDirectory); - - /// - /// An [entity] will only be animatable after an animation component is attached. - /// Any calls to [playAnimation]/[setBoneAnimation]/[setMorphAnimation] will have no visual effect until [addAnimationComponent] has been called on the instance. - /// - Future addAnimationComponent(ThermionEntity entity); - - /// - /// Removes an animation component from [entity]. - /// - Future removeAnimationComponent(ThermionEntity entity); - - /// - /// Makes [entity] collidable. - /// This allows you to call [testCollisions] with any other entity ("entity B") to see if [entity] has collided with entity B. The callback will be invoked if so. - /// Alternatively, if [affectsTransform] is true and this entity collides with another entity, any queued position updates to the latter entity will be ignored. - /// - Future addCollisionComponent(ThermionEntity entity, - {void Function(int entityId1, int entityId2)? callback, - bool affectsTransform = false}); - - /// - /// Removes the collision component from [entity], meaning this will no longer be tested when [testCollisions] or [queuePositionUpdate] is called with another entity. - /// - Future removeCollisionComponent(ThermionEntity entity); - - /// - /// Creates a (renderable) entity with the specified geometry and adds to the scene. - /// - Future createGeometry(List vertices, List indices, - {String? materialPath, - List? normals, - PrimitiveType primitiveType = PrimitiveType.TRIANGLES}); - - /// - /// Gets the parent entity of [entity]. Returns null if the entity has no parent. - /// - 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]. - /// - Future setParent(ThermionEntity child, ThermionEntity parent, - {bool preserveScaling}); - - /// - /// Test all collidable entities against this entity to see if any have collided. - /// This method returns void; the relevant callback passed to [addCollisionComponent] will be fired if a collision is detected. - /// - Future testCollisions(ThermionEntity entity); - - /// - /// Sets the draw priority for the given entity. See RenderableManager.h for more details. - /// - Future setPriority(ThermionEntity entityId, int priority); - - /// - /// The Scene holds all loaded entities/lights. - /// - Scene get scene; - - /// - /// The gizmo for translating/rotating objects. Only one gizmo is present in the scene. - /// - AbstractGizmo? get gizmo; - - /// - /// 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 getViewportBoundingBox(ThermionEntity entity); - - /// - /// Filament assigns renderables to a numeric layer. - /// We place all scene assets in layer 0 (enabled by default), gizmos in layer 1 (enabled by default), world grid in layer 2 (disabled by default). - /// Use this method to toggle visibility of the respective layer. - /// - Future setLayerEnabled(int layer, bool enabled); - - /// - /// Show/hide the translation gizmo. - /// - Future setGizmoVisibility(bool visible); - - /// - /// Renders an outline around [entity] with the given color. - /// - Future setStencilHighlight(ThermionEntity entity, - {double r = 1.0, double g = 0.0, double b = 0.0}); - - /// - /// Removes the outline around [entity]. Noop if there was no highlight. - /// - Future removeStencilHighlight(ThermionEntity entity); - -} +export 'viewer/thermion_viewer_base.dart'; +export 'viewer/thermion_viewer_stub.dart' + if (dart.library.io) 'viewer/ffi/thermion_viewer_ffi.dart' + if (dart.library.js_interop) 'viewer/web/thermion_viewer_wasm.dart'; diff --git a/thermion_dart/lib/thermion_dart/utils/dart_resources.dart b/thermion_dart/lib/thermion_dart/utils/dart_resources.dart index e9b0f71d..fafe69de 100644 --- a/thermion_dart/lib/thermion_dart/utils/dart_resources.dart +++ b/thermion_dart/lib/thermion_dart/utils/dart_resources.dart @@ -1,6 +1,8 @@ import 'dart:ffi'; import 'dart:io'; -import '../compatibility/compatibility.dart'; + +import 'package:ffi/ffi.dart'; +import 'package:thermion_dart/thermion_dart/viewer/ffi/thermion_dart.g.dart'; class DartResourceLoader { static final _assets = {}; diff --git a/thermion_dart/lib/thermion_dart/geometry_helper.dart b/thermion_dart/lib/thermion_dart/utils/geometry.dart similarity index 72% rename from thermion_dart/lib/thermion_dart/geometry_helper.dart rename to thermion_dart/lib/thermion_dart/utils/geometry.dart index 8ddd1aa4..62660536 100644 --- a/thermion_dart/lib/thermion_dart/geometry_helper.dart +++ b/thermion_dart/lib/thermion_dart/utils/geometry.dart @@ -1,26 +1,14 @@ import 'dart:math'; -class Geometry { - final List vertices; - final List indices; - final List? normals; - - Geometry(this.vertices, this.indices, this.normals); - - void scale(double factor) { - for (int i = 0; i < vertices.length; i++) { - vertices[i] = vertices[i] * factor; - } - } -} +import 'package:thermion_dart/thermion_dart/viewer/shared_types/geometry.dart'; class GeometryHelper { -static Geometry sphere() { + static Geometry sphere({bool normals = true, bool uvs = true}) { int latitudeBands = 20; int longitudeBands = 20; List vertices = []; - List normals = []; + List _normals = []; List indices = []; for (int latNumber = 0; latNumber <= latitudeBands; latNumber++) { @@ -38,7 +26,7 @@ static Geometry sphere() { double z = sinPhi * sinTheta; vertices.addAll([x, y, z]); - normals.addAll([ + _normals.addAll([ x, y, z @@ -56,10 +44,10 @@ static Geometry sphere() { } } - return Geometry(vertices, indices, normals); + return Geometry(vertices, indices, normals: normals ? _normals : null); } - static Geometry cube() { + static Geometry cube({bool normals = true, bool uvs = true}) { final vertices = [ // Front face -1, -1, 1, @@ -98,7 +86,7 @@ static Geometry sphere() { -1, 1, -1, ]; - final normals = [ + final _normals = [ // Front face 0, 0, 1, 0, 0, 1, @@ -136,7 +124,7 @@ static Geometry sphere() { -1, 0, 0, ]; - final indices = [ + final indices = [ // Front face 0, 1, 2, 0, 2, 3, // Back face @@ -150,13 +138,13 @@ static Geometry sphere() { // Left face 20, 21, 22, 20, 22, 23 ]; - return Geometry(vertices, indices, normals); + return Geometry(vertices, indices, normals: normals ? _normals : null); } - static Geometry cylinder({double radius = 1.0, double length = 1.0}) { + static Geometry cylinder({double radius = 1.0, double length = 1.0, bool normals = true, bool uvs = true }) { int segments = 32; List vertices = []; - List normals = []; + List _normals = []; List indices = []; // Create vertices and normals @@ -167,11 +155,11 @@ static Geometry sphere() { // Top circle vertices.addAll([x, length / 2, z]); - normals.addAll([x / radius, 0, z / radius]); + _normals.addAll([x / radius, 0, z / radius]); // Bottom circle vertices.addAll([x, -length / 2, z]); - normals.addAll([x / radius, 0, z / radius]); + _normals.addAll([x / radius, 0, z / radius]); } // Create indices @@ -192,23 +180,23 @@ static Geometry sphere() { // Add center vertices and normals for top and bottom faces vertices.addAll([0, length / 2, 0]); // Top center - normals.addAll([0, 1, 0]); + _normals.addAll([0, 1, 0]); vertices.addAll([0, -length / 2, 0]); // Bottom center - normals.addAll([0, -1, 0]); + _normals.addAll([0, -1, 0]); // Add top and bottom face normals for (int i = 0; i <= segments; i++) { - normals.addAll([0, 1, 0]); // Top face normal - normals.addAll([0, -1, 0]); // Bottom face normal + _normals.addAll([0, 1, 0]); // Top face normal + _normals.addAll([0, -1, 0]); // Bottom face normal } - return Geometry(vertices, indices, normals); + return Geometry(vertices, indices, normals: normals ? _normals : null); } - static Geometry conic({double radius = 1.0, double length = 1.0}) { + static Geometry conic({double radius = 1.0, double length = 1.0, bool normals = true, bool uvs = true}) { int segments = 32; List vertices = []; - List normals = []; + List _normals = []; List indices = []; // Create vertices and normals @@ -219,53 +207,73 @@ static Geometry sphere() { // Base circle vertices.addAll([x, 0, z]); - + // Calculate normal for the side double nx = x / sqrt(x * x + length * length); double nz = z / sqrt(z * z + length * length); double ny = radius / sqrt(radius * radius + length * length); - normals.addAll([nx, ny, nz]); + _normals.addAll([nx, ny, nz]); } // Apex vertices.addAll([0, length, 0]); - normals.addAll([0, 1, 0]); // Normal at apex points straight up + _normals.addAll([0, 1, 0]); // Normal at apex points straight up // Create indices for (int i = 0; i < segments; i++) { - // Base face - indices.addAll([i, i + 1, segments + 1]); - // Side faces + // Base face (fixed to counterclockwise) + indices.addAll([segments + 1, i + 1, i]); + // Side faces (already correct) indices.addAll([i, segments, i + 1]); } // Add base face normals for (int i = 0; i <= segments; i++) { - normals.addAll([0, -1, 0]); // Base face normal + _normals.addAll([0, -1, 0]); // Base face normal } - return Geometry(vertices, indices, normals); + return Geometry(vertices, indices, normals: normals ? _normals : null); } - static Geometry plane({double width = 1.0, double height = 1.0}) { + static Geometry plane({double width = 1.0, double height = 1.0, bool normals = true, bool uvs = true}) { List vertices = [ - -width / 2, 0, -height / 2, - width / 2, 0, -height / 2, - width / 2, 0, height / 2, - -width / 2, 0, height / 2, + -width / 2, + 0, + -height / 2, + width / 2, + 0, + -height / 2, + width / 2, + 0, + height / 2, + -width / 2, + 0, + height / 2, ]; - List normals = [ - 0, 1, 0, - 0, 1, 0, - 0, 1, 0, - 0, 1, 0, + List _normals = [ + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, ]; List indices = [ - 0, 1, 2, - 0, 2, 3, + 0, + 2, + 1, + 0, + 3, + 2, ]; - return Geometry(vertices, indices, normals); + return Geometry(vertices, indices, normals: normals ? _normals : null); } -} \ No newline at end of file +} diff --git a/thermion_dart/lib/thermion_dart/utils/light_options.dart b/thermion_dart/lib/thermion_dart/utils/light_options.dart deleted file mode 100644 index 76f7b187..00000000 --- a/thermion_dart/lib/thermion_dart/utils/light_options.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:vector_math/vector_math_64.dart' as v; - -class LightOptions { - String? iblPath; - double iblIntensity; - int directionalType; - double directionalColor; - double directionalIntensity; - bool directionalCastShadows; - late v.Vector3 directionalPosition; - late v.Vector3 directionalDirection; - - LightOptions( - {required this.iblPath, - required this.iblIntensity, - required this.directionalType, - required this.directionalColor, - required this.directionalIntensity, - required this.directionalCastShadows, - v.Vector3? directionalDirection, - v.Vector3? directionalPosition}) { - this.directionalDirection = directionalDirection == null - ? v.Vector3(0, -1, 0) - : directionalDirection; - this.directionalPosition = directionalPosition == null - ? v.Vector3(0, 100, 0) - : directionalPosition; - } -} diff --git a/thermion_dart/lib/thermion_dart/matrix_helper.dart b/thermion_dart/lib/thermion_dart/utils/matrix.dart similarity index 89% rename from thermion_dart/lib/thermion_dart/matrix_helper.dart rename to thermion_dart/lib/thermion_dart/utils/matrix.dart index d384fc1b..42c5647a 100644 --- a/thermion_dart/lib/thermion_dart/matrix_helper.dart +++ b/thermion_dart/lib/thermion_dart/utils/matrix.dart @@ -1,5 +1,5 @@ // Helper function to convert double4x4 to Matrix4 -import 'package:thermion_dart/thermion_dart/compatibility/native/thermion_dart.g.dart'; +import 'package:thermion_dart/thermion_dart/viewer/ffi/thermion_dart.g.dart'; import 'package:vector_math/vector_math_64.dart'; import 'dart:ffi'; diff --git a/thermion_dart/lib/thermion_dart/utils/using_pointer.dart b/thermion_dart/lib/thermion_dart/utils/using_pointer.dart deleted file mode 100644 index d192ec03..00000000 --- a/thermion_dart/lib/thermion_dart/utils/using_pointer.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'dart:ffi'; - -import 'package:ffi/ffi.dart'; - -final allocator = calloc; - -void using(Pointer ptr, Future Function(Pointer ptr) function) async { - await function.call(ptr); - allocator.free(ptr); -} diff --git a/thermion_dart/lib/thermion_dart/viewer/events.dart b/thermion_dart/lib/thermion_dart/viewer/events.dart new file mode 100644 index 00000000..eddf168e --- /dev/null +++ b/thermion_dart/lib/thermion_dart/viewer/events.dart @@ -0,0 +1,86 @@ +import 'package:thermion_dart/thermion_dart/viewer/shared_types/entities.dart'; +import 'shared_types/shared_types.dart'; + +/// +/// To ensure we can easily store/recreate a particular, [ThermionViewer] will raise an event whenever an +/// entity is added/removed. +/// +enum EventType { EntityAdded, EntityRemoved, EntityHidden, EntityRevealed, ClearLights } + +/// +/// An "entity added" event must provide sufficient detail to enable that asset to be reloaded in future. +/// This requires a bit more legwork because entities may be lights (direct/indirect), geometry or gltf. +/// +enum EntityType { Geometry, Gltf, DirectLight, IBL } + +class SceneUpdateEvent { + late final ThermionEntity? entity; + late final EventType eventType; + + EntityType get addedEntityType { + if (_directLight != null) { + return EntityType.DirectLight; + } else if (_ibl != null) { + return EntityType.IBL; + } else if (_gltf != null) { + return EntityType.Gltf; + } else if (_geometry != null) { + return EntityType.Geometry; + } else { + throw Exception("Unknown entity type"); + } + } + + DirectLight? _directLight; + IBL? _ibl; + GLTF? _gltf; + Geometry? _geometry; + + SceneUpdateEvent.remove(this.entity) { + this.eventType = EventType.EntityRemoved; + } + + SceneUpdateEvent.reveal(this.entity) { + this.eventType = EventType.EntityRevealed; + } + + SceneUpdateEvent.hide(this.entity) { + this.eventType = EventType.EntityHidden; + } + + SceneUpdateEvent.addDirectLight(this.entity, this._directLight) { + this.eventType = EventType.EntityAdded; + } + + SceneUpdateEvent.addIbl(this.entity, this._ibl) { + this.eventType = EventType.EntityAdded; + } + + SceneUpdateEvent.addGltf(this.entity, this._gltf) { + this.eventType = EventType.EntityAdded; + } + + SceneUpdateEvent.addGeometry(this.entity, this._geometry) { + this.eventType = EventType.EntityAdded; + } + + SceneUpdateEvent.clearLights() { + this.eventType = EventType.ClearLights; + } + + DirectLight getDirectLight() { + return _directLight!; + } + + IBL getAsIBL() { + return _ibl!; + } + + GLTF getAsGLTF() { + return _gltf!; + } + + Geometry getAsGeometry() { + return _geometry!; + } +} diff --git a/thermion_dart/lib/thermion_dart/compatibility/native/compatibility.dart b/thermion_dart/lib/thermion_dart/viewer/ffi/callbacks.dart similarity index 95% rename from thermion_dart/lib/thermion_dart/compatibility/native/compatibility.dart rename to thermion_dart/lib/thermion_dart/viewer/ffi/callbacks.dart index 453f3bb4..c3bd3157 100644 --- a/thermion_dart/lib/thermion_dart/compatibility/native/compatibility.dart +++ b/thermion_dart/lib/thermion_dart/viewer/ffi/callbacks.dart @@ -8,6 +8,11 @@ export 'thermion_dart.g.dart'; final allocator = calloc; +void using(Pointer ptr, Future Function(Pointer ptr) function) async { + await function.call(ptr); + allocator.free(ptr); +} + Future withVoidCallback( Function(Pointer>) func) async { final completer = Completer(); @@ -82,4 +87,3 @@ Future withCharPtrCallback( return completer.future; } -class Compatibility {} diff --git a/thermion_dart/lib/thermion_dart/compatibility/native/thermion_dart.g.dart b/thermion_dart/lib/thermion_dart/viewer/ffi/thermion_dart.g.dart similarity index 100% rename from thermion_dart/lib/thermion_dart/compatibility/native/thermion_dart.g.dart rename to thermion_dart/lib/thermion_dart/viewer/ffi/thermion_dart.g.dart diff --git a/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart b/thermion_dart/lib/thermion_dart/viewer/ffi/thermion_viewer_ffi.dart similarity index 94% rename from thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart rename to thermion_dart/lib/thermion_dart/viewer/ffi/thermion_viewer_ffi.dart index dbed65dc..42c6e9de 100644 --- a/thermion_dart/lib/thermion_dart/thermion_viewer_ffi.dart +++ b/thermion_dart/lib/thermion_dart/viewer/ffi/thermion_viewer_ffi.dart @@ -1,19 +1,18 @@ import 'dart:async'; +import 'dart:ffi'; import 'dart:math'; import 'dart:typed_data'; import 'package:animation_tools_dart/animation_tools_dart.dart'; -import 'package:thermion_dart/thermion_dart/compatibility/compatibility.dart'; import 'package:thermion_dart/thermion_dart/entities/gizmo.dart'; -import 'package:thermion_dart/thermion_dart/matrix_helper.dart'; -import 'package:thermion_dart/thermion_dart/scene.dart'; + +import 'package:thermion_dart/thermion_dart/utils/matrix.dart'; +import 'package:thermion_dart/thermion_dart/viewer/events.dart'; +import 'package:thermion_dart/thermion_dart/viewer/ffi/callbacks.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 '../thermion_viewer_base.dart'; import 'package:logging/logging.dart'; -typedef ThermionViewerImpl = ThermionViewerFFI; - // ignore: constant_identifier_names const ThermionEntity _FILAMENT_ASSET_ERROR = 0; @@ -26,9 +25,6 @@ double kFocalLength = 28.0; class ThermionViewerFFI extends ThermionViewer { final _logger = Logger("ThermionViewerFFI"); - SceneImpl? _scene; - Scene get scene => _scene!; - double pixelRatio = 1.0; Pointer? _sceneManager; @@ -48,12 +44,22 @@ class ThermionViewerFFI extends ThermionViewer { final _pickResultController = StreamController.broadcast(); + /// + /// + /// @override Stream get gizmoPickResult => _gizmoPickResultController.stream; final _gizmoPickResultController = StreamController.broadcast(); + /// + /// + /// + Stream get sceneUpdated => + _sceneUpdateEventController.stream; + final _sceneUpdateEventController = StreamController.broadcast(); + final Pointer resourceLoader; var _driver = nullptr.cast(); @@ -150,7 +156,6 @@ class ThermionViewerFFI extends ThermionViewer { } _sceneManager = get_scene_manager(_viewer!); - _scene = SceneImpl(this); await setCameraManipulatorOptions(zoomSpeed: 1.0); @@ -229,13 +234,14 @@ class ThermionViewerFFI extends ThermionViewer { await setRendering(false); await clearEntities(); await clearLights(); - await _scene!.dispose(); - _scene = null; + destroy_filament_viewer_ffi(_viewer!); _sceneManager = null; _viewer = null; await _pickResultController.close(); + await _gizmoPickResultController.close(); + await _sceneUpdateEventController.close(); for (final callback in _onDispose) { await callback.call(); @@ -393,7 +399,38 @@ class ThermionViewerFFI extends ThermionViewer { throw Exception("Failed to add light to scene"); } - _scene!.registerLight(entity); + return entity; + } + + /// + /// + /// + @override + Future addDirectLight(DirectLight directLight) async { + var entity = await withIntCallback((callback) => add_light_ffi( + _viewer!, + directLight.type.index, + directLight.color, + directLight.intensity, + directLight.position.x, + directLight.position.y, + directLight.position.z, + directLight.direction.x, + directLight.direction.y, + directLight.direction.z, + directLight.falloffRadius, + directLight.spotLightConeInner, + directLight.spotLightConeOuter, + directLight.sunAngularRadius, + directLight.sunHaloSize, + directLight.sunHaloFallof, + directLight.castShadows, + callback)); + if (entity == _FILAMENT_ASSET_ERROR) { + throw Exception("Failed to add light to scene"); + } + _sceneUpdateEventController + .add(SceneUpdateEvent.addDirectLight(entity, directLight)); return entity; } @@ -402,8 +439,8 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future removeLight(ThermionEntity entity) async { - _scene!.unregisterLight(entity); remove_light_ffi(_viewer!, entity); + _sceneUpdateEventController.add(SceneUpdateEvent.remove(entity)); } /// @@ -413,7 +450,7 @@ class ThermionViewerFFI extends ThermionViewer { Future clearLights() async { clear_lights_ffi(_viewer!); - _scene!.clearLights(); + _sceneUpdateEventController.add(SceneUpdateEvent.clearLights()); } /// @@ -468,7 +505,9 @@ class ThermionViewerFFI extends ThermionViewer { if (entity == _FILAMENT_ASSET_ERROR) { throw Exception("An error occurred loading the asset at $path"); } - _scene!.registerEntity(entity); + + _sceneUpdateEventController + .add(SceneUpdateEvent.addGltf(entity, GLTF(path, numInstances))); return entity; } @@ -494,7 +533,6 @@ class ThermionViewerFFI extends ThermionViewer { if (entity == _FILAMENT_ASSET_ERROR) { throw Exception("An error occurred loading GLB from buffer"); } - _scene!.registerEntity(entity); return entity; } @@ -514,7 +552,6 @@ class ThermionViewerFFI extends ThermionViewer { if (entity == _FILAMENT_ASSET_ERROR) { throw Exception("An error occurred loading the asset at $path"); } - _scene!.registerEntity(entity); return entity; } @@ -995,10 +1032,9 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future removeEntity(ThermionEntity entity) async { - _scene!.unregisterEntity(entity); - await withVoidCallback( (callback) => remove_entity_ffi(_viewer!, entity, callback)); + _sceneUpdateEventController.add(SceneUpdateEvent.remove(entity)); } /// @@ -1012,7 +1048,6 @@ class ThermionViewerFFI extends ThermionViewer { await withVoidCallback((callback) { clear_entities_ffi(_viewer!, callback); }); - _scene!.clearEntities(); } /// @@ -1498,7 +1533,6 @@ class ThermionViewerFFI extends ThermionViewer { x: (x / pixelRatio).toDouble(), y: (viewportDimensions.$2 - y) / pixelRatio )); - _scene!.registerSelected(entityId); } void _onGizmoPickResult(ThermionEntity entityId, int x, int y) { @@ -1519,8 +1553,6 @@ class ThermionViewerFFI extends ThermionViewer { /// @override void pick(int x, int y) async { - _scene!.unregisterSelected(); - x = (x * pixelRatio).ceil(); y = (viewportDimensions.$2 - (y * pixelRatio)).ceil(); @@ -1787,33 +1819,29 @@ class ThermionViewerFFI extends ThermionViewer { /// /// @override - Future createGeometry( - List vertices, List indices, - {String? materialPath, - List? normals, - bool keepData = false, - PrimitiveType primitiveType = PrimitiveType.TRIANGLES}) async { + Future createGeometry(Geometry geometry, + {bool keepData = false}) async { if (_viewer == null) { throw Exception("Viewer must not be null"); } final materialPathPtr = - materialPath?.toNativeUtf8(allocator: allocator) ?? nullptr; - final vertexPtr = allocator(vertices.length); - final indicesPtr = allocator(indices.length); - for (int i = 0; i < vertices.length; i++) { - vertexPtr[i] = vertices[i]; + geometry.materialPath?.toNativeUtf8(allocator: allocator) ?? nullptr; + final vertexPtr = allocator(geometry.vertices.length); + final indicesPtr = allocator(geometry.indices.length); + for (int i = 0; i < geometry.vertices.length; i++) { + vertexPtr[i] = geometry.vertices[i]; } - for (int i = 0; i < indices.length; i++) { - (indicesPtr + i).value = indices[i]; + for (int i = 0; i < geometry.indices.length; i++) { + (indicesPtr + i).value = geometry.indices[i]; } var normalsPtr = nullptr.cast(); - if (normals != null) { - normalsPtr = allocator(normals.length); - for (int i = 0; i < normals.length; i++) { - normalsPtr[i] = normals[i]; + if (geometry.normals != null) { + normalsPtr = allocator(geometry.normals!.length); + for (int i = 0; i < geometry.normals!.length; i++) { + normalsPtr[i] = geometry.normals![i]; } } @@ -1821,12 +1849,12 @@ class ThermionViewerFFI extends ThermionViewer { create_geometry_with_normals_ffi( _sceneManager!, vertexPtr, - vertices.length, + geometry.vertices.length, normalsPtr, - normals?.length ?? 0, + geometry.normals?.length ?? 0, indicesPtr, - indices.length, - primitiveType.index, + geometry.indices.length, + geometry.primitiveType.index, materialPathPtr.cast(), keepData, callback)); @@ -1834,16 +1862,17 @@ class ThermionViewerFFI extends ThermionViewer { throw Exception("Failed to create geometry"); } - _scene!.registerEntity(entity); - allocator.free(materialPathPtr); allocator.free(vertexPtr); allocator.free(indicesPtr); - if (normals != null) { + if (geometry.normals != null) { allocator.free(normalsPtr); } + _sceneUpdateEventController + .add(SceneUpdateEvent.addGeometry(entity, geometry)); + return entity; } @@ -1968,8 +1997,8 @@ class ThermionViewerFFI extends ThermionViewer { struct.y = f2; struct.z = f3; struct.w = f3; - set_material_property_float4(_sceneManager!, entity, materialIndex, - ptr.cast(), struct); + set_material_property_float4( + _sceneManager!, entity, materialIndex, ptr.cast(), struct); allocator.free(ptr); } } diff --git a/thermion_dart/lib/thermion_dart/viewer/shared_types/entities.dart b/thermion_dart/lib/thermion_dart/viewer/shared_types/entities.dart new file mode 100644 index 00000000..637bb800 --- /dev/null +++ b/thermion_dart/lib/thermion_dart/viewer/shared_types/entities.dart @@ -0,0 +1,9 @@ +library; + +export 'geometry.dart'; +export 'gltf.dart'; + +export 'light_options.dart'; + +// a handle that can be safely passed back to the rendering layer to manipulate an Entity +typedef ThermionEntity = int; diff --git a/thermion_dart/lib/thermion_dart/viewer/shared_types/entity.dart b/thermion_dart/lib/thermion_dart/viewer/shared_types/entity.dart new file mode 100644 index 00000000..e69de29b diff --git a/thermion_dart/lib/thermion_dart/viewer/shared_types/geometry.dart b/thermion_dart/lib/thermion_dart/viewer/shared_types/geometry.dart new file mode 100644 index 00000000..b6fa4f96 --- /dev/null +++ b/thermion_dart/lib/thermion_dart/viewer/shared_types/geometry.dart @@ -0,0 +1,19 @@ +import 'package:thermion_dart/thermion_dart/viewer/thermion_viewer_base.dart'; + +class Geometry { + final List vertices; + final List indices; + final List? normals; + final List<(double, double)>? uvs; + final PrimitiveType primitiveType; + final String? materialPath; + + Geometry(this.vertices, this.indices, { this.normals=null, this.uvs=null, + this.primitiveType = PrimitiveType.TRIANGLES, this.materialPath = null}); + + void scale(double factor) { + for (int i = 0; i < vertices.length; i++) { + vertices[i] = vertices[i] * factor; + } + } +} diff --git a/thermion_dart/lib/thermion_dart/viewer/shared_types/gltf.dart b/thermion_dart/lib/thermion_dart/viewer/shared_types/gltf.dart new file mode 100644 index 00000000..1189c38e --- /dev/null +++ b/thermion_dart/lib/thermion_dart/viewer/shared_types/gltf.dart @@ -0,0 +1,6 @@ +class GLTF { + final String uri; + final int numInstances; + + GLTF(this.uri, this.numInstances); +} diff --git a/thermion_dart/lib/thermion_dart/viewer/shared_types/light.dart b/thermion_dart/lib/thermion_dart/viewer/shared_types/light.dart new file mode 100644 index 00000000..9075911b --- /dev/null +++ b/thermion_dart/lib/thermion_dart/viewer/shared_types/light.dart @@ -0,0 +1,7 @@ +enum LightType { + SUN, //!< Directional light that also draws a sun's disk in the sky. + DIRECTIONAL, //!< Directional light, emits light in a given direction. + POINT, //!< Point light, emits light from a position, in all directions. + FOCUSED_SPOT, //!< Physically correct spot light. + SPOT, +} \ No newline at end of file diff --git a/thermion_dart/lib/thermion_dart/viewer/shared_types/light_options.dart b/thermion_dart/lib/thermion_dart/viewer/shared_types/light_options.dart new file mode 100644 index 00000000..1bbe3c84 --- /dev/null +++ b/thermion_dart/lib/thermion_dart/viewer/shared_types/light_options.dart @@ -0,0 +1,56 @@ +import 'dart:math'; +import 'package:vector_math/vector_math_64.dart' as v; +import 'package:vector_math/vector_math_64.dart'; +import 'light.dart'; + +class IBL { + String? iblPath; + final double iblIntensity; + + IBL(this.iblIntensity); +} + +class DirectLight { + final LightType type; + final double color; + final double intensity; + final bool castShadows; + late final v.Vector3 position; + late final v.Vector3 direction; + final double falloffRadius; + final double spotLightConeInner; + final double spotLightConeOuter; + final double sunAngularRadius; + final double sunHaloSize; + final double sunHaloFallof; + + DirectLight({ + required this.type, + required this.color, + required this.intensity, + this.castShadows = false, + required this.direction, + required this.position, + this.falloffRadius = 1.0, + this.spotLightConeInner = pi / 8, + this.spotLightConeOuter = pi / 4, + this.sunAngularRadius = 0.545, + this.sunHaloSize = 10.0, + this.sunHaloFallof = 80.0, + }); +DirectLight.point({ + double color = 6500, + double intensity = 100000, + bool castShadows = false, + Vector3? position, + double falloffRadius = 1.0, +}) : this( + type: LightType.POINT, + color: color, + intensity: intensity, + castShadows: castShadows, + position: position ?? Vector3(0, 1, 0), + direction: Vector3.zero(), + falloffRadius: falloffRadius, +); +} diff --git a/thermion_dart/lib/thermion_dart/viewer/shared_types/manipulator.dart b/thermion_dart/lib/thermion_dart/viewer/shared_types/manipulator.dart new file mode 100644 index 00000000..21350980 --- /dev/null +++ b/thermion_dart/lib/thermion_dart/viewer/shared_types/manipulator.dart @@ -0,0 +1,4 @@ +// see filament Manipulator.h for more details +@Deprecated( + "This is used the native pointer manipulator Prefer ThermionGestureHandler instead") +enum ManipulatorMode { ORBIT, MAP, FREE_FLIGHT } diff --git a/thermion_dart/lib/thermion_dart/viewer/shared_types/pick_result.dart b/thermion_dart/lib/thermion_dart/viewer/shared_types/pick_result.dart new file mode 100644 index 00000000..a3d9ac31 --- /dev/null +++ b/thermion_dart/lib/thermion_dart/viewer/shared_types/pick_result.dart @@ -0,0 +1,4 @@ +// "picking" means clicking/tapping on the viewport, and unprojecting the X/Y coordinate to determine whether any renderable entities were present at those coordinates. +import 'package:thermion_dart/thermion_dart/viewer/shared_types/shared_types.dart'; + +typedef FilamentPickResult = ({ThermionEntity entity, double x, double y}); \ No newline at end of file diff --git a/thermion_dart/lib/thermion_dart/viewer/shared_types/primitive.dart b/thermion_dart/lib/thermion_dart/viewer/shared_types/primitive.dart new file mode 100644 index 00000000..6763d0a0 --- /dev/null +++ b/thermion_dart/lib/thermion_dart/viewer/shared_types/primitive.dart @@ -0,0 +1,10 @@ +// copied from filament/backened/DriverEnums.h +enum PrimitiveType { + // don't change the enums values (made to match GL) + POINTS, //!< points + LINES, //!< lines + UNUSED1, + LINE_STRIP, //!< line strip + TRIANGLES, //!< triangles + TRIANGLE_STRIP, //!< triangle strip +} \ No newline at end of file diff --git a/thermion_dart/lib/thermion_dart/viewer/shared_types/shadow.dart b/thermion_dart/lib/thermion_dart/viewer/shared_types/shadow.dart new file mode 100644 index 00000000..a8ac9ba5 --- /dev/null +++ b/thermion_dart/lib/thermion_dart/viewer/shared_types/shadow.dart @@ -0,0 +1,6 @@ +enum ShadowType { + PCF, //!< percentage-closer filtered shadows (default) + VSM, //!< variance shadows + DPCF, //!< PCF with contact hardening simulation + PCSS, //!< PCF with soft shadows and contact hardening +} \ No newline at end of file diff --git a/thermion_dart/lib/thermion_dart/viewer/shared_types/shared_types.dart b/thermion_dart/lib/thermion_dart/viewer/shared_types/shared_types.dart new file mode 100644 index 00000000..c41d441e --- /dev/null +++ b/thermion_dart/lib/thermion_dart/viewer/shared_types/shared_types.dart @@ -0,0 +1,10 @@ +library shared_types; + +export 'entities.dart'; +export 'light.dart'; +export 'shadow.dart'; +export 'manipulator.dart'; +export 'pick_result.dart'; +export 'primitive.dart'; +export 'texture_details.dart'; +export 'tone_mapper.dart'; diff --git a/thermion_dart/lib/thermion_dart/viewer/shared_types/texture_details.dart b/thermion_dart/lib/thermion_dart/viewer/shared_types/texture_details.dart new file mode 100644 index 00000000..ffbcdfa9 --- /dev/null +++ b/thermion_dart/lib/thermion_dart/viewer/shared_types/texture_details.dart @@ -0,0 +1,14 @@ +/// +/// This represents the backing "surface" that we render into. +/// "Texture" here is a misnomer as it is only a render target texture on certain platforms. +/// +class TextureDetails { + final int textureId; + + // both width and height are in physical, not logical pixels + final int width; + final int height; + + TextureDetails( + {required this.textureId, required this.width, required this.height}); +} diff --git a/thermion_dart/lib/thermion_dart/viewer/shared_types/tone_mapper.dart b/thermion_dart/lib/thermion_dart/viewer/shared_types/tone_mapper.dart new file mode 100644 index 00000000..a6e1ca69 --- /dev/null +++ b/thermion_dart/lib/thermion_dart/viewer/shared_types/tone_mapper.dart @@ -0,0 +1 @@ +enum ToneMapper { ACES, FILMIC, LINEAR } diff --git a/thermion_dart/lib/thermion_dart/viewer/thermion_viewer_base.dart b/thermion_dart/lib/thermion_dart/viewer/thermion_viewer_base.dart new file mode 100644 index 00000000..621e64c5 --- /dev/null +++ b/thermion_dart/lib/thermion_dart/viewer/thermion_viewer_base.dart @@ -0,0 +1,856 @@ +import 'package:thermion_dart/thermion_dart/viewer/events.dart'; + +import 'shared_types/shared_types.dart'; +export 'shared_types/shared_types.dart'; + +import 'dart:math'; +import 'dart:typed_data'; +import 'package:thermion_dart/thermion_dart/entities/abstract_gizmo.dart'; +import 'package:vector_math/vector_math_64.dart'; +import 'dart:async'; +import 'package:animation_tools_dart/animation_tools_dart.dart'; + +abstract class ThermionViewer { + + /// + /// A Future that resolves when the underlying rendering context has been successfully created. + /// + Future get initialized; + + /// + /// The current dimensions of the viewport (in physical pixels). + /// + var viewportDimensions = (0.0, 0.0); + + /// + /// The current ratio of logical to physical pixels. + /// + late double pixelRatio; + + /// + /// The result(s) of calling [pick] (see below). + /// This may be a broadcast stream, so you should ensure you have subscribed to this stream before calling [pick]. + /// If [pick] is called without an active subscription to this stream, the results will be silently discarded. + /// + Stream get pickResult; + + /// + /// The result(s) of calling [pickGizmo] (see below). + /// + Stream get gizmoPickResult; + + /// + /// A Stream containing entities added/removed to/from to the scene. + /// + Stream get sceneUpdated; + + /// + /// Whether the controller is currently rendering at [framerate]. + /// + bool get rendering; + + /// + /// Set to true to continuously render the scene at the framerate specified by [setFrameRate] (60 fps by default). + /// + Future setRendering(bool render); + + /// + /// Render a single frame. + /// + Future render(); + + /// + /// Render a single frame to the viewport and copy the pixel buffer to [out]. + /// + Future capture(); + + /// + /// Sets the framerate for continuous rendering when [setRendering] is enabled. + /// + Future setFrameRate(int framerate); + + /// + /// Destroys/disposes the viewer (including the entire scene). You cannot use the viewer after calling this method. + /// + Future dispose(); + + /// + /// Set the background image to [path] (which should have a file extension .png, .jpg, or .ktx). + /// This will be rendered at the maximum depth (i.e. behind all other objects including the skybox). + /// If [fillHeight] is false, the image will be rendered at its original size. Note this may cause issues with pixel density so be sure to specify the correct resolution + /// If [fillHeight] is true, the image will be stretched/compressed to fit the height of the viewport. + /// + Future setBackgroundImage(String path, {bool fillHeight = false}); + + /// + /// Moves the background image to the relative offset from the origin (bottom-left) specified by [x] and [y]. + /// If [clamp] is true, the image cannot be positioned outside the bounds of the viewport. + /// + Future setBackgroundImagePosition(double x, double y, {bool clamp = false}); + + /// + /// Removes the background image. + /// + Future clearBackgroundImage(); + + /// + /// Sets the color for the background plane (positioned at the maximum depth, i.e. behind all other objects including the skybox). + /// + Future setBackgroundColor(double r, double g, double b, double alpha); + + /// + /// Load a skybox from [skyboxPath] (which must be a .ktx file) + /// + Future loadSkybox(String skyboxPath); + + /// + /// Removes the skybox from the scene. + /// + Future removeSkybox(); + + /// + /// Creates an indirect light by loading the reflections/irradiance from the KTX file. + /// Only one indirect light can be active at any given time; if an indirect light has already been loaded, it will be replaced. + /// + Future loadIbl(String lightingPath, {double intensity = 30000}); + + /// + /// Creates a indirect light with the given color. + /// Only one indirect light can be active at any given time; if an indirect light has already been loaded, it will be replaced. + /// + Future createIbl(double r, double g, double b, double intensity); + + /// + /// Rotates the IBL & skybox. + /// + Future rotateIbl(Matrix3 rotation); + + /// + /// Removes the image-based light from the scene. + /// + Future removeIbl(); + + /// + /// Add a light to the scene. + /// See LightManager.h for details + /// Note that [sunAngularRadius] is in degrees, + /// whereas [spotLightConeInner] and [spotLightConeOuter] are in radians + /// + @Deprecated("This will be removed in future versions. Use addDirectLight instead.") + Future addLight( + LightType type, + double colour, + double intensity, + double posX, + double posY, + double posZ, + double dirX, + double dirY, + double dirZ, + {double falloffRadius = 1.0, + double spotLightConeInner = pi / 8, + double spotLightConeOuter = pi / 4, + double sunAngularRadius = 0.545, + double sunHaloSize = 10.0, + double sunHaloFallof = 80.0, + bool castShadows = true}); + + /// + /// Adds a direct light to the scene. + /// See LightManager.h for details + /// Note that [sunAngularRadius] is in degrees, + /// whereas [spotLightConeInner] and [spotLightConeOuter] are in radians + /// + Future addDirectLight( + DirectLight light); + + /// + /// Remove a light from the scene. + /// + Future removeLight(ThermionEntity light); + + /// + /// Remove all lights (excluding IBL) from the scene. + /// + Future clearLights(); + + /// + /// Load the .glb asset at the given path and insert into the scene. + /// Specify [numInstances] to create multiple instances (this is more efficient than dynamically instantating at a later time). You can then retrieve the created instances with [getInstances]. + /// If you want to be able to call [createInstance] at a later time, you must pass true for [keepData]. + /// If [keepData] is false, the source glTF data will be released and [createInstance] will throw an exception. + /// + Future loadGlb(String path, + {int numInstances = 1, bool keepData = false}); + + /// + /// Load the .glb asset from the specified buffer and insert into the scene. + /// Specify [numInstances] to create multiple instances (this is more efficient than dynamically instantating at a later time). You can then retrieve the created instances with [getInstances]. + /// If you want to be able to call [createInstance] at a later time, you must pass true for [keepData]. + /// If [keepData] is false, the source glTF data will be released and [createInstance] will throw an exception. + /// + Future loadGlbFromBuffer(Uint8List data, + {int numInstances = 1, bool keepData = false}); + + /// + /// Create a new instance of [entity]. + /// + Future createInstance(ThermionEntity entity); + + /// + /// Returns the number of instances of the asset associated with [entity]. + /// + Future getInstanceCount(ThermionEntity entity); + + /// + /// Returns all instances of [entity]. + /// + Future> getInstances(ThermionEntity entity); + + /// + /// Load the .gltf asset at the given path and insert into the scene. + /// [relativeResourcePath] is the folder path where the glTF resources are stored; + /// this is usually the parent directory of the .gltf file itself. + /// + /// See [loadGlb] for an explanation of [keepData]. + /// + Future loadGltf(String path, String relativeResourcePath, + {bool keepData = false}); + + /// + /// Called by `FilamentGestureDetector`. You probably don't want to call this yourself. + /// + Future panStart(double x, double y); + + /// + /// Called by `FilamentGestureDetector`. You probably don't want to call this yourself. + /// + Future panUpdate(double x, double y); + + /// + /// Called by `FilamentGestureDetector`. You probably don't want to call this yourself. + /// + Future panEnd(); + + /// + /// Called by `FilamentGestureDetector`. You probably don't want to call this yourself. + /// + Future rotateStart(double x, double y); + + /// + /// Called by `FilamentGestureDetector`. You probably don't want to call this yourself. + /// + Future rotateUpdate(double x, double y); + + /// + /// Called by `FilamentGestureDetector`. You probably don't want to call this yourself. + /// + Future rotateEnd(); + + /// + /// Set the weights for all morph targets in [entity] to [weights]. + /// Note that [weights] must contain values for ALL morph targets, but no exception will be thrown if you don't do so (you'll just get incorrect results). + /// If you only want to set one value, set all others to zero (check [getMorphTargetNames] if you need the get a list of all morph targets). + /// IMPORTANT - this accepts the actual ThermionEntity with the relevant morph targets (unlike [getMorphTargetNames], which uses the parent entity and the child mesh name). + /// Use [getChildEntityByName] if you are setting the weights for a child mesh. + /// + Future setMorphTargetWeights(ThermionEntity entity, List weights); + + /// + /// Gets the names of all morph targets for the child renderable [childEntity] under [entity]. + /// + Future> getMorphTargetNames( + ThermionEntity entity, ThermionEntity childEntity); + + /// + /// Gets the names of all bones for the armature at [skinIndex] under the specified [entity]. + /// + Future> getBoneNames(ThermionEntity entity, {int skinIndex = 0}); + + /// + /// Gets the names of all glTF animations embedded in the specified entity. + /// + Future> getAnimationNames(ThermionEntity entity); + + /// + /// Returns the length (in seconds) of the animation at the given index. + /// + Future getAnimationDuration( + ThermionEntity entity, int animationIndex); + + /// + /// Animate the morph targets in [entity]. See [MorphTargetAnimation] for an explanation as to how to construct the animation frame data. + /// This method will check the morph target names specified in [animation] against the morph target names that actually exist exist under [meshName] in [entity], + /// throwing an exception if any cannot be found. + /// It is permissible for [animation] to omit any targets that do exist under [meshName]; these simply won't be animated. + /// + Future setMorphAnimationData( + ThermionEntity entity, MorphAnimationData animation, + {List? targetMeshNames}); + + /// + /// Clear all current morph animations for [entity]. + /// + Future clearMorphAnimationData(ThermionEntity entity); + + /// + /// Resets all bones in the given entity to their rest pose. + /// This should be done before every call to addBoneAnimation. + /// + Future resetBones(ThermionEntity entity); + + /// + /// Enqueues and plays the [animation] for the specified bone(s). + /// By default, frame data is interpreted as being in *parent* bone space; + /// a 45 degree around Y means the bone will rotate 45 degrees around the + /// Y axis of the parent bone *in its current orientation*. + /// (i.e NOT the parent bone's rest position!). + /// Currently, only [Space.ParentBone] and [Space.Model] are supported; if you want + /// to transform to another space, you will need to do so manually. + /// + /// [fadeInInSecs]/[fadeOutInSecs]/[maxDelta] are used to cross-fade between + /// the current active glTF animation ("animation1") and the animation you + /// set via this method ("animation2"). The bone orientations will be + /// linearly interpolated between animation1 and animation2; at time 0, + /// the orientation will be 100% animation1, at time [fadeInInSecs], the + /// animation will be ((1 - maxDelta) * animation1) + (maxDelta * animation2). + /// This will be applied in reverse after [fadeOutInSecs]. + /// + /// + Future addBoneAnimation(ThermionEntity entity, BoneAnimationData animation, + {int skinIndex = 0, + double fadeInInSecs = 0.0, + double fadeOutInSecs = 0.0, + double maxDelta = 1.0}); + + /// + /// Gets the entity representing the bone at [boneIndex]/[skinIndex]. + /// The returned entity is only intended for use with [getWorldTransform]. + /// + Future getBone(ThermionEntity parent, int boneIndex, + {int skinIndex = 0}); + + /// + /// Gets the local (relative to parent) transform for [entity]. + /// + Future getLocalTransform(ThermionEntity entity); + + /// + /// Gets the world transform for [entity]. + /// + Future getWorldTransform(ThermionEntity entity); + + /// + /// Gets the inverse bind (pose) matrix for the bone. + /// Note that [parent] must be the ThermionEntity returned by [loadGlb/loadGltf], not any other method ([getChildEntity] etc). + /// This is because all joint information is internally stored with the parent entity. + /// + Future getInverseBindMatrix(ThermionEntity parent, int boneIndex, + {int skinIndex = 0}); + + /// + /// Sets the transform (relative to its parent) for [entity]. + /// + Future setTransform(ThermionEntity entity, Matrix4 transform); + + /// + /// Updates the bone matrices for [entity] (which must be the ThermionEntity + /// returned by [loadGlb/loadGltf]). + /// Under the hood, this just calls [updateBoneMatrices] on the Animator + /// instance of the relevant FilamentInstance (which uses the local + /// bone transform and the inverse bind matrix to set the bone matrix). + /// + Future updateBoneMatrices(ThermionEntity entity); + + /// + /// Directly set the bone matrix for the bone at the given index. + /// Don't call this manually unless you know what you're doing. + /// + Future setBoneTransform( + ThermionEntity entity, int boneIndex, Matrix4 transform, + {int skinIndex = 0}); + + /// + /// Removes/destroys the specified entity from the scene. + /// [entity] will no longer be a valid handle after this method is called; ensure you immediately discard all references once this method is complete. + /// + Future removeEntity(ThermionEntity entity); + + /// + /// Removes/destroys all renderable entities from the scene (including cameras). + /// All [ThermionEntity] handles will no longer be valid after this method is called; ensure you immediately discard all references to all entities once this method is complete. + /// + Future clearEntities(); + + /// + /// Called by `FilamentGestureDetector`. You probably don't want to call this yourself. + /// + Future zoomBegin(); + + /// + /// Called by `FilamentGestureDetector`. You probably don't want to call this yourself. + /// + Future zoomUpdate(double x, double y, double z); + + /// + /// Called by `FilamentGestureDetector`. You probably don't want to call this yourself. + /// + Future zoomEnd(); + + /// + /// Schedules the glTF animation at [index] in [entity] to start playing on the next frame. + /// + Future playAnimation(ThermionEntity entity, int index, + {bool loop = false, + bool reverse = false, + bool replaceActive = true, + double crossfade = 0.0, + double startOffset = 0.0}); + + /// + /// Schedules the glTF animation at [index] in [entity] to start playing on the next frame. + /// + Future playAnimationByName(ThermionEntity entity, String name, + {bool loop = false, + bool reverse = false, + bool replaceActive = true, + double crossfade = 0.0}); + + Future setAnimationFrame( + ThermionEntity entity, int index, int animationFrame); + + Future stopAnimation(ThermionEntity entity, int animationIndex); + Future stopAnimationByName(ThermionEntity entity, String name); + + /// + /// Sets the current scene camera to the glTF camera under [name] in [entity]. + /// + Future setCamera(ThermionEntity entity, String? name); + + /// + /// Sets the current scene camera to the main camera (which is always available and added to every scene by default). + /// + Future setMainCamera(); + + /// + /// Returns the entity associated with the main camera. + /// + Future getMainCamera(); + + /// + /// Sets the horizontal field of view (if [horizontal] is true) or vertical field of view for the currently active camera to [degrees]. + /// The aspect ratio of the current viewport is used. + /// + Future setCameraFov(double degrees, {bool horizontal = true}); + + /// + /// Gets the field of view (in degrees). + /// + Future getCameraFov(bool horizontal); + + /// + /// Sets the tone mapping (requires postprocessing). + /// + Future setToneMapping(ToneMapper mapper); + + /// + /// Sets the strength of the bloom. + /// + Future setBloom(double bloom); + + /// + /// Sets the focal length of the camera. Default value is 28.0. + /// + Future setCameraFocalLength(double focalLength); + + /// + /// Sets the distance (in world units) to the near/far planes for the active camera. Default values are 0.05/1000.0. See Camera.h for details. + /// + Future setCameraCulling(double near, double far); + + /// + /// Get the distance (in world units) to the near plane for the active camera. + /// + @Deprecated("Use getCameraNear") + Future getCameraCullingNear(); + + /// + /// Get the distance (in world units) to the near plane for the active camera. + /// + Future getCameraNear(); + + /// + /// Get the distance (in world units) to the far culling plane for the active camera. + /// + Future getCameraCullingFar(); + + /// + /// + /// + Future setCameraLensProjection( + double near, double far, double aspect, double focalLength); + + /// + /// Sets the focus distance for the camera. + /// + Future setCameraFocusDistance(double focusDistance); + + /// + /// Get the camera position in world space. + /// + Future getCameraPosition(); + + /// + /// Get the camera's model matrix. + /// + Future getCameraModelMatrix(); + + /// + /// Get the camera's view matrix. See Camera.h for more details. + /// + Future getCameraViewMatrix(); + + /// + /// Get the camera's projection matrix. See Camera.h for more details. + /// + Future getCameraProjectionMatrix(); + + /// + /// Get the camera's culling projection matrix. See Camera.h for more details. + /// + Future getCameraCullingProjectionMatrix(); + + /// + /// Get the camera's culling frustum in world space. Returns a (vector_math) [Frustum] instance where plane0-plane6 define the left, right, bottom, top, far and near planes respectively. + /// See Camera.h and (filament) Frustum.h for more details. + /// + Future getCameraFrustum(); + + /// + /// Set the camera position in world space. Note this is not persistent - any viewport navigation will reset the camera transform. + /// + Future setCameraPosition(double x, double y, double z); + + /// + /// Get the camera rotation matrix. + /// + Future getCameraRotation(); + + /// + /// Repositions the camera to the last vertex of the bounding box of [entity], looking at the penultimate vertex. + /// + Future moveCameraToAsset(ThermionEntity entity); + + /// + /// Enables/disables frustum culling. Currently we don't expose a method for manipulating the camera projection/culling matrices so this is your only option to deal with unwanted near/far clipping. + /// + Future setViewFrustumCulling(bool enabled); + + /// + /// Sets the camera exposure. + /// + Future setCameraExposure( + double aperture, double shutterSpeed, double sensitivity); + + /// + /// Rotate the camera by [rads] around the given axis. + /// + Future setCameraRotation(Quaternion quaternion); + + /// + /// Sets the camera model matrix. + /// + @Deprecated("Will be superseded by setCameraModelMatrix4") + Future setCameraModelMatrix(List matrix); + + /// + /// Sets the camera model matrix. + /// + Future setCameraModelMatrix4(Matrix4 matrix); + + /// + /// Sets the `baseColorFactor` property for the material at index [materialIndex] in [entity] under node [meshName] to [color]. + /// + @Deprecated("Use setMaterialPropertyFloat4 instead") + Future setMaterialColor(ThermionEntity entity, String meshName, + int materialIndex, double r, double g, double b, double a); + + /// + /// Sets the material property [propertyName] under material [materialIndex] for [entity] to [value]. + /// [entity] must have a Renderable attached. + /// + Future setMaterialPropertyFloat4(ThermionEntity entity, String propertyName, + int materialIndex, double f1, double f2, double f3, double f4); + + /// + /// Sets the material property [propertyName] under material [materialIndex] for [entity] to [value]. + /// [entity] must have a Renderable attached. + /// + Future setMaterialPropertyFloat(ThermionEntity entity, String propertyName, + int materialIndex, double value); + + /// + /// Scale [entity] to fit within the unit cube. + /// + Future transformToUnitCube(ThermionEntity entity); + + /// + /// Directly sets the world space position for [entity] to the given coordinates. + /// + Future setPosition(ThermionEntity entity, double x, double y, double z); + + /// + /// Set the world space position for [lightEntity] to the given coordinates. + /// + Future setLightPosition( + ThermionEntity lightEntity, double x, double y, double z); + + /// + /// Sets the world space direction for [lightEntity] to the given vector. + /// + Future setLightDirection(ThermionEntity lightEntity, Vector3 direction); + + /// + /// Directly sets the scale for [entity], skipping all collision detection. + /// + Future setScale(ThermionEntity entity, double scale); + + /// + /// Directly sets the rotation for [entity] to [rads] around the axis {x,y,z}, skipping all collision detection. + /// + Future setRotation( + ThermionEntity entity, double rads, double x, double y, double z); + + /// + /// Queues an update to the worldspace position for [entity] to {x,y,z}. + /// The actual update will occur on the next frame, and will be subject to collision detection. + /// + Future queuePositionUpdate( + ThermionEntity entity, double x, double y, double z, + {bool relative = false}); + + /// + /// TODO + /// + Future queuePositionUpdateFromViewportCoords( + ThermionEntity entity, double x, double y); + + /// + /// TODO + /// + Future queueRelativePositionUpdateWorldAxis(ThermionEntity entity, + double viewportX, double viewportY, double x, double y, double z); + + /// + /// Queues an update to the worldspace rotation for [entity]. + /// The actual update will occur on the next frame, and will be subject to collision detection. + /// + Future queueRotationUpdate( + ThermionEntity entity, double rads, double x, double y, double z, + {bool relative = false}); + + /// + /// Same as [queueRotationUpdate]. + /// + Future queueRotationUpdateQuat(ThermionEntity entity, Quaternion quat, + {bool relative = false}); + + /// + /// Enable/disable postprocessing (disabled by default). + /// + Future setPostProcessing(bool enabled); + + /// + /// Enable/disable shadows (disabled by default). + /// + Future setShadowsEnabled(bool enabled); + + /// + /// Set shadow type. + /// + Future setShadowType(ShadowType shadowType); + + /// + /// Set soft shadow options (ShadowType DPCF and PCSS) + /// + Future setSoftShadowOptions(double penumbraScale, double penumbraRatioScale); + + /// + /// Set antialiasing options. + /// + Future setAntiAliasing(bool msaa, bool fxaa, bool taa); + + /// + /// Sets the rotation for [entity] to the specified quaternion. + /// + Future setRotationQuat(ThermionEntity entity, Quaternion rotation); + + /// + /// Reveal the node [meshName] under [entity]. Only applicable if [hide] had previously been called; this is a no-op otherwise. + /// + Future reveal(ThermionEntity entity, String? meshName); + + /// + /// If [meshName] is provided, hide the node [meshName] under [entity], otherwise hide the root node for [entity]. + /// The entity still exists in memory, but is no longer being rendered into the scene. Call [reveal] to re-commence rendering. + /// + Future hide(ThermionEntity entity, String? meshName); + + /// + /// Used to select the entity in the scene at the given viewport coordinates. + /// Called by `FilamentGestureDetector` on a mouse/finger down event. You probably don't want to call this yourself. + /// This is asynchronous and will require 2-3 frames to complete - subscribe to the [pickResult] stream to receive the results of this method. + /// [x] and [y] must be in local logical coordinates (i.e. where 0,0 is at top-left of the ThermionWidget). + /// + void pick(int x, int y); + + /// + /// Used to test whether a Gizmo is at the given viewport coordinates. + /// Called by `FilamentGestureDetector` on a mouse/finger down event. You probably don't want to call this yourself. + /// This is asynchronous and will require 2-3 frames to complete - subscribe to the [gizmoPickResult] stream to receive the results of this method. + /// [x] and [y] must be in local logical coordinates (i.e. where 0,0 is at top-left of the ThermionWidget). + /// + void pickGizmo(int x, int y); + + /// + /// Retrieves the name assigned to the given ThermionEntity (usually corresponds to the glTF mesh name). + /// + String? getNameForEntity(ThermionEntity entity); + + /// + /// Sets the options for manipulating the camera via the viewport. + /// ManipulatorMode.FREE_FLIGHT and ManipulatorMode.MAP are currently unsupported and will throw an exception. + /// + @Deprecated("Use ThermionGestureHandler instead") + Future setCameraManipulatorOptions( + {ManipulatorMode mode = ManipulatorMode.ORBIT, + double orbitSpeedX = 0.01, + double orbitSpeedY = 0.01, + double zoomSpeed = 0.01}); + + /// + /// Returns all child entities under [parent]. + /// + Future> getChildEntities( + ThermionEntity parent, bool renderableOnly); + + /// + /// Finds the child entity named [childName] associated with the given parent. + /// Usually, [parent] will be the return value from [loadGlb]/[loadGltf] and [childName] will be the name of a node/mesh. + /// + Future getChildEntity( + ThermionEntity parent, String childName); + + /// + /// List the name of all child entities under the given entity. + /// + Future> getChildEntityNames(ThermionEntity entity, + {bool renderableOnly = true}); + + /// + /// If [recording] is set to true, each frame the framebuffer/texture will be written to /tmp/output_*.png. + /// This will impact performance; handle with care. + /// + Future setRecording(bool recording); + + /// + /// Sets the output directory where recorded PNGs will be placed. + /// + Future setRecordingOutputDirectory(String outputDirectory); + + /// + /// An [entity] will only be animatable after an animation component is attached. + /// Any calls to [playAnimation]/[setBoneAnimation]/[setMorphAnimation] will have no visual effect until [addAnimationComponent] has been called on the instance. + /// + Future addAnimationComponent(ThermionEntity entity); + + /// + /// Removes an animation component from [entity]. + /// + Future removeAnimationComponent(ThermionEntity entity); + + /// + /// Makes [entity] collidable. + /// This allows you to call [testCollisions] with any other entity ("entity B") to see if [entity] has collided with entity B. The callback will be invoked if so. + /// Alternatively, if [affectsTransform] is true and this entity collides with another entity, any queued position updates to the latter entity will be ignored. + /// + Future addCollisionComponent(ThermionEntity entity, + {void Function(int entityId1, int entityId2)? callback, + bool affectsTransform = false}); + + /// + /// Removes the collision component from [entity], meaning this will no longer be tested when [testCollisions] or [queuePositionUpdate] is called with another entity. + /// + Future removeCollisionComponent(ThermionEntity entity); + + /// + /// Creates a (renderable) entity with the specified geometry and adds to the scene. + /// If [keepData] is true, the source data will not be released. + /// + Future createGeometry(Geometry geometry, { bool keepData= false}); + + /// + /// Gets the parent entity of [entity]. Returns null if the entity has no parent. + /// + 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]. + /// + Future setParent(ThermionEntity child, ThermionEntity parent, + {bool preserveScaling}); + + /// + /// Test all collidable entities against this entity to see if any have collided. + /// This method returns void; the relevant callback passed to [addCollisionComponent] will be fired if a collision is detected. + /// + Future testCollisions(ThermionEntity entity); + + /// + /// Sets the draw priority for the given entity. See RenderableManager.h for more details. + /// + Future setPriority(ThermionEntity entityId, int priority); + + /// + /// The gizmo for translating/rotating objects. Only one gizmo is present in the scene. + /// + AbstractGizmo? get gizmo; + + /// + /// 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 getViewportBoundingBox(ThermionEntity entity); + + /// + /// Filament assigns renderables to a numeric layer. + /// We place all scene assets in layer 0 (enabled by default), gizmos in layer 1 (enabled by default), world grid in layer 2 (disabled by default). + /// Use this method to toggle visibility of the respective layer. + /// + Future setLayerEnabled(int layer, bool enabled); + + /// + /// Show/hide the translation gizmo. + /// + Future setGizmoVisibility(bool visible); + + /// + /// Renders an outline around [entity] with the given color. + /// + Future setStencilHighlight(ThermionEntity entity, + {double r = 1.0, double g = 0.0, double b = 0.0}); + + /// + /// Removes the outline around [entity]. Noop if there was no highlight. + /// + Future removeStencilHighlight(ThermionEntity entity); +} diff --git a/thermion_dart/lib/thermion_dart/thermion_viewer_stub.dart b/thermion_dart/lib/thermion_dart/viewer/thermion_viewer_stub.dart similarity index 95% rename from thermion_dart/lib/thermion_dart/thermion_viewer_stub.dart rename to thermion_dart/lib/thermion_dart/viewer/thermion_viewer_stub.dart index 118c1746..f30e22a1 100644 --- a/thermion_dart/lib/thermion_dart/thermion_viewer_stub.dart +++ b/thermion_dart/lib/thermion_dart/viewer/thermion_viewer_stub.dart @@ -2,14 +2,13 @@ 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:thermion_dart/thermion_dart/viewer/events.dart'; +import 'package:thermion_dart/thermion_dart/viewer/thermion_viewer_base.dart'; import 'package:vector_math/vector_math_64.dart'; import 'dart:async'; import 'package:animation_tools_dart/animation_tools_dart.dart'; -typedef ThermionViewerImpl = ThermionViewerStub; - class ThermionViewerStub extends ThermionViewer { @override Future addAnimationComponent(ThermionEntity entity) { @@ -436,10 +435,6 @@ class ThermionViewerStub extends ThermionViewer { throw UnimplementedError(); } - @override - // TODO: implement scene - Scene get scene => throw UnimplementedError(); - @override Future setAnimationFrame( ThermionEntity entity, int index, int animationFrame) { @@ -850,12 +845,7 @@ class ThermionViewerStub extends ThermionViewer { throw UnimplementedError(); } - @override - Future createGeometry(List vertices, List indices, {String? materialPath, List? normals, PrimitiveType primitiveType = PrimitiveType.TRIANGLES}) { - // TODO: implement createGeometry - throw UnimplementedError(); - } - + @override Future loadGlb(String path, {int numInstances = 1, bool keepData = false}) { // TODO: implement loadGlb @@ -867,4 +857,34 @@ class ThermionViewerStub extends ThermionViewer { // TODO: implement loadGltf throw UnimplementedError(); } + + @override + Future setMaterialPropertyFloat(ThermionEntity entity, String propertyName, int materialIndex, double value) { + // TODO: implement setMaterialPropertyFloat + throw UnimplementedError(); + } + + @override + Future setMaterialPropertyFloat4(ThermionEntity entity, String propertyName, int materialIndex, double f1, double f2, double f3, double f4) { + // TODO: implement setMaterialPropertyFloat4 + throw UnimplementedError(); + } + + @override + Future createGeometry(Geometry geometry, {bool keepData=false}) { + // TODO: implement createGeometry + throw UnimplementedError(); + } + + @override + // TODO: implement sceneUpdated + Stream get sceneUpdated => throw UnimplementedError(); + + @override + Future addDirectLight(DirectLight light) { + // TODO: implement addDirectLight + throw UnimplementedError(); + } + + } diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_dart_bridge.dart b/thermion_dart/lib/thermion_dart/viewer/web/thermion_viewer_dart_bridge.dart similarity index 99% rename from thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_dart_bridge.dart rename to thermion_dart/lib/thermion_dart/viewer/web/thermion_viewer_dart_bridge.dart index 86413551..e88e1859 100644 --- a/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_dart_bridge.dart +++ b/thermion_dart/lib/thermion_dart/viewer/web/thermion_viewer_dart_bridge.dart @@ -3,7 +3,7 @@ library thermion_flutter_js; import 'dart:js_interop'; import 'package:logging/logging.dart'; -import 'package:thermion_dart/thermion_dart/compatibility/web/interop/thermion_viewer_js_shim.dart'; +import 'package:thermion_dart/thermion_dart/viewer/web/thermion_viewer_js_shim.dart'; import 'package:vector_math/vector_math_64.dart' as v64; import 'package:animation_tools_dart/animation_tools_dart.dart'; @@ -166,9 +166,9 @@ class ThermionViewerJSDartBridge { @JSExport() JSPromise loadGltf(String path, String relativeResourcePath, - {bool force = false}) { + {bool keepData = false}) { return viewer - .loadGltf(path, relativeResourcePath, force: force) + .loadGltf(path, relativeResourcePath, keepData: keepData) .then((entity) => entity.toJS) .toJS; } diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_js.dart b/thermion_dart/lib/thermion_dart/viewer/web/thermion_viewer_js.dart similarity index 90% rename from thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_js.dart rename to thermion_dart/lib/thermion_dart/viewer/web/thermion_viewer_js.dart index b44fde87..f40e0765 100644 --- a/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_js.dart +++ b/thermion_dart/lib/thermion_dart/viewer/web/thermion_viewer_js.dart @@ -9,7 +9,6 @@ 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:thermion_dart/thermion_dart/scene_impl.dart'; import 'package:vector_math/vector_math_64.dart'; import 'thermion_viewer_js_shim.dart'; @@ -911,4 +910,98 @@ class ThermionViewerJS implements ThermionViewer { // TODO: implement setLayerEnabled throw UnimplementedError(); } + + @override + // TODO: implement entitiesAdded + Stream get entitiesAdded => throw UnimplementedError(); + + @override + // TODO: implement entitiesRemoved + Stream get entitiesRemoved => throw UnimplementedError(); + + @override + Future getAncestor(ThermionEntity entity) { + // TODO: implement getAncestor + throw UnimplementedError(); + } + + @override + Future getCameraNear() { + // TODO: implement getCameraNear + throw UnimplementedError(); + } + + @override + Future getViewportBoundingBox(ThermionEntity entity) { + // TODO: implement getViewportBoundingBox + throw UnimplementedError(); + } + + @override + // TODO: implement lightsAdded + Stream get lightsAdded => throw UnimplementedError(); + + @override + // TODO: implement lightsRemoved + Stream get lightsRemoved => throw UnimplementedError(); + + @override + Future loadGlbFromBuffer(Uint8List data, {int numInstances = 1, bool keepData = false}) { + // TODO: implement loadGlbFromBuffer + throw UnimplementedError(); + } + + @override + Future queuePositionUpdateFromViewportCoords(ThermionEntity entity, double x, double y) { + // TODO: implement queuePositionUpdateFromViewportCoords + throw UnimplementedError(); + } + + @override + Future removeStencilHighlight(ThermionEntity entity) { + // TODO: implement removeStencilHighlight + throw UnimplementedError(); + } + + @override + Future setCameraLensProjection(double near, double far, double aspect, double focalLength) { + // TODO: implement setCameraLensProjection + throw UnimplementedError(); + } + + @override + Future setCameraModelMatrix4(Matrix4 matrix) { + // TODO: implement setCameraModelMatrix4 + throw UnimplementedError(); + } + + @override + Future setLightDirection(ThermionEntity lightEntity, Vector3 direction) { + // TODO: implement setLightDirection + throw UnimplementedError(); + } + + @override + Future setLightPosition(ThermionEntity lightEntity, double x, double y, double z) { + // TODO: implement setLightPosition + throw UnimplementedError(); + } + + @override + Future setMaterialPropertyFloat(ThermionEntity entity, String propertyName, int materialIndex, double value) { + // TODO: implement setMaterialPropertyFloat + throw UnimplementedError(); + } + + @override + Future setMaterialPropertyFloat4(ThermionEntity entity, String propertyName, int materialIndex, double f1, double f2, double f3, double f4) { + // TODO: implement setMaterialPropertyFloat4 + throw UnimplementedError(); + } + + @override + Future setStencilHighlight(ThermionEntity entity, {double r = 1.0, double g = 0.0, double b = 0.0}) { + // TODO: implement setStencilHighlight + throw UnimplementedError(); + } } diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_js_shim.dart b/thermion_dart/lib/thermion_dart/viewer/web/thermion_viewer_js_shim.dart similarity index 100% rename from thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_js_shim.dart rename to thermion_dart/lib/thermion_dart/viewer/web/thermion_viewer_js_shim.dart diff --git a/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_wasm.dart b/thermion_dart/lib/thermion_dart/viewer/web/thermion_viewer_wasm.dart similarity index 96% rename from thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_wasm.dart rename to thermion_dart/lib/thermion_dart/viewer/web/thermion_viewer_wasm.dart index 9c758c45..0ebb0399 100644 --- a/thermion_dart/lib/thermion_dart/compatibility/web/interop/thermion_viewer_wasm.dart +++ b/thermion_dart/lib/thermion_dart/viewer/web/thermion_viewer_wasm.dart @@ -185,7 +185,6 @@ class ThermionViewerWasm implements ThermionViewer { _width = (width * pixelRatio).ceil(); _height = (height * pixelRatio).ceil(); viewportDimensions = (_width.toDouble(), _height.toDouble()); - print("Update viewport camera projection : $_width $height"); _module!.ccall( "update_viewport_and_camera_projection", "void", @@ -718,7 +717,7 @@ class ThermionViewerWasm implements ThermionViewer { } @override - Future loadGlb(String path, {int numInstances = 1}) async { + Future loadGlb(String path, {int numInstances = 1, bool keepData = false}) async { final promise = _module!.ccall( "load_glb", "int", @@ -733,8 +732,7 @@ class ThermionViewerWasm implements ThermionViewer { } @override - Future loadGltf(String path, String relativeResourcePath, - {bool force = false}) async { + Future loadGltf(String path, String relativeResourcePath, { bool keepData = false}) async { final promise = _module!.ccall( "load_gltf", "int", @@ -2280,4 +2278,80 @@ class ThermionViewerWasm implements ThermionViewer { .toJS, null); } + + @override + Future getAncestor(ThermionEntity entity) { + // TODO: implement getAncestor + throw UnimplementedError(); + } + + @override + Future queuePositionUpdateFromViewportCoords(ThermionEntity entity, double x, double y) { + // TODO: implement queuePositionUpdateFromViewportCoords + throw UnimplementedError(); + } + + @override + Future removeStencilHighlight(ThermionEntity entity) { + // TODO: implement removeStencilHighlight + throw UnimplementedError(); + } + + @override + Future setStencilHighlight(ThermionEntity entity, {double r = 1.0, double g = 0.0, double b = 0.0}) { + // TODO: implement setStencilHighlight + throw UnimplementedError(); + } + + @override + // TODO: implement entitiesAdded + Stream get entitiesAdded => throw UnimplementedError(); + + @override + // TODO: implement entitiesRemoved + Stream get entitiesRemoved => throw UnimplementedError(); + + @override + Future getCameraNear() { + // TODO: implement getCameraNear + throw UnimplementedError(); + } + + @override + Future getViewportBoundingBox(ThermionEntity entity) { + // TODO: implement getViewportBoundingBox + throw UnimplementedError(); + } + + @override + // TODO: implement lightsAdded + Stream get lightsAdded => throw UnimplementedError(); + + @override + // TODO: implement lightsRemoved + Stream get lightsRemoved => throw UnimplementedError(); + + @override + Future setCameraLensProjection(double near, double far, double aspect, double focalLength) { + // TODO: implement setCameraLensProjection + throw UnimplementedError(); + } + + @override + Future setCameraModelMatrix4(Matrix4 matrix) { + // TODO: implement setCameraModelMatrix4 + throw UnimplementedError(); + } + + @override + Future setMaterialPropertyFloat(ThermionEntity entity, String propertyName, int materialIndex, double value) { + // TODO: implement setMaterialPropertyFloat + throw UnimplementedError(); + } + + @override + Future setMaterialPropertyFloat4(ThermionEntity entity, String propertyName, int materialIndex, double f1, double f2, double f3, double f4) { + // TODO: implement setMaterialPropertyFloat4 + throw UnimplementedError(); + } }