diff --git a/thermion_dart/lib/src/input/src/delegate_input_handler.dart b/thermion_dart/lib/src/input/src/delegate_input_handler.dart index 72cc75c6..32b35622 100644 --- a/thermion_dart/lib/src/input/src/delegate_input_handler.dart +++ b/thermion_dart/lib/src/input/src/delegate_input_handler.dart @@ -12,8 +12,8 @@ class DelegateInputHandler implements InputHandler { Stream> get gestures => _gesturesController.stream; final _gesturesController = StreamController>.broadcast(); - Stream get cameraUpdated => _cameraUpdatedController.stream; - final _cameraUpdatedController = StreamController.broadcast(); + Stream get cameraUpdated => _cameraUpdatedController.stream; + final _cameraUpdatedController = StreamController.broadcast(); final _logger = Logger("DelegateInputHandler"); @@ -172,11 +172,13 @@ class DelegateInputHandler implements InputHandler { } } - await transformDelegate?.execute(); + var transform = await transformDelegate?.execute(); var updates = _inputDeltas.keys.followedBy(keyTypes).toList(); if (updates.isNotEmpty) { _gesturesController.add(updates); - _cameraUpdatedController.add(true); + } + if (transform != null) { + _cameraUpdatedController.add(transform); } _inputDeltas.clear(); @@ -310,4 +312,7 @@ class DelegateInputHandler implements InputHandler { throw UnimplementedError("Only pointerCount <= 2 supported"); } } + + @override + Stream get transformUpdated => cameraUpdated; } diff --git a/thermion_dart/lib/src/input/src/delegates.dart b/thermion_dart/lib/src/input/src/delegates.dart index fc1909bd..9098ea7e 100644 --- a/thermion_dart/lib/src/input/src/delegates.dart +++ b/thermion_dart/lib/src/input/src/delegates.dart @@ -4,7 +4,7 @@ import 'input_handler.dart'; abstract class InputHandlerDelegate { Future queue(InputAction action, Vector3? delta); - Future execute(); + Future execute(); } abstract class VelocityDelegate { 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 d4c671ea..c1d49256 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 @@ -14,7 +14,7 @@ class FixedOrbitRotateInputHandlerDelegate implements InputHandlerDelegate { late Future _camera; final double minimumDistance; late final Vector3 target; - + final double rotationSensitivity; final double zoomSensitivity; @@ -70,13 +70,13 @@ class FixedOrbitRotateInputHandlerDelegate implements InputHandlerDelegate { bool _executing = false; @override - Future execute() async { + Future execute() async { if (_queuedRotationDelta.length2 == 0.0 && _queuedZoomDelta == 0.0) { - return; + return null; } if (_executing) { - return; + return null; } _executing = true; @@ -97,6 +97,8 @@ class FixedOrbitRotateInputHandlerDelegate implements InputHandlerDelegate { currentPosition = Vector3(0, 0, minimumDistance); } + Matrix4? updatedModelMatrix = null; + // Zoom if (_queuedZoomDelta != 0.0) { var newPosition = currentPosition + @@ -116,6 +118,7 @@ class FixedOrbitRotateInputHandlerDelegate implements InputHandlerDelegate { newViewMatrix.invert(); await (await _camera).setModelMatrix(newViewMatrix); + updatedModelMatrix = newViewMatrix; } } else if (_queuedRotationDelta.length != 0) { double rotateX = _queuedRotationDelta.x * rotationSensitivity; @@ -134,6 +137,7 @@ class FixedOrbitRotateInputHandlerDelegate implements InputHandlerDelegate { modelMatrix = rot1 * rot2 * modelMatrix; await (await _camera).setModelMatrix(modelMatrix); + updatedModelMatrix = modelMatrix; } // Reset queued deltas @@ -141,5 +145,6 @@ class FixedOrbitRotateInputHandlerDelegate implements InputHandlerDelegate { _queuedZoomDelta = 0.0; _executing = false; + return updatedModelMatrix; } } diff --git a/thermion_dart/lib/src/input/src/implementations/free_flight_camera_delegate.dart b/thermion_dart/lib/src/input/src/implementations/free_flight_camera_delegate.dart index 73de7af1..f064fed7 100644 --- a/thermion_dart/lib/src/input/src/implementations/free_flight_camera_delegate.dart +++ b/thermion_dart/lib/src/input/src/implementations/free_flight_camera_delegate.dart @@ -61,9 +61,9 @@ class FreeFlightInputHandlerDelegate implements InputHandlerDelegate { bool _executing = false; @override - Future execute() async { + Future execute() async { if (_executing) { - return; + return null; } _executing = true; @@ -73,7 +73,7 @@ class FreeFlightInputHandlerDelegate implements InputHandlerDelegate { _queuedZoomDelta == 0.0 && _queuedMoveDelta.length2 == 0.0) { _executing = false; - return; + return null; } final activeCamera = await viewer.getActiveCamera(); @@ -128,12 +128,12 @@ class FreeFlightInputHandlerDelegate implements InputHandlerDelegate { relativeTranslation = modelMatrix.getRotation() * relativeTranslation; } - await viewer.setTransform( - await entity, - Matrix4.compose( - relativeTranslation, relativeRotation, Vector3(1, 1, 1)) * - current); + var updated = Matrix4.compose( + relativeTranslation, relativeRotation, Vector3(1, 1, 1)) * + current; + await viewer.setTransform(await entity, updated); _executing = false; + return updated; } } diff --git a/thermion_dart/lib/src/input/src/implementations/gizmo_input_handler.dart b/thermion_dart/lib/src/input/src/implementations/gizmo_input_handler.dart index e35897c1..1e455917 100644 --- a/thermion_dart/lib/src/input/src/implementations/gizmo_input_handler.dart +++ b/thermion_dart/lib/src/input/src/implementations/gizmo_input_handler.dart @@ -4,27 +4,35 @@ import 'package:thermion_dart/thermion_dart.dart'; import 'package:vector_math/vector_math_64.dart'; class _Gizmo { - bool isVisible = false; final ThermionViewer viewer; + final GizmoAsset _gizmo; - ThermionEntity? _attachedTo; - final attachedTo = StreamController.broadcast(); + final transformUpdates = StreamController<({Matrix4 transform})>.broadcast(); - final GizmoType _gizmoType; + Axis? _active; + final GizmoType type; - _Gizmo(this._gizmo, this.viewer, this._gizmoType); + _Gizmo(this._gizmo, this.viewer, this.type); static Future<_Gizmo> forType(ThermionViewer viewer, GizmoType type) async { final view = await viewer.getViewAt(0); return _Gizmo(await viewer.createGizmo(view, type), viewer, type); } - final _onEntityTransformUpdated = StreamController< - ({ThermionEntity entity, Matrix4 transform})>.broadcast(); + Future dispose() async { + await transformUpdates.close(); + await viewer.removeAsset(_gizmo); + } - Axis? _active; - Axis? get active => _active; + Future hide() async { + await _gizmo.removeFromScene(); + } + + Future reveal() async { + await _gizmo.addToScene(); + gizmoTransform = await viewer.getWorldTransform(_gizmo.entity); + } double _getAngleBetweenVectors(Vector2 v1, Vector2 v2) { // Normalize vectors to ensure consistent rotation regardless of distance from center @@ -61,49 +69,16 @@ class _Gizmo { Matrix4? gizmoTransform; - Future attach(ThermionEntity entity) async { - print("Attached to ${entity}"); - - if (_attachedTo != null && entity != _attachedTo) { - await viewer.setParent(_attachedTo!, 0); - } - - _attachedTo = entity; - attachedTo.add(_attachedTo); - - await viewer.setParent(_gizmo.entity, entity); - await viewer.setTransform(_gizmo.entity, Matrix4.identity()); - - if (!isVisible) { - await _gizmo.addToScene(); - isVisible = true; - } - gizmoTransform = await viewer.getWorldTransform(entity); - } - - Future detach() async { - await _gizmo.removeFromScene(); - if (_attachedTo != null) { - await viewer.setParent(_attachedTo!, 0); - } - attachedTo.add(null); - _active = null; - isVisible = false; - } - void _updateTransform(Vector2 currentPosition, Vector2 delta) async { - if (_attachedTo == null) { - return; - } - - if (_gizmoType == GizmoType.translation) { + if (type == GizmoType.translation) { await _updateTranslation(currentPosition, delta); - } else if (_gizmoType == GizmoType.rotation) { + } else if (type == GizmoType.rotation) { await _updateRotation(currentPosition, delta); } - _onEntityTransformUpdated - .add((entity: _attachedTo!, transform: gizmoTransform!)); + await viewer.setTransform(_gizmo.entity, gizmoTransform!); + + transformUpdates.add((transform: gizmoTransform!)); } Future _updateTranslation( @@ -143,8 +118,6 @@ class _Gizmo { gizmoTransform! .setTranslation(gizmoTransform!.getTranslation() + worldSpaceDelta); - - await viewer.setTransform(_attachedTo!, gizmoTransform!); } Future _updateRotation(Vector2 currentPosition, Vector2 delta) async { @@ -210,7 +183,6 @@ class _Gizmo { // Apply rotation to the current transform gizmoTransform = gizmoTransform! * rotationMatrix; - await viewer.setTransform(_attachedTo!, gizmoTransform!); } } @@ -221,56 +193,60 @@ class GizmoInputHandler extends InputHandler { late final _gizmos = {}; - _Gizmo? active; + _Gizmo? _active; - StreamSubscription? _entityTransformUpdatedListener; - StreamSubscription? _attachedToListener; + ThermionEntity? _attached; - final _attachedTo = StreamController.broadcast(); - Stream get attachedTo => _attachedTo.stream; - - GizmoType? getGizmoType() { - return active?._gizmoType; + Future attach(ThermionEntity entity) async { + if (_attached != null) { + await detach(); + } + _attached = entity; + if (_active != null) { + await viewer.setParent(_attached!, _active!._gizmo.entity); + await _active!.reveal(); + } } - Future setGizmoType(GizmoType? type) async { - if (type == null) { - await active?.detach(); - active = null; + Future detach() async { + if (_attached == null) { return; } - - var target = _gizmos[type]!; - if (target != active) { - await _entityTransformUpdatedListener?.cancel(); - await _attachedToListener?.cancel(); - if (active?._attachedTo != null) { - var attachedTo = active!._attachedTo!; - await active!.detach(); - await target.attach(attachedTo); - } - active = target; - _entityTransformUpdatedListener = - active!._onEntityTransformUpdated.stream.listen((event) { - _transformUpdatedController.add(event); - }); - - _attachedToListener = active!.attachedTo.stream.listen((entity) { - _attachedTo.add(entity); - }); - } + await viewer.setParent(_attached!, 0); + await _active?.hide(); + _attached = null; } - final _transformUpdatedController = - StreamController<({ThermionEntity entity, Matrix4 transform})>(); - Stream<({ThermionEntity entity, Matrix4 transform})> - get onEntityTransformUpdated => _transformUpdatedController.stream; + final _initialized = Completer(); + + final _transformController = StreamController.broadcast(); + Stream get transformUpdated => _transformController.stream; + + final _pickResultController = StreamController.broadcast(); + Stream get onPickResult => _pickResultController.stream; GizmoInputHandler({required this.wrapped, required this.viewer}) { initialize(); } - final _initialized = Completer(); + GizmoType? getGizmoType() { + return _active?.type; + } + + Future setGizmoType(GizmoType? type) async { + if (type == null) { + await detach(); + _active?.hide(); + _active = null; + } else { + _active?.hide(); + _active = _gizmos[type]!; + _active!.reveal(); + if (_attached != null) { + await attach(_attached!); + } + } + } Future initialize() async { if (_initialized.isCompleted) { @@ -283,16 +259,19 @@ class GizmoInputHandler extends InputHandler { _gizmos[GizmoType.rotation] = await _Gizmo.forType(viewer, GizmoType.rotation); await setGizmoType(GizmoType.translation); + for (final gizmo in _gizmos.values) { + gizmo.transformUpdates.stream.listen((update) { + _transformController.add(update.transform); + }); + } _initialized.complete(true); } - @override - Stream get cameraUpdated => throw UnimplementedError(); - @override Future dispose() async { - await viewer.removeEntity(_gizmos[GizmoType.rotation]!._gizmo); - await viewer.removeEntity(_gizmos[GizmoType.translation]!._gizmo); + _gizmos[GizmoType.rotation]!.dispose(); + _gizmos[GizmoType.translation]!.dispose(); + _gizmos.clear(); } @override @@ -328,13 +307,13 @@ class GizmoInputHandler extends InputHandler { await viewer.pick(localPosition.x.toInt(), localPosition.y.toInt(), (result) async { - if (active?._gizmo.isNonPickable(result.entity) == true || + if (_active?._gizmo.isNonPickable(result.entity) == true || result.entity == FILAMENT_ENTITY_NULL) { - await active!.detach(); + _pickResultController.add(null); return; } - if (active?._gizmo.isGizmoEntity(result.entity) != true) { - active!.attach(result.entity); + if (_active?._gizmo.isGizmoEntity(result.entity) != true) { + _pickResultController.add(result.entity); } }); } @@ -344,18 +323,18 @@ class GizmoInputHandler extends InputHandler { if (!_initialized.isCompleted) { return; } - active?.checkHover(localPosition.x.floor(), localPosition.y.floor()); + _active?.checkHover(localPosition.x.floor(), localPosition.y.floor()); } @override Future onPointerMove( Vector2 localPosition, Vector2 delta, bool isMiddle) async { - if (!isMiddle && active?._active != null) { + if (!isMiddle && _active?._active != null) { final scaledDelta = Vector2( delta.x, delta.y, ); - active!._updateTransform(localPosition, scaledDelta); + _active!._updateTransform(localPosition, scaledDelta); return; } return wrapped.onPointerMove(localPosition, delta, isMiddle); @@ -401,18 +380,4 @@ class GizmoInputHandler extends InputHandler { void setActionForType(InputType gestureType, InputAction gestureAction) { throw UnimplementedError(); } - - Future detach(ThermionAsset asset) async { - if (active?._attachedTo == asset.entity) { - await active!.detach(); - return; - } - final childEntities = await asset.getChildEntities(); - for (final childEntity in childEntities) { - if (active?._attachedTo == childEntity) { - await active!.detach(); - return; - } - } - } } diff --git a/thermion_dart/lib/src/input/src/implementations/picking_camera_gesture_handler.dart b/thermion_dart/lib/src/input/src/implementations/picking_camera_gesture_handler.dart index 5a4f88e5..fb99708e 100644 --- a/thermion_dart/lib/src/input/src/implementations/picking_camera_gesture_handler.dart +++ b/thermion_dart/lib/src/input/src/implementations/picking_camera_gesture_handler.dart @@ -23,7 +23,7 @@ // } // Future dispose() async { -// await viewer.removeEntity(_translationGizmo); +// await viewer.removeAsset(_translationGizmo); // } // @override diff --git a/thermion_dart/lib/src/input/src/implementations/third_person_camera_delegate.dart b/thermion_dart/lib/src/input/src/implementations/third_person_camera_delegate.dart index eaa73807..8e2ebb4a 100644 --- a/thermion_dart/lib/src/input/src/implementations/third_person_camera_delegate.dart +++ b/thermion_dart/lib/src/input/src/implementations/third_person_camera_delegate.dart @@ -65,9 +65,9 @@ class OverTheShoulderCameraDelegate implements InputHandlerDelegate { static bool get executing => _executing; @override - Future execute() async { + Future execute() async { if (_executing) { - return; + return null; } _executing = true; @@ -76,7 +76,7 @@ class OverTheShoulderCameraDelegate implements InputHandlerDelegate { _queuedZoomDelta == 0.0 && _queuedMoveDelta.length2 == 0.0) { _executing = false; - return; + return null; } Matrix4 currentPlayerTransform = await viewer.getWorldTransform(player); @@ -97,10 +97,8 @@ class OverTheShoulderCameraDelegate implements InputHandlerDelegate { // camera is always looking at -Z, whereas models generally face towards +Z if (_queuedRotationDelta.length2 > 0.0) { - double deltaX = - _queuedRotationDelta.x * rotationSensitivity; - double deltaY = - _queuedRotationDelta.y * rotationSensitivity; + double deltaX = _queuedRotationDelta.x * rotationSensitivity; + double deltaY = _queuedRotationDelta.y * rotationSensitivity; cameraLookAt = Matrix4.rotationY(-deltaX) * Matrix4.rotationX(-deltaY) * @@ -118,5 +116,6 @@ class OverTheShoulderCameraDelegate implements InputHandlerDelegate { [camera.getEntity(), player], [newCameraTransform, newPlayerTransform]); onUpdate?.call(newPlayerTransform); _executing = false; + return newCameraTransform; } } diff --git a/thermion_dart/lib/src/input/src/input_handler.dart b/thermion_dart/lib/src/input/src/input_handler.dart index bac17a34..7b8afe0c 100644 --- a/thermion_dart/lib/src/input/src/input_handler.dart +++ b/thermion_dart/lib/src/input/src/input_handler.dart @@ -1,6 +1,4 @@ import 'dart:async'; - -import 'package:thermion_dart/thermion_dart.dart'; import 'package:vector_math/vector_math_64.dart'; enum InputType { @@ -30,7 +28,11 @@ enum PhysicalKey { W, A, S, D } enum InputAction { TRANSLATE, ROTATE, PICK, ZOOM, NONE } abstract class InputHandler { - Stream get cameraUpdated; + + @Deprecated("Use @transformUpdated instead") + Stream get cameraUpdated => transformUpdated; + + Stream get transformUpdated; Future onPointerHover(Vector2 localPosition, Vector2 delta); Future onPointerScroll(Vector2 localPosition, double scrollDelta); @@ -59,5 +61,4 @@ abstract class InputHandler { void keyDown(PhysicalKey key); void keyUp(PhysicalKey key); - // Future setCamera(Camera camera); }