renaming to Thermion

This commit is contained in:
Nick Fisher
2024-06-15 15:05:34 +08:00
parent 1a5f573bc0
commit fe62a70e29
719 changed files with 7291 additions and 3946 deletions

View File

@@ -0,0 +1,6 @@
library filament_dart;
export 'thermion_dart/entities/filament_entity.dart';
export 'thermion_dart/abstract_filament_viewer.dart';
export 'thermion_dart/filament_viewer_impl.dart';
export 'thermion_dart/entities/entity_transform_controller.dart';

View File

@@ -0,0 +1,759 @@
import 'dart:math';
import 'package:vector_math/vector_math_64.dart';
import 'dart:async';
import 'package:animation_tools_dart/animation_tools_dart.dart';
import 'package:thermion_dart/thermion_dart/entities/filament_entity.dart';
// "picking" means clicking/tapping on the viewport, and unprojecting the X/Y coordinate to determine whether any renderable entities were present at those coordinates.
typedef FilamentPickResult = ({FilamentEntity entity, double x, double y});
enum LightType {
SUN, //!< Directional light that also draws a sun's disk in the sky.
DIRECTIONAL, //!< Directional light, emits light in a given direction.
POINT, //!< Point light, emits light from a position, in all directions.
FOCUSED_SPOT, //!< Physically correct spot light.
SPOT,
}
// copied from filament/backened/DriverEnums.h
enum PrimitiveType {
// don't change the enums values (made to match GL)
POINTS, //!< points
LINES, //!< lines
UNUSED1,
LINE_STRIP, //!< line strip
TRIANGLES, //!< triangles
TRIANGLE_STRIP, //!< triangle strip
}
enum ToneMapper { ACES, FILMIC, LINEAR }
// see filament Manipulator.h for more details
enum ManipulatorMode { ORBIT, MAP, FREE_FLIGHT }
class TextureDetails {
final int textureId;
// both width and height are in physical, not logical pixels
final int width;
final int height;
TextureDetails(
{required this.textureId, required this.width, required this.height});
}
abstract class AbstractFilamentViewer {
Future<bool> get initialized;
///
/// The result(s) of calling [pick] (see below).
/// This may be a broadcast stream, so you should ensure you have subscribed to this stream before calling [pick].
/// If [pick] is called without an active subscription to this stream, the results will be silently discarded.
///
Stream<FilamentPickResult> get pickResult;
///
/// Whether the controller is currently rendering at [framerate].
///
bool get rendering;
///
/// Set to true to continuously render the scene at the framerate specified by [setFrameRate] (60 fps by default).
///
Future setRendering(bool render);
///
/// Render a single frame.
///
Future render();
///
/// Sets the framerate for continuous rendering when [setRendering] is enabled.
///
Future setFrameRate(int framerate);
///
/// Destroys/disposes the viewer (including the entire scene). You cannot use the viewer after calling this method.
///
Future dispose();
///
/// Set the background image to [path] (which should have a file extension .png, .jpg, or .ktx).
/// This will be rendered at the maximum depth (i.e. behind all other objects including the skybox).
/// If [fillHeight] is false, the image will be rendered at its original size. Note this may cause issues with pixel density so be sure to specify the correct resolution
/// If [fillHeight] is true, the image will be stretched/compressed to fit the height of the viewport.
///
Future setBackgroundImage(String path, {bool fillHeight = false});
///
/// Moves the background image to the relative offset from the origin (bottom-left) specified by [x] and [y].
/// If [clamp] is true, the image cannot be positioned outside the bounds of the viewport.
///
Future setBackgroundImagePosition(double x, double y, {bool clamp = false});
///
/// Removes the background image.
///
Future clearBackgroundImage();
///
/// Sets the color for the background plane (positioned at the maximum depth, i.e. behind all other objects including the skybox).
///
Future setBackgroundColor(double r, double g, double b, double alpha);
///
/// Load a skybox from [skyboxPath] (which must be a .ktx file)
///
Future loadSkybox(String skyboxPath);
///
/// Removes the skybox from the scene.
///
Future removeSkybox();
///
/// Loads an image-based light from the specified path at the given intensity.
/// Only one IBL can be active at any given time; if an IBL has already been loaded, it will be replaced.
///
Future loadIbl(String lightingPath, {double intensity = 30000});
///
/// Rotates the IBL & skybox.
///
Future rotateIbl(Matrix3 rotation);
///
/// Removes the image-based light from the scene.
///
Future removeIbl();
///
/// Add a light to the scene.
/// See LightManager.h for details
/// Note that [sunAngularRadius] is in degrees,
/// whereas [spotLightConeInner] and [spotLightConeOuter] are in radians
///
Future<FilamentEntity> addLight(
LightType type,
double colour,
double intensity,
double posX,
double posY,
double posZ,
double dirX,
double dirY,
double dirZ,
{double falloffRadius = 1.0,
double spotLightConeInner = pi / 8,
double spotLightConeOuter = pi / 4,
double sunAngularRadius = 0.545,
double sunHaloSize = 10.0,
double sunHaloFallof = 80.0,
bool castShadows = true});
Future removeLight(FilamentEntity light);
///
/// Remove all lights (excluding IBL) from the scene.
///
Future clearLights();
///
/// Load the .glb asset at the given path and insert into the scene.
///
Future<FilamentEntity> loadGlb(String path, {int numInstances = 1});
///
/// Create a new instance of [entity].
///
Future<FilamentEntity> createInstance(FilamentEntity entity);
///
/// Returns the number of instances of the asset associated with [entity].
///
Future<int> getInstanceCount(FilamentEntity entity);
///
/// Returns all instances of [entity].
///
Future<List<FilamentEntity>> getInstances(FilamentEntity entity);
///
/// Load the .gltf asset at the given path and insert into the scene.
/// [relativeResourcePath] is the folder path where the glTF resources are stored;
/// this is usually the parent directory of the .gltf file itself.
///
Future<FilamentEntity> loadGltf(String path, String relativeResourcePath,
{bool force = false});
///
/// Called by `FilamentGestureDetector`. You probably don't want to call this yourself.
///
Future panStart(double x, double y);
///
/// Called by `FilamentGestureDetector`. You probably don't want to call this yourself.
///
Future panUpdate(double x, double y);
///
/// Called by `FilamentGestureDetector`. You probably don't want to call this yourself.
///
Future panEnd();
///
/// Called by `FilamentGestureDetector`. You probably don't want to call this yourself.
///
Future rotateStart(double x, double y);
///
/// Called by `FilamentGestureDetector`. You probably don't want to call this yourself.
///
Future rotateUpdate(double x, double y);
///
/// Called by `FilamentGestureDetector`. You probably don't want to call this yourself.
///
Future rotateEnd();
///
/// Set the weights for all morph targets in [entity] to [weights].
/// Note that [weights] must contain values for ALL morph targets, but no exception will be thrown if you don't do so (you'll just get incorrect results).
/// If you only want to set one value, set all others to zero (check [getMorphTargetNames] if you need the get a list of all morph targets).
/// IMPORTANT - this accepts the actual FilamentEntity 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(FilamentEntity entity, List<double> weights);
///
/// Gets the names of all morph targets for the child renderable [childEntity] under [entity].
///
Future<List<String>> getMorphTargetNames(
FilamentEntity entity, FilamentEntity childEntity);
///
/// Gets the names of all bones for the armature at [skinIndex] under the specified [entity].
///
Future<List<String>> getBoneNames(FilamentEntity entity, {int skinIndex = 0});
///
/// Gets the names of all glTF animations embedded in the specified entity.
///
Future<List<String>> getAnimationNames(FilamentEntity entity);
///
/// Returns the length (in seconds) of the animation at the given index.
///
Future<double> getAnimationDuration(
FilamentEntity entity, int animationIndex);
///
/// Animate the morph targets in [entity]. See [MorphTargetAnimation] for an explanation as to how to construct the animation frame data.
/// This method will check the morph target names specified in [animation] against the morph target names that actually exist exist under [meshName] in [entity],
/// throwing an exception if any cannot be found.
/// It is permissible for [animation] to omit any targets that do exist under [meshName]; these simply won't be animated.
///
Future setMorphAnimationData(
FilamentEntity entity, MorphAnimationData animation,
{List<String>? targetMeshNames});
///
/// Resets all bones in the given entity to their rest pose.
/// This should be done before every call to addBoneAnimation.
///
Future resetBones(FilamentEntity entity);
///
/// Enqueues and plays the [animation] for the specified bone(s).
/// By default, frame data is interpreted as being in *parent* bone space;
/// a 45 degree around Y means the bone will rotate 45 degrees around the
/// Y axis of the parent bone *in its current orientation*.
/// (i.e NOT the parent bone's rest position!).
/// Currently, only [Space.ParentBone] and [Space.Model] are supported; if you want
/// to transform to another space, you will need to do so manually.
///
/// [fadeInInSecs]/[fadeOutInSecs]/[maxDelta] are used to cross-fade between
/// the current active glTF animation ("animation1") and the animation you
/// set via this method ("animation2"). The bone orientations will be
/// linearly interpolated between animation1 and animation2; at time 0,
/// the orientation will be 100% animation1, at time [fadeInInSecs], the
/// animation will be ((1 - maxDelta) * animation1) + (maxDelta * animation2).
/// This will be applied in reverse after [fadeOutInSecs].
///
///
Future addBoneAnimation(FilamentEntity entity, BoneAnimationData animation,
{int skinIndex = 0,
double fadeInInSecs = 0.0,
double fadeOutInSecs = 0.0,
double maxDelta = 1.0});
///
/// Gets the entity representing the bone at [boneIndex]/[skinIndex].
/// The returned entity is only intended for use with [getWorldTransform].
///
Future<FilamentEntity> getBone(FilamentEntity parent, int boneIndex,
{int skinIndex = 0});
///
/// Gets the local (relative to parent) transform for [entity].
///
Future<Matrix4> getLocalTransform(FilamentEntity entity);
///
/// Gets the world transform for [entity].
///
Future<Matrix4> getWorldTransform(FilamentEntity entity);
///
/// Gets the inverse bind (pose) matrix for the bone.
/// Note that [parent] must be the FilamentEntity returned by [loadGlb/loadGltf], not any other method ([getChildEntity] etc).
/// This is because all joint information is internally stored with the parent entity.
///
Future<Matrix4> getInverseBindMatrix(FilamentEntity parent, int boneIndex,
{int skinIndex = 0});
///
/// Sets the transform (relative to its parent) for [entity].
///
Future setTransform(FilamentEntity entity, Matrix4 transform);
///
/// Updates the bone matrices for [entity] (which must be the FilamentEntity
/// 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(FilamentEntity 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(
FilamentEntity entity, int boneIndex, Matrix4 transform,
{int skinIndex = 0});
///
/// Removes/destroys the specified entity from the scene.
/// [entity] will no longer be a valid handle after this method is called; ensure you immediately discard all references once this method is complete.
///
Future removeEntity(FilamentEntity entity);
///
/// Removes/destroys all renderable entities from the scene (including cameras).
/// All [FilamentEntity] handles will no longer be valid after this method is called; ensure you immediately discard all references to all entities once this method is complete.
///
Future clearEntities();
///
/// Called by `FilamentGestureDetector`. You probably don't want to call this yourself.
///
Future zoomBegin();
///
/// Called by `FilamentGestureDetector`. You probably don't want to call this yourself.
///
Future zoomUpdate(double x, double y, double z);
///
/// Called by `FilamentGestureDetector`. You probably don't want to call this yourself.
///
Future zoomEnd();
///
/// Schedules the glTF animation at [index] in [entity] to start playing on the next frame.
///
Future playAnimation(FilamentEntity entity, int index,
{bool loop = false,
bool reverse = false,
bool replaceActive = true,
double crossfade = 0.0});
///
/// Schedules the glTF animation at [index] in [entity] to start playing on the next frame.
///
Future playAnimationByName(FilamentEntity entity, String name,
{bool loop = false,
bool reverse = false,
bool replaceActive = true,
double crossfade = 0.0});
Future setAnimationFrame(
FilamentEntity entity, int index, int animationFrame);
Future stopAnimation(FilamentEntity entity, int animationIndex);
Future stopAnimationByName(FilamentEntity entity, String name);
///
/// Sets the current scene camera to the glTF camera under [name] in [entity].
///
Future setCamera(FilamentEntity entity, String? name);
///
/// Sets the current scene camera to the main camera (which is always available and added to every scene by default).
///
Future setMainCamera();
///
/// Returns the entity associated with the main camera.
///
Future<FilamentEntity> getMainCamera();
///
/// Sets the current scene camera to the glTF camera under [name] in [entity].
///
Future setCameraFov(double degrees, double width, double height);
///
/// Sets the tone mapping (requires postprocessing).
///
Future setToneMapping(ToneMapper mapper);
///
/// Sets the strength of the bloom.
///
Future setBloom(double bloom);
///
/// Sets the focal length of the camera. Default value is 28.0.
///
Future setCameraFocalLength(double focalLength);
///
/// Sets the distance (in world units) to the near/far planes for the active camera. Default values are 0.05/1000.0. See Camera.h for details.
///
Future setCameraCulling(double near, double far);
///
/// Get the distance (in world units) to the near culling plane for the active camera.
///
Future<double> getCameraCullingNear();
///
/// Get the distance (in world units) to the far culling plane for the active camera.
///
Future<double> getCameraCullingFar();
///
/// Sets the focus distance for the camera.
///
Future setCameraFocusDistance(double focusDistance);
///
/// Get the camera position in world space.
///
Future<Vector3> getCameraPosition();
///
/// Get the camera's model matrix.
///
Future<Matrix4> getCameraModelMatrix();
///
/// Get the camera's view matrix. See Camera.h for more details.
///
Future<Matrix4> getCameraViewMatrix();
///
/// Get the camera's projection matrix. See Camera.h for more details.
///
Future<Matrix4> getCameraProjectionMatrix();
///
/// Get the camera's culling projection matrix. See Camera.h for more details.
///
Future<Matrix4> 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<Frustum> 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<Matrix3> getCameraRotation();
///
/// Repositions the camera to the last vertex of the bounding box of [entity], looking at the penultimate vertex.
///
Future moveCameraToAsset(FilamentEntity entity);
///
/// Enables/disables frustum culling. Currently we don't expose a method for manipulating the camera projection/culling matrices so this is your only option to deal with unwanted near/far clipping.
///
Future setViewFrustumCulling(bool enabled);
///
/// Sets the camera exposure.
///
Future setCameraExposure(
double aperture, double shutterSpeed, double sensitivity);
///
/// Rotate the camera by [rads] around the given axis. Note this is not persistent - any viewport navigation will reset the camera transform.
///
Future setCameraRotation(Quaternion quaternion);
///
/// Sets the camera model matrix.
///
Future setCameraModelMatrix(List<double> matrix);
///
/// Sets the `baseColorFactor` property for the material at index [materialIndex] in [entity] under node [meshName] to [color].
///
Future setMaterialColor(FilamentEntity entity, String meshName,
int materialIndex, double r, double g, double b, double a);
///
/// Scale [entity] to fit within the unit cube.
///
Future transformToUnitCube(FilamentEntity entity);
///
/// Directly sets the world space position for [entity] to the given coordinates, skipping all collision detection.
///
Future setPosition(FilamentEntity entity, double x, double y, double z);
///
/// Directly sets the scale for [entity], skipping all collision detection.
///
Future setScale(FilamentEntity entity, double scale);
///
/// Directly sets the rotation for [entity] to [rads] around the axis {x,y,z}, skipping all collision detection.
///
Future setRotation(
FilamentEntity entity, double rads, double x, double y, double z);
///
/// Queues an update to the worldspace position for [entity] to {x,y,z}.
/// The actual update will occur on the next frame, and will be subject to collision detection.
///
Future queuePositionUpdate(
FilamentEntity entity, double x, double y, double z,
{bool relative = false});
///
/// Queues an update to the worldspace rotation for [entity].
/// The actual update will occur on the next frame, and will be subject to collision detection.
///
Future queueRotationUpdate(
FilamentEntity entity, double rads, double x, double y, double z,
{bool relative = false});
///
/// Same as [queueRotationUpdate].
///
Future queueRotationUpdateQuat(FilamentEntity entity, Quaternion quat,
{bool relative = false});
///
/// Enable/disable postprocessing.
///
Future setPostProcessing(bool enabled);
///
/// Set antialiasing options.
///
Future setAntiAliasing(bool msaa, bool fxaa, bool taa);
///
/// Sets the rotation for [entity] to the specified quaternion.
///
Future setRotationQuat(FilamentEntity entity, Quaternion rotation);
///
/// Reveal the node [meshName] under [entity]. Only applicable if [hide] had previously been called; this is a no-op otherwise.
///
Future reveal(FilamentEntity entity, String? meshName);
///
/// If [meshName] is provided, hide the node [meshName] under [entity], otherwise hide the root node for [entity].
/// The entity still exists in memory, but is no longer being rendered into the scene. Call [reveal] to re-commence rendering.
///
Future hide(FilamentEntity entity, String? meshName);
///
/// Used to select the entity in the scene at the given viewport coordinates.
/// Called by `FilamentGestureDetector` on a mouse/finger down event. You probably don't want to call this yourself.
/// This is asynchronous and will require 2-3 frames to complete - subscribe to the [pickResult] stream to receive the results of this method.
/// [x] and [y] must be in local logical coordinates (i.e. where 0,0 is at top-left of the FilamentWidget).
///
void pick(int x, int y);
///
/// Retrieves the name assigned to the given FilamentEntity (usually corresponds to the glTF mesh name).
///
String? getNameForEntity(FilamentEntity entity);
///
/// Sets the options for manipulating the camera via the viewport.
/// ManipulatorMode.FREE_FLIGHT and ManipulatorMode.MAP are currently unsupported and will throw an exception.
///
Future setCameraManipulatorOptions(
{ManipulatorMode mode = ManipulatorMode.ORBIT,
double orbitSpeedX = 0.01,
double orbitSpeedY = 0.01,
double zoomSpeed = 0.01});
///
/// Returns all child entities under [parent].
///
Future<List<FilamentEntity>> getChildEntities(
FilamentEntity parent, bool renderableOnly);
///
/// Finds the child entity named [childName] associated with the given parent.
/// Usually, [parent] will be the return value from [loadGlb]/[loadGltf] and [childName] will be the name of a node/mesh.
///
Future<FilamentEntity> getChildEntity(
FilamentEntity parent, String childName);
///
/// List the name of all child entities under the given entity.
///
Future<List<String>> getChildEntityNames(FilamentEntity entity,
{bool renderableOnly = true});
///
/// If [recording] is set to true, each frame the framebuffer/texture will be written to /tmp/output_*.png.
/// This will impact performance; handle with care.
///
Future setRecording(bool recording);
///
/// Sets the output directory where recorded PNGs will be placed.
///
Future setRecordingOutputDirectory(String outputDirectory);
///
/// An [entity] will only be animatable after an animation component is attached.
/// Any calls to [playAnimation]/[setBoneAnimation]/[setMorphAnimation] will have no visual effect until [addAnimationComponent] has been called on the instance.
///
Future addAnimationComponent(FilamentEntity entity);
///
/// Removes an animation component from [entity].
///
Future removeAnimationComponent(FilamentEntity entity);
///
/// Makes [entity] collidable.
/// This allows you to call [testCollisions] with any other entity ("entity B") to see if [entity] has collided with entity B. The callback will be invoked if so.
/// Alternatively, if [affectsTransform] is true and this entity collides with another entity, any queued position updates to the latter entity will be ignored.
///
Future addCollisionComponent(FilamentEntity entity,
{void Function(int entityId1, int entityId2)? callback,
bool affectsTransform = false});
///
/// Removes the collision component from [entity], meaning this will no longer be tested when [testCollisions] or [queuePositionUpdate] is called with another entity.
///
Future removeCollisionComponent(FilamentEntity entity);
///
/// Creates a (renderable) entity with the specified geometry and adds to the scene.
///
Future createGeometry(List<double> vertices, List<int> indices,
{String? materialPath,
PrimitiveType primitiveType = PrimitiveType.TRIANGLES});
///
/// Gets the parent transform of [child].
///
Future<FilamentEntity?> getParent(FilamentEntity child);
///
/// Sets the parent transform of [child] to [parent].
///
Future setParent(FilamentEntity child, FilamentEntity parent);
///
/// Test all collidable entities against this entity to see if any have collided.
/// This method returns void; the relevant callback passed to [addCollisionComponent] will be fired if a collision is detected.
///
Future testCollisions(FilamentEntity entity);
///
/// Sets the draw priority for the given entity. See RenderableManager.h for more details.
///
Future setPriority(FilamentEntity entityId, int priority);
///
/// The Scene holds all loaded entities/lights.
///
Scene get scene;
///
///
///
AbstractGizmo? get gizmo;
}
///
/// For now, this class just holds the entities that have been loaded (though not necessarily visible in the Filament Scene).
///
abstract class Scene {
///
/// The last entity clicked/tapped in the viewport (internally, the result of calling pick);
FilamentEntity? selected;
///
/// A Stream updated whenever an entity is added/removed from the scene.
///
Stream<bool> get onUpdated;
///
/// A Stream containing every FilamentEntity added to the scene (i.e. via [loadGlb], [loadGltf] or [addLight]).
/// This is provided for convenience so you can set listeners in front-end widgets that can respond to entity loads without manually passing around the FilamentEntity returned from those methods.
///
Stream<FilamentEntity> get onLoad;
///
/// A Stream containing every FilamentEntity removed from the scene (i.e. via [removeEntity], [clearEntities], [removeLight] or [clearLights]).
Stream<FilamentEntity> get onUnload;
///
/// Lists all light entities currently loaded (not necessarily active in the scene). Does not account for instances.
///
Iterable<FilamentEntity> listLights();
///
/// Lists all entities currently loaded (not necessarily active in the scene). Does not account for instances.
///
Iterable<FilamentEntity> listEntities();
///
/// Attach the gizmo to the specified entity.
///
void select(FilamentEntity entity);
///
///
///
void registerEntity(FilamentEntity entity);
}
abstract class AbstractGizmo {
bool get isActive;
void translate(double transX, double transY);
void reset();
void attach(FilamentEntity entity);
void detach();
}

View File

@@ -0,0 +1,3 @@
export 'web/compatibility.dart' if (dart.library.io) 'native/compatibility.dart';

View File

@@ -0,0 +1,85 @@
import 'dart:async';
import 'dart:ffi';
import 'package:ffi/ffi.dart';
export 'package:ffi/ffi.dart';
export 'dart:ffi';
export 'thermion_dart.g.dart';
final allocator = calloc;
Future<void> withVoidCallback(
Function(Pointer<NativeFunction<Void Function()>>) func) async {
final completer = Completer();
// ignore: prefer_function_declarations_over_variables
void Function() callback = () {
completer.complete();
};
final nativeCallable = NativeCallable<Void Function()>.listener(callback);
func.call(nativeCallable.nativeFunction);
await completer.future;
nativeCallable.close();
}
Future<int> withVoidPointerCallback(
Function(Pointer<NativeFunction<Void Function(Pointer<Void>)>>)
func) async {
final completer = Completer<Pointer<Void>>();
// ignore: prefer_function_declarations_over_variables
void Function(Pointer<Void>) callback = (Pointer<Void> ptr) {
completer.complete(ptr);
};
final nativeCallable =
NativeCallable<Void Function(Pointer<Void>)>.listener(callback);
func.call(nativeCallable.nativeFunction);
var ptr = await completer.future;
nativeCallable.close();
return ptr.address;
}
Future<bool> withBoolCallback(
Function(Pointer<NativeFunction<Void Function(Bool)>>) func) async {
final completer = Completer<bool>();
// ignore: prefer_function_declarations_over_variables
void Function(bool) callback = (bool result) {
completer.complete(result);
};
final nativeCallable = NativeCallable<Void Function(Bool)>.listener(callback);
func.call(nativeCallable.nativeFunction);
await completer.future;
nativeCallable.close();
return completer.future;
}
Future<int> withIntCallback(
Function(Pointer<NativeFunction<Void Function(Int32)>>) func) async {
final completer = Completer<int>();
// ignore: prefer_function_declarations_over_variables
void Function(int) callback = (int result) {
completer.complete(result);
};
final nativeCallable =
NativeCallable<Void Function(Int32)>.listener(callback);
func.call(nativeCallable.nativeFunction);
await completer.future;
nativeCallable.close();
return completer.future;
}
Future<String> withCharPtrCallback(
Function(Pointer<NativeFunction<Void Function(Pointer<Char>)>>)
func) async {
final completer = Completer<String>();
// ignore: prefer_function_declarations_over_variables
void Function(Pointer<Char>) callback = (Pointer<Char> result) {
completer.complete(result.cast<Utf8>().toDartString());
};
final nativeCallable =
NativeCallable<Void Function(Pointer<Char>)>.listener(callback);
func.call(nativeCallable.nativeFunction);
await completer.future;
nativeCallable.close();
return completer.future;
}
class Compatibility {}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,238 @@
import 'dart:ffi';
export "allocator.dart";
export "thermion_dart.g.dart";
import 'dart:convert';
import 'dart:ffi' as ffi hide Uint8Pointer, FloatPointer;
import 'dart:typed_data';
import 'package:thermion_dart/thermion_dart/compatibility/web/thermion_dart.g.dart';
import 'package:ffi/ffi.dart';
export 'package:ffi/ffi.dart' hide StringUtf8Pointer, Utf8Pointer;
export 'dart:ffi'
hide
Uint8Pointer,
FloatPointer,
DoublePointer,
Int32Pointer,
Int64Pointer,
PointerPointer,
Allocator;
class Allocator implements ffi.Allocator {
const Allocator();
@override
ffi.Pointer<T> allocate<T extends ffi.NativeType>(int byteCount,
{int? alignment}) {
return thermion_flutter_web_allocate(byteCount).cast<T>();
}
@override
void free(ffi.Pointer<ffi.NativeType> pointer) {
thermion_flutter_web_free(pointer.cast<ffi.Void>());
}
}
extension CharPointer on ffi.Pointer<ffi.Char> {
int get value {
return thermion_flutter_web_get(this, 0);
}
set value(int value) {
thermion_flutter_web_set(this, 0, value);
}
void operator []=(int index, int value) {
this.elementAt(index).value = value;
}
ffi.Pointer<ffi.Char> elementAt(int index) =>
ffi.Pointer.fromAddress(address + ffi.sizeOf<ffi.Char>() * index);
}
extension IntPointer on ffi.Pointer<ffi.Int> {
int get value {
return thermion_flutter_web_get_int32(this.cast<ffi.Int32>(), 0);
}
set value(int value) {
thermion_flutter_web_set_int32(this.cast<ffi.Int32>(), 0, value);
}
void operator []=(int index, int value) {
this.elementAt(index).value = value;
}
int operator [](int index) {
return this.elementAt(index).value;
}
ffi.Pointer<ffi.Int> elementAt(int index) =>
ffi.Pointer.fromAddress(address + ffi.sizeOf<ffi.Int>() * index);
}
extension Int32Pointer on ffi.Pointer<ffi.Int32> {
int get value {
return thermion_flutter_web_get_int32(this, 0);
}
set value(int value) {
thermion_flutter_web_set_int32(this, 0, value);
}
void operator []=(int index, int value) {
this.elementAt(index).value = value;
}
int operator [](int index) {
return this.elementAt(index).value;
}
ffi.Pointer<ffi.Int32> elementAt(int index) =>
ffi.Pointer.fromAddress(address + ffi.sizeOf<ffi.Int32>() * index);
}
extension UInt8Pointer on ffi.Pointer<ffi.Uint8> {
int get value {
return thermion_flutter_web_get(this.cast<ffi.Char>(), 0);
}
set value(int value) {
thermion_flutter_web_set(this.cast<ffi.Char>(), 0, value);
}
void operator []=(int index, int value) {
this.elementAt(index).value = value;
}
int operator [](int index) {
return this.elementAt(index).value;
}
ffi.Pointer<ffi.Uint8> elementAt(int index) =>
ffi.Pointer.fromAddress(address + ffi.sizeOf<ffi.Uint8>() * index);
}
extension PointerPointer<T extends ffi.NativeType>
on ffi.Pointer<ffi.Pointer<T>> {
ffi.Pointer<T> get value {
return thermion_flutter_web_get_pointer(cast<ffi.Pointer<ffi.Void>>(), 0)
.cast<T>();
}
set value(ffi.Pointer<T> value) {
thermion_flutter_web_set_pointer(
cast<ffi.Pointer<ffi.Void>>(), 0, value.cast<ffi.Void>());
}
ffi.Pointer<T> operator [](int index) {
return this.elementAt(index).value;
}
void operator []=(int index, ffi.Pointer<T> value) {
this.elementAt(index).value = value;
}
ffi.Pointer<ffi.Pointer<T>> elementAt(int index) =>
ffi.Pointer.fromAddress(address + ffi.sizeOf<ffi.Pointer>() * index);
}
extension FloatPointer on ffi.Pointer<ffi.Float> {
double get value {
return thermion_flutter_web_get_float(this, 0);
}
set value(double value) {
thermion_flutter_web_set_float(this, 0, value);
}
double operator [](int index) {
return this.elementAt(index).value;
}
void operator []=(int index, double value) {
this.elementAt(index).value = value;
}
ffi.Pointer<ffi.Float> elementAt(int index) =>
ffi.Pointer.fromAddress(address + ffi.sizeOf<ffi.Float>() * index);
Float32List asTypedList(int length) {
var list = Float32List(length);
for (int i = 0; i < length; i++) {
list[i] = this[i];
}
return list;
}
}
extension StringConversion on String {
ffi.Pointer<Utf8> toNativeUtf8({ffi.Allocator? allocator}) {
final units = utf8.encode(this);
final ffi.Pointer<ffi.Uint8> result =
allocator!<ffi.Uint8>(units.length + 1);
for (int i = 0; i < units.length; i++) {
result.elementAt(i).value = units[i];
}
result.elementAt(units.length).value = 0;
return result.cast();
}
}
extension StringUtf8Pointer on ffi.Pointer<Utf8> {
static int _length(ffi.Pointer<ffi.Uint8> codeUnits) {
var length = 0;
while (codeUnits[length] != 0) {
length++;
}
return length;
}
String toDartString({int? length}) {
final codeUnits = this.cast<ffi.Uint8>();
final list = <int>[];
if (length != null) {
RangeError.checkNotNegative(length, 'length');
} else {
length = _length(codeUnits);
}
for (int i = 0; i < length; i++) {
list.add(codeUnits.elementAt(i).value);
}
return utf8.decode(list);
}
}
extension DoublePointer on ffi.Pointer<ffi.Double> {
double get value {
return thermion_flutter_web_get_double(this, 0);
}
set value(double value) {
return thermion_flutter_web_set_double(this, 0, value);
}
Float64List asTypedList(int length) {
var list = Float64List(length);
for (int i = 0; i < length; i++) {
list[i] = elementAt(i).value;
}
return list;
}
double operator [](int index) {
return elementAt(index).value;
}
void operator []=(int index, double value) {
elementAt(index).value = value;
}
ffi.Pointer<ffi.Double> elementAt(int index) =>
ffi.Pointer.fromAddress(address + ffi.sizeOf<ffi.Double>() * index);
}

View File

@@ -0,0 +1,118 @@
import 'dart:async';
import 'dart:js_interop';
import 'package:thermion_dart/thermion_dart/compatibility/web/interop.dart';
import "allocator.dart";
export "allocator.dart";
export "thermion_dart.g.dart";
export 'package:ffi/ffi.dart' hide StringUtf8Pointer, Utf8Pointer;
export 'dart:ffi'
hide
Uint8Pointer,
FloatPointer,
DoublePointer,
Int32Pointer,
Int64Pointer,
PointerPointer,
Allocator;
const allocator = Allocator();
@AbiSpecificIntegerMapping({
Abi.androidArm: Uint8(),
Abi.androidArm64: Uint8(),
Abi.androidIA32: Int8(),
Abi.androidX64: Int8(),
Abi.androidRiscv64: Uint8(),
Abi.fuchsiaArm64: Uint8(),
Abi.fuchsiaX64: Int8(),
Abi.fuchsiaRiscv64: Uint8(),
Abi.iosArm: Int8(),
Abi.iosArm64: Int8(),
Abi.iosX64: Int8(),
Abi.linuxArm: Uint8(),
Abi.linuxArm64: Uint8(),
Abi.linuxIA32: Int8(),
Abi.linuxX64: Int8(),
Abi.linuxRiscv32: Uint8(),
Abi.linuxRiscv64: Uint8(),
Abi.macosArm64: Int8(),
Abi.macosX64: Int8(),
Abi.windowsArm64: Int8(),
Abi.windowsIA32: Int8(),
Abi.windowsX64: Int8(),
})
final class FooChar extends AbiSpecificInteger {
const FooChar();
}
class Compatibility {
final _foo = FooChar();
}
Future<void> withVoidCallback(
Function(Pointer<NativeFunction<Void Function()>>) func) async {
JSArray retVal = createVoidCallback();
var promise = retVal.toDart[0] as JSPromise<JSNumber>;
var fnPtrAddress = retVal.toDart[1] as JSNumber;
var fnPtr = Pointer<NativeFunction<Void Function()>>.fromAddress(
fnPtrAddress.toDartInt);
func(fnPtr);
await promise.toDart;
}
Future<int> withVoidPointerCallback(
void Function(Pointer<NativeFunction<Void Function(Pointer<Void>)>>)
func) async {
JSArray retVal = createVoidPointerCallback();
var promise = retVal.toDart[0] as JSPromise<JSNumber>;
var fnPtrAddress = retVal.toDart[1] as JSNumber;
var fnPtr = Pointer<NativeFunction<Void Function(Pointer<Void>)>>.fromAddress(
fnPtrAddress.toDartInt);
func(fnPtr);
final addr = await promise.toDart;
return addr.toDartInt;
}
Future<bool> withBoolCallback(
Function(Pointer<NativeFunction<Void Function(Bool)>>) func) async {
JSArray retVal = createBoolCallback();
var promise = retVal.toDart[0] as JSPromise<JSBoolean>;
var fnPtrAddress = retVal.toDart[1] as JSNumber;
var fnPtr = Pointer<NativeFunction<Void Function(Bool)>>.fromAddress(
fnPtrAddress.toDartInt);
func(fnPtr);
final addr = await promise.toDart;
return addr.toDart;
}
Future<int> withIntCallback(
Function(Pointer<NativeFunction<Void Function(Int32)>>) func) async {
JSArray retVal = createBoolCallback();
var promise = retVal.toDart[0] as JSPromise<JSNumber>;
var fnPtrAddress = retVal.toDart[1] as JSNumber;
var fnPtr = Pointer<NativeFunction<Void Function(Int32)>>.fromAddress(
fnPtrAddress.toDartInt);
func(fnPtr);
final addr = await promise.toDart;
return addr.toDartInt;
}
Future<String> withCharPtrCallback(
Function(Pointer<NativeFunction<Void Function(Pointer<Char>)>>)
func) async {
JSArray retVal = createVoidPointerCallback();
var promise = retVal.toDart[0] as JSPromise<JSNumber>;
var fnPtrAddress = retVal.toDart[1] as JSNumber;
var fnPtr = Pointer<NativeFunction<Void Function(Pointer<Char>)>>.fromAddress(
fnPtrAddress.toDartInt);
func(fnPtr);
final addr = await promise.toDart;
return Pointer<Utf8>.fromAddress(addr.toDartInt).toDartString();
}

View File

@@ -0,0 +1,16 @@
import 'dart:js_interop';
@JS()
external JSArray createIntCallback();
@JS()
external JSArray createBoolCallback();
@JS()
external JSArray createVoidPointerCallback();
@JS()
external JSArray createVoidCallback();

View File

@@ -0,0 +1,816 @@
import 'dart:js_interop';
import 'dart:js_interop_unsafe';
import 'dart:math';
import 'package:animation_tools_dart/animation_tools_dart.dart';
import 'package:thermion_dart/thermion_dart/abstract_filament_viewer.dart';
import 'package:thermion_dart/thermion_dart/entities/filament_entity.dart';
import 'package:thermion_dart/thermion_dart/scene.dart';
import 'package:vector_math/vector_math_64.dart';
import 'shims/abstract_filament_viewer_js_shim.dart';
///
/// An [AbstractFilamentViewer] implementation that forwards calls to
/// a corresponding Javascript shim implementation (see [AbstractFilamentViewerJSShim]).
///
class FilamentViewerJS implements AbstractFilamentViewer {
late final AbstractFilamentViewerJSShim _shim;
FilamentViewerJS.fromGlobalProperty(String globalPropertyName) {
this._shim = globalContext.getProperty(globalPropertyName.toJS)
as AbstractFilamentViewerJSShim;
}
FilamentViewerJS(this._shim);
@override
Future<bool> get initialized async {
var inited = _shim.initialized;
final JSBoolean result = await inited.toDart;
return result.toDart;
}
@override
Stream<FilamentPickResult> get pickResult {
throw UnimplementedError();
}
@override
bool get rendering => _shim.rendering;
@override
Future<void> setRendering(bool render) async {
await _shim.setRendering(render).toDart;
}
@override
Future<void> render() async {
await _shim.render().toDart;
}
@override
Future<void> setFrameRate(int framerate) async {
await _shim.setFrameRate(framerate).toDart;
}
@override
Future<void> dispose() async {
await _shim.dispose().toDart;
}
@override
Future<void> setBackgroundImage(String path,
{bool fillHeight = false}) async {
await _shim.setBackgroundImage(path, fillHeight).toDart;
}
@override
Future<void> setBackgroundImagePosition(double x, double y,
{bool clamp = false}) async {
await _shim.setBackgroundImagePosition(x, y, clamp).toDart;
}
@override
Future<void> clearBackgroundImage() async {
await _shim.clearBackgroundImage().toDart;
}
@override
Future<void> setBackgroundColor(
double r, double g, double b, double alpha) async {
await _shim.setBackgroundColor(r, g, b, alpha).toDart;
}
@override
Future<void> loadSkybox(String skyboxPath) async {
await _shim.loadSkybox(skyboxPath).toDart;
}
@override
Future<void> removeSkybox() async {
await _shim.removeSkybox().toDart;
}
@override
Future<void> loadIbl(String lightingPath, {double intensity = 30000}) async {
await _shim.loadIbl(lightingPath, intensity).toDart;
}
@override
Future<void> rotateIbl(Matrix3 rotation) async {
await _shim
.rotateIbl(rotation.storage.map((v) => v.toJS).toList().toJS)
.toDart;
}
@override
Future<void> removeIbl() async {
await _shim.removeIbl().toDart;
}
@override
Future<FilamentEntity> 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}) async {
return (await _shim
.addLight(
type.index,
colour,
intensity,
posX,
posY,
posZ,
dirX,
dirY,
dirZ,
falloffRadius,
spotLightConeInner,
spotLightConeOuter,
sunAngularRadius,
sunHaloSize,
sunHaloFallof,
castShadows)
.toDart)
.toDartInt;
}
@override
Future<void> removeLight(FilamentEntity light) async {
await _shim.removeLight(light).toDart;
}
@override
Future<void> clearLights() async {
await _shim.clearLights().toDart;
}
@override
Future<FilamentEntity> loadGlb(String path, {int numInstances = 1}) async {
var entity = (await _shim.loadGlb(path, numInstances).toDart).toDartInt;
scene.registerEntity(entity);
return entity;
}
@override
Future<FilamentEntity> createInstance(FilamentEntity entity) async {
return (await _shim.createInstance(entity).toDart).toDartInt;
}
@override
Future<int> getInstanceCount(FilamentEntity entity) async {
return (await _shim.getInstanceCount(entity).toDart).toDartInt;
}
@override
Future<List<FilamentEntity>> getInstances(FilamentEntity entity) async {
throw UnimplementedError();
// final List<JSObject> jsInstances =
// await _shim.getInstances(entity).toDart;
// return jsInstances
// .map((js) => FilamentEntity._fromJSObject(js))
// .toList()
// .toDart;
}
@override
Future<FilamentEntity> loadGltf(String path, String relativeResourcePath,
{bool force = false}) async {
throw UnimplementedError();
// final FilamentEntity jsEntity = await _shim
// .loadGltf(path, relativeResourcePath, force: force)
// .toDart;
// return FilamentEntity._fromJSObject(jsEntity).toDart;
}
@override
Future<void> panStart(double x, double y) async {
await _shim.panStart(x, y).toDart;
}
@override
Future<void> panUpdate(double x, double y) async {
await _shim.panUpdate(x, y).toDart;
}
@override
Future<void> panEnd() async {
await _shim.panEnd().toDart;
}
@override
Future<void> rotateStart(double x, double y) async {
await _shim.rotateStart(x, y).toDart;
}
@override
Future<void> rotateUpdate(double x, double y) async {
await _shim.rotateUpdate(x, y).toDart;
}
@override
Future<void> rotateEnd() async {
await _shim.rotateEnd().toDart;
}
@override
Future<void> setMorphTargetWeights(
FilamentEntity entity, List<double> weights) async {
var jsWeights = weights.map((x) => x.toJS).cast<JSNumber>().toList().toJS;
var promise = _shim.setMorphTargetWeights(entity, jsWeights);
await promise.toDart;
}
@override
Future<List<String>> getMorphTargetNames(
FilamentEntity entity, FilamentEntity childEntity) async {
var result = await _shim.getMorphTargetNames(entity, childEntity).toDart;
return result.toDart.map((r) => r.toDart).toList();
}
@override
Future<List<String>> getAnimationNames(FilamentEntity entity) async {
var names = (await (_shim.getAnimationNames(entity).toDart))
.toDart
.map((x) => x.toDart)
.toList();
return names;
}
@override
Future<double> getAnimationDuration(
FilamentEntity entity, int animationIndex) async {
return (await _shim.getAnimationDuration(entity, animationIndex).toDart)
.toDartDouble;
}
@override
Future<void> setMorphAnimationData(
FilamentEntity entity, MorphAnimationData animation,
{List<String>? targetMeshNames}) async {
try {
var animationDataJs = animation.data
.map((x) => x.map((y) => y.toJS).toList().toJS)
.toList()
.toJS;
var morphTargetsJs = animation.morphTargets
.map((x) => x.toJS)
.cast<JSString>()
.toList()
.toJS;
var targetMeshNamesJS =
targetMeshNames?.map((x) => x.toJS).cast<JSString>().toList().toJS;
await _shim
.setMorphAnimationData(entity, animationDataJs, morphTargetsJs,
targetMeshNamesJS, animation.frameLengthInMs)
.toDart;
} catch (err, st) {
print(err);
print(st);
rethrow;
}
}
@override
Future<void> resetBones(FilamentEntity entity) async {
await _shim.resetBones(entity).toDart;
}
@override
Future<void> addBoneAnimation(
FilamentEntity entity, BoneAnimationData animation,
{int skinIndex = 0,
double fadeInInSecs = 0.0,
double fadeOutInSecs = 0.0,
double maxDelta = 1.0}) async {
var boneNames = animation.bones.map((n) => n.toJS).toList().toJS;
var frameData = animation.frameData
.map((frame) => frame
.map((q) => [
q.translation[0].toJS,
q.translation[1].toJS,
q.translation[2].toJS,
q.rotation.x.toJS,
q.rotation.y.toJS,
q.rotation.z.toJS,
q.rotation.w.toJS,
].toJS)
.toList()
.toJS)
.toList()
.toJS;
await _shim
.addBoneAnimation(
entity,
boneNames,
frameData,
animation.frameLengthInMs.toJS,
animation.space.index.toJS,
skinIndex.toJS,
fadeInInSecs.toJS,
fadeOutInSecs.toJS,
maxDelta.toJS)
.toDart;
}
@override
Future<void> removeEntity(FilamentEntity entity) async {
await _shim.removeEntity(entity).toDart;
}
@override
Future<void> clearEntities() async {
await _shim.clearEntities().toDart;
}
@override
Future<void> zoomBegin() async {
await _shim.zoomBegin().toDart;
}
@override
Future<void> zoomUpdate(double x, double y, double z) async {
await _shim.zoomUpdate(x, y, z).toDart;
}
@override
Future<void> zoomEnd() async {
await _shim.zoomEnd().toDart;
}
@override
Future<void> playAnimation(FilamentEntity entity, int index,
{bool loop = false,
bool reverse = false,
bool replaceActive = true,
double crossfade = 0.0}) async {
await _shim
.playAnimation(entity, index, loop, reverse, replaceActive, crossfade)
.toDart;
}
@override
Future<void> playAnimationByName(FilamentEntity entity, String name,
{bool loop = false,
bool reverse = false,
bool replaceActive = true,
double crossfade = 0.0}) async {
await _shim
.playAnimationByName(
entity, name, loop, reverse, replaceActive, crossfade)
.toDart;
}
@override
Future<void> setAnimationFrame(
FilamentEntity entity, int index, int animationFrame) async {
await _shim.setAnimationFrame(entity, index, animationFrame).toDart;
}
@override
Future<void> stopAnimation(FilamentEntity entity, int animationIndex) async {
await _shim.stopAnimation(entity, animationIndex).toDart;
}
@override
Future<void> stopAnimationByName(FilamentEntity entity, String name) async {
await _shim.stopAnimationByName(entity, name).toDart;
}
@override
Future<void> setCamera(FilamentEntity entity, String? name) async {
await _shim.setCamera(entity, name).toDart;
}
@override
Future<void> setMainCamera() async {
await _shim.setMainCamera().toDart;
}
@override
Future<FilamentEntity> getMainCamera() async {
throw UnimplementedError();
// final FilamentEntity jsEntity = await _shim.getMainCamera().toDart;
// return FilamentEntity._fromJSObject(jsEntity).toDart;
}
@override
Future<void> setCameraFov(double degrees, double width, double height) async {
await _shim.setCameraFov(degrees, width, height).toDart;
}
@override
Future<void> setToneMapping(ToneMapper mapper) async {
await _shim.setToneMapping(mapper.index).toDart;
}
@override
Future<void> setBloom(double bloom) async {
await _shim.setBloom(bloom).toDart;
}
@override
Future<void> setCameraFocalLength(double focalLength) async {
await _shim.setCameraFocalLength(focalLength).toDart;
}
@override
Future<void> setCameraCulling(double near, double far) async {
await _shim.setCameraCulling(near, far).toDart;
}
@override
Future<double> getCameraCullingNear() async {
return (await _shim.getCameraCullingNear().toDart).toDartDouble;
}
@override
Future<double> getCameraCullingFar() async {
return (await _shim.getCameraCullingFar().toDart).toDartDouble;
}
@override
Future<void> setCameraFocusDistance(double focusDistance) async {
await _shim.setCameraFocusDistance(focusDistance).toDart;
}
@override
Future<Vector3> getCameraPosition() async {
final jsPosition = (await _shim.getCameraPosition().toDart).toDart;
return Vector3(jsPosition[0].toDartDouble, jsPosition[1].toDartDouble,
jsPosition[2].toDartDouble);
}
@override
Future<Matrix4> getCameraModelMatrix() async {
throw UnimplementedError();
// final JSMatrix4 jsMatrix = await _shim.getCameraModelMatrix().toDart;
// return Matrix4.fromList(jsMatrix.storage).toDart;
}
@override
Future<Matrix4> getCameraViewMatrix() async {
throw UnimplementedError();
// final JSMatrix4 jsMatrix = await _shim.getCameraViewMatrix().toDart;
// return Matrix4.fromList(jsMatrix.storage).toDart;
}
@override
Future<Matrix4> getCameraProjectionMatrix() async {
throw UnimplementedError();
// final JSMatrix4 jsMatrix =
// await _shim.getCameraProjectionMatrix().toDart;
// return Matrix4.fromList(jsMatrix.storage).toDart;
}
@override
Future<Matrix4> getCameraCullingProjectionMatrix() async {
throw UnimplementedError();
// final JSMatrix4 jsMatrix =
// await _shim.getCameraCullingProjectionMatrix().toDart;
// return Matrix4.fromList(jsMatrix.storage).toDart;
}
@override
Future<Frustum> getCameraFrustum() async {
throw UnimplementedError();
// final JSObject jsFrustum = await _shim.getCameraFrustum().toDart;
// // Assuming Frustum is a class that can be constructed from the JSObject
// return Frustum._fromJSObject(jsFrustum).toDart;
}
@override
Future<void> setCameraPosition(double x, double y, double z) async {
await _shim.setCameraPosition(x, y, z).toDart;
}
@override
Future<Matrix3> getCameraRotation() async {
throw UnimplementedError();
// final JSMatrix3 jsRotation = await _shim.getCameraRotation().toDart;
// return Matrix3.fromList(jsRotation.storage).toDart;
}
@override
Future<void> moveCameraToAsset(FilamentEntity entity) async {
await _shim.moveCameraToAsset(entity).toDart;
}
@override
Future<void> setViewFrustumCulling(bool enabled) async {
throw UnimplementedError();
// await _shim.setViewFrustumCulling(enabled.toJSBoolean()).toDart;
}
@override
Future<void> setCameraExposure(
double aperture, double shutterSpeed, double sensitivity) async {
await _shim.setCameraExposure(aperture, shutterSpeed, sensitivity).toDart;
}
@override
Future<void> setCameraRotation(Quaternion quaternion) async {
final values = <JSNumber>[
quaternion.x.toJS,
quaternion.y.toJS,
quaternion.z.toJS,
quaternion.w.toJS
];
await _shim.setCameraRotation(values.toJS).toDart;
}
@override
Future<void> setCameraModelMatrix(List<double> matrix) async {
throw UnimplementedError();
// await _shim.setCameraModelMatrix(matrix.toJSBox).toDart;
}
@override
Future<void> setMaterialColor(FilamentEntity entity, String meshName,
int materialIndex, double r, double g, double b, double a) async {
await _shim
.setMaterialColor(entity, meshName, materialIndex, r, g, b, a)
.toDart;
}
@override
Future<void> transformToUnitCube(FilamentEntity entity) async {
await _shim.transformToUnitCube(entity).toDart;
}
@override
Future<void> setPosition(
FilamentEntity entity, double x, double y, double z) async {
await _shim.setPosition(entity, x, y, z).toDart;
}
@override
Future<void> setScale(FilamentEntity entity, double scale) async {
await _shim.setScale(entity, scale).toDart;
}
@override
Future<void> setRotation(
FilamentEntity entity, double rads, double x, double y, double z) async {
await _shim.setRotation(entity, rads, x, y, z).toDart;
}
@override
Future<void> queuePositionUpdate(
FilamentEntity entity, double x, double y, double z,
{bool relative = false}) async {
await _shim.queuePositionUpdate(entity, x, y, z, relative).toDart;
}
@override
Future<void> queueRotationUpdate(
FilamentEntity entity, double rads, double x, double y, double z,
{bool relative = false}) async {
await _shim.queueRotationUpdate(entity, rads, x, y, z, relative).toDart;
}
@override
Future<void> queueRotationUpdateQuat(FilamentEntity entity, Quaternion quat,
{bool relative = false}) async {
throw UnimplementedError();
// final JSQuaternion jsQuat = quat.toJSQuaternion().toDart;
// await _shim
// .queueRotationUpdateQuat(entity, jsQuat, relative: relative)
// .toDart;
}
@override
Future<void> setPostProcessing(bool enabled) async {
await _shim.setPostProcessing(enabled).toDart;
}
@override
Future<void> setAntiAliasing(bool msaa, bool fxaa, bool taa) async {
await _shim.setAntiAliasing(msaa, fxaa, taa).toDart;
}
@override
Future<void> setRotationQuat(
FilamentEntity entity, Quaternion rotation) async {
throw UnimplementedError();
// final JSQuaternion jsRotation = rotation.toJSQuaternion().toDart;
// await _shim.setRotationQuat(entity, jsRotation).toDart;
}
@override
Future<void> reveal(FilamentEntity entity, String? meshName) async {
throw UnimplementedError();
// await _shim.reveal(entity, meshName).toDart;
}
@override
Future<void> hide(FilamentEntity entity, String? meshName) async {
throw UnimplementedError();
// await _shim.hide(entity, meshName).toDart;
}
@override
void pick(int x, int y) {
throw UnimplementedError();
// _shim.pick(x, y).toDart;
}
@override
String? getNameForEntity(FilamentEntity entity) {
return _shim.getNameForEntity(entity);
}
@override
Future<void> setCameraManipulatorOptions(
{ManipulatorMode mode = ManipulatorMode.ORBIT,
double orbitSpeedX = 0.01,
double orbitSpeedY = 0.01,
double zoomSpeed = 0.01}) async {
await _shim
.setCameraManipulatorOptions(
mode.index, orbitSpeedX, orbitSpeedY, zoomSpeed)
.toDart;
}
@override
Future<List<FilamentEntity>> getChildEntities(
FilamentEntity parent, bool renderableOnly) async {
final children =
await _shim.getChildEntities(parent, renderableOnly).toDart;
return children.toDart
.map((js) => js.toDartInt)
.cast<FilamentEntity>()
.toList();
}
@override
Future<FilamentEntity> getChildEntity(
FilamentEntity parent, String childName) async {
return (await _shim.getChildEntity(parent, childName).toDart).toDartInt;
}
@override
Future<List<String>> getChildEntityNames(FilamentEntity entity,
{bool renderableOnly = true}) async {
var names = await _shim.getChildEntityNames(entity, renderableOnly).toDart;
return names.toDart.map((x) => x.toDart).toList();
}
@override
Future<void> setRecording(bool recording) async {
throw UnimplementedError();
// await _shim.setRecording(recording.toJSBoolean()).toDart;
}
@override
Future<void> setRecordingOutputDirectory(String outputDirectory) async {
await _shim.setRecordingOutputDirectory(outputDirectory).toDart;
}
@override
Future<void> addAnimationComponent(FilamentEntity entity) async {
await _shim.addAnimationComponent(entity).toDart;
}
@override
Future<void> addCollisionComponent(FilamentEntity entity,
{void Function(int entityId1, int entityId2)? callback,
bool affectsTransform = false}) async {
throw UnimplementedError();
// final JSFunction? jsCallback = callback != null
// ? allowInterop(
// (int entityId1, int entityId2) => callback(entityId1, entityId2))
// : null;
// await _shim
// .addCollisionComponent(entity,
// callback: jsCallback,
// affectsTransform: affectsTransform.toJSBoolean())
// .toDart;
}
@override
Future<void> removeCollisionComponent(FilamentEntity entity) async {
await _shim.removeCollisionComponent(entity).toDart;
}
@override
Future<FilamentEntity> createGeometry(
List<double> vertices, List<int> indices,
{String? materialPath,
PrimitiveType primitiveType = PrimitiveType.TRIANGLES}) async {
throw UnimplementedError();
// final FilamentEntity jsEntity = await _shim
// .createGeometry(vertices, indices,
// materialPath: materialPath, primitiveType: primitiveType.index)
// .toDart;
// return FilamentEntity._fromJSObject(jsEntity).toDart;
}
@override
Future<void> setParent(FilamentEntity child, FilamentEntity parent) async {
await _shim.setParent(child, parent).toDart;
}
@override
Future<void> testCollisions(FilamentEntity entity) async {
await _shim.testCollisions(entity).toDart;
}
@override
Future<void> setPriority(FilamentEntity entityId, int priority) async {
await _shim.setPriority(entityId, priority).toDart;
}
Scene? _scene;
// @override
Scene get scene {
_scene ??= SceneImpl(this);
return _scene!;
}
AbstractGizmo? get gizmo => null;
@override
Future<List<String>> getBoneNames(FilamentEntity entity,
{int skinIndex = 0}) async {
var result = await _shim.getBoneNames(entity, skinIndex).toDart;
return result.toDart.map((n) => n.toDart).toList();
}
@override
Future<FilamentEntity> getBone(FilamentEntity entity, int boneIndex,
{int skinIndex = 0}) async {
var result = await _shim.getBone(entity, boneIndex, skinIndex).toDart;
return result.toDartInt;
}
@override
Future<Matrix4> getInverseBindMatrix(FilamentEntity parent, int boneIndex,
{int skinIndex = 0}) {
// TODO: implement getInverseBindMatrix
throw UnimplementedError();
}
@override
Future<Matrix4> getLocalTransform(FilamentEntity entity) async {
var result = await _shim.getLocalTransform(entity).toDart;
return Matrix4.fromList(result.toDart.map((v) => v.toDartDouble).toList());
}
@override
Future<FilamentEntity?> getParent(FilamentEntity child) async {
var result = await _shim.getParent(child).toDart;
return result.toDartInt;
}
@override
Future<Matrix4> getWorldTransform(FilamentEntity entity) async {
var result = await _shim.getLocalTransform(entity).toDart;
return Matrix4.fromList(result.toDart.map((v) => v.toDartDouble).toList());
}
@override
Future removeAnimationComponent(FilamentEntity entity) {
return _shim.removeAnimationComponent(entity).toDart;
}
@override
Future setBoneTransform(
FilamentEntity entity, int boneIndex, Matrix4 transform,
{int skinIndex = 0}) {
return _shim
.setBoneTransform(entity, boneIndex,
transform.storage.map((v) => v.toJS).toList().toJS, skinIndex)
.toDart;
}
@override
Future setTransform(FilamentEntity entity, Matrix4 transform) {
return _shim
.setTransform(
entity, transform.storage.map((v) => v.toJS).toList().toJS)
.toDart;
}
@override
Future updateBoneMatrices(FilamentEntity entity) {
return _shim.updateBoneMatrices(entity).toDart;
}
}

View File

