diff --git a/thermion_dart/lib/src/filament/filament.dart b/thermion_dart/lib/src/filament/filament.dart new file mode 100644 index 00000000..cda760f9 --- /dev/null +++ b/thermion_dart/lib/src/filament/filament.dart @@ -0,0 +1,2 @@ +export 'src/filament_app.dart'; +export 'src/engine.dart'; diff --git a/thermion_dart/lib/src/filament/src/asset.dart b/thermion_dart/lib/src/filament/src/asset.dart new file mode 100644 index 00000000..4345de5b --- /dev/null +++ b/thermion_dart/lib/src/filament/src/asset.dart @@ -0,0 +1,267 @@ +library; + +import 'package:animation_tools_dart/animation_tools_dart.dart'; +import 'package:thermion_dart/src/filament/src/layers.dart'; +import 'package:thermion_dart/thermion_dart.dart'; +import 'package:vector_math/vector_math_64.dart'; +import 'entity.dart'; + +export 'geometry.dart'; +export 'gltf.dart'; + +export 'light_options.dart'; + +abstract class ThermionAsset { + /// + /// + /// + ThermionEntity get entity; + + /// + /// + /// + Future> getChildEntities(); + + /// + /// + /// + Future setMaterialInstanceAt(covariant MaterialInstance instance); + + /// + /// Renders an outline around [entity] with the given color. + /// + Future setStencilHighlight( + {double r = 1.0, double g = 0.0, double b = 0.0, int? entityIndex}); + + /// + /// Removes the outline around [entity]. Noop if there was no highlight. + /// + Future removeStencilHighlight(); + + /// + /// When visible is [true], renders the bounding box. + /// + Future setBoundingBoxVisibility(bool visible); + + /// + /// + /// + Future getInstance(int index); + + /// + /// Create a new instance of [entity]. + /// Instances are not automatically added to the scene; you must + /// call [addToScene]. + /// + Future createInstance( + {covariant List? materialInstances = null}); + + /// + /// Returns the number of instances associated with this asset. + /// + Future getInstanceCount(); + + /// + /// Returns all instances of associated with this asset. + /// + Future> getInstances(); + + /// + /// + /// + Future setCastShadows(bool castShadows); + + /// + /// + /// + Future setReceiveShadows(bool castShadows); + + /// + /// All renderable entities are assigned a layer mask. + /// + /// By calling [setLayerVisibility], all renderable entities allocated to + /// the given layer can be efficiently hidden/revealed. + /// + /// By default, all renderable entities are assigned to layer 0 (and this + /// layer is enabled by default). Call [setVisibilityLayer] to change the + /// layer for the specified entity. + /// + /// Note that we currently also assign gizmos to layer 1 (enabled by default) + /// and the world grid to layer 2 (disabled by default). We suggest you avoid + /// using these layers. + /// + Future setVisibilityLayer(ThermionEntity entity, VisibilityLayers layer); + + /// + /// Schedules the glTF animation at [index] in [asset] to start playing on the next frame. + /// + Future playAnimation(ThermionAsset asset, int index, + {bool loop = false, + bool reverse = false, + bool replaceActive = true, + double crossfade = 0.0, + double startOffset = 0.0}); + + /// + /// Schedules the glTF animation at [index] in [entity] to start playing on the next frame. + /// + Future playAnimationByName(covariant ThermionAsset asset, String name, + {bool loop = false, + bool reverse = false, + bool replaceActive = true, + double crossfade = 0.0}); + + /// + /// + /// + Future setGltfAnimationFrame( + covariant ThermionAsset asset, int index, int animationFrame); + + /// + /// + /// + Future stopAnimation(covariant ThermionAsset asset, int animationIndex); + + /// + /// + /// + Future stopAnimationByName(covariant ThermionAsset asset, String name); + + /// + /// Set the weights for all morph targets in [entity] to [weights]. + /// Note that [weights] must contain values for ALL morph targets, but no exception will be thrown if you don't do so (you'll just get incorrect results). + /// If you only want to set one value, set all others to zero (check [getMorphTargetNames] if you need the get a list of all morph targets). + /// IMPORTANT - this accepts the actual ThermionEntity with the relevant morph targets (unlike [getMorphTargetNames], which uses the parent entity and the child mesh name). + /// Use [getChildEntityByName] if you are setting the weights for a child mesh. + /// + Future setMorphTargetWeights(ThermionEntity entity, List weights); + + /// + /// Gets the names of all morph targets for the child renderable [childEntity] under [entity]. + /// + Future> getMorphTargetNames( + covariant ThermionAsset asset, ThermionEntity childEntity); + + /// + /// Gets the names of all bones for the armature at [skinIndex] under the specified [entity]. + /// + Future> getBoneNames(covariant ThermionAsset asset, + {int skinIndex = 0}); + + /// + /// Gets the names of all glTF animations embedded in the specified entity. + /// + Future> getAnimationNames(covariant ThermionAsset asset); + + /// + /// Returns the length (in seconds) of the animation at the given index. + /// + Future getAnimationDuration( + 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. + /// [MorphTargetAnimation] for an explanation as to how to construct the animation frame data. + /// This method will check the morph target names specified in [animation] against the morph target names that actually exist exist under [meshName] in [entity], + /// throwing an exception if any cannot be found. + /// It is permissible for [animation] to omit any targets that do exist under [meshName]; these simply won't be animated. + /// + Future setMorphAnimationData( + covariant ThermionAsset asset, MorphAnimationData animation, + {List? targetMeshNames}); + + /// + /// Clear all current morph animations for [entity]. + /// + Future clearMorphAnimationData(ThermionEntity entity); + + /// + /// Resets all bones in the given entity to their rest pose. + /// This should be done before every call to addBoneAnimation. + /// + Future resetBones(ThermionAsset asset); + + /// + /// Enqueues and plays the [animation] for the specified bone(s). + /// By default, frame data is interpreted as being in *parent* bone space; + /// a 45 degree around Y means the bone will rotate 45 degrees around the + /// Y axis of the parent bone *in its current orientation*. + /// (i.e NOT the parent bone's rest position!). + /// Currently, only [Space.ParentBone] and [Space.Model] are supported; if you want + /// to transform to another space, you will need to do so manually. + /// + /// [fadeInInSecs]/[fadeOutInSecs]/[maxDelta] are used to cross-fade between + /// the current active glTF animation ("animation1") and the animation you + /// set via this method ("animation2"). The bone orientations will be + /// linearly interpolated between animation1 and animation2; at time 0, + /// the orientation will be 100% animation1, at time [fadeInInSecs], the + /// animation will be ((1 - maxDelta) * animation1) + (maxDelta * animation2). + /// This will be applied in reverse after [fadeOutInSecs]. + /// + /// + Future addBoneAnimation(ThermionAsset asset, BoneAnimationData animation, + {int skinIndex = 0, + double fadeInInSecs = 0.0, + double fadeOutInSecs = 0.0, + double maxDelta = 1.0}); + + /// + /// Gets the entity representing the bone at [boneIndex]/[skinIndex]. + /// The returned entity is only intended for use with [getWorldTransform]. + /// + Future getBone(covariant ThermionAsset asset, int boneIndex, + {int skinIndex = 0}); + + /// + /// Gets the local (relative to parent) transform for [entity]. + /// + Future getLocalTransform(ThermionEntity entity); + + /// + /// Gets the world transform for [entity]. + /// + Future getWorldTransform(ThermionEntity entity); + + /// + /// Gets the inverse bind (pose) matrix for the bone. + /// Note that [parent] must be the ThermionEntity returned by [loadGlb/loadGltf], not any other method ([getChildEntity] etc). + /// This is because all joint information is internally stored with the parent entity. + /// + Future getInverseBindMatrix( + covariant ThermionAsset asset, int boneIndex, + {int skinIndex = 0}); + + /// + /// Sets the transform (relative to its parent) for [entity]. + /// + Future setTransform(ThermionEntity entity, Matrix4 transform); + + /// + /// Updates the bone matrices for [entity] (which must be the ThermionEntity + /// returned by [loadGlb/loadGltf]). + /// Under the hood, this just calls [updateBoneMatrices] on the Animator + /// instance of the relevant FilamentInstance (which uses the local + /// bone transform and the inverse bind matrix to set the bone matrix). + /// + Future updateBoneMatrices(ThermionEntity entity); + + /// + /// Directly set the bone matrix for the bone at the given index. + /// Don't call this manually unless you know what you're doing. + /// + Future setBoneTransform( + ThermionEntity entity, int boneIndex, Matrix4 transform, + {int skinIndex = 0}); + + /// + /// An [entity] will only be animatable after an animation component is attached. + /// Any calls to [playAnimation]/[setBoneAnimation]/[setMorphAnimation] will have no visual effect until [addAnimationComponent] has been called on the instance. + /// + Future addAnimationComponent(ThermionEntity entity); + + /// + /// Removes an animation component from [entity]. + /// + Future removeAnimationComponent(ThermionEntity entity); +} + diff --git a/thermion_dart/lib/src/viewer/src/shared_types/camera.dart b/thermion_dart/lib/src/filament/src/camera.dart similarity index 76% rename from thermion_dart/lib/src/viewer/src/shared_types/camera.dart rename to thermion_dart/lib/src/filament/src/camera.dart index ff0a834e..225b7427 100644 --- a/thermion_dart/lib/src/viewer/src/shared_types/camera.dart +++ b/thermion_dart/lib/src/filament/src/camera.dart @@ -1,10 +1,10 @@ -import 'package:thermion_dart/src/viewer/src/shared_types/layers.dart'; -import 'package:vector_math/vector_math_64.dart'; -import '../thermion_viewer_base.dart'; +import 'package:thermion_dart/src/filament/src/layers.dart'; +import 'package:thermion_dart/thermion_dart.dart'; enum Projection { Perspective, Orthographic } abstract class Camera { + Future lookAt(Vector3 position, {Vector3? focus, Vector3? up}) async { focus ??= Vector3.zero(); up ??= Vector3(0, 1, 0); @@ -13,6 +13,12 @@ abstract class Camera { await setModelMatrix(viewMatrix); } + /// + /// Sets the camera exposure. + /// + Future setCameraExposure( + double aperture, double shutterSpeed, double sensitivity); + Future setProjection(Projection projection, double left, double right, double bottom, double top, double near, double far); Future setProjectionMatrixWithCulling( @@ -37,6 +43,8 @@ abstract class Camera { Future getNear(); Future getCullingFar(); Future getFocalLength(); + Future getFocusDistance(); + Future setFocusDistance(double focusDistance); Future destroy(); } diff --git a/thermion_dart/lib/src/viewer/src/shared_types/engine.dart b/thermion_dart/lib/src/filament/src/engine.dart similarity index 100% rename from thermion_dart/lib/src/viewer/src/shared_types/engine.dart rename to thermion_dart/lib/src/filament/src/engine.dart diff --git a/thermion_dart/lib/src/filament/src/entity.dart b/thermion_dart/lib/src/filament/src/entity.dart new file mode 100644 index 00000000..44134486 --- /dev/null +++ b/thermion_dart/lib/src/filament/src/entity.dart @@ -0,0 +1,2 @@ +typedef ThermionEntity = int; +final ThermionEntity FILAMENT_ENTITY_NULL = 0; \ No newline at end of file diff --git a/thermion_dart/lib/src/filament/src/filament_app.dart b/thermion_dart/lib/src/filament/src/filament_app.dart new file mode 100644 index 00000000..1ae80081 --- /dev/null +++ b/thermion_dart/lib/src/filament/src/filament_app.dart @@ -0,0 +1,209 @@ +import 'dart:typed_data'; + +import 'package:thermion_dart/src/filament/src/engine.dart'; +import 'package:thermion_dart/thermion_dart.dart'; + +class FilamentConfig { + final Backend backend; + final T? renderCallback; + final U? renderCallbackOwner; + final U resourceLoader; + final U? platform; + final U? driver; + final U? sharedContext; + final String? uberArchivePath; + final int stereoscopicEyeCount; + final bool disableHandleUseAfterFreeCheck; + + FilamentConfig( + {required this.backend, + required this.resourceLoader, + this.uberArchivePath, + this.renderCallback, + this.renderCallbackOwner, + this.platform, + this.driver, + this.sharedContext, + this.stereoscopicEyeCount = 1, + this.disableHandleUseAfterFreeCheck = false}); +} + +abstract class FilamentApp { + + static FilamentApp? instance; + + final T engine; + final T gltfAssetLoader; + final T gltfResourceLoader; + final T renderer; + final T transformManager; + final T lightManager; + final T renderableManager; + final T ubershaderMaterialProvider; + + FilamentApp( + {required this.engine, + required this.gltfAssetLoader, + required this.gltfResourceLoader, + required this.renderer, + required this.transformManager, + required this.lightManager, + required this.renderableManager, + required this.ubershaderMaterialProvider}); + + /// + /// + /// + Future createHeadlessSwapChain(int width, int height, + {bool hasStencilBuffer = false}); + + /// + /// + /// + Future createSwapChain(T handle, {bool hasStencilBuffer = false}); + + /// + /// + /// + Future destroySwapChain(SwapChain swapChain); + + /// + /// + /// + Future destroy(); + + /// + /// + /// + Future createRenderTarget(int width, int height, + {covariant Texture? color, covariant Texture? depth}); + + /// + /// + /// + Future createTexture(int width, int height, + {int depth = 1, + int levels = 1, + Set flags = const {TextureUsage.TEXTURE_USAGE_SAMPLEABLE}, + TextureSamplerType textureSamplerType = TextureSamplerType.SAMPLER_2D, + TextureFormat textureFormat = TextureFormat.RGBA16F, + int? importedTextureHandle}); + + /// + /// + /// + Future createTextureSampler( + {TextureMinFilter minFilter = TextureMinFilter.LINEAR, + TextureMagFilter magFilter = TextureMagFilter.LINEAR, + TextureWrapMode wrapS = TextureWrapMode.CLAMP_TO_EDGE, + TextureWrapMode wrapT = TextureWrapMode.CLAMP_TO_EDGE, + TextureWrapMode wrapR = TextureWrapMode.CLAMP_TO_EDGE, + double anisotropy = 0.0, + TextureCompareMode compareMode = TextureCompareMode.NONE, + TextureCompareFunc compareFunc = TextureCompareFunc.LESS_EQUAL}); + + /// + /// Decodes the specified image data. + /// + Future decodeImage(Uint8List data); + + /// + /// Creates an (empty) imge with the given dimensions. + /// + Future createImage(int width, int height, int channels); + + /// + /// + /// + Future createMaterial(Uint8List data); + + /// + /// + /// + Future createUbershaderMaterialInstance({ + bool doubleSided = false, + bool unlit = false, + bool hasVertexColors = false, + bool hasBaseColorTexture = false, + bool hasNormalTexture = false, + bool hasOcclusionTexture = false, + bool hasEmissiveTexture = false, + bool useSpecularGlossiness = false, + AlphaMode alphaMode = AlphaMode.OPAQUE, + bool enableDiagnostics = false, + bool hasMetallicRoughnessTexture = false, + int metallicRoughnessUV = -1, + int baseColorUV = -1, + bool hasClearCoatTexture = false, + int clearCoatUV = -1, + bool hasClearCoatRoughnessTexture = false, + int clearCoatRoughnessUV = -1, + bool hasClearCoatNormalTexture = false, + int clearCoatNormalUV = -1, + bool hasClearCoat = false, + bool hasTransmission = false, + bool hasTextureTransforms = false, + int emissiveUV = -1, + int aoUV = -1, + int normalUV = -1, + bool hasTransmissionTexture = false, + int transmissionUV = -1, + bool hasSheenColorTexture = false, + int sheenColorUV = -1, + bool hasSheenRoughnessTexture = false, + int sheenRoughnessUV = -1, + bool hasVolumeThicknessTexture = false, + int volumeThicknessUV = -1, + bool hasSheen = false, + bool hasIOR = false, + bool hasVolume = false, + }); + + /// + /// + /// + Future createUnlitMaterialInstance(); + + /// + /// + /// + Future getMaterialInstanceAt( + ThermionEntity entity, int index); + + /// + /// + /// + @override + Future createGeometry(Geometry geometry, + {List? materialInstances, bool keepData = false}); + + /// + /// + /// + Future setRenderable(covariant View view, bool renderable); + + /// + /// + /// + Future register(covariant SwapChain swapChain, covariant View view); + + /// + /// + /// + Future render(); + + /// + /// + /// + Future requestFrame(); + + /// + /// + /// + Future registerRequestFrameHook(Future Function() hook); + + /// + /// + /// + Future unregisterRequestFrameHook(Future Function() hook); +} diff --git a/thermion_dart/lib/src/viewer/src/shared_types/geometry.dart b/thermion_dart/lib/src/filament/src/geometry.dart similarity index 96% rename from thermion_dart/lib/src/viewer/src/shared_types/geometry.dart rename to thermion_dart/lib/src/filament/src/geometry.dart index 05c2fb26..554d8dad 100644 --- a/thermion_dart/lib/src/viewer/src/shared_types/geometry.dart +++ b/thermion_dart/lib/src/filament/src/geometry.dart @@ -1,6 +1,6 @@ import 'dart:typed_data'; -import '../../viewer.dart'; +import '../../viewer/viewer.dart'; class Geometry { final Float32List vertices; diff --git a/thermion_dart/lib/src/filament/src/gizmo.dart b/thermion_dart/lib/src/filament/src/gizmo.dart new file mode 100644 index 00000000..e107712e --- /dev/null +++ b/thermion_dart/lib/src/filament/src/gizmo.dart @@ -0,0 +1,28 @@ +import 'entity.dart'; +import 'package:thermion_dart/thermion_dart.dart'; +import 'package:vector_math/vector_math_64.dart'; + +enum Axis { + X(const [1.0, 0.0, 0.0]), + Y(const [0.0, 1.0, 0.0]), + Z(const [0.0, 0.0, 1.0]); + + const Axis(this.vector); + + final List vector; + + Vector3 asVector() => Vector3(vector[0], vector[1], vector[2]); +} + +enum GizmoPickResultType { AxisX, AxisY, AxisZ, Parent, None } + +enum GizmoType { translation, rotation } + +abstract class GizmoAsset extends ThermionAsset { + Future pick(int x, int y, + {Future Function(GizmoPickResultType axis, Vector3 coords)? handler}); + Future highlight(Axis axis); + Future unhighlight(); + bool isNonPickable(ThermionEntity entity); + bool isGizmoEntity(ThermionEntity entity); +} diff --git a/thermion_dart/lib/src/viewer/src/shared_types/gltf.dart b/thermion_dart/lib/src/filament/src/gltf.dart similarity index 100% rename from thermion_dart/lib/src/viewer/src/shared_types/gltf.dart rename to thermion_dart/lib/src/filament/src/gltf.dart diff --git a/thermion_dart/lib/src/viewer/src/shared_types/config.dart b/thermion_dart/lib/src/filament/src/grid_overlay.dart similarity index 100% rename from thermion_dart/lib/src/viewer/src/shared_types/config.dart rename to thermion_dart/lib/src/filament/src/grid_overlay.dart diff --git a/thermion_dart/lib/src/viewer/src/shared_types/layers.dart b/thermion_dart/lib/src/filament/src/layers.dart similarity index 100% rename from thermion_dart/lib/src/viewer/src/shared_types/layers.dart rename to thermion_dart/lib/src/filament/src/layers.dart diff --git a/thermion_dart/lib/src/viewer/src/shared_types/light.dart b/thermion_dart/lib/src/filament/src/light.dart similarity index 100% rename from thermion_dart/lib/src/viewer/src/shared_types/light.dart rename to thermion_dart/lib/src/filament/src/light.dart diff --git a/thermion_dart/lib/src/viewer/src/shared_types/light_options.dart b/thermion_dart/lib/src/filament/src/light_options.dart similarity index 100% rename from thermion_dart/lib/src/viewer/src/shared_types/light_options.dart rename to thermion_dart/lib/src/filament/src/light_options.dart diff --git a/thermion_dart/lib/src/viewer/src/shared_types/manipulator.dart b/thermion_dart/lib/src/filament/src/manipulator.dart similarity index 100% rename from thermion_dart/lib/src/viewer/src/shared_types/manipulator.dart rename to thermion_dart/lib/src/filament/src/manipulator.dart diff --git a/thermion_dart/lib/src/viewer/src/shared_types/material.dart b/thermion_dart/lib/src/filament/src/material.dart similarity index 100% rename from thermion_dart/lib/src/viewer/src/shared_types/material.dart rename to thermion_dart/lib/src/filament/src/material.dart diff --git a/thermion_dart/lib/src/viewer/src/shared_types/pick_result.dart b/thermion_dart/lib/src/filament/src/pick_result.dart similarity index 93% rename from thermion_dart/lib/src/viewer/src/shared_types/pick_result.dart rename to thermion_dart/lib/src/filament/src/pick_result.dart index 5f4cd380..2615be4b 100644 --- a/thermion_dart/lib/src/viewer/src/shared_types/pick_result.dart +++ b/thermion_dart/lib/src/filament/src/pick_result.dart @@ -1,4 +1,4 @@ -import '../../viewer.dart'; +import '../../viewer/viewer.dart'; /// The result of a picking operation (see [ThermionViewer.pick] for more details). /// [x] and [y] refer to the original screen coordinates used to call [pick]; this should diff --git a/thermion_dart/lib/src/viewer/src/shared_types/primitive.dart b/thermion_dart/lib/src/filament/src/primitive.dart similarity index 100% rename from thermion_dart/lib/src/viewer/src/shared_types/primitive.dart rename to thermion_dart/lib/src/filament/src/primitive.dart diff --git a/thermion_dart/lib/src/viewer/src/shared_types/render_target.dart b/thermion_dart/lib/src/filament/src/render_target.dart similarity index 100% rename from thermion_dart/lib/src/viewer/src/shared_types/render_target.dart rename to thermion_dart/lib/src/filament/src/render_target.dart diff --git a/thermion_dart/lib/src/viewer/src/shared_types/scene.dart b/thermion_dart/lib/src/filament/src/scene.dart similarity index 61% rename from thermion_dart/lib/src/viewer/src/shared_types/scene.dart rename to thermion_dart/lib/src/filament/src/scene.dart index 8ddb534c..b6b5bf57 100644 --- a/thermion_dart/lib/src/viewer/src/shared_types/scene.dart +++ b/thermion_dart/lib/src/filament/src/scene.dart @@ -1,4 +1,4 @@ -import 'package:thermion_dart/src/viewer/src/thermion_viewer_base.dart'; +import 'package:thermion_dart/thermion_dart.dart'; abstract class Scene { Future add(covariant ThermionAsset asset); diff --git a/thermion_dart/lib/src/viewer/src/shared_types/shadow.dart b/thermion_dart/lib/src/filament/src/shadow.dart similarity index 100% rename from thermion_dart/lib/src/viewer/src/shared_types/shadow.dart rename to thermion_dart/lib/src/filament/src/shadow.dart diff --git a/thermion_dart/lib/src/viewer/src/shared_types/shared_types.dart b/thermion_dart/lib/src/filament/src/shared_types.dart similarity index 84% rename from thermion_dart/lib/src/viewer/src/shared_types/shared_types.dart rename to thermion_dart/lib/src/filament/src/shared_types.dart index 80480a67..4cf0178b 100644 --- a/thermion_dart/lib/src/viewer/src/shared_types/shared_types.dart +++ b/thermion_dart/lib/src/filament/src/shared_types.dart @@ -6,7 +6,9 @@ export 'render_target.dart'; export 'camera.dart'; export 'material.dart'; export 'texture.dart'; -export 'entities.dart'; +export 'asset.dart'; +export 'entity.dart'; +export 'gizmo.dart'; export 'light.dart'; export 'shadow.dart'; export 'manipulator.dart'; diff --git a/thermion_dart/lib/src/viewer/src/shared_types/swap_chain.dart b/thermion_dart/lib/src/filament/src/swap_chain.dart similarity index 100% rename from thermion_dart/lib/src/viewer/src/shared_types/swap_chain.dart rename to thermion_dart/lib/src/filament/src/swap_chain.dart diff --git a/thermion_dart/lib/src/viewer/src/shared_types/texture.dart b/thermion_dart/lib/src/filament/src/texture.dart similarity index 100% rename from thermion_dart/lib/src/viewer/src/shared_types/texture.dart rename to thermion_dart/lib/src/filament/src/texture.dart diff --git a/thermion_dart/lib/src/viewer/src/shared_types/texture_details.dart b/thermion_dart/lib/src/filament/src/texture_details.dart similarity index 100% rename from thermion_dart/lib/src/viewer/src/shared_types/texture_details.dart rename to thermion_dart/lib/src/filament/src/texture_details.dart diff --git a/thermion_dart/lib/src/viewer/src/shared_types/tone_mapper.dart b/thermion_dart/lib/src/filament/src/tone_mapper.dart similarity index 100% rename from thermion_dart/lib/src/viewer/src/shared_types/tone_mapper.dart rename to thermion_dart/lib/src/filament/src/tone_mapper.dart diff --git a/thermion_dart/lib/src/viewer/src/shared_types/view.dart b/thermion_dart/lib/src/filament/src/view.dart similarity index 89% rename from thermion_dart/lib/src/viewer/src/shared_types/view.dart rename to thermion_dart/lib/src/filament/src/view.dart index 7b37e475..e34bef75 100644 --- a/thermion_dart/lib/src/viewer/src/shared_types/view.dart +++ b/thermion_dart/lib/src/filament/src/view.dart @@ -1,4 +1,4 @@ -import 'package:thermion_dart/src/viewer/src/shared_types/layers.dart'; +import 'package:thermion_dart/src/filament/src/layers.dart'; import 'package:thermion_dart/thermion_dart.dart'; /// @@ -26,7 +26,6 @@ abstract class View { Future getCamera(); Future setPostProcessing(bool enabled); Future setAntiAliasing(bool msaa, bool fxaa, bool taa); - Future setRenderable(bool renderable, covariant SwapChain swapChain); Future setFrustumCullingEnabled(bool enabled); Future setToneMapper(ToneMapper mapper); Future setStencilBufferEnabled(bool enabled); 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 c1d49256..9b631cc5 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 @@ -1,6 +1,6 @@ import 'dart:async'; import 'package:vector_math/vector_math_64.dart'; -import '../../../viewer/src/shared_types/camera.dart'; +import '../../../filament/src/camera.dart'; import '../../../viewer/viewer.dart'; import '../../input.dart'; import '../input_handler.dart'; diff --git a/thermion_dart/lib/src/utils/src/geometry.dart b/thermion_dart/lib/src/utils/src/geometry.dart index 2ddaedc9..482e0970 100644 --- a/thermion_dart/lib/src/utils/src/geometry.dart +++ b/thermion_dart/lib/src/utils/src/geometry.dart @@ -6,6 +6,13 @@ import 'package:vector_math/vector_math_64.dart'; import '../../../thermion_dart.dart'; class GeometryHelper { + static Geometry fullscreenQuad() { + final vertices = Float32List.fromList( + [-1.0, -1.0, 1.0, 1.0, 3.0, -1.0, 1.0, 1.0, -1.0, 3.0, 1.0, 1.0]); + final indices = [0, 1, 2]; + return Geometry(vertices, indices); + } + static Geometry sphere({bool normals = true, bool uvs = true}) { int latitudeBands = 20; int longitudeBands = 20; @@ -459,13 +466,18 @@ class GeometryHelper { ]) : null; - Float32List? _uvs = - uvs ? Float32List.fromList([ - 0, 0, - 1, 0, - 1, 1, - 0, 1, - ]) : null; + Float32List? _uvs = uvs + ? Float32List.fromList([ + 0, + 0, + 1, + 0, + 1, + 1, + 0, + 1, + ]) + : null; List indices = [ 0, diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/background_image.dart b/thermion_dart/lib/src/viewer/src/ffi/src/background_image.dart new file mode 100644 index 00000000..92ffb695 --- /dev/null +++ b/thermion_dart/lib/src/viewer/src/ffi/src/background_image.dart @@ -0,0 +1,262 @@ +import 'dart:typed_data'; + +import 'package:animation_tools_dart/src/bone_animation_data.dart'; +import 'package:animation_tools_dart/src/morph_animation_data.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/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_texture.dart'; +import 'package:thermion_dart/thermion_dart.dart'; + +class BackgroundImage extends ThermionAsset { + final ThermionAsset asset; + ThermionEntity get entity => asset.entity; + + Texture? _backgroundImageTexture; + FFIMaterial? _imageMaterial; + FFITextureSampler? _imageSampler; + + final FFIScene scene; + final FilamentApp app; + + BackgroundImage._(this.asset, this.scene, this.app, + this._backgroundImageTexture, this._imageMaterial, this._imageSampler); + + Future destroy() async { + Scene_removeEntity(scene.scene, entity); + await _backgroundImageTexture!.dispose(); + await _imageSampler!.dispose(); + } + + static Future create( + FFIFilamentApp app, FFIScene scene, Uint8List imageData) async { + final image = await app.decodeImage(imageData); + var backgroundImageTexture = await app.createTexture( + await image.getWidth(), await image.getHeight()); + var imageMaterial = FFIMaterial(Material_createImageMaterial(), app); + var imageSampler = await app.createTextureSampler() as FFITextureSampler; + + var imageMaterialInstance = + await imageMaterial!.createInstance() as FFIMaterialInstance; + await imageMaterialInstance.setParameterTexture( + "image", + backgroundImageTexture as FFITexture, + imageSampler as FFITextureSampler); + var backgroundImage = + await app.createGeometry(GeometryHelper.fullscreenQuad()) as FFIAsset; + backgroundImage.setMaterialInstanceAt(imageMaterialInstance); + await scene.add(backgroundImage); + return BackgroundImage._(backgroundImage, scene, app, + backgroundImageTexture, imageMaterial, imageSampler); + } + + @override + Future createInstance( + {covariant List? materialInstances = null}) { + throw UnimplementedError(); + } + + @override + Future> getChildEntities() async { + return []; + } + + @override + Future getInstance(int index) { + throw UnimplementedError(); + } + + @override + Future getInstanceCount() async { + return 0; + } + + @override + Future> getInstances() async { + return []; + } + + @override + Future removeStencilHighlight() { + // TODO: implement removeStencilHighlight + throw UnimplementedError(); + } + + @override + Future setBoundingBoxVisibility(bool visible) { + // TODO: implement setBoundingBoxVisibility + throw UnimplementedError(); + } + + @override + Future setCastShadows(bool castShadows) { + // TODO: implement setCastShadows + throw UnimplementedError(); + } + + @override + Future setMaterialInstanceAt(covariant MaterialInstance instance) { + // TODO: implement setMaterialInstanceAt + throw UnimplementedError(); + } + + @override + Future setReceiveShadows(bool castShadows) { + // TODO: implement setReceiveShadows + throw UnimplementedError(); + } + + @override + Future setStencilHighlight( + {double r = 1.0, double g = 0.0, double b = 0.0, int? entityIndex}) { + // TODO: implement setStencilHighlight + throw UnimplementedError(); + } + + @override + Future setVisibilityLayer(ThermionEntity entity, VisibilityLayers layer) { + // TODO: implement setVisibilityLayer + throw UnimplementedError(); + } + + @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 + Future clearMorphAnimationData(ThermionEntity entity) { + // TODO: implement clearMorphAnimationData + throw UnimplementedError(); + } + + @override + Future getAnimationDuration(covariant ThermionAsset asset, int animationIndex) { + // TODO: implement getAnimationDuration + throw UnimplementedError(); + } + + @override + Future> getAnimationNames(covariant ThermionAsset asset) { + // TODO: implement getAnimationNames + throw UnimplementedError(); + } + + @override + Future getBone(covariant ThermionAsset asset, int boneIndex, {int skinIndex = 0}) { + // TODO: implement getBone + throw UnimplementedError(); + } + + @override + Future> getBoneNames(covariant ThermionAsset asset, {int skinIndex = 0}) { + // TODO: implement getBoneNames + throw UnimplementedError(); + } + + @override + Future getInverseBindMatrix(covariant ThermionAsset asset, int boneIndex, {int skinIndex = 0}) { + // TODO: implement getInverseBindMatrix + throw UnimplementedError(); + } + + @override + Future getLocalTransform(ThermionEntity entity) { + // TODO: implement getLocalTransform + throw UnimplementedError(); + } + + @override + Future> getMorphTargetNames(covariant ThermionAsset asset, ThermionEntity childEntity) { + // TODO: implement getMorphTargetNames + throw UnimplementedError(); + } + + @override + Future getWorldTransform(ThermionEntity entity) { + // TODO: implement getWorldTransform + 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 removeAnimationComponent(ThermionEntity entity) { + // TODO: implement removeAnimationComponent + throw UnimplementedError(); + } + + @override + Future resetBones(ThermionAsset asset) { + // TODO: implement resetBones + throw UnimplementedError(); + } + + @override + Future setBoneTransform(ThermionEntity entity, int boneIndex, Matrix4 transform, {int skinIndex = 0}) { + // TODO: implement setBoneTransform + throw UnimplementedError(); + } + + @override + Future setGltfAnimationFrame(covariant ThermionAsset asset, int index, int animationFrame) { + // TODO: implement setGltfAnimationFrame + throw UnimplementedError(); + } + + @override + Future setMorphAnimationData(covariant ThermionAsset asset, MorphAnimationData animation, {List? targetMeshNames}) { + // TODO: implement setMorphAnimationData + throw UnimplementedError(); + } + + @override + Future setMorphTargetWeights(ThermionEntity entity, List weights) { + // TODO: implement setMorphTargetWeights + throw UnimplementedError(); + } + + @override + Future setTransform(ThermionEntity entity, Matrix4 transform) { + // TODO: implement setTransform + 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 updateBoneMatrices(ThermionEntity entity) { + // TODO: implement updateBoneMatrices + throw UnimplementedError(); + } +} diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/ffi_asset.dart b/thermion_dart/lib/src/viewer/src/ffi/src/ffi_asset.dart index 5bfefb72..7fec1f28 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/ffi_asset.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/ffi_asset.dart @@ -1,13 +1,17 @@ import 'dart:typed_data'; +import 'package:thermion_dart/src/filament/src/layers.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/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/thermion_viewer_ffi.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'; class FFIAsset extends ThermionAsset { + /// /// /// @@ -389,7 +393,477 @@ class FFIAsset extends ThermionAsset { return RenderableManager_isShadowReceiver(app.renderableManager, entity); } + /// + /// + /// Future transformToUnitCube() async { - SceneAsset_transformToUnitCube(asset); + TransformManager_transformToUnitCube(app.transformManager, entity, SceneAsset_getBoundingBox(asset)); } + + /// + /// + /// + Future setVisibilityLayer( + ThermionEntity entity, VisibilityLayers layer) async { + RenderableManager_setVisibilityLayer( + app.renderableManager, entity, layer.value); + } + + /// + /// + /// + @override + Future setMorphTargetWeights( + ThermionEntity entity, List weights) async { + if (weights.isEmpty) { + throw Exception("Weights must not be empty"); + } + var weightsPtr = allocator(weights.length); + + for (int i = 0; i < weights.length; i++) { + weightsPtr[i] = weights[i]; + } + var success = await withBoolCallback((cb) { + AnimationManager_setMorphTargetWeightsRenderThread( + animationManager, entity, weightsPtr, weights.length, cb); + }); + allocator.free(weightsPtr); + + if (!success) { + throw Exception( + "Failed to set morph target weights, check logs for details"); + } + } + + /// + /// + /// + @override + Future> getMorphTargetNames( + covariant FFIAsset asset, ThermionEntity childEntity) async { + var names = []; + + var count = AnimationManager_getMorphTargetNameCount( + animationManager, asset.asset, childEntity); + var outPtr = allocator(255); + for (int i = 0; i < count; i++) { + AnimationManager_getMorphTargetName( + animationManager, asset.asset, childEntity, outPtr, i); + names.add(outPtr.cast().toDartString()); + } + allocator.free(outPtr); + return names.cast(); + } + + Future> getBoneNames(covariant FFIAsset asset, + {int skinIndex = 0}) async { + var count = + AnimationManager_getBoneCount(animationManager, asset.asset, skinIndex); + var out = allocator>(count); + for (int i = 0; i < count; i++) { + out[i] = allocator(255); + } + + AnimationManager_getBoneNames( + animationManager, asset.asset, out, skinIndex); + var names = []; + for (int i = 0; i < count; i++) { + var namePtr = out[i]; + names.add(namePtr.cast().toDartString()); + } + return names; + } + + /// + /// + /// + @override + Future> getAnimationNames(covariant FFIAsset asset) async { + var animationCount = + AnimationManager_getAnimationCount(animationManager, asset.asset); + var names = []; + var outPtr = allocator(255); + for (int i = 0; i < animationCount; i++) { + AnimationManager_getAnimationName( + animationManager, asset.asset, outPtr, i); + names.add(outPtr.cast().toDartString()); + } + allocator.free(outPtr); + + return names; + } + + /// + /// + /// + @override + Future getAnimationDuration( + FFIAsset asset, int animationIndex) async { + return AnimationManager_getAnimationDuration( + animationManager, asset.asset, animationIndex); + } + + /// + /// + /// + Future getAnimationDurationByName(FFIAsset asset, String name) async { + var animations = await getAnimationNames(asset); + var index = animations.indexOf(name); + if (index == -1) { + throw Exception("Failed to find animation $name"); + } + return getAnimationDuration(asset, index); + } + + Future clearMorphAnimationData(ThermionEntity entity) async { + if (!AnimationManager_clearMorphAnimation(animationManager, entity)) { + throw Exception("Failed to clear morph animation"); + } + } + + /// + /// + /// + @override + Future setMorphAnimationData(FFIAsset asset, MorphAnimationData animation, + {List? targetMeshNames}) async { + var meshEntities = await getChildEntities(asset); + + var meshNames = meshEntities.map((e) => getNameForEntity(e)).toList(); + if (targetMeshNames != null) { + for (final targetMeshName in targetMeshNames) { + if (!meshNames.contains(targetMeshName)) { + throw Exception( + "Error: mesh ${targetMeshName} does not exist under the specified entity. Available meshes : ${meshNames}"); + } + } + } + + // Entities are not guaranteed to have the same morph targets (or share the same order), + // either from each other, or from those specified in [animation]. + // We therefore set morph targets separately for each mesh. + // For each mesh, allocate enough memory to hold FxM 32-bit floats + // (where F is the number of Frames, and M is the number of morph targets in the mesh). + // we call [extract] on [animation] to return frame data only for morph targets that present in both the mesh and the animation + for (int i = 0; i < meshNames.length; i++) { + var meshName = meshNames[i]; + var meshEntity = meshEntities[i]; + + if (targetMeshNames?.contains(meshName) == false) { + // _logger.info("Skipping $meshName, not contained in target"); + continue; + } + + var meshMorphTargets = await getMorphTargetNames(asset, meshEntity); + + var intersection = animation.morphTargets + .toSet() + .intersection(meshMorphTargets.toSet()) + .toList(); + + if (intersection.isEmpty) { + throw Exception( + """No morph targets specified in animation are present on mesh $meshName. + If you weren't intending to animate every mesh, specify [targetMeshNames] when invoking this method. + Animation morph targets: ${animation.morphTargets}\n + Mesh morph targets ${meshMorphTargets} + Child meshes: ${meshNames}"""); + } + + var indices = Uint32List.fromList( + intersection.map((m) => meshMorphTargets.indexOf(m)).toList()); + + // var frameData = animation.data; + var frameData = animation.subset(intersection); + + assert( + frameData.data.length == animation.numFrames * intersection.length); + + var result = AnimationManager_setMorphAnimation( + animationManager, + meshEntity, + frameData.data.address, + indices.address, + indices.length, + animation.numFrames, + animation.frameLengthInMs); + + if (!result) { + throw Exception("Failed to set morph animation data for ${meshName}"); + } + } + } + + /// + /// Currently, scale is not supported. + /// + @override + Future addBoneAnimation(covariant FFIAsset asset, BoneAnimationData animation, + {int skinIndex = 0, + double fadeOutInSecs = 0.0, + double fadeInInSecs = 0.0, + double maxDelta = 1.0}) async { + if (animation.space != Space.Bone && + animation.space != Space.ParentWorldRotation) { + throw UnimplementedError("TODO - support ${animation.space}"); + } + if (skinIndex != 0) { + throw UnimplementedError("TODO - support skinIndex != 0 "); + } + var boneNames = await getBoneNames(asset); + var restLocalTransformsRaw = allocator(boneNames.length * 16); + AnimationManager_getRestLocalTransforms(animationManager, asset.asset, + skinIndex, restLocalTransformsRaw, boneNames.length); + + var restLocalTransforms = []; + for (int i = 0; i < boneNames.length; i++) { + var values = []; + for (int j = 0; j < 16; j++) { + values.add(restLocalTransformsRaw[(i * 16) + j]); + } + restLocalTransforms.add(Matrix4.fromList(values)); + } + allocator.free(restLocalTransformsRaw); + + var numFrames = animation.frameData.length; + + var data = allocator(numFrames * 16); + + var bones = await Future.wait(List>.generate( + boneNames.length, (i) => getBone(asset, i))); + + for (int i = 0; i < animation.bones.length; i++) { + var boneName = animation.bones[i]; + var entityBoneIndex = boneNames.indexOf(boneName); + if (entityBoneIndex == -1) { + _logger.warning("Bone $boneName not found, skipping"); + continue; + } + var boneEntity = bones[entityBoneIndex]; + + var baseTransform = restLocalTransforms[entityBoneIndex]; + + var world = Matrix4.identity(); + // this odd use of ! is intentional, without it, the WASM optimizer gets in trouble + var parentBoneEntity = (await getParent(boneEntity))!; + while (true) { + if (!bones.contains(parentBoneEntity!)) { + break; + } + world = restLocalTransforms[bones.indexOf(parentBoneEntity!)] * world; + parentBoneEntity = (await getParent(parentBoneEntity))!; + } + + world = Matrix4.identity()..setRotation(world.getRotation()); + var worldInverse = Matrix4.identity()..copyInverse(world); + + for (int frameNum = 0; frameNum < numFrames; frameNum++) { + var rotation = animation.frameData[frameNum][i].rotation; + var translation = animation.frameData[frameNum][i].translation; + var frameTransform = + Matrix4.compose(translation, rotation, Vector3.all(1.0)); + var newLocalTransform = frameTransform.clone(); + if (animation.space == Space.Bone) { + newLocalTransform = baseTransform * frameTransform; + } else if (animation.space == Space.ParentWorldRotation) { + newLocalTransform = + baseTransform * (worldInverse * frameTransform * world); + } + for (int j = 0; j < 16; j++) { + data.elementAt((frameNum * 16) + j).value = + newLocalTransform.storage[j]; + } + } + + AnimationManager_addBoneAnimation( + animationManager, + asset.asset, + skinIndex, + entityBoneIndex, + data, + numFrames, + animation.frameLengthInMs, + fadeOutInSecs, + fadeInInSecs, + maxDelta); + } + allocator.free(data); + } + + /// + /// + /// + Future getLocalTransform(ThermionEntity entity) async { + return double4x4ToMatrix4( + TransformManager_getLocalTransform(app.transformManager, entity)); + } + + /// + /// + /// + Future getWorldTransform(ThermionEntity entity) async { + return double4x4ToMatrix4( + TransformManager_getWorldTransform(app.transformManager, entity)); + } + + /// + /// + /// + Future setTransform(ThermionEntity entity, Matrix4 transform) async { + TransformManager_setTransform( + app.transformManager, entity, matrix4ToDouble4x4(transform)); + } + + /// + /// + /// + Future updateBoneMatrices(ThermionEntity entity) async { + throw UnimplementedError(); + + // var result = await withBoolCallback((cb) { + // update_bone_matrices_render_thread(_sceneManager!, entity, cb); + // }); + // if (!result) { + // throw Exception("Failed to update bone matrices"); + // } + } + + /// + /// + /// + Future getInverseBindMatrix(FFIAsset asset, int boneIndex, + {int skinIndex = 0}) async { + var matrix = Float32List(16); + AnimationManager_getInverseBindMatrix( + animationManager, asset.asset, skinIndex, boneIndex, matrix.address); + return Matrix4.fromList(matrix); + } + + /// + /// + /// + Future getBone(FFIAsset asset, int boneIndex, + {int skinIndex = 0}) async { + if (skinIndex != 0) { + throw UnimplementedError("TOOD"); + } + return AnimationManager_getBone( + animationManager, asset.asset, skinIndex, boneIndex); + } + + /// + /// + /// + @override + Future setBoneTransform( + ThermionEntity entity, int boneIndex, Matrix4 transform, + {int skinIndex = 0}) async { + if (skinIndex != 0) { + throw UnimplementedError("TOOD"); + } + final ptr = allocator(16); + for (int i = 0; i < 16; i++) { + ptr[i] = transform.storage[i]; + } + var result = await withBoolCallback((cb) { + AnimationManager_setBoneTransformRenderThread( + animationManager, entity, skinIndex, boneIndex, ptr, cb); + }); + + allocator.free(ptr); + if (!result) { + throw Exception("Failed to set bone transform"); + } + } + + /// + /// + /// + /// + @override + Future resetBones(covariant FFIAsset asset) async { + AnimationManager_resetToRestPose(animationManager, asset.asset); + } + + + /// + /// + /// + @override + Future playAnimation(covariant FFIAsset asset, int index, + {bool loop = false, + bool reverse = false, + bool replaceActive = true, + double crossfade = 0.0, + double startOffset = 0.0}) async { + AnimationManager_playAnimation(animationManager, asset.asset, index, loop, + reverse, replaceActive, crossfade, startOffset); + } + + /// + /// + /// + @override + Future stopAnimation(FFIAsset asset, int animationIndex) async { + AnimationManager_stopAnimation( + animationManager, asset.asset, animationIndex); + } + + /// + /// + /// + @override + Future stopAnimationByName(FFIAsset asset, String name) async { + var animations = await getAnimationNames(asset); + await stopAnimation(asset, animations.indexOf(name)); + } + + /// + /// + /// + @override + Future playAnimationByName(FFIAsset asset, String name, + {bool loop = false, + bool reverse = false, + bool replaceActive = true, + double crossfade = 0.0, + bool wait = false}) async { + var animations = await getAnimationNames(asset); + var index = animations.indexOf(name); + var duration = await getAnimationDuration(asset, index); + await playAnimation(asset, index, + loop: loop, + reverse: reverse, + replaceActive: replaceActive, + crossfade: crossfade); + if (wait) { + await Future.delayed(Duration(milliseconds: (duration * 1000).toInt())); + } + } + + /// + /// + /// + @override + Future setGltfAnimationFrame( + FFIAsset asset, int index, int animationFrame) async { + AnimationManager_setGltfAnimationFrame( + animationManager, asset.asset, index, animationFrame); + } + + /// + /// + /// + @override + Future addAnimationComponent(ThermionEntity entity) async { + AnimationManager_addAnimationComponent(animationManager, entity); + } + + /// + /// + /// + Future removeAnimationComponent(ThermionEntity entity) async { + AnimationManager_removeAnimationComponent(animationManager, entity); + } + } diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/ffi_camera.dart b/thermion_dart/lib/src/viewer/src/ffi/src/ffi_camera.dart index e1fb9ac4..fc065746 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/ffi_camera.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/ffi_camera.dart @@ -3,10 +3,10 @@ import 'dart:typed_data'; import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_dart.g.dart'; -import 'package:thermion_dart/src/viewer/src/shared_types/layers.dart'; +import 'package:thermion_dart/src/filament/src/layers.dart'; +import 'package:thermion_dart/thermion_dart.dart'; import 'package:vector_math/vector_math_64.dart'; import '../../../../utils/src/matrix.dart'; -import '../../thermion_viewer_base.dart'; class FFICamera extends Camera { final Pointer camera; @@ -150,4 +150,12 @@ class FFICamera extends Camera { Future destroy() async { throw UnimplementedError(); } + + Future setCameraExposure( + double aperture, double shutterSpeed, double sensitivity) async { + Camera_setExposure(camera, aperture, shutterSpeed, sensitivity); + } + + Future getFocusDistance() async => Camera_getFocusDistance(camera); + Future setFocusDistance(double focusDistance) async => Camera_setFocusDistance(camera, focusDistance); } diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/ffi_filament_app.dart b/thermion_dart/lib/src/viewer/src/ffi/src/ffi_filament_app.dart index 82d2daf6..e367d92c 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/ffi_filament_app.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/ffi_filament_app.dart @@ -1,8 +1,13 @@ +import 'dart:async'; +import 'dart:typed_data'; + 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_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_texture.dart'; -import 'package:thermion_dart/src/viewer/src/filament/filament.dart'; +import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_view.dart'; import 'package:thermion_dart/thermion_dart.dart'; typedef RenderCallback = Pointer)>>; @@ -11,12 +16,13 @@ class FFIFilamentConfig extends FilamentConfig> { FFIFilamentConfig( {required super.backend, required super.resourceLoader, + required super.driver, + required super.platform, + required super.sharedContext, required super.uberArchivePath}); } class FFIFilamentApp extends FilamentApp { - static FFIFilamentApp? _instance; - final Pointer engine; final Pointer gltfAssetLoader; final Pointer gltfResourceLoader; @@ -47,53 +53,82 @@ class FFIFilamentApp extends FilamentApp { transformManager: transformManager, lightManager: lightManager, renderableManager: renderableManager, - ubershaderMaterialProvider: ubershaderMaterialProvider); + ubershaderMaterialProvider: ubershaderMaterialProvider) {} - Future create(FFIFilamentConfig config) async { - if (_instance == null) { - RenderLoop_destroy(); - RenderLoop_create(); - - final engine = await withPointerCallback((cb) => - Engine_createRenderThread( - TBackend.values[config.backend.index].index, - config.platform ?? nullptr, - config.sharedContext ?? nullptr, - config.stereoscopicEyeCount, - config.disableHandleUseAfterFreeCheck, - cb)); - - final gltfResourceLoader = await withPointerCallback( - (cb) => GltfResourceLoader_createRenderThread(engine, cb)); - final gltfAssetLoader = await withPointerCallback( - (cb) => GltfAssetLoader_createRenderThread(engine, nullptr, cb)); - final renderer = await withPointerCallback( - (cb) => Engine_createRendererRenderThread(engine, cb)); - final ubershaderMaterialProvider = - await withPointerCallback( - (cb) => GltfAssetLoader_getMaterialProvider(gltfAssetLoader)); - - final transformManager = Engine_getTransformManager(engine); - final lightManager = Engine_getLightManager(engine); - final renderableManager = Engine_getRenderableManager(engine); - - final renderTicker = await withPointerCallback( - (cb) => RenderTicker_create(renderer)); - - final nameComponentManager = NameComponentManager_create(); - - _instance = FFIFilamentApp( - engine, - gltfAssetLoader, - gltfResourceLoader, - renderer, - transformManager, - lightManager, - renderableManager, - ubershaderMaterialProvider, - renderTicker, nameComponentManager); + static Future create(FFIFilamentConfig config) async { + if (FilamentApp.instance != null) { + await FilamentApp.instance!.destroy(); } - return _instance!; + + RenderLoop_destroy(); + RenderLoop_create(); + + final engine = await withPointerCallback((cb) => + Engine_createRenderThread( + TBackend.values[config.backend.index].index, + config.platform ?? nullptr, + config.sharedContext ?? nullptr, + config.stereoscopicEyeCount, + config.disableHandleUseAfterFreeCheck, + cb)); + + final gltfResourceLoader = await withPointerCallback( + (cb) => GltfResourceLoader_createRenderThread(engine, cb)); + final gltfAssetLoader = await withPointerCallback( + (cb) => GltfAssetLoader_createRenderThread(engine, nullptr, cb)); + final renderer = await withPointerCallback( + (cb) => Engine_createRendererRenderThread(engine, cb)); + final ubershaderMaterialProvider = + await withPointerCallback( + (cb) => GltfAssetLoader_getMaterialProvider(gltfAssetLoader)); + + final transformManager = Engine_getTransformManager(engine); + final lightManager = Engine_getLightManager(engine); + final renderableManager = Engine_getRenderableManager(engine); + + final renderTicker = await withPointerCallback( + (cb) => RenderTicker_create(renderer)); + + final nameComponentManager = NameComponentManager_create(); + + FilamentApp.instance = FFIFilamentApp( + engine, + gltfAssetLoader, + gltfResourceLoader, + renderer, + transformManager, + lightManager, + renderableManager, + ubershaderMaterialProvider, + renderTicker, + nameComponentManager); + + } + + final _views = >{}; + final _viewMappings = {}; + + /// + /// + /// + Future setRenderable(covariant FFIView view, bool renderable) async { + final swapChain = _viewMappings[view]!; + if (renderable && !_views[swapChain]!.contains(view)) { + _views[swapChain]!.add(view); + } else if (!renderable && _views[swapChain]!.contains(view)) { + _views[swapChain]!.remove(view); + } + + final views = calloc>(255); + for (final swapChain in _views.keys) { + var numViews = _views[swapChain]!.length; + for (int i = 0; i < numViews; i++) { + views[i] = _views[swapChain]![i].view; + } + RenderTicker_setRenderable( + renderTicker, swapChain.swapChain, views, numViews); + } + calloc.free(views); } @override @@ -136,8 +171,12 @@ class FFIFilamentApp extends FilamentApp { } @override - Future destroy() { - throw UnimplementedError(); + Future destroy() async { + for (final swapChain in _views.keys) { + for (final view in _views[swapChain]!) { + await setRenderable(view, false); + } + } } /// @@ -153,16 +192,316 @@ class FFIFilamentApp extends FilamentApp { return FFIRenderTarget(renderTarget, this); } - // /// - // /// - // /// - // Future createRenderTarget(int width, int height, - // {covariant FFITexture? color, covariant FFITexture? depth}) async { - // final renderTarget = await withPointerCallback((cb) { - // RenderTarget_createRenderThread(app.engine, width, height, - // color?.pointer ?? nullptr, depth?.pointer ?? nullptr, cb); - // }); + /// + /// + /// + Future createTexture(int width, int height, + {int depth = 1, + int levels = 1, + Set flags = const {TextureUsage.TEXTURE_USAGE_SAMPLEABLE}, + TextureSamplerType textureSamplerType = TextureSamplerType.SAMPLER_2D, + TextureFormat textureFormat = TextureFormat.RGBA16F, + int? importedTextureHandle}) async { + var bitmask = flags.fold(0, (a, b) => a | b.index); + final texturePtr = await withPointerCallback((cb) { + Texture_buildRenderThread( + engine!, + width, + height, + depth, + levels, + importedTextureHandle ?? 0, + bitmask, + TTextureSamplerType.values[textureSamplerType.index], + TTextureFormat.values[textureFormat.index], + cb); + }); + if (texturePtr == nullptr) { + throw Exception("Failed to create texture"); + } + return FFITexture( + engine!, + texturePtr, + ); + } - // return FFIRenderTarget(renderTarget, app); - // } + Future createTextureSampler( + {TextureMinFilter minFilter = TextureMinFilter.LINEAR, + TextureMagFilter magFilter = TextureMagFilter.LINEAR, + TextureWrapMode wrapS = TextureWrapMode.CLAMP_TO_EDGE, + TextureWrapMode wrapT = TextureWrapMode.CLAMP_TO_EDGE, + TextureWrapMode wrapR = TextureWrapMode.CLAMP_TO_EDGE, + double anisotropy = 0.0, + TextureCompareMode compareMode = TextureCompareMode.NONE, + TextureCompareFunc compareFunc = TextureCompareFunc.LESS_EQUAL}) async { + final samplerPtr = TextureSampler_create(); + TextureSampler_setMinFilter( + samplerPtr, TSamplerMinFilter.values[minFilter.index]); + TextureSampler_setMagFilter( + samplerPtr, TSamplerMagFilter.values[magFilter.index]); + TextureSampler_setWrapModeS( + samplerPtr, TSamplerWrapMode.values[wrapS.index]); + TextureSampler_setWrapModeT( + samplerPtr, TSamplerWrapMode.values[wrapT.index]); + TextureSampler_setWrapModeR( + samplerPtr, TSamplerWrapMode.values[wrapR.index]); + if (anisotropy > 0) { + TextureSampler_setAnisotropy(samplerPtr, anisotropy); + } + + TextureSampler_setCompareMode( + samplerPtr, + TSamplerCompareMode.values[compareMode.index], + TSamplerCompareFunc.values[compareFunc.index]); + + return FFITextureSampler(samplerPtr); + } + + /// + /// + /// + Future decodeImage(Uint8List data) async { + final name = "image"; + var ptr = Image_decode( + data.address, + data.length, + name.toNativeUtf8().cast(), + ); + if (ptr == nullptr) { + throw Exception("Failed to decode image"); + } + return FFILinearImage(ptr); + } + + /// + /// Creates an (empty) imge with the given dimensions. + /// + Future createImage(int width, int height, int channels) async { + final ptr = Image_createEmpty(width, height, channels); + return FFILinearImage(ptr); + } + + /// + /// + /// + Future createMaterial(Uint8List data) async { + var ptr = await withPointerCallback((cb) { + Engine_buildMaterialRenderThread(engine!, data.address, data.length, cb); + }); + return FFIMaterial(ptr, this); + } + + /// + /// + /// + Future createUbershaderMaterialInstance( + {bool doubleSided = false, + bool unlit = false, + bool hasVertexColors = false, + bool hasBaseColorTexture = false, + bool hasNormalTexture = false, + bool hasOcclusionTexture = false, + bool hasEmissiveTexture = false, + bool useSpecularGlossiness = false, + AlphaMode alphaMode = AlphaMode.OPAQUE, + bool enableDiagnostics = false, + bool hasMetallicRoughnessTexture = false, + int metallicRoughnessUV = 0, + int baseColorUV = 0, + bool hasClearCoatTexture = false, + int clearCoatUV = 0, + bool hasClearCoatRoughnessTexture = false, + int clearCoatRoughnessUV = 0, + bool hasClearCoatNormalTexture = false, + int clearCoatNormalUV = 0, + bool hasClearCoat = false, + bool hasTransmission = false, + bool hasTextureTransforms = false, + int emissiveUV = 0, + int aoUV = 0, + int normalUV = 0, + bool hasTransmissionTexture = false, + int transmissionUV = 0, + bool hasSheenColorTexture = false, + int sheenColorUV = 0, + bool hasSheenRoughnessTexture = false, + int sheenRoughnessUV = 0, + bool hasVolumeThicknessTexture = false, + int volumeThicknessUV = 0, + bool hasSheen = false, + bool hasIOR = false, + bool hasVolume = false}) async { + final key = Struct.create(); + + key.doubleSided = doubleSided; + key.unlit = unlit; + key.hasVertexColors = hasVertexColors; + key.hasBaseColorTexture = hasBaseColorTexture; + key.hasNormalTexture = hasNormalTexture; + key.hasOcclusionTexture = hasOcclusionTexture; + key.hasEmissiveTexture = hasEmissiveTexture; + key.useSpecularGlossiness = useSpecularGlossiness; + key.alphaMode = alphaMode.index; + key.enableDiagnostics = enableDiagnostics; + key.unnamed.unnamed.hasMetallicRoughnessTexture = + hasMetallicRoughnessTexture; + key.unnamed.unnamed.metallicRoughnessUV = 0; + key.baseColorUV = baseColorUV; + key.hasClearCoatTexture = hasClearCoatTexture; + key.clearCoatUV = clearCoatUV; + key.hasClearCoatRoughnessTexture = hasClearCoatRoughnessTexture; + key.clearCoatRoughnessUV = clearCoatRoughnessUV; + key.hasClearCoatNormalTexture = hasClearCoatNormalTexture; + key.clearCoatNormalUV = clearCoatNormalUV; + key.hasClearCoat = hasClearCoat; + key.hasTransmission = hasTransmission; + key.hasTextureTransforms = hasTextureTransforms; + key.emissiveUV = emissiveUV; + key.aoUV = aoUV; + key.normalUV = normalUV; + key.hasTransmissionTexture = hasTransmissionTexture; + key.transmissionUV = transmissionUV; + key.hasSheenColorTexture = hasSheenColorTexture; + key.sheenColorUV = sheenColorUV; + key.hasSheenRoughnessTexture = hasSheenRoughnessTexture; + key.sheenRoughnessUV = sheenRoughnessUV; + key.hasVolumeThicknessTexture = hasVolumeThicknessTexture; + key.volumeThicknessUV = volumeThicknessUV; + key.hasSheen = hasSheen; + key.hasIOR = hasIOR; + key.hasVolume = hasVolume; + + final materialInstance = await withPointerCallback((cb) { + MaterialProvider_createMaterialInstanceRenderThread( + ubershaderMaterialProvider, key.address, cb); + }); + if (materialInstance == nullptr) { + throw Exception("Failed to create material instance"); + } + + var instance = FFIMaterialInstance(materialInstance, this); + return instance; + } + + /// + /// + /// + Future createUnlitMaterialInstance() async { + final instance = await createUbershaderMaterialInstance(unlit: true); + return instance as FFIMaterialInstance; + } + + FFIMaterial? _gridMaterial; + Future get gridMaterial async { + _gridMaterial ??= FFIMaterial(Material_createGridMaterial(), this); + return _gridMaterial!; + } + + /// + /// + /// + @override + Future createGeometry(Geometry geometry, + {List? materialInstances, + bool keepData = false}) async { + var assetPtr = await withPointerCallback((callback) { + var ptrList = Int64List(materialInstances?.length ?? 0); + if (materialInstances != null && materialInstances.isNotEmpty) { + ptrList.setRange( + 0, + materialInstances.length, + materialInstances + .cast() + .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>(), + ptrList.length, + callback); + }); + if (assetPtr == nullptr) { + throw Exception("Failed to create geometry"); + } + + var asset = FFIAsset(assetPtr, this); + + return asset; + } + + /// + /// + /// + Future getMaterialInstanceAt( + ThermionEntity entity, int index) async { + final instancePtr = RenderableManager_getMaterialInstanceAt( + renderableManager, entity, index); + + final instance = FFIMaterialInstance(instancePtr, this); + return instance; + } + + /// + /// + /// + @override + Future render() async { + RenderTicker_renderRenderThread(renderTicker, 0); + } + + @override + Future register( + covariant FFISwapChain swapChain, covariant FFIView view) async { + _viewMappings[view] = swapChain; + } + + final _hooks = []; + + @override + Future registerRequestFrameHook(Future Function() hook) async { + if (!_hooks.contains(hook)) { + _hooks.add(hook); + } + } + + @override + Future unregisterRequestFrameHook(Future Function() hook) async { + if (_hooks.contains(hook)) { + _hooks.remove(hook); + } + } + + /// + /// + /// + @override + Future requestFrame() async { + for (final hook in _hooks) { + await hook.call(); + } + final completer = Completer(); + + final callback = NativeCallable.listener(() { + completer.complete(true); + }); + + RenderLoop_requestAnimationFrame(callback.nativeFunction.cast()); + + try { + await completer.future.timeout(Duration(seconds: 1)); + } catch (err) { + print("WARNING - render call timed out"); + } + } } diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/ffi_gizmo.dart b/thermion_dart/lib/src/viewer/src/ffi/src/ffi_gizmo.dart index 27d40b3d..9e2cd50b 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/ffi_gizmo.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/ffi_gizmo.dart @@ -1,14 +1,12 @@ import 'dart:async'; 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/shared_types/entities.dart'; -import 'thermion_dart.g.dart'; import 'package:thermion_dart/thermion_dart.dart'; -import 'package:vector_math/vector_math_64.dart'; import 'ffi_view.dart'; class FFIGizmo extends FFIAsset implements GizmoAsset { + final Set gizmoEntities; late NativeCallable _nativeCallback; diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/ffi_scene.dart b/thermion_dart/lib/src/viewer/src/ffi/src/ffi_scene.dart index f102d62a..46a3609b 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/ffi_scene.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/ffi_scene.dart @@ -1,7 +1,7 @@ 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/viewer/src/shared_types/scene.dart'; +import 'package:thermion_dart/src/filament/src/scene.dart'; import 'callbacks.dart'; diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/ffi_view.dart b/thermion_dart/lib/src/viewer/src/ffi/src/ffi_view.dart index f406283f..b16e20b9 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/ffi_view.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/ffi_view.dart @@ -2,8 +2,8 @@ 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_scene.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_swapchain.dart'; -import 'package:thermion_dart/src/viewer/src/shared_types/layers.dart'; -import 'package:thermion_dart/src/viewer/src/shared_types/shared_types.dart'; +import 'package:thermion_dart/src/filament/src/layers.dart'; +import 'package:thermion_dart/src/filament/src/shared_types.dart'; import 'callbacks.dart'; import 'ffi_camera.dart'; @@ -66,10 +66,6 @@ class FFIView extends View { View_setPostProcessing(view, enabled); } - Future setRenderable(bool renderable, FFISwapChain swapChain) async { - throw UnimplementedError(); - } - @override Future setFrustumCullingEnabled(bool enabled) async { View_setFrustumCullingEnabled(view, enabled); @@ -84,7 +80,8 @@ class FFIView extends View { @override Future setToneMapper(ToneMapper mapper) async { - View_setToneMappingRenderThread(view, app.engine, TToneMapping.values[mapper.index].value); + View_setToneMappingRenderThread( + view, app.engine, TToneMapping.values[mapper.index].value); } Future setStencilBufferEnabled(bool enabled) async { @@ -111,7 +108,7 @@ class FFIView extends View { Future setScene(covariant FFIScene scene) async { await withVoidCallback((cb) => View_setScene(view, scene.scene)); } - + @override Future setLayerVisibility(VisibilityLayers layer, bool visible) async { View_setLayerEnabled(view, layer.value, visible); diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/grid_overlay.dart b/thermion_dart/lib/src/viewer/src/ffi/src/grid_overlay.dart new file mode 100644 index 00000000..051d39cd --- /dev/null +++ b/thermion_dart/lib/src/viewer/src/ffi/src/grid_overlay.dart @@ -0,0 +1,13 @@ +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_filament_app.dart'; + +class GridOverlay extends FFIAsset { + GridOverlay(super.asset, super.app); + + static Future create(FFIFilamentApp app) async { + final gridMaterial = await app.gridMaterial; + final asset = SceneAsset_createGrid(app.engine, gridMaterial.pointer); + return GridOverlay(asset, app); + } +} diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart index aef545d6..eea0a5ea 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart @@ -25,6 +25,12 @@ external ffi.Pointer Material_createInstance( ffi.Pointer tMaterial, ); +@ffi.Native Function()>(isLeaf: true) +external ffi.Pointer Material_createImageMaterial(); + +@ffi.Native Function()>(isLeaf: true) +external ffi.Pointer Material_createGridMaterial(); + @ffi.Native, ffi.Pointer)>( isLeaf: true) external bool Material_hasParameter( @@ -1227,6 +1233,11 @@ external double Camera_getFov( bool horizontal, ); +@ffi.Native)>(isLeaf: true) +external double Camera_getFocusDistance( + ffi.Pointer camera, +); + @ffi.Native, ffi.Float)>(isLeaf: true) external void Camera_setFocusDistance( ffi.Pointer camera, @@ -1324,11 +1335,12 @@ external void TransformManager_setTransform( double4x4 transform, ); -@ffi.Native, EntityId)>( +@ffi.Native, EntityId, Aabb3)>( isLeaf: true) external void TransformManager_transformToUnitCube( ffi.Pointer tTransformManager, int entityId, + Aabb3 boundingBox, ); @ffi.Native< @@ -1458,7 +1470,7 @@ external void RenderTicker_render( ffi.Void Function(ffi.Pointer, ffi.Pointer, ffi.Pointer>, ffi.Uint8)>(isLeaf: true) external void RenderTicker_setRenderable( - ffi.Pointer tFilamentRender, + ffi.Pointer tRenderTicker, ffi.Pointer swapChain, ffi.Pointer> views, int numViews, @@ -2719,15 +2731,15 @@ external void TextureSampler_destroyRenderThread( @ffi.Native< ffi.Void Function( - ffi.Pointer, + ffi.Pointer, EntityId, ffi.Int, ffi.Int, ffi.Pointer, ffi.Pointer>)>( isLeaf: true) -external void set_bone_transform_render_thread( - ffi.Pointer sceneManager, +external void AnimationManager_setBoneTransformRenderThread( + ffi.Pointer tAnimationManager, int asset, int skinIndex, int boneIndex, @@ -2736,10 +2748,10 @@ external void set_bone_transform_render_thread( ); @ffi.Native< - ffi.Void Function(ffi.Pointer, EntityId, + ffi.Void Function(ffi.Pointer, EntityId, ffi.Pointer>)>(isLeaf: true) -external void reset_to_rest_pose_render_thread( - ffi.Pointer sceneManager, +external void AnimationManager_resetToRestPoseRenderThread( + ffi.Pointer tAnimationManager, int entityId, ffi.Pointer> callback, ); @@ -3181,6 +3193,14 @@ external ffi.Pointer SceneAsset_loadGltf( int numInstances, ); +@ffi.Native< + ffi.Pointer Function( + ffi.Pointer, ffi.Pointer)>(isLeaf: true) +external ffi.Pointer SceneAsset_createGrid( + ffi.Pointer tEngine, + ffi.Pointer tMaterial, +); + @ffi.Native)>(isLeaf: true) external void SceneAsset_destroy( ffi.Pointer tSceneAsset, diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart index e37c1e43..56e5c0b2 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart @@ -3,18 +3,14 @@ import 'dart:io'; import 'dart:math'; import 'dart:typed_data'; import 'package:animation_tools_dart/animation_tools_dart.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_filament_app.dart'; -import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_gizmo.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_scene.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/filament/filament.dart'; -import 'package:thermion_dart/src/viewer/src/shared_types/config.dart'; -import 'package:thermion_dart/src/viewer/src/shared_types/entity.dart'; -import 'package:thermion_dart/src/viewer/src/shared_types/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/thermion_dart.dart'; import 'package:vector_math/vector_math_64.dart'; import 'package:vector_math/vector_math_64.dart' as v64; import '../../../../utils/src/matrix.dart'; @@ -31,7 +27,7 @@ const FILAMENT_ASSET_ERROR = 0; /// /// class ThermionViewerFFI extends ThermionViewer { - final _logger = Logger("ThermionViewerFFI"); + late final _logger = Logger(runtimeType.toString()); final _initialized = Completer(); Future get initialized => _initialized.future; @@ -39,16 +35,24 @@ class ThermionViewerFFI extends ThermionViewer { late NativeCallable _onPickResultCallable; late final FFIFilamentApp app; - late final FFIRenderTarget? renderTarget; - late final Future Function(String path) assetLoader; + + final FFIRenderTarget? renderTarget; + + late final FFIView view; + late final FFIScene scene; + late final Pointer animationManager; + late final Future Function(String path) loadAsset; /// /// /// - ThermionViewerFFI(this.assetLoader, this.app, {this.renderTarget}) { + ThermionViewerFFI({required this.loadAsset, this.renderTarget}) { + if (FilamentApp.instance == null) { + throw Exception("FilamentApp has not been created"); + } + app = FilamentApp.instance as FFIFilamentApp; _onPickResultCallable = NativeCallable.listener(_onPickResult); - _initialize(); } @@ -78,10 +82,6 @@ class ThermionViewerFFI extends ThermionViewer { } } - late final FFIView view; - late final FFIScene scene; - late final Pointer animationManager; - Future _initialize() async { _logger.info("Initializing ThermionViewerFFI"); view = FFIView( @@ -102,6 +102,7 @@ class ThermionViewerFFI extends ThermionViewer { AnimationManager_createRenderThread(app.engine, scene.scene, cb)); RenderTicker_addAnimationManager(app.renderTicker, animationManager); + this._initialized.complete(true); } @@ -119,6 +120,7 @@ class ThermionViewerFFI extends ThermionViewer { @override Future setRendering(bool render) async { _rendering = render; + await app.setRenderable(view, render); } /// @@ -149,8 +151,6 @@ class ThermionViewerFFI extends ThermionViewer { final _onDispose = []; bool _disposing = false; - final _materialInstances = []; - /// /// /// @@ -159,15 +159,12 @@ class ThermionViewerFFI extends ThermionViewer { _disposing = true; await setRendering(false); await destroyAssets(); - for (final mInstance in _materialInstances) { - await mInstance.destroy(); - } await destroyLights(); for (final callback in _onDispose) { await callback.call(); } - await app.destroy(); + _onDispose.clear(); _disposing = false; } @@ -179,12 +176,15 @@ class ThermionViewerFFI extends ThermionViewer { _onDispose.add(callback); } + BackgroundImage? _backgroundImage; + /// /// /// @override Future clearBackgroundImage() async { - clear_background_image_render_thread(_viewer!); + await _backgroundImage?.destroy(); + _backgroundImage = null; } /// @@ -192,12 +192,8 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future setBackgroundImage(String path, {bool fillHeight = false}) async { - final pathPtr = path.toNativeUtf8(allocator: allocator).cast(); - await withVoidCallback((cb) { - set_background_image_render_thread(_viewer!, pathPtr, fillHeight, cb); - }); - - allocator.free(pathPtr); + final imageData = await loadAsset(path); + _backgroundImage = await BackgroundImage.create(app, scene, imageData); } /// @@ -205,7 +201,7 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future setBackgroundColor(double r, double g, double b, double a) async { - set_background_color_render_thread(_viewer!, r, g, b, a); + throw UnimplementedError(); } /// @@ -214,7 +210,7 @@ class ThermionViewerFFI extends ThermionViewer { @override Future setBackgroundImagePosition(double x, double y, {bool clamp = false}) async { - set_background_image_position_render_thread(_viewer!, x, y, clamp); + throw UnimplementedError(); } /// @@ -222,7 +218,7 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future loadSkybox(String skyboxPath) async { - var data = await _loadAsset(skyboxPath); + var data = await loadAsset(skyboxPath); skybox = await withPointerCallback((cb) { Engine_buildSkyboxRenderThread( @@ -231,16 +227,6 @@ class ThermionViewerFFI extends ThermionViewer { Scene_setSkybox(scene.scene, skybox!); } - Future _loadAsset(String path) async { - if (path.startsWith("file://")) { - return File(path.replaceAll("file://", "")).readAsBytesSync(); - } - if (path.startsWith("asset://")) { - throw UnimplementedError(); - } - throw UnimplementedError(); - } - Pointer? indirectLight; Pointer? skybox; @@ -249,7 +235,7 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future loadIbl(String lightingPath, {double intensity = 30000}) async { - var data = await _loadAsset(lightingPath); + var data = await loadAsset(lightingPath); indirectLight = await withPointerCallback((cb) { Engine_buildIndirectLightRenderThread( app.engine, data.address, data.length, intensity, cb, nullptr); @@ -391,7 +377,7 @@ class ThermionViewerFFI extends ThermionViewer { @override Future loadGlb(String path, {int numInstances = 1, bool keepData = false}) async { - final data = await assetLoader(path); + final data = await loadAsset(path); return loadGlbFromBuffer(data, numInstances: numInstances, keepData: keepData); @@ -450,401 +436,6 @@ class ThermionViewerFFI extends ThermionViewer { // assetPtr, _sceneManager!, app.engine!, _unlitMaterialProvider!, this); } - /// - /// - /// - @override - Future setMorphTargetWeights( - ThermionEntity entity, List weights) async { - if (weights.isEmpty) { - throw Exception("Weights must not be empty"); - } - var weightsPtr = allocator(weights.length); - - for (int i = 0; i < weights.length; i++) { - weightsPtr[i] = weights[i]; - } - var success = await withBoolCallback((cb) { - AnimationManager_setMorphTargetWeightsRenderThread( - animationManager, entity, weightsPtr, weights.length, cb); - }); - allocator.free(weightsPtr); - - if (!success) { - throw Exception( - "Failed to set morph target weights, check logs for details"); - } - } - - /// - /// - /// - @override - Future> getMorphTargetNames( - covariant FFIAsset asset, ThermionEntity childEntity) async { - var names = []; - - var count = AnimationManager_getMorphTargetNameCount( - animationManager, asset.asset, childEntity); - var outPtr = allocator(255); - for (int i = 0; i < count; i++) { - AnimationManager_getMorphTargetName( - animationManager, asset.asset, childEntity, outPtr, i); - names.add(outPtr.cast().toDartString()); - } - allocator.free(outPtr); - return names.cast(); - } - - Future> getBoneNames(covariant FFIAsset asset, - {int skinIndex = 0}) async { - var count = - AnimationManager_getBoneCount(animationManager, asset.asset, skinIndex); - var out = allocator>(count); - for (int i = 0; i < count; i++) { - out[i] = allocator(255); - } - - AnimationManager_getBoneNames( - animationManager, asset.asset, out, skinIndex); - var names = []; - for (int i = 0; i < count; i++) { - var namePtr = out[i]; - names.add(namePtr.cast().toDartString()); - } - return names; - } - - /// - /// - /// - @override - Future> getAnimationNames(covariant FFIAsset asset) async { - var animationCount = - AnimationManager_getAnimationCount(animationManager, asset.asset); - var names = []; - var outPtr = allocator(255); - for (int i = 0; i < animationCount; i++) { - AnimationManager_getAnimationName( - animationManager, asset.asset, outPtr, i); - names.add(outPtr.cast().toDartString()); - } - allocator.free(outPtr); - - return names; - } - - /// - /// - /// - @override - Future getAnimationDuration( - FFIAsset asset, int animationIndex) async { - return AnimationManager_getAnimationDuration( - animationManager, asset.asset, animationIndex); - } - - /// - /// - /// - Future getAnimationDurationByName(FFIAsset asset, String name) async { - var animations = await getAnimationNames(asset); - var index = animations.indexOf(name); - if (index == -1) { - throw Exception("Failed to find animation $name"); - } - return getAnimationDuration(asset, index); - } - - Future clearMorphAnimationData(ThermionEntity entity) async { - if (!AnimationManager_clearMorphAnimation(animationManager, entity)) { - throw Exception("Failed to clear morph animation"); - } - } - - /// - /// - /// - @override - Future setMorphAnimationData(FFIAsset asset, MorphAnimationData animation, - {List? targetMeshNames}) async { - var meshEntities = await getChildEntities(asset); - - var meshNames = meshEntities.map((e) => getNameForEntity(e)).toList(); - if (targetMeshNames != null) { - for (final targetMeshName in targetMeshNames) { - if (!meshNames.contains(targetMeshName)) { - throw Exception( - "Error: mesh ${targetMeshName} does not exist under the specified entity. Available meshes : ${meshNames}"); - } - } - } - - // Entities are not guaranteed to have the same morph targets (or share the same order), - // either from each other, or from those specified in [animation]. - // We therefore set morph targets separately for each mesh. - // For each mesh, allocate enough memory to hold FxM 32-bit floats - // (where F is the number of Frames, and M is the number of morph targets in the mesh). - // we call [extract] on [animation] to return frame data only for morph targets that present in both the mesh and the animation - for (int i = 0; i < meshNames.length; i++) { - var meshName = meshNames[i]; - var meshEntity = meshEntities[i]; - - if (targetMeshNames?.contains(meshName) == false) { - // _logger.info("Skipping $meshName, not contained in target"); - continue; - } - - var meshMorphTargets = await getMorphTargetNames(asset, meshEntity); - - var intersection = animation.morphTargets - .toSet() - .intersection(meshMorphTargets.toSet()) - .toList(); - - if (intersection.isEmpty) { - throw Exception( - """No morph targets specified in animation are present on mesh $meshName. - If you weren't intending to animate every mesh, specify [targetMeshNames] when invoking this method. - Animation morph targets: ${animation.morphTargets}\n - Mesh morph targets ${meshMorphTargets} - Child meshes: ${meshNames}"""); - } - - var indices = Uint32List.fromList( - intersection.map((m) => meshMorphTargets.indexOf(m)).toList()); - - // var frameData = animation.data; - var frameData = animation.subset(intersection); - - assert( - frameData.data.length == animation.numFrames * intersection.length); - - var result = AnimationManager_setMorphAnimation( - animationManager, - meshEntity, - frameData.data.address, - indices.address, - indices.length, - animation.numFrames, - animation.frameLengthInMs); - - if (!result) { - throw Exception("Failed to set morph animation data for ${meshName}"); - } - } - } - - /// - /// Currently, scale is not supported. - /// - @override - Future addBoneAnimation(covariant FFIAsset asset, BoneAnimationData animation, - {int skinIndex = 0, - double fadeOutInSecs = 0.0, - double fadeInInSecs = 0.0, - double maxDelta = 1.0}) async { - if (animation.space != Space.Bone && - animation.space != Space.ParentWorldRotation) { - throw UnimplementedError("TODO - support ${animation.space}"); - } - if (skinIndex != 0) { - throw UnimplementedError("TODO - support skinIndex != 0 "); - } - var boneNames = await getBoneNames(asset); - var restLocalTransformsRaw = allocator(boneNames.length * 16); - AnimationManager_getRestLocalTransforms(animationManager, asset.asset, - skinIndex, restLocalTransformsRaw, boneNames.length); - - var restLocalTransforms = []; - for (int i = 0; i < boneNames.length; i++) { - var values = []; - for (int j = 0; j < 16; j++) { - values.add(restLocalTransformsRaw[(i * 16) + j]); - } - restLocalTransforms.add(Matrix4.fromList(values)); - } - allocator.free(restLocalTransformsRaw); - - var numFrames = animation.frameData.length; - - var data = allocator(numFrames * 16); - - var bones = await Future.wait(List>.generate( - boneNames.length, (i) => getBone(asset, i))); - - for (int i = 0; i < animation.bones.length; i++) { - var boneName = animation.bones[i]; - var entityBoneIndex = boneNames.indexOf(boneName); - if (entityBoneIndex == -1) { - _logger.warning("Bone $boneName not found, skipping"); - continue; - } - var boneEntity = bones[entityBoneIndex]; - - var baseTransform = restLocalTransforms[entityBoneIndex]; - - var world = Matrix4.identity(); - // this odd use of ! is intentional, without it, the WASM optimizer gets in trouble - var parentBoneEntity = (await getParent(boneEntity))!; - while (true) { - if (!bones.contains(parentBoneEntity!)) { - break; - } - world = restLocalTransforms[bones.indexOf(parentBoneEntity!)] * world; - parentBoneEntity = (await getParent(parentBoneEntity))!; - } - - world = Matrix4.identity()..setRotation(world.getRotation()); - var worldInverse = Matrix4.identity()..copyInverse(world); - - for (int frameNum = 0; frameNum < numFrames; frameNum++) { - var rotation = animation.frameData[frameNum][i].rotation; - var translation = animation.frameData[frameNum][i].translation; - var frameTransform = - Matrix4.compose(translation, rotation, Vector3.all(1.0)); - var newLocalTransform = frameTransform.clone(); - if (animation.space == Space.Bone) { - newLocalTransform = baseTransform * frameTransform; - } else if (animation.space == Space.ParentWorldRotation) { - newLocalTransform = - baseTransform * (worldInverse * frameTransform * world); - } - for (int j = 0; j < 16; j++) { - data.elementAt((frameNum * 16) + j).value = - newLocalTransform.storage[j]; - } - } - - AnimationManager_addBoneAnimation( - animationManager, - asset.asset, - skinIndex, - entityBoneIndex, - data, - numFrames, - animation.frameLengthInMs, - fadeOutInSecs, - fadeInInSecs, - maxDelta); - } - allocator.free(data); - } - - /// - /// - /// - Future getLocalTransform(ThermionEntity entity) async { - return double4x4ToMatrix4( - TransformManager_getLocalTransform(app.transformManager, entity)); - } - - /// - /// - /// - Future getWorldTransform(ThermionEntity entity) async { - return double4x4ToMatrix4( - TransformManager_getWorldTransform(app.transformManager, entity)); - } - - /// - /// - /// - Future setTransform(ThermionEntity entity, Matrix4 transform) async { - TransformManager_setTransform( - app.transformManager, entity, matrix4ToDouble4x4(transform)); - } - - /// - /// - /// - Future queueTransformUpdates( - List entities, List transforms) async { - throw UnimplementedError(); - // var tEntities = Int32List.fromList(entities); - // var tTransforms = - // Float64List.fromList(transforms.expand((t) => t.storage).toList()); - - // SceneManager_queueTransformUpdates(_sceneManager!, tEntities.address, - // tTransforms.address, tEntities.length); - } - - /// - /// - /// - Future updateBoneMatrices(ThermionEntity entity) async { - throw UnimplementedError(); - - // var result = await withBoolCallback((cb) { - // update_bone_matrices_render_thread(_sceneManager!, entity, cb); - // }); - // if (!result) { - // throw Exception("Failed to update bone matrices"); - // } - } - - /// - /// - /// - Future getInverseBindMatrix(FFIAsset asset, int boneIndex, - {int skinIndex = 0}) async { - var matrix = Float32List(16); - AnimationManager_getInverseBindMatrix( - animationManager, asset.asset, skinIndex, boneIndex, matrix.address); - return Matrix4.fromList(matrix); - } - - /// - /// - /// - Future getBone(FFIAsset asset, int boneIndex, - {int skinIndex = 0}) async { - if (skinIndex != 0) { - throw UnimplementedError("TOOD"); - } - return AnimationManager_getBone( - animationManager, asset.asset, skinIndex, boneIndex); - } - - /// - /// - /// - @override - Future setBoneTransform( - ThermionEntity entity, int boneIndex, Matrix4 transform, - {int skinIndex = 0}) async { - if (skinIndex != 0) { - throw UnimplementedError("TOOD"); - } - final ptr = allocator(16); - for (int i = 0; i < 16; i++) { - ptr[i] = transform.storage[i]; - } - var result = await withBoolCallback((cb) { - set_bone_transform_render_thread( - _sceneManager!, entity, skinIndex, boneIndex, ptr, cb); - }); - - allocator.free(ptr); - if (!result) { - throw Exception("Failed to set bone transform"); - } - } - - /// - /// - /// - /// - /// - /// - @override - Future resetBones(covariant FFIAsset asset) async { - AnimationManager_resetToRestPose(animationManager, asset.asset); - } - - /// - /// - /// /// /// /// @@ -872,115 +463,11 @@ class ThermionViewerFFI extends ThermionViewer { } } - /// - /// - /// - @override - Future playAnimation(covariant FFIAsset asset, int index, - {bool loop = false, - bool reverse = false, - bool replaceActive = true, - double crossfade = 0.0, - double startOffset = 0.0}) async { - AnimationManager_playAnimation(animationManager, asset.asset, index, loop, - reverse, replaceActive, crossfade, startOffset); - } - - /// - /// - /// - @override - Future stopAnimation(FFIAsset asset, int animationIndex) async { - AnimationManager_stopAnimation( - animationManager, asset.asset, animationIndex); - } - - /// - /// - /// - @override - Future stopAnimationByName(FFIAsset asset, String name) async { - var animations = await getAnimationNames(asset); - await stopAnimation(asset, animations.indexOf(name)); - } - - /// - /// - /// - @override - Future playAnimationByName(FFIAsset asset, String name, - {bool loop = false, - bool reverse = false, - bool replaceActive = true, - double crossfade = 0.0, - bool wait = false}) async { - var animations = await getAnimationNames(asset); - var index = animations.indexOf(name); - var duration = await getAnimationDuration(asset, index); - await playAnimation(asset, index, - loop: loop, - reverse: reverse, - replaceActive: replaceActive, - crossfade: crossfade); - if (wait) { - await Future.delayed(Duration(milliseconds: (duration * 1000).toInt())); - } - } - - /// - /// - /// - @override - Future setGltfAnimationFrame( - FFIAsset asset, int index, int animationFrame) async { - AnimationManager_setGltfAnimationFrame( - animationManager, asset.asset, index, animationFrame); - } - - /// - /// - /// - Future getMainCamera() async { - final mainCameraEntity = await getMainCameraEntity(); - var camera = await getCameraComponent(mainCameraEntity); - return camera!; - } - - /// - /// - /// - Future getCameraComponent(ThermionEntity cameraEntity) async { - var camera = Engine_getCameraComponent(app.engine, cameraEntity); - if (camera == nullptr) { - throw Exception( - "Failed to get camera component for entity $cameraEntity"); - } - return FFICamera(camera, app); - } - - /// - /// - /// - @override - Future setCamera(ThermionEntity entity, String? name) async { - var cameraNamePtr = - name?.toNativeUtf8(allocator: allocator).cast() ?? nullptr; - final camera = - SceneManager_findCameraByName(_sceneManager!, entity, cameraNamePtr); - if (camera == nullptr) { - throw Exception("Failed to set camera"); - } - final view = (await getViewAt(0)) as FFIView; - View_setCamera(view.view, camera); - allocator.free(cameraNamePtr); - } - /// /// /// @override Future setToneMapping(ToneMapper mapper) async { - final view = await getViewAt(0); view.setToneMapper(mapper); } @@ -1035,134 +522,12 @@ class ThermionViewerFFI extends ThermionViewer { View_setBloom(view.view, enabled, strength); } - /// - /// - /// - @override - Future setCameraFocalLength(double focalLength) async { - throw Exception("DONT USE"); - } - - /// - /// - /// - Future getCameraFov(bool horizontal) async { - var mainCamera = await getMainCamera() as FFICamera; - return Camera_getFov(mainCamera.camera, horizontal); - } - - /// - /// - /// - @override - Future setCameraFov(double degrees, {bool horizontal = true}) async { - throw Exception("DONT USE"); - } - - /// - /// - /// - @override - Future setCameraCulling(double near, double far) async { - throw Exception("DONT USE"); - } - - /// - /// - /// - @override - Future getCameraCullingNear() async { - return getCameraNear(); - } - - Future getCameraNear() async { - var mainCamera = await getMainCamera() as FFICamera; - return Camera_getNear(mainCamera.camera); - } - - /// - /// - /// - @override - Future getCameraCullingFar() async { - var mainCamera = await getMainCamera() as FFICamera; - return Camera_getCullingFar(mainCamera.camera); - } - - /// - /// - /// - @override - Future setCameraFocusDistance(double focusDistance) async { - var mainCamera = await getMainCamera() as FFICamera; - Camera_setFocusDistance(mainCamera.camera, focusDistance); - } - - /// - /// - /// - @override - Future setCameraPosition(double x, double y, double z) async { - var modelMatrix = await getCameraModelMatrix(); - modelMatrix.setTranslation(Vector3(x, y, z)); - return setCameraModelMatrix4(modelMatrix); - } - - /// - /// - /// - @override - Future moveCameraToAsset(ThermionEntity entity) async { - throw Exception("DON'T USE"); - } - /// /// /// @override Future setViewFrustumCulling(bool enabled) async { - var view = await getViewAt(0); - view.setFrustumCullingEnabled(enabled); - } - - /// - /// - /// - @override - Future setCameraExposure( - double aperture, double shutterSpeed, double sensitivity) async { - var mainCamera = await getMainCamera() as FFICamera; - Camera_setExposure(mainCamera.camera, aperture, shutterSpeed, sensitivity); - } - - /// - /// - /// - @override - Future setCameraRotation(Quaternion quaternion) async { - var modelMatrix = await getCameraModelMatrix(); - var translation = modelMatrix.getTranslation(); - modelMatrix = Matrix4.compose(translation, quaternion, Vector3.all(1.0)); - await setCameraModelMatrix4(modelMatrix); - } - - /// - /// - /// - @override - Future setCameraModelMatrix(List matrix) async { - assert(matrix.length == 16); - await setCameraModelMatrix4(Matrix4.fromList(matrix)); - } - - /// - /// - /// - @override - Future setCameraModelMatrix4(Matrix4 modelMatrix) async { - var mainCamera = await getMainCamera() as FFICamera; - final out = matrix4ToDouble4x4(modelMatrix); - Camera_setModelMatrix(mainCamera.camera, out); + await view.setFrustumCullingEnabled(enabled); } /// @@ -1185,13 +550,13 @@ class ThermionViewerFFI extends ThermionViewer { app.lightManager, lightEntity, direction.x, direction.y, direction.z); } - /// /// /// @override String? getNameForEntity(ThermionEntity entity) { - final result = NameComponentManager_getName(app.nameComponentManager, entity); + final result = + NameComponentManager_getName(app.nameComponentManager, entity); if (result == nullptr) { return null; } @@ -1232,7 +597,6 @@ class ThermionViewerFFI extends ThermionViewer { Future pick(int x, int y, void Function(PickResult) resultHandler) async { _pickRequestId++; var pickRequestId = _pickRequestId; - final view = (await getViewAt(0)) as FFIView; _pickRequests[pickRequestId] = (handler: resultHandler, x: x, y: y, view: view); @@ -1247,68 +611,6 @@ class ThermionViewerFFI extends ThermionViewer { }); } - - - /// - /// - /// - @override - Future addAnimationComponent(ThermionEntity entity) async { - AnimationManager_addAnimationComponent(animationManager, entity); - } - - /// - /// - /// - Future removeAnimationComponent(ThermionEntity entity) async { - AnimationManager_removeAnimationComponent(animationManager, entity); - } - - /// - /// - /// - @override - Future createGeometry(Geometry geometry, - {List? materialInstances, - bool keepData = false}) async { - var assetPtr = await withPointerCallback((callback) { - var ptrList = Int64List(materialInstances?.length ?? 0); - if (materialInstances != null && materialInstances.isNotEmpty) { - ptrList.setRange( - 0, - materialInstances.length, - materialInstances - .cast() - .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>(), - ptrList.length, - callback); - }); - if (assetPtr == nullptr) { - throw Exception("Failed to create geometry"); - } - - var asset = FFIAsset(assetPtr, app); - - _assets.add(asset); - - return asset; - } - /// /// /// @@ -1368,295 +670,29 @@ class ThermionViewerFFI extends ThermionViewer { @override Future getViewportBoundingBox(ThermionEntity entityId) async { throw UnimplementedError(); - // final view = (await getViewAt(0)) as FFIView; - // final result = get_bounding_box(_sceneManager!, view.view, entityId); - // return v64.Aabb2.minMax(v64.Vector2(result.minX, result.minY), - // v64.Vector2(result.maxX, result.maxY)); + } + + GridOverlay? _grid; + + /// + /// + /// + Future showGridOverlay() async { + _grid ??= _grid = await GridOverlay.create(app); + await scene.add(_grid!); + await view.setLayerVisibility(VisibilityLayers.OVERLAY, true); } /// /// /// - Future setVisibilityLayer( - ThermionEntity entity, VisibilityLayers layer) async { - RenderableManager_setVisibilityLayer( - app.renderableManager, entity, layer.value); - } - - FFIAsset? _grid; - - /// - /// - /// - Future showGridOverlay({FFIMaterial? material}) async { - if (_grid == null) { - final ptr = await withPointerCallback((cb) { - if (material == null) { - SceneManager_createGridRenderThread(_sceneManager!, nullptr, cb); - } else { - SceneManager_createGridRenderThread( - _sceneManager!, material.pointer, cb); - } - }); - _grid = FFIAsset( - ptr, _sceneManager!, app.engine!, _unlitMaterialProvider!, this); - } - await _grid!.addToScene(); - await setLayerVisibility(VisibilityLayers.OVERLAY, true); - } - - /// - /// - /// - Future removeGridOverlay() async { + Future removeGridOverlay({bool destroy = false}) async { if (_grid != null) { - await _grid!.removeFromScene(); - SceneManager_destroyAsset(_sceneManager!, _grid!.pointer); - _grid = null; - } - } - - /// - /// - /// - Future createTexture(int width, int height, - {int depth = 1, - int levels = 1, - Set flags = const {TextureUsage.TEXTURE_USAGE_SAMPLEABLE}, - TextureSamplerType textureSamplerType = TextureSamplerType.SAMPLER_2D, - TextureFormat textureFormat = TextureFormat.RGBA16F, - int? importedTextureHandle}) async { - var bitmask = flags.fold(0, (a, b) => a | b.index); - final texturePtr = await withPointerCallback((cb) { - Texture_buildRenderThread( - app.engine!, - width, - height, - depth, - levels, - importedTextureHandle ?? 0, - bitmask, - TTextureSamplerType.values[textureSamplerType.index], - TTextureFormat.values[textureFormat.index], - cb); - }); - if (texturePtr == nullptr) { - throw Exception("Failed to create texture"); - } - return FFITexture( - app.engine!, - texturePtr, - ); - } - - Future createTextureSampler( - {TextureMinFilter minFilter = TextureMinFilter.LINEAR, - TextureMagFilter magFilter = TextureMagFilter.LINEAR, - TextureWrapMode wrapS = TextureWrapMode.CLAMP_TO_EDGE, - TextureWrapMode wrapT = TextureWrapMode.CLAMP_TO_EDGE, - TextureWrapMode wrapR = TextureWrapMode.CLAMP_TO_EDGE, - double anisotropy = 0.0, - TextureCompareMode compareMode = TextureCompareMode.NONE, - TextureCompareFunc compareFunc = TextureCompareFunc.LESS_EQUAL}) async { - final samplerPtr = TextureSampler_create(); - TextureSampler_setMinFilter( - samplerPtr, TSamplerMinFilter.values[minFilter.index]); - TextureSampler_setMagFilter( - samplerPtr, TSamplerMagFilter.values[magFilter.index]); - TextureSampler_setWrapModeS( - samplerPtr, TSamplerWrapMode.values[wrapS.index]); - TextureSampler_setWrapModeT( - samplerPtr, TSamplerWrapMode.values[wrapT.index]); - TextureSampler_setWrapModeR( - samplerPtr, TSamplerWrapMode.values[wrapR.index]); - if (anisotropy > 0) { - TextureSampler_setAnisotropy(samplerPtr, anisotropy); - } - - TextureSampler_setCompareMode( - samplerPtr, - TSamplerCompareMode.values[compareMode.index], - TSamplerCompareFunc.values[compareFunc.index]); - - return FFITextureSampler(samplerPtr); - } - - /// - /// - /// - Future decodeImage(Uint8List data) async { - final name = "image"; - var ptr = Image_decode( - data.address, - data.length, - name.toNativeUtf8().cast(), - ); - if (ptr == nullptr) { - throw Exception("Failed to decode image"); - } - return FFILinearImage(ptr); - } - - /// - /// Creates an (empty) imge with the given dimensions. - /// - Future createImage(int width, int height, int channels) async { - final ptr = Image_createEmpty(width, height, channels); - return FFILinearImage(ptr); - } - - /// - /// - /// - Future createMaterial(Uint8List data) async { - var ptr = await withPointerCallback((cb) { - Engine_buildMaterialRenderThread( - app.engine!, data.address, data.length, cb); - }); - return FFIMaterial(ptr, app); - } - - /// - /// - /// - Future createUbershaderMaterialInstance( - {bool doubleSided = false, - bool unlit = false, - bool hasVertexColors = false, - bool hasBaseColorTexture = false, - bool hasNormalTexture = false, - bool hasOcclusionTexture = false, - bool hasEmissiveTexture = false, - bool useSpecularGlossiness = false, - AlphaMode alphaMode = AlphaMode.OPAQUE, - bool enableDiagnostics = false, - bool hasMetallicRoughnessTexture = false, - int metallicRoughnessUV = 0, - int baseColorUV = 0, - bool hasClearCoatTexture = false, - int clearCoatUV = 0, - bool hasClearCoatRoughnessTexture = false, - int clearCoatRoughnessUV = 0, - bool hasClearCoatNormalTexture = false, - int clearCoatNormalUV = 0, - bool hasClearCoat = false, - bool hasTransmission = false, - bool hasTextureTransforms = false, - int emissiveUV = 0, - int aoUV = 0, - int normalUV = 0, - bool hasTransmissionTexture = false, - int transmissionUV = 0, - bool hasSheenColorTexture = false, - int sheenColorUV = 0, - bool hasSheenRoughnessTexture = false, - int sheenRoughnessUV = 0, - bool hasVolumeThicknessTexture = false, - int volumeThicknessUV = 0, - bool hasSheen = false, - bool hasIOR = false, - bool hasVolume = false}) async { - final key = Struct.create(); - - key.doubleSided = doubleSided; - key.unlit = unlit; - key.hasVertexColors = hasVertexColors; - key.hasBaseColorTexture = hasBaseColorTexture; - key.hasNormalTexture = hasNormalTexture; - key.hasOcclusionTexture = hasOcclusionTexture; - key.hasEmissiveTexture = hasEmissiveTexture; - key.useSpecularGlossiness = useSpecularGlossiness; - key.alphaMode = alphaMode.index; - key.enableDiagnostics = enableDiagnostics; - key.unnamed.unnamed.hasMetallicRoughnessTexture = - hasMetallicRoughnessTexture; - key.unnamed.unnamed.metallicRoughnessUV = 0; - key.baseColorUV = baseColorUV; - key.hasClearCoatTexture = hasClearCoatTexture; - key.clearCoatUV = clearCoatUV; - key.hasClearCoatRoughnessTexture = hasClearCoatRoughnessTexture; - key.clearCoatRoughnessUV = clearCoatRoughnessUV; - key.hasClearCoatNormalTexture = hasClearCoatNormalTexture; - key.clearCoatNormalUV = clearCoatNormalUV; - key.hasClearCoat = hasClearCoat; - key.hasTransmission = hasTransmission; - key.hasTextureTransforms = hasTextureTransforms; - key.emissiveUV = emissiveUV; - key.aoUV = aoUV; - key.normalUV = normalUV; - key.hasTransmissionTexture = hasTransmissionTexture; - key.transmissionUV = transmissionUV; - key.hasSheenColorTexture = hasSheenColorTexture; - key.sheenColorUV = sheenColorUV; - key.hasSheenRoughnessTexture = hasSheenRoughnessTexture; - key.sheenRoughnessUV = sheenRoughnessUV; - key.hasVolumeThicknessTexture = hasVolumeThicknessTexture; - key.volumeThicknessUV = volumeThicknessUV; - key.hasSheen = hasSheen; - key.hasIOR = hasIOR; - key.hasVolume = hasVolume; - - final materialInstance = await withPointerCallback((cb) { - MaterialProvider_createMaterialInstanceRenderThread( - app.ubershaderMaterialProvider, key.address, cb); - }); - if (materialInstance == nullptr) { - throw Exception("Failed to create material instance"); - } - - var instance = FFIMaterialInstance(materialInstance, app); - _materialInstances.add(instance); - return instance; - } - - /// - /// - /// - Future destroyMaterialInstance(FFIMaterialInstance materialInstance) async { - await materialInstance.destroy(); - _materialInstances.remove(materialInstance); - } - - /// - /// - /// - Future createUnlitMaterialInstance() async { - final instance = await createUbershaderMaterialInstance(unlit: true); - return instance as FFIMaterialInstance; - } - - /// - /// - /// - Future getMaterialInstanceAt( - ThermionEntity entity, int index) async { - final instancePtr = RenderableManager_getMaterialInstanceAt( - app.renderableManager, entity, index); - - final instance = FFIMaterialInstance(instancePtr, _sceneManager!); - return instance; - } - - /// - /// - /// - @override - Future requestFrame() async { - for (final hook in _hooks) { - await hook.call(); - } - final completer = Completer(); - - final callback = NativeCallable.listener(() { - completer.complete(true); - }); - - RenderLoop_requestAnimationFrame(callback.nativeFunction.cast()); - - try { - await completer.future.timeout(Duration(seconds: 1)); - } catch (err) { - print("WARNING - render call timed out"); + await scene.remove(_grid!); + if (destroy) { + await destroyAsset(_grid!); + _grid = null; + } } } @@ -1697,22 +733,6 @@ class ThermionViewerFFI extends ThermionViewer { return view.getCamera(); } - final _hooks = []; - - @override - Future registerRequestFrameHook(Future Function() hook) async { - if (!_hooks.contains(hook)) { - _hooks.add(hook); - } - } - - @override - Future unregisterRequestFrameHook(Future Function() hook) async { - if (_hooks.contains(hook)) { - _hooks.remove(hook); - } - } - /// /// /// @@ -1758,107 +778,3 @@ class ThermionViewerFFI extends ThermionViewer { // ..add(SceneAsset_getEntity(gizmo.cast()))); } } - - - - - - // /// - // /// - // /// - // Future setClearOptions( - // Vector4 clearColor, int clearStencil, bool clear, bool discard) async { - // Renderer_setClearOptions(app.renderer, clearColor.r, clearColor.g, - // clearColor.b, clearColor.a, clearStencil, clear, discard); - // } - - // /// - // /// - // /// - // @override - // Future capture() async { - // final fence = await withPointerCallback((cb) { - // Engine_createFenceRenderThread(app.engine!, cb); - // }); - - // var pixelBuffers = []; - - // for (final entry in targets) { - // final view = entry.view as FFIView; - // var swapChain = entry.swapChain as FFISwapChain?; - // final renderTarget = entry.renderTarget as FFIRenderTarget?; - // final vp = await view.getViewport(); - // final length = vp.width * vp.height * 4; - - // await withBoolCallback((cb) { - // Renderer_beginFrameRenderThread(renderer, - // swapChain?.swapChain ?? Viewer_getSwapChainAt(_viewer!, 0), 0, cb); - // }); - - // await withVoidCallback((cb) { - // Renderer_renderRenderThread(renderer, view.view, cb); - // }); - // final out = Uint8List(length); - // await withVoidCallback((cb) { - // Renderer_readPixelsRenderThread( - // renderer, - // view.view, - // renderTarget!.renderTarget, - // TPixelDataFormat.PIXELDATAFORMAT_RGBA, - // TPixelDataType.PIXELDATATYPE_UBYTE, - // out.address, - // cb); - // }); - - // pixelBuffers.add(out); - // } - - // await withVoidCallback((cb) { - // Renderer_endFrameRenderThread(renderer, cb); - // }); - - // await withVoidCallback((cb) { - // Engine_flushAndWaitRenderThead(app.engine!, cb); - // }); - - // await withVoidCallback((cb) { - // Engine_destroyFenceRenderThread(app.engine!, fence, cb); - // }); - - // // await withVoidCallback((cb) { - // // if (renderTarget != null) { - // // Viewer_captureRenderTargetRenderThread( - // // _viewer!, - // // view!.view, - // // swapChain!.swapChain, - // // renderTarget.renderTarget, - // // out.address, - // // useFence, - // // cb); - // // } else { - // // Viewer_captureRenderThread(_viewer!, view!.view, swapChain!.swapChain, - // // out.address, useFence, cb); - // // } - // // }); - // return pixelBuffers; - // } - - // /// - // /// Queues an update to the worldspace position for [entity] to the viewport coordinates {x,y}. - // /// The actual update will occur on the next frame, and will be subject to collision detection. - // /// - // Future queuePositionUpdateFromViewportCoords( - // ThermionEntity entity, double x, double y) async { - // final view = (await getViewAt(0)) as FFIView; - // queue_position_update_from_viewport_coords( - // _sceneManager!, view.view, entity, x, y); - // } - - // /// - // /// - // /// - // Future queueRelativePositionUpdateWorldAxis(ThermionEntity entity, - // double viewportX, double viewportY, double x, double y, double z) async { - // queue_relative_position_update_world_axis( - // _sceneManager!, entity, viewportX, viewportY, x, y, z); - // } \ No newline at end of file diff --git a/thermion_dart/lib/src/viewer/src/ffi/thermion_viewer_ffi.dart b/thermion_dart/lib/src/viewer/src/ffi/thermion_viewer_ffi.dart index 06003446..87c933b8 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/thermion_viewer_ffi.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/thermion_viewer_ffi.dart @@ -1,3 +1,4 @@ library; export 'src/thermion_viewer_ffi.dart' show ThermionViewerFFI; + diff --git a/thermion_dart/lib/src/viewer/src/filament/filament.dart b/thermion_dart/lib/src/viewer/src/filament/filament.dart deleted file mode 100644 index a9889e9a..00000000 --- a/thermion_dart/lib/src/viewer/src/filament/filament.dart +++ /dev/null @@ -1,78 +0,0 @@ -import 'package:thermion_dart/src/viewer/src/shared_types/engine.dart'; -import 'package:thermion_dart/thermion_dart.dart'; - -class FilamentConfig { - final Backend backend; - final T? renderCallback; - final U? renderCallbackOwner; - final U resourceLoader; - final U? platform; - final U? driver; - final U? sharedContext; - final String uberArchivePath; - final int stereoscopicEyeCount; - final bool disableHandleUseAfterFreeCheck; - - FilamentConfig( - {required this.backend, - required this.resourceLoader, - required this.uberArchivePath, - this.renderCallback, - this.renderCallbackOwner, - this.platform, - this.driver, - this.sharedContext, - this.stereoscopicEyeCount = 1, - this.disableHandleUseAfterFreeCheck = false}); -} - -abstract class FilamentApp { - final T engine; - final T gltfAssetLoader; - final T gltfResourceLoader; - final T renderer; - final T transformManager; - final T lightManager; - final T renderableManager; - final T ubershaderMaterialProvider; - - FilamentApp( - {required this.engine, - required this.gltfAssetLoader, - required this.gltfResourceLoader, - required this.renderer, - required this.transformManager, - required this.lightManager, - required this.renderableManager, - required this.ubershaderMaterialProvider - }); - - /// - /// - /// - Future createHeadlessSwapChain(int width, int height, - {bool hasStencilBuffer = false}); - - /// - /// - /// - Future createSwapChain(T handle, {bool hasStencilBuffer = false}); - - /// - /// - /// - Future destroySwapChain(SwapChain swapChain); - - /// - /// - /// - Future destroy(); - - - /// - /// - /// - Future createRenderTarget( - int width, int height, { covariant Texture? color, covariant Texture? depth }); - -} diff --git a/thermion_dart/lib/src/viewer/src/shared_types/entities.dart b/thermion_dart/lib/src/viewer/src/shared_types/entities.dart deleted file mode 100644 index b6f0cc8f..00000000 --- a/thermion_dart/lib/src/viewer/src/shared_types/entities.dart +++ /dev/null @@ -1,96 +0,0 @@ -library; - -import 'package:thermion_dart/thermion_dart.dart'; -import 'package:vector_math/vector_math_64.dart'; - -export 'geometry.dart'; -export 'gltf.dart'; - -export 'light_options.dart'; - -// handle manipulate an Entity -typedef ThermionEntity = int; - -abstract class ThermionAsset { - /// - /// - /// - ThermionEntity get entity; - - /// - /// - /// - Future> getChildEntities(); - - /// - /// - /// - Future setMaterialInstanceAt(covariant MaterialInstance instance); - - /// - /// Renders an outline around [entity] with the given color. - /// - Future setStencilHighlight( - {double r = 1.0, double g = 0.0, double b = 0.0, int? entityIndex}); - - /// - /// Removes the outline around [entity]. Noop if there was no highlight. - /// - Future removeStencilHighlight(); - - /// - /// When visible is [true], renders the bounding box. - /// - Future setBoundingBoxVisibility(bool visible); - - /// - /// - /// - Future getInstance(int index); - - /// - /// Create a new instance of [entity]. - /// Instances are not automatically added to the scene; you must - /// call [addToScene]. - /// - Future createInstance( - {covariant List? materialInstances = null}); - - /// - /// Returns the number of instances associated with this asset. - /// - Future getInstanceCount(); - - /// - /// Returns all instances of associated with this asset. - /// - Future> getInstances(); - - Future setCastShadows(bool castShadows); - Future setReceiveShadows(bool castShadows); -} - -enum Axis { - X(const [1.0, 0.0, 0.0]), - Y(const [0.0, 1.0, 0.0]), - Z(const [0.0, 0.0, 1.0]); - - const Axis(this.vector); - - final List vector; - - Vector3 asVector() => Vector3(vector[0], vector[1], vector[2]); -} - -enum GizmoPickResultType { AxisX, AxisY, AxisZ, Parent, None } - -enum GizmoType { translation, rotation } - -abstract class GizmoAsset extends ThermionAsset { - Future pick(int x, int y, - {Future Function(GizmoPickResultType axis, Vector3 coords)? handler}); - Future highlight(Axis axis); - Future unhighlight(); - bool isNonPickable(ThermionEntity entity); - bool isGizmoEntity(ThermionEntity entity); -} diff --git a/thermion_dart/lib/src/viewer/src/shared_types/entity.dart b/thermion_dart/lib/src/viewer/src/shared_types/entity.dart deleted file mode 100644 index f9e8b651..00000000 --- a/thermion_dart/lib/src/viewer/src/shared_types/entity.dart +++ /dev/null @@ -1,3 +0,0 @@ -import 'package:thermion_dart/thermion_dart.dart'; - -final ThermionEntity FILAMENT_ENTITY_NULL = 0; \ No newline at end of file diff --git a/thermion_dart/lib/src/viewer/src/thermion_viewer_base.dart b/thermion_dart/lib/src/viewer/src/thermion_viewer_base.dart index 13fb7e07..a433378e 100644 --- a/thermion_dart/lib/src/viewer/src/thermion_viewer_base.dart +++ b/thermion_dart/lib/src/viewer/src/thermion_viewer_base.dart @@ -1,23 +1,28 @@ -import 'package:thermion_dart/src/viewer/src/shared_types/layers.dart'; - -import '../../utils/src/gizmo.dart'; -import 'shared_types/shared_types.dart'; -export 'shared_types/shared_types.dart'; - -import 'dart:math'; +import 'package:thermion_dart/src/filament/src/filament_app.dart'; +import '../../filament/src/shared_types.dart'; import 'dart:typed_data'; import 'package:vector_math/vector_math_64.dart'; import 'dart:async'; -import 'package:animation_tools_dart/animation_tools_dart.dart'; /// -/// A high-level interface for interacting with a 3D scene. -/// This broadly maps to a single scene/view +/// A (high-level) interface for a 3D scene. +/// +/// Use this to add/remove assets, lights and cameras. +/// +/// Multiple instances can be created; each will correspond +/// broadly to a single Filament Scene/View. +/// +/// If you know yhat you are doing, you can use a lower level interface by +/// using the methods directly via FilamentApp.instance; /// abstract class ThermionViewer { - /// - /// Whether the controller is currently rendering at [framerate]. + /// + /// + View get view; + + /// + /// If [true], this Viewer should render itself /// bool get rendering; @@ -31,29 +36,6 @@ abstract class ThermionViewer { /// Future render(); - /// - /// Requests a single frame to be rendered. This is only intended to be used internally. - /// - Future requestFrame(); - - /// - /// Render a single frame and return the captured image as a pixel buffer. - /// - Future> capture( - covariant List< - ({View view, SwapChain? swapChain, RenderTarget? renderTarget})> - targets); - - /// - /// - /// - Future createView(); - - /// - /// - /// - Future getViewAt(int index); - /// /// /// @@ -119,32 +101,6 @@ abstract class ThermionViewer { /// Future removeIbl(); - /// - /// Add a light to the scene. - /// See LightManager.h for details - /// Note that [sunAngularRadius] is in degrees, - /// whereas [spotLightConeInner] and [spotLightConeOuter] are in radians - /// - @Deprecated( - "This will be removed in future versions. Use addDirectLight instead.") - Future addLight( - LightType type, - double colour, - double intensity, - double posX, - double posY, - double posZ, - double dirX, - double dirY, - double dirZ, - {double falloffRadius = 1.0, - double spotLightConeInner = pi / 8, - double spotLightConeOuter = pi / 4, - double sunAngularRadius = 0.545, - double sunHaloSize = 10.0, - double sunHaloFallof = 80.0, - bool castShadows = true}); - /// /// Adds a direct light to the scene. /// See LightManager.h for details @@ -198,139 +154,6 @@ abstract class ThermionViewer { Future loadGltf(String path, String relativeResourcePath, {bool keepData = false}); - /// - /// Set the weights for all morph targets in [entity] to [weights]. - /// Note that [weights] must contain values for ALL morph targets, but no exception will be thrown if you don't do so (you'll just get incorrect results). - /// If you only want to set one value, set all others to zero (check [getMorphTargetNames] if you need the get a list of all morph targets). - /// IMPORTANT - this accepts the actual ThermionEntity with the relevant morph targets (unlike [getMorphTargetNames], which uses the parent entity and the child mesh name). - /// Use [getChildEntityByName] if you are setting the weights for a child mesh. - /// - Future setMorphTargetWeights(ThermionEntity entity, List weights); - - /// - /// Gets the names of all morph targets for the child renderable [childEntity] under [entity]. - /// - Future> getMorphTargetNames( - covariant ThermionAsset asset, ThermionEntity childEntity); - - /// - /// Gets the names of all bones for the armature at [skinIndex] under the specified [entity]. - /// - Future> getBoneNames(covariant ThermionAsset asset, - {int skinIndex = 0}); - - /// - /// Gets the names of all glTF animations embedded in the specified entity. - /// - Future> getAnimationNames(covariant ThermionAsset asset); - - /// - /// Returns the length (in seconds) of the animation at the given index. - /// - Future getAnimationDuration( - 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. - /// [MorphTargetAnimation] for an explanation as to how to construct the animation frame data. - /// This method will check the morph target names specified in [animation] against the morph target names that actually exist exist under [meshName] in [entity], - /// throwing an exception if any cannot be found. - /// It is permissible for [animation] to omit any targets that do exist under [meshName]; these simply won't be animated. - /// - Future setMorphAnimationData( - covariant ThermionAsset asset, MorphAnimationData animation, - {List? targetMeshNames}); - - /// - /// Clear all current morph animations for [entity]. - /// - Future clearMorphAnimationData(ThermionEntity entity); - - /// - /// Resets all bones in the given entity to their rest pose. - /// This should be done before every call to addBoneAnimation. - /// - Future resetBones(ThermionAsset asset); - - /// - /// Enqueues and plays the [animation] for the specified bone(s). - /// By default, frame data is interpreted as being in *parent* bone space; - /// a 45 degree around Y means the bone will rotate 45 degrees around the - /// Y axis of the parent bone *in its current orientation*. - /// (i.e NOT the parent bone's rest position!). - /// Currently, only [Space.ParentBone] and [Space.Model] are supported; if you want - /// to transform to another space, you will need to do so manually. - /// - /// [fadeInInSecs]/[fadeOutInSecs]/[maxDelta] are used to cross-fade between - /// the current active glTF animation ("animation1") and the animation you - /// set via this method ("animation2"). The bone orientations will be - /// linearly interpolated between animation1 and animation2; at time 0, - /// the orientation will be 100% animation1, at time [fadeInInSecs], the - /// animation will be ((1 - maxDelta) * animation1) + (maxDelta * animation2). - /// This will be applied in reverse after [fadeOutInSecs]. - /// - /// - Future addBoneAnimation(ThermionAsset asset, BoneAnimationData animation, - {int skinIndex = 0, - double fadeInInSecs = 0.0, - double fadeOutInSecs = 0.0, - double maxDelta = 1.0}); - - /// - /// Gets the entity representing the bone at [boneIndex]/[skinIndex]. - /// The returned entity is only intended for use with [getWorldTransform]. - /// - Future getBone(covariant ThermionAsset asset, int boneIndex, - {int skinIndex = 0}); - - /// - /// Gets the local (relative to parent) transform for [entity]. - /// - Future getLocalTransform(ThermionEntity entity); - - /// - /// Gets the world transform for [entity]. - /// - Future getWorldTransform(ThermionEntity entity); - - /// - /// Gets the inverse bind (pose) matrix for the bone. - /// Note that [parent] must be the ThermionEntity returned by [loadGlb/loadGltf], not any other method ([getChildEntity] etc). - /// This is because all joint information is internally stored with the parent entity. - /// - Future getInverseBindMatrix( - covariant ThermionAsset asset, int boneIndex, - {int skinIndex = 0}); - - /// - /// Sets the transform (relative to its parent) for [entity]. - /// - Future setTransform(ThermionEntity entity, Matrix4 transform); - - /// - /// Sets multiple transforms (relative to parent) simultaneously for [entity]. - /// Uses mutex to ensure that transform updates aren't split across frames. - /// - Future queueTransformUpdates( - List entities, List transforms); - - /// - /// Updates the bone matrices for [entity] (which must be the ThermionEntity - /// returned by [loadGlb/loadGltf]). - /// Under the hood, this just calls [updateBoneMatrices] on the Animator - /// instance of the relevant FilamentInstance (which uses the local - /// bone transform and the inverse bind matrix to set the bone matrix). - /// - Future updateBoneMatrices(ThermionEntity entity); - - /// - /// Directly set the bone matrix for the bone at the given index. - /// Don't call this manually unless you know what you're doing. - /// - Future setBoneTransform( - ThermionEntity entity, int boneIndex, Matrix4 transform, - {int skinIndex = 0}); - /// /// Destroys [asset] and all underlying resources /// (including instances, but excluding any manually created material instances). @@ -343,72 +166,6 @@ abstract class ThermionViewer { /// Future destroyAssets(); - /// - /// Schedules the glTF animation at [index] in [asset] to start playing on the next frame. - /// - Future playAnimation(ThermionAsset asset, int index, - {bool loop = false, - bool reverse = false, - bool replaceActive = true, - double crossfade = 0.0, - double startOffset = 0.0}); - - /// - /// Schedules the glTF animation at [index] in [entity] to start playing on the next frame. - /// - Future playAnimationByName(covariant ThermionAsset asset, String name, - {bool loop = false, - bool reverse = false, - bool replaceActive = true, - double crossfade = 0.0}); - - /// - /// - /// - Future setGltfAnimationFrame( - covariant ThermionAsset asset, int index, int animationFrame); - - /// - /// - /// - Future stopAnimation(covariant ThermionAsset asset, int animationIndex); - - /// - /// - /// - Future stopAnimationByName(covariant ThermionAsset asset, String name); - - /// - /// Sets the current scene camera to the glTF camera under [name] in [entity]. - /// - Future setCamera(ThermionEntity entity, String? name); - - /// - /// Sets the current scene camera to the main camera (which is always available and added to every scene by default). - /// - Future setMainCamera(); - - /// - /// Returns the entity associated with the main camera. You probably never need this; use getMainCamera instead. - /// - Future getMainCameraEntity(); - - /// - /// Returns the Camera instance for the main camera. - /// - Future getMainCamera(); - - /// - /// Sets the horizontal field of view (if [horizontal] is true) or vertical field of view for the currently active camera to [degrees]. - /// The aspect ratio of the current viewport is used. - /// - Future setCameraFov(double degrees, {bool horizontal = true}); - - /// - /// Gets the field of view (in degrees). - /// - Future getCameraFov(bool horizontal); - /// /// Sets the tone mapping (requires postprocessing). /// @@ -419,115 +176,11 @@ abstract class ThermionViewer { /// Future setBloom(bool enabled, double strength); - /// - /// Sets the focal length of the camera. Default value is 28.0. - /// - Future setCameraFocalLength(double focalLength); - - /// - /// Sets the distance (in world units) to the near/far planes for the active camera. Default values are 0.05/1000.0. See Camera.h for details. - /// - Future setCameraCulling(double near, double far); - - /// - /// Get the distance (in world units) to the near plane for the active camera. - /// - @Deprecated("Use getCameraNear") - Future getCameraCullingNear(); - - /// - /// Get the distance (in world units) to the near plane for the active camera. - /// - Future getCameraNear(); - - /// - /// Get the distance (in world units) to the far culling plane for the active camera. - /// - Future getCameraCullingFar(); - - /// - /// Sets the focus distance for the camera. - /// - Future setCameraFocusDistance(double focusDistance); - - /// - /// Get the camera position in world space. - /// - Future getCameraPosition(); - - /// - /// Get the camera's model matrix. - /// - Future getCameraModelMatrix(); - - /// - /// Get the camera's view matrix. See Camera.h for more details. - /// - Future getCameraViewMatrix(); - - /// - /// Get the camera's projection matrix. See Camera.h for more details. - /// - Future getCameraProjectionMatrix(); - - /// - /// Get the camera's culling projection matrix. See Camera.h for more details. - /// - Future getCameraCullingProjectionMatrix(); - - /// - /// Get the camera's culling frustum in world space. Returns a (vector_math) [Frustum] instance where plane0-plane6 define the left, right, bottom, top, far and near planes respectively. - /// See Camera.h and (filament) Frustum.h for more details. - /// - Future getCameraFrustum(); - - /// - /// Set the camera position in world space. Note this is not persistent - any viewport navigation will reset the camera transform. - /// - Future setCameraPosition(double x, double y, double z); - - /// - /// Get the camera rotation matrix. - /// - Future getCameraRotation(); - - /// - /// Repositions the camera to the last vertex of the bounding box of [entity], looking at the penultimate vertex. - /// - Future moveCameraToAsset(ThermionEntity entity); - /// /// Enables/disables frustum culling. /// Future setViewFrustumCulling(bool enabled); - /// - /// Sets the camera exposure. - /// - Future setCameraExposure( - double aperture, double shutterSpeed, double sensitivity); - - /// - /// Rotate the camera by [rads] around the given axis. - /// - Future setCameraRotation(Quaternion quaternion); - - /// - /// Sets the camera model matrix. - /// - @Deprecated("Will be superseded by setCameraModelMatrix4") - Future setCameraModelMatrix(List matrix); - - /// - /// Sets the camera model matrix. - /// - Future setCameraModelMatrix4(Matrix4 matrix); - - /// - /// Scale [entity] to fit within the unit cube. - /// - Future transformToUnitCube(ThermionEntity entity); - /// /// Set the world space position for [lightEntity] to the given coordinates. /// @@ -539,18 +192,6 @@ abstract class ThermionViewer { /// Future setLightDirection(ThermionEntity lightEntity, Vector3 direction); - /// - /// TODO - /// - Future queuePositionUpdateFromViewportCoords( - ThermionEntity entity, double x, double y); - - /// - /// TODO - /// - Future queueRelativePositionUpdateWorldAxis(ThermionEntity entity, - double viewportX, double viewportY, double x, double y, double z); - /// /// Enable/disable postprocessing effects (anti-aliasing, tone mapping, bloom). Disabled by default. /// @@ -576,16 +217,6 @@ abstract class ThermionViewer { /// Future setAntiAliasing(bool msaa, bool fxaa, bool taa); - /// - /// Adds a single [entity] to the scene. - /// - Future addEntityToScene(ThermionEntity entity); - - /// - /// Removes a single [entity] from the scene. - /// - Future removeAssetFromScene(ThermionEntity entity); - /// /// Hit test the viewport at the given coordinates. If the coordinates intersect /// with a renderable entity, [resultHandler] will be called. @@ -599,37 +230,6 @@ abstract class ThermionViewer { /// String? getNameForEntity(ThermionEntity entity); - /// - /// Returns all child entities under [asset]. - /// - Future> getChildEntities(covariant ThermionAsset asset); - - /// - /// Finds the child entity named [childName] associated with the given parent. - /// Usually, [parent] will be the return value from [loadGlb]/[loadGltf] and [childName] will be the name of a node/mesh. - /// - Future getChildEntity( - covariant ThermionAsset asset, String childName); - - /// - /// An [entity] will only be animatable after an animation component is attached. - /// Any calls to [playAnimation]/[setBoneAnimation]/[setMorphAnimation] will have no visual effect until [addAnimationComponent] has been called on the instance. - /// - Future addAnimationComponent(ThermionEntity entity); - - /// - /// Removes an animation component from [entity]. - /// - Future removeAnimationComponent(ThermionEntity entity); - - /// - /// Creates a (renderable) entity with the specified geometry and adds to the scene. - /// If [keepData] is true, the source data will not be released. - /// - Future createGeometry(Geometry geometry, - {covariant List? materialInstances, - bool keepData = false}); - /// /// Gets the parent entity of [entity]. Returns null if the entity has no parent. /// @@ -672,131 +272,15 @@ abstract class ThermionViewer { Future getViewportBoundingBox(ThermionEntity entity); /// - /// Toggles the visibility of the respective layer. - /// - Future setLayerVisibility(VisibilityLayers layer, bool visible); - - /// - /// All renderable entities are assigned a layer mask. - /// - /// By calling [setLayerVisibility], all renderable entities allocated to - /// the given layer can be efficiently hidden/revealed. - /// - /// By default, all renderable entities are assigned to layer 0 (and this - /// layer is enabled by default). Call [setVisibilityLayer] to change the - /// layer for the specified entity. - /// - /// Note that we currently also assign gizmos to layer 1 (enabled by default) - /// and the world grid to layer 2 (disabled by default). We suggest you avoid - /// using these layers. - /// - Future setVisibilityLayer(ThermionEntity entity, VisibilityLayers layer); - /// /// - /// - Future showGridOverlay({covariant Material? material}); + Future showGridOverlay(); /// /// /// Future removeGridOverlay(); - /// - /// - /// - Future createTexture(int width, int height, - {int depth = 1, - int levels = 1, - TextureSamplerType textureSamplerType = TextureSamplerType.SAMPLER_2D, - TextureFormat textureFormat = TextureFormat.RGBA32F}); - - /// - /// - /// - Future createTextureSampler( - {TextureMinFilter minFilter = TextureMinFilter.LINEAR, - TextureMagFilter magFilter = TextureMagFilter.LINEAR, - TextureWrapMode wrapS = TextureWrapMode.CLAMP_TO_EDGE, - TextureWrapMode wrapT = TextureWrapMode.CLAMP_TO_EDGE, - TextureWrapMode wrapR = TextureWrapMode.CLAMP_TO_EDGE, - double anisotropy = 0.0, - TextureCompareMode compareMode = TextureCompareMode.NONE, - TextureCompareFunc compareFunc = TextureCompareFunc.LESS_EQUAL}); - - /// - /// Decodes the specified image data. - /// - Future decodeImage(Uint8List data); - - /// - /// Creates an (empty) imge with the given dimensions. - /// - Future createImage(int width, int height, int channels); - - /// - /// - /// - Future createMaterial(Uint8List data); - - /// - /// - /// - Future createUbershaderMaterialInstance({ - bool doubleSided = false, - bool unlit = false, - bool hasVertexColors = false, - bool hasBaseColorTexture = false, - bool hasNormalTexture = false, - bool hasOcclusionTexture = false, - bool hasEmissiveTexture = false, - bool useSpecularGlossiness = false, - AlphaMode alphaMode = AlphaMode.OPAQUE, - bool enableDiagnostics = false, - bool hasMetallicRoughnessTexture = false, - int metallicRoughnessUV = -1, - int baseColorUV = -1, - bool hasClearCoatTexture = false, - int clearCoatUV = -1, - bool hasClearCoatRoughnessTexture = false, - int clearCoatRoughnessUV = -1, - bool hasClearCoatNormalTexture = false, - int clearCoatNormalUV = -1, - bool hasClearCoat = false, - bool hasTransmission = false, - bool hasTextureTransforms = false, - int emissiveUV = -1, - int aoUV = -1, - int normalUV = -1, - bool hasTransmissionTexture = false, - int transmissionUV = -1, - bool hasSheenColorTexture = false, - int sheenColorUV = -1, - bool hasSheenRoughnessTexture = false, - int sheenRoughnessUV = -1, - bool hasVolumeThicknessTexture = false, - int volumeThicknessUV = -1, - bool hasSheen = false, - bool hasIOR = false, - bool hasVolume = false, - }); - - /// - /// - /// - Future destroyMaterialInstance(covariant MaterialInstance materialInstance); - - /// - /// - /// - Future createUnlitMaterialInstance(); - - /// - /// - /// - Future getMaterialInstanceAt( - ThermionEntity entity, int index); - /// /// /// @@ -817,52 +301,8 @@ abstract class ThermionViewer { /// Future getActiveCamera(); - /// - /// - /// - Future registerRequestFrameHook(Future Function() hook); - - /// - /// - /// - Future unregisterRequestFrameHook(Future Function() hook); - /// /// /// int getCameraCount(); - - /// - /// Returns the camera specified by the given index. Note that the camera at - /// index 0 is always the main camera; this cannot be destroyed. - /// - /// Throws an exception if the index is out-of-bounds. - /// - Camera getCameraAt(int index); - - /// - /// - /// - Future setCastShadows(ThermionEntity entity, bool castShadows); - - /// - /// - /// - Future isCastShadowsEnabled(ThermionEntity entity); - - /// - /// - /// - Future setReceiveShadows(ThermionEntity entity, bool receiveShadows); - - /// - /// - /// - Future isReceiveShadowsEnabled(ThermionEntity entity); - - /// - /// - /// - Future setClearOptions( - Vector4 clearColor, int clearStencil, bool clear, bool discard); } diff --git a/thermion_dart/lib/src/viewer/src/thermion_viewer_stub.dart b/thermion_dart/lib/src/viewer/src/thermion_viewer_stub.dart index b16ea582..9d32fdd6 100644 --- a/thermion_dart/lib/src/viewer/src/thermion_viewer_stub.dart +++ b/thermion_dart/lib/src/viewer/src/thermion_viewer_stub.dart @@ -1,15 +1,9 @@ -import 'dart:math'; import 'dart:typed_data'; -import 'package:thermion_dart/src/utils/src/gizmo.dart'; -import 'package:thermion_dart/src/viewer/src/shared_types/swap_chain.dart'; -import 'package:thermion_dart/src/viewer/src/shared_types/view.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:vector_math/vector_math_64.dart'; -import 'dart:async'; -import 'package:animation_tools_dart/animation_tools_dart.dart'; - -import 'shared_types/camera.dart'; class ThermionViewerStub extends ThermionViewer { @override @@ -19,112 +13,120 @@ class ThermionViewerStub extends ThermionViewer { } @override - Future addCollisionComponent(ThermionEntity entity, - {void Function(int entityId1, int entityId2)? callback, - bool affectsTransform = false}) { - // TODO: implement addCollisionComponent + 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 - Future addLight( - LightType type, - double colour, - double intensity, - double posX, - double posY, - double posZ, - double dirX, - double dirY, - double dirZ, - {double falloffRadius = 1.0, - double spotLightConeInner = pi / 8, - double spotLightConeOuter = pi / 4, - double sunAngularRadius = 0.545, - double sunHaloSize = 10.0, - double sunHaloFallof = 80.0, - bool castShadows = true}) { - // TODO: implement addLight + Future addDirectLight(DirectLight light) { + // TODO: implement addDirectLight throw UnimplementedError(); } + @override + // TODO: implement app + FilamentApp get app => throw UnimplementedError(); + @override Future clearBackgroundImage() { // TODO: implement clearBackgroundImage throw UnimplementedError(); } + @override + Future clearMorphAnimationData(ThermionEntity entity) { + // TODO: implement clearMorphAnimationData + throw UnimplementedError(); + } + + @override + Future createCamera() { + // TODO: implement createCamera + throw UnimplementedError(); + } + + @override + Future createGizmo(covariant View view, GizmoType type) { + // TODO: implement createGizmo + throw UnimplementedError(); + } + + @override + Future destroyAsset(ThermionAsset asset) { + // TODO: implement destroyAsset + throw UnimplementedError(); + } + @override Future destroyAssets() { // TODO: implement destroyAssets throw UnimplementedError(); } + @override + Future destroyCamera(covariant Camera camera) { + // TODO: implement destroyCamera + throw UnimplementedError(); + } + @override Future destroyLights() { // TODO: implement destroyLights throw UnimplementedError(); } - @override Future dispose() { // TODO: implement dispose throw UnimplementedError(); } - @override - Future getCameraCullingFar() { - // TODO: implement getCameraCullingFar + Future getActiveCamera() { + // TODO: implement getActiveCamera throw UnimplementedError(); } @override - Future getCameraCullingNear() { - // TODO: implement getCameraCullingNear + Future getAncestor(ThermionEntity entity) { + // TODO: implement getAncestor throw UnimplementedError(); } @override - Future getCameraCullingProjectionMatrix() { - // TODO: implement getCameraCullingProjectionMatrix + Future getAnimationDuration(covariant ThermionAsset asset, int animationIndex) { + // TODO: implement getAnimationDuration throw UnimplementedError(); } @override - Future getCameraFrustum() { - // TODO: implement getCameraFrustum + Future> getAnimationNames(covariant ThermionAsset asset) { + // TODO: implement getAnimationNames throw UnimplementedError(); } @override - Future getCameraModelMatrix() { - // TODO: implement getCameraModelMatrix + Future getBone(covariant ThermionAsset asset, int boneIndex, {int skinIndex = 0}) { + // TODO: implement getBone throw UnimplementedError(); } @override - Future getCameraPosition() { - // TODO: implement getCameraPosition + Future> getBoneNames(covariant ThermionAsset asset, {int skinIndex = 0}) { + // TODO: implement getBoneNames throw UnimplementedError(); } @override - Future getCameraProjectionMatrix() { - // TODO: implement getCameraProjectionMatrix + int getCameraCount() { + // TODO: implement getCameraCount throw UnimplementedError(); } @override - Future getCameraRotation() { - // TODO: implement getCameraRotation - throw UnimplementedError(); - } - - @override - Future getCameraViewMatrix() { - // TODO: implement getCameraViewMatrix + Future getInverseBindMatrix(covariant ThermionAsset asset, int boneIndex, {int skinIndex = 0}) { + // TODO: implement getInverseBindMatrix throw UnimplementedError(); } @@ -134,6 +136,11 @@ class ThermionViewerStub extends ThermionViewer { throw UnimplementedError(); } + @override + Future> getMorphTargetNames(covariant ThermionAsset asset, ThermionEntity childEntity) { + // TODO: implement getMorphTargetNames + throw UnimplementedError(); + } @override String? getNameForEntity(ThermionEntity entity) { @@ -142,21 +149,46 @@ class ThermionViewerStub extends ThermionViewer { } @override - Future getParent(ThermionEntity child) { + Future getParent(ThermionEntity entity) { // TODO: implement getParent throw UnimplementedError(); } + @override + Future getRenderableBoundingBox(ThermionEntity entity) { + // TODO: implement getRenderableBoundingBox + throw UnimplementedError(); + } + + @override + Future getViewportBoundingBox(ThermionEntity entity) { + // TODO: implement getViewportBoundingBox + throw UnimplementedError(); + } + @override Future getWorldTransform(ThermionEntity entity) { // TODO: implement getWorldTransform throw UnimplementedError(); } + @override + Future loadGlb(String path, {int numInstances = 1, bool keepData = false}) { + // TODO: implement loadGlb + throw UnimplementedError(); + } @override - // TODO: implement initialized - Future get initialized => throw UnimplementedError(); + Future loadGlbFromBuffer(Uint8List data, {int numInstances = 1, bool keepData = false, int priority = 4, int layer = 0, bool loadResourcesAsync = false}) { + // TODO: implement loadGlbFromBuffer + throw UnimplementedError(); + } + + @override + Future loadGltf(String path, String relativeResourcePath, {bool keepData = false}) { + // TODO: implement loadGltf + throw UnimplementedError(); + } @override Future loadIbl(String lightingPath, {double intensity = 30000}) { @@ -171,10 +203,8 @@ class ThermionViewerStub extends ThermionViewer { } @override - Future moveCameraToAsset(ThermionEntity entity) { - // TODO: implement moveCameraToAsset - throw UnimplementedError(); - } + // TODO: implement msPerFrame + double get msPerFrame => throw UnimplementedError(); @override void onDispose(Future Function() callback) { @@ -182,47 +212,26 @@ class ThermionViewerStub extends ThermionViewer { } @override - Future panEnd() { - // TODO: implement panEnd + Future pick(int x, int y, void Function(PickResult p1) resultHandler) { + // TODO: implement pick throw UnimplementedError(); } @override - Future panStart(double x, double y) { - // TODO: implement panStart + 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 panUpdate(double x, double y) { - // TODO: implement panUpdate + 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 - // TODO: implement pickResult - Stream get pickResult => throw UnimplementedError(); - - @override - Future queuePositionUpdate( - ThermionEntity entity, double x, double y, double z, - {bool relative = false}) { - // TODO: implement queuePositionUpdate - throw UnimplementedError(); - } - - @override - Future queueRotationUpdate( - ThermionEntity entity, double rads, double x, double y, double z, - {bool relative = false}) { - // TODO: implement queueRotationUpdate - throw UnimplementedError(); - } - - @override - Future queueRotationUpdateQuat(ThermionEntity entity, Quaternion quat, - {bool relative = false}) { - // TODO: implement queueRotationUpdateQuat + Future registerRequestFrameHook(Future Function() hook) { + // TODO: implement registerRequestFrameHook throw UnimplementedError(); } @@ -233,8 +242,8 @@ class ThermionViewerStub extends ThermionViewer { } @override - Future removeCollisionComponent(ThermionEntity entity) { - // TODO: implement removeCollisionComponent + Future removeGridOverlay() { + // TODO: implement removeGridOverlay throw UnimplementedError(); } @@ -256,15 +265,25 @@ class ThermionViewerStub extends ThermionViewer { throw UnimplementedError(); } - + @override + Future render() { + // TODO: implement render + throw UnimplementedError(); + } + @override // TODO: implement rendering bool get rendering => throw UnimplementedError(); + @override + Future requestFrame() { + // TODO: implement requestFrame + throw UnimplementedError(); + } @override - Future rotateEnd() { - // TODO: implement rotateEnd + Future resetBones(ThermionAsset asset) { + // TODO: implement resetBones throw UnimplementedError(); } @@ -275,19 +294,11 @@ class ThermionViewerStub extends ThermionViewer { } @override - Future rotateStart(double x, double y) { - // TODO: implement rotateStart + Future setActiveCamera(covariant Camera camera) { + // TODO: implement setActiveCamera throw UnimplementedError(); } - @override - Future rotateUpdate(double x, double y) { - // TODO: implement rotateUpdate - throw UnimplementedError(); - } - - - @override Future setAntiAliasing(bool msaa, bool fxaa, bool taa) { // TODO: implement setAntiAliasing @@ -313,9 +324,13 @@ class ThermionViewerStub extends ThermionViewer { } @override - Future setBoneTransform( - ThermionEntity entity, int boneIndex, Matrix4 transform, - {int skinIndex = 0}) { + Future setBloom(bool enabled, double strength) { + // TODO: implement setBloom + throw UnimplementedError(); + } + + @override + Future setBoneTransform(ThermionEntity entity, int boneIndex, Matrix4 transform, {int skinIndex = 0}) { // TODO: implement setBoneTransform throw UnimplementedError(); } @@ -326,65 +341,6 @@ class ThermionViewerStub extends ThermionViewer { throw UnimplementedError(); } - @override - Future setCameraCulling(double near, double far) { - // TODO: implement setCameraCulling - throw UnimplementedError(); - } - - @override - Future setCameraExposure( - double aperture, double shutterSpeed, double sensitivity) { - // TODO: implement setCameraExposure - throw UnimplementedError(); - } - - @override - Future setCameraFocalLength(double focalLength) { - // TODO: implement setCameraFocalLength - throw UnimplementedError(); - } - - @override - Future setCameraFocusDistance(double focusDistance) { - // TODO: implement setCameraFocusDistance - throw UnimplementedError(); - } - - @override - Future setCameraFov(double degrees, {bool horizontal=true}) { - // TODO: implement setCameraFov - throw UnimplementedError(); - } - - @override - Future setCameraManipulatorOptions( - {ManipulatorMode mode = ManipulatorMode.ORBIT, - double orbitSpeedX = 0.01, - double orbitSpeedY = 0.01, - double zoomSpeed = 0.01}) { - // TODO: implement setCameraManipulatorOptions - throw UnimplementedError(); - } - - @override - Future setCameraModelMatrix(List matrix) { - // TODO: implement setCameraModelMatrix - throw UnimplementedError(); - } - - @override - Future setCameraPosition(double x, double y, double z) { - // TODO: implement setCameraPosition - throw UnimplementedError(); - } - - @override - Future setCameraRotation(Quaternion quaternion) { - // TODO: implement setCameraRotation - throw UnimplementedError(); - } - @override Future setFrameRate(int framerate) { // TODO: implement setFrameRate @@ -392,21 +348,38 @@ class ThermionViewerStub extends ThermionViewer { } @override - Future setMainCamera() { - // TODO: implement setMainCamera + Future setGltfAnimationFrame(covariant ThermionAsset asset, int index, int animationFrame) { + // TODO: implement setGltfAnimationFrame throw UnimplementedError(); } @override - Future setMaterialColor(ThermionEntity entity, String meshName, - int materialIndex, double r, double g, double b, double a) { - // TODO: implement setMaterialColor + Future setLightDirection(ThermionEntity lightEntity, Vector3 direction) { + // TODO: implement setLightDirection throw UnimplementedError(); } @override - Future setPosition(ThermionEntity entity, double x, double y, double z) { - // TODO: implement setPosition + Future setLightPosition(ThermionEntity lightEntity, double x, double y, double z) { + // TODO: implement setLightPosition + throw UnimplementedError(); + } + + @override + Future setMorphAnimationData(covariant ThermionAsset asset, MorphAnimationData animation, {List? targetMeshNames}) { + // TODO: implement setMorphAnimationData + throw UnimplementedError(); + } + + @override + Future setMorphTargetWeights(ThermionEntity entity, List weights) { + // TODO: implement setMorphTargetWeights + throw UnimplementedError(); + } + + @override + Future setParent(ThermionEntity child, ThermionEntity? parent, {bool preserveScaling= false}) { + // TODO: implement setParent throw UnimplementedError(); } @@ -422,98 +395,12 @@ class ThermionViewerStub extends ThermionViewer { throw UnimplementedError(); } - @override - Future setRecording(bool recording) { - // TODO: implement setRecording - throw UnimplementedError(); - } - - @override - Future setRecordingOutputDirectory(String outputDirectory) { - // TODO: implement setRecordingOutputDirectory - throw UnimplementedError(); - } - @override Future setRendering(bool render) { // TODO: implement setRendering throw UnimplementedError(); } - @override - Future setRotation( - ThermionEntity entity, double rads, double x, double y, double z) { - // TODO: implement setRotation - throw UnimplementedError(); - } - - @override - Future setRotationQuat(ThermionEntity entity, Quaternion rotation) { - // TODO: implement setRotationQuat - throw UnimplementedError(); - } - - @override - Future setScale(ThermionEntity entity, double scale) { - // TODO: implement setScale - throw UnimplementedError(); - } - - @override - Future setToneMapping(ToneMapper mapper) { - // TODO: implement setToneMapping - throw UnimplementedError(); - } - - @override - Future setTransform(ThermionEntity entity, Matrix4 transform) { - // TODO: implement setTransform - throw UnimplementedError(); - } - - @override - Future setViewFrustumCulling(bool enabled) { - // TODO: implement setViewFrustumCulling - throw UnimplementedError(); - } - - - @override - Future testCollisions(ThermionEntity entity) { - // TODO: implement testCollisions - throw UnimplementedError(); - } - - @override - Future transformToUnitCube(ThermionEntity entity) { - // TODO: implement transformToUnitCube - throw UnimplementedError(); - } - - @override - Future updateBoneMatrices(ThermionEntity entity) { - // TODO: implement updateBoneMatrices - throw UnimplementedError(); - } - - @override - Future zoomBegin() { - // TODO: implement zoomBegin - throw UnimplementedError(); - } - - @override - Future zoomEnd() { - // TODO: implement zoomEnd - throw UnimplementedError(); - } - - @override - Future zoomUpdate(double x, double y, double z) { - // TODO: implement zoomUpdate - throw UnimplementedError(); - } - @override Future setShadowType(ShadowType shadowType) { // TODO: implement setShadowType @@ -533,626 +420,55 @@ class ThermionViewerStub extends ThermionViewer { } @override - Future getBoundingBox(ThermionEntity entity) { - // TODO: implement getBoundingBox - throw UnimplementedError(); - } - - @override - Future getCameraFov(bool horizontal) { - // TODO: implement getCameraFov - throw UnimplementedError(); - } - - @override - Future queueRelativePositionUpdateWorldAxis(ThermionEntity entity, double viewportX, double viewportY, double x, double y, double z) { - // TODO: implement queueRelativePositionUpdateWorldAxis - throw UnimplementedError(); - } - - @override - Future setLayerEnabled(int layer, bool enabled) { - // TODO: implement setLayerEnabled - throw UnimplementedError(); - } - - @override - Future createIbl(double r, double g, double b, double intensity) { - // TODO: implement createIbl - throw UnimplementedError(); - } - - @override - // TODO: implement gizmoPickResult - Stream get gizmoPickResult => throw UnimplementedError(); - - @override - void pickGizmo(int x, int y) { - // TODO: implement pickGizmo - } - - @override - Future setGizmoVisibility(bool visible) { - // TODO: implement setGizmoVisibility - throw UnimplementedError(); - } - - @override - Future getAncestor(ThermionEntity entity) { - // TODO: implement getAncestor - throw UnimplementedError(); - } - - - @override - Future queuePositionUpdateFromViewportCoords(ThermionEntity entity, double x, double y) { - // TODO: implement queuePositionUpdateFromViewportCoords - throw UnimplementedError(); - } - - @override - Future removeStencilHighlight(ThermionEntity entity) { - // TODO: implement removeStencilHighlight - throw UnimplementedError(); - } - - @override - Future setLightDirection(ThermionEntity lightEntity, Vector3 direction) { - // TODO: implement setLightDirection - throw UnimplementedError(); - } - - @override - Future setLightPosition(ThermionEntity lightEntity, double x, double y, double z) { - // TODO: implement setLightPosition - throw UnimplementedError(); - } - - @override - Future setStencilHighlight(ThermionEntity entity, {double r = 1.0, double g = 0.0, double b = 0.0}) { - // TODO: implement setStencilHighlight - throw UnimplementedError(); - } - - @override - Future getCameraNear() { - // TODO: implement getCameraNear - throw UnimplementedError(); - } - - @override - Future getViewportBoundingBox(ThermionEntity entity) { - // TODO: implement getViewportBoundingBox - throw UnimplementedError(); - } - - - @override - Future setCameraModelMatrix4(Matrix4 matrix) { - // TODO: implement setCameraModelMatrix4 - throw UnimplementedError(); - } - - - - @override - Future setMaterialPropertyFloat(ThermionEntity entity, String propertyName, int materialIndex, double value) { - // TODO: implement setMaterialPropertyFloat - throw UnimplementedError(); - } - - @override - Future setMaterialPropertyFloat4(ThermionEntity entity, String propertyName, int materialIndex, double f1, double f2, double f3, double f4) { - // TODO: implement setMaterialPropertyFloat4 - throw UnimplementedError(); - } - - - @override - Future addDirectLight(DirectLight light) { - // TODO: implement addDirectLight + Future setToneMapping(ToneMapper mapper) { + // TODO: implement setToneMapping throw UnimplementedError(); } @override - Future createUbershaderMaterialInstance({bool doubleSided = false, bool unlit = false, bool hasVertexColors = false, bool hasBaseColorTexture = false, bool hasNormalTexture = false, bool hasOcclusionTexture = false, bool hasEmissiveTexture = false, bool useSpecularGlossiness = false, AlphaMode alphaMode = AlphaMode.OPAQUE, bool enableDiagnostics = false, bool hasMetallicRoughnessTexture = false, int metallicRoughnessUV = 0, int baseColorUV = 0, bool hasClearCoatTexture = false, int clearCoatUV = 0, bool hasClearCoatRoughnessTexture = false, int clearCoatRoughnessUV = 0, bool hasClearCoatNormalTexture = false, int clearCoatNormalUV = 0, bool hasClearCoat = false, bool hasTransmission = false, bool hasTextureTransforms = false, int emissiveUV = 0, int aoUV = 0, int normalUV = 0, bool hasTransmissionTexture = false, int transmissionUV = 0, bool hasSheenColorTexture = false, int sheenColorUV = 0, bool hasSheenRoughnessTexture = false, int sheenRoughnessUV = 0, bool hasVolumeThicknessTexture = false, int volumeThicknessUV = 0, bool hasSheen = false, bool hasIOR = false, bool hasVolume = false}) { - // TODO: implement createUbershaderMaterialInstance - throw UnimplementedError(); - } - - @override - Future createUnlitMaterialInstance() { - // TODO: implement createUnlitMaterialInstance - throw UnimplementedError(); - } - - @override - Future destroyMaterialInstance(covariant MaterialInstance materialInstance) { - // TODO: implement destroyMaterialInstance - throw UnimplementedError(); - } - - @override - Future destroyTexture(covariant ThermionTexture texture) { - // TODO: implement destroyTexture - throw UnimplementedError(); - } - - @override - Future setMaterialPropertyInt(ThermionEntity entity, String propertyName, int materialIndex, int value) { - // TODO: implement setMaterialPropertyInt + Future setTransform(ThermionEntity entity, Matrix4 transform) { + // TODO: implement setTransform throw UnimplementedError(); } @override - Future setMaterialDepthWrite(ThermionEntity entity, int materialIndex, bool enabled) { - // TODO: implement setMaterialDepthWrite - throw UnimplementedError(); - } - - - @override - Future requestFrame() { - throw UnimplementedError(); - } - - @override - Future setCameraLensProjection({double near = kNear, double far = kFar, double? aspect, double focalLength = kFocalLength}) { - // TODO: implement setCameraLensProjection - throw UnimplementedError(); - } - - @override - Future getMainCameraEntity() { - // TODO: implement getMainCameraEntity + Future setViewFrustumCulling(bool enabled) { + // TODO: implement setViewFrustumCulling throw UnimplementedError(); } @override - Future getMainCamera() { - // TODO: implement getMainCamera - throw UnimplementedError(); - } - - @override - Future createCamera() { - // TODO: implement createCamera - throw UnimplementedError(); - } - - @override - Future registerRenderHook(Future Function() hook) { - // TODO: implement registerRenderHook - throw UnimplementedError(); - } - - @override - Future setActiveCamera(covariant Camera camera) { - // TODO: implement setActiveCamera - throw UnimplementedError(); - } - - @override - Future registerRequestFrameHook(Future Function() hook) { - // TODO: implement registerRequestFrameHook - throw UnimplementedError(); - } - - @override - Future unregisterRequestFrameHook(Future Function() hook) { - // TODO: implement unregisterRequestFrameHook - throw UnimplementedError(); - } - - @override - Camera getCameraAt(int index) { - // TODO: implement getCameraAt - throw UnimplementedError(); - } - - @override - int getCameraCount() { - // TODO: implement getCameraCount + Future showGridOverlay() { + // TODO: implement showGridOverlay throw UnimplementedError(); } - @override - Future queueTransformUpdates(List entities, List transforms) { - // TODO: implement queueTransformUpdates - throw UnimplementedError(); - } - - @override - Future setRenderTarget(covariant RenderTarget renderTarget) { - // TODO: implement setRenderTarget - throw UnimplementedError(); - } - - @override - Future createView() { - // TODO: implement createView - throw UnimplementedError(); - } - - @override - Future getViewAt(int index) { - // TODO: implement getViewAt - throw UnimplementedError(); - } - - - - @override - Future createHeadlessSwapChain(int width, int height) { - // TODO: implement createHeadlessSwapChain - throw UnimplementedError(); - } - - @override - Future createSwapChain(handle) { - // TODO: implement createSwapChain - throw UnimplementedError(); - } - - @override - Future render({covariant SwapChain? swapChain}) { - // TODO: implement render - throw UnimplementedError(); - } - - @override - Future getActiveCamera() { - // TODO: implement getActiveCamera - throw UnimplementedError(); - } - - @override - Future destroyRenderTarget(covariant RenderTarget renderTarget) { - // TODO: implement destroyRenderTarget - throw UnimplementedError(); - } - - @override - Future destroySwapChain(covariant SwapChain swapChain) { - // TODO: implement destroySwapChain - throw UnimplementedError(); - } - - @override - Future createUnlitFixedSizeMaterialInstance() { - // TODO: implement createUnlitFixedSizeMaterialInstance - throw UnimplementedError(); - } - - @override - Future getRenderableBoundingBox(ThermionEntity entity) { - // TODO: implement getRenderableBoundingBox - throw UnimplementedError(); - } - - @override - Future createInstance(covariant ThermionAsset asset, {covariant List? materialInstances = null}) { - // TODO: implement createInstance - throw UnimplementedError(); - } - - @override - Future getInstanceCount(covariant ThermionAsset entity) { - // TODO: implement getInstanceCount - throw UnimplementedError(); - } - - @override - Future> getInstances(covariant ThermionAsset entity) { - // TODO: implement getInstances - throw UnimplementedError(); - } - - @override - Future hide(ThermionEntity entity) { - // TODO: implement hide - throw UnimplementedError(); - } - - @override - Future loadGlb(String path, {int numInstances = 1, bool keepData = false}) { - // TODO: implement loadGlb - throw UnimplementedError(); - } - - - @override - Future loadGltf(String path, String relativeResourcePath, {bool keepData = false}) { - // TODO: implement loadGltf - throw UnimplementedError(); - } - - @override - Future reveal(ThermionEntity entity) { - // TODO: implement reveal - throw UnimplementedError(); - } - - @override - Future loadGlbFromBuffer(Uint8List data, {int numInstances = 1, bool keepData = false, int priority = 4, int layer = 0, bool loadResourcesAsync=false}) { - // TODO: implement loadGlbFromBuffer - 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 - Future getAnimationDuration(covariant ThermionAsset asset, int animationIndex) { - // TODO: implement getAnimationDuration - throw UnimplementedError(); - } - - @override - Future> getAnimationNames(covariant ThermionAsset asset) { - // TODO: implement getAnimationNames - throw UnimplementedError(); - } - - @override - Future getBone(covariant ThermionAsset asset, int boneIndex, {int skinIndex = 0}) { - // TODO: implement getBone - throw UnimplementedError(); - } - - @override - Future> getBoneNames(covariant ThermionAsset asset, {int skinIndex = 0}) { - // TODO: implement getBoneNames - throw UnimplementedError(); - } - - @override - Future> getChildEntities(covariant ThermionAsset asset) { - // TODO: implement getChildEntities - throw UnimplementedError(); - } - - @override - Future getChildEntity(covariant ThermionAsset asset, String childName) { - // TODO: implement getChildEntity - throw UnimplementedError(); - } - - @override - Future getInverseBindMatrix(covariant ThermionAsset asset, int boneIndex, {int skinIndex = 0}) { - // TODO: implement getInverseBindMatrix - throw UnimplementedError(); - } - - @override - Future> getMorphTargetNames(covariant ThermionAsset asset, ThermionEntity childEntity) { - // TODO: implement getMorphTargetNames - 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 setAnimationFrame(covariant ThermionAsset asset, int index, int animationFrame) { - // TODO: implement setAnimationFrame - throw UnimplementedError(); - } - - @override - Future setMorphAnimationData(ThermionAsset asset, MorphAnimationData animation, {List? targetMeshNames}) { - // TODO: implement setMorphAnimationData - 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 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 destroyAsset(ThermionAsset asset) { - // TODO: implement removeAsset - throw UnimplementedError(); - } - - @override - Future resetBones(ThermionAsset asset) { - // TODO: implement resetBones - throw UnimplementedError(); - } - - @override - Future clearMorphAnimationData(ThermionEntity entity) { - // TODO: implement clearMorphAnimationData - throw UnimplementedError(); - } - - @override - Future createGeometry(Geometry geometry, {covariant List? materialInstances, bool keepData = false}) { - // TODO: implement createGeometry - throw UnimplementedError(); - } - - @override - Future setGltfAnimationFrame(covariant ThermionAsset asset, int index, int animationFrame) { - // TODO: implement setGltfAnimationFrame - throw UnimplementedError(); - } - - @override - Future setMorphTargetWeights(ThermionEntity entity, List weights) { - // TODO: implement setMorphTargetWeights - throw UnimplementedError(); - } - - @override - Future addEntityToScene(ThermionEntity entity) { - // TODO: implement addEntityToScene - throw UnimplementedError(); - } - - @override - Future createGizmo(covariant View view, GizmoType type) { - // TODO: implement createGizmo - throw UnimplementedError(); - } - - @override - Future createMaterial(Uint8List data) { - // TODO: implement createMaterial - throw UnimplementedError(); - } - - @override - Future getMaterialInstanceAt(ThermionEntity entity, int index) { - // TODO: implement getMaterialInstanceAt - throw UnimplementedError(); - } - - @override - Future pick(int x, int y, void Function(PickResult p1) resultHandler) { - // TODO: implement pick - throw UnimplementedError(); - } - - @override - Future removeAssetFromScene(ThermionEntity entity) { - // TODO: implement removeAssetFromScene - throw UnimplementedError(); - } - - @override - Future removeGridOverlay() { - // TODO: implement removeGridOverlay - throw UnimplementedError(); - } - - @override - Future setLayerVisibility(VisibilityLayers layer, bool visible) { - // TODO: implement setLayerVisibility - throw UnimplementedError(); - } - - @override - Future setVisibilityLayer(ThermionEntity entity, VisibilityLayers layer) { - // TODO: implement setVisibilityLayer - throw UnimplementedError(); - } - - @override - Future showGridOverlay({covariant Material? material}) { - // TODO: implement showGridOverlay - throw UnimplementedError(); - } - - @override - Future setParent(ThermionEntity child, ThermionEntity? parent, {bool preserveScaling=false}) { - // TODO: implement setParent - throw UnimplementedError(); - } - - @override - Future createImage(int width, int height, int channels) { - // TODO: implement createImage - throw UnimplementedError(); - } - - @override - Future createTexture(int width, int height, {int depth = 1, int levels = 1, TextureSamplerType textureSamplerType = TextureSamplerType.SAMPLER_2D, TextureFormat textureFormat = TextureFormat.RGBA32F}) { - // TODO: implement createTexture - throw UnimplementedError(); - } - - @override - Future createTextureSampler({TextureMinFilter minFilter = TextureMinFilter.LINEAR, TextureMagFilter magFilter = TextureMagFilter.LINEAR, TextureWrapMode wrapS = TextureWrapMode.CLAMP_TO_EDGE, TextureWrapMode wrapT = TextureWrapMode.CLAMP_TO_EDGE, TextureWrapMode wrapR = TextureWrapMode.CLAMP_TO_EDGE, double anisotropy = 0.0, TextureCompareMode compareMode = TextureCompareMode.NONE, TextureCompareFunc compareFunc = TextureCompareFunc.LESS_EQUAL}) { - // TODO: implement createTextureSampler - throw UnimplementedError(); - } - - @override - Future decodeImage(Uint8List data) { - // TODO: implement decodeImage - throw UnimplementedError(); - } - - @override - Future destroyCamera(covariant Camera camera) { - // TODO: implement destroyCamera - throw UnimplementedError(); - } - - @override - Future isCastShadowsEnabled(ThermionEntity entity) { - // TODO: implement isCastShadowsEnabled - throw UnimplementedError(); - } - - @override - Future isReceiveShadowsEnabled(ThermionEntity entity) { - // TODO: implement isReceiveShadowsEnabled - throw UnimplementedError(); - } - - @override - // TODO: implement msPerFrame - double get msPerFrame => throw UnimplementedError(); - - @override - Future setCastShadows(ThermionEntity entity, bool castShadows) { - // TODO: implement setCastShadows - throw UnimplementedError(); - } - - @override - Future setClearOptions(Vector4 clearColor, int clearStencil, bool clear, bool discard) { - // TODO: implement setClearOptions - throw UnimplementedError(); - } - - @override - Future setReceiveShadows(ThermionEntity entity, bool receiveShadows) { - // TODO: implement setReceiveShadows - throw UnimplementedError(); - } - - @override - Future> capture(covariant List<({RenderTarget? renderTarget, SwapChain? swapChain, View view})> targets) { - // TODO: implement capture - throw UnimplementedError(); - } - - @override - Future createRenderTarget(int width, int height, {int? colorTextureHandle, int? depthTextureHandle}) { - // TODO: implement createRenderTarget - throw UnimplementedError(); - } - - @override - Future setBloom(bool enabled, double strength) { - // TODO: implement setBloom + Future unregisterRequestFrameHook(Future Function() hook) { + // TODO: implement unregisterRequestFrameHook throw UnimplementedError(); } - + @override + Future updateBoneMatrices(ThermionEntity entity) { + // TODO: implement updateBoneMatrices + throw UnimplementedError(); + } + + @override + // TODO: implement view + View get view => throw UnimplementedError(); + } diff --git a/thermion_dart/lib/src/viewer/src/web_js/src/thermion_viewer_js_shim.dart b/thermion_dart/lib/src/viewer/src/web_js/src/thermion_viewer_js_shim.dart index a594854f..b45a2a31 100644 --- a/thermion_dart/lib/src/viewer/src/web_js/src/thermion_viewer_js_shim.dart +++ b/thermion_dart/lib/src/viewer/src/web_js/src/thermion_viewer_js_shim.dart @@ -3,7 +3,7 @@ library thermion_flutter_js; import 'dart:js_interop'; -import '../../shared_types/shared_types.dart'; +import '../../../../filament/src/shared_types.dart'; /// /// An extension type on [JSObject] that represents a diff --git a/thermion_dart/lib/src/viewer/viewer.dart b/thermion_dart/lib/src/viewer/viewer.dart index f5ede7db..292333e2 100644 --- a/thermion_dart/lib/src/viewer/viewer.dart +++ b/thermion_dart/lib/src/viewer/viewer.dart @@ -1,7 +1,9 @@ library thermion_viewer; export 'src/thermion_viewer_base.dart'; +export '../filament/src/filament_app.dart'; export 'src/thermion_viewer_stub.dart' if (dart.library.io) 'src/ffi/thermion_viewer_ffi.dart' if (dart.library.js_interop) 'src/web_wasm/thermion_viewer_web_wasm.dart'; -export 'src/shared_types/shared_types.dart'; \ No newline at end of file +export '../filament/src/shared_types.dart'; + diff --git a/thermion_dart/lib/thermion_dart.dart b/thermion_dart/lib/thermion_dart.dart index c43d29c7..22a6afc4 100644 --- a/thermion_dart/lib/thermion_dart.dart +++ b/thermion_dart/lib/thermion_dart.dart @@ -1,5 +1,6 @@ library filament_dart; +export 'package:vector_math/vector_math_64.dart' hide Colors; export 'src/viewer/viewer.dart'; export 'src/input/input.dart'; export 'src/utils/utils.dart'; diff --git a/thermion_dart/native/include/RenderTicker.hpp b/thermion_dart/native/include/RenderTicker.hpp index d93df76a..205851a3 100644 --- a/thermion_dart/native/include/RenderTicker.hpp +++ b/thermion_dart/native/include/RenderTicker.hpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include // for std::pair #include #include @@ -49,10 +49,7 @@ namespace thermion std::mutex mMutex; filament::Renderer *mRenderer = nullptr; std::vector mAnimationManagers; - std::vector mSwapChains; - std::map> mRenderable; - + std::vector>> mRenderable; }; - } \ No newline at end of file diff --git a/thermion_dart/native/include/c_api/TCamera.h b/thermion_dart/native/include/c_api/TCamera.h index a112daa9..6c3b2977 100644 --- a/thermion_dart/native/include/c_api/TCamera.h +++ b/thermion_dart/native/include/c_api/TCamera.h @@ -32,6 +32,7 @@ EMSCRIPTEN_KEEPALIVE void Camera_lookAt(TCamera* camera, double3 eye, double3 fo EMSCRIPTEN_KEEPALIVE double Camera_getNear(TCamera *camera); EMSCRIPTEN_KEEPALIVE double Camera_getCullingFar(TCamera *camera); EMSCRIPTEN_KEEPALIVE float Camera_getFov(TCamera *camera, bool horizontal); +EMSCRIPTEN_KEEPALIVE double Camera_getFocusDistance(TCamera *camera); EMSCRIPTEN_KEEPALIVE void Camera_setFocusDistance(TCamera *camera, float focusDistance); EMSCRIPTEN_KEEPALIVE void Camera_setCustomProjectionWithCulling( diff --git a/thermion_dart/native/include/c_api/TMaterialInstance.h b/thermion_dart/native/include/c_api/TMaterialInstance.h index 0dd620a5..14e4cedf 100644 --- a/thermion_dart/native/include/c_api/TMaterialInstance.h +++ b/thermion_dart/native/include/c_api/TMaterialInstance.h @@ -68,6 +68,8 @@ extern "C" }; EMSCRIPTEN_KEEPALIVE TMaterialInstance *Material_createInstance(TMaterial *tMaterial); + EMSCRIPTEN_KEEPALIVE TMaterial *Material_createImageMaterial(); + EMSCRIPTEN_KEEPALIVE TMaterial *Material_createGridMaterial(); EMSCRIPTEN_KEEPALIVE bool Material_hasParameter(TMaterial *tMaterial, const char *propertyName); EMSCRIPTEN_KEEPALIVE bool MaterialInstance_isStencilWriteEnabled(TMaterialInstance *materialInstance); EMSCRIPTEN_KEEPALIVE void MaterialInstance_setStencilWrite(TMaterialInstance *materialInstance, bool enabled); diff --git a/thermion_dart/native/include/c_api/TRenderTicker.h b/thermion_dart/native/include/c_api/TRenderTicker.h index df274ccb..06733ef4 100644 --- a/thermion_dart/native/include/c_api/TRenderTicker.h +++ b/thermion_dart/native/include/c_api/TRenderTicker.h @@ -13,7 +13,7 @@ extern "C" EMSCRIPTEN_KEEPALIVE void RenderTicker_removeAnimationManager(TRenderTicker *tRenderTicker, TAnimationManager *tAnimationManager); EMSCRIPTEN_KEEPALIVE void RenderTicker_render(TRenderTicker *tRenderTicker, uint64_t frameTimeInNanos); - EMSCRIPTEN_KEEPALIVE void RenderTicker_setRenderable(TRenderTicker *tFilamentRender, TSwapChain *swapChain, TView **views, uint8_t numViews); + EMSCRIPTEN_KEEPALIVE void RenderTicker_setRenderable(TRenderTicker *tRenderTicker, TSwapChain *swapChain, TView **views, uint8_t numViews); #ifdef __cplusplus } diff --git a/thermion_dart/native/include/c_api/TSceneAsset.h b/thermion_dart/native/include/c_api/TSceneAsset.h index bb21197f..185c9636 100644 --- a/thermion_dart/native/include/c_api/TSceneAsset.h +++ b/thermion_dart/native/include/c_api/TSceneAsset.h @@ -43,6 +43,8 @@ extern "C" size_t numInstances ); + EMSCRIPTEN_KEEPALIVE TSceneAsset *SceneAsset_createGrid(TEngine *tEngine, TMaterial * tMaterial); + EMSCRIPTEN_KEEPALIVE void SceneAsset_destroy(TSceneAsset *tSceneAsset); EMSCRIPTEN_KEEPALIVE void SceneAsset_addToScene(TSceneAsset *tSceneAsset, TScene *tScene); EMSCRIPTEN_KEEPALIVE void SceneAsset_removeFromScene(TSceneAsset *tSceneAsset, TScene *tScene); diff --git a/thermion_dart/native/include/c_api/TTransformManager.h b/thermion_dart/native/include/c_api/TTransformManager.h index db26e0f0..198ea26b 100644 --- a/thermion_dart/native/include/c_api/TTransformManager.h +++ b/thermion_dart/native/include/c_api/TTransformManager.h @@ -11,7 +11,7 @@ extern "C" EMSCRIPTEN_KEEPALIVE double4x4 TransformManager_getLocalTransform(TTransformManager *tTransformManager, EntityId entityId); EMSCRIPTEN_KEEPALIVE double4x4 TransformManager_getWorldTransform(TTransformManager *tTransformManager, EntityId entityId); EMSCRIPTEN_KEEPALIVE void TransformManager_setTransform(TTransformManager *tTransformManager, EntityId entityId, double4x4 transform); - EMSCRIPTEN_KEEPALIVE void TransformManager_transformToUnitCube(TTransformManager *tTransformManager, EntityId entityId); + EMSCRIPTEN_KEEPALIVE void TransformManager_transformToUnitCube(TTransformManager *tTransformManager, EntityId entityId, Aabb3 boundingBox); EMSCRIPTEN_KEEPALIVE void TransformManager_setParent(TTransformManager *tTransformManager, EntityId child, EntityId parent, bool preserveScaling); EMSCRIPTEN_KEEPALIVE EntityId TransformManager_getParent(TTransformManager *tTransformManager, EntityId child); EMSCRIPTEN_KEEPALIVE EntityId TransformManager_getAncestor(TTransformManager *tTransformManager, EntityId childEntityId); diff --git a/thermion_dart/native/include/c_api/ThermionDartRenderThreadApi.h b/thermion_dart/native/include/c_api/ThermionDartRenderThreadApi.h index ffa06735..e0957b0b 100644 --- a/thermion_dart/native/include/c_api/ThermionDartRenderThreadApi.h +++ b/thermion_dart/native/include/c_api/ThermionDartRenderThreadApi.h @@ -294,15 +294,15 @@ namespace thermion EMSCRIPTEN_KEEPALIVE void AnimationManager_updateBoneMatricesRenderThread(TSceneManager *sceneManager, EntityId asset, void (*callback)(bool)); - EMSCRIPTEN_KEEPALIVE void set_bone_transform_render_thread( - TSceneManager *sceneManager, + EMSCRIPTEN_KEEPALIVE void AnimationManager_setBoneTransformRenderThread( + TAnimationManager *tAnimationManager, EntityId asset, int skinIndex, int boneIndex, const float *const transform, void (*callback)(bool)); - EMSCRIPTEN_KEEPALIVE void reset_to_rest_pose_render_thread(TSceneManager *sceneManager, EntityId entityId, void (*callback)()); + EMSCRIPTEN_KEEPALIVE void AnimationManager_resetToRestPoseRenderThread(TAnimationManager *tAnimationManager, EntityId entityId, void (*callback)()); EMSCRIPTEN_KEEPALIVE void GltfAssetLoader_createRenderThread(TEngine *tEngine, TMaterialProvider *tMaterialProvider, void (*callback)(TGltfAssetLoader *)); EMSCRIPTEN_KEEPALIVE void GltfResourceLoader_createRenderThread(TEngine *tEngine, void (*callback)(TGltfResourceLoader *)); diff --git a/thermion_dart/native/include/scene/AnimationManager.hpp b/thermion_dart/native/include/scene/AnimationManager.hpp index 365e438e..e5f83e02 100644 --- a/thermion_dart/native/include/scene/AnimationManager.hpp +++ b/thermion_dart/native/include/scene/AnimationManager.hpp @@ -33,7 +33,7 @@ namespace thermion Scene *scene); ~AnimationManager(); - void update(); + void update(uint64_t frameTimeInNanos); /// @brief /// @param asset diff --git a/thermion_dart/native/src/RenderTicker.cpp b/thermion_dart/native/src/RenderTicker.cpp index e4e1ddb2..ffc9f310 100644 --- a/thermion_dart/native/src/RenderTicker.cpp +++ b/thermion_dart/native/src/RenderTicker.cpp @@ -1,5 +1,3 @@ - - #if __APPLE__ #include "TargetConditionals.h" #endif @@ -48,57 +46,39 @@ namespace thermion using std::string; - static constexpr filament::math::float4 sFullScreenTriangleVertices[3] = { - {-1.0f, -1.0f, 1.0f, 1.0f}, - {3.0f, -1.0f, 1.0f, 1.0f}, - {-1.0f, 3.0f, 1.0f, 1.0f}}; - - static const uint16_t sFullScreenTriangleIndices[3] = {0, 1, 2}; - void RenderTicker::setRenderable(SwapChain *swapChain, View **views, uint8_t numViews) { - { - std::lock_guard lock(mMutex); - auto swapChainViews = mRenderable[swapChain]; + // Find if this swapChain already exists in our collection + auto it = std::find_if(mRenderable.begin(), mRenderable.end(), + [swapChain](const auto& pair) { return pair.first == swapChain; }); - swapChainViews.clear(); + // Prepare the vector of views + std::vector swapChainViews; for(int i = 0; i < numViews; i++) { swapChainViews.push_back(views[i]); } - mRenderable[swapChain] = swapChainViews; - - // Keep track of the swapchains, so we can iterate them in the render method. - bool found = false; - for (auto existingSwapChain : mSwapChains) { - if (existingSwapChain == swapChain) { - found = true; - break; - } - } - if (!found) { - mSwapChains.push_back(swapChain); + if (it != mRenderable.end()) { + // Update existing entry + it->second = swapChainViews; + } else { + // Add new entry + mRenderable.emplace_back(swapChain, swapChainViews); } } -} void RenderTicker::render(uint64_t frameTimeInNanos) { std::lock_guard lock(mMutex); - // Update all animation managers for (auto animationManager : mAnimationManagers) { - if (animationManager) { // Check for nullptr just in case - animationManager->update(frameTimeInNanos * 1e-9); - } + animationManager->update(frameTimeInNanos * 1e-9); } - - for (auto swapChain : mSwapChains) + for (const auto& [swapChain, views] : mRenderable) { - auto views = mRenderable[swapChain]; - if (views.size() > 0) + if (!views.empty()) { bool beginFrame = mRenderer->beginFrame(swapChain, frameTimeInNanos); if (beginFrame) @@ -116,19 +96,19 @@ namespace thermion #endif } - void RenderTicker::addAnimationManager(AnimationManager* animationManager) { - std::lock_guard lock(mMutex); - mAnimationManagers.push_back(animationManager); - } + void RenderTicker::addAnimationManager(AnimationManager* animationManager) { + std::lock_guard lock(mMutex); + mAnimationManagers.push_back(animationManager); + } - void RenderTicker::removeAnimationManager(AnimationManager* animationManager) { - std::lock_guard lock(mMutex); - auto it = std::find(mAnimationManagers.begin(), mAnimationManagers.end(), animationManager); - if (it != mAnimationManagers.end()) { - mAnimationManagers.erase(it); - } + void RenderTicker::removeAnimationManager(AnimationManager* animationManager) { + std::lock_guard lock(mMutex); + auto it = std::find(mAnimationManagers.begin(), mAnimationManagers.end(), animationManager); + if (it != mAnimationManagers.end()) { + mAnimationManagers.erase(it); } + } - RenderTicker::~RenderTicker() {} + RenderTicker::~RenderTicker() {} } // namespace thermion \ No newline at end of file diff --git a/thermion_dart/native/src/c_api/TCamera.cpp b/thermion_dart/native/src/c_api/TCamera.cpp index 4dd9cfcf..774d0f10 100644 --- a/thermion_dart/native/src/c_api/TCamera.cpp +++ b/thermion_dart/native/src/c_api/TCamera.cpp @@ -47,6 +47,16 @@ namespace thermion camera->setCustomProjection(convert_double4x4_to_mat4(projectionMatrix), near, far); } + EMSCRIPTEN_KEEPALIVE double Camera_getFocusDistance(TCamera *camera) { + auto *camera = reinterpret_cast(tCamera); + return camera->getFocusDistance(); + } + + EMSCRIPTEN_KEEPALIVE void Camera_setFocusDistance(TCamera *camera, float distance) { + auto *camera = reinterpret_cast(tCamera); + return camera->setFocusDistance(distance); + } + EMSCRIPTEN_KEEPALIVE double4x4 Camera_getModelMatrix(TCamera *tCamera) { auto *camera = reinterpret_cast(tCamera); diff --git a/thermion_dart/native/src/c_api/TMaterialInstance.cpp b/thermion_dart/native/src/c_api/TMaterialInstance.cpp index aef8c8e2..d9f98661 100644 --- a/thermion_dart/native/src/c_api/TMaterialInstance.cpp +++ b/thermion_dart/native/src/c_api/TMaterialInstance.cpp @@ -7,6 +7,7 @@ #include #include "Log.hpp" +#include "materials/image.h" #include "c_api/TMaterialInstance.h" #ifdef __cplusplus @@ -23,6 +24,26 @@ namespace thermion return reinterpret_cast(instance); } + EMSCRIPTEN_KEEPALIVE TMaterial *Material_createImageMaterial(TEngine *tEngine) { + auto *engine = reinterpret_cast(tEngine); + auto *material = Material::Builder() + .package(IMAGE_IMAGE_DATA, IMAGE_IMAGE_SIZE) + .build(*engine); + + return reinterpret_cast(material); + } + + EMSCRIPTEN_KEEPALIVE TMaterial *Material_createGridMaterial(TEngine *tEngine) { + auto *engine = reinterpret_cast(tEngine); + auto *material = Material::Builder() + .package(GRID_GRID_DATA, GRID_GRID_SIZE) + .build(*engine); + + return reinterpret_cast(material); + } + + + EMSCRIPTEN_KEEPALIVE bool Material_hasParameter(TMaterial *tMaterial, const char *propertyName) { auto *material = reinterpret_cast(tMaterial); return material->hasParameter(propertyName); diff --git a/thermion_dart/native/src/c_api/TSceneAsset.cpp b/thermion_dart/native/src/c_api/TSceneAsset.cpp index bda4cb2a..cc0890ff 100644 --- a/thermion_dart/native/src/c_api/TSceneAsset.cpp +++ b/thermion_dart/native/src/c_api/TSceneAsset.cpp @@ -6,6 +6,7 @@ #include "c_api/TGltfAssetLoader.h" #include "c_api/TSceneAsset.h" +#include "scene/GridOverlay.hpp" #include "scene/SceneAsset.hpp" #include "scene/GltfSceneAsset.hpp" #include "scene/GeometrySceneAssetBuilder.hpp" @@ -89,6 +90,13 @@ extern "C" return reinterpret_cast(sceneAsset); } + + EMSCRIPTEN_KEEPALIVE TSceneAsset *SceneAsset_createGrid(TEngine *tEngine, TMaterial* tMaterial) { + auto *engine = reinterpret_cast(tEngine); + auto *material = reinterpret_cast(tMaterial); + auto *asset = new GridOverlay(*engine, material); + return reinterpret_cast(asset); + } EMSCRIPTEN_KEEPALIVE void SceneAsset_destroy(TSceneAsset *tSceneAsset) { auto *asset = reinterpret_cast(tSceneAsset); diff --git a/thermion_dart/native/src/c_api/TTransformManager.cpp b/thermion_dart/native/src/c_api/TTransformManager.cpp index 50d58ddd..299f978c 100644 --- a/thermion_dart/native/src/c_api/TTransformManager.cpp +++ b/thermion_dart/native/src/c_api/TTransformManager.cpp @@ -67,8 +67,8 @@ extern "C" return; } - auto center = aabb.center(); - auto halfExtent = aabb.extent(); + auto center = filament::math::float3 { boundingBox.centerX, boundingBox.centerY, boundingBox.centerZ }; + auto halfExtent = filament::math::float3 { boundingBox.halfExtentX, boundingBox.halfExtentY, boundingBox.halfExtentZ }; auto maxExtent = max(halfExtent) * 2; auto scaleFactor = 2.0f / maxExtent; auto transform = math::mat4f::scaling(scaleFactor) * math::mat4f::translation(-center); diff --git a/thermion_flutter/thermion_flutter/lib/src/widgets/src/thermion_texture_widget.dart b/thermion_flutter/thermion_flutter/lib/src/widgets/src/thermion_texture_widget.dart index 6aa3116d..821dbc55 100644 --- a/thermion_flutter/thermion_flutter/lib/src/widgets/src/thermion_texture_widget.dart +++ b/thermion_flutter/thermion_flutter/lib/src/widgets/src/thermion_texture_widget.dart @@ -1,21 +1,16 @@ import 'dart:async'; -import 'package:flutter/material.dart'; +import 'package:flutter/material.dart' hide View; import 'package:logging/logging.dart'; -import 'package:thermion_dart/src/viewer/src/shared_types/view.dart' as t; import 'package:thermion_flutter/src/widgets/src/resize_observer.dart'; import 'package:thermion_flutter/thermion_flutter.dart' hide Texture; class ThermionTextureWidget extends StatefulWidget { + /// /// /// final ThermionViewer viewer; - /// - /// - /// - final t.View view; - /// /// /// @@ -24,7 +19,7 @@ class ThermionTextureWidget extends StatefulWidget { /// /// A callback that will be invoked whenever this widget (and the underlying texture is resized). /// - final Future Function(Size size, t.View view, double pixelRatio)? onResize; + final Future Function(Size size, View view, double pixelRatio)? onResize; /// /// When true, an FPS counter will be displayed at the top right of the widget @@ -34,7 +29,6 @@ class ThermionTextureWidget extends StatefulWidget { const ThermionTextureWidget({ super.key, required this.viewer, - required this.view, this.initial, this.onResize, this.showFpsCounter = false, @@ -49,7 +43,7 @@ class ThermionTextureWidget extends StatefulWidget { class _ThermionTextureWidgetState extends State { PlatformTextureDescriptor? _texture; - static final _views = []; + static final _views = []; final _logger = Logger("_ThermionTextureWidgetState"); @@ -63,7 +57,7 @@ class _ThermionTextureWidgetState extends State { @override void dispose() { super.dispose(); - _views.remove(widget.view); + _views.remove(widget.viewer.view); if (_texture != null) { ThermionFlutterPlatform.instance.destroyTextureDescriptor(_texture!); } @@ -73,10 +67,10 @@ class _ThermionTextureWidgetState extends State { @override void initState() { - if (_views.contains(widget.view)) { + if (_views.contains(widget.viewer.view)) { throw Exception("View already embedded in a widget"); } - _views.add(widget.view); + _views.add(widget.viewer.view); // Start FPS counter update timer if enabled if (widget.showFpsCounter) { @@ -95,7 +89,6 @@ class _ThermionTextureWidgetState extends State { } WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { - await widget.viewer.initialized; var dpr = MediaQuery.of(context).devicePixelRatio; @@ -111,17 +104,17 @@ class _ThermionTextureWidgetState extends State { "Target texture dimensions ${width}x${height} (pixel ratio : $dpr)"); _texture = await ThermionFlutterPlatform.instance - .createTextureAndBindToView(widget.view, width, height); + .createTextureAndBindToView(widget.viewer.view, width, height); _logger.info( "Actual texture dimensions ${_texture!.width}x${_texture!.height} (pixel ratio : $dpr)"); - await widget.view.setViewport(_texture!.width, _texture!.height); + await widget.viewer.view.setViewport(_texture!.width, _texture!.height); try { await widget.onResize?.call( Size(_texture!.width.toDouble(), _texture!.height.toDouble()), - widget.view, + widget.viewer.view, dpr); } catch (err, st) { _logger.severe(err); @@ -191,7 +184,7 @@ class _ThermionTextureWidgetState extends State { widget.viewer.msPerFrame - _headroomInMs)) { _rendering = true; if (this == _states.first && _texture != null) { - await widget.viewer.requestFrame(); + await FilamentApp.instance!.requestFrame(); lastRender = d.inMilliseconds; if (widget.showFpsCounter) { @@ -243,16 +236,16 @@ class _ThermionTextureWidgetState extends State { "Resizing texture to dimensions ${newWidth}x${newHeight} (pixel ratio : $dpr)"); _texture = await ThermionFlutterPlatform.instance - .resizeTexture(_texture!, widget.view, newWidth, newHeight); + .resizeTexture(_texture!, widget.viewer.view, newWidth, newHeight); _logger.info( "Resized texture to dimensions ${_texture!.width}x${_texture!.height} (pixel ratio : $dpr)"); - await widget.view.setViewport(_texture!.width, _texture!.height); + await widget.viewer.view.setViewport(_texture!.width, _texture!.height); await widget.onResize?.call( Size(_texture!.width.toDouble(), _texture!.height.toDouble()), - widget.view, + widget.viewer.view, dpr); if (!mounted) { diff --git a/thermion_flutter/thermion_flutter/lib/src/widgets/src/thermion_widget.dart b/thermion_flutter/thermion_flutter/lib/src/widgets/src/thermion_widget.dart index 19a362ee..bdbdda29 100644 --- a/thermion_flutter/thermion_flutter/lib/src/widgets/src/thermion_widget.dart +++ b/thermion_flutter/thermion_flutter/lib/src/widgets/src/thermion_widget.dart @@ -1,13 +1,11 @@ -import 'dart:io'; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; +import 'package:flutter/material.dart' hide View; import 'package:thermion_flutter/src/widgets/src/thermion_texture_widget.dart'; import 'package:thermion_flutter/src/widgets/src/thermion_widget_web.dart'; import 'package:thermion_flutter/thermion_flutter.dart'; import 'package:thermion_flutter_web/thermion_flutter_web_options.dart'; -import 'package:thermion_dart/src/viewer/src/shared_types/view.dart' as t; -Future kDefaultResizeCallback(Size size, t.View view, double pixelRatio) async { +Future kDefaultResizeCallback(Size size, View view, double pixelRatio) async { var camera = await view.getCamera(); var near = await camera.getNear(); var far = await camera.getCullingFar(); @@ -26,11 +24,6 @@ class ThermionWidget extends StatefulWidget { /// final ThermionViewer viewer; - /// - /// The [t.View] associated with this widget. If null, the default View will be used. - /// - final t.View? view; - /// /// A callback to invoke whenever this widget and the underlying surface are /// resized. If a callback is not explicitly provided, the default callback @@ -45,7 +38,7 @@ class ThermionWidget extends StatefulWidget { /// If you need to work with Flutter dimensions, divide [size] by /// [pixelRatio]. /// - final Future Function(Size size, t.View view, double pixelRatio)? onResize; + final Future Function(Size size, View view, double pixelRatio)? onResize; final bool showFpsCounter; @@ -59,7 +52,6 @@ class ThermionWidget extends StatefulWidget { {Key? key, this.initial, required this.viewer, - this.view, this.showFpsCounter = false, this.onResize = kDefaultResizeCallback}) : super(key: key); @@ -69,42 +61,22 @@ class ThermionWidget extends StatefulWidget { } class _ThermionWidgetState extends State { - t.View? view; - - @override - void initState() { - super.initState(); - initialize(); - } - - Future initialize() async { - if (widget.view != null) { - view = widget.view; - } else { - view = await widget.viewer.getViewAt(0); - } - setState(() {}); - } - @override Widget build(BuildContext context) { - if (view == null) { - return widget.initial ?? Container(color: Colors.red); - } - // Web doesn't support imported textures yet if (kIsWeb) { - return ThermionWidgetWeb( - viewer: widget.viewer, - options: ThermionFlutterPlugin.options as ThermionFlutterWebOptions?); + throw Exception(); + // return ThermionWidgetWeb( + // viewer: widget.viewer, + // options: ThermionFlutterPlugin.options as ThermionFlutterWebOptions?); } return ThermionTextureWidget( - key: ObjectKey(view!), + key: ObjectKey(widget.viewer), initial: widget.initial, viewer: widget.viewer, - view: view!, - showFpsCounter:widget.showFpsCounter, + view: widget.viewer.view, + showFpsCounter: widget.showFpsCounter, onResize: widget.onResize); } } diff --git a/thermion_flutter/thermion_flutter/lib/src/widgets/src/viewer_widget.dart b/thermion_flutter/thermion_flutter/lib/src/widgets/src/viewer_widget.dart new file mode 100644 index 00000000..55736c0b --- /dev/null +++ b/thermion_flutter/thermion_flutter/lib/src/widgets/src/viewer_widget.dart @@ -0,0 +1,116 @@ +import 'package:flutter/material.dart'; +import 'package:thermion_flutter/thermion_flutter.dart' hide Texture; + +enum ViewerManipulatorType { ORBIT, FIRST_PERSON } + +class ViewerOptions { + /// + /// + /// + final Widget initial; + + /// + /// When true, an FPS counter will be displayed at the top right of the widget + /// + final bool showFpsCounter; + + /// + /// + /// + final String? assetPath; + + /// + /// + /// + final String? skyboxPath; + + /// + /// + /// + final String? iblPath; + + /// + /// + /// + final LightType? directLightType; + + /// + /// + /// + final bool transformToUnitCube; + + /// + /// + /// + final bool postProcessing; + + /// + /// + /// + final Color? background; + + /// + /// + /// + final bool destroyAppOnUnload; + + const ViewerOptions( + {this.initial = + const DecoratedBox(decoration: BoxDecoration(color: Colors.red)), + this.showFpsCounter = false, + this.transformToUnitCube = true, + this.postProcessing = true, + this.destroyAppOnUnload = false, + this.assetPath, + this.skyboxPath, + this.iblPath, + this.directLightType, + this.background}); +} + +class ViewerWidget extends StatefulWidget { + final ViewerOptions options; + + const ViewerWidget({super.key, this.options = const ViewerOptions()}); + + @override + State createState() { + return _ViewerWidgetState(); + } +} + +class _ViewerWidgetState extends State { + ThermionViewer? viewer; + + @override + void initState() { + super.initState(); + ThermionFlutterPlugin.createViewer().then((viewer) async { + viewer = viewer; + + setState(() {}); + }); + } + + @override + void dispose() { + if (viewer != null) { + _tearDown(); + } + } + + Future _tearDown() async { + await viewer!.dispose(); + if (widget.options.destroyAppOnUnload) { + await viewer!.app.destroy(); + } + } + + @override + Widget build(BuildContext context) { + if (viewer == null) { + return widget.options.initial!; + } + return ThermionWidget(viewer: viewer!); + } +} diff --git a/thermion_flutter/thermion_flutter_method_channel/lib/src/thermion_flutter_method_channel_platform.dart b/thermion_flutter/thermion_flutter_method_channel/lib/src/thermion_flutter_method_channel_platform.dart index f0baf44e..f0963216 100644 --- a/thermion_flutter/thermion_flutter_method_channel/lib/src/thermion_flutter_method_channel_platform.dart +++ b/thermion_flutter/thermion_flutter_method_channel/lib/src/thermion_flutter_method_channel_platform.dart @@ -1,24 +1,25 @@ import 'dart:async'; import 'dart:ffi'; -import 'dart:async'; import 'dart:io'; -import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:thermion_dart/thermion_dart.dart'; -import 'package:thermion_dart/thermion_dart.dart' as t; +import 'package:thermion_dart/src/filament/filament.dart'; +import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_viewer_ffi.dart'; +import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart'; + import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_interface.dart'; import 'package:logging/logging.dart'; import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart'; -import 'package:thermion_flutter_platform_interface/thermion_flutter_window.dart'; /// -/// An abstract implementation of [ThermionFlutterPlatform] that uses -/// Flutter platform channels to create a rendering context, -/// resource loaders, and surface/render target(s). +/// An implementation of [ThermionFlutterPlatform] that uses +/// a Flutter platform channel to create a native rendering context, resource +/// loader and rendering surfaces. /// class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform { final channel = const MethodChannel("dev.thermion.flutter/event"); - final _logger = Logger("ThermionFlutterMethodChannelPlatform"); + + late final _logger = Logger(this.runtimeType.toString()); static SwapChain? _swapChain; @@ -31,14 +32,17 @@ class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform { ThermionFlutterPlatform.instance = instance!; } - t.ThermionViewerFFI? viewer; + static Future loadAsset(String path) async { + if (path.startsWith("file://")) { + return File(path.replaceAll("file://", "")).readAsBytesSync(); + } + if (path.startsWith("asset://")) { + throw UnimplementedError(); + } + throw UnimplementedError(); + } Future createViewer({ThermionFlutterOptions? options}) async { - if (viewer != null) { - throw Exception( - "Only one ThermionViewer can be created at any given time; ensure you have called [dispose] on the previous instance before constructing a new instance."); - } - var resourceLoader = Pointer.fromAddress( await channel.invokeMethod("getResourceLoaderWrapper")); @@ -47,6 +51,7 @@ class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform { } var driverPlatform = await channel.invokeMethod("getDriverPlatform"); + var driverPtr = driverPlatform == null ? nullptr : Pointer.fromAddress(driverPlatform); @@ -57,21 +62,53 @@ class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform { ? nullptr : Pointer.fromAddress(sharedContext); - viewer = ThermionViewerFFI( + late Backend backend; + if (options?.backend != null) { + switch (options!.backend) { + case Backend.VULKAN: + if (!Platform.isWindows) { + throw Exception("Vulkan only supported on Windows"); + } + case Backend.METAL: + if (!Platform.isIOS || !Platform.isMacOS) { + throw Exception("Metal only supported on iOS/macOS"); + } + case Backend.OPENGL: + if (!Platform.isAndroid) { + throw Exception("OpenGL only supported on Android"); + } + default: + throw Exception("Unsupported backend"); + } + backend = options.backend!; + } else { + if (Platform.isWindows) { + backend = Backend.VULKAN; + } else if (Platform.isMacOS || Platform.isIOS) { + backend = Backend.METAL; + } else if (Platform.isAndroid) { + backend = Backend.OPENGL; + } else { + throw Exception("Unsupported platform"); + } + } + final config = FFIFilamentConfig( + backend: backend, resourceLoader: resourceLoader, driver: driverPtr, + platform: nullptr, sharedContext: sharedContextPtr, uberArchivePath: options?.uberarchivePath); - await viewer!.initialized; - viewer!.onDispose(() async { - _swapChain = null; - this.viewer = null; - }); + await FFIFilamentApp.create(config); - if (_swapChain != null) { - throw Exception("Only a single swapchain can be created"); - } + final viewer = ThermionViewerFFI( + loadAsset: loadAsset, + ); + + await viewer.initialized; + + viewer.onDispose(() async {}); // this implementation renders directly into a texture/render target // for some reason we still need to create a (headless) swapchain, but the @@ -79,7 +116,7 @@ class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform { // TODO - see if we can use `renderStandaloneView` in FilamentViewer to // avoid this if (Platform.isMacOS || Platform.isIOS) { - _swapChain = await viewer!.createHeadlessSwapChain(1, 1); + _swapChain = await FilamentApp.instance!.createHeadlessSwapChain(1, 1); } return viewer!; @@ -96,8 +133,8 @@ class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform { final hardwareId = result[1] as int; var window = result[2] as int?; // usually 0 for nullptr - return PlatformTextureDescriptor(flutterId, hardwareId, window, width, height); - + return PlatformTextureDescriptor( + flutterId, hardwareId, window, width, height); } @override @@ -109,43 +146,62 @@ class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform { /// /// Future createTextureAndBindToView( - t.View view, int width, int height) async { + View view, int width, int height) async { var descriptor = await createTextureDescriptor(width, height); - if (Platform.isWindows) { if (_swapChain != null) { - await view.setRenderable(false, _swapChain!); - await viewer!.destroySwapChain(_swapChain!); + await FilamentApp.instance!.setRenderable(view, false); + await FilamentApp.instance!.destroySwapChain(_swapChain!); } - _swapChain = - await viewer!.createHeadlessSwapChain(descriptor.width, descriptor.height); + _swapChain = await FilamentApp.instance! + .createHeadlessSwapChain(descriptor.width, descriptor.height); } else if (Platform.isAndroid) { if (_swapChain != null) { - await view.setRenderable(false, _swapChain!); - await viewer!.destroySwapChain(_swapChain!); + await FilamentApp.instance!.setRenderable(view, false); + await FilamentApp.instance!.destroySwapChain(_swapChain!); } - _swapChain = await viewer!.createSwapChain(descriptor.windowHandle!); + _swapChain = await FilamentApp.instance!.createSwapChain(descriptor.windowHandle!); } else { - var renderTarget = await viewer!.createRenderTarget( - descriptor.width, descriptor.height, descriptor.hardwareId); + final color = await FilamentApp.instance!.createTexture( + descriptor.width, descriptor.height, + importedTextureHandle: descriptor.hardwareId, + flags: { + TextureUsage.TEXTURE_USAGE_BLIT_DST, + TextureUsage.TEXTURE_USAGE_COLOR_ATTACHMENT, + TextureUsage.TEXTURE_USAGE_SAMPLEABLE + }); + final depth = + await FilamentApp.instance!.createTexture(descriptor.width, descriptor.height, flags: { + TextureUsage.TEXTURE_USAGE_BLIT_DST, + TextureUsage.TEXTURE_USAGE_DEPTH_ATTACHMENT, + TextureUsage.TEXTURE_USAGE_SAMPLEABLE + }); - await view.setRenderTarget(renderTarget!); + var renderTarget = await FilamentApp.instance!.createRenderTarget( + descriptor.width, descriptor.height, + color: color, depth: depth); + + await view.setRenderTarget(renderTarget); } - await view.setRenderable(true, _swapChain!); + await FilamentApp.instance!.register(_swapChain!, view); return descriptor; } @override Future markTextureFrameAvailable(PlatformTextureDescriptor texture) async { - await channel.invokeMethod("markTextureFrameAvailable", texture.flutterTextureId); + await channel.invokeMethod( + "markTextureFrameAvailable", texture.flutterTextureId); } @override - Future resizeTexture(PlatformTextureDescriptor texture, - t.View view, int width, int height) async { + Future resizeTexture( + PlatformTextureDescriptor texture, + View view, + int width, + int height) async { var newTexture = await createTextureAndBindToView(view, width, height); if (newTexture == null) { throw Exception(); diff --git a/thermion_flutter/thermion_flutter_method_channel/pubspec.yaml b/thermion_flutter/thermion_flutter_method_channel/pubspec.yaml index 2f086373..a353c4cd 100644 --- a/thermion_flutter/thermion_flutter_method_channel/pubspec.yaml +++ b/thermion_flutter/thermion_flutter_method_channel/pubspec.yaml @@ -27,6 +27,8 @@ dependencies: thermion_dart: ^0.2.1-dev.20.0 logging: ^1.2.0 dependency_overrides: + thermion_dart: + path: ../../thermion_dart thermion_flutter_platform_interface: path: ../thermion_flutter_platform_interface dev_dependencies: