refactoring

This commit is contained in:
Nick Fisher
2025-03-19 15:42:11 +08:00
parent 627447f8b0
commit 102429e090
20 changed files with 508 additions and 590 deletions

View File

@@ -3,17 +3,27 @@ library;
import 'package:animation_tools_dart/animation_tools_dart.dart'; import 'package:animation_tools_dart/animation_tools_dart.dart';
import 'package:thermion_dart/src/filament/src/layers.dart'; import 'package:thermion_dart/src/filament/src/layers.dart';
import 'package:thermion_dart/thermion_dart.dart'; import 'package:thermion_dart/thermion_dart.dart';
import 'package:vector_math/vector_math_64.dart';
import 'entity.dart';
export 'geometry.dart'; export 'geometry.dart';
export 'gltf.dart'; export 'gltf.dart';
export 'light_options.dart'; ///
/// Represents a renderable object (i.e. not cameras or lights).
///
/// At a low level, Filament works with entities. In practice,
/// it can be difficult to work directly with these at a higher level
/// because:
/// a) certain objects don't map exactly to entities (e.g. glTF assets, which
/// are represented by a hierarchy of entities).
/// b) it is not trivial to create instances directly from entities
///
/// [ThermionAsset] is intended to provide a unified high-level interface
/// for working with renderable objects.
///
///
abstract class ThermionAsset { abstract class ThermionAsset {
/// ///
/// /// The top-most entity in the hierarchy. If this is a glTF asset
/// ///
ThermionEntity get entity; ThermionEntity get entity;
@@ -22,6 +32,11 @@ abstract class ThermionAsset {
/// ///
Future<List<ThermionEntity>> getChildEntities(); Future<List<ThermionEntity>> getChildEntities();
///
///
///
Future<ThermionEntity?> getChildEntity(String childName);
/// ///
/// ///
/// ///
@@ -95,7 +110,7 @@ abstract class ThermionAsset {
/// ///
/// Schedules the glTF animation at [index] in [asset] to start playing on the next frame. /// Schedules the glTF animation at [index] in [asset] to start playing on the next frame.
/// ///
Future playAnimation(ThermionAsset asset, int index, Future playAnimation(int index,
{bool loop = false, {bool loop = false,
bool reverse = false, bool reverse = false,
bool replaceActive = true, bool replaceActive = true,
@@ -105,7 +120,7 @@ abstract class ThermionAsset {
/// ///
/// Schedules the glTF animation at [index] in [entity] to start playing on the next frame. /// Schedules the glTF animation at [index] in [entity] to start playing on the next frame.
/// ///
Future playAnimationByName(covariant ThermionAsset asset, String name, Future playAnimationByName(String name,
{bool loop = false, {bool loop = false,
bool reverse = false, bool reverse = false,
bool replaceActive = true, bool replaceActive = true,
@@ -114,20 +129,19 @@ abstract class ThermionAsset {
/// ///
/// ///
/// ///
Future setGltfAnimationFrame( Future setGltfAnimationFrame(int index, int animationFrame);
covariant ThermionAsset asset, int index, int animationFrame);
/// ///
/// ///
/// ///
Future stopAnimation(covariant ThermionAsset asset, int animationIndex); Future stopAnimation(int animationIndex);
/// ///
/// ///
/// ///
Future stopAnimationByName(covariant ThermionAsset asset, String name); Future stopAnimationByName(String name);
/// ///
/// Set the weights for all morph targets in [entity] to [weights]. /// 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). /// 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). /// 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).
@@ -137,27 +151,24 @@ abstract class ThermionAsset {
Future setMorphTargetWeights(ThermionEntity entity, List<double> weights); Future setMorphTargetWeights(ThermionEntity entity, List<double> weights);
/// ///
/// Gets the names of all morph targets for the child renderable [childEntity] under [entity]. /// Gets the names of all morph targets for [entity] (which must be a renderable entity)
/// ///
Future<List<String>> getMorphTargetNames( Future<List<String>> getMorphTargetNames({ThermionEntity? entity});
covariant ThermionAsset asset, ThermionEntity childEntity);
/// ///
/// Gets the names of all bones for the armature at [skinIndex] under the specified [entity]. /// Gets the names of all bones for the skin at [skinIndex].
/// ///
Future<List<String>> getBoneNames(covariant ThermionAsset asset, Future<List<String>> getBoneNames({int skinIndex = 0});
{int skinIndex = 0});
/// ///
/// Gets the names of all glTF animations embedded in the specified entity. /// Gets the names of all glTF animations embedded in the specified entity.
/// ///
Future<List<String>> getAnimationNames(covariant ThermionAsset asset); Future<List<String>> getAnimationNames();
/// ///
/// Returns the length (in seconds) of the animation at the given index. /// Returns the length (in seconds) of the animation at the given index.
/// ///
Future<double> getAnimationDuration( Future<double> getAnimationDuration(int animationIndex);
covariant ThermionAsset asset, int animationIndex);
/// ///
/// Construct animation(s) for every entity under [asset]. If [targetMeshNames] is provided, only entities with matching names will be animated. /// Construct animation(s) for every entity under [asset]. If [targetMeshNames] is provided, only entities with matching names will be animated.
@@ -166,8 +177,7 @@ abstract class ThermionAsset {
/// throwing an exception if any cannot be found. /// 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. /// It is permissible for [animation] to omit any targets that do exist under [meshName]; these simply won't be animated.
/// ///
Future setMorphAnimationData( Future setMorphAnimationData(MorphAnimationData animation,
covariant ThermionAsset asset, MorphAnimationData animation,
{List<String>? targetMeshNames}); {List<String>? targetMeshNames});
/// ///
@@ -179,7 +189,7 @@ abstract class ThermionAsset {
/// Resets all bones in the given entity to their rest pose. /// Resets all bones in the given entity to their rest pose.
/// This should be done before every call to addBoneAnimation. /// This should be done before every call to addBoneAnimation.
/// ///
Future resetBones(ThermionAsset asset); Future resetBones();
/// ///
/// Enqueues and plays the [animation] for the specified bone(s). /// Enqueues and plays the [animation] for the specified bone(s).
@@ -199,7 +209,7 @@ abstract class ThermionAsset {
/// This will be applied in reverse after [fadeOutInSecs]. /// This will be applied in reverse after [fadeOutInSecs].
/// ///
/// ///
Future addBoneAnimation(ThermionAsset asset, BoneAnimationData animation, Future addBoneAnimation(BoneAnimationData animation,
{int skinIndex = 0, {int skinIndex = 0,
double fadeInInSecs = 0.0, double fadeInInSecs = 0.0,
double fadeOutInSecs = 0.0, double fadeOutInSecs = 0.0,
@@ -209,32 +219,29 @@ abstract class ThermionAsset {
/// Gets the entity representing the bone at [boneIndex]/[skinIndex]. /// Gets the entity representing the bone at [boneIndex]/[skinIndex].
/// The returned entity is only intended for use with [getWorldTransform]. /// The returned entity is only intended for use with [getWorldTransform].
/// ///
Future<ThermionEntity> getBone(covariant ThermionAsset asset, int boneIndex, Future<ThermionEntity> getBone(int boneIndex, {int skinIndex = 0});
{int skinIndex = 0});
/// ///
/// Gets the local (relative to parent) transform for [entity]. /// Gets the local (relative to parent) transform for [entity].
/// ///
Future<Matrix4> getLocalTransform(ThermionEntity entity); Future<Matrix4> getLocalTransform({ThermionEntity? entity});
/// ///
/// Gets the world transform for [entity]. /// Gets the world transform for [entity].
/// ///
Future<Matrix4> getWorldTransform(ThermionEntity entity); Future<Matrix4> getWorldTransform({ThermionEntity? entity});
/// ///
/// Gets the inverse bind (pose) matrix for the bone. /// 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). /// 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. /// This is because all joint information is internally stored with the parent entity.
/// ///
Future<Matrix4> getInverseBindMatrix( Future<Matrix4> getInverseBindMatrix(int boneIndex, {int skinIndex = 0});
covariant ThermionAsset asset, int boneIndex,
{int skinIndex = 0});
/// ///
/// Sets the transform (relative to its parent) for [entity]. /// Sets the transform (relative to its parent) for [entity].
/// ///
Future setTransform(ThermionEntity entity, Matrix4 transform); Future setTransform(Matrix4 transform, {ThermionEntity? entity});
/// ///
/// Updates the bone matrices for [entity] (which must be the ThermionEntity /// Updates the bone matrices for [entity] (which must be the ThermionEntity
@@ -264,4 +271,3 @@ abstract class ThermionAsset {
/// ///
Future removeAnimationComponent(ThermionEntity entity); Future removeAnimationComponent(ThermionEntity entity);
} }

View File

@@ -1,5 +1,4 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:thermion_dart/src/filament/src/engine.dart'; import 'package:thermion_dart/src/filament/src/engine.dart';
import 'package:thermion_dart/thermion_dart.dart'; import 'package:thermion_dart/thermion_dart.dart';
@@ -29,7 +28,6 @@ class FilamentConfig<T, U> {
} }
abstract class FilamentApp<T> { abstract class FilamentApp<T> {
static FilamentApp? instance; static FilamentApp? instance;
final T engine; final T engine;
@@ -170,13 +168,6 @@ abstract class FilamentApp<T> {
Future<MaterialInstance> getMaterialInstanceAt( Future<MaterialInstance> getMaterialInstanceAt(
ThermionEntity entity, int index); ThermionEntity entity, int index);
///
///
///
@override
Future<ThermionAsset> createGeometry(Geometry geometry,
{List<MaterialInstance>? materialInstances, bool keepData = false});
/// ///
/// ///
/// ///
@@ -206,4 +197,27 @@ abstract class FilamentApp<T> {
/// ///
/// ///
Future unregisterRequestFrameHook(Future Function() hook); Future unregisterRequestFrameHook(Future Function() hook);
///
/// Retrieves the name assigned to the given entity (usually corresponds to the glTF mesh name).
///
String? getNameForEntity(ThermionEntity entity);
///
/// Gets the parent entity of [entity]. Returns null if the entity has no parent.
///
Future<ThermionEntity?> getParent(ThermionEntity entity);
///
/// Gets the ancestor (ultimate parent) entity of [entity]. Returns null if the entity has no parent.
///
Future<ThermionEntity?> getAncestor(ThermionEntity entity);
///
/// Sets the parent transform of [child] to [parent].
///
Future setParent(ThermionEntity child, ThermionEntity? parent,
{bool preserveScaling});
Future<MaterialInstance> createImageMaterialInstance();
} }

View File

@@ -1,4 +1,5 @@
import 'package:thermion_dart/src/filament/src/layers.dart'; import 'package:thermion_dart/src/filament/src/layers.dart';
import 'package:thermion_dart/src/filament/src/scene.dart';
import 'package:thermion_dart/thermion_dart.dart'; import 'package:thermion_dart/thermion_dart.dart';
/// ///
@@ -18,6 +19,7 @@ class Viewport {
enum QualityLevel { LOW, MEDIUM, HIGH, ULTRA } enum QualityLevel { LOW, MEDIUM, HIGH, ULTRA }
abstract class View { abstract class View {
Future<Scene> getScene();
Future<Viewport> getViewport(); Future<Viewport> getViewport();
Future setViewport(int width, int height); Future setViewport(int width, int height);
Future<RenderTarget?> getRenderTarget(); Future<RenderTarget?> getRenderTarget();

View File

@@ -1,8 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:thermion_dart/thermion_dart.dart'; import 'package:thermion_dart/thermion_dart.dart';
import 'package:vector_math/vector_math_64.dart';
import 'implementations/fixed_orbit_camera_rotation_delegate.dart'; import 'implementations/fixed_orbit_camera_rotation_delegate.dart';
import 'implementations/free_flight_camera_delegate.dart'; import 'implementations/free_flight_camera_delegate.dart';
@@ -64,7 +62,7 @@ class DelegateInputHandler implements InputHandler {
_inputDeltas[gestureType] = Vector3.zero(); _inputDeltas[gestureType] = Vector3.zero();
} }
viewer.registerRequestFrameHook(process); FilamentApp.instance!.registerRequestFrameHook(process);
} }
factory DelegateInputHandler.fixedOrbit(ThermionViewer viewer, factory DelegateInputHandler.fixedOrbit(ThermionViewer viewer,
@@ -75,7 +73,7 @@ class DelegateInputHandler implements InputHandler {
DelegateInputHandler( DelegateInputHandler(
viewer: viewer, viewer: viewer,
pickDelegate: pickDelegate, pickDelegate: pickDelegate,
transformDelegate: FixedOrbitRotateInputHandlerDelegate(viewer, transformDelegate: FixedOrbitRotateInputHandlerDelegate(viewer.view,
minimumDistance: minimumDistance), minimumDistance: minimumDistance),
actions: { actions: {
InputType.MMB_HOLD_AND_MOVE: InputAction.ROTATE, InputType.MMB_HOLD_AND_MOVE: InputAction.ROTATE,
@@ -96,9 +94,8 @@ class DelegateInputHandler implements InputHandler {
DelegateInputHandler( DelegateInputHandler(
viewer: viewer, viewer: viewer,
pickDelegate: pickDelegate, pickDelegate: pickDelegate,
transformDelegate: FreeFlightInputHandlerDelegate(viewer, transformDelegate: FreeFlightInputHandlerDelegate(viewer.view,
clampY: clampY, clampY: clampY,
entity: entity,
rotationSensitivity: rotateSensitivity, rotationSensitivity: rotateSensitivity,
zoomSensitivity: zoomSensitivity, zoomSensitivity: zoomSensitivity,
panSensitivity: panSensitivity, panSensitivity: panSensitivity,
@@ -245,7 +242,7 @@ class DelegateInputHandler implements InputHandler {
@override @override
Future dispose() async { Future dispose() async {
viewer.unregisterRequestFrameHook(process); FilamentApp.instance!.unregisterRequestFrameHook(process);
} }
@override @override

View File

@@ -10,8 +10,7 @@ import '../input_handler.dart';
/// point. /// point.
/// ///
class FixedOrbitRotateInputHandlerDelegate implements InputHandlerDelegate { class FixedOrbitRotateInputHandlerDelegate implements InputHandlerDelegate {
final ThermionViewer viewer; final View view;
late Future<Camera> _camera;
final double minimumDistance; final double minimumDistance;
late final Vector3 target; late final Vector3 target;
@@ -24,20 +23,17 @@ class FixedOrbitRotateInputHandlerDelegate implements InputHandlerDelegate {
Timer? _updateTimer; Timer? _updateTimer;
FixedOrbitRotateInputHandlerDelegate( FixedOrbitRotateInputHandlerDelegate(
this.viewer, { this.view, {
Vector3? target, Vector3? target,
this.minimumDistance = 10.0, this.minimumDistance = 10.0,
this.rotationSensitivity = 0.01, this.rotationSensitivity = 0.01,
this.zoomSensitivity = 0.1, this.zoomSensitivity = 0.1,
}) { }) {
this.target = target ?? Vector3.zero(); this.target = target ?? Vector3.zero();
_camera = viewer.getMainCamera().then((Camera cam) async {
var viewMatrix = makeViewMatrix(Vector3(0.0, 0, -minimumDistance),
this.target, Vector3(0.0, 1.0, 0.0));
viewMatrix.invert();
await cam.setTransform(viewMatrix); view.getCamera().then((camera) {
return cam; camera.lookAt(Vector3(0.0, 0, -minimumDistance),
focus: this.target, up: Vector3(0.0, 1.0, 0.0));
}); });
} }
@@ -81,12 +77,13 @@ class FixedOrbitRotateInputHandlerDelegate implements InputHandlerDelegate {
_executing = true; _executing = true;
final view = await viewer.getViewAt(0); final camera = await view.getCamera();
final viewport = await view.getViewport(); final viewport = await view.getViewport();
var viewMatrix = await viewer.getCameraViewMatrix(); var viewMatrix = await camera.getViewMatrix();
var modelMatrix = await viewer.getCameraModelMatrix(); var modelMatrix = await camera.getModelMatrix();
var projectionMatrix = await viewer.getCameraProjectionMatrix(); var projectionMatrix = await camera.getProjectionMatrix();
var inverseProjectionMatrix = projectionMatrix.clone()..invert(); var inverseProjectionMatrix = projectionMatrix.clone()..invert();
Vector3 currentPosition = modelMatrix.getTranslation(); Vector3 currentPosition = modelMatrix.getTranslation();
@@ -117,14 +114,14 @@ class FixedOrbitRotateInputHandlerDelegate implements InputHandlerDelegate {
Matrix4 newViewMatrix = makeViewMatrix(currentPosition, target, up); Matrix4 newViewMatrix = makeViewMatrix(currentPosition, target, up);
newViewMatrix.invert(); newViewMatrix.invert();
await (await _camera).setModelMatrix(newViewMatrix); await camera.setModelMatrix(newViewMatrix);
updatedModelMatrix = newViewMatrix; updatedModelMatrix = newViewMatrix;
} }
} else if (_queuedRotationDelta.length != 0) { } else if (_queuedRotationDelta.length != 0) {
double rotateX = _queuedRotationDelta.x * rotationSensitivity; double rotateX = _queuedRotationDelta.x * rotationSensitivity;
double rotateY = _queuedRotationDelta.y * rotationSensitivity; double rotateY = _queuedRotationDelta.y * rotationSensitivity;
var modelMatrix = await viewer.getCameraModelMatrix(); var modelMatrix = await camera.getModelMatrix();
// for simplicity, we always assume a fixed coordinate system where // for simplicity, we always assume a fixed coordinate system where
// we are rotating around world Y and camera X // we are rotating around world Y and camera X
@@ -136,7 +133,7 @@ class FixedOrbitRotateInputHandlerDelegate implements InputHandlerDelegate {
.asRotationMatrix()); .asRotationMatrix());
modelMatrix = rot1 * rot2 * modelMatrix; modelMatrix = rot1 * rot2 * modelMatrix;
await (await _camera).setModelMatrix(modelMatrix); await camera.setModelMatrix(modelMatrix);
updatedModelMatrix = modelMatrix; updatedModelMatrix = modelMatrix;
} }

View File

@@ -5,8 +5,8 @@ import '../delegates.dart';
import '../input_handler.dart'; import '../input_handler.dart';
class FreeFlightInputHandlerDelegate implements InputHandlerDelegate { class FreeFlightInputHandlerDelegate implements InputHandlerDelegate {
final ThermionViewer viewer; final View view;
late Future<ThermionEntity> entity;
final Vector3? minBounds; final Vector3? minBounds;
final Vector3? maxBounds; final Vector3? maxBounds;
final double rotationSensitivity; final double rotationSensitivity;
@@ -20,21 +20,14 @@ class FreeFlightInputHandlerDelegate implements InputHandlerDelegate {
double _queuedZoomDelta = 0.0; double _queuedZoomDelta = 0.0;
Vector3 _queuedMoveDelta = Vector3.zero(); Vector3 _queuedMoveDelta = Vector3.zero();
FreeFlightInputHandlerDelegate(this.viewer, FreeFlightInputHandlerDelegate(this.view,
{this.minBounds, {this.minBounds,
this.maxBounds, this.maxBounds,
this.rotationSensitivity = 0.001, this.rotationSensitivity = 0.001,
this.movementSensitivity = 0.1, this.movementSensitivity = 0.1,
this.zoomSensitivity = 0.1, this.zoomSensitivity = 0.1,
this.panSensitivity = 0.1, this.panSensitivity = 0.1,
this.clampY, this.clampY}) {}
ThermionEntity? entity}) {
if (entity != null) {
this.entity = Future.value(entity);
} else {
this.entity = viewer.getMainCameraEntity();
}
}
@override @override
Future<void> queue(InputAction action, Vector3? delta) async { Future<void> queue(InputAction action, Vector3? delta) async {
@@ -76,9 +69,9 @@ class FreeFlightInputHandlerDelegate implements InputHandlerDelegate {
return null; return null;
} }
final activeCamera = await viewer.getActiveCamera(); final activeCamera = await view.getCamera();
Matrix4 current = await viewer.getLocalTransform(await entity); Matrix4 current = await activeCamera.getModelMatrix();
Vector3 relativeTranslation = Vector3.zero(); Vector3 relativeTranslation = Vector3.zero();
Quaternion relativeRotation = Quaternion.identity(); Quaternion relativeRotation = Quaternion.identity();
@@ -121,17 +114,18 @@ class FreeFlightInputHandlerDelegate implements InputHandlerDelegate {
_queuedMoveDelta = Vector3.zero(); _queuedMoveDelta = Vector3.zero();
} }
// If the managed entity is not the active camera, we need to apply the rotation from the current camera model matrix // // If the managed entity is not the active camera, we need to apply the rotation from the current camera model matrix
// to the entity's translation // // to the entity's translation
if (await entity != activeCamera.getEntity()) { // if (await entity != activeCamera.getEntity()) {
Matrix4 modelMatrix = await activeCamera.getModelMatrix(); // Matrix4 modelMatrix = await activeCamera.getModelMatrix();
relativeTranslation = modelMatrix.getRotation() * relativeTranslation; // relativeTranslation = modelMatrix.getRotation() * relativeTranslation;
} // }
var updated = Matrix4.compose( var updated = Matrix4.compose(
relativeTranslation, relativeRotation, Vector3(1, 1, 1)) * relativeTranslation, relativeRotation, Vector3(1, 1, 1)) *
current; current;
await viewer.setTransform(await entity, updated);
await activeCamera.setModelMatrix(updated);
_executing = false; _executing = false;
return updated; return updated;

View File

@@ -1,7 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:math'; import 'dart:math';
import 'package:thermion_dart/thermion_dart.dart'; import 'package:thermion_dart/thermion_dart.dart';
import 'package:vector_math/vector_math_64.dart';
class _Gizmo { class _Gizmo {
final ThermionViewer viewer; final ThermionViewer viewer;
@@ -16,7 +15,7 @@ class _Gizmo {
_Gizmo(this._gizmo, this.viewer, this.type); _Gizmo(this._gizmo, this.viewer, this.type);
static Future<_Gizmo> forType(ThermionViewer viewer, GizmoType type) async { static Future<_Gizmo> forType(ThermionViewer viewer, GizmoType type) async {
final view = await viewer.getViewAt(0); final view = await viewer.view;
return _Gizmo(await viewer.createGizmo(view, type), viewer, type); return _Gizmo(await viewer.createGizmo(view, type), viewer, type);
} }
@@ -26,12 +25,14 @@ class _Gizmo {
} }
Future hide() async { Future hide() async {
await _gizmo.removeFromScene(); final scene = await viewer.view.getScene();
await scene.remove(_gizmo);
} }
Future reveal() async { Future reveal() async {
await _gizmo.addToScene(); final scene = await viewer.view.getScene();
gizmoTransform = await viewer.getWorldTransform(_gizmo.entity); await scene.add(_gizmo);
gizmoTransform = await _gizmo.getWorldTransform();
} }
double _getAngleBetweenVectors(Vector2 v1, Vector2 v2) { double _getAngleBetweenVectors(Vector2 v1, Vector2 v2) {
@@ -76,17 +77,17 @@ class _Gizmo {
await _updateRotation(currentPosition, delta); await _updateRotation(currentPosition, delta);
} }
await viewer.setTransform(_gizmo.entity, gizmoTransform!); await _gizmo.setTransform(gizmoTransform!);
transformUpdates.add((transform: gizmoTransform!)); transformUpdates.add((transform: gizmoTransform!));
} }
Future<void>? _updateTranslation( Future<void>? _updateTranslation(
Vector2 currentPosition, Vector2 delta) async { Vector2 currentPosition, Vector2 delta) async {
var view = await viewer.getViewAt(0); var view = await viewer.view;
var camera = await viewer.getActiveCamera(); var camera = await viewer.getActiveCamera();
var viewport = await view.getViewport(); var viewport = await view.getViewport();
var projectionMatrix = await viewer.getCameraProjectionMatrix(); var projectionMatrix = await camera.getProjectionMatrix();
var viewMatrix = await camera.getViewMatrix(); var viewMatrix = await camera.getViewMatrix();
var inverseViewMatrix = await camera.getModelMatrix(); var inverseViewMatrix = await camera.getModelMatrix();
var inverseProjectionMatrix = projectionMatrix.clone()..invert(); var inverseProjectionMatrix = projectionMatrix.clone()..invert();
@@ -121,10 +122,9 @@ class _Gizmo {
} }
Future<void>? _updateRotation(Vector2 currentPosition, Vector2 delta) async { Future<void>? _updateRotation(Vector2 currentPosition, Vector2 delta) async {
var view = await viewer.getViewAt(0); var camera = await viewer.view.getCamera();
var camera = await viewer.getActiveCamera(); var viewport = await viewer.view.getViewport();
var viewport = await view.getViewport(); var projectionMatrix = await camera.getProjectionMatrix();
var projectionMatrix = await viewer.getCameraProjectionMatrix();
var viewMatrix = await camera.getViewMatrix(); var viewMatrix = await camera.getViewMatrix();
// Get gizmo center in screen space // Get gizmo center in screen space
@@ -187,7 +187,6 @@ class _Gizmo {
} }
class GizmoInputHandler extends InputHandler { class GizmoInputHandler extends InputHandler {
final ThermionViewer viewer; final ThermionViewer viewer;
late final _gizmos = <GizmoType, _Gizmo>{}; late final _gizmos = <GizmoType, _Gizmo>{};
@@ -202,7 +201,7 @@ class GizmoInputHandler extends InputHandler {
} }
_attached = entity; _attached = entity;
if (_active != null) { if (_active != null) {
await viewer.setParent(_attached!, _active!._gizmo.entity); await FilamentApp.instance!.setParent(_attached!, _active!._gizmo.entity);
await _active!.reveal(); await _active!.reveal();
} }
} }
@@ -215,7 +214,7 @@ class GizmoInputHandler extends InputHandler {
if (_attached == null) { if (_attached == null) {
return; return;
} }
await viewer.setParent(_attached!, 0); await FilamentApp.instance!.setParent(_attached!, null);
await _active?.hide(); await _active?.hide();
_attached = null; _attached = null;
} }

View File

@@ -1,43 +0,0 @@
import 'dart:typed_data';
import 'package:vector_math/vector_math_64.dart';
import '../../viewer/viewer.dart';
class AxisWidget {
final ThermionViewer _viewer;
final ThermionEntity xAxis;
final ThermionEntity yAxis;
final ThermionEntity zAxis;
AxisWidget._(this.xAxis, this.yAxis, this.zAxis, this._viewer);
static Future<Axis> create(ThermionViewer viewer) async {
final xAxis = await viewer.createGeometry(
Geometry(Float32List.fromList([0, 0, 0, 10, 0, 0]), [0, 1],
primitiveType: PrimitiveType.LINES),
materialInstances: [await viewer.createUnlitMaterialInstance()]);
final yAxis = await viewer.createGeometry(
Geometry(Float32List.fromList([0, 0, 0, 0, 10, 0]), [0, 1],
primitiveType: PrimitiveType.LINES),
materialInstances: [await viewer.createUnlitMaterialInstance()]);
final zAxis = await viewer.createGeometry(
Geometry(Float32List.fromList([0, 0, 0, 0, 0, 10]), [0, 1],
primitiveType: PrimitiveType.LINES),
materialInstances: [await viewer.createUnlitMaterialInstance()]);
throw Exception("TODO");
// await viewer!.setMaterialPropertyFloat4(
// xAxis, "baseColorFactor", 0, 1.0, 0.0, 0.0, 1.0);
// await viewer!.setMaterialPropertyFloat4(
// yAxis, "baseColorFactor", 0, 0.0, 1.0, 0.0, 1.0);
// await viewer!.setMaterialPropertyFloat4(
// zAxis, "baseColorFactor", 0, 0.0, 0.0, 1.0, 1.0);
// return Axis._(xAxis, yAxis, zAxis, viewer);
}
Future setTransform(Matrix4 transform) async {
await _viewer.setTransform(xAxis, transform);
await _viewer.setTransform(yAxis, transform);
await _viewer.setTransform(zAxis, transform);
}
}

View File

@@ -1,5 +1,4 @@
library; library;
export 'src/geometry.dart'; export 'src/geometry.dart';
export 'src/axis.dart';
export 'src/image.dart'; export 'src/image.dart';

View File

@@ -5,25 +5,23 @@ import 'package:animation_tools_dart/src/morph_animation_data.dart';
import 'package:thermion_dart/src/filament/src/layers.dart'; import 'package:thermion_dart/src/filament/src/layers.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_asset.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_asset.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_material.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_scene.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_scene.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_texture.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_texture.dart';
import 'package:thermion_dart/thermion_dart.dart'; import 'package:thermion_dart/thermion_dart.dart';
class BackgroundImage extends ThermionAsset { class BackgroundImage extends ThermionAsset {
final ThermionAsset asset; final ThermionAsset asset;
ThermionEntity get entity => asset.entity; ThermionEntity get entity => asset.entity;
Texture? _backgroundImageTexture; Texture? _backgroundImageTexture;
FFIMaterial? _imageMaterial;
FFITextureSampler? _imageSampler; FFITextureSampler? _imageSampler;
final FFIScene scene; final FFIScene scene;
final FilamentApp app;
BackgroundImage._(this.asset, this.scene, this.app, BackgroundImage._(
this._backgroundImageTexture, this._imageMaterial, this._imageSampler); this.asset, this.scene, this._backgroundImageTexture, this._imageSampler);
Future destroy() async { Future destroy() async {
Scene_removeEntity(scene.scene, entity); Scene_removeEntity(scene.scene, entity);
@@ -32,33 +30,39 @@ class BackgroundImage extends ThermionAsset {
} }
static Future<BackgroundImage> create( static Future<BackgroundImage> create(
FFIFilamentApp app, FFIScene scene, Uint8List imageData) async { ThermionViewer viewer, FFIScene scene, Uint8List imageData) async {
final image = await app.decodeImage(imageData); final image = await FilamentApp.instance!.decodeImage(imageData);
var backgroundImageTexture = await app.createTexture( var backgroundImageTexture = await FilamentApp.instance!
await image.getWidth(), await image.getHeight()); .createTexture(await image.getWidth(), await image.getHeight());
var imageMaterial = FFIMaterial(Material_createImageMaterial(), app);
var imageSampler = await app.createTextureSampler() as FFITextureSampler; var imageSampler =
await FilamentApp.instance!.createTextureSampler() as FFITextureSampler;
var imageMaterialInstance = var imageMaterialInstance =
await imageMaterial!.createInstance() as FFIMaterialInstance; await FilamentApp.instance!.createImageMaterialInstance();
await imageMaterialInstance.setParameterTexture( await imageMaterialInstance.setParameterTexture(
"image", "image", backgroundImageTexture as FFITexture, imageSampler);
backgroundImageTexture as FFITexture,
imageSampler as FFITextureSampler);
var backgroundImage = var backgroundImage =
await app.createGeometry(GeometryHelper.fullscreenQuad()) as FFIAsset; await viewer.createGeometry(GeometryHelper.fullscreenQuad());
backgroundImage.setMaterialInstanceAt(imageMaterialInstance); backgroundImage.setMaterialInstanceAt(imageMaterialInstance);
await scene.add(backgroundImage); await scene.add(backgroundImage as FFIAsset);
return BackgroundImage._(backgroundImage, scene, app, return BackgroundImage._(
backgroundImageTexture, imageMaterial, imageSampler); backgroundImage, scene, backgroundImageTexture, imageSampler);
} }
///
///
///
@override @override
Future<ThermionAsset> createInstance( Future<ThermionAsset> createInstance(
{covariant List<MaterialInstance>? materialInstances = null}) { {covariant List<MaterialInstance>? materialInstances = null}) {
throw UnimplementedError(); throw UnimplementedError();
} }
///
///
///
@override @override
Future<List<ThermionEntity>> getChildEntities() async { Future<List<ThermionEntity>> getChildEntities() async {
return []; return [];
@@ -129,7 +133,11 @@ class BackgroundImage extends ThermionAsset {
} }
@override @override
Future addBoneAnimation(ThermionAsset asset, BoneAnimationData animation, {int skinIndex = 0, double fadeInInSecs = 0.0, double fadeOutInSecs = 0.0, double maxDelta = 1.0}) { Future addBoneAnimation(BoneAnimationData animation,
{int skinIndex = 0,
double fadeInInSecs = 0.0,
double fadeOutInSecs = 0.0,
double maxDelta = 1.0}) {
// TODO: implement addBoneAnimation // TODO: implement addBoneAnimation
throw UnimplementedError(); throw UnimplementedError();
} }
@@ -141,61 +149,76 @@ class BackgroundImage extends ThermionAsset {
} }
@override @override
Future<double> getAnimationDuration(covariant ThermionAsset asset, int animationIndex) { Future<double> getAnimationDuration(int animationIndex) {
// TODO: implement getAnimationDuration // TODO: implement getAnimationDuration
throw UnimplementedError(); throw UnimplementedError();
} }
@override @override
Future<List<String>> getAnimationNames(covariant ThermionAsset asset) { Future<List<String>> getAnimationNames() {
// TODO: implement getAnimationNames // TODO: implement getAnimationNames
throw UnimplementedError(); throw UnimplementedError();
} }
@override @override
Future<ThermionEntity> getBone(covariant ThermionAsset asset, int boneIndex, {int skinIndex = 0}) { Future<ThermionEntity> getBone(int boneIndex, {int skinIndex = 0}) {
// TODO: implement getBone // TODO: implement getBone
throw UnimplementedError(); throw UnimplementedError();
} }
@override @override
Future<List<String>> getBoneNames(covariant ThermionAsset asset, {int skinIndex = 0}) { Future<List<String>> getBoneNames({int skinIndex = 0}) {
// TODO: implement getBoneNames // TODO: implement getBoneNames
throw UnimplementedError(); throw UnimplementedError();
} }
@override @override
Future<Matrix4> getInverseBindMatrix(covariant ThermionAsset asset, int boneIndex, {int skinIndex = 0}) { Future<ThermionEntity?> getChildEntity(String childName) {
// TODO: implement getChildEntity
throw UnimplementedError();
}
@override
Future<Matrix4> getInverseBindMatrix(int boneIndex, {int skinIndex = 0}) {
// TODO: implement getInverseBindMatrix // TODO: implement getInverseBindMatrix
throw UnimplementedError(); throw UnimplementedError();
} }
@override @override
Future<Matrix4> getLocalTransform(ThermionEntity entity) { Future<Matrix4> getLocalTransform({ThermionEntity? entity}) {
// TODO: implement getLocalTransform // TODO: implement getLocalTransform
throw UnimplementedError(); throw UnimplementedError();
} }
@override @override
Future<List<String>> getMorphTargetNames(covariant ThermionAsset asset, ThermionEntity childEntity) { Future<List<String>> getMorphTargetNames({ThermionEntity? entity}) {
// TODO: implement getMorphTargetNames // TODO: implement getMorphTargetNames
throw UnimplementedError(); throw UnimplementedError();
} }
@override @override
Future<Matrix4> getWorldTransform(ThermionEntity entity) { Future<Matrix4> getWorldTransform({ThermionEntity? entity}) {
// TODO: implement getWorldTransform // TODO: implement getWorldTransform
throw UnimplementedError(); throw UnimplementedError();
} }
@override @override
Future playAnimation(ThermionAsset asset, int index, {bool loop = false, bool reverse = false, bool replaceActive = true, double crossfade = 0.0, double startOffset = 0.0}) { Future playAnimation(int index,
{bool loop = false,
bool reverse = false,
bool replaceActive = true,
double crossfade = 0.0,
double startOffset = 0.0}) {
// TODO: implement playAnimation // TODO: implement playAnimation
throw UnimplementedError(); throw UnimplementedError();
} }
@override @override
Future playAnimationByName(covariant ThermionAsset asset, String name, {bool loop = false, bool reverse = false, bool replaceActive = true, double crossfade = 0.0}) { Future playAnimationByName(String name,
{bool loop = false,
bool reverse = false,
bool replaceActive = true,
double crossfade = 0.0}) {
// TODO: implement playAnimationByName // TODO: implement playAnimationByName
throw UnimplementedError(); throw UnimplementedError();
} }
@@ -207,25 +230,28 @@ class BackgroundImage extends ThermionAsset {
} }
@override @override
Future resetBones(ThermionAsset asset) { Future resetBones() {
// TODO: implement resetBones // TODO: implement resetBones
throw UnimplementedError(); throw UnimplementedError();
} }
@override @override
Future setBoneTransform(ThermionEntity entity, int boneIndex, Matrix4 transform, {int skinIndex = 0}) { Future setBoneTransform(
ThermionEntity entity, int boneIndex, Matrix4 transform,
{int skinIndex = 0}) {
// TODO: implement setBoneTransform // TODO: implement setBoneTransform
throw UnimplementedError(); throw UnimplementedError();
} }
@override @override
Future setGltfAnimationFrame(covariant ThermionAsset asset, int index, int animationFrame) { Future setGltfAnimationFrame(int index, int animationFrame) {
// TODO: implement setGltfAnimationFrame // TODO: implement setGltfAnimationFrame
throw UnimplementedError(); throw UnimplementedError();
} }
@override @override
Future setMorphAnimationData(covariant ThermionAsset asset, MorphAnimationData animation, {List<String>? targetMeshNames}) { Future setMorphAnimationData(MorphAnimationData animation,
{List<String>? targetMeshNames}) {
// TODO: implement setMorphAnimationData // TODO: implement setMorphAnimationData
throw UnimplementedError(); throw UnimplementedError();
} }
@@ -243,13 +269,13 @@ class BackgroundImage extends ThermionAsset {
} }
@override @override
Future stopAnimation(covariant ThermionAsset asset, int animationIndex) { Future stopAnimation(int animationIndex) {
// TODO: implement stopAnimation // TODO: implement stopAnimation
throw UnimplementedError(); throw UnimplementedError();
} }
@override @override
Future stopAnimationByName(covariant ThermionAsset asset, String name) { Future stopAnimationByName(String name) {
// TODO: implement stopAnimationByName // TODO: implement stopAnimationByName
throw UnimplementedError(); throw UnimplementedError();
} }

View File

@@ -1,5 +1,7 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:animation_tools_dart/animation_tools_dart.dart';
import 'package:logging/logging.dart';
import 'package:thermion_dart/src/filament/src/layers.dart'; import 'package:thermion_dart/src/filament/src/layers.dart';
import 'package:thermion_dart/src/utils/src/matrix.dart'; import 'package:thermion_dart/src/utils/src/matrix.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
@@ -8,10 +10,8 @@ import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_material.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_viewer_ffi.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_viewer_ffi.dart';
import 'package:thermion_dart/thermion_dart.dart'; import 'package:thermion_dart/thermion_dart.dart';
import 'package:vector_math/vector_math_64.dart' as v64; import 'package:vector_math/vector_math_64.dart' as v64;
import 'package:vector_math/vector_math_64.dart';
class FFIAsset extends ThermionAsset { class FFIAsset extends ThermionAsset {
/// ///
/// ///
/// ///
@@ -22,6 +22,11 @@ class FFIAsset extends ThermionAsset {
/// ///
final FFIFilamentApp app; final FFIFilamentApp app;
///
///
///
final Pointer<TAnimationManager> animationManager;
/// ///
/// ///
/// ///
@@ -37,10 +42,13 @@ class FFIAsset extends ThermionAsset {
/// ///
late final ThermionEntity entity; late final ThermionEntity entity;
late final _logger = Logger(this.runtimeType.toString());
/// ///
/// ///
/// ///
FFIAsset(this.asset, this.app, {this.isInstance = false}) { FFIAsset(this.asset, this.app, this.animationManager,
{this.isInstance = false}) {
entity = SceneAsset_getEntity(asset); entity = SceneAsset_getEntity(asset);
} }
@@ -59,8 +67,7 @@ class FFIAsset extends ThermionAsset {
/// ///
/// ///
@override @override
Future<ThermionEntity?> getChildEntity( Future<ThermionEntity?> getChildEntity(String childName) async {
FFIAsset asset, String childName) async {
final childEntities = await getChildEntities(); final childEntities = await getChildEntities();
for (final entity in childEntities) { for (final entity in childEntities) {
var name = NameComponentManager_getName(app.nameComponentManager, entity); var name = NameComponentManager_getName(app.nameComponentManager, entity);
@@ -71,6 +78,9 @@ class FFIAsset extends ThermionAsset {
return null; return null;
} }
///
///
///
@override @override
Future<ThermionAsset> getInstance(int index) async { Future<ThermionAsset> getInstance(int index) async {
if (isInstance) { if (isInstance) {
@@ -81,7 +91,7 @@ class FFIAsset extends ThermionAsset {
if (instance == nullptr) { if (instance == nullptr) {
throw Exception("No instance available at index $index"); throw Exception("No instance available at index $index");
} }
return FFIAsset(instance, app); return FFIAsset(instance, app, animationManager);
} }
/// ///
@@ -111,7 +121,7 @@ class FFIAsset extends ThermionAsset {
if (created == FILAMENT_ASSET_ERROR) { if (created == FILAMENT_ASSET_ERROR) {
throw Exception("Failed to create instance"); throw Exception("Failed to create instance");
} }
return FFIAsset(created, app); return FFIAsset(created, app, animationManager);
} }
/// ///
@@ -129,7 +139,7 @@ class FFIAsset extends ThermionAsset {
Future<List<ThermionAsset>> getInstances() async { Future<List<ThermionAsset>> getInstances() async {
var count = await getInstanceCount(); var count = await getInstanceCount();
final result = List<ThermionAsset>.generate(count, (i) { final result = List<ThermionAsset>.generate(count, (i) {
return FFIAsset(SceneAsset_getInstance(asset, i), app); return FFIAsset(SceneAsset_getInstance(asset, i), app, animationManager);
}); });
return result; return result;
@@ -163,7 +173,7 @@ class FFIAsset extends ThermionAsset {
var targetEntity = this.entity; var targetEntity = this.entity;
if (entityIndex != null) { if (entityIndex != null) {
final childEntities = await this.getChildEntities(); final childEntities = await this.getChildEntities();
targetEntity = childEntities[entityIndex!]; targetEntity = childEntities[entityIndex];
} }
var sourceMaterialInstance = FFIMaterialInstance( var sourceMaterialInstance = FFIMaterialInstance(
RenderableManager_getMaterialInstanceAt( RenderableManager_getMaterialInstanceAt(
@@ -209,7 +219,7 @@ class FFIAsset extends ThermionAsset {
if (entityIndex != null) { if (entityIndex != null) {
var highlightChildEntities = await _highlight!.getChildEntities(); var highlightChildEntities = await _highlight!.getChildEntities();
targetHighlightEntity = highlightChildEntities[entityIndex!]; targetHighlightEntity = highlightChildEntities[entityIndex];
} }
RenderableManager_setPriority( RenderableManager_setPriority(
@@ -397,7 +407,8 @@ class FFIAsset extends ThermionAsset {
/// ///
/// ///
Future transformToUnitCube() async { Future transformToUnitCube() async {
TransformManager_transformToUnitCube(app.transformManager, entity, SceneAsset_getBoundingBox(asset)); TransformManager_transformToUnitCube(
app.transformManager, entity, SceneAsset_getBoundingBox(asset));
} }
/// ///
@@ -439,33 +450,35 @@ class FFIAsset extends ThermionAsset {
/// ///
/// ///
@override @override
Future<List<String>> getMorphTargetNames( Future<List<String>> getMorphTargetNames({ThermionEntity? entity}) async {
covariant FFIAsset asset, ThermionEntity childEntity) async {
var names = <String>[]; var names = <String>[];
entity ??= this.entity;
var count = AnimationManager_getMorphTargetNameCount( var count = AnimationManager_getMorphTargetNameCount(
animationManager, asset.asset, childEntity); animationManager, asset, entity);
var outPtr = allocator<Char>(255); var outPtr = allocator<Char>(255);
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
AnimationManager_getMorphTargetName( AnimationManager_getMorphTargetName(
animationManager, asset.asset, childEntity, outPtr, i); animationManager, asset, entity, outPtr, i);
names.add(outPtr.cast<Utf8>().toDartString()); names.add(outPtr.cast<Utf8>().toDartString());
} }
allocator.free(outPtr); allocator.free(outPtr);
return names.cast<String>(); return names.cast<String>();
} }
Future<List<String>> getBoneNames(covariant FFIAsset asset, ///
{int skinIndex = 0}) async { ///
///
Future<List<String>> getBoneNames({int skinIndex = 0}) async {
var count = var count =
AnimationManager_getBoneCount(animationManager, asset.asset, skinIndex); AnimationManager_getBoneCount(animationManager, asset, skinIndex);
var out = allocator<Pointer<Char>>(count); var out = allocator<Pointer<Char>>(count);
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
out[i] = allocator<Char>(255); out[i] = allocator<Char>(255);
} }
AnimationManager_getBoneNames( AnimationManager_getBoneNames(animationManager, asset, out, skinIndex);
animationManager, asset.asset, out, skinIndex);
var names = <String>[]; var names = <String>[];
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
var namePtr = out[i]; var namePtr = out[i];
@@ -478,14 +491,13 @@ class FFIAsset extends ThermionAsset {
/// ///
/// ///
@override @override
Future<List<String>> getAnimationNames(covariant FFIAsset asset) async { Future<List<String>> getAnimationNames() async {
var animationCount = var animationCount =
AnimationManager_getAnimationCount(animationManager, asset.asset); AnimationManager_getAnimationCount(animationManager, asset);
var names = <String>[]; var names = <String>[];
var outPtr = allocator<Char>(255); var outPtr = allocator<Char>(255);
for (int i = 0; i < animationCount; i++) { for (int i = 0; i < animationCount; i++) {
AnimationManager_getAnimationName( AnimationManager_getAnimationName(animationManager, asset, outPtr, i);
animationManager, asset.asset, outPtr, i);
names.add(outPtr.cast<Utf8>().toDartString()); names.add(outPtr.cast<Utf8>().toDartString());
} }
allocator.free(outPtr); allocator.free(outPtr);
@@ -497,24 +509,26 @@ class FFIAsset extends ThermionAsset {
/// ///
/// ///
@override @override
Future<double> getAnimationDuration( Future<double> getAnimationDuration(int animationIndex) async {
FFIAsset asset, int animationIndex) async {
return AnimationManager_getAnimationDuration( return AnimationManager_getAnimationDuration(
animationManager, asset.asset, animationIndex); animationManager, asset, animationIndex);
} }
/// ///
/// ///
/// ///
Future<double> getAnimationDurationByName(FFIAsset asset, String name) async { Future<double> getAnimationDurationByName(String name) async {
var animations = await getAnimationNames(asset); var animations = await getAnimationNames();
var index = animations.indexOf(name); var index = animations.indexOf(name);
if (index == -1) { if (index == -1) {
throw Exception("Failed to find animation $name"); throw Exception("Failed to find animation $name");
} }
return getAnimationDuration(asset, index); return getAnimationDuration(index);
} }
///
///
///
Future clearMorphAnimationData(ThermionEntity entity) async { Future clearMorphAnimationData(ThermionEntity entity) async {
if (!AnimationManager_clearMorphAnimation(animationManager, entity)) { if (!AnimationManager_clearMorphAnimation(animationManager, entity)) {
throw Exception("Failed to clear morph animation"); throw Exception("Failed to clear morph animation");
@@ -525,11 +539,13 @@ class FFIAsset extends ThermionAsset {
/// ///
/// ///
@override @override
Future setMorphAnimationData(FFIAsset asset, MorphAnimationData animation, Future setMorphAnimationData(MorphAnimationData animation,
{List<String>? targetMeshNames}) async { {List<String>? targetMeshNames}) async {
var meshEntities = await getChildEntities(asset); var meshEntities = await getChildEntities();
var meshNames = meshEntities.map((e) => getNameForEntity(e)).toList(); var meshNames = meshEntities
.map((e) => FilamentApp.instance!.getNameForEntity(e))
.toList();
if (targetMeshNames != null) { if (targetMeshNames != null) {
for (final targetMeshName in targetMeshNames) { for (final targetMeshName in targetMeshNames) {
if (!meshNames.contains(targetMeshName)) { if (!meshNames.contains(targetMeshName)) {
@@ -554,7 +570,7 @@ class FFIAsset extends ThermionAsset {
continue; continue;
} }
var meshMorphTargets = await getMorphTargetNames(asset, meshEntity); var meshMorphTargets = await getMorphTargetNames(entity: meshEntity);
var intersection = animation.morphTargets var intersection = animation.morphTargets
.toSet() .toSet()
@@ -598,7 +614,7 @@ class FFIAsset extends ThermionAsset {
/// Currently, scale is not supported. /// Currently, scale is not supported.
/// ///
@override @override
Future addBoneAnimation(covariant FFIAsset asset, BoneAnimationData animation, Future addBoneAnimation(BoneAnimationData animation,
{int skinIndex = 0, {int skinIndex = 0,
double fadeOutInSecs = 0.0, double fadeOutInSecs = 0.0,
double fadeInInSecs = 0.0, double fadeInInSecs = 0.0,
@@ -610,10 +626,10 @@ class FFIAsset extends ThermionAsset {
if (skinIndex != 0) { if (skinIndex != 0) {
throw UnimplementedError("TODO - support skinIndex != 0 "); throw UnimplementedError("TODO - support skinIndex != 0 ");
} }
var boneNames = await getBoneNames(asset); var boneNames = await getBoneNames();
var restLocalTransformsRaw = allocator<Float>(boneNames.length * 16); var restLocalTransformsRaw = allocator<Float>(boneNames.length * 16);
AnimationManager_getRestLocalTransforms(animationManager, asset.asset, AnimationManager_getRestLocalTransforms(animationManager, asset, skinIndex,
skinIndex, restLocalTransformsRaw, boneNames.length); restLocalTransformsRaw, boneNames.length);
var restLocalTransforms = <Matrix4>[]; var restLocalTransforms = <Matrix4>[];
for (int i = 0; i < boneNames.length; i++) { for (int i = 0; i < boneNames.length; i++) {
@@ -630,7 +646,7 @@ class FFIAsset extends ThermionAsset {
var data = allocator<Float>(numFrames * 16); var data = allocator<Float>(numFrames * 16);
var bones = await Future.wait(List<Future<ThermionEntity>>.generate( var bones = await Future.wait(List<Future<ThermionEntity>>.generate(
boneNames.length, (i) => getBone(asset, i))); boneNames.length, (i) => getBone(i)));
for (int i = 0; i < animation.bones.length; i++) { for (int i = 0; i < animation.bones.length; i++) {
var boneName = animation.bones[i]; var boneName = animation.bones[i];
@@ -645,13 +661,15 @@ class FFIAsset extends ThermionAsset {
var world = Matrix4.identity(); var world = Matrix4.identity();
// this odd use of ! is intentional, without it, the WASM optimizer gets in trouble // this odd use of ! is intentional, without it, the WASM optimizer gets in trouble
var parentBoneEntity = (await getParent(boneEntity))!; var parentBoneEntity =
(await FilamentApp.instance!.getParent(boneEntity))!;
while (true) { while (true) {
if (!bones.contains(parentBoneEntity!)) { if (!bones.contains(parentBoneEntity!)) {
break; break;
} }
world = restLocalTransforms[bones.indexOf(parentBoneEntity!)] * world; world = restLocalTransforms[bones.indexOf(parentBoneEntity!)] * world;
parentBoneEntity = (await getParent(parentBoneEntity))!; parentBoneEntity =
(await FilamentApp.instance!.getParent(parentBoneEntity))!;
} }
world = Matrix4.identity()..setRotation(world.getRotation()); world = Matrix4.identity()..setRotation(world.getRotation());
@@ -677,7 +695,7 @@ class FFIAsset extends ThermionAsset {
AnimationManager_addBoneAnimation( AnimationManager_addBoneAnimation(
animationManager, animationManager,
asset.asset, asset,
skinIndex, skinIndex,
entityBoneIndex, entityBoneIndex,
data, data,
@@ -693,7 +711,8 @@ class FFIAsset extends ThermionAsset {
/// ///
/// ///
/// ///
Future<Matrix4> getLocalTransform(ThermionEntity entity) async { Future<Matrix4> getLocalTransform({ThermionEntity? entity}) async {
entity ??= this.entity;
return double4x4ToMatrix4( return double4x4ToMatrix4(
TransformManager_getLocalTransform(app.transformManager, entity)); TransformManager_getLocalTransform(app.transformManager, entity));
} }
@@ -701,7 +720,8 @@ class FFIAsset extends ThermionAsset {
/// ///
/// ///
/// ///
Future<Matrix4> getWorldTransform(ThermionEntity entity) async { Future<Matrix4> getWorldTransform({ThermionEntity? entity}) async {
entity ??= this.entity;
return double4x4ToMatrix4( return double4x4ToMatrix4(
TransformManager_getWorldTransform(app.transformManager, entity)); TransformManager_getWorldTransform(app.transformManager, entity));
} }
@@ -709,12 +729,13 @@ class FFIAsset extends ThermionAsset {
/// ///
/// ///
/// ///
Future setTransform(ThermionEntity entity, Matrix4 transform) async { Future setTransform(Matrix4 transform, {ThermionEntity? entity}) async {
entity ??= this.entity;
TransformManager_setTransform( TransformManager_setTransform(
app.transformManager, entity, matrix4ToDouble4x4(transform)); app.transformManager, entity, matrix4ToDouble4x4(transform));
} }
/// ///
/// ///
/// ///
Future updateBoneMatrices(ThermionEntity entity) async { Future updateBoneMatrices(ThermionEntity entity) async {
@@ -731,24 +752,23 @@ class FFIAsset extends ThermionAsset {
/// ///
/// ///
/// ///
Future<Matrix4> getInverseBindMatrix(FFIAsset asset, int boneIndex, Future<Matrix4> getInverseBindMatrix(int boneIndex,
{int skinIndex = 0}) async { {int skinIndex = 0}) async {
var matrix = Float32List(16); var matrix = Float32List(16);
AnimationManager_getInverseBindMatrix( AnimationManager_getInverseBindMatrix(
animationManager, asset.asset, skinIndex, boneIndex, matrix.address); animationManager, asset, skinIndex, boneIndex, matrix.address);
return Matrix4.fromList(matrix); return Matrix4.fromList(matrix);
} }
/// ///
/// ///
/// ///
Future<ThermionEntity> getBone(FFIAsset asset, int boneIndex, Future<ThermionEntity> getBone(int boneIndex, {int skinIndex = 0}) async {
{int skinIndex = 0}) async {
if (skinIndex != 0) { if (skinIndex != 0) {
throw UnimplementedError("TOOD"); throw UnimplementedError("TOOD");
} }
return AnimationManager_getBone( return AnimationManager_getBone(
animationManager, asset.asset, skinIndex, boneIndex); animationManager, asset, skinIndex, boneIndex);
} }
/// ///
@@ -781,22 +801,21 @@ class FFIAsset extends ThermionAsset {
/// ///
/// ///
@override @override
Future resetBones(covariant FFIAsset asset) async { Future resetBones() async {
AnimationManager_resetToRestPose(animationManager, asset.asset); AnimationManager_resetToRestPose(animationManager, asset);
} }
/// ///
/// ///
/// ///
@override @override
Future playAnimation(covariant FFIAsset asset, int index, Future playAnimation(int index,
{bool loop = false, {bool loop = false,
bool reverse = false, bool reverse = false,
bool replaceActive = true, bool replaceActive = true,
double crossfade = 0.0, double crossfade = 0.0,
double startOffset = 0.0}) async { double startOffset = 0.0}) async {
AnimationManager_playAnimation(animationManager, asset.asset, index, loop, AnimationManager_playAnimation(animationManager, asset, index, loop,
reverse, replaceActive, crossfade, startOffset); reverse, replaceActive, crossfade, startOffset);
} }
@@ -804,34 +823,33 @@ class FFIAsset extends ThermionAsset {
/// ///
/// ///
@override @override
Future stopAnimation(FFIAsset asset, int animationIndex) async { Future stopAnimation(int animationIndex) async {
AnimationManager_stopAnimation( AnimationManager_stopAnimation(animationManager, asset, animationIndex);
animationManager, asset.asset, animationIndex);
} }
/// ///
/// ///
/// ///
@override @override
Future stopAnimationByName(FFIAsset asset, String name) async { Future stopAnimationByName(String name) async {
var animations = await getAnimationNames(asset); var animations = await getAnimationNames();
await stopAnimation(asset, animations.indexOf(name)); await stopAnimation(animations.indexOf(name));
} }
/// ///
/// ///
/// ///
@override @override
Future playAnimationByName(FFIAsset asset, String name, Future playAnimationByName(String name,
{bool loop = false, {bool loop = false,
bool reverse = false, bool reverse = false,
bool replaceActive = true, bool replaceActive = true,
double crossfade = 0.0, double crossfade = 0.0,
bool wait = false}) async { bool wait = false}) async {
var animations = await getAnimationNames(asset); var animations = await getAnimationNames();
var index = animations.indexOf(name); var index = animations.indexOf(name);
var duration = await getAnimationDuration(asset, index); var duration = await getAnimationDuration(index);
await playAnimation(asset, index, await playAnimation(index,
loop: loop, loop: loop,
reverse: reverse, reverse: reverse,
replaceActive: replaceActive, replaceActive: replaceActive,
@@ -845,13 +863,12 @@ class FFIAsset extends ThermionAsset {
/// ///
/// ///
@override @override
Future setGltfAnimationFrame( Future setGltfAnimationFrame(int index, int animationFrame) async {
FFIAsset asset, int index, int animationFrame) async {
AnimationManager_setGltfAnimationFrame( AnimationManager_setGltfAnimationFrame(
animationManager, asset.asset, index, animationFrame); animationManager, asset, index, animationFrame);
} }
/// ///
/// ///
/// ///
@override @override
@@ -865,5 +882,4 @@ class FFIAsset extends ThermionAsset {
Future removeAnimationComponent(ThermionEntity entity) async { Future removeAnimationComponent(ThermionEntity entity) async {
AnimationManager_removeAnimationComponent(animationManager, entity); AnimationManager_removeAnimationComponent(animationManager, entity);
} }
} }

View File

@@ -2,12 +2,12 @@ import 'dart:async';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_asset.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_material.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_material.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_render_target.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_render_target.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_swapchain.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_swapchain.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_texture.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_texture.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_view.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_view.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_viewer_ffi.dart';
import 'package:thermion_dart/thermion_dart.dart'; import 'package:thermion_dart/thermion_dart.dart';
typedef RenderCallback = Pointer<NativeFunction<Void Function(Pointer<Void>)>>; typedef RenderCallback = Pointer<NativeFunction<Void Function(Pointer<Void>)>>;
@@ -59,7 +59,7 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
if (FilamentApp.instance != null) { if (FilamentApp.instance != null) {
await FilamentApp.instance!.destroy(); await FilamentApp.instance!.destroy();
} }
RenderLoop_destroy(); RenderLoop_destroy();
RenderLoop_create(); RenderLoop_create();
@@ -102,7 +102,6 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
ubershaderMaterialProvider, ubershaderMaterialProvider,
renderTicker, renderTicker,
nameComponentManager); nameComponentManager);
} }
final _views = <FFISwapChain, List<FFIView>>{}; final _views = <FFISwapChain, List<FFIView>>{};
@@ -170,6 +169,9 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
}); });
} }
///
///
///
@override @override
Future destroy() async { Future destroy() async {
for (final swapChain in _views.keys) { for (final swapChain in _views.keys) {
@@ -177,6 +179,12 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
await setRenderable(view, false); await setRenderable(view, false);
} }
} }
for (final swapChain in _views.keys) {
await destroySwapChain(swapChain);
}
RenderLoop_destroy();
RenderTicker_destroy(renderTicker);
Engine_destroy(engine);
} }
/// ///
@@ -205,7 +213,7 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
var bitmask = flags.fold(0, (a, b) => a | b.index); var bitmask = flags.fold(0, (a, b) => a | b.index);
final texturePtr = await withPointerCallback<TTexture>((cb) { final texturePtr = await withPointerCallback<TTexture>((cb) {
Texture_buildRenderThread( Texture_buildRenderThread(
engine!, engine,
width, width,
height, height,
depth, depth,
@@ -225,6 +233,9 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
); );
} }
///
///
///
Future<TextureSampler> createTextureSampler( Future<TextureSampler> createTextureSampler(
{TextureMinFilter minFilter = TextureMinFilter.LINEAR, {TextureMinFilter minFilter = TextureMinFilter.LINEAR,
TextureMagFilter magFilter = TextureMagFilter.LINEAR, TextureMagFilter magFilter = TextureMagFilter.LINEAR,
@@ -397,49 +408,6 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
return _gridMaterial!; return _gridMaterial!;
} }
///
///
///
@override
Future<ThermionAsset> createGeometry(Geometry geometry,
{List<MaterialInstance>? materialInstances,
bool keepData = false}) async {
var assetPtr = await withPointerCallback<TSceneAsset>((callback) {
var ptrList = Int64List(materialInstances?.length ?? 0);
if (materialInstances != null && materialInstances.isNotEmpty) {
ptrList.setRange(
0,
materialInstances.length,
materialInstances
.cast<FFIMaterialInstance>()
.map((mi) => mi.pointer.address)
.toList());
}
return SceneAsset_createGeometryRenderThread(
engine,
geometry.vertices.address,
geometry.vertices.length,
geometry.normals.address,
geometry.normals.length,
geometry.uvs.address,
geometry.uvs.length,
geometry.indices.address,
geometry.indices.length,
geometry.primitiveType.index,
ptrList.address.cast<Pointer<TMaterialInstance>>(),
ptrList.length,
callback);
});
if (assetPtr == nullptr) {
throw Exception("Failed to create geometry");
}
var asset = FFIAsset(assetPtr, this);
return asset;
}
/// ///
/// ///
/// ///
@@ -460,6 +428,9 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
RenderTicker_renderRenderThread(renderTicker, 0); RenderTicker_renderRenderThread(renderTicker, 0);
} }
///
///
///
@override @override
Future register( Future register(
covariant FFISwapChain swapChain, covariant FFIView view) async { covariant FFISwapChain swapChain, covariant FFIView view) async {
@@ -468,6 +439,9 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
final _hooks = <Future Function()>[]; final _hooks = <Future Function()>[];
///
///
///
@override @override
Future registerRequestFrameHook(Future Function() hook) async { Future registerRequestFrameHook(Future Function() hook) async {
if (!_hooks.contains(hook)) { if (!_hooks.contains(hook)) {
@@ -475,6 +449,9 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
} }
} }
///
///
///
@override @override
Future unregisterRequestFrameHook(Future Function() hook) async { Future unregisterRequestFrameHook(Future Function() hook) async {
if (_hooks.contains(hook)) { if (_hooks.contains(hook)) {
@@ -504,4 +481,61 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
print("WARNING - render call timed out"); print("WARNING - render call timed out");
} }
} }
///
///
///
@override
Future setParent(ThermionEntity child, ThermionEntity? parent,
{bool preserveScaling = false}) async {
TransformManager_setParent(transformManager, child,
parent ?? FILAMENT_ENTITY_NULL, preserveScaling);
}
///
///
///
@override
Future<ThermionEntity?> getParent(ThermionEntity child) async {
var parent = TransformManager_getParent(transformManager, child);
if (parent == FILAMENT_ASSET_ERROR) {
return null;
}
return parent;
}
///
///
///
@override
Future<ThermionEntity?> getAncestor(ThermionEntity child) async {
var parent = TransformManager_getAncestor(transformManager, child);
if (parent == FILAMENT_ASSET_ERROR) {
return null;
}
return parent;
}
///
///
///
@override
String? getNameForEntity(ThermionEntity entity) {
final result = NameComponentManager_getName(nameComponentManager, entity);
if (result == nullptr) {
return null;
}
return result.cast<Utf8>().toDartString();
}
Material? _imageMaterial;
@override
Future<MaterialInstance> createImageMaterialInstance() async {
_imageMaterial ??= FFIMaterial(Material_createImageMaterial(),
FilamentApp.instance! as FFIFilamentApp);
var instance =
await _imageMaterial!.createInstance() as FFIMaterialInstance;
return instance;
}
} }

View File

@@ -6,13 +6,12 @@ import 'package:thermion_dart/thermion_dart.dart';
import 'ffi_view.dart'; import 'ffi_view.dart';
class FFIGizmo extends FFIAsset implements GizmoAsset { class FFIGizmo extends FFIAsset implements GizmoAsset {
final Set<ThermionEntity> entities;
final Set<ThermionEntity> gizmoEntities;
late NativeCallable<GizmoPickCallbackFunction> _nativeCallback; late NativeCallable<GizmoPickCallbackFunction> _nativeCallback;
void Function(GizmoPickResultType axis, Vector3 coords)? _callback; void Function(GizmoPickResultType axis, Vector3 coords)? _callback;
late FFIView _view; late FFIView view;
void _onPickResult(int resultType, double x, double y, double z) { void _onPickResult(int resultType, double x, double y, double z) {
_callback?.call(GizmoPickResultType.values[resultType], Vector3(x, y, z)); _callback?.call(GizmoPickResultType.values[resultType], Vector3(x, y, z));
@@ -23,13 +22,16 @@ class FFIGizmo extends FFIAsset implements GizmoAsset {
// return SceneManager_isGridEntity(sceneManager, entity); // return SceneManager_isGridEntity(sceneManager, entity);
} }
bool isGizmoEntity(ThermionEntity entity) => gizmoEntities.contains(entity); bool isGizmoEntity(ThermionEntity entity) => entities.contains(entity);
FFIGizmo( FFIGizmo(
this._view, super.asset,
super.pointer, super.app,
super.app, super.animationManager,
this.gizmoEntities) { {
required this.view,
required this.entities,
}) {
_nativeCallback = _nativeCallback =
NativeCallable<GizmoPickCallbackFunction>.listener(_onPickResult); NativeCallable<GizmoPickCallbackFunction>.listener(_onPickResult);
} }
@@ -53,7 +55,7 @@ class FFIGizmo extends FFIAsset implements GizmoAsset {
{Future Function(GizmoPickResultType result, Vector3 coords)? {Future Function(GizmoPickResultType result, Vector3 coords)?
handler}) async { handler}) async {
_callback = handler; _callback = handler;
final viewport = await _view.getViewport(); final viewport = await view.getViewport();
y = viewport.height - y; y = viewport.height - y;
Gizmo_pick(asset.cast<TGizmo>(), x, y, _nativeCallback.nativeFunction); Gizmo_pick(asset.cast<TGizmo>(), x, y, _nativeCallback.nativeFunction);

View File

@@ -1,17 +1,11 @@
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_asset.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_asset.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_render_target.dart';
import 'package:thermion_dart/src/filament/src/scene.dart'; import 'package:thermion_dart/src/filament/src/scene.dart';
import 'callbacks.dart'; import 'callbacks.dart';
class FFIScene extends Scene { class FFIScene extends Scene {
final Pointer<TScene> scene; final Pointer<TScene> scene;
final FFIFilamentApp app;
FFIRenderTarget? renderTarget; FFIScene(this.scene);
FFIScene(this.scene, this.app) {}
@override @override
Future add(covariant FFIAsset asset) async { Future add(covariant FFIAsset asset) async {

View File

@@ -1,3 +1,4 @@
import 'package:thermion_dart/src/filament/src/scene.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_render_target.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_render_target.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_scene.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_scene.dart';
@@ -113,4 +114,10 @@ class FFIView extends View {
Future setLayerVisibility(VisibilityLayers layer, bool visible) async { Future setLayerVisibility(VisibilityLayers layer, bool visible) async {
View_setLayerEnabled(view, layer.value, visible); View_setLayerEnabled(view, layer.value, visible);
} }
@override
Future<Scene> getScene() async {
final ptr = View_getScene(view);
return FFIScene(ptr);
}
} }

View File

@@ -3,11 +3,12 @@ import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_asset.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
class GridOverlay extends FFIAsset { class GridOverlay extends FFIAsset {
GridOverlay(super.asset, super.app);
GridOverlay(super.asset, super.app, super.animationManager);
static Future<GridOverlay> create(FFIFilamentApp app) async { static Future<GridOverlay> create(FFIFilamentApp app, Pointer<TAnimationManager> animationManager) async {
final gridMaterial = await app.gridMaterial; final gridMaterial = await app.gridMaterial;
final asset = SceneAsset_createGrid(app.engine, gridMaterial.pointer); final asset = SceneAsset_createGrid(app.engine, gridMaterial.pointer);
return GridOverlay(asset, app); return GridOverlay(asset, app, animationManager);
} }
} }

View File

@@ -1443,6 +1443,11 @@ external ffi.Pointer<TRenderTicker> RenderTicker_create(
ffi.Pointer<TRenderer> tRenderer, ffi.Pointer<TRenderer> tRenderer,
); );
@ffi.Native<ffi.Void Function(ffi.Pointer<TRenderTicker>)>(isLeaf: true)
external void RenderTicker_destroy(
ffi.Pointer<TRenderTicker> tRenderTicker,
);
@ffi.Native< @ffi.Native<
ffi.Void Function(ffi.Pointer<TRenderTicker>, ffi.Void Function(ffi.Pointer<TRenderTicker>,
ffi.Pointer<TAnimationManager>)>(isLeaf: true) ffi.Pointer<TAnimationManager>)>(isLeaf: true)
@@ -2958,6 +2963,11 @@ ffi.Pointer<TEngine> Engine_create(
disableHandleUseAfterFreeCheck, disableHandleUseAfterFreeCheck,
); );
@ffi.Native<ffi.Pointer<TEngine> Function(ffi.Pointer<TEngine>)>(isLeaf: true)
external ffi.Pointer<TEngine> Engine_destroy(
ffi.Pointer<TEngine> tEngine,
);
@ffi.Native<ffi.Pointer<TRenderer> Function(ffi.Pointer<TEngine>)>(isLeaf: true) @ffi.Native<ffi.Pointer<TRenderer> Function(ffi.Pointer<TEngine>)>(isLeaf: true)
external ffi.Pointer<TRenderer> Engine_createRenderer( external ffi.Pointer<TRenderer> Engine_createRenderer(
ffi.Pointer<TEngine> tEngine, ffi.Pointer<TEngine> tEngine,
@@ -3521,6 +3531,15 @@ external void AnimationManager_setGltfAnimationFrame(
int frame, int frame,
); );
@ffi.Native<
ffi.Pointer<ffi.Void> Function(LoadFilamentResourceFromOwner,
FreeFilamentResourceFromOwner, ffi.Pointer<ffi.Void>)>(isLeaf: true)
external ffi.Pointer<ffi.Void> make_resource_loader(
LoadFilamentResourceFromOwner loadFn,
FreeFilamentResourceFromOwner freeFn,
ffi.Pointer<ffi.Void> owner,
);
final class TCamera extends ffi.Opaque {} final class TCamera extends ffi.Opaque {}
final class TEngine extends ffi.Opaque {} final class TEngine extends ffi.Opaque {}
@@ -4575,6 +4594,55 @@ enum TBackend {
}; };
} }
final class ResourceBuffer extends ffi.Struct {
external ffi.Pointer<ffi.Void> data;
@ffi.Int32()
external int size;
@ffi.Int32()
external int id;
}
final class ResourceLoaderWrapper extends ffi.Struct {
external LoadFilamentResource loadResource;
external FreeFilamentResource freeResource;
external LoadFilamentResourceFromOwner loadFromOwner;
external FreeFilamentResourceFromOwner freeFromOwner;
external ffi.Pointer<ffi.Void> owner;
external LoadFilamentResourceIntoOutPointer loadToOut;
}
typedef LoadFilamentResource
= ffi.Pointer<ffi.NativeFunction<LoadFilamentResourceFunction>>;
typedef LoadFilamentResourceFunction = ResourceBuffer Function(
ffi.Pointer<ffi.Char> uri);
typedef FreeFilamentResource
= ffi.Pointer<ffi.NativeFunction<FreeFilamentResourceFunction>>;
typedef FreeFilamentResourceFunction = ffi.Void Function(ResourceBuffer);
typedef DartFreeFilamentResourceFunction = void Function(ResourceBuffer);
typedef LoadFilamentResourceFromOwner
= ffi.Pointer<ffi.NativeFunction<LoadFilamentResourceFromOwnerFunction>>;
typedef LoadFilamentResourceFromOwnerFunction = ResourceBuffer Function(
ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Void>);
typedef FreeFilamentResourceFromOwner
= ffi.Pointer<ffi.NativeFunction<FreeFilamentResourceFromOwnerFunction>>;
typedef FreeFilamentResourceFromOwnerFunction = ffi.Void Function(
ResourceBuffer, ffi.Pointer<ffi.Void>);
typedef DartFreeFilamentResourceFromOwnerFunction = void Function(
ResourceBuffer, ffi.Pointer<ffi.Void>);
typedef LoadFilamentResourceIntoOutPointer = ffi
.Pointer<ffi.NativeFunction<LoadFilamentResourceIntoOutPointerFunction>>;
typedef LoadFilamentResourceIntoOutPointerFunction = ffi.Void Function(
ffi.Pointer<ffi.Char> uri, ffi.Pointer<ResourceBuffer> out);
typedef DartLoadFilamentResourceIntoOutPointerFunction = void Function(
ffi.Pointer<ffi.Char> uri, ffi.Pointer<ResourceBuffer> out);
const int __bool_true_false_are_defined = 1; const int __bool_true_false_are_defined = 1;
const int true1 = 1; const int true1 = 1;

View File

@@ -2,19 +2,17 @@ import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:animation_tools_dart/animation_tools_dart.dart'; import 'package:thermion_dart/src/filament/src/light_options.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/background_image.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/background_image.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_asset.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_asset.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_material.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_render_target.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_render_target.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_scene.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_scene.dart';
import 'package:thermion_dart/src/filament/src/layers.dart'; import 'package:thermion_dart/src/filament/src/layers.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/grid_overlay.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/grid_overlay.dart';
import 'package:thermion_dart/thermion_dart.dart'; import 'package:thermion_dart/thermion_dart.dart';
import 'package:vector_math/vector_math_64.dart';
import 'package:vector_math/vector_math_64.dart' as v64; import 'package:vector_math/vector_math_64.dart' as v64;
import '../../../../utils/src/matrix.dart';
import '../../thermion_viewer_base.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'callbacks.dart'; import 'callbacks.dart';
@@ -88,13 +86,15 @@ class ThermionViewerFFI extends ThermionViewer {
await withPointerCallback<TView>( await withPointerCallback<TView>(
(cb) => Engine_createViewRenderThread(app.engine, cb)), (cb) => Engine_createViewRenderThread(app.engine, cb)),
app); app);
scene = FFIScene(Engine_createScene(app.engine), app); scene = FFIScene(Engine_createScene(app.engine));
await view.setScene(scene); await view.setScene(scene);
final camera = FFICamera( final camera = FFICamera(
await withPointerCallback<TCamera>( await withPointerCallback<TCamera>(
(cb) => Engine_createCameraRenderThread(app.engine, cb)), (cb) => Engine_createCameraRenderThread(app.engine, cb)),
app); app);
_cameras.add(camera); _cameras.add(camera);
await view.setCamera(camera);
if (renderTarget != null) { if (renderTarget != null) {
await view.setRenderTarget(renderTarget); await view.setRenderTarget(renderTarget);
} }
@@ -193,7 +193,7 @@ class ThermionViewerFFI extends ThermionViewer {
@override @override
Future setBackgroundImage(String path, {bool fillHeight = false}) async { Future setBackgroundImage(String path, {bool fillHeight = false}) async {
final imageData = await loadAsset(path); final imageData = await loadAsset(path);
_backgroundImage = await BackgroundImage.create(app, scene, imageData); _backgroundImage = await BackgroundImage.create(this, scene, imageData);
} }
/// ///
@@ -406,7 +406,7 @@ class ThermionViewerFFI extends ThermionViewer {
throw Exception("An error occurred loading the asset"); throw Exception("An error occurred loading the asset");
} }
var thermionAsset = FFIAsset(asset, app); var thermionAsset = FFIAsset(asset, app, animationManager);
_assets.add(thermionAsset); _assets.add(thermionAsset);
@@ -550,19 +550,6 @@ class ThermionViewerFFI extends ThermionViewer {
app.lightManager, lightEntity, direction.x, direction.y, direction.z); app.lightManager, lightEntity, direction.x, direction.y, direction.z);
} }
///
///
///
@override
String? getNameForEntity(ThermionEntity entity) {
final result =
NameComponentManager_getName(app.nameComponentManager, entity);
if (result == nullptr) {
return null;
}
return result.cast<Utf8>().toDartString();
}
void _onPickResult(int requestId, ThermionEntity entityId, double depth, void _onPickResult(int requestId, ThermionEntity entityId, double depth,
double fragX, double fragY, double fragZ) async { double fragX, double fragY, double fragZ) async {
if (!_pickRequests.containsKey(requestId)) { if (!_pickRequests.containsKey(requestId)) {
@@ -611,40 +598,6 @@ class ThermionViewerFFI extends ThermionViewer {
}); });
} }
///
///
///
@override
Future setParent(ThermionEntity child, ThermionEntity? parent,
{bool preserveScaling = false}) async {
TransformManager_setParent(app.transformManager, child,
parent ?? FILAMENT_ENTITY_NULL, preserveScaling);
}
///
///
///
@override
Future<ThermionEntity?> getParent(ThermionEntity child) async {
var parent = TransformManager_getParent(app.transformManager, child);
if (parent == FILAMENT_ASSET_ERROR) {
return null;
}
return parent;
}
///
///
///
@override
Future<ThermionEntity?> getAncestor(ThermionEntity child) async {
var parent = TransformManager_getAncestor(app.transformManager, child);
if (parent == FILAMENT_ASSET_ERROR) {
return null;
}
return parent;
}
/// ///
/// ///
/// ///
@@ -678,7 +631,7 @@ class ThermionViewerFFI extends ThermionViewer {
/// ///
/// ///
Future showGridOverlay() async { Future showGridOverlay() async {
_grid ??= _grid = await GridOverlay.create(app); _grid ??= _grid = await GridOverlay.create(app, animationManager);
await scene.add(_grid!); await scene.add(_grid!);
await view.setLayerVisibility(VisibilityLayers.OVERLAY, true); await view.setLayerVisibility(VisibilityLayers.OVERLAY, true);
} }
@@ -749,6 +702,52 @@ class ThermionViewerFFI extends ThermionViewer {
} }
} }
///
///
///
@override
Future<ThermionAsset> createGeometry(Geometry geometry,
{List<MaterialInstance>? materialInstances,
bool keepData = false}) async {
var assetPtr = await withPointerCallback<TSceneAsset>((callback) {
var ptrList = Int64List(materialInstances?.length ?? 0);
if (materialInstances != null && materialInstances.isNotEmpty) {
ptrList.setRange(
0,
materialInstances.length,
materialInstances
.cast<FFIMaterialInstance>()
.map((mi) => mi.pointer.address)
.toList());
}
return SceneAsset_createGeometryRenderThread(
app.engine,
geometry.vertices.address,
geometry.vertices.length,
geometry.normals.address,
geometry.normals.length,
geometry.uvs.address,
geometry.uvs.length,
geometry.indices.address,
geometry.indices.length,
geometry.primitiveType.index,
ptrList.address.cast<Pointer<TMaterialInstance>>(),
ptrList.length,
callback);
});
if (assetPtr == nullptr) {
throw Exception("Failed to create geometry");
}
var asset = FFIAsset(assetPtr, app, animationManager);
return asset;
}
////
///
//
@override @override
Future<GizmoAsset> createGizmo(FFIView view, GizmoType gizmoType) async { Future<GizmoAsset> createGizmo(FFIView view, GizmoType gizmoType) async {
throw UnimplementedError(); throw UnimplementedError();

View File

@@ -1,4 +1,5 @@
import 'package:thermion_dart/src/filament/src/filament_app.dart'; import 'package:thermion_dart/src/filament/src/light_options.dart';
import '../../filament/src/shared_types.dart'; import '../../filament/src/shared_types.dart';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:vector_math/vector_math_64.dart'; import 'package:vector_math/vector_math_64.dart';
@@ -12,10 +13,13 @@ import 'dart:async';
/// Multiple instances can be created; each will correspond /// Multiple instances can be created; each will correspond
/// broadly to a single Filament Scene/View. /// broadly to a single Filament Scene/View.
/// ///
/// If you know yhat you are doing, you can use a lower level interface by /// If you know yhat you are doing, you can use a lower level interface by
/// using the methods directly via FilamentApp.instance; /// using the methods directly via FilamentApp.instance;
/// ///
abstract class ThermionViewer { abstract class ThermionViewer {
Future<bool> get initialized;
/// ///
/// ///
/// ///
@@ -142,7 +146,7 @@ abstract class ThermionViewer {
bool keepData = false, bool keepData = false,
int priority = 4, int priority = 4,
int layer = 0, int layer = 0,
bool loadResourcesAsync}); bool loadResourcesAsync = false});
/// ///
/// Load the .gltf asset at the given path, adding all entities to the scene. /// Load the .gltf asset at the given path, adding all entities to the scene.
@@ -225,32 +229,17 @@ abstract class ThermionViewer {
/// ///
Future pick(int x, int y, void Function(PickResult) resultHandler); Future pick(int x, int y, void Function(PickResult) resultHandler);
///
/// Retrieves the name assigned to the given ThermionEntity (usually corresponds to the glTF mesh name).
///
String? getNameForEntity(ThermionEntity entity);
///
/// Gets the parent entity of [entity]. Returns null if the entity has no parent.
///
Future<ThermionEntity?> getParent(ThermionEntity entity);
///
/// Gets the ancestor (ultimate parent) entity of [entity]. Returns null if the entity has no parent.
///
Future<ThermionEntity?> getAncestor(ThermionEntity entity);
///
/// Sets the parent transform of [child] to [parent].
///
Future setParent(ThermionEntity child, ThermionEntity? parent,
{bool preserveScaling});
/// ///
/// Sets the draw priority for the given entity. See RenderableManager.h for more details. /// Sets the draw priority for the given entity. See RenderableManager.h for more details.
/// ///
Future setPriority(ThermionEntity entityId, int priority); Future setPriority(ThermionEntity entityId, int priority);
///
///
///
Future<ThermionAsset> createGeometry(Geometry geometry,
{List<MaterialInstance>? materialInstances, bool keepData = false});
/// ///
/// The gizmo for translating/rotating objects. Only one gizmo can be active for a given view. /// The gizmo for translating/rotating objects. Only one gizmo can be active for a given view.
/// ///

View File

@@ -1,33 +1,14 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:thermion_dart/src/filament/src/light_options.dart';
import 'package:animation_tools_dart/src/bone_animation_data.dart';
import 'package:animation_tools_dart/src/morph_animation_data.dart';
import 'package:thermion_dart/thermion_dart.dart'; import 'package:thermion_dart/thermion_dart.dart';
import 'package:vector_math/vector_math_64.dart';
class ThermionViewerStub extends ThermionViewer { class ThermionViewerStub extends ThermionViewer {
@override
Future addAnimationComponent(ThermionEntity entity) {
// TODO: implement addAnimationComponent
throw UnimplementedError();
}
@override
Future addBoneAnimation(ThermionAsset asset, BoneAnimationData animation, {int skinIndex = 0, double fadeInInSecs = 0.0, double fadeOutInSecs = 0.0, double maxDelta = 1.0}) {
// TODO: implement addBoneAnimation
throw UnimplementedError();
}
@override @override
Future<ThermionEntity> addDirectLight(DirectLight light) { Future<ThermionEntity> addDirectLight(DirectLight light) {
// TODO: implement addDirectLight // TODO: implement addDirectLight
throw UnimplementedError(); throw UnimplementedError();
} }
@override
// TODO: implement app
FilamentApp get app => throw UnimplementedError();
@override @override
Future clearBackgroundImage() { Future clearBackgroundImage() {
// TODO: implement clearBackgroundImage // TODO: implement clearBackgroundImage
@@ -35,14 +16,14 @@ class ThermionViewerStub extends ThermionViewer {
} }
@override @override
Future clearMorphAnimationData(ThermionEntity entity) { Future<Camera> createCamera() {
// TODO: implement clearMorphAnimationData // TODO: implement createCamera
throw UnimplementedError(); throw UnimplementedError();
} }
@override @override
Future<Camera> createCamera() { Future<ThermionAsset> createGeometry(Geometry geometry, {List<MaterialInstance>? materialInstances, bool keepData = false}) {
// TODO: implement createCamera // TODO: implement createGeometry
throw UnimplementedError(); throw UnimplementedError();
} }
@@ -88,72 +69,12 @@ class ThermionViewerStub extends ThermionViewer {
throw UnimplementedError(); throw UnimplementedError();
} }
@override
Future<ThermionEntity?> getAncestor(ThermionEntity entity) {
// TODO: implement getAncestor
throw UnimplementedError();
}
@override
Future<double> getAnimationDuration(covariant ThermionAsset asset, int animationIndex) {
// TODO: implement getAnimationDuration
throw UnimplementedError();
}
@override
Future<List<String>> getAnimationNames(covariant ThermionAsset asset) {
// TODO: implement getAnimationNames
throw UnimplementedError();
}
@override
Future<ThermionEntity> getBone(covariant ThermionAsset asset, int boneIndex, {int skinIndex = 0}) {
// TODO: implement getBone
throw UnimplementedError();
}
@override
Future<List<String>> getBoneNames(covariant ThermionAsset asset, {int skinIndex = 0}) {
// TODO: implement getBoneNames
throw UnimplementedError();
}
@override @override
int getCameraCount() { int getCameraCount() {
// TODO: implement getCameraCount // TODO: implement getCameraCount
throw UnimplementedError(); throw UnimplementedError();
} }
@override
Future<Matrix4> getInverseBindMatrix(covariant ThermionAsset asset, int boneIndex, {int skinIndex = 0}) {
// TODO: implement getInverseBindMatrix
throw UnimplementedError();
}
@override
Future<Matrix4> getLocalTransform(ThermionEntity entity) {
// TODO: implement getLocalTransform
throw UnimplementedError();
}
@override
Future<List<String>> getMorphTargetNames(covariant ThermionAsset asset, ThermionEntity childEntity) {
// TODO: implement getMorphTargetNames
throw UnimplementedError();
}
@override
String? getNameForEntity(ThermionEntity entity) {
// TODO: implement getNameForEntity
throw UnimplementedError();
}
@override
Future<ThermionEntity?> getParent(ThermionEntity entity) {
// TODO: implement getParent
throw UnimplementedError();
}
@override @override
Future<Aabb3> getRenderableBoundingBox(ThermionEntity entity) { Future<Aabb3> getRenderableBoundingBox(ThermionEntity entity) {
// TODO: implement getRenderableBoundingBox // TODO: implement getRenderableBoundingBox
@@ -167,10 +88,8 @@ class ThermionViewerStub extends ThermionViewer {
} }
@override @override
Future<Matrix4> getWorldTransform(ThermionEntity entity) { // TODO: implement initialized
// TODO: implement getWorldTransform Future<bool> get initialized => throw UnimplementedError();
throw UnimplementedError();
}
@override @override
Future<ThermionAsset> loadGlb(String path, {int numInstances = 1, bool keepData = false}) { Future<ThermionAsset> loadGlb(String path, {int numInstances = 1, bool keepData = false}) {
@@ -217,30 +136,6 @@ class ThermionViewerStub extends ThermionViewer {
throw UnimplementedError(); throw UnimplementedError();
} }
@override
Future playAnimation(ThermionAsset asset, int index, {bool loop = false, bool reverse = false, bool replaceActive = true, double crossfade = 0.0, double startOffset = 0.0}) {
// TODO: implement playAnimation
throw UnimplementedError();
}
@override
Future playAnimationByName(covariant ThermionAsset asset, String name, {bool loop = false, bool reverse = false, bool replaceActive = true, double crossfade = 0.0}) {
// TODO: implement playAnimationByName
throw UnimplementedError();
}
@override
Future registerRequestFrameHook(Future Function() hook) {
// TODO: implement registerRequestFrameHook
throw UnimplementedError();
}
@override
Future removeAnimationComponent(ThermionEntity entity) {
// TODO: implement removeAnimationComponent
throw UnimplementedError();
}
@override @override
Future removeGridOverlay() { Future removeGridOverlay() {
// TODO: implement removeGridOverlay // TODO: implement removeGridOverlay
@@ -275,18 +170,6 @@ class ThermionViewerStub extends ThermionViewer {
// TODO: implement rendering // TODO: implement rendering
bool get rendering => throw UnimplementedError(); bool get rendering => throw UnimplementedError();
@override
Future requestFrame() {
// TODO: implement requestFrame
throw UnimplementedError();
}
@override
Future resetBones(ThermionAsset asset) {
// TODO: implement resetBones
throw UnimplementedError();
}
@override @override
Future rotateIbl(Matrix3 rotation) { Future rotateIbl(Matrix3 rotation) {
// TODO: implement rotateIbl // TODO: implement rotateIbl
@@ -329,30 +212,12 @@ class ThermionViewerStub extends ThermionViewer {
throw UnimplementedError(); throw UnimplementedError();
} }
@override
Future setBoneTransform(ThermionEntity entity, int boneIndex, Matrix4 transform, {int skinIndex = 0}) {
// TODO: implement setBoneTransform
throw UnimplementedError();
}
@override
Future setCamera(ThermionEntity entity, String? name) {
// TODO: implement setCamera
throw UnimplementedError();
}
@override @override
Future setFrameRate(int framerate) { Future setFrameRate(int framerate) {
// TODO: implement setFrameRate // TODO: implement setFrameRate
throw UnimplementedError(); throw UnimplementedError();
} }
@override
Future setGltfAnimationFrame(covariant ThermionAsset asset, int index, int animationFrame) {
// TODO: implement setGltfAnimationFrame
throw UnimplementedError();
}
@override @override
Future setLightDirection(ThermionEntity lightEntity, Vector3 direction) { Future setLightDirection(ThermionEntity lightEntity, Vector3 direction) {
// TODO: implement setLightDirection // TODO: implement setLightDirection
@@ -365,24 +230,6 @@ class ThermionViewerStub extends ThermionViewer {
throw UnimplementedError(); throw UnimplementedError();
} }
@override
Future setMorphAnimationData(covariant ThermionAsset asset, MorphAnimationData animation, {List<String>? targetMeshNames}) {
// TODO: implement setMorphAnimationData
throw UnimplementedError();
}
@override
Future setMorphTargetWeights(ThermionEntity entity, List<double> weights) {
// TODO: implement setMorphTargetWeights
throw UnimplementedError();
}
@override
Future setParent(ThermionEntity child, ThermionEntity? parent, {bool preserveScaling= false}) {
// TODO: implement setParent
throw UnimplementedError();
}
@override @override
Future setPostProcessing(bool enabled) { Future setPostProcessing(bool enabled) {
// TODO: implement setPostProcessing // TODO: implement setPostProcessing
@@ -425,12 +272,6 @@ class ThermionViewerStub extends ThermionViewer {
throw UnimplementedError(); throw UnimplementedError();
} }
@override
Future setTransform(ThermionEntity entity, Matrix4 transform) {
// TODO: implement setTransform
throw UnimplementedError();
}
@override @override
Future setViewFrustumCulling(bool enabled) { Future setViewFrustumCulling(bool enabled) {
// TODO: implement setViewFrustumCulling // TODO: implement setViewFrustumCulling
@@ -443,32 +284,8 @@ class ThermionViewerStub extends ThermionViewer {
throw UnimplementedError(); throw UnimplementedError();
} }
@override
Future stopAnimation(covariant ThermionAsset asset, int animationIndex) {
// TODO: implement stopAnimation
throw UnimplementedError();
}
@override
Future stopAnimationByName(covariant ThermionAsset asset, String name) {
// TODO: implement stopAnimationByName
throw UnimplementedError();
}
@override
Future unregisterRequestFrameHook(Future Function() hook) {
// TODO: implement unregisterRequestFrameHook
throw UnimplementedError();
}
@override
Future updateBoneMatrices(ThermionEntity entity) {
// TODO: implement updateBoneMatrices
throw UnimplementedError();
}
@override @override
// TODO: implement view // TODO: implement view
View get view => throw UnimplementedError(); View get view => throw UnimplementedError();
} }