@@ -0,0 +1,721 @@
@JS()
library thermion_flutter_js;
import 'dart:js_interop';
import 'package:thermion_dart/thermion_dart/compatibility/web/interop/shims/abstract_filament_viewer_js_shim.dart';
import 'package:thermion_dart/thermion_dart/entities/filament_entity.dart';
import 'package:vector_math/vector_math_64.dart' as v64;
import 'package:animation_tools_dart/animation_tools_dart.dart';
import 'package:thermion_dart/thermion_dart/abstract_filament_viewer.dart';
import 'dart:js_interop_unsafe';
import 'package:vector_math/vector_math_64.dart';
///
/// A (Dart) class that wraps a (Dart) instance of [AbstractFilamentViewer],
/// but exported to JS by binding to a global property.
/// This is effectively an implementation of [AbstractFilamentViewerJSShim];
/// allowing users to interact with an instance of [AbstractFilamentViewer]
/// (presumably compiled to WASM) from any Javascript context (including
/// the browser console).
///
@JSExport()
class FilamentViewerJSDartBridge {
final AbstractFilamentViewer viewer;
FilamentViewerJSDartBridge(this.viewer);
void bind(
{String globalPropertyName = "filamentViewer"}) {
var wrapper = createJSInteropWrapper<FilamentViewerJSDartBridge>(this)
as AbstractFilamentViewerJSShim;
globalContext.setProperty(globalPropertyName.toJS, wrapper);
}
JSPromise<JSBoolean> get initialized {
return viewer.initialized.then((v) => v.toJS).toJS;
}
@JSExport()
JSBoolean get rendering => viewer.rendering.toJS;
@JSExport()
JSPromise setRendering(bool render) {
return viewer.setRendering(render).toJS;
}
@JSExport()
JSPromise render() => viewer.render().toJS;
@JSExport()
JSPromise setFrameRate(int framerate) => viewer.setFrameRate(framerate).toJS;
@JSExport()
JSPromise dispose() => viewer.dispose().toJS;
@JSExport()
JSPromise setBackgroundImage(String path, {bool fillHeight = false}) =>
viewer.setBackgroundImage(path, fillHeight: fillHeight).toJS;
@JSExport()
JSPromise setBackgroundImagePosition(double x, double y,
{bool clamp = false}) =>
viewer.setBackgroundImagePosition(x, y, clamp: clamp).toJS;
@JSExport()
JSPromise clearBackgroundImage() => viewer.clearBackgroundImage().toJS;
@JSExport()
JSPromise setBackgroundColor(double r, double g, double b, double alpha) =>
viewer.setBackgroundColor(r, g, b, alpha).toJS;
@JSExport()
JSPromise loadSkybox(String skyboxPath) => viewer.loadSkybox(skyboxPath).toJS;
@JSExport()
JSPromise removeSkybox() => viewer.removeSkybox().toJS;
@JSExport()
JSPromise loadIbl(String lightingPath, double intensity) {
print("Loading IBL from $lightingPath with intensity $intensity");
return viewer.loadIbl(lightingPath, intensity: intensity).toJS;
}
@JSExport()
JSPromise rotateIbl(JSArray<JSNumber> rotation) {
var matrix =
Matrix3.fromList(rotation.toDart.map((v) => v.toDartDouble).toList());
return viewer.rotateIbl(matrix).toJS;
}
@JSExport()
JSPromise removeIbl() => viewer.removeIbl().toJS;
@JSExport()
JSPromise<JSNumber> addLight(
int type,
double colour,
double intensity,
double posX,
double posY,
double posZ,
double dirX,
double dirY,
double dirZ,
double falloffRadius,
double spotLightConeInner,
double spotLightConeOuter,
double sunAngularRadius,
double sunHaloSize,
double sunHaloFallof,
bool castShadows) {
return viewer
.addLight(LightType.values[type], colour, intensity, posX, posY, posZ,
dirX, dirY, dirZ,
falloffRadius: falloffRadius,
spotLightConeInner: spotLightConeInner,
spotLightConeOuter: spotLightConeOuter,
sunAngularRadius: sunAngularRadius,
sunHaloSize: sunHaloSize,
sunHaloFallof: sunHaloFallof,
castShadows: castShadows)
.then((entity) => entity.toJS)
.toJS;
}
@JSExport()
JSPromise removeLight(FilamentEntity light) => viewer.removeLight(light).toJS;
@JSExport()
JSPromise clearLights() => viewer.clearLights().toJS;
@JSExport()
JSPromise<JSNumber> loadGlb(String path, {int numInstances = 1}) {
return viewer
.loadGlb(path, numInstances: numInstances)
.then((entity) => entity.toJS)
.toJS;
}
@JSExport()
JSPromise<JSNumber> createInstance(FilamentEntity entity) {
return viewer.createInstance(entity).then((instance) => instance.toJS).toJS;
}
@JSExport()
JSPromise<JSNumber> getInstanceCount(FilamentEntity entity) =>
viewer.getInstanceCount(entity).then((v) => v.toJS).toJS;
@JSExport()
JSPromise<JSArray<JSNumber>> getInstances(FilamentEntity entity) {
return viewer
.getInstances(entity)
.then((instances) =>
instances.map((instance) => instance.toJS).toList().toJS)
.toJS;
}
@JSExport()
JSPromise<JSNumber> loadGltf(String path, String relativeResourcePath,
{bool force = false}) {
return viewer
.loadGltf(path, relativeResourcePath, force: force)
.then((entity) => entity.toJS)
.toJS;
}
@JSExport()
JSPromise panStart(double x, double y) => viewer.panStart(x, y).toJS;
@JSExport()
JSPromise panUpdate(double x, double y) => viewer.panUpdate(x, y).toJS;
@JSExport()
JSPromise panEnd() => viewer.panEnd().toJS;
@JSExport()
JSPromise rotateStart(double x, double y) => viewer.rotateStart(x, y).toJS;
@JSExport()
JSPromise rotateUpdate(double x, double y) => viewer.rotateUpdate(x, y).toJS;
@JSExport()
JSPromise rotateEnd() => viewer.rotateEnd().toJS;
@JSExport()
JSPromise setMorphTargetWeights(
FilamentEntity entity, JSArray<JSNumber> weights) {
var dartWeights = weights.toDart.map((w) => w.toDartDouble).toList();
return viewer.setMorphTargetWeights(entity, dartWeights).toJS;
}
@JSExport()
JSPromise<JSArray<JSString>> getMorphTargetNames(
FilamentEntity entity, FilamentEntity childEntity) {
var morphTargetNames = viewer
.getMorphTargetNames(entity, childEntity)
.then((v) => v.map((s) => s.toJS).toList().toJS);
return morphTargetNames.toJS;
}
@JSExport()
JSPromise<JSArray<JSString>> getBoneNames(
FilamentEntity entity, int skinIndex) {
return viewer
.getBoneNames(entity, skinIndex: skinIndex)
.then((v) => v.map((s) => s.toJS).toList().toJS)
.toJS;
}
@JSExport()
JSPromise<JSArray<JSString>> getAnimationNames(FilamentEntity entity) =>
viewer
.getAnimationNames(entity)
.then((v) => v.map((s) => s.toJS).toList().toJS)
.toJS;
@JSExport()
JSPromise<JSNumber> getAnimationDuration(
FilamentEntity entity, int animationIndex) =>
viewer
.getAnimationDuration(entity, animationIndex)
.then((v) => v.toJS)
.toJS;
@JSExport()
JSPromise setMorphAnimationData(
FilamentEntity entity,
JSArray<JSArray<JSNumber>> animation,
JSArray<JSString> morphTargets,
JSArray<JSString>? targetMeshNames,
double frameLengthInMs) {
try {
var morphTargetsDart = morphTargets.toDart.map((m) => m.toDart).toList();
var animationDataDart = animation.toDart
.map((x) => x.toDart.map((y) => y.toDartDouble).toList())
.toList();
var morphAnimationData = MorphAnimationData(
animationDataDart, morphTargetsDart,
frameLengthInMs: frameLengthInMs);
var targetMeshNamesDart =
targetMeshNames?.toDart.map((x) => x.toDart).toList();
if (animationDataDart.first.length != morphTargetsDart.length) {
throw Exception(
"Length mismatch between morph targets and animation data");
}
var result = viewer
.setMorphAnimationData(
entity,
morphAnimationData,
targetMeshNames: targetMeshNamesDart,
)
.onError((err, st) {
print("ERROR SETTING MORPH ANIMATION DATA : $err\n$st");
return null;
});
return result.toJS;
} catch (err, st) {
print(err);
print(st);
rethrow;
}
}
@JSExport()
JSPromise resetBones(FilamentEntity entity) => viewer.resetBones(entity).toJS;
@JSExport()
JSPromise addBoneAnimation(
FilamentEntity entity,
JSArray<JSString> bones,
JSArray<JSArray<JSArray<JSNumber>>> frameData,
JSNumber frameLengthInMs,
JSNumber spaceEnum,
JSNumber skinIndex,
JSNumber fadeInInSecs,
JSNumber fadeOutInSecs,
JSNumber maxDelta) {
var frameDataDart = frameData.toDart
.map((frame) => frame.toDart
.map((v) {
var values = v.toDart;
var trans = v64.Vector3(values[0].toDartDouble,
values[1].toDartDouble, values[2].toDartDouble);
var rot = v64.Quaternion(
values[3].toDartDouble,
values[4].toDartDouble,
values[5].toDartDouble,
values[6].toDartDouble);
return (rotation: rot, translation: trans);
})
.cast<BoneAnimationFrame>()
.toList())
.toList();
var data = BoneAnimationData(
bones.toDart.map((n) => n.toDart).toList(), frameDataDart,
frameLengthInMs: frameLengthInMs.toDartDouble,
space: Space.values[spaceEnum.toDartInt]);
return viewer
.addBoneAnimation(entity, data,
skinIndex: skinIndex.toDartInt,
fadeInInSecs: fadeInInSecs.toDartDouble,
fadeOutInSecs: fadeOutInSecs.toDartDouble)
.toJS;
}
@JSExport()
JSPromise removeEntity(FilamentEntity entity) =>
viewer.removeEntity(entity).toJS;
@JSExport()
JSPromise clearEntities() {
return viewer.clearEntities().toJS;
}
@JSExport()
JSPromise zoomBegin() => viewer.zoomBegin().toJS;
@JSExport()
JSPromise zoomUpdate(double x, double y, double z) =>
viewer.zoomUpdate(x, y, z).toJS;
@JSExport()
JSPromise zoomEnd() => viewer.zoomEnd().toJS;
@JSExport()
JSPromise playAnimation(FilamentEntity entity, int index,
{bool loop = false,
bool reverse = false,
bool replaceActive = true,
double crossfade = 0.0}) =>
viewer
.playAnimation(
entity,
index,
loop: loop,
reverse: reverse,
replaceActive: replaceActive,
crossfade: crossfade,
)
.toJS;
@JSExport()
JSPromise playAnimationByName(FilamentEntity entity, String name,
{bool loop = false,
bool reverse = false,
bool replaceActive = true,
double crossfade = 0.0}) =>
viewer
.playAnimationByName(
entity,
name,
loop: loop,
reverse: reverse,
replaceActive: replaceActive,
crossfade: crossfade,
)
.toJS;
@JSExport()
JSPromise setAnimationFrame(
FilamentEntity entity, int index, int animationFrame) =>
viewer
.setAnimationFrame(
entity,
index,
animationFrame,
)
.toJS;
@JSExport()
JSPromise stopAnimation(FilamentEntity entity, int animationIndex) =>
viewer.stopAnimation(entity, animationIndex).toJS;
@JSExport()
JSPromise stopAnimationByName(FilamentEntity entity, String name) =>
viewer.stopAnimationByName(entity, name).toJS;
@JSExport()
JSPromise setCamera(FilamentEntity entity, String? name) =>
viewer.setCamera(entity, name).toJS;
@JSExport()
JSPromise setMainCamera() => viewer.setMainCamera().toJS;
@JSExport()
JSPromise<JSNumber> getMainCamera() {
return viewer.getMainCamera().then((camera) => camera.toJS).toJS;
}
@JSExport()
JSPromise setCameraFov(double degrees, double width, double height) =>
viewer.setCameraFov(degrees, width, height).toJS;
@JSExport()
JSPromise setToneMapping(int mapper) =>
viewer.setToneMapping(ToneMapper.values[mapper]).toJS;
@JSExport()
JSPromise setBloom(double bloom) => viewer.setBloom(bloom).toJS;
@JSExport()
JSPromise setCameraFocalLength(double focalLength) =>
viewer.setCameraFocalLength(focalLength).toJS;
@JSExport()
JSPromise setCameraCulling(double near, double far) =>
viewer.setCameraCulling(near, far).toJS;
@JSExport()
JSPromise<JSNumber> getCameraCullingNear() =>
viewer.getCameraCullingNear().then((v) => v.toJS).toJS;
@JSExport()
JSPromise<JSNumber> getCameraCullingFar() =>
viewer.getCameraCullingFar().then((v) => v.toJS).toJS;
@JSExport()
JSPromise setCameraFocusDistance(double focusDistance) =>
viewer.setCameraFocusDistance(focusDistance).toJS;
@JSExport()
JSPromise<JSArray<JSNumber>> getCameraPosition() {
throw UnimplementedError();
// return viewer.getCameraPosition().then((position) => position.toJS).toJS;
}
@JSExport()
JSPromise<JSArray<JSNumber>> getCameraModelMatrix() {
throw UnimplementedError();
// return viewer.getCameraModelMatrix().then((matrix) => matrix.toJSArray<JSNumber>()).toJS;
}
@JSExport()
JSPromise<JSArray<JSNumber>> getCameraViewMatrix() {
throw UnimplementedError();
// return viewer.getCameraViewMatrix().then((matrix) => matrix.toJSArray<JSNumber>()).toJS;
}
@JSExport()
JSPromise<JSArray<JSNumber>> getCameraProjectionMatrix() {
throw UnimplementedError();
// return viewer.getCameraProjectionMatrix().then((matrix) => matrix.toJSArray<JSNumber>()).toJS;
}
@JSExport()
JSPromise<JSArray<JSNumber>> getCameraCullingProjectionMatrix() {
throw UnimplementedError();
// return viewer.getCameraCullingProjectionMatrix().then((matrix) => matrix.toJSArray<JSNumber>()).toJS;
}
@JSExport()
JSPromise<JSNumber> getCameraFrustum() {
throw UnimplementedError();
// return viewer.getCameraFrustum().then((frustum) => frustum.toJS).toJS;
}
@JSExport()
JSPromise setCameraPosition(double x, double y, double z) =>
viewer.setCameraPosition(x, y, z).toJS;
@JSExport()
JSPromise<JSArray<JSNumber>> getCameraRotation() {
return viewer
.getCameraRotation()
.then((rotation) => rotation.storage.map((v) => v.toJS).toList().toJS)
.toJS;
}
@JSExport()
JSPromise moveCameraToAsset(FilamentEntity entity) =>
throw UnimplementedError();
// viewer.moveCameraToAsset(entity)).toJS;
@JSExport()
JSPromise setViewFrustumCulling(JSBoolean enabled) =>
throw UnimplementedError();
// viewer.setViewFrustumCulling(enabled).toJS;
@JSExport()
JSPromise setCameraExposure(
double aperture, double shutterSpeed, double sensitivity) =>
viewer.setCameraExposure(aperture, shutterSpeed, sensitivity).toJS;
@JSExport()
JSPromise setCameraRotation(JSArray<JSNumber> quaternion) {
var dartVals = quaternion.toDart;
return viewer
.setCameraRotation(v64.Quaternion(
dartVals[0].toDartDouble,
dartVals[1].toDartDouble,
dartVals[2].toDartDouble,
dartVals[3].toDartDouble))
.toJS;
}
@JSExport()
JSPromise setCameraModelMatrix(JSArray<JSNumber> matrix) {
throw UnimplementedError();
// viewer.setCameraModelMatrix(matrix).toJS;
}
@JSExport()
JSPromise setMaterialColor(FilamentEntity entity, String meshName,
int materialIndex, double r, double g, double b, double a) =>
throw UnimplementedError();
// viewer.setMaterialColor(
// entity),
// meshName,
// materialIndex,
// r,
// g,
// b,
// a,
// ).toJS;
@JSExport()
JSPromise transformToUnitCube(FilamentEntity entity) =>
viewer.transformToUnitCube(entity).toJS;
@JSExport()
JSPromise setPosition(FilamentEntity entity, double x, double y, double z) =>
viewer.setPosition(entity, x, y, z).toJS;
@JSExport()
JSPromise setScale(FilamentEntity entity, double scale) =>
viewer.setScale(entity, scale).toJS;
@JSExport()
JSPromise setRotation(
FilamentEntity entity, double rads, double x, double y, double z) =>
viewer.setRotation(entity, rads, x, y, z).toJS;
@JSExport()
JSPromise queuePositionUpdate(
FilamentEntity entity, double x, double y, double z, bool relative) =>
viewer
.queuePositionUpdate(
entity,
x,
y,
z,
relative: relative,
)
.toJS;
@JSExport()
JSPromise queueRotationUpdate(FilamentEntity entity, double rads, double x,
double y, double z, bool relative) =>
viewer
.queueRotationUpdate(
entity,
rads,
x,
y,
z,
relative: relative,
)
.toJS;
@JSExport()
JSPromise queueRotationUpdateQuat(
FilamentEntity entity, JSArray<JSNumber> quat, JSBoolean relative) =>
throw UnimplementedError();
// viewer.queueRotationUpdateQuat(
// entity,
// quat.toDartQuaternion(),
// relative: relative,
// ).toJS;
@JSExport()
JSPromise setPostProcessing(bool enabled) =>
viewer.setPostProcessing(enabled).toJS;
@JSExport()
JSPromise setAntiAliasing(bool msaa, bool fxaa, bool taa) =>
viewer.setAntiAliasing(msaa, fxaa, taa).toJS;
@JSExport()
JSPromise setRotationQuat(
FilamentEntity entity, JSArray<JSNumber> rotation) =>
throw UnimplementedError();
@JSExport()
JSPromise reveal(FilamentEntity entity, String? meshName) =>
viewer.reveal(entity, meshName).toJS;
@JSExport()
JSPromise hide(FilamentEntity entity, String? meshName) =>
viewer.hide(entity, meshName).toJS;
@JSExport()
void pick(int x, int y) => viewer.pick(x, y);
@JSExport()
String? getNameForEntity(FilamentEntity entity) =>
viewer.getNameForEntity(entity);
@JSExport()
JSPromise setCameraManipulatorOptions({
int mode = 0,
double orbitSpeedX = 0.01,
double orbitSpeedY = 0.01,
double zoomSpeed = 0.01,
}) =>
viewer
.setCameraManipulatorOptions(
mode: ManipulatorMode.values[mode],
orbitSpeedX: orbitSpeedX,
orbitSpeedY: orbitSpeedY,
zoomSpeed: zoomSpeed,
)
.toJS;
@JSExport()
JSPromise<JSArray<JSNumber>> getChildEntities(
FilamentEntity parent, bool renderableOnly) {
return viewer
.getChildEntities(
parent,
renderableOnly,
)
.then((entities) => entities.map((entity) => entity.toJS).toList().toJS)
.onError((e, st) async {
print("Error : $e\n$st");
return <JSNumber>[].toJS;
}).toJS;
}
@JSExport()
JSPromise<JSNumber> getChildEntity(FilamentEntity parent, String childName) {
return viewer
.getChildEntity(
parent,
childName,
)
.then((entity) => entity.toJS)
.onError((e, st) async {
print("Error getChildEntity : $e\n$st");
return 0.toJS;
}).toJS;
}
@JSExport()
JSPromise<JSArray<JSString>> getChildEntityNames(
FilamentEntity entity, bool renderableOnly) =>
viewer
.getChildEntityNames(
entity,
renderableOnly: renderableOnly,
)
.then((v) => v.map((s) => s.toJS).toList().toJS)
.toJS;
@JSExport()
JSPromise setRecording(bool recording) => viewer.setRecording(recording).toJS;
@JSExport()
JSPromise setRecordingOutputDirectory(String outputDirectory) =>
viewer.setRecordingOutputDirectory(outputDirectory).toJS;
@JSExport()
JSPromise addAnimationComponent(FilamentEntity entity) =>
viewer.addAnimationComponent(entity).toJS;
@JSExport()
JSPromise removeAnimationComponent(FilamentEntity entity) =>
viewer.removeAnimationComponent(entity).toJS;
@JSExport()
JSPromise getParent(FilamentEntity entity) =>
viewer.removeAnimationComponent(entity).toJS;
@JSExport()
JSPromise getBone(FilamentEntity entity, int boneIndex, int skinIndex) =>
viewer.getBone(entity, boneIndex, skinIndex: skinIndex).toJS;
@JSExport()
JSPromise<JSArray<JSNumber>> getLocalTransform(FilamentEntity entity) {
return viewer
.getLocalTransform(entity)
.then((t) => t.storage.map((v) => v.toJS).toList().toJS)
.toJS;
}
@JSExport()
JSPromise<JSArray<JSNumber>> getWorldTransform(FilamentEntity entity) {
return viewer
.getWorldTransform(entity)
.then((t) => t.storage.map((v) => v.toJS).toList().toJS)
.toJS;
}
@JSExport()
JSPromise setTransform(FilamentEntity entity, JSArray<JSNumber> transform) {
return viewer
.setTransform(
entity,
Matrix4.fromList(
transform.toDart.map((v) => v.toDartDouble).toList()))
.toJS;
}
@JSExport()
JSPromise updateBoneMatrices(FilamentEntity entity) {
return viewer.updateBoneMatrices(entity).toJS;
}
@JSExport()
JSPromise setBoneTransform(FilamentEntity entity, int boneIndex,
JSArray<JSNumber> transform, int skinIndex) {
return viewer
.setBoneTransform(
entity,
boneIndex,
Matrix4.fromList(
transform.toDart.map((v) => v.toDartDouble).toList()),
skinIndex: skinIndex)
.toJS;
}
@JSExport()
JSPromise addCollisionComponent(FilamentEntity entity,
{JSFunction? callback, bool affectsTransform = false}) {
throw UnimplementedError();
}
}

