From 562ecf2ee5274a1027165ad250085ea6dcffe6ac Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 2 Oct 2024 16:47:55 +0800 Subject: [PATCH] feat: camera and resizing improvements --- thermion_dart/ffigen/native.yaml | 2 + .../input/src/delegate_gesture_handler.dart | 38 +- .../fixed_orbit_camera_rotation_delegate.dart | 87 ++- .../lib/src/input/src/input_handler.dart | 6 +- .../src/{camera_ffi.dart => ffi_camera.dart} | 36 +- .../lib/src/viewer/src/ffi/src/ffi_view.dart | 22 +- .../viewer/src/ffi/src/thermion_dart.g.dart | 694 ++++++++++-------- .../src/ffi/src/thermion_viewer_ffi.dart | 34 +- .../viewer/src/ffi/thermion_viewer_ffi.dart | 1 - .../src/viewer/src/shared_types/camera.dart | 4 + .../lib/src/viewer/src/shared_types/view.dart | 8 + .../src/viewer/src/thermion_viewer_base.dart | 2 +- thermion_dart/native/include/TCamera.h | 50 ++ .../native/include/ThermionDartAPIUtils.h | 27 + .../native/include/ThermionDartApi.h | 38 +- .../include/ThermionDartRenderThreadApi.h | 11 +- thermion_dart/native/src/FilamentViewer.cpp | 1 - thermion_dart/native/src/TCamera.cpp | 84 +++ thermion_dart/native/src/TView.cpp | 1 - thermion_dart/native/src/ThermionDartApi.cpp | 64 +- .../src/ThermionDartRenderThreadApi.cpp | 23 +- thermion_dart/test/camera_tests.dart | 11 + thermion_dart/test/helpers.dart | 2 +- .../lib/src/thermion_flutter_plugin.dart | 16 +- .../widgets/src/thermion_texture_widget.dart | 152 +--- .../lib/src/widgets/src/thermion_widget.dart | 38 +- .../lib/src/widgets/widgets.dart | 1 + 27 files changed, 840 insertions(+), 613 deletions(-) rename thermion_dart/lib/src/viewer/src/ffi/src/{camera_ffi.dart => ffi_camera.dart} (57%) create mode 100644 thermion_dart/native/include/TCamera.h create mode 100644 thermion_dart/native/include/ThermionDartAPIUtils.h create mode 100644 thermion_dart/native/src/TCamera.cpp diff --git a/thermion_dart/ffigen/native.yaml b/thermion_dart/ffigen/native.yaml index 085fd90a..bfb0c0d2 100644 --- a/thermion_dart/ffigen/native.yaml +++ b/thermion_dart/ffigen/native.yaml @@ -4,12 +4,14 @@ headers: - '../native/include/ThermionDartRenderThreadApi.h' - '../native/include/ThermionDartApi.h' - '../native/include/TView.h' + - '../native/include/TCamera.h' - '../native/include/TGizmo.h' - '../native/include/ResourceBuffer.h' include-directives: - '../native/include/ThermionDartRenderThreadApi.h' - '../native/include/ThermionDartApi.h' - '../native/include/TView.h' + - '../native/include/TCamera.h' - '../native/include/TGizmo.h' - '../native/include/ResourceBuffer.h' - '../native/include/APIBoundaryTypes.h' diff --git a/thermion_dart/lib/src/input/src/delegate_gesture_handler.dart b/thermion_dart/lib/src/input/src/delegate_gesture_handler.dart index 0d599028..c6550975 100644 --- a/thermion_dart/lib/src/input/src/delegate_gesture_handler.dart +++ b/thermion_dart/lib/src/input/src/delegate_gesture_handler.dart @@ -9,6 +9,10 @@ import 'implementations/free_flight_camera_delegate.dart'; class DelegateInputHandler implements InputHandler { final ThermionViewer viewer; + Stream> get gestures => _gesturesController.stream; + + final _gesturesController = StreamController>.broadcast(); + final _logger = Logger("DelegateInputHandler"); InputHandlerDelegate? transformDelegate; @@ -54,7 +58,7 @@ class DelegateInputHandler implements InputHandler { factory DelegateInputHandler.fixedOrbit(ThermionViewer viewer, {double minimumDistance = 10.0, - double? Function(Vector3)? getDistanceToTarget, + Future Function(Vector3)? getDistanceToTarget, ThermionEntity? entity, PickDelegate? pickDelegate}) => DelegateInputHandler( @@ -105,39 +109,51 @@ class DelegateInputHandler implements InputHandler { await transformDelegate?.queue(action, vector); } - + final keyTypes = []; for (final key in _pressedKeys) { InputAction? keyAction; + InputType? keyType = null; Vector3? vector; switch (key) { case PhysicalKey.W: - keyAction = _actions[InputType.KEYDOWN_W]; + keyType = InputType.KEYDOWN_W; vector = Vector3(0, 0, -1); break; case PhysicalKey.A: - keyAction = _actions[InputType.KEYDOWN_A]; + keyType = InputType.KEYDOWN_A; vector = Vector3(-1, 0, 0); break; case PhysicalKey.S: - keyAction = _actions[InputType.KEYDOWN_S]; + keyType = InputType.KEYDOWN_S; vector = Vector3(0, 0, 1); break; case PhysicalKey.D: - keyAction = _actions[InputType.KEYDOWN_D]; + keyType = InputType.KEYDOWN_D; vector = Vector3(1, 0, 0); break; } - if (keyAction != null) { - var transform = _axes[keyAction]; - if (transform != null) { - vector = transform * vector; + + if (keyType != null) { + keyAction = _actions[keyType]; + + if (keyAction != null) { + var transform = _axes[keyAction]; + if (transform != null) { + vector = transform * vector; + } + transformDelegate?.queue(keyAction, vector!); + keyTypes.add(keyType); } - transformDelegate?.queue(keyAction, vector!); } } await transformDelegate?.execute(); + var updates = _inputDeltas.keys.followedBy(keyTypes).toList(); + if(updates.isNotEmpty) { + _gesturesController.add(updates); + } + _inputDeltas.clear(); _processing = false; } diff --git a/thermion_dart/lib/src/input/src/implementations/fixed_orbit_camera_rotation_delegate.dart b/thermion_dart/lib/src/input/src/implementations/fixed_orbit_camera_rotation_delegate.dart index 4059a553..b8e0fcef 100644 --- a/thermion_dart/lib/src/input/src/implementations/fixed_orbit_camera_rotation_delegate.dart +++ b/thermion_dart/lib/src/input/src/implementations/fixed_orbit_camera_rotation_delegate.dart @@ -4,12 +4,13 @@ import '../../../viewer/src/shared_types/camera.dart'; import '../../../viewer/viewer.dart'; import '../../input.dart'; import '../input_handler.dart'; +import 'dart:math'; class FixedOrbitRotateInputHandlerDelegate implements InputHandlerDelegate { final ThermionViewer viewer; late Future _camera; final double minimumDistance; - double? Function(Vector3)? getDistanceToTarget; + Future Function(Vector3)? getDistanceToTarget; Vector2 _queuedRotationDelta = Vector2.zero(); double _queuedZoomDelta = 0.0; @@ -22,7 +23,14 @@ class FixedOrbitRotateInputHandlerDelegate implements InputHandlerDelegate { this.getDistanceToTarget, this.minimumDistance = 10.0, }) { - _camera = viewer.getMainCamera(); + _camera = viewer.getMainCamera().then((Camera cam) async { + var viewMatrix = makeViewMatrix(Vector3(0.0, 0, -minimumDistance), + Vector3.zero(), Vector3(0.0, 1.0, 0.0)); + viewMatrix.invert(); + + await cam.setTransform(viewMatrix); + return cam; + }); } void dispose() { @@ -50,12 +58,20 @@ class FixedOrbitRotateInputHandlerDelegate implements InputHandlerDelegate { } } + bool _executing = false; + @override Future execute() async { if (_queuedRotationDelta.length2 == 0.0 && _queuedZoomDelta == 0.0) { return; } + if (_executing) { + return; + } + + _executing = true; + final view = await viewer.getViewAt(0); final viewport = await view.getViewport(); @@ -66,15 +82,22 @@ class FixedOrbitRotateInputHandlerDelegate implements InputHandlerDelegate { Vector3 currentPosition = modelMatrix.getTranslation(); Vector3 forward = -currentPosition.normalized(); + + if (forward.length == 0) { + forward = Vector3(0, 0, -1); + currentPosition = Vector3(0, 0, minimumDistance); + } + Vector3 right = _up.cross(forward).normalized(); Vector3 up = forward.cross(right); - // Calculate intersection point and depth - double radius = getDistanceToTarget?.call(currentPosition) ?? 1.0; - if (radius != 1.0) { - radius = currentPosition.length - radius; - } - Vector3 intersection = (-forward).scaled(radius); + // Calculate the point where the camera forward ray intersects with the + // surface of the target sphere + var distanceToTarget = + (await getDistanceToTarget?.call(currentPosition)) ?? 0; + + Vector3 intersection = + (-forward).scaled(currentPosition.length - distanceToTarget); final intersectionInViewSpace = viewMatrix * Vector4(intersection.x, intersection.y, intersection.z, 1.0); @@ -98,17 +121,45 @@ class FixedOrbitRotateInputHandlerDelegate implements InputHandlerDelegate { var worldSpace3 = worldSpace.xyz.normalized() * currentPosition.length; currentPosition = worldSpace3; - // Apply zoom + // Zoom if (_queuedZoomDelta != 0.0) { - Vector3 toSurface = currentPosition - intersection; - currentPosition = - currentPosition + toSurface.scaled(_queuedZoomDelta * 0.1); - } + var distToIntersection = + (currentPosition - intersection).length - minimumDistance; - // Ensure minimum distance - if (currentPosition.length < radius + minimumDistance) { - currentPosition = - (currentPosition.normalized() * (radius + minimumDistance)); + // if we somehow overshot the minimum distance, reset the camera to the minimum distance + if (distToIntersection < 0) { + currentPosition += + (intersection.normalized().scaled(-distToIntersection * 10)); + } else { + bool zoomingOut = _queuedZoomDelta > 0; + late Vector3 offset; + + // when zooming, we don't always use fractions of the distance from + // the camera to the target (this is due to float precision issues at + // large distances, and also it doesn't work well for UI). + + // if we're zooming out and the distance is less than 10m, we zoom out by 1 unit + if (zoomingOut) { + if (distToIntersection < 10) { + offset = intersection.normalized(); + } else { + offset = intersection.normalized().scaled(distToIntersection / 10); + } + // if we're zooming in and the distance is less than 5m, zoom in by 1/2 the distance, + // otherwise 1/10 of the distance each time + } else { + if (distToIntersection < 5) { + offset = intersection.normalized().scaled(-distToIntersection / 2); + } else { + offset = intersection.normalized().scaled(-distToIntersection / 10); + } + + if (offset.length > distToIntersection) { + offset = Vector3.zero(); + } + } + currentPosition += offset; + } } // Calculate view matrix @@ -126,5 +177,7 @@ class FixedOrbitRotateInputHandlerDelegate implements InputHandlerDelegate { // Reset queued deltas _queuedRotationDelta = Vector2.zero(); _queuedZoomDelta = 0.0; + + _executing = false; } } diff --git a/thermion_dart/lib/src/input/src/input_handler.dart b/thermion_dart/lib/src/input/src/input_handler.dart index 44096647..a8baa214 100644 --- a/thermion_dart/lib/src/input/src/input_handler.dart +++ b/thermion_dart/lib/src/input/src/input_handler.dart @@ -27,11 +27,13 @@ enum PhysicalKey { W, A, S, D } enum InputAction { TRANSLATE, ROTATE, PICK, NONE } abstract class InputHandler { - + Stream> get gestures; + Future onPointerHover(Vector2 localPosition, Vector2 delta); Future onPointerScroll(Vector2 localPosition, double scrollDelta); Future onPointerDown(Vector2 localPosition, bool isMiddle); - Future onPointerMove(Vector2 localPosition, Vector2 delta, bool isMiddle); + Future onPointerMove( + Vector2 localPosition, Vector2 delta, bool isMiddle); Future onPointerUp(bool isMiddle); Future onScaleStart(); Future onScaleUpdate(); diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/camera_ffi.dart b/thermion_dart/lib/src/viewer/src/ffi/src/ffi_camera.dart similarity index 57% rename from thermion_dart/lib/src/viewer/src/ffi/src/camera_ffi.dart rename to thermion_dart/lib/src/viewer/src/ffi/src/ffi_camera.dart index c2756d79..e10c6bd0 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/camera_ffi.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/ffi_camera.dart @@ -4,32 +4,32 @@ import 'package:vector_math/vector_math_64.dart'; import '../../../../utils/matrix.dart'; import '../../thermion_viewer_base.dart'; -import 'thermion_dart.g.dart'; +import 'thermion_dart.g.dart' as g; class FFICamera extends Camera { - final Pointer camera; - final Pointer engine; + final Pointer camera; + final Pointer engine; late ThermionEntity _entity; FFICamera(this.camera, this.engine) { - _entity = Camera_getEntity(camera); + _entity = g.Camera_getEntity(camera); } @override Future setProjectionMatrixWithCulling( Matrix4 projectionMatrix, double near, double far) async { - Camera_setCustomProjectionWithCulling( + g.Camera_setCustomProjectionWithCulling( camera, matrix4ToDouble4x4(projectionMatrix), near, far); } Future getModelMatrix() async { - return double4x4ToMatrix4(Camera_getModelMatrix(camera)); + return double4x4ToMatrix4(g.Camera_getModelMatrix(camera)); } @override Future setTransform(Matrix4 transform) async { - var entity = Camera_getEntity(camera); - Engine_setTransform(engine, entity, matrix4ToDouble4x4(transform)); + var entity = g.Camera_getEntity(camera); + g.Engine_setTransform(engine, entity, matrix4ToDouble4x4(transform)); } @override @@ -38,7 +38,7 @@ class FFICamera extends Camera { double far = kFar, double aspect = 1.0, double focalLength = kFocalLength}) async { - Camera_setLensProjection(camera, near, far, aspect, focalLength); + g.Camera_setLensProjection(camera, near, far, aspect, focalLength); } @override @@ -48,7 +48,7 @@ class FFICamera extends Camera { @override Future setModelMatrix(Matrix4 matrix) async { - Camera_setModelMatrix(camera, matrix4ToDouble4x4(matrix)); + g.Camera_setModelMatrix(camera, matrix4ToDouble4x4(matrix)); } @override @@ -63,21 +63,29 @@ class FFICamera extends Camera { @override Future getCullingFar() async { - return Camera_getCullingFar(camera); + return g.Camera_getCullingFar(camera); } @override Future getNear() async { - return Camera_getNear(camera); + return g.Camera_getNear(camera); } @override Future getFocalLength() async { - return Camera_getFocalLength(camera); + return g.Camera_getFocalLength(camera); } @override Future getViewMatrix() async { - return double4x4ToMatrix4(Camera_getViewMatrix(camera)); + return double4x4ToMatrix4(g.Camera_getViewMatrix(camera)); + } + + @override + Future setProjection(Projection projection, double left, double right, + double bottom, double top, double near, double far) async { + var pType = g.Projection.values[projection.index]; + g.Camera_setProjection(camera, pType, left, + right, bottom, top, near, far); } } diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/ffi_view.dart b/thermion_dart/lib/src/viewer/src/ffi/src/ffi_view.dart index b6a45f53..99850908 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/ffi_view.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/ffi_view.dart @@ -1,11 +1,7 @@ import 'dart:ffi'; - import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_dart.g.dart'; -import 'package:thermion_dart/src/viewer/src/shared_types/camera.dart'; import 'package:thermion_dart/src/viewer/src/shared_types/shared_types.dart'; - -import '../../shared_types/view.dart'; -import '../thermion_viewer_ffi.dart'; +import 'ffi_camera.dart'; import 'thermion_viewer_ffi.dart'; class FFIView extends View { @@ -54,4 +50,20 @@ class FFIView extends View { Future setRenderable(bool renderable, FFISwapChain swapChain) async { Viewer_setViewRenderable(viewer, swapChain.swapChain, view, renderable); } + + @override + Future setFrustumCullingEnabled(bool enabled) async { + View_setFrustumCullingEnabled(view, enabled); + } + + @override + Future setBloom(double strength) async { + View_setBloom(view, strength); + } + + @override + Future setToneMapper(ToneMapper mapper) async { + final engine = await Viewer_getEngine(viewer); + View_setToneMappingRenderThread(view, engine, mapper.index); + } } diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart index 5fadb9ce..de0c1cda 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart @@ -16,6 +16,16 @@ external ffi.Pointer make_resource_loader( ffi.Pointer owner, ); +@ffi.Native< + ffi.Pointer Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer, ffi.Pointer)>(isLeaf: true) +external ffi.Pointer Viewer_create( + ffi.Pointer context, + ffi.Pointer loader, + ffi.Pointer platform, + ffi.Pointer uberArchivePath, +); + @ffi.Native)>(isLeaf: true) external void destroy_filament_viewer( ffi.Pointer viewer, @@ -197,12 +207,6 @@ external void set_background_color( double a, ); -@ffi.Native, ffi.Float)>(isLeaf: true) -external void set_bloom( - ffi.Pointer viewer, - double strength, -); - @ffi.Native, ffi.Pointer)>( isLeaf: true) external void load_skybox( @@ -329,18 +333,6 @@ external int load_glb( bool keepData, ); -@ffi.Native< - EntityId Function(ffi.Pointer, ffi.Pointer, - ffi.Size, ffi.Bool, ffi.Int, ffi.Int)>(isLeaf: true) -external int load_glb_from_buffer( - ffi.Pointer sceneManager, - ffi.Pointer data, - int length, - bool keepData, - int priority, - int layer, -); - @ffi.Native< EntityId Function(ffi.Pointer, ffi.Pointer, ffi.Pointer, ffi.Bool)>(isLeaf: true) @@ -379,12 +371,6 @@ external int get_main_camera( ffi.Pointer viewer, ); -@ffi.Native, ffi.Bool)>(isLeaf: true) -external void set_view_frustum_culling( - ffi.Pointer viewer, - bool enabled, -); - @ffi.Native, ffi.Float)>(isLeaf: true) external void set_frame_interval( ffi.Pointer viewer, @@ -671,6 +657,18 @@ external ffi.Pointer SceneManager_getScene( ffi.Pointer tSceneManager, ); +@ffi.Native< + EntityId Function(ffi.Pointer, ffi.Pointer, + ffi.Size, ffi.Bool, ffi.Int, ffi.Int)>(isLeaf: true) +external int SceneManager_loadGlbFromBuffer( + ffi.Pointer sceneManager, + ffi.Pointer arg1, + int length, + bool keepData, + int priority, + int layer, +); + @ffi.Native, EntityId)>( isLeaf: true) external bool update_bone_matrices( @@ -792,161 +790,6 @@ external void set_scale( double scale, ); -@ffi.Native< - ffi.Void Function( - ffi.Pointer, ffi.Float, ffi.Float, ffi.Float)>(isLeaf: true) -external void set_camera_exposure( - ffi.Pointer camera, - double aperture, - double shutterSpeed, - double sensitivity, -); - -@ffi.Native, double4x4)>(isLeaf: true) -external void set_camera_model_matrix( - ffi.Pointer camera, - double4x4 matrix, -); - -@ffi.Native Function(ffi.Pointer, EntityId)>( - isLeaf: true) -external ffi.Pointer get_camera( - ffi.Pointer viewer, - int entity, -); - -@ffi.Native)>(isLeaf: true) -external double4x4 get_camera_model_matrix( - ffi.Pointer camera, -); - -@ffi.Native)>(isLeaf: true) -external double4x4 get_camera_view_matrix( - ffi.Pointer camera, -); - -@ffi.Native)>(isLeaf: true) -external double4x4 get_camera_projection_matrix( - ffi.Pointer camera, -); - -@ffi.Native)>(isLeaf: true) -external double4x4 get_camera_culling_projection_matrix( - ffi.Pointer camera, -); - -@ffi.Native Function(ffi.Pointer)>( - isLeaf: true) -external ffi.Pointer get_camera_frustum( - ffi.Pointer camera, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, double4x4, ffi.Double, ffi.Double)>(isLeaf: true) -external void set_camera_projection_matrix( - ffi.Pointer camera, - double4x4 matrix, - double near, - double far, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Double, ffi.Double, ffi.Double, - ffi.Double, ffi.Bool)>(isLeaf: true) -external void set_camera_projection_from_fov( - ffi.Pointer camera, - double fovInDegrees, - double aspect, - double near, - double far, - bool horizontal, -); - -@ffi.Native)>(isLeaf: true) -external double get_camera_focal_length( - ffi.Pointer camera, -); - -@ffi.Native)>(isLeaf: true) -external double Camera_getFocalLength( - ffi.Pointer camera, -); - -@ffi.Native)>(isLeaf: true) -external double Camera_getNear( - ffi.Pointer camera, -); - -@ffi.Native)>(isLeaf: true) -external double Camera_getCullingFar( - ffi.Pointer camera, -); - -@ffi.Native)>(isLeaf: true) -external double4x4 Camera_getViewMatrix( - ffi.Pointer camera, -); - -@ffi.Native)>(isLeaf: true) -external double4x4 Camera_getModelMatrix( - ffi.Pointer camera, -); - -@ffi.Native)>(isLeaf: true) -external double get_camera_near( - ffi.Pointer camera, -); - -@ffi.Native)>(isLeaf: true) -external double get_camera_culling_far( - ffi.Pointer camera, -); - -@ffi.Native, ffi.Bool)>(isLeaf: true) -external double get_camera_fov( - ffi.Pointer camera, - bool horizontal, -); - -@ffi.Native, ffi.Float)>(isLeaf: true) -external void set_camera_focus_distance( - ffi.Pointer camera, - double focusDistance, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, double4x4, ffi.Double, ffi.Double)>(isLeaf: true) -external void Camera_setCustomProjectionWithCulling( - ffi.Pointer camera, - double4x4 projectionMatrix, - double near, - double far, -); - -@ffi.Native, double4x4)>(isLeaf: true) -external void Camera_setModelMatrix( - ffi.Pointer camera, - double4x4 modelMatrix, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Double, ffi.Double, ffi.Double, - ffi.Double)>(isLeaf: true) -external void Camera_setLensProjection( - ffi.Pointer camera, - double near, - double far, - double aspect, - double focalLength, -); - -@ffi.Native)>(isLeaf: true) -external int Camera_getEntity( - ffi.Pointer camera, -); - @ffi.Native Function(ffi.Pointer)>( isLeaf: true) external ffi.Pointer Engine_getEntityManager( @@ -1350,6 +1193,119 @@ external void MaterialInstance_setParameterFloat2( double y, ); +@ffi.Native)>(isLeaf: true) +external TViewport View_getViewport( + ffi.Pointer view, +); + +@ffi.Native, ffi.Uint32, ffi.Uint32)>( + isLeaf: true) +external void View_updateViewport( + ffi.Pointer view, + int width, + int height, +); + +@ffi.Native, ffi.Pointer)>( + isLeaf: true) +external void View_setRenderTarget( + ffi.Pointer view, + ffi.Pointer renderTarget, +); + +@ffi.Native, ffi.Bool)>(isLeaf: true) +external void View_setFrustumCullingEnabled( + ffi.Pointer view, + bool enabled, +); + +@ffi.Native, ffi.Bool)>(isLeaf: true) +external void View_setPostProcessing( + ffi.Pointer tView, + bool enabled, +); + +@ffi.Native, ffi.Bool)>(isLeaf: true) +external void View_setShadowsEnabled( + ffi.Pointer tView, + bool enabled, +); + +@ffi.Native, ffi.Int)>(isLeaf: true) +external void View_setShadowType( + ffi.Pointer tView, + int shadowType, +); + +@ffi.Native, ffi.Float, ffi.Float)>( + isLeaf: true) +external void View_setSoftShadowOptions( + ffi.Pointer tView, + double penumbraScale, + double penumbraRatioScale, +); + +@ffi.Native, ffi.Float)>(isLeaf: true) +external void View_setBloom( + ffi.Pointer tView, + double strength, +); + +@ffi.Native< + ffi.Void Function(ffi.Pointer, ffi.Pointer, + ffi.UnsignedInt)>(symbol: "View_setToneMapping", isLeaf: true) +external void _View_setToneMapping( + ffi.Pointer tView, + ffi.Pointer tEngine, + int toneMapping, +); + +void View_setToneMapping( + ffi.Pointer tView, + ffi.Pointer tEngine, + ToneMapping toneMapping, +) => + _View_setToneMapping( + tView, + tEngine, + toneMapping.value, + ); + +@ffi.Native< + ffi.Void Function( + ffi.Pointer, ffi.Bool, ffi.Bool, ffi.Bool)>(isLeaf: true) +external void View_setAntiAliasing( + ffi.Pointer tView, + bool msaa, + bool fxaa, + bool taa, +); + +@ffi.Native, ffi.Int, ffi.Bool)>( + isLeaf: true) +external void View_setLayerEnabled( + ffi.Pointer tView, + int layer, + bool visible, +); + +@ffi.Native, ffi.Pointer)>( + isLeaf: true) +external void View_setCamera( + ffi.Pointer tView, + ffi.Pointer tCamera, +); + +@ffi.Native Function(ffi.Pointer)>(isLeaf: true) +external ffi.Pointer View_getScene( + ffi.Pointer tView, +); + +@ffi.Native Function(ffi.Pointer)>(isLeaf: true) +external ffi.Pointer View_getCamera( + ffi.Pointer tView, +); + @ffi.Native< ffi.Void Function( ffi.Pointer, @@ -1468,6 +1424,21 @@ external void Viewer_requestFrameRenderThread( ffi.Pointer> onComplete, ); +@ffi.Native< + ffi.Void Function( + ffi.Pointer, ffi.Pointer, ffi.Int)>(isLeaf: true) +external void View_setToneMappingRenderThread( + ffi.Pointer tView, + ffi.Pointer tEngine, + int arg2, +); + +@ffi.Native, ffi.Double)>(isLeaf: true) +external void View_setBloomRenderThread( + ffi.Pointer tView, + double bloom, +); + @ffi.Native)>(isLeaf: true) external void destroy_filament_viewer_render_thread( ffi.Pointer viewer, @@ -1530,18 +1501,6 @@ external void set_background_image_position_render_thread( bool clamp, ); -@ffi.Native, ffi.Int)>(isLeaf: true) -external void set_tone_mapping_render_thread( - ffi.Pointer viewer, - int toneMapping, -); - -@ffi.Native, ffi.Float)>(isLeaf: true) -external void set_bloom_render_thread( - ffi.Pointer viewer, - double strength, -); - @ffi.Native< ffi.Void Function(ffi.Pointer, ffi.Pointer, ffi.Pointer>)>(isLeaf: true) @@ -1556,6 +1515,28 @@ external void remove_skybox_render_thread( ffi.Pointer viewer, ); +@ffi.Native< + ffi.Void Function( + ffi.Pointer, + ffi.Pointer, + ffi.Size, + ffi.Int, + ffi.Bool, + ffi.Int, + ffi.Int, + ffi.Pointer>)>( + isLeaf: true) +external void SceneManager_loadGlbFromBufferRenderThread( + ffi.Pointer sceneManager, + ffi.Pointer data, + int length, + int numInstances, + bool keepData, + int priority, + int layer, + ffi.Pointer> callback, +); + @ffi.Native< ffi.Void Function( ffi.Pointer, @@ -1572,28 +1553,6 @@ external void load_glb_render_thread( ffi.Pointer> callback, ); -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Size, - ffi.Int, - ffi.Bool, - ffi.Int, - ffi.Int, - ffi.Pointer>)>( - isLeaf: true) -external void load_glb_from_buffer_render_thread( - ffi.Pointer sceneManager, - ffi.Pointer data, - int length, - int numInstances, - bool keepData, - int priority, - int layer, - ffi.Pointer> callback, -); - @ffi.Native< ffi.Void Function( ffi.Pointer, @@ -1833,118 +1792,202 @@ external void unproject_texture_render_thread( ffi.Pointer> callback, ); -@ffi.Native)>(isLeaf: true) -external TViewport View_getViewport( - ffi.Pointer view, -); - -@ffi.Native, ffi.Uint32, ffi.Uint32)>( - isLeaf: true) -external void View_updateViewport( - ffi.Pointer view, - int width, - int height, -); - -@ffi.Native, ffi.Pointer)>( - isLeaf: true) -external void View_setRenderTarget( - ffi.Pointer view, - ffi.Pointer renderTarget, -); - -@ffi.Native, ffi.Bool)>(isLeaf: true) -external void View_setFrustumCullingEnabled( - ffi.Pointer view, - bool enabled, -); - -@ffi.Native, ffi.Bool)>(isLeaf: true) -external void View_setPostProcessing( - ffi.Pointer tView, - bool enabled, -); - -@ffi.Native, ffi.Bool)>(isLeaf: true) -external void View_setShadowsEnabled( - ffi.Pointer tView, - bool enabled, -); - -@ffi.Native, ffi.Int)>(isLeaf: true) -external void View_setShadowType( - ffi.Pointer tView, - int shadowType, -); - -@ffi.Native, ffi.Float, ffi.Float)>( - isLeaf: true) -external void View_setSoftShadowOptions( - ffi.Pointer tView, - double penumbraScale, - double penumbraRatioScale, -); - -@ffi.Native, ffi.Float)>(isLeaf: true) -external void View_setBloom( - ffi.Pointer tView, - double strength, -); - @ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Pointer, - ffi.UnsignedInt)>(symbol: "View_setToneMapping", isLeaf: true) -external void _View_setToneMapping( - ffi.Pointer tView, - ffi.Pointer tEngine, - int toneMapping, + ffi.Void Function( + ffi.Pointer, ffi.Float, ffi.Float, ffi.Float)>(isLeaf: true) +external void set_camera_exposure( + ffi.Pointer camera, + double aperture, + double shutterSpeed, + double sensitivity, ); -void View_setToneMapping( - ffi.Pointer tView, - ffi.Pointer tEngine, - ToneMapping toneMapping, -) => - _View_setToneMapping( - tView, - tEngine, - toneMapping.value, - ); +@ffi.Native, double4x4)>(isLeaf: true) +external void set_camera_model_matrix( + ffi.Pointer camera, + double4x4 matrix, +); + +@ffi.Native Function(ffi.Pointer, EntityId)>( + isLeaf: true) +external ffi.Pointer get_camera( + ffi.Pointer viewer, + int entity, +); + +@ffi.Native)>(isLeaf: true) +external double4x4 get_camera_model_matrix( + ffi.Pointer camera, +); + +@ffi.Native)>(isLeaf: true) +external double4x4 get_camera_view_matrix( + ffi.Pointer camera, +); + +@ffi.Native)>(isLeaf: true) +external double4x4 get_camera_projection_matrix( + ffi.Pointer camera, +); + +@ffi.Native)>(isLeaf: true) +external double4x4 get_camera_culling_projection_matrix( + ffi.Pointer camera, +); + +@ffi.Native Function(ffi.Pointer)>( + isLeaf: true) +external ffi.Pointer get_camera_frustum( + ffi.Pointer camera, +); @ffi.Native< ffi.Void Function( - ffi.Pointer, ffi.Bool, ffi.Bool, ffi.Bool)>(isLeaf: true) -external void View_setAntiAliasing( - ffi.Pointer tView, - bool msaa, - bool fxaa, - bool taa, + ffi.Pointer, double4x4, ffi.Double, ffi.Double)>(isLeaf: true) +external void set_camera_projection_matrix( + ffi.Pointer camera, + double4x4 matrix, + double near, + double far, ); -@ffi.Native, ffi.Int, ffi.Bool)>( - isLeaf: true) -external void View_setLayerEnabled( - ffi.Pointer tView, - int layer, - bool visible, +@ffi.Native< + ffi.Void Function(ffi.Pointer, ffi.Double, ffi.Double, ffi.Double, + ffi.Double, ffi.Bool)>(isLeaf: true) +external void set_camera_projection_from_fov( + ffi.Pointer camera, + double fovInDegrees, + double aspect, + double near, + double far, + bool horizontal, ); -@ffi.Native, ffi.Pointer)>( - isLeaf: true) -external void View_setCamera( - ffi.Pointer tView, +@ffi.Native)>(isLeaf: true) +external double get_camera_focal_length( + ffi.Pointer camera, +); + +@ffi.Native)>(isLeaf: true) +external double Camera_getFocalLength( + ffi.Pointer camera, +); + +@ffi.Native)>(isLeaf: true) +external double Camera_getNear( + ffi.Pointer camera, +); + +@ffi.Native)>(isLeaf: true) +external double Camera_getCullingFar( + ffi.Pointer camera, +); + +@ffi.Native)>(isLeaf: true) +external double4x4 Camera_getViewMatrix( + ffi.Pointer camera, +); + +@ffi.Native)>(isLeaf: true) +external double4x4 Camera_getModelMatrix( + ffi.Pointer camera, +); + +@ffi.Native)>(isLeaf: true) +external double get_camera_near( + ffi.Pointer camera, +); + +@ffi.Native)>(isLeaf: true) +external double get_camera_culling_far( + ffi.Pointer camera, +); + +@ffi.Native, ffi.Bool)>(isLeaf: true) +external double get_camera_fov( + ffi.Pointer camera, + bool horizontal, +); + +@ffi.Native, ffi.Float)>(isLeaf: true) +external void set_camera_focus_distance( + ffi.Pointer camera, + double focusDistance, +); + +@ffi.Native< + ffi.Void Function( + ffi.Pointer, double4x4, ffi.Double, ffi.Double)>(isLeaf: true) +external void Camera_setCustomProjectionWithCulling( + ffi.Pointer camera, + double4x4 projectionMatrix, + double near, + double far, +); + +@ffi.Native, double4x4)>(isLeaf: true) +external void Camera_setModelMatrix( + ffi.Pointer camera, + double4x4 modelMatrix, +); + +@ffi.Native< + ffi.Void Function(ffi.Pointer, ffi.Double, ffi.Double, ffi.Double, + ffi.Double)>(isLeaf: true) +external void Camera_setLensProjection( + ffi.Pointer camera, + double near, + double far, + double aspect, + double focalLength, +); + +@ffi.Native)>(isLeaf: true) +external int Camera_getEntity( + ffi.Pointer camera, +); + +@ffi.Native< + ffi.Void Function( + ffi.Pointer, + ffi.UnsignedInt, + ffi.Double, + ffi.Double, + ffi.Double, + ffi.Double, + ffi.Double, + ffi.Double)>(symbol: "Camera_setProjection", isLeaf: true) +external void _Camera_setProjection( ffi.Pointer tCamera, + int projection, + double left, + double right, + double bottom, + double top, + double near, + double far, ); -@ffi.Native Function(ffi.Pointer)>(isLeaf: true) -external ffi.Pointer View_getScene( - ffi.Pointer tView, -); - -@ffi.Native Function(ffi.Pointer)>(isLeaf: true) -external ffi.Pointer View_getCamera( - ffi.Pointer tView, -); +void Camera_setProjection( + ffi.Pointer tCamera, + Projection projection, + double left, + double right, + double bottom, + double top, + double near, + double far, +) => + _Camera_setProjection( + tCamera, + projection.value, + left, + right, + bottom, + top, + near, + far, + ); @ffi.Native< ffi.Pointer Function(ffi.Pointer, ffi.Pointer, @@ -2218,12 +2261,6 @@ typedef DartLoadFilamentResourceIntoOutPointerFunction = void Function( /// - setting up a render loop typedef EntityId = ffi.Int32; typedef DartEntityId = int; -typedef FilamentRenderCallback - = ffi.Pointer>; -typedef FilamentRenderCallbackFunction = ffi.Void Function( - ffi.Pointer owner); -typedef DartFilamentRenderCallbackFunction = void Function( - ffi.Pointer owner); final class TViewport extends ffi.Struct { @ffi.Int32() @@ -2255,6 +2292,27 @@ enum ToneMapping { }; } +typedef FilamentRenderCallback + = ffi.Pointer>; +typedef FilamentRenderCallbackFunction = ffi.Void Function( + ffi.Pointer owner); +typedef DartFilamentRenderCallbackFunction = void Function( + ffi.Pointer owner); + +enum Projection { + Perspective(0), + Orthographic(1); + + final int value; + const Projection(this.value); + + static Projection fromValue(int value) => switch (value) { + 0 => Perspective, + 1 => Orthographic, + _ => throw ArgumentError("Unknown value for Projection: $value"), + }; +} + typedef GizmoPickCallback = ffi.Pointer>; typedef GizmoPickCallbackFunction = ffi.Void Function( diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart index 69a46afe..9e51ba3a 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart @@ -13,7 +13,7 @@ import '../../thermion_viewer_base.dart'; import 'package:logging/logging.dart'; import 'callbacks.dart'; -import 'camera_ffi.dart'; +import 'ffi_camera.dart'; import 'ffi_view.dart'; // ignore: constant_identifier_names @@ -180,14 +180,8 @@ class ThermionViewerFFI extends ThermionViewer { nullptr; _viewer = await withPointerCallback( (Pointer)>> callback) { - Viewer_createOnRenderThread( - _sharedContext, - _driver, - uberarchivePtr, - resourceLoader, - _renderCallback, - _renderCallbackOwner, - callback); + Viewer_createOnRenderThread(_sharedContext, _driver, uberarchivePtr, + resourceLoader, _renderCallback, _renderCallbackOwner, callback); }); allocator.free(uberarchivePtr); @@ -529,8 +523,9 @@ class ThermionViewerFFI extends ThermionViewer { throw Exception("Not yet implemented"); } final pathPtr = path.toNativeUtf8(allocator: allocator).cast(); - var entity = await withIntCallback((callback) => load_glb_render_thread( - _sceneManager!, pathPtr, numInstances, keepData, callback)); + var entity = await withIntCallback((callback)=> load_glb_render_thread( + _sceneManager!, pathPtr, numInstances, keepData, callback)); + allocator.free(pathPtr); if (entity == _FILAMENT_ASSET_ERROR) { throw Exception("An error occurred loading the asset at $path"); @@ -561,7 +556,7 @@ class ThermionViewerFFI extends ThermionViewer { } var entity = await withIntCallback((callback) => - load_glb_from_buffer_render_thread(_sceneManager!, data.address, + SceneManager_loadGlbFromBufferRenderThread(_sceneManager!, data.address, data.length, numInstances, keepData, priority, layer, callback)); if (entity == _FILAMENT_ASSET_ERROR) { @@ -1160,7 +1155,8 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future setToneMapping(ToneMapper mapper) async { - set_tone_mapping_render_thread(_viewer!, mapper.index); + final view = await getViewAt(0); + view.setToneMapper(mapper); } /// @@ -1302,7 +1298,8 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future setViewFrustumCulling(bool enabled) async { - set_view_frustum_culling(_viewer!, enabled); + var view = await getViewAt(0); + view.setFrustumCullingEnabled(enabled); } /// @@ -2052,10 +2049,13 @@ class ThermionViewerFFI extends ThermionViewer { completer.complete(true); }); - Viewer_requestFrameRenderThread( - _viewer!, callback.nativeFunction); + Viewer_requestFrameRenderThread(_viewer!, callback.nativeFunction); - await completer.future.timeout(Duration(seconds: 1)); + try { + await completer.future.timeout(Duration(seconds: 1)); + } catch (err) { + print("WARNING - render call timed out"); + } } Future createCamera() async { diff --git a/thermion_dart/lib/src/viewer/src/ffi/thermion_viewer_ffi.dart b/thermion_dart/lib/src/viewer/src/ffi/thermion_viewer_ffi.dart index 53a9c860..06003446 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/thermion_viewer_ffi.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/thermion_viewer_ffi.dart @@ -1,4 +1,3 @@ library; export 'src/thermion_viewer_ffi.dart' show ThermionViewerFFI; -export 'src/camera_ffi.dart'; diff --git a/thermion_dart/lib/src/viewer/src/shared_types/camera.dart b/thermion_dart/lib/src/viewer/src/shared_types/camera.dart index 6e1f4d6e..27d35bf2 100644 --- a/thermion_dart/lib/src/viewer/src/shared_types/camera.dart +++ b/thermion_dart/lib/src/viewer/src/shared_types/camera.dart @@ -2,7 +2,11 @@ import 'package:vector_math/vector_math_64.dart'; import '../thermion_viewer_base.dart'; +enum Projection { Perspective, Orthographic } + abstract class Camera { + Future setProjection(Projection projection, double left, double right, + double bottom, double top, double near, double far); Future setProjectionMatrixWithCulling( Matrix4 projectionMatrix, double near, double far); diff --git a/thermion_dart/lib/src/viewer/src/shared_types/view.dart b/thermion_dart/lib/src/viewer/src/shared_types/view.dart index 1d88b1d9..c862505a 100644 --- a/thermion_dart/lib/src/viewer/src/shared_types/view.dart +++ b/thermion_dart/lib/src/viewer/src/shared_types/view.dart @@ -1,6 +1,11 @@ import 'package:thermion_dart/thermion_dart.dart'; import 'swap_chain.dart'; +/// +/// The viewport currently attached to a [View]. +/// +/// The dimensions here are guaranteed to be in physical pixels. +/// class Viewport { final int left; final int bottom; @@ -19,4 +24,7 @@ abstract class View { Future setPostProcessing(bool enabled); Future setAntiAliasing(bool msaa, bool fxaa, bool taa); Future setRenderable(bool renderable, covariant SwapChain swapChain); + Future setFrustumCullingEnabled(bool enabled); + Future setToneMapper(ToneMapper mapper); + Future setBloom(double strength); } diff --git a/thermion_dart/lib/src/viewer/src/thermion_viewer_base.dart b/thermion_dart/lib/src/viewer/src/thermion_viewer_base.dart index bb9d5ac1..420f2393 100644 --- a/thermion_dart/lib/src/viewer/src/thermion_viewer_base.dart +++ b/thermion_dart/lib/src/viewer/src/thermion_viewer_base.dart @@ -887,7 +887,7 @@ abstract class ThermionViewer { /// /// /// - Future getActiveCamera(); + Future getActiveCamera(); /// /// diff --git a/thermion_dart/native/include/TCamera.h b/thermion_dart/native/include/TCamera.h new file mode 100644 index 00000000..a0ca7e17 --- /dev/null +++ b/thermion_dart/native/include/TCamera.h @@ -0,0 +1,50 @@ +#pragma once + +#ifdef __cplusplus +namespace thermion { +extern "C" +{ +#endif + +#include "ThermionDartApi.h" + +enum Projection { + Perspective, + Orthographic +}; + +// Camera methods +EMSCRIPTEN_KEEPALIVE void set_camera_exposure(TCamera *camera, float aperture, float shutterSpeed, float sensitivity); +EMSCRIPTEN_KEEPALIVE void set_camera_model_matrix(TCamera *camera, double4x4 matrix); +EMSCRIPTEN_KEEPALIVE TCamera *get_camera(TViewer *viewer, EntityId entity); +EMSCRIPTEN_KEEPALIVE double4x4 get_camera_model_matrix(TCamera *const camera); +EMSCRIPTEN_KEEPALIVE double4x4 get_camera_view_matrix(TCamera *const camera); +EMSCRIPTEN_KEEPALIVE double4x4 get_camera_projection_matrix(TCamera *const camera); +EMSCRIPTEN_KEEPALIVE double4x4 get_camera_culling_projection_matrix(TCamera *const camera); +EMSCRIPTEN_KEEPALIVE const double *const get_camera_frustum(TCamera *const camera); +EMSCRIPTEN_KEEPALIVE void set_camera_projection_matrix(TCamera *camera, double4x4 matrix, double near, double far); +EMSCRIPTEN_KEEPALIVE void set_camera_projection_from_fov(TCamera *camera, double fovInDegrees, double aspect, double near, double far, bool horizontal); +EMSCRIPTEN_KEEPALIVE double get_camera_focal_length(TCamera *const camera); +EMSCRIPTEN_KEEPALIVE double Camera_getFocalLength(TCamera *const camera); +EMSCRIPTEN_KEEPALIVE double Camera_getNear(TCamera *const camera); +EMSCRIPTEN_KEEPALIVE double Camera_getCullingFar(TCamera *const camera); +EMSCRIPTEN_KEEPALIVE double4x4 Camera_getViewMatrix(TCamera *const camera); +EMSCRIPTEN_KEEPALIVE double4x4 Camera_getModelMatrix(TCamera* camera); + +EMSCRIPTEN_KEEPALIVE double get_camera_near(TCamera *camera); +EMSCRIPTEN_KEEPALIVE double get_camera_culling_far(TCamera *camera); +EMSCRIPTEN_KEEPALIVE float get_camera_fov(TCamera *camera, bool horizontal); +EMSCRIPTEN_KEEPALIVE void set_camera_focus_distance(TCamera *camera, float focusDistance); + +EMSCRIPTEN_KEEPALIVE void Camera_setCustomProjectionWithCulling(TCamera* camera, double4x4 projectionMatrix, double near, double far); +EMSCRIPTEN_KEEPALIVE void Camera_setModelMatrix(TCamera* camera, double4x4 modelMatrix); +EMSCRIPTEN_KEEPALIVE void Camera_setLensProjection(TCamera *camera, double near, double far, double aspect, double focalLength); +EMSCRIPTEN_KEEPALIVE EntityId Camera_getEntity(TCamera* camera); +EMSCRIPTEN_KEEPALIVE void Camera_setProjection(TCamera *const tCamera, Projection projection, double left, double right, + double bottom, double top, + double near, double far); + +#ifdef __cplusplus +} +} +#endif diff --git a/thermion_dart/native/include/ThermionDartAPIUtils.h b/thermion_dart/native/include/ThermionDartAPIUtils.h new file mode 100644 index 00000000..9e8e7858 --- /dev/null +++ b/thermion_dart/native/include/ThermionDartAPIUtils.h @@ -0,0 +1,27 @@ +#pragma once +#include +#include "APIBoundaryTypes.h" + +namespace thermion { + +// Helper function to convert filament::math::mat4 to double4x4 +static double4x4 convert_mat4_to_double4x4(const filament::math::mat4 &mat) +{ + return double4x4{ + {mat[0][0], mat[0][1], mat[0][2], mat[0][3]}, + {mat[1][0], mat[1][1], mat[1][2], mat[1][3]}, + {mat[2][0], mat[2][1], mat[2][2], mat[2][3]}, + {mat[3][0], mat[3][1], mat[3][2], mat[3][3]}, + }; +} + +// Helper function to convert double4x4 to filament::math::mat4 +static filament::math::mat4 convert_double4x4_to_mat4(const double4x4 &d_mat) +{ + return filament::math::mat4{ + filament::math::float4{float(d_mat.col1[0]), float(d_mat.col1[1]), float(d_mat.col1[2]), float(d_mat.col1[3])}, + filament::math::float4{float(d_mat.col2[0]), float(d_mat.col2[1]), float(d_mat.col2[2]), float(d_mat.col2[3])}, + filament::math::float4{float(d_mat.col3[0]), float(d_mat.col3[1]), float(d_mat.col3[2]), float(d_mat.col3[3])}, + filament::math::float4{float(d_mat.col4[0]), float(d_mat.col4[1]), float(d_mat.col4[2]), float(d_mat.col4[3])}}; +} +} \ No newline at end of file diff --git a/thermion_dart/native/include/ThermionDartApi.h b/thermion_dart/native/include/ThermionDartApi.h index fdd4e557..1af84dc5 100644 --- a/thermion_dart/native/include/ThermionDartApi.h +++ b/thermion_dart/native/include/ThermionDartApi.h @@ -48,12 +48,15 @@ #include "APIBoundaryTypes.h" #include "ResourceBuffer.hpp" +#include "ThermionDartAPIUtils.h" #ifdef __cplusplus extern "C" { #endif + + EMSCRIPTEN_KEEPALIVE TViewer *Viewer_create(const void *const context, const void *const loader, void *const platform, const char *uberArchivePath); EMSCRIPTEN_KEEPALIVE void destroy_filament_viewer(TViewer *viewer); EMSCRIPTEN_KEEPALIVE TSceneManager *Viewer_getSceneManager(TViewer *viewer); @@ -93,7 +96,7 @@ extern "C" EMSCRIPTEN_KEEPALIVE void set_background_image_position(TViewer *viewer, float x, float y, bool clamp); EMSCRIPTEN_KEEPALIVE void set_background_color(TViewer *viewer, const float r, const float g, const float b, const float a); - EMSCRIPTEN_KEEPALIVE void set_bloom(TViewer *viewer, float strength); + EMSCRIPTEN_KEEPALIVE void load_skybox(TViewer *viewer, const char *skyboxPath); EMSCRIPTEN_KEEPALIVE void load_ibl(TViewer *viewer, const char *iblPath, float intensity); EMSCRIPTEN_KEEPALIVE void create_ibl(TViewer *viewer, float r, float g, float b, float intensity); @@ -123,14 +126,12 @@ extern "C" EMSCRIPTEN_KEEPALIVE void set_light_position(TViewer *viewer, EntityId light, float x, float y, float z); EMSCRIPTEN_KEEPALIVE void set_light_direction(TViewer *viewer, EntityId light, float x, float y, float z); EMSCRIPTEN_KEEPALIVE EntityId load_glb(TSceneManager *sceneManager, const char *assetPath, int numInstances, bool keepData); - EMSCRIPTEN_KEEPALIVE EntityId load_glb_from_buffer(TSceneManager *sceneManager, const void *const data, size_t length, bool keepData, int priority, int layer); EMSCRIPTEN_KEEPALIVE EntityId load_gltf(TSceneManager *sceneManager, const char *assetPath, const char *relativePath, bool keepData); EMSCRIPTEN_KEEPALIVE EntityId create_instance(TSceneManager *sceneManager, EntityId id); EMSCRIPTEN_KEEPALIVE int get_instance_count(TSceneManager *sceneManager, EntityId entityId); EMSCRIPTEN_KEEPALIVE void get_instances(TSceneManager *sceneManager, EntityId entityId, EntityId *out); EMSCRIPTEN_KEEPALIVE EntityId get_main_camera(TViewer *viewer); - EMSCRIPTEN_KEEPALIVE void set_view_frustum_culling(TViewer *viewer, bool enabled); EMSCRIPTEN_KEEPALIVE void set_frame_interval(TViewer *viewer, float interval); @@ -208,6 +209,7 @@ extern "C" EMSCRIPTEN_KEEPALIVE TCamera* SceneManager_findCameraByName(TSceneManager* tSceneManager, EntityId entity, const char* name); EMSCRIPTEN_KEEPALIVE void SceneManager_setVisibilityLayer(TSceneManager *tSceneManager, EntityId entity, int layer); EMSCRIPTEN_KEEPALIVE TScene* SceneManager_getScene(TSceneManager *tSceneManager); + EMSCRIPTEN_KEEPALIVE EntityId SceneManager_loadGlbFromBuffer(TSceneManager *sceneManager, const uint8_t *const, size_t length, bool keepData, int priority, int layer); EMSCRIPTEN_KEEPALIVE bool update_bone_matrices(TSceneManager *sceneManager, EntityId entityId); @@ -225,37 +227,7 @@ extern "C" EMSCRIPTEN_KEEPALIVE void set_rotation(TSceneManager *sceneManager, EntityId entity, float rads, float x, float y, float z, float w); EMSCRIPTEN_KEEPALIVE void set_scale(TSceneManager *sceneManager, EntityId entity, float scale); - // Camera methods - EMSCRIPTEN_KEEPALIVE void set_view_frustum_culling(TViewer *viewer, bool enabled); - EMSCRIPTEN_KEEPALIVE void set_camera_exposure(TCamera *camera, float aperture, float shutterSpeed, float sensitivity); - EMSCRIPTEN_KEEPALIVE void set_camera_model_matrix(TCamera *camera, double4x4 matrix); - EMSCRIPTEN_KEEPALIVE TCamera *get_camera(TViewer *viewer, EntityId entity); - EMSCRIPTEN_KEEPALIVE double4x4 get_camera_model_matrix(TCamera *const camera); - EMSCRIPTEN_KEEPALIVE double4x4 get_camera_view_matrix(TCamera *const camera); - EMSCRIPTEN_KEEPALIVE double4x4 get_camera_projection_matrix(TCamera *const camera); - EMSCRIPTEN_KEEPALIVE double4x4 get_camera_culling_projection_matrix(TCamera *const camera); - EMSCRIPTEN_KEEPALIVE const double *const get_camera_frustum(TCamera *const camera); - EMSCRIPTEN_KEEPALIVE void set_camera_projection_matrix(TCamera *camera, double4x4 matrix, double near, double far); - EMSCRIPTEN_KEEPALIVE void set_camera_projection_from_fov(TCamera *camera, double fovInDegrees, double aspect, double near, double far, bool horizontal); - EMSCRIPTEN_KEEPALIVE double get_camera_focal_length(TCamera *const camera); - EMSCRIPTEN_KEEPALIVE double Camera_getFocalLength(TCamera *const camera); - EMSCRIPTEN_KEEPALIVE double Camera_getNear(TCamera *const camera); - EMSCRIPTEN_KEEPALIVE double Camera_getCullingFar(TCamera *const camera); - EMSCRIPTEN_KEEPALIVE double4x4 Camera_getViewMatrix(TCamera *const camera); - EMSCRIPTEN_KEEPALIVE double4x4 Camera_getModelMatrix(TCamera* camera); - - EMSCRIPTEN_KEEPALIVE double get_camera_near(TCamera *camera); - EMSCRIPTEN_KEEPALIVE double get_camera_culling_far(TCamera *camera); - EMSCRIPTEN_KEEPALIVE float get_camera_fov(TCamera *camera, bool horizontal); - EMSCRIPTEN_KEEPALIVE void set_camera_focus_distance(TCamera *camera, float focusDistance); - - EMSCRIPTEN_KEEPALIVE void Camera_setCustomProjectionWithCulling(TCamera* camera, double4x4 projectionMatrix, double near, double far); - EMSCRIPTEN_KEEPALIVE void Camera_setModelMatrix(TCamera* camera, double4x4 modelMatrix); - EMSCRIPTEN_KEEPALIVE void Camera_setLensProjection(TCamera *camera, double near, double far, double aspect, double focalLength); - - EMSCRIPTEN_KEEPALIVE EntityId Camera_getEntity(TCamera* camera); EMSCRIPTEN_KEEPALIVE TCamera *Engine_getCameraComponent(TEngine *engine, EntityId entity); - EMSCRIPTEN_KEEPALIVE TEntityManager *Engine_getEntityManager(TEngine *engine); // SceneManager diff --git a/thermion_dart/native/include/ThermionDartRenderThreadApi.h b/thermion_dart/native/include/ThermionDartRenderThreadApi.h index 0caedbca..967823f6 100644 --- a/thermion_dart/native/include/ThermionDartRenderThreadApi.h +++ b/thermion_dart/native/include/ThermionDartRenderThreadApi.h @@ -2,8 +2,10 @@ #define _DART_FILAMENT_FFI_API_H #include "ThermionDartApi.h" +#include "TView.h" #ifdef __cplusplus +namespace thermion { extern "C" { #endif @@ -33,6 +35,10 @@ extern "C" void Viewer_captureRenderTargetRenderThread(TViewer *viewer, TView* view, TSwapChain* swapChain, TRenderTarget* renderTarget, uint8_t* out, void (*onComplete)()); void Viewer_requestFrameRenderThread(TViewer *viewer, void(*onComplete)()); + void View_setToneMappingRenderThread(TView *tView, TEngine *tEngine, thermion::ToneMapping toneMapping); + void View_setBloomRenderThread(TView *tView, double bloom); + + void destroy_filament_viewer_render_thread(TViewer *viewer); FilamentRenderCallback make_render_callback_fn_pointer(FilamentRenderCallback); @@ -43,13 +49,11 @@ extern "C" void clear_background_image_render_thread(TViewer *viewer); void set_background_image_render_thread(TViewer *viewer, const char *path, bool fillHeight, void (*onComplete)()); void set_background_image_position_render_thread(TViewer *viewer, float x, float y, bool clamp); - void set_tone_mapping_render_thread(TViewer *viewer, int toneMapping); - void set_bloom_render_thread(TViewer *viewer, float strength); void load_skybox_render_thread(TViewer *viewer, const char *skyboxPath, void (*onComplete)()); void remove_skybox_render_thread(TViewer *viewer); + void SceneManager_loadGlbFromBufferRenderThread(TSceneManager *sceneManager, const uint8_t *const data, size_t length, int numInstances, bool keepData, int priority, int layer, void (*callback)(EntityId)); void load_glb_render_thread(TSceneManager *sceneManager, const char *assetPath, int numInstances, bool keepData, void (*callback)(EntityId)); - void load_glb_from_buffer_render_thread(TSceneManager *sceneManager, const uint8_t *const data, size_t length, int numInstances, bool keepData, int priority, int layer, void (*callback)(EntityId)); void load_gltf_render_thread(TSceneManager *sceneManager, const char *assetPath, const char *relativePath, bool keepData, void (*callback)(EntityId)); void create_instance_render_thread(TSceneManager *sceneManager, EntityId entityId, void (*callback)(EntityId)); void remove_entity_render_thread(TViewer *viewer, EntityId asset, void (*callback)()); @@ -103,6 +107,7 @@ extern "C" #ifdef __cplusplus } +} #endif #endif // _DART_FILAMENT_FFI_API_H diff --git a/thermion_dart/native/src/FilamentViewer.cpp b/thermion_dart/native/src/FilamentViewer.cpp index ed51f0e4..9b3614d1 100644 --- a/thermion_dart/native/src/FilamentViewer.cpp +++ b/thermion_dart/native/src/FilamentViewer.cpp @@ -783,7 +783,6 @@ namespace thermion /// View *FilamentViewer::getViewAt(int32_t index) { - Log("Getting view at %d", index); if (index < _views.size()) { return _views[index]; diff --git a/thermion_dart/native/src/TCamera.cpp b/thermion_dart/native/src/TCamera.cpp new file mode 100644 index 00000000..716c0bf2 --- /dev/null +++ b/thermion_dart/native/src/TCamera.cpp @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "ThermionDartApi.h" +#include "TCamera.h" +#include "ThermionDartAPIUtils.h" + +#include "Log.hpp" + +#ifdef __cplusplus +namespace thermion +{ + extern "C" + { + using namespace filament; + +#endif + + EMSCRIPTEN_KEEPALIVE void Camera_setCustomProjectionWithCulling(TCamera *tCamera, double4x4 projectionMatrix, double near, double far) + { + auto *camera = reinterpret_cast(tCamera); + camera->setCustomProjection(convert_double4x4_to_mat4(projectionMatrix), near, far); + } + + EMSCRIPTEN_KEEPALIVE double4x4 Camera_getModelMatrix(TCamera *tCamera) + { + auto *camera = reinterpret_cast(tCamera); + return convert_mat4_to_double4x4(camera->getModelMatrix()); + } + + EMSCRIPTEN_KEEPALIVE double4x4 Camera_getViewMatrix(TCamera *const tCamera) + { + auto *camera = reinterpret_cast(tCamera); + return convert_mat4_to_double4x4(camera->getViewMatrix()); + } + + EMSCRIPTEN_KEEPALIVE EntityId Camera_getEntity(TCamera *tCamera) + { + auto *camera = reinterpret_cast(tCamera); + return utils::Entity::smuggle(camera->getEntity()); + } + + EMSCRIPTEN_KEEPALIVE double Camera_getFocalLength(TCamera *const tCamera) + { + auto *camera = reinterpret_cast(tCamera); + return camera->getFocalLength() * 1000.0; + } + + EMSCRIPTEN_KEEPALIVE double Camera_getNear(TCamera *const tCamera) + { + auto *camera = reinterpret_cast(tCamera); + return camera->getNear(); + } + + EMSCRIPTEN_KEEPALIVE double Camera_getCullingFar(TCamera *const tCamera) + { + auto *camera = reinterpret_cast(tCamera); + return camera->getCullingFar(); + } + + EMSCRIPTEN_KEEPALIVE void Camera_setProjection(TCamera *const tCamera, Projection projection, double left, double right, + double bottom, double top, + double near, double far) + { + auto *camera = reinterpret_cast(tCamera); + filament::Camera::Projection filamentProjection; + switch(projection) { + case Projection::Orthographic: + filamentProjection = filament::Camera::Projection::ORTHO; + case Projection::Perspective: + filamentProjection = filament::Camera::Projection::PERSPECTIVE; + } + camera->setProjection(filamentProjection, left, right, bottom, top, near, far); + } + +#ifdef __cplusplus + } +} +#endif diff --git a/thermion_dart/native/src/TView.cpp b/thermion_dart/native/src/TView.cpp index cf8ec654..cc8df3b6 100644 --- a/thermion_dart/native/src/TView.cpp +++ b/thermion_dart/native/src/TView.cpp @@ -9,7 +9,6 @@ #include "TView.h" #include "Log.hpp" - #ifdef __cplusplus namespace thermion { extern "C" diff --git a/thermion_dart/native/src/ThermionDartApi.cpp b/thermion_dart/native/src/ThermionDartApi.cpp index b2518e75..e69ccfee 100644 --- a/thermion_dart/native/src/ThermionDartApi.cpp +++ b/thermion_dart/native/src/ThermionDartApi.cpp @@ -23,27 +23,6 @@ extern "C" #include "ThermionDartApi.h" - // Helper function to convert filament::math::mat4 to double4x4 - static double4x4 convert_mat4_to_double4x4(const filament::math::mat4 &mat) - { - return double4x4{ - {mat[0][0], mat[0][1], mat[0][2], mat[0][3]}, - {mat[1][0], mat[1][1], mat[1][2], mat[1][3]}, - {mat[2][0], mat[2][1], mat[2][2], mat[2][3]}, - {mat[3][0], mat[3][1], mat[3][2], mat[3][3]}, - }; - } - - // Helper function to convert double4x4 to filament::math::mat4 - static filament::math::mat4 convert_double4x4_to_mat4(const double4x4 &d_mat) - { - return filament::math::mat4{ - filament::math::float4{float(d_mat.col1[0]), float(d_mat.col1[1]), float(d_mat.col1[2]), float(d_mat.col1[3])}, - filament::math::float4{float(d_mat.col2[0]), float(d_mat.col2[1]), float(d_mat.col2[2]), float(d_mat.col2[3])}, - filament::math::float4{float(d_mat.col3[0]), float(d_mat.col3[1]), float(d_mat.col3[2]), float(d_mat.col3[3])}, - filament::math::float4{float(d_mat.col4[0]), float(d_mat.col4[1]), float(d_mat.col4[2]), float(d_mat.col4[3])}}; - } - EMSCRIPTEN_KEEPALIVE TViewer *Viewer_create(const void *context, const void *const loader, void *const platform, const char *uberArchivePath) { const auto *loaderImpl = new ResourceLoaderWrapperImpl((ResourceLoaderWrapper *)loader); @@ -183,7 +162,7 @@ extern "C" return ((SceneManager *)sceneManager)->loadGlb(assetPath, numInstances, keepData); } - EMSCRIPTEN_KEEPALIVE EntityId load_glb_from_buffer(TSceneManager *sceneManager, const void *const data, size_t length, bool keepData, int priority, int layer) + EMSCRIPTEN_KEEPALIVE EntityId SceneManager_loadGlbFromBuffer(TSceneManager *sceneManager, const uint8_t *const data, size_t length, bool keepData, int priority, int layer) { return ((SceneManager *)sceneManager)->loadGlbFromBuffer((const uint8_t *)data, length, 1, keepData, priority, layer); } @@ -1064,47 +1043,6 @@ extern "C" reinterpret_cast(materialInstance)->setParameter(propertyName, data); } - EMSCRIPTEN_KEEPALIVE void Camera_setCustomProjectionWithCulling(TCamera *tCamera, double4x4 projectionMatrix, double near, double far) - { - auto *camera = reinterpret_cast(tCamera); - camera->setCustomProjection(convert_double4x4_to_mat4(projectionMatrix), near, far); - } - - EMSCRIPTEN_KEEPALIVE double4x4 Camera_getModelMatrix(TCamera *tCamera) - { - auto *camera = reinterpret_cast(tCamera); - return convert_mat4_to_double4x4(camera->getModelMatrix()); - } - - EMSCRIPTEN_KEEPALIVE double4x4 Camera_getViewMatrix(TCamera *const tCamera) - { - auto *camera = reinterpret_cast(tCamera); - return convert_mat4_to_double4x4(camera->getViewMatrix()); - } - - EMSCRIPTEN_KEEPALIVE EntityId Camera_getEntity(TCamera *tCamera) - { - auto *camera = reinterpret_cast(tCamera); - return Entity::smuggle(camera->getEntity()); - } - - EMSCRIPTEN_KEEPALIVE double Camera_getFocalLength(TCamera *const tCamera) - { - auto *camera = reinterpret_cast(tCamera); - return camera->getFocalLength(); - } - - EMSCRIPTEN_KEEPALIVE double Camera_getNear(TCamera *const tCamera) - { - auto *camera = reinterpret_cast(tCamera); - return camera->getNear(); - } - - EMSCRIPTEN_KEEPALIVE double Camera_getCullingFar(TCamera *const tCamera) - { - auto *camera = reinterpret_cast(tCamera); - return camera->getCullingFar(); - } EMSCRIPTEN_KEEPALIVE TCamera *Engine_getCameraComponent(TEngine *tEngine, EntityId entityId) { diff --git a/thermion_dart/native/src/ThermionDartRenderThreadApi.cpp b/thermion_dart/native/src/ThermionDartRenderThreadApi.cpp index 896e0bd0..ffaba43b 100644 --- a/thermion_dart/native/src/ThermionDartRenderThreadApi.cpp +++ b/thermion_dart/native/src/ThermionDartRenderThreadApi.cpp @@ -1,5 +1,6 @@ #include "ThermionDartRenderThreadApi.h" #include "FilamentViewer.hpp" +#include "TView.h" #include "Log.hpp" #include "ThreadPool.hpp" #include "filament/LightManager.h" @@ -328,7 +329,7 @@ extern "C" auto fut = _rl->add_task(lambda); } - void load_glb_from_buffer_render_thread(TSceneManager *sceneManager, + void SceneManager_loadGlbFromBufferRenderThread(TSceneManager *sceneManager, const uint8_t *const data, size_t length, int numInstances, @@ -340,7 +341,7 @@ extern "C" std::packaged_task lambda( [=]() mutable { - auto entity = load_glb_from_buffer(sceneManager, data, length, keepData, priority, layer); + auto entity = SceneManager_loadGlbFromBuffer(sceneManager, data, length, keepData, priority, layer); callback(entity); return entity; }); @@ -562,6 +563,24 @@ extern "C" auto fut = _rl->add_task(lambda); } + void View_setToneMappingRenderThread(TView *tView, TEngine *tEngine, thermion::ToneMapping toneMapping) { + std::packaged_task lambda( + [=] + { + View_setToneMapping(tView, tEngine, toneMapping); + }); + auto fut = _rl->add_task(lambda); + } + + void View_setBloomRenderThread(TView *tView, double bloom) { + std::packaged_task lambda( + [=] + { + View_setBloom(tView, bloom); + }); + auto fut = _rl->add_task(lambda); + } + void reset_to_rest_pose_render_thread(TSceneManager *sceneManager, EntityId entityId, void (*callback)()) { std::packaged_task lambda( diff --git a/thermion_dart/test/camera_tests.dart b/thermion_dart/test/camera_tests.dart index c93e4fde..39677e59 100644 --- a/thermion_dart/test/camera_tests.dart +++ b/thermion_dart/test/camera_tests.dart @@ -76,6 +76,17 @@ void main() async { print(frustum.plane5.constant); }); + test('set orthographic projection', () async { + var viewer = await testHelper.createViewer( + bg: kRed, cameraPosition: Vector3(0, 0, 4)); + var camera = await viewer.getMainCamera(); + await viewer.createGeometry(GeometryHelper.cube()); + + await camera.setProjection(Projection.Orthographic, -0.05, 0.05, -0.05, 0.05, 0.05, 10000); + await testHelper.capture( + viewer, "camera_set_orthographic_projection"); + }); + test('set custom projection/culling matrix', () async { var viewer = await testHelper.createViewer( bg: kRed, cameraPosition: Vector3(0, 0, 4)); diff --git a/thermion_dart/test/helpers.dart b/thermion_dart/test/helpers.dart index c1661106..c6332bc3 100644 --- a/thermion_dart/test/helpers.dart +++ b/thermion_dart/test/helpers.dart @@ -121,7 +121,7 @@ class TestHelper { var viewer = ThermionViewerFFI(resourceLoader: resourceLoader.cast()); await viewer.initialized; - swapChain = await viewer.createSwapChain( + swapChain = await viewer.createHeadlessSwapChain( viewportDimensions.width, viewportDimensions.height); await viewer.updateViewportAndCameraProjection( diff --git a/thermion_flutter/thermion_flutter/lib/src/thermion_flutter_plugin.dart b/thermion_flutter/thermion_flutter/lib/src/thermion_flutter_plugin.dart index 7a1ba8d6..2938e0ad 100644 --- a/thermion_flutter/thermion_flutter/lib/src/thermion_flutter_plugin.dart +++ b/thermion_flutter/thermion_flutter/lib/src/thermion_flutter_plugin.dart @@ -9,30 +9,40 @@ import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_in /// Call [createViewerWithOptions] to create an instance of [ThermionViewer]. /// class ThermionFlutterPlugin { - ThermionFlutterPlugin._(); static bool _initializing = false; static ThermionViewer? _viewer; + static ThermionFlutterOptions? options; + static Future createViewer( {ThermionFlutterOptions options = const ThermionFlutterOptions.empty()}) async { - if (_initializing) { throw Exception("Existing call to createViewer has not completed."); } _initializing = true; + if (_viewer != null) { + throw Exception( + "Instance of ThermionViewer has already been created. Ensure you call dispose() on that instance."); + } + + options = options; + _viewer = await ThermionFlutterPlatform.instance.createViewer(options: options); + var camera = await _viewer!.getActiveCamera(); + camera.setLensProjection(); + _viewer!.onDispose(() async { _viewer = null; + ThermionFlutterPlugin.options = null; }); _initializing = false; return _viewer!; } - } diff --git a/thermion_flutter/thermion_flutter/lib/src/widgets/src/thermion_texture_widget.dart b/thermion_flutter/thermion_flutter/lib/src/widgets/src/thermion_texture_widget.dart index c8cfc949..840f02f7 100644 --- a/thermion_flutter/thermion_flutter/lib/src/widgets/src/thermion_texture_widget.dart +++ b/thermion_flutter/thermion_flutter/lib/src/widgets/src/thermion_texture_widget.dart @@ -7,14 +7,32 @@ import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dar import 'package:vector_math/vector_math_64.dart' hide Colors; class ThermionTextureWidget extends StatefulWidget { + /// + /// + /// final ThermionViewer viewer; + /// + /// + /// final t.View view; + /// + /// + /// final Widget? initial; + /// + /// A callback that will be invoked whenever this widget (and the underlying texture is resized). + /// + final Future Function(Size size, t.View view, double pixelRatio)? onResize; + const ThermionTextureWidget( - {super.key, required this.viewer, required this.view, this.initial}); + {super.key, + required this.viewer, + required this.view, + this.initial, + this.onResize}); @override State createState() { @@ -54,9 +72,16 @@ class _ThermionTextureWidgetState extends State { .createTexture(widget.view, width, height); await widget.view.updateViewport(_texture!.width, _texture!.height); - var camera = await widget.view.getCamera(); - await camera.setLensProjection( - aspect: _texture!.width / _texture!.height); + + try { + await widget.onResize?.call( + Size(_texture!.width.toDouble(), _texture!.height.toDouble()), + widget.view, + dpr); + } catch (err, st) { + print(err); + print(st); + } if (mounted) { setState(() {}); @@ -124,7 +149,9 @@ class _ThermionTextureWidgetState extends State { _resizing.add(completer.future); - newSize *= MediaQuery.of(context).devicePixelRatio; + final dpr = MediaQuery.of(context).devicePixelRatio; + + newSize *= dpr; var newWidth = newSize.width.ceil(); var newHeight = newSize.height.ceil(); @@ -136,11 +163,13 @@ class _ThermionTextureWidgetState extends State { 0, ); - await widget.view.updateViewport(_texture!.width, _texture!.height); - var camera = await widget.view.getCamera(); - await camera.setLensProjection( - aspect: _texture!.width.toDouble() / _texture!.height.toDouble()); + + await widget.onResize?.call( + Size(_texture!.width.toDouble(), _texture!.height.toDouble()), + widget.view, + dpr); + if (!mounted) { return; } @@ -169,108 +198,3 @@ class _ThermionTextureWidgetState extends State { ])); } } - - - -// class _ThermionWidgetState extends State { - -// ThermionFlutterTexture? _texture; - -// @override -// void initState() { -// WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { -// await widget.viewer.initialized; -// widget.viewer.onDispose(() async { -// _rendering = false; - -// if (_texture != null) { -// var texture = _texture; -// _texture = null; -// if (mounted) { -// setState(() {}); -// } -// await ThermionFlutterPlugin.destroyTexture(texture!); -// } -// }); -// var dpr = MediaQuery.of(context).devicePixelRatio; - -// var size = ((context.findRenderObject()) as RenderBox).size; -// _texture = await ThermionFlutterPlugin.createTexture( -// size.width, size.height, 0, 0, dpr); - -// if (mounted) { -// setState(() {}); -// } - -// _requestFrame(); -// }); -// super.initState(); -// } - -// bool _rendering = false; - -// void _requestFrame() { -// WidgetsBinding.instance.scheduleFrameCallback((d) async { -// if (!_rendering) { -// _rendering = true; -// await widget.viewer.requestFrame(); -// _rendering = false; -// } -// _requestFrame(); -// }); -// } - -// bool _resizing = false; -// Timer? _resizeTimer; - -// Future _resizeTexture(Size newSize) async { -// _resizeTimer?.cancel(); -// _resizeTimer = Timer(const Duration(milliseconds: 500), () async { -// if (_resizing || !mounted) { -// return; -// } -// _resizeTimer!.cancel(); -// _resizing = true; - -// if (!mounted) { -// return; -// } - -// var dpr = MediaQuery.of(context).devicePixelRatio; - -// _texture.resize(newSize.width.ceil(), newSize.height.ceil(), 0, 0, dpr); -// setState(() {}); -// _resizing = false; -// }); -// } - -// @override -// Widget build(BuildContext context) { -// if (_texture == null || _resizing) { -// return widget.initial ?? -// Container( -// color: -// kIsWeb ? const Color.fromARGB(0, 170, 129, 129) : Colors.red); -// } - -// var textureWidget = Texture( -// key: ObjectKey("texture_${_texture!.flutterId}"), -// textureId: _texture!.flutterId!, -// filterQuality: FilterQuality.none, -// freeze: false, -// ); - -// return ResizeObserver( -// onResized: _resizeTexture, -// child: Stack(children: [ -// Positioned.fill( -// child: Platform.isLinux || Platform.isWindows -// ? Transform( -// alignment: Alignment.center, -// transform: Matrix4.rotationX( -// pi), // TODO - this rotation is due to OpenGL texture coordinate working in a different space from Flutter, can we move this to the C++ side somewhere? -// child: textureWidget) -// : textureWidget) -// ])); -// } -// } diff --git a/thermion_flutter/thermion_flutter/lib/src/widgets/src/thermion_widget.dart b/thermion_flutter/thermion_flutter/lib/src/widgets/src/thermion_widget.dart index 8433028b..ae0e19dc 100644 --- a/thermion_flutter/thermion_flutter/lib/src/widgets/src/thermion_widget.dart +++ b/thermion_flutter/thermion_flutter/lib/src/widgets/src/thermion_widget.dart @@ -8,6 +8,16 @@ import 'package:thermion_flutter_web/thermion_flutter_web_options.dart'; import 'package:thermion_dart/src/viewer/src/shared_types/view.dart' as t; import 'thermion_widget_windows.dart'; +Future kDefaultResizeCallback(Size size, t.View view, double pixelRatio) async { + var camera = await view.getCamera(); + var near = await camera.getNear(); + var far = await camera.getCullingFar(); + var focalLength = await camera.getFocalLength(); + + await camera.setLensProjection(near:near, far:far, focalLength: focalLength, + aspect: size.width.toDouble() / size.height.toDouble()); +} + class ThermionWidget extends StatefulWidget { /// /// The viewer. @@ -15,14 +25,25 @@ class ThermionWidget extends StatefulWidget { final ThermionViewer viewer; /// - /// The view. + /// The [View] associated with this widget. If null, the default View will be used. /// final t.View? view; /// - /// The options to use when creating this widget. + /// A callback to invoke whenever this widget and the underlying surface are + /// resized. If a callback is not explicitly provided, the default callback + /// will be run, which changes the aspect ratio for the active camera in + /// the View managed by this widget. If you specify your own callback, + /// you probably want to preserve this behaviour (otherwise the aspect ratio) + /// will be incorrect. + /// + /// To completely disable the resize callback, pass [null]. + /// + /// IMPORTANT - size is specified in physical pixels, not logical pixels. + /// If you need to work with Flutter dimensions, divide [size] by + /// [pixelRatio]. /// - final ThermionFlutterOptions? options; + final Future Function(Size size, t.View view, double pixelRatio)? onResize; /// /// The content to render before the texture widget is available. @@ -31,7 +52,11 @@ class ThermionWidget extends StatefulWidget { final Widget? initial; const ThermionWidget( - {Key? key, this.initial, required this.viewer, this.view, this.options}) + {Key? key, + this.initial, + required this.viewer, + this.view, + this.onResize = kDefaultResizeCallback}) : super(key: key); @override @@ -66,7 +91,7 @@ class _ThermionWidgetState extends State { if (kIsWeb) { return ThermionWidgetWeb( viewer: widget.viewer, - options: widget.options as ThermionFlutterWebOptions); + options: ThermionFlutterPlugin.options as ThermionFlutterWebOptions?); } if (Platform.isWindows) { @@ -77,6 +102,7 @@ class _ThermionWidgetState extends State { key: ObjectKey(view!), initial: widget.initial, viewer: widget.viewer, - view: view!); + view: view!, + onResize: widget.onResize); } } diff --git a/thermion_flutter/thermion_flutter/lib/src/widgets/widgets.dart b/thermion_flutter/thermion_flutter/lib/src/widgets/widgets.dart index ea801eaa..083271e9 100644 --- a/thermion_flutter/thermion_flutter/lib/src/widgets/widgets.dart +++ b/thermion_flutter/thermion_flutter/lib/src/widgets/widgets.dart @@ -3,3 +3,4 @@ library; export 'src/thermion_widget.dart'; export 'src/thermion_listener_widget.dart'; export 'src/camera/camera_selector_widget.dart'; +export 'src/camera/camera_orientation_widget.dart';