View File

@@ -0,0 +1,409 @@
@JS()
library thermion_flutter_js;
import 'dart:js_interop';
import 'package:thermion_dart/thermion_dart/entities/filament_entity.dart';
import 'package:thermion_dart/thermion_dart/abstract_filament_viewer.dart';
///
/// An extension type on [JSObject] that represents a
/// Javascript shim implementation of the [AbstractFilamentViewer] interface.
///
extension type AbstractFilamentViewerJSShim(JSObject _) implements JSObject {
@JS('initialized')
external JSPromise<JSBoolean> get initialized;
@JS('rendering')
external bool get rendering;
@JS('setRendering')
external JSPromise setRendering(bool render);
@JS('render')
external JSPromise render();
@JS('setFrameRate')
external JSPromise setFrameRate(int framerate);
@JS('dispose')
external JSPromise dispose();
@JS('setBackgroundImage')
external JSPromise setBackgroundImage(String path, bool fillHeight);
@JS('setBackgroundImagePosition')
external JSPromise setBackgroundImagePosition(double x, double y, bool clamp);
@JS('clearBackgroundImage')
external JSPromise clearBackgroundImage();
@JS('setBackgroundColor')
external JSPromise setBackgroundColor(
double r, double g, double b, double alpha);
@JS('loadSkybox')
external JSPromise loadSkybox(String skyboxPath);
@JS('removeSkybox')
external JSPromise removeSkybox();
@JS('loadIbl')
external JSPromise loadIbl(String lightingPath, double intensity);
@JS('rotateIbl')
external JSPromise rotateIbl(JSArray<JSNumber> rotationMatrix);
@JS('removeIbl')
external JSPromise removeIbl();
@JS('addLight')
external JSPromise<JSNumber> addLight(
int type,
double colour,
double intensity,
double posX,
double posY,
double posZ,
double dirX,
double dirY,
double dirZ,
double falloffRadius,
double spotLightConeInner,
double spotLightConeOuter,
double sunAngularRadius,
double sunHaloSize,
double sunHaloFallof,
bool castShadows);
@JS('removeLight')
external JSPromise removeLight(FilamentEntity light);
@JS('clearLights')
external JSPromise clearLights();
@JS('loadGlb')
external JSPromise<JSNumber> loadGlb(String path, int numInstances);
@JS('createInstance')
external JSPromise<JSNumber> createInstance(FilamentEntity entity);
@JS('getInstanceCount')
external JSPromise<JSNumber> getInstanceCount(FilamentEntity entity);
@JS('getInstances')
external JSPromise<JSArray<JSNumber>> getInstances(FilamentEntity entity);
@JS('loadGltf')
external JSPromise<JSNumber> loadGltf(
String path, String relativeResourcePath);
@JS('panStart')
external JSPromise panStart(double x, double y);
@JS('panUpdate')
external JSPromise panUpdate(double x, double y);
@JS('panEnd')
external JSPromise panEnd();
@JS('rotateStart')
external JSPromise rotateStart(double x, double y);
@JS('rotateUpdate')
external JSPromise rotateUpdate(double x, double y);
@JS('rotateEnd')
external JSPromise rotateEnd();
@JS('setMorphTargetWeights')
external JSPromise setMorphTargetWeights(
FilamentEntity entity, JSArray<JSNumber> weights);
@JS('getMorphTargetNames')
external JSPromise<JSArray<JSString>> getMorphTargetNames(
FilamentEntity entity, FilamentEntity childEntity);
@JS('getBoneNames')
external JSPromise<JSArray<JSString>> getBoneNames(
FilamentEntity entity, int skinIndex);
@JS('getAnimationNames')
external JSPromise<JSArray<JSString>> getAnimationNames(
FilamentEntity entity);
@JS('getAnimationDuration')
external JSPromise<JSNumber> getAnimationDuration(
FilamentEntity entity, int animationIndex);
@JS('setMorphAnimationData')
external JSPromise setMorphAnimationData(
FilamentEntity entity,
JSArray<JSArray<JSNumber>> animation,
JSArray<JSString> morphTargets,
JSArray<JSString>? targetMeshNames,
double frameLengthInMs);
@JS('resetBones')
external JSPromise resetBones(FilamentEntity entity);
@JS('addBoneAnimation')
external JSPromise addBoneAnimation(
FilamentEntity entity,
JSArray<JSString> bones,
JSArray<JSArray<JSArray<JSNumber>>> frameData,
JSNumber frameLengthInMs,
JSNumber spaceEnum,
JSNumber skinIndex,
JSNumber fadeInInSecs,
JSNumber fadeOutInSecs,
JSNumber maxDelta);
@JS('removeEntity')
external JSPromise removeEntity(FilamentEntity entity);
@JS('clearEntities')
external JSPromise clearEntities();
@JS('zoomBegin')
external JSPromise zoomBegin();
@JS('zoomUpdate')
external JSPromise zoomUpdate(double x, double y, double z);
@JS('zoomEnd')
external JSPromise zoomEnd();
@JS('playAnimation')
external JSPromise playAnimation(
FilamentEntity entity,
int index,
bool loop,
bool reverse,
bool replaceActive,
double crossfade,
);
@JS('playAnimationByName')
external JSPromise playAnimationByName(
FilamentEntity entity,
String name,
bool loop,
bool reverse,
bool replaceActive,
double crossfade,
);
@JS('setAnimationFrame')
external JSPromise setAnimationFrame(
FilamentEntity entity, int index, int animationFrame);
@JS('stopAnimation')
external JSPromise stopAnimation(FilamentEntity entity, int animationIndex);
@JS('stopAnimationByName')
external JSPromise stopAnimationByName(FilamentEntity entity, String name);
@JS('setCamera')
external JSPromise setCamera(FilamentEntity entity, String? name);
@JS('setMainCamera')
external JSPromise setMainCamera();
@JS('getMainCamera')
external JSPromise<JSNumber> getMainCamera();
@JS('setCameraFov')
external JSPromise setCameraFov(double degrees, double width, double height);
@JS('setToneMapping')
external JSPromise setToneMapping(int mapper);
@JS('setBloom')
external JSPromise setBloom(double bloom);
@JS('setCameraFocalLength')
external JSPromise setCameraFocalLength(double focalLength);
@JS('setCameraCulling')
external JSPromise setCameraCulling(double near, double far);
@JS('getCameraCullingNear')
external JSPromise<JSNumber> getCameraCullingNear();
@JS('getCameraCullingFar')
external JSPromise<JSNumber> getCameraCullingFar();
@JS('setCameraFocusDistance')
external JSPromise setCameraFocusDistance(double focusDistance);
@JS('getCameraPosition')
external JSPromise<JSArray<JSNumber>> getCameraPosition();
@JS('getCameraModelMatrix')
external JSPromise<JSArray<JSNumber>> getCameraModelMatrix();
@JS('getCameraViewMatrix')
external JSPromise<JSArray<JSNumber>> getCameraViewMatrix();
@JS('getCameraProjectionMatrix')
external JSPromise<JSArray<JSNumber>> getCameraProjectionMatrix();
@JS('getCameraCullingProjectionMatrix')
external JSPromise<JSArray<JSNumber>> getCameraCullingProjectionMatrix();
@JS('getCameraFrustum')
external JSPromise<JSObject> getCameraFrustum();
@JS('setCameraPosition')
external JSPromise setCameraPosition(double x, double y, double z);
@JS('getCameraRotation')
external JSPromise<JSArray<JSNumber>> getCameraRotation();
@JS('moveCameraToAsset')
external JSPromise moveCameraToAsset(FilamentEntity entity);
@JS('setViewFrustumCulling')
external JSPromise setViewFrustumCulling(JSBoolean enabled);
@JS('setCameraExposure')
external JSPromise setCameraExposure(
double aperture, double shutterSpeed, double sensitivity);
@JS('setCameraRotation')
external JSPromise setCameraRotation(JSArray<JSNumber> quaternion);
@JS('setCameraModelMatrix')
external JSPromise setCameraModelMatrix(JSArray<JSNumber> matrix);
@JS('setMaterialColor')
external JSPromise setMaterialColor(FilamentEntity entity, String meshName,
int materialIndex, double r, double g, double b, double a);
@JS('transformToUnitCube')
external JSPromise transformToUnitCube(FilamentEntity entity);
@JS('setPosition')
external JSPromise setPosition(
FilamentEntity entity, double x, double y, double z);
@JS('setScale')
external JSPromise setScale(FilamentEntity entity, double scale);
@JS('setRotation')
external JSPromise setRotation(
FilamentEntity entity, double rads, double x, double y, double z);
@JS('queuePositionUpdate')
external JSPromise queuePositionUpdate(
FilamentEntity entity, double x, double y, double z, bool relative);
@JS('queueRotationUpdate')
external JSPromise queueRotationUpdate(FilamentEntity entity, double rads,
double x, double y, double z, bool relative);
@JS('queueRotationUpdateQuat')
external JSPromise queueRotationUpdateQuat(
FilamentEntity entity, JSArray<JSNumber> quat, bool relative);
@JS('setPostProcessing')
external JSPromise setPostProcessing(bool enabled);
@JS('setAntiAliasing')
external JSPromise setAntiAliasing(bool msaa, bool fxaa, bool taa);
@JS('setRotationQuat')
external JSPromise setRotationQuat(
FilamentEntity entity, JSArray<JSNumber> rotation);
@JS('reveal')
external JSPromise reveal(FilamentEntity entity, String? meshName);
@JS('hide')
external JSPromise hide(FilamentEntity entity, String? meshName);
@JS('pick')
external void pick(int x, int y);
@JS('getNameForEntity')
external String? getNameForEntity(FilamentEntity entity);
@JS('setCameraManipulatorOptions')
external JSPromise setCameraManipulatorOptions(
int mode,
double orbitSpeedX,
double orbitSpeedY,
double zoomSpeed,
);
@JS('getChildEntities')
external JSPromise<JSArray<JSNumber>> getChildEntities(
FilamentEntity parent, bool renderableOnly);
@JS('getChildEntity')
external JSPromise<JSNumber> getChildEntity(
FilamentEntity parent, String childName);
@JS('getChildEntityNames')
external JSPromise<JSArray<JSString>> getChildEntityNames(
FilamentEntity entity, bool renderableOnly);
@JS('setRecording')
external JSPromise setRecording(JSBoolean recording);
@JS('setRecordingOutputDirectory')
external JSPromise setRecordingOutputDirectory(String outputDirectory);
@JS('addAnimationComponent')
external JSPromise addAnimationComponent(FilamentEntity entity);
@JS('removeAnimationComponent')
external JSPromise removeAnimationComponent(FilamentEntity entity);
@JS('addCollisionComponent')
external JSPromise addCollisionComponent(FilamentEntity entity);
@JS('removeCollisionComponent')
external JSPromise removeCollisionComponent(FilamentEntity entity);
@JS('createGeometry')
external JSPromise<JSNumber> createGeometry(JSArray<JSNumber> vertices,
JSArray<JSNumber> indices, String? materialPath, int primitiveType);
@JS('setParent')
external JSPromise setParent(FilamentEntity child, FilamentEntity parent);
@JS('getParent')
external JSPromise<JSNumber> getParent(FilamentEntity child);
@JS('getParent')
external JSPromise<JSNumber> getBone(
FilamentEntity child, int boneIndex, int skinIndex);
@JS('testCollisions')
external JSPromise testCollisions(FilamentEntity entity);
@JS('setPriority')
external JSPromise setPriority(FilamentEntity entityId, int priority);
@JS('getLocalTransform')
external JSPromise<JSArray<JSNumber>> getLocalTransform(
FilamentEntity entity);
@JS('getWorldTransform')
external JSPromise<JSArray<JSNumber>> getWorldTransform(
FilamentEntity entity);
@JS('updateBoneMatrices')
external JSPromise updateBoneMatrices(FilamentEntity entity);
@JS('setTransform')
external JSPromise setTransform(
FilamentEntity entity, JSArray<JSNumber> transform);
@JS('setBoneTransform')
external JSPromise setBoneTransform(
FilamentEntity entity, int boneIndex, JSArray<JSNumber> transform, int skinIndex);
}

View File

@@ -0,0 +1,403 @@
@JS()
library thermion_flutter_js;
import 'dart:js_interop';
import 'package:thermion_dart/thermion_dart/entities/filament_entity.dart';
import 'package:thermion_dart/thermion_dart/abstract_filament_viewer.dart';
///
/// An extension type on [JSObject] that represents a
/// Javascript shim implementation for the [AbstractFilamentViewer] interface.
///
extension type ThermionDartAPIJSShim(JSObject _) implements JSObject {
@JS('wasm_test')
external JSPromise wasm_test(String str);
@JS('set_rendering')
external JSPromise set_rendering(bool render);
@JS('render')
external JSPromise render();
@JS('setFrameRate')
external JSPromise setFrameRate(int framerate);
@JS('setBackgroundImage')
external JSPromise setBackgroundImage(String path, bool fillHeight);
@JS('setBackgroundImagePosition')
external JSPromise setBackgroundImagePosition(double x, double y, bool clamp);
@JS('clearBackgroundImage')
external JSPromise clearBackgroundImage();
@JS('setBackgroundColor')
external JSPromise setBackgroundColor(
double r, double g, double b, double alpha);
@JS('loadSkybox')
external JSPromise loadSkybox(String skyboxPath);
@JS('removeSkybox')
external JSPromise removeSkybox();
@JS('loadIbl')
external JSPromise loadIbl(String lightingPath, double intensity);
@JS('rotateIbl')
external JSPromise rotateIbl(JSArray<JSNumber> rotationMatrix);
@JS('removeIbl')
external JSPromise removeIbl();
@JS('addLight')
external JSPromise<JSNumber> addLight(
int type,
double colour,
double intensity,
double posX,
double posY,
double posZ,
double dirX,
double dirY,
double dirZ,
double falloffRadius,
double spotLightConeInner,
double spotLightConeOuter,
double sunAngularRadius,
double sunHaloSize,
double sunHaloFallof,
bool castShadows);
@JS('removeLight')
external JSPromise removeLight(FilamentEntity light);
@JS('clearLights')
external JSPromise clearLights();
@JS('loadGlb')
external JSPromise<JSNumber> loadGlb(String path, int numInstances);
@JS('createInstance')
external JSPromise<JSNumber> createInstance(FilamentEntity entity);
@JS('getInstanceCount')
external JSPromise<JSNumber> getInstanceCount(FilamentEntity entity);
@JS('getInstances')
external JSPromise<JSArray<JSNumber>> getInstances(FilamentEntity entity);
@JS('loadGltf')
external JSPromise<JSNumber> loadGltf(
String path, String relativeResourcePath);
@JS('panStart')
external JSPromise panStart(double x, double y);
@JS('panUpdate')
external JSPromise panUpdate(double x, double y);
@JS('panEnd')
external JSPromise panEnd();
@JS('rotateStart')
external JSPromise rotateStart(double x, double y);
@JS('rotateUpdate')
external JSPromise rotateUpdate(double x, double y);
@JS('rotateEnd')
external JSPromise rotateEnd();
@JS('setMorphTargetWeights')
external JSPromise setMorphTargetWeights(
FilamentEntity entity, JSArray<JSNumber> weights);
@JS('getMorphTargetNames')
external JSPromise<JSArray<JSString>> getMorphTargetNames(
FilamentEntity entity, FilamentEntity childEntity);
@JS('getBoneNames')
external JSPromise<JSArray<JSString>> getBoneNames(
FilamentEntity entity, int skinIndex);
@JS('getAnimationNames')
external JSPromise<JSArray<JSString>> getAnimationNames(
FilamentEntity entity);
@JS('getAnimationDuration')
external JSPromise<JSNumber> getAnimationDuration(
FilamentEntity entity, int animationIndex);
@JS('setMorphAnimationData')
external JSPromise setMorphAnimationData(
FilamentEntity entity,
JSArray<JSArray<JSNumber>> animation,
JSArray<JSString> morphTargets,
JSArray<JSString>? targetMeshNames,
double frameLengthInMs);
@JS('resetBones')
external JSPromise resetBones(FilamentEntity entity);
@JS('addBoneAnimation')
external JSPromise addBoneAnimation(
FilamentEntity entity,
JSArray<JSString> bones,
JSArray<JSArray<JSArray<JSNumber>>> frameData,
JSNumber frameLengthInMs,
JSNumber spaceEnum,
JSNumber skinIndex,
JSNumber fadeInInSecs,
JSNumber fadeOutInSecs,
JSNumber maxDelta);
@JS('removeEntity')
external JSPromise removeEntity(FilamentEntity entity);
@JS('clearEntities')
external JSPromise clearEntities();
@JS('zoomBegin')
external JSPromise zoomBegin();
@JS('zoomUpdate')
external JSPromise zoomUpdate(double x, double y, double z);
@JS('zoomEnd')
external JSPromise zoomEnd();
@JS('playAnimation')
external JSPromise playAnimation(
FilamentEntity entity,
int index,
bool loop,
bool reverse,
bool replaceActive,
double crossfade,
);
@JS('playAnimationByName')
external JSPromise playAnimationByName(
FilamentEntity entity,
String name,
bool loop,
bool reverse,
bool replaceActive,
double crossfade,
);
@JS('setAnimationFrame')
external JSPromise setAnimationFrame(
FilamentEntity entity, int index, int animationFrame);
@JS('stopAnimation')
external JSPromise stopAnimation(FilamentEntity entity, int animationIndex);
@JS('stopAnimationByName')
external JSPromise stopAnimationByName(FilamentEntity entity, String name);
@JS('setCamera')
external JSPromise setCamera(FilamentEntity entity, String? name);
@JS('setMainCamera')
external JSPromise setMainCamera();
@JS('getMainCamera')
external JSPromise<JSNumber> getMainCamera();
@JS('setCameraFov')
external JSPromise setCameraFov(double degrees, double width, double height);
@JS('setToneMapping')
external JSPromise setToneMapping(int mapper);
@JS('setBloom')
external JSPromise setBloom(double bloom);
@JS('setCameraFocalLength')
external JSPromise setCameraFocalLength(double focalLength);
@JS('setCameraCulling')
external JSPromise setCameraCulling(double near, double far);
@JS('getCameraCullingNear')
external JSPromise<JSNumber> getCameraCullingNear();
@JS('getCameraCullingFar')
external JSPromise<JSNumber> getCameraCullingFar();
@JS('setCameraFocusDistance')
external JSPromise setCameraFocusDistance(double focusDistance);
@JS('getCameraPosition')
external JSPromise<JSArray<JSNumber>> getCameraPosition();
@JS('getCameraModelMatrix')
external JSPromise<JSArray<JSNumber>> getCameraModelMatrix();
@JS('getCameraViewMatrix')
external JSPromise<JSArray<JSNumber>> getCameraViewMatrix();
@JS('getCameraProjectionMatrix')
external JSPromise<JSArray<JSNumber>> getCameraProjectionMatrix();
@JS('getCameraCullingProjectionMatrix')
external JSPromise<JSArray<JSNumber>> getCameraCullingProjectionMatrix();
@JS('getCameraFrustum')
external JSPromise<JSObject> getCameraFrustum();
@JS('setCameraPosition')
external JSPromise setCameraPosition(double x, double y, double z);
@JS('getCameraRotation')
external JSPromise<JSArray<JSNumber>> getCameraRotation();
@JS('moveCameraToAsset')
external JSPromise moveCameraToAsset(FilamentEntity entity);
@JS('setViewFrustumCulling')
external JSPromise setViewFrustumCulling(JSBoolean enabled);
@JS('setCameraExposure')
external JSPromise setCameraExposure(
double aperture, double shutterSpeed, double sensitivity);
@JS('setCameraRotation')
external JSPromise setCameraRotation(JSArray<JSNumber> quaternion);
@JS('setCameraModelMatrix')
external JSPromise setCameraModelMatrix(JSArray<JSNumber> matrix);
@JS('setMaterialColor')
external JSPromise setMaterialColor(FilamentEntity entity, String meshName,
int materialIndex, double r, double g, double b, double a);
@JS('transformToUnitCube')
external JSPromise transformToUnitCube(FilamentEntity entity);
@JS('setPosition')
external JSPromise setPosition(
FilamentEntity entity, double x, double y, double z);
@JS('setScale')
external JSPromise setScale(FilamentEntity entity, double scale);
@JS('setRotation')
external JSPromise setRotation(
FilamentEntity entity, double rads, double x, double y, double z);
@JS('queuePositionUpdate')
external JSPromise queuePositionUpdate(
FilamentEntity entity, double x, double y, double z, bool relative);
@JS('queueRotationUpdate')
external JSPromise queueRotationUpdate(FilamentEntity entity, double rads,
double x, double y, double z, bool relative);
@JS('queueRotationUpdateQuat')
external JSPromise queueRotationUpdateQuat(
FilamentEntity entity, JSArray<JSNumber> quat, bool relative);
@JS('setPostProcessing')
external JSPromise setPostProcessing(bool enabled);
@JS('setAntiAliasing')
external JSPromise setAntiAliasing(bool msaa, bool fxaa, bool taa);
@JS('setRotationQuat')
external JSPromise setRotationQuat(
FilamentEntity entity, JSArray<JSNumber> rotation);
@JS('reveal')
external JSPromise reveal(FilamentEntity entity, String? meshName);
@JS('hide')
external JSPromise hide(FilamentEntity entity, String? meshName);
@JS('pick')
external void pick(int x, int y);
@JS('getNameForEntity')
external String? getNameForEntity(FilamentEntity entity);
@JS('setCameraManipulatorOptions')
external JSPromise setCameraManipulatorOptions(
int mode,
double orbitSpeedX,
double orbitSpeedY,
double zoomSpeed,
);
@JS('getChildEntities')
external JSPromise<JSArray<JSNumber>> getChildEntities(
FilamentEntity parent, bool renderableOnly);
@JS('getChildEntity')
external JSPromise<JSNumber> getChildEntity(
FilamentEntity parent, String childName);
@JS('getChildEntityNames')
external JSPromise<JSArray<JSString>> getChildEntityNames(
FilamentEntity entity, bool renderableOnly);
@JS('setRecording')
external JSPromise setRecording(JSBoolean recording);
@JS('setRecordingOutputDirectory')
external JSPromise setRecordingOutputDirectory(String outputDirectory);
@JS('addAnimationComponent')
external JSPromise addAnimationComponent(FilamentEntity entity);
@JS('removeAnimationComponent')
external JSPromise removeAnimationComponent(FilamentEntity entity);
@JS('addCollisionComponent')
external JSPromise addCollisionComponent(FilamentEntity entity);
@JS('removeCollisionComponent')
external JSPromise removeCollisionComponent(FilamentEntity entity);
@JS('createGeometry')
external JSPromise<JSNumber> createGeometry(JSArray<JSNumber> vertices,
JSArray<JSNumber> indices, String? materialPath, int primitiveType);
@JS('setParent')
external JSPromise setParent(FilamentEntity child, FilamentEntity parent);
@JS('getParent')
external JSPromise<JSNumber> getParent(FilamentEntity child);
@JS('getParent')
external JSPromise<JSNumber> getBone(
FilamentEntity child, int boneIndex, int skinIndex);
@JS('testCollisions')
external JSPromise testCollisions(FilamentEntity entity);
@JS('setPriority')
external JSPromise setPriority(FilamentEntity entityId, int priority);
@JS('getLocalTransform')
external JSPromise<JSArray<JSNumber>> getLocalTransform(
FilamentEntity entity);
@JS('getWorldTransform')
external JSPromise<JSArray<JSNumber>> getWorldTransform(
FilamentEntity entity);
@JS('updateBoneMatrices')
external JSPromise updateBoneMatrices(FilamentEntity entity);
@JS('setTransform')
external JSPromise setTransform(
FilamentEntity entity, JSArray<JSNumber> transform);
@JS('setBoneTransform')
external JSPromise setBoneTransform(
FilamentEntity entity, int boneIndex, JSArray<JSNumber> transform, int skinIndex);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
import 'package:thermion_dart/thermion_dart/entities/filament_entity.dart';

View File

@@ -0,0 +1,181 @@
import 'dart:async';
import 'dart:math';
import 'package:thermion_dart/thermion_dart/entities/filament_entity.dart';
import 'package:thermion_dart/thermion_dart/abstract_filament_viewer.dart';
import 'package:vector_math/vector_math_64.dart' as v;
class EntityTransformController {
final AbstractFilamentViewer controller;
final FilamentEntity _entity;
late Timer _ticker;
double translationSpeed;
double rotationRadsPerSecond;
bool _forward = false;
bool _strafeLeft = false;
bool _strafeRight = false;
bool _back = false;
bool _rotateLeft = false;
bool _rotateRight = false;
double _rotY = 0;
int? forwardAnimationIndex;
int? backwardAnimationIndex;
int? strafeLeftAnimationIndex;
int? strafeRightAnimationIndex;
EntityTransformController(this.controller, this._entity,
{this.translationSpeed = 1,
this.rotationRadsPerSecond = pi / 2,
this.forwardAnimationIndex,
this.backwardAnimationIndex,
this.strafeLeftAnimationIndex,
this.strafeRightAnimationIndex}) {
var translationSpeedPerTick = translationSpeed / (1000 / 16.667);
var rotationRadsPerTick = rotationRadsPerSecond / (1000 / 16.667);
_ticker = Timer.periodic(const Duration(milliseconds: 16), (timer) {
_update(translationSpeedPerTick, rotationRadsPerTick);
});
}
bool _enabled = true;
void enable() {
_enabled = true;
}
void disable() {
_enabled = false;
}
void _update(
double translationSpeedPerTick, double rotationRadsPerTick) async {
if (!_enabled) {
return;
}
var _position = v.Vector3.zero();
bool updateTranslation = false;
if (_forward) {
_position.add(v.Vector3(0, 0, -translationSpeedPerTick));
updateTranslation = true;
}
if (_back) {
_position.add(v.Vector3(0, 0, translationSpeedPerTick));
updateTranslation = true;
}
if (_strafeLeft) {
_position.add(v.Vector3(-translationSpeedPerTick, 0, 0));
updateTranslation = true;
}
if (_strafeRight) {
_position.add(v.Vector3(translationSpeedPerTick, 0, 0));
updateTranslation = true;
}
// TODO - use pitch/yaw/roll
bool updateRotation = false;
var _rotation = v.Quaternion.identity();
double rads = 0.0;
if (_rotY != 0) {
rads = _rotY * pi / 1000;
var rotY = v.Quaternion.axisAngle(v.Vector3(0, 1, 0), rads).normalized();
_rotation = rotY;
updateRotation = true;
_rotY = 0;
}
if (updateTranslation) {
await controller.queuePositionUpdate(
_entity, _position.x, _position.y, _position.z,
relative: true);
}
if (updateRotation) {
await controller.queueRotationUpdateQuat(_entity, _rotation,
relative: true);
}
}
void look(double deltaX) async {
_rotY -= deltaX;
}
void dispose() {
_ticker.cancel();
}
bool _playingForwardAnimation = false;
bool _playingBackwardAnimation = false;
void forwardPressed() async {
_forward = true;
if (forwardAnimationIndex != null && !_playingForwardAnimation) {
await controller.playAnimation(_entity, forwardAnimationIndex!,
loop: true, replaceActive: false);
_playingForwardAnimation = true;
}
}
void forwardReleased() async {
_forward = false;
await Future.delayed(Duration(milliseconds: 50));
if (!_forward) {
_playingForwardAnimation = false;
if (forwardAnimationIndex != null) {
await controller.stopAnimation(_entity, forwardAnimationIndex!);
}
}
}
void backPressed() async {
_back = true;
if (forwardAnimationIndex != null) {
if (!_playingBackwardAnimation) {
await controller.playAnimation(_entity, forwardAnimationIndex!,
loop: true, replaceActive: false, reverse: true);
_playingBackwardAnimation = true;
}
}
}
void backReleased() async {
_back = false;
if (forwardAnimationIndex != null) {
await controller.stopAnimation(_entity, forwardAnimationIndex!);
}
_playingBackwardAnimation = false;
}
void strafeLeftPressed() {
_strafeLeft = true;
}
void strafeLeftReleased() async {
_strafeLeft = false;
}
void strafeRightPressed() {
_strafeRight = true;
}
void strafeRightReleased() async {
_strafeRight = false;
}
void Function()? _mouse1DownCallback;
void onMouse1Down(void Function() callback) {
_mouse1DownCallback = callback;
}
void mouse1Down() async {
_mouse1DownCallback?.call();
}
void mouse1Up() async {}
void mouse2Up() async {}
void mouse2Down() async {}
}

View File

@@ -0,0 +1,2 @@
// a handle that can be safely passed back to the rendering layer to manipulate an Entity
typedef FilamentEntity = int;

View File

@@ -0,0 +1,79 @@
import 'package:thermion_dart/thermion_dart/entities/filament_entity.dart';
import 'package:vector_math/vector_math_64.dart';
import '../abstract_filament_viewer.dart';
class Gizmo extends AbstractGizmo {
final FilamentEntity x;
Vector3 _x = Vector3(0.1, 0, 0);
final FilamentEntity y;
Vector3 _y = Vector3(0.0, 0.1, 0);
final FilamentEntity z;
Vector3 _z = Vector3(0.0, 0.0, 0.1);
final AbstractFilamentViewer controller;
FilamentEntity? _activeAxis;
FilamentEntity? _activeEntity;
bool get isActive => _activeAxis != null;
final Set<FilamentEntity> ignore;
Gizmo(this.x, this.y, this.z, this.controller,
{this.ignore = const <FilamentEntity>{}}) {
controller.pickResult.listen(_onPickResult);
}
Future _reveal() async {
await controller.reveal(x, null);
await controller.reveal(y, null);
await controller.reveal(z, null);
}
void translate(double transX, double transY) async {
late Vector3 vec;
if (_activeAxis == x) {
vec = _x;
} else if (_activeAxis == y) {
vec = _y;
} else if (_activeAxis == z) {
vec = _z;
}
await controller.queuePositionUpdate(
_activeEntity!, transX * vec.x, -transY * vec.y, -transX * vec.z,
relative: true);
}
void reset() {
_activeAxis = null;
}
void _onPickResult(FilamentPickResult result) async {
if (ignore.contains(result)) {
detach();
return;
}
if (result.entity == x || result.entity == y || result.entity == z) {
_activeAxis = result.entity;
} else {
attach(result.entity);
}
}
void attach(FilamentEntity entity) async {
_activeAxis = null;
_activeEntity = entity;
await _reveal();
await controller.setParent(x, entity);
await controller.setParent(y, entity);
await controller.setParent(z, entity);
}
void detach() async {
await controller.setParent(x, 0);
await controller.setParent(y, 0);
await controller.setParent(z, 0);
await controller.hide(x, null);
await controller.hide(y, null);
await controller.hide(z, null);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,120 @@
import 'dart:async';
import 'package:thermion_dart/thermion_dart/entities/filament_entity.dart';
import 'abstract_filament_viewer.dart';
///
/// For now, this class just holds the entities that have been loaded (though not necessarily visible in the Filament Scene).
///
class SceneImpl extends Scene {
AbstractFilamentViewer controller;
SceneImpl(this.controller);
@override
FilamentEntity? selected;
final _onUpdatedController = StreamController<bool>.broadcast();
@override
Stream<bool> get onUpdated => _onUpdatedController.stream;
final _onLoadController = StreamController<FilamentEntity>.broadcast();
@override
Stream<FilamentEntity> get onLoad => _onLoadController.stream;
final _onUnloadController = StreamController<FilamentEntity>.broadcast();
@override
Stream<FilamentEntity> get onUnload => _onUnloadController.stream;
final _lights = <FilamentEntity>{};
final _entities = <FilamentEntity>{};
void registerLight(FilamentEntity entity) {
_lights.add(entity);
_onLoadController.sink.add(entity);
_onUpdatedController.add(true);
}
void unregisterLight(FilamentEntity entity) async {
var children = await controller.getChildEntities(entity, true);
if (selected == entity || children.contains(selected)) {
selected = null;
controller.gizmo?.detach();
}
_lights.remove(entity);
_onUnloadController.add(entity);
_onUpdatedController.add(true);
}
void unregisterEntity(FilamentEntity entity) async {
var children = await controller.getChildEntities(entity, true);
if (selected == entity || children.contains(selected)) {
selected = null;
controller.gizmo?.detach();
}
_entities.remove(entity);
_onUnloadController.add(entity);
_onUpdatedController.add(true);
}
void registerEntity(FilamentEntity entity) {
_entities.add(entity);
_onLoadController.sink.add(entity);
_onUpdatedController.add(true);
}
void clearLights() {
for (final light in _lights) {
if (selected == light) {
selected = null;
controller.gizmo?.detach();
}
_onUnloadController.add(light);
}
_lights.clear();
_onUpdatedController.add(true);
}
void clearEntities() {
for (final entity in _entities) {
if (selected == entity) {
selected = null;
controller.gizmo?.detach();
}
_onUnloadController.add(entity);
}
_entities.clear();
_onUpdatedController.add(true);
}
///
/// Lists all entities currently loaded (not necessarily active in the scene).
///
Iterable<FilamentEntity> listLights() {
return _lights;
}
@override
Iterable<FilamentEntity> listEntities() {
return _entities;
}
void registerSelected(FilamentEntity entity) {
selected = entity;
_onUpdatedController.add(true);
}
void unregisterSelected() {
selected = null;
_onUpdatedController.add(true);
}
@override
void select(FilamentEntity entity) {
selected = entity;
controller.gizmo?.attach(entity);
_onUpdatedController.add(true);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,15 @@
import 'package:vector_math/vector_math_64.dart' as v;
class CameraOrientation {
v.Vector3 position = v.Vector3(0, 0, 0);
var rotationX = 0.0;
var rotationY = 0.0;
var rotationZ = 0.0;
v.Quaternion compose() {
return v.Quaternion.axisAngle(v.Vector3(0, 0, 1), rotationZ) *
v.Quaternion.axisAngle(v.Vector3(0, 1, 0), rotationY) *
v.Quaternion.axisAngle(v.Vector3(1, 0, 0), rotationX);
}
}

View File

@@ -0,0 +1,28 @@
import 'dart:ffi';
import 'dart:io';
import '../compatibility/compatibility.dart';
import 'package:ffi/ffi.dart' hide Utf8Pointer;
class DartResourceLoader {
static final _assets = <int, Pointer>{};
static void loadResource(Pointer<Char> uri, Pointer<ResourceBuffer> out) {
try {
var data = File(uri.cast<Utf8>().toDartString().replaceAll("file://", ""))
.readAsBytesSync();
var ptr = calloc<Uint8>(data.lengthInBytes);
ptr.asTypedList(data.lengthInBytes).setRange(0, data.lengthInBytes, data);
out.ref.data = ptr.cast<Void>();
out.ref.size = data.lengthInBytes;
out.ref.id = _assets.length;
_assets[out.ref.id] = ptr;
} catch (err) {
print(err);
out.ref.size = -1;
}
}
static void freeResource(ResourceBuffer rb) {
calloc.free(_assets[rb.id]!);
}
}

View File

@@ -0,0 +1,29 @@
import 'package:vector_math/vector_math_64.dart' as v;
class LightOptions {
String? iblPath;
double iblIntensity;
int directionalType;
double directionalColor;
double directionalIntensity;
bool directionalCastShadows;
late v.Vector3 directionalPosition;
late v.Vector3 directionalDirection;
LightOptions(
{required this.iblPath,
required this.iblIntensity,
required this.directionalType,
required this.directionalColor,
required this.directionalIntensity,
required this.directionalCastShadows,
v.Vector3? directionalDirection,
v.Vector3? directionalPosition}) {
this.directionalDirection = directionalDirection == null
? v.Vector3(0, -1, 0)
: directionalDirection;
this.directionalPosition = directionalPosition == null
? v.Vector3(0, 100, 0)
: directionalPosition;
}
}

View File

@@ -0,0 +1,10 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
final allocator = calloc;
void using(Pointer ptr, Future Function(Pointer ptr) function) async {
await function.call(ptr);
allocator.free(ptr);
}