From 49f33cd7bf441a8d3fdf6721bca51a40a195f5b7 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Sat, 15 Jun 2024 13:49:24 +0800 Subject: [PATCH] update web/js interop types --- ...nt_viewer.dart => filament_viewer_js.dart} | 240 +-- ...rt => filament_viewer_js_dart_bridge.dart} | 50 +- .../web/interop/filament_viewer_wasm.dart | 1451 +++++++++++++++++ .../abstract_filament_viewer_js_shim.dart} | 10 +- .../shims/dart_filament_api_js_shim.dart | 403 +++++ 5 files changed, 2009 insertions(+), 145 deletions(-) rename dart_filament/lib/dart_filament/compatibility/web/interop/{js_interop_filament_viewer.dart => filament_viewer_js.dart} (73%) rename dart_filament/lib/dart_filament/compatibility/web/interop/{dart_filament_js_export_type.dart => filament_viewer_js_dart_bridge.dart} (96%) create mode 100644 dart_filament/lib/dart_filament/compatibility/web/interop/filament_viewer_wasm.dart rename dart_filament/lib/dart_filament/compatibility/web/interop/{dart_filament_js_extension_type.dart => shims/abstract_filament_viewer_js_shim.dart} (97%) create mode 100644 dart_filament/lib/dart_filament/compatibility/web/interop/shims/dart_filament_api_js_shim.dart diff --git a/dart_filament/lib/dart_filament/compatibility/web/interop/js_interop_filament_viewer.dart b/dart_filament/lib/dart_filament/compatibility/web/interop/filament_viewer_js.dart similarity index 73% rename from dart_filament/lib/dart_filament/compatibility/web/interop/js_interop_filament_viewer.dart rename to dart_filament/lib/dart_filament/compatibility/web/interop/filament_viewer_js.dart index 458dc8a3..98d87bfb 100644 --- a/dart_filament/lib/dart_filament/compatibility/web/interop/js_interop_filament_viewer.dart +++ b/dart_filament/lib/dart_filament/compatibility/web/interop/filament_viewer_js.dart @@ -7,19 +7,25 @@ import 'package:dart_filament/dart_filament/abstract_filament_viewer.dart'; import 'package:dart_filament/dart_filament/entities/filament_entity.dart'; import 'package:dart_filament/dart_filament/scene.dart'; import 'package:vector_math/vector_math_64.dart'; -import 'dart_filament_js_extension_type.dart'; +import 'shims/abstract_filament_viewer_js_shim.dart'; -class JsInteropFilamentViewer implements AbstractFilamentViewer { - late final DartFilamentJSShim _jsObject; +/// +/// An [AbstractFilamentViewer] implementation that forwards calls to +/// a corresponding Javascript shim implementation (see [AbstractFilamentViewerJSShim]). +/// +class FilamentViewerJS implements AbstractFilamentViewer { + late final AbstractFilamentViewerJSShim _shim; - JsInteropFilamentViewer(String globalPropertyName) { - this._jsObject = globalContext.getProperty(globalPropertyName.toJS) - as DartFilamentJSShim; + FilamentViewerJS.fromGlobalProperty(String globalPropertyName) { + this._shim = globalContext.getProperty(globalPropertyName.toJS) + as AbstractFilamentViewerJSShim; } + FilamentViewerJS(this._shim); + @override Future get initialized async { - var inited = _jsObject.initialized; + var inited = _shim.initialized; final JSBoolean result = await inited.toDart; return result.toDart; } @@ -30,76 +36,76 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { } @override - bool get rendering => _jsObject.rendering; + bool get rendering => _shim.rendering; @override Future setRendering(bool render) async { - await _jsObject.setRendering(render).toDart; + await _shim.setRendering(render).toDart; } @override Future render() async { - await _jsObject.render().toDart; + await _shim.render().toDart; } @override Future setFrameRate(int framerate) async { - await _jsObject.setFrameRate(framerate).toDart; + await _shim.setFrameRate(framerate).toDart; } @override Future dispose() async { - await _jsObject.dispose().toDart; + await _shim.dispose().toDart; } @override Future setBackgroundImage(String path, {bool fillHeight = false}) async { - await _jsObject.setBackgroundImage(path, fillHeight).toDart; + await _shim.setBackgroundImage(path, fillHeight).toDart; } @override Future setBackgroundImagePosition(double x, double y, {bool clamp = false}) async { - await _jsObject.setBackgroundImagePosition(x, y, clamp).toDart; + await _shim.setBackgroundImagePosition(x, y, clamp).toDart; } @override Future clearBackgroundImage() async { - await _jsObject.clearBackgroundImage().toDart; + await _shim.clearBackgroundImage().toDart; } @override Future setBackgroundColor( double r, double g, double b, double alpha) async { - await _jsObject.setBackgroundColor(r, g, b, alpha).toDart; + await _shim.setBackgroundColor(r, g, b, alpha).toDart; } @override Future loadSkybox(String skyboxPath) async { - await _jsObject.loadSkybox(skyboxPath).toDart; + await _shim.loadSkybox(skyboxPath).toDart; } @override Future removeSkybox() async { - await _jsObject.removeSkybox().toDart; + await _shim.removeSkybox().toDart; } @override Future loadIbl(String lightingPath, {double intensity = 30000}) async { - await _jsObject.loadIbl(lightingPath, intensity).toDart; + await _shim.loadIbl(lightingPath, intensity).toDart; } @override Future rotateIbl(Matrix3 rotation) async { - await _jsObject + await _shim .rotateIbl(rotation.storage.map((v) => v.toJS).toList().toJS) .toDart; } @override Future removeIbl() async { - await _jsObject.removeIbl().toDart; + await _shim.removeIbl().toDart; } @override @@ -120,7 +126,7 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { double sunHaloSize = 10.0, double sunHaloFallof = 80.0, bool castShadows = true}) async { - return (await _jsObject + return (await _shim .addLight( type.index, colour, @@ -144,36 +150,36 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { @override Future removeLight(FilamentEntity light) async { - await _jsObject.removeLight(light).toDart; + await _shim.removeLight(light).toDart; } @override Future clearLights() async { - await _jsObject.clearLights().toDart; + await _shim.clearLights().toDart; } @override Future loadGlb(String path, {int numInstances = 1}) async { - var entity = (await _jsObject.loadGlb(path, numInstances).toDart).toDartInt; + var entity = (await _shim.loadGlb(path, numInstances).toDart).toDartInt; scene.registerEntity(entity); return entity; } @override Future createInstance(FilamentEntity entity) async { - return (await _jsObject.createInstance(entity).toDart).toDartInt; + return (await _shim.createInstance(entity).toDart).toDartInt; } @override Future getInstanceCount(FilamentEntity entity) async { - return (await _jsObject.getInstanceCount(entity).toDart).toDartInt; + return (await _shim.getInstanceCount(entity).toDart).toDartInt; } @override Future> getInstances(FilamentEntity entity) async { throw UnimplementedError(); // final List jsInstances = - // await _jsObject.getInstances(entity).toDart; + // await _shim.getInstances(entity).toDart; // return jsInstances // .map((js) => FilamentEntity._fromJSObject(js)) // .toList() @@ -184,7 +190,7 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { Future loadGltf(String path, String relativeResourcePath, {bool force = false}) async { throw UnimplementedError(); - // final FilamentEntity jsEntity = await _jsObject + // final FilamentEntity jsEntity = await _shim // .loadGltf(path, relativeResourcePath, force: force) // .toDart; // return FilamentEntity._fromJSObject(jsEntity).toDart; @@ -192,53 +198,52 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { @override Future panStart(double x, double y) async { - await _jsObject.panStart(x, y).toDart; + await _shim.panStart(x, y).toDart; } @override Future panUpdate(double x, double y) async { - await _jsObject.panUpdate(x, y).toDart; + await _shim.panUpdate(x, y).toDart; } @override Future panEnd() async { - await _jsObject.panEnd().toDart; + await _shim.panEnd().toDart; } @override Future rotateStart(double x, double y) async { - await _jsObject.rotateStart(x, y).toDart; + await _shim.rotateStart(x, y).toDart; } @override Future rotateUpdate(double x, double y) async { - await _jsObject.rotateUpdate(x, y).toDart; + await _shim.rotateUpdate(x, y).toDart; } @override Future rotateEnd() async { - await _jsObject.rotateEnd().toDart; + await _shim.rotateEnd().toDart; } @override Future setMorphTargetWeights( FilamentEntity entity, List weights) async { var jsWeights = weights.map((x) => x.toJS).cast().toList().toJS; - var promise = _jsObject.setMorphTargetWeights(entity, jsWeights); + var promise = _shim.setMorphTargetWeights(entity, jsWeights); await promise.toDart; } @override Future> getMorphTargetNames( FilamentEntity entity, FilamentEntity childEntity) async { - var result = - await _jsObject.getMorphTargetNames(entity, childEntity).toDart; + var result = await _shim.getMorphTargetNames(entity, childEntity).toDart; return result.toDart.map((r) => r.toDart).toList(); } @override Future> getAnimationNames(FilamentEntity entity) async { - var names = (await (_jsObject.getAnimationNames(entity).toDart)) + var names = (await (_shim.getAnimationNames(entity).toDart)) .toDart .map((x) => x.toDart) .toList(); @@ -248,7 +253,7 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { @override Future getAnimationDuration( FilamentEntity entity, int animationIndex) async { - return (await _jsObject.getAnimationDuration(entity, animationIndex).toDart) + return (await _shim.getAnimationDuration(entity, animationIndex).toDart) .toDartDouble; } @@ -268,7 +273,7 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { .toJS; var targetMeshNamesJS = targetMeshNames?.map((x) => x.toJS).cast().toList().toJS; - await _jsObject + await _shim .setMorphAnimationData(entity, animationDataJs, morphTargetsJs, targetMeshNamesJS, animation.frameLengthInMs) .toDart; @@ -281,7 +286,7 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { @override Future resetBones(FilamentEntity entity) async { - await _jsObject.resetBones(entity).toDart; + await _shim.resetBones(entity).toDart; } @override @@ -290,7 +295,7 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { {int skinIndex = 0, double fadeInInSecs = 0.0, double fadeOutInSecs = 0.0, - double maxDelta=1.0}) async { + double maxDelta = 1.0}) async { var boneNames = animation.bones.map((n) => n.toJS).toList().toJS; var frameData = animation.frameData .map((frame) => frame @@ -308,7 +313,7 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { .toList() .toJS; - await _jsObject + await _shim .addBoneAnimation( entity, boneNames, @@ -318,33 +323,33 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { skinIndex.toJS, fadeInInSecs.toJS, fadeOutInSecs.toJS, - maxDelta) + maxDelta.toJS) .toDart; } @override Future removeEntity(FilamentEntity entity) async { - await _jsObject.removeEntity(entity).toDart; + await _shim.removeEntity(entity).toDart; } @override Future clearEntities() async { - await _jsObject.clearEntities().toDart; + await _shim.clearEntities().toDart; } @override Future zoomBegin() async { - await _jsObject.zoomBegin().toDart; + await _shim.zoomBegin().toDart; } @override Future zoomUpdate(double x, double y, double z) async { - await _jsObject.zoomUpdate(x, y, z).toDart; + await _shim.zoomUpdate(x, y, z).toDart; } @override Future zoomEnd() async { - await _jsObject.zoomEnd().toDart; + await _shim.zoomEnd().toDart; } @override @@ -353,7 +358,7 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { bool reverse = false, bool replaceActive = true, double crossfade = 0.0}) async { - await _jsObject + await _shim .playAnimation(entity, index, loop, reverse, replaceActive, crossfade) .toDart; } @@ -364,7 +369,7 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { bool reverse = false, bool replaceActive = true, double crossfade = 0.0}) async { - await _jsObject + await _shim .playAnimationByName( entity, name, loop, reverse, replaceActive, crossfade) .toDart; @@ -373,79 +378,79 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { @override Future setAnimationFrame( FilamentEntity entity, int index, int animationFrame) async { - await _jsObject.setAnimationFrame(entity, index, animationFrame).toDart; + await _shim.setAnimationFrame(entity, index, animationFrame).toDart; } @override Future stopAnimation(FilamentEntity entity, int animationIndex) async { - await _jsObject.stopAnimation(entity, animationIndex).toDart; + await _shim.stopAnimation(entity, animationIndex).toDart; } @override Future stopAnimationByName(FilamentEntity entity, String name) async { - await _jsObject.stopAnimationByName(entity, name).toDart; + await _shim.stopAnimationByName(entity, name).toDart; } @override Future setCamera(FilamentEntity entity, String? name) async { - await _jsObject.setCamera(entity, name).toDart; + await _shim.setCamera(entity, name).toDart; } @override Future setMainCamera() async { - await _jsObject.setMainCamera().toDart; + await _shim.setMainCamera().toDart; } @override Future getMainCamera() async { throw UnimplementedError(); - // final FilamentEntity jsEntity = await _jsObject.getMainCamera().toDart; + // final FilamentEntity jsEntity = await _shim.getMainCamera().toDart; // return FilamentEntity._fromJSObject(jsEntity).toDart; } @override Future setCameraFov(double degrees, double width, double height) async { - await _jsObject.setCameraFov(degrees, width, height).toDart; + await _shim.setCameraFov(degrees, width, height).toDart; } @override Future setToneMapping(ToneMapper mapper) async { - await _jsObject.setToneMapping(mapper.index).toDart; + await _shim.setToneMapping(mapper.index).toDart; } @override Future setBloom(double bloom) async { - await _jsObject.setBloom(bloom).toDart; + await _shim.setBloom(bloom).toDart; } @override Future setCameraFocalLength(double focalLength) async { - await _jsObject.setCameraFocalLength(focalLength).toDart; + await _shim.setCameraFocalLength(focalLength).toDart; } @override Future setCameraCulling(double near, double far) async { - await _jsObject.setCameraCulling(near, far).toDart; + await _shim.setCameraCulling(near, far).toDart; } @override Future getCameraCullingNear() async { - return (await _jsObject.getCameraCullingNear().toDart).toDartDouble; + return (await _shim.getCameraCullingNear().toDart).toDartDouble; } @override Future getCameraCullingFar() async { - return (await _jsObject.getCameraCullingFar().toDart).toDartDouble; + return (await _shim.getCameraCullingFar().toDart).toDartDouble; } @override Future setCameraFocusDistance(double focusDistance) async { - await _jsObject.setCameraFocusDistance(focusDistance).toDart; + await _shim.setCameraFocusDistance(focusDistance).toDart; } @override Future getCameraPosition() async { - final jsPosition = (await _jsObject.getCameraPosition().toDart).toDart; + final jsPosition = (await _shim.getCameraPosition().toDart).toDart; return Vector3(jsPosition[0].toDartDouble, jsPosition[1].toDartDouble, jsPosition[2].toDartDouble); } @@ -453,14 +458,14 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { @override Future getCameraModelMatrix() async { throw UnimplementedError(); - // final JSMatrix4 jsMatrix = await _jsObject.getCameraModelMatrix().toDart; + // final JSMatrix4 jsMatrix = await _shim.getCameraModelMatrix().toDart; // return Matrix4.fromList(jsMatrix.storage).toDart; } @override Future getCameraViewMatrix() async { throw UnimplementedError(); - // final JSMatrix4 jsMatrix = await _jsObject.getCameraViewMatrix().toDart; + // final JSMatrix4 jsMatrix = await _shim.getCameraViewMatrix().toDart; // return Matrix4.fromList(jsMatrix.storage).toDart; } @@ -468,7 +473,7 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { Future getCameraProjectionMatrix() async { throw UnimplementedError(); // final JSMatrix4 jsMatrix = - // await _jsObject.getCameraProjectionMatrix().toDart; + // await _shim.getCameraProjectionMatrix().toDart; // return Matrix4.fromList(jsMatrix.storage).toDart; } @@ -476,47 +481,45 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { Future getCameraCullingProjectionMatrix() async { throw UnimplementedError(); // final JSMatrix4 jsMatrix = - // await _jsObject.getCameraCullingProjectionMatrix().toDart; + // await _shim.getCameraCullingProjectionMatrix().toDart; // return Matrix4.fromList(jsMatrix.storage).toDart; } @override Future getCameraFrustum() async { throw UnimplementedError(); - // final JSObject jsFrustum = await _jsObject.getCameraFrustum().toDart; + // 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 setCameraPosition(double x, double y, double z) async { - await _jsObject.setCameraPosition(x, y, z).toDart; + await _shim.setCameraPosition(x, y, z).toDart; } @override Future getCameraRotation() async { throw UnimplementedError(); - // final JSMatrix3 jsRotation = await _jsObject.getCameraRotation().toDart; + // final JSMatrix3 jsRotation = await _shim.getCameraRotation().toDart; // return Matrix3.fromList(jsRotation.storage).toDart; } @override Future moveCameraToAsset(FilamentEntity entity) async { - await _jsObject.moveCameraToAsset(entity).toDart; + await _shim.moveCameraToAsset(entity).toDart; } @override Future setViewFrustumCulling(bool enabled) async { throw UnimplementedError(); - // await _jsObject.setViewFrustumCulling(enabled.toJSBoolean()).toDart; + // await _shim.setViewFrustumCulling(enabled.toJSBoolean()).toDart; } @override Future setCameraExposure( double aperture, double shutterSpeed, double sensitivity) async { - await _jsObject - .setCameraExposure(aperture, shutterSpeed, sensitivity) - .toDart; + await _shim.setCameraExposure(aperture, shutterSpeed, sensitivity).toDart; } @override @@ -527,58 +530,58 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { quaternion.z.toJS, quaternion.w.toJS ]; - await _jsObject.setCameraRotation(values.toJS).toDart; + await _shim.setCameraRotation(values.toJS).toDart; } @override Future setCameraModelMatrix(List matrix) async { throw UnimplementedError(); - // await _jsObject.setCameraModelMatrix(matrix.toJSBox).toDart; + // await _shim.setCameraModelMatrix(matrix.toJSBox).toDart; } @override Future setMaterialColor(FilamentEntity entity, String meshName, int materialIndex, double r, double g, double b, double a) async { - await _jsObject + await _shim .setMaterialColor(entity, meshName, materialIndex, r, g, b, a) .toDart; } @override Future transformToUnitCube(FilamentEntity entity) async { - await _jsObject.transformToUnitCube(entity).toDart; + await _shim.transformToUnitCube(entity).toDart; } @override Future setPosition( FilamentEntity entity, double x, double y, double z) async { - await _jsObject.setPosition(entity, x, y, z).toDart; + await _shim.setPosition(entity, x, y, z).toDart; } @override Future setScale(FilamentEntity entity, double scale) async { - await _jsObject.setScale(entity, scale).toDart; + await _shim.setScale(entity, scale).toDart; } @override Future setRotation( FilamentEntity entity, double rads, double x, double y, double z) async { - await _jsObject.setRotation(entity, rads, x, y, z).toDart; + await _shim.setRotation(entity, rads, x, y, z).toDart; } @override Future queuePositionUpdate( FilamentEntity entity, double x, double y, double z, {bool relative = false}) async { - await _jsObject.queuePositionUpdate(entity, x, y, z, relative).toDart; + await _shim.queuePositionUpdate(entity, x, y, z, relative).toDart; } @override Future queueRotationUpdate( FilamentEntity entity, double rads, double x, double y, double z, {bool relative = false}) async { - await _jsObject.queueRotationUpdate(entity, rads, x, y, z, relative).toDart; + await _shim.queueRotationUpdate(entity, rads, x, y, z, relative).toDart; } @override @@ -587,19 +590,19 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { throw UnimplementedError(); // final JSQuaternion jsQuat = quat.toJSQuaternion().toDart; - // await _jsObject + // await _shim // .queueRotationUpdateQuat(entity, jsQuat, relative: relative) // .toDart; } @override Future setPostProcessing(bool enabled) async { - await _jsObject.setPostProcessing(enabled).toDart; + await _shim.setPostProcessing(enabled).toDart; } @override Future setAntiAliasing(bool msaa, bool fxaa, bool taa) async { - await _jsObject.setAntiAliasing(msaa, fxaa, taa).toDart; + await _shim.setAntiAliasing(msaa, fxaa, taa).toDart; } @override @@ -607,30 +610,30 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { FilamentEntity entity, Quaternion rotation) async { throw UnimplementedError(); // final JSQuaternion jsRotation = rotation.toJSQuaternion().toDart; - // await _jsObject.setRotationQuat(entity, jsRotation).toDart; + // await _shim.setRotationQuat(entity, jsRotation).toDart; } @override Future reveal(FilamentEntity entity, String? meshName) async { throw UnimplementedError(); - // await _jsObject.reveal(entity, meshName).toDart; + // await _shim.reveal(entity, meshName).toDart; } @override Future hide(FilamentEntity entity, String? meshName) async { throw UnimplementedError(); - // await _jsObject.hide(entity, meshName).toDart; + // await _shim.hide(entity, meshName).toDart; } @override void pick(int x, int y) { throw UnimplementedError(); - // _jsObject.pick(x, y).toDart; + // _shim.pick(x, y).toDart; } @override String? getNameForEntity(FilamentEntity entity) { - return _jsObject.getNameForEntity(entity); + return _shim.getNameForEntity(entity); } @override @@ -639,7 +642,7 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { double orbitSpeedX = 0.01, double orbitSpeedY = 0.01, double zoomSpeed = 0.01}) async { - await _jsObject + await _shim .setCameraManipulatorOptions( mode.index, orbitSpeedX, orbitSpeedY, zoomSpeed) .toDart; @@ -649,7 +652,7 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { Future> getChildEntities( FilamentEntity parent, bool renderableOnly) async { final children = - await _jsObject.getChildEntities(parent, renderableOnly).toDart; + await _shim.getChildEntities(parent, renderableOnly).toDart; return children.toDart .map((js) => js.toDartInt) .cast() @@ -659,31 +662,30 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { @override Future getChildEntity( FilamentEntity parent, String childName) async { - return (await _jsObject.getChildEntity(parent, childName).toDart).toDartInt; + return (await _shim.getChildEntity(parent, childName).toDart).toDartInt; } @override Future> getChildEntityNames(FilamentEntity entity, {bool renderableOnly = true}) async { - var names = - await _jsObject.getChildEntityNames(entity, renderableOnly).toDart; + var names = await _shim.getChildEntityNames(entity, renderableOnly).toDart; return names.toDart.map((x) => x.toDart).toList(); } @override Future setRecording(bool recording) async { throw UnimplementedError(); - // await _jsObject.setRecording(recording.toJSBoolean()).toDart; + // await _shim.setRecording(recording.toJSBoolean()).toDart; } @override Future setRecordingOutputDirectory(String outputDirectory) async { - await _jsObject.setRecordingOutputDirectory(outputDirectory).toDart; + await _shim.setRecordingOutputDirectory(outputDirectory).toDart; } @override Future addAnimationComponent(FilamentEntity entity) async { - await _jsObject.addAnimationComponent(entity).toDart; + await _shim.addAnimationComponent(entity).toDart; } @override @@ -695,7 +697,7 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { // ? allowInterop( // (int entityId1, int entityId2) => callback(entityId1, entityId2)) // : null; - // await _jsObject + // await _shim // .addCollisionComponent(entity, // callback: jsCallback, // affectsTransform: affectsTransform.toJSBoolean()) @@ -704,7 +706,7 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { @override Future removeCollisionComponent(FilamentEntity entity) async { - await _jsObject.removeCollisionComponent(entity).toDart; + await _shim.removeCollisionComponent(entity).toDart; } @override @@ -713,7 +715,7 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { {String? materialPath, PrimitiveType primitiveType = PrimitiveType.TRIANGLES}) async { throw UnimplementedError(); - // final FilamentEntity jsEntity = await _jsObject + // final FilamentEntity jsEntity = await _shim // .createGeometry(vertices, indices, // materialPath: materialPath, primitiveType: primitiveType.index) // .toDart; @@ -722,17 +724,17 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { @override Future setParent(FilamentEntity child, FilamentEntity parent) async { - await _jsObject.setParent(child, parent).toDart; + await _shim.setParent(child, parent).toDart; } @override Future testCollisions(FilamentEntity entity) async { - await _jsObject.testCollisions(entity).toDart; + await _shim.testCollisions(entity).toDart; } @override Future setPriority(FilamentEntity entityId, int priority) async { - await _jsObject.setPriority(entityId, priority).toDart; + await _shim.setPriority(entityId, priority).toDart; } Scene? _scene; @@ -748,14 +750,14 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { @override Future> getBoneNames(FilamentEntity entity, {int skinIndex = 0}) async { - var result = await _jsObject.getBoneNames(entity, skinIndex).toDart; + var result = await _shim.getBoneNames(entity, skinIndex).toDart; return result.toDart.map((n) => n.toDart).toList(); } @override Future getBone(FilamentEntity entity, int boneIndex, {int skinIndex = 0}) async { - var result = await _jsObject.getBone(entity, boneIndex, skinIndex).toDart; + var result = await _shim.getBone(entity, boneIndex, skinIndex).toDart; return result.toDartInt; } @@ -768,32 +770,32 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { @override Future getLocalTransform(FilamentEntity entity) async { - var result = await _jsObject.getLocalTransform(entity).toDart; + var result = await _shim.getLocalTransform(entity).toDart; return Matrix4.fromList(result.toDart.map((v) => v.toDartDouble).toList()); } @override Future getParent(FilamentEntity child) async { - var result = await _jsObject.getParent(child).toDart; + var result = await _shim.getParent(child).toDart; return result.toDartInt; } @override Future getWorldTransform(FilamentEntity entity) async { - var result = await _jsObject.getLocalTransform(entity).toDart; + var result = await _shim.getLocalTransform(entity).toDart; return Matrix4.fromList(result.toDart.map((v) => v.toDartDouble).toList()); } @override Future removeAnimationComponent(FilamentEntity entity) { - return _jsObject.removeAnimationComponent(entity).toDart; + return _shim.removeAnimationComponent(entity).toDart; } @override Future setBoneTransform( FilamentEntity entity, int boneIndex, Matrix4 transform, {int skinIndex = 0}) { - return _jsObject + return _shim .setBoneTransform(entity, boneIndex, transform.storage.map((v) => v.toJS).toList().toJS, skinIndex) .toDart; @@ -801,7 +803,7 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { @override Future setTransform(FilamentEntity entity, Matrix4 transform) { - return _jsObject + return _shim .setTransform( entity, transform.storage.map((v) => v.toJS).toList().toJS) .toDart; @@ -809,6 +811,6 @@ class JsInteropFilamentViewer implements AbstractFilamentViewer { @override Future updateBoneMatrices(FilamentEntity entity) { - return _jsObject.updateBoneMatrices(entity).toDart; + return _shim.updateBoneMatrices(entity).toDart; } } diff --git a/dart_filament/lib/dart_filament/compatibility/web/interop/dart_filament_js_export_type.dart b/dart_filament/lib/dart_filament/compatibility/web/interop/filament_viewer_js_dart_bridge.dart similarity index 96% rename from dart_filament/lib/dart_filament/compatibility/web/interop/dart_filament_js_export_type.dart rename to dart_filament/lib/dart_filament/compatibility/web/interop/filament_viewer_js_dart_bridge.dart index ceb8e34b..08598830 100644 --- a/dart_filament/lib/dart_filament/compatibility/web/interop/dart_filament_js_export_type.dart +++ b/dart_filament/lib/dart_filament/compatibility/web/interop/filament_viewer_js_dart_bridge.dart @@ -2,30 +2,36 @@ library flutter_filament_js; import 'dart:js_interop'; -import 'dart:math'; +import 'package:dart_filament/dart_filament/compatibility/web/interop/shims/abstract_filament_viewer_js_shim.dart'; +import 'package:dart_filament/dart_filament/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:animation_tools_dart/src/morph_animation_data.dart'; import 'package:dart_filament/dart_filament/abstract_filament_viewer.dart'; -import 'package:dart_filament/dart_filament/entities/filament_entity.dart'; -import 'package:dart_filament/dart_filament/compatibility/web/interop/dart_filament_js_extension_type.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 DartFilamentJSExportViewer { +class FilamentViewerJSDartBridge { final AbstractFilamentViewer viewer; - static void initializeBindings(AbstractFilamentViewer viewer) { - var shim = DartFilamentJSExportViewer(viewer); - var wrapper = createJSInteropWrapper(shim) - as DartFilamentJSShim; - globalContext.setProperty("filamentViewer".toJS, wrapper); + FilamentViewerJSDartBridge(this.viewer); + + void bind( + {String globalPropertyName = "filamentViewer"}) { + var wrapper = createJSInteropWrapper(this) + as AbstractFilamentViewerJSShim; + globalContext.setProperty(globalPropertyName.toJS, wrapper); } - DartFilamentJSExportViewer(this.viewer); - JSPromise get initialized { return viewer.initialized.then((v) => v.toJS).toJS; } @@ -559,28 +565,31 @@ class DartFilamentJSExportViewer { @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 rotation) => throw UnimplementedError(); -// viewer.setRotationQuat( -// entity, -// rotation.toDartQuaternion(), -// ).toJS; + @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, @@ -596,6 +605,7 @@ class DartFilamentJSExportViewer { zoomSpeed: zoomSpeed, ) .toJS; + @JSExport() JSPromise> getChildEntities( FilamentEntity parent, bool renderableOnly) { @@ -707,13 +717,5 @@ class DartFilamentJSExportViewer { JSPromise addCollisionComponent(FilamentEntity entity, {JSFunction? callback, bool affectsTransform = false}) { throw UnimplementedError(); -// final Function? dartCallback = callback != null -// ? allowInterop((int entityId1, int entityId2) => callback.apply([entityId1, entityId2])) -// : null; -// return viewer.addCollisionComponent( -// entity), -// callback: dartCallback, -// affectsTransform: affectsTransform, -// ).toJs } } diff --git a/dart_filament/lib/dart_filament/compatibility/web/interop/filament_viewer_wasm.dart b/dart_filament/lib/dart_filament/compatibility/web/interop/filament_viewer_wasm.dart new file mode 100644 index 00000000..e7165ebe --- /dev/null +++ b/dart_filament/lib/dart_filament/compatibility/web/interop/filament_viewer_wasm.dart @@ -0,0 +1,1451 @@ +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; +import 'dart:math'; +import 'dart:typed_data' as td; +import 'dart:typed_data'; +import 'package:web/web.dart'; +import 'package:animation_tools_dart/animation_tools_dart.dart'; +import 'package:dart_filament/dart_filament/abstract_filament_viewer.dart'; +import 'package:dart_filament/dart_filament/compatibility/web/interop/shims/dart_filament_api_js_shim.dart'; +import 'package:dart_filament/dart_filament/entities/filament_entity.dart'; +import 'package:dart_filament/dart_filament/scene.dart'; +import 'package:vector_math/vector_math_64.dart'; +import 'shims/abstract_filament_viewer_js_shim.dart'; + +extension type _EmscriptenModule(JSObject _) implements JSObject { + external JSAny? ccall(String name, String returnType, + JSArray argTypes, JSArray args, JSAny? opts); + + external JSNumber _malloc(int numBytes); + external void _free(JSNumber addr); + external JSNumber stackAlloc(int numBytes); + + external JSAny getValue(JSNumber addr, String llvmType); + external void setValue(JSNumber addr, JSNumber value, String llvmType); + + external JSString intArrayToString(JSAny ptr); + external JSString UTF8ToString(JSAny ptr); + external void stringToUTF8( + JSString str, JSNumber ptr, JSNumber maxBytesToWrite); + external void writeArrayToMemory(JSUint8Array data, JSNumber ptr); + external JSAny get ALLOC_STACK; + external JSAny get HEAPU32; + external JSAny get HEAP32; +} + +/// +/// An [AbstractFilamentViewer] implementation that forwards calls to +/// the (Emscripten-generated) DartFilament JS module. +/// +class FilamentViewerWasm implements AbstractFilamentViewer { + late _EmscriptenModule _module; + + bool _initialized = false; + bool _rendering = false; + + FilamentViewerWasm() { + _module = window.getProperty<_EmscriptenModule>("df".toJS); + } + + JSBigInt? _viewer; + JSBigInt? _sceneManager; + + @override + Future initialize(int width, int height, {String? uberArchivePath}) async { + final context = _module.ccall("dart_filament_web_create_gl_context", "int", + [].toJS, [].toJS, null); + final loader = _module.ccall( + "dart_filament_web_get_resource_loader_wrapper", + "void*", + [].toJS, + [].toJS, + null); + _viewer = _module.ccall( + "create_filament_viewer", + "void*", + ["void*".toJS, "void*".toJS, "void*".toJS, "string".toJS].toJS, + [context, loader, null, uberArchivePath?.toJS].toJS, + null) as JSBigInt; + print("Created viewer"); + await createSwapChain(width, height); + _updateViewportAndCameraProjection(width, height, 1.0); + _sceneManager = _module.ccall("get_scene_manager", "void*", + ["void*".toJS].toJS, [_viewer!].toJS, null) as JSBigInt; + _initialized = true; + } + + Future createSwapChain(int width, int height) async { + _module.ccall( + "create_swap_chain", + "void", + ["void*".toJS, "void*".toJS, "uint32_t".toJS, "uint32_t".toJS].toJS, + [_viewer!, null, width.toJS, height.toJS].toJS, + null); + } + + Future destroySwapChain() async { + _module.ccall("destroy_swap_chain", "void", ["void*".toJS].toJS, + [_viewer!].toJS, null); + } + + @override + void _updateViewportAndCameraProjection( + int width, int height, double scaleFactor) { + _module.ccall( + "update_viewport_and_camera_projection", + "void", + ["void*".toJS, "uint32_t".toJS, "uint32_t".toJS, "float".toJS].toJS, + [_viewer!, width.toJS, height.toJS, scaleFactor.toJS].toJS, + null); + } + + @override + Future get initialized async { + return _initialized; + } + + @override + Stream get pickResult { + throw UnimplementedError(); + } + + @override + bool get rendering => _rendering; + + @override + Future dispose() async { + _module.ccall("destroy_filament_viewer", "void", ["void*".toJS].toJS, + [_viewer].toJS, null); + _initialized = false; + _viewer = null; + } + + @override + Future setBackgroundColor(double r, double g, double b, double alpha) async { + _module.ccall( + "set_background_color", + "void", + ["void*".toJS, "float".toJS, "float".toJS, "float".toJS, "float".toJS] + .toJS, + [_viewer!, r.toJS, g.toJS, b.toJS, alpha.toJS].toJS, + null); + } + + @override + Future addAnimationComponent(FilamentEntity entity) async { + _module.ccall( + "add_animation_component", + "bool", + ["void*".toJS, "int32_t".toJS].toJS, + [_sceneManager!, entity.toJS].toJS, + null); + } + + Matrix4 _matrixFromPtr(JSNumber matPtr) { + final mat = Matrix4.zero(); + for (int i = 0; i < 16; i++) { + mat[i] = (_module.getValue((matPtr.toDartInt + (i * 4)).toJS, "float") + as JSNumber) + .toDartDouble; + } + return mat; + } + + @override + Future> getRestLocalTransforms(FilamentEntity entity, + {int skinIndex = 0}) async { + var boneCountJS = _module.ccall( + "get_bone_count", + "int", + ["void*".toJS, "int".toJS, "int".toJS].toJS, + [_sceneManager!, entity.toJS, skinIndex.toJS].toJS, + null) as JSNumber; + var boneCount = boneCountJS.toDartInt; + var buf = _module._malloc(boneCount * 16 * 4) as JSNumber; + _module.ccall( + "get_rest_local_transforms", + "void", + ["void*".toJS, "int".toJS, "int".toJS, "float*".toJS, "int".toJS].toJS, + [_sceneManager!, entity.toJS, skinIndex.toJS, buf, boneCount.toJS].toJS, + null); + var transforms = []; + for (int i = 0; i < boneCount; i++) { + var matPtr = (buf.toDartInt + (i * 16 * 4)).toJS; + transforms.add(_matrixFromPtr(matPtr)); + } + _module._free(buf); + return transforms; + } + + @override + Future getBone(FilamentEntity parent, int boneIndex, + {int skinIndex = 0}) async { + final boneId = _module.ccall( + "get_bone", + "int", + ["void*".toJS, "int32_t".toJS, "int32_t".toJS, "int32_t".toJS].toJS, + [_sceneManager!, parent.toJS, skinIndex.toJS, boneIndex.toJS].toJS, + null) as JSNumber; + if (boneId.toDartInt == -1) { + throw Exception("Failed to get bone"); + } + return boneId.toDartInt; + } + + Future> getBones(FilamentEntity entity, + {int skinIndex = 0}) async { + final boneNames = await getBoneNames(entity); + final bones = await Future.wait(List.generate( + boneNames.length, (i) => getBone(entity, i, skinIndex: skinIndex))); + return bones; + } + + @override + Future addBoneAnimation(FilamentEntity entity, BoneAnimationData animation, + {int skinIndex = 0, + double fadeInInSecs = 0.0, + double fadeOutInSecs = 0.0, + double maxDelta = 1.0}) async { + final boneNames = await getBoneNames(entity); + final bones = await getBones(entity); + + var numBytes = animation.numFrames * 16 * 4; + var floatPtr = _module._malloc(numBytes); + + var restLocalTransforms = await getRestLocalTransforms(entity); + + for (int i = 0; i < animation.bones.length; i++) { + final boneName = animation.bones[i]; + final entityBoneIndex = boneNames.indexOf(boneName); + + var boneEntity = bones[entityBoneIndex]; + + var baseTransform = restLocalTransforms[entityBoneIndex]; + + var world = Matrix4.identity(); + + // this odd use of ! is intentional, without it, the WASM optimizer gets in trouble + var parentBoneEntity = (await getParent(boneEntity))!; + while (true) { + if (!bones.contains(parentBoneEntity!)) { + break; + } + world = restLocalTransforms[bones.indexOf(parentBoneEntity!)] * world; + parentBoneEntity = (await getParent(parentBoneEntity))!; + } + + world = Matrix4.identity()..setRotation(world.getRotation()); + var worldInverse = Matrix4.identity()..copyInverse(world); + + for (int frameNum = 0; frameNum < animation.numFrames; frameNum++) { + var rotation = animation.frameData[frameNum][i].rotation; + var translation = animation.frameData[frameNum][i].translation; + var frameTransform = + Matrix4.compose(translation, rotation, Vector3.all(1.0)); + var newLocalTransform = frameTransform.clone(); + if (animation.space == Space.Bone) { + newLocalTransform = baseTransform * frameTransform; + } else if (animation.space == Space.ParentWorldRotation) { + newLocalTransform = + baseTransform * (worldInverse * frameTransform * world); + } + for (int j = 0; j < 16; j++) { + var offset = ((frameNum * 16) + j) * 4; + _module.setValue((floatPtr.toDartInt + offset).toJS, + newLocalTransform.storage[j].toJS, "float"); + } + } + + _module.ccall( + "add_bone_animation", + "void", + [ + "void*".toJS, + "int".toJS, + "int".toJS, + "int".toJS, + "float*".toJS, + "int".toJS, + "float".toJS, + "float".toJS, + "float".toJS, + "float".toJS + ].toJS, + [ + _sceneManager!, + entity.toJS, + skinIndex.toJS, + entityBoneIndex.toJS, + floatPtr, + animation.numFrames.toJS, + animation.frameLengthInMs.toJS, + fadeOutInSecs.toJS, + fadeInInSecs.toJS, + maxDelta.toJS + ].toJS, + null); + } + _module._free(floatPtr); + } + + @override + Future addCollisionComponent(FilamentEntity entity, + {void Function(int entityId1, int entityId2)? callback, + bool affectsTransform = false}) { + // TODO: implement addCollisionComponent + throw UnimplementedError(); + } + + @override + Future addLight( + LightType type, + double colour, + double intensity, + double posX, + double posY, + double posZ, + double dirX, + double dirY, + double dirZ, + {double falloffRadius = 1.0, + double spotLightConeInner = pi / 8, + double spotLightConeOuter = pi / 4, + double sunAngularRadius = 0.545, + double sunHaloSize = 10.0, + double sunHaloFallof = 80.0, + bool castShadows = true}) async { + final entityId = _module.ccall( + "add_light", + "int", + [ + "void*".toJS, + "uint8_t".toJS, + "float".toJS, + "float".toJS, + "float".toJS, + "float".toJS, + "float".toJS, + "float".toJS, + "float".toJS, + "float".toJS, + "float".toJS, + "float".toJS, + "float".toJS, + "float".toJS, + "float".toJS, + "float".toJS, + "float".toJS, + "bool".toJS + ].toJS, + [ + _viewer, + type.index.toJS, + colour.toJS, + intensity.toJS, + posX.toJS, + posY.toJS, + posZ.toJS, + dirX.toJS, + dirY.toJS, + dirZ.toJS, + falloffRadius.toJS, + spotLightConeInner.toJS, + spotLightConeOuter.toJS, + sunAngularRadius.toJS, + sunHaloSize.toJS, + sunHaloFallof.toJS, + castShadows.toJS + ].toJS, + null) as JSNumber; + if (entityId.toDartInt == -1) { + throw Exception("Failed to add light"); + } + return entityId.toDartInt; + } + + @override + Future clearBackgroundImage() { + // TODO: implement clearBackgroundImage + throw UnimplementedError(); + } + + @override + Future clearEntities() { + // TODO: implement clearEntities + throw UnimplementedError(); + } + + @override + Future clearLights() { + // TODO: implement clearLights + throw UnimplementedError(); + } + + @override + Future createGeometry(List vertices, List indices, + {String? materialPath, + PrimitiveType primitiveType = PrimitiveType.TRIANGLES}) { + // TODO: implement createGeometry + throw UnimplementedError(); + } + + @override + Future createInstance(FilamentEntity entity) { + // TODO: implement createInstance + throw UnimplementedError(); + } + + @override + Future getAnimationDuration( + FilamentEntity entity, int animationIndex) { + // TODO: implement getAnimationDuration + throw UnimplementedError(); + } + + @override + Future> getAnimationNames(FilamentEntity entity) { + // TODO: implement getAnimationNames + throw UnimplementedError(); + } + + @override + Future> getBoneNames(FilamentEntity entity, + {int skinIndex = 0}) async { + var boneCountJS = _module.ccall( + "get_bone_count", + "int", + ["void*".toJS, "int".toJS, "int".toJS].toJS, + [_sceneManager!, entity.toJS, skinIndex.toJS].toJS, + null) as JSNumber; + var boneCount = boneCountJS.toDartInt; + var buf = _module._malloc(boneCount * 4) as JSNumber; + + var empty = " ".toJS; + var ptrs = []; + for (int i = 0; i < boneCount; i++) { + var ptr = _module._malloc(256); + _module.stringToUTF8(empty, ptr, 255.toJS); + ptrs.add(ptr); + _module.setValue((buf.toDartInt + (i * 4)).toJS, ptr, "i32"); + } + _module.ccall( + "get_bone_names", + "void", + ["void*".toJS, "int".toJS, "char**".toJS, "int".toJS].toJS, + [_sceneManager!, entity.toJS, buf, skinIndex.toJS].toJS, + null); + var names = []; + for (int i = 0; i < boneCount; i++) { + var name = _module.UTF8ToString(ptrs[i]).toDart; + names.add(name); + } + + return names; + } + + @override + Future getCameraCullingFar() { + // TODO: implement getCameraCullingFar + throw UnimplementedError(); + } + + @override + Future getCameraCullingNear() { + // TODO: implement getCameraCullingNear + throw UnimplementedError(); + } + + @override + Future getCameraCullingProjectionMatrix() { + // TODO: implement getCameraCullingProjectionMatrix + throw UnimplementedError(); + } + + @override + Future getCameraFrustum() { + // TODO: implement getCameraFrustum + throw UnimplementedError(); + } + + @override + Future getCameraModelMatrix() { + // TODO: implement getCameraModelMatrix + throw UnimplementedError(); + } + + @override + Future getCameraPosition() { + // TODO: implement getCameraPosition + throw UnimplementedError(); + } + + @override + Future getCameraProjectionMatrix() { + // TODO: implement getCameraProjectionMatrix + throw UnimplementedError(); + } + + @override + Future getCameraRotation() { + // TODO: implement getCameraRotation + throw UnimplementedError(); + } + + @override + Future getCameraViewMatrix() { + // TODO: implement getCameraViewMatrix + throw UnimplementedError(); + } + + @override + Future> getChildEntities( + FilamentEntity parent, bool renderableOnly) async { + var entityCountJS = _module.ccall( + "get_entity_count", + "int", + ["void*".toJS, "int".toJS, "bool".toJS].toJS, + [_sceneManager!, parent.toJS, renderableOnly.toJS].toJS, + null) as JSNumber; + var entityCount = entityCountJS.toDartInt; + var entities = []; + var buf = _module._malloc(entityCount * 4) as JSNumber; + + _module.ccall( + "get_entities", + "void", + ["void*".toJS, "int".toJS, "bool".toJS, "int*".toJS].toJS, + [_sceneManager!, parent.toJS, renderableOnly.toJS, buf].toJS, + null); + for (int i = 0; i < entityCount; i++) { + var entityId = + _module.getValue((buf.toDartInt + (i * 4)).toJS, "i32") as JSNumber; + entities.add(entityId.toDartInt); + } + _module._free(buf); + return entities; + } + + @override + Future getChildEntity( + FilamentEntity parent, String childName) async { + final entityId = _module.ccall( + "find_child_entity_by_name", + "int", + ["void*".toJS, "int".toJS, "string".toJS].toJS, + [_sceneManager!, parent.toJS, childName.toJS].toJS, + null) as JSNumber; + if (entityId.toDartInt == -1) { + throw Exception("Failed to find child entity"); + } + return entityId.toDartInt; + } + + @override + Future> getChildEntityNames(FilamentEntity entity, + {bool renderableOnly = true}) async { + var entityCountJS = _module.ccall( + "get_entity_count", + "int", + ["void*".toJS, "int".toJS, "bool".toJS].toJS, + [_sceneManager!, entity.toJS, renderableOnly.toJS].toJS, + null) as JSNumber; + var entityCount = entityCountJS.toDartInt; + var names = []; + for (int i = 0; i < entityCount; i++) { + var namePtr = _module.ccall( + "get_entity_name_at", + "char*", + ["void*".toJS, "int".toJS, "int".toJS, "bool".toJS].toJS, + [_sceneManager!, entity.toJS, i.toJS, renderableOnly.toJS].toJS, + null) as JSNumber; + names.add(_module.UTF8ToString(namePtr).toDart); + } + return names; + } + + @override + Future getInstanceCount(FilamentEntity entity) { + // TODO: implement getInstanceCount + throw UnimplementedError(); + } + + @override + Future> getInstances(FilamentEntity entity) { + // TODO: implement getInstances + throw UnimplementedError(); + } + + @override + Future getInverseBindMatrix(FilamentEntity parent, int boneIndex, + {int skinIndex = 0}) { + // TODO: implement getInverseBindMatrix + throw UnimplementedError(); + } + + @override + Future getLocalTransform(FilamentEntity entity) { + // TODO: implement getLocalTransform + throw UnimplementedError(); + } + + @override + Future getMainCamera() async { + final entityId = _module.ccall( + "get_main_camera", "int", ["void*".toJS].toJS, [_viewer].toJS, null) + as JSNumber; + if (entityId.toDartInt == -1) { + throw Exception("Failed to get main camera"); + } + return entityId.toDartInt; + } + + @override + Future> getMorphTargetNames( + FilamentEntity entity, FilamentEntity childEntity) async { + var morphTargetCountJS = _module.ccall( + "get_morph_target_name_count", + "int", + ["void*".toJS, "int32_t".toJS, "int32_t".toJS].toJS, + [_sceneManager!, entity.toJS, childEntity.toJS].toJS, + null) as JSNumber; + var morphTargetCount = morphTargetCountJS.toDartInt; + var names = []; + for (int i = 0; i < morphTargetCount; i++) { + var buf = _module._malloc(256) as JSNumber; + _module.ccall( + "get_morph_target_name", + "void", + [ + "void*".toJS, + "int32_t".toJS, + "int32_t".toJS, + "char*".toJS, + "int32_t".toJS + ].toJS, + [_sceneManager!, entity.toJS, childEntity.toJS, buf, i.toJS].toJS, + null); + names.add(_module.UTF8ToString(buf).toDart); + _module._free(buf); + } + return names; + } + + @override + String? getNameForEntity(FilamentEntity entity) { + final namePtr = _module.ccall( + "get_name_for_entity", + "char*", + ["void*".toJS, "int32_t".toJS].toJS, + [_sceneManager!, entity.toJS].toJS, + null) as JSNumber; + if (namePtr.toDartInt == 0) { + return null; + } + return _module.UTF8ToString(namePtr).toDart; + } + + @override + Future getParent(FilamentEntity child) async { + final parentId = _module.ccall( + "get_parent", + "int", + ["void*".toJS, "int32_t".toJS].toJS, + [_sceneManager!, child.toJS].toJS, + null) as JSNumber; + if (parentId.toDartInt == -1) { + return null; + } + return parentId.toDartInt; + } + + @override + Future getWorldTransform(FilamentEntity entity) async { + final matrixPtr = _module._malloc(16 * 4) as JSNumber; + _module.ccall( + "get_world_transform", + "void", + ["void*".toJS, "int32_t".toJS, "float*".toJS].toJS, + [_sceneManager!, entity.toJS, matrixPtr].toJS, + null); + final matrix = _matrixFromPtr(matrixPtr); + _module._free(matrixPtr); + return matrix; + } + + @override + // TODO: implement gizmo + AbstractGizmo? get gizmo => throw UnimplementedError(); + + @override + Future hide(FilamentEntity entity, String? meshName) async { + if (meshName != null) { + final result = _module.ccall( + "hide_mesh", + "int", + ["void*".toJS, "int".toJS, "string".toJS].toJS, + [_sceneManager!, entity.toJS, meshName.toJS].toJS, + null) as JSNumber; + if (result.toDartInt == -1) { + throw Exception( + "Failed to hide mesh ${meshName} on entity ${entity.toJS}"); + } + } else { + throw Exception( + "Cannot hide mesh, meshName must be specified when invoking this method"); + } + } + + Future loadGlbFromBuffer(Uint8List data, + {int numInstances = 1}) async { + if (numInstances != 1) { + throw Exception("TODO"); + } + final ptr = _module._malloc(data.length); + _module.writeArrayToMemory(data.toJS, ptr); + + final result = _module.ccall( + "load_glb_from_buffer", + "int", + ["void*".toJS, "void*".toJS, "size_t".toJS].toJS, + [_sceneManager!, ptr, data.lengthInBytes.toJS].toJS, + null) as JSNumber; + final entityId = result.toDartInt; + _module._free(ptr); + if (entityId == -1) { + throw Exception("Failed to load GLB"); + } + return entityId; + } + + @override + Future loadGlb(String path, {int numInstances = 1}) async { + final promise = _module.ccall( + "load_glb", + "int", + ["void*".toJS, "string".toJS, "int".toJS].toJS, + [_sceneManager!, path.toJS, numInstances.toJS].toJS, + {"async": true}.jsify()) as JSPromise; + final entityId = (await promise.toDart).toDartInt; + if (entityId == -1) { + throw Exception("Failed to load GLB"); + } + return entityId; + } + + @override + Future loadGltf(String path, String relativeResourcePath, + {bool force = false}) async { + final promise = _module.ccall( + "load_gltf", + "int", + ["void*".toJS, "string".toJS, "string".toJS, "bool".toJS].toJS, + [_sceneManager!, path.toJS, relativeResourcePath.toJS, force.toJS].toJS, + {"async": true}.jsify()) as JSPromise; + final entityId = (await promise.toDart).toDartInt; + if (entityId == -1) { + throw Exception("Failed to load GLTF"); + } + return entityId; + } + + @override + Future loadIbl(String lightingPath, {double intensity = 30000}) async { + var promise = _module.ccall( + "load_ibl", + "void", + ["void*".toJS, "string".toJS, "float".toJS].toJS, + [_viewer!, lightingPath.toJS, intensity.toJS].toJS, + {"async": true}.jsify()) as JSPromise; + await promise.toDart; + } + + @override + Future loadSkybox(String skyboxPath) async { + var promise = _module.ccall( + "load_skybox", + "void", + ["void*".toJS, "string".toJS].toJS, + [_viewer!, skyboxPath.toJS].toJS, + {"async": true}.jsify()) as JSPromise; + await promise.toDart; + } + + @override + Future moveCameraToAsset(FilamentEntity entity) { + // TODO: implement moveCameraToAsset + throw UnimplementedError(); + } + + @override + Future panEnd() { + // TODO: implement panEnd + throw UnimplementedError(); + } + + @override + Future panStart(double x, double y) { + // TODO: implement panStart + throw UnimplementedError(); + } + + @override + Future panUpdate(double x, double y) { + // TODO: implement panUpdate + throw UnimplementedError(); + } + + @override + void pick(int x, int y) { + // TODO: implement pick + } + + @override + Future playAnimation(FilamentEntity entity, int index, + {bool loop = false, + bool reverse = false, + bool replaceActive = true, + double crossfade = 0.0}) async { + _module.ccall( + "play_animation", + "void", + [ + "void*".toJS, + "int32_t".toJS, + "int32_t".toJS, + "bool".toJS, + "bool".toJS, + "bool".toJS, + "float".toJS + ].toJS, + [ + _sceneManager!, + entity.toJS, + index.toJS, + loop.toJS, + reverse.toJS, + replaceActive.toJS, + crossfade.toJS + ].toJS, + null); + } + + @override + Future playAnimationByName(FilamentEntity entity, String name, + {bool loop = false, + bool reverse = false, + bool replaceActive = true, + double crossfade = 0.0}) { + // TODO: implement playAnimationByName + throw UnimplementedError(); + } + + @override + Future queuePositionUpdate( + FilamentEntity entity, double x, double y, double z, + {bool relative = false}) { + // TODO: implement queuePositionUpdate + throw UnimplementedError(); + } + + @override + Future queueRotationUpdate( + FilamentEntity entity, double rads, double x, double y, double z, + {bool relative = false}) { + // TODO: implement queueRotationUpdate + throw UnimplementedError(); + } + + @override + Future queueRotationUpdateQuat(FilamentEntity entity, Quaternion quat, + {bool relative = false}) { + // TODO: implement queueRotationUpdateQuat + throw UnimplementedError(); + } + + @override + Future removeAnimationComponent(FilamentEntity entity) { + // TODO: implement removeAnimationComponent + throw UnimplementedError(); + } + + @override + Future removeCollisionComponent(FilamentEntity entity) { + // TODO: implement removeCollisionComponent + throw UnimplementedError(); + } + + @override + Future removeEntity(FilamentEntity entity) { + // TODO: implement removeEntity + throw UnimplementedError(); + } + + @override + Future removeIbl() { + // TODO: implement removeIbl + throw UnimplementedError(); + } + + @override + Future removeLight(FilamentEntity light) { + // TODO: implement removeLight + throw UnimplementedError(); + } + + @override + Future removeSkybox() { + // TODO: implement removeSkybox + throw UnimplementedError(); + } + + int _last = 0; + + @override + Future render() async { + _last = DateTime.now().millisecondsSinceEpoch * 1000000; + _module.ccall( + "render", + "void", + [ + "void*".toJS, + "uint64_t".toJS, + "void*".toJS, + "void*".toJS, + "void*".toJS + ].toJS, + [ + _viewer!, + 0.toJS, + null, // pixelBuffer, + null, // callback + null // data + ].toJS, + null); + } + + @override + Future resetBones(FilamentEntity entity) { + // TODO: implement resetBones + throw UnimplementedError(); + } + + @override + Future reveal(FilamentEntity entity, String? meshName) { + // TODO: implement reveal + throw UnimplementedError(); + } + + @override + Future rotateEnd() { + // TODO: implement rotateEnd + throw UnimplementedError(); + } + + @override + Future rotateIbl(Matrix3 rotation) { + // TODO: implement rotateIbl + throw UnimplementedError(); + } + + @override + Future rotateStart(double x, double y) { + // TODO: implement rotateStart + throw UnimplementedError(); + } + + @override + Future rotateUpdate(double x, double y) { + // TODO: implement rotateUpdate + throw UnimplementedError(); + } + + @override + // TODO: implement scene + Scene get scene => throw UnimplementedError(); + + @override + Future setAnimationFrame( + FilamentEntity entity, int index, int animationFrame) { + // TODO: implement setAnimationFrame + throw UnimplementedError(); + } + + @override + Future setAntiAliasing(bool msaa, bool fxaa, bool taa) async { + _module.ccall( + "set_antialiasing", + "void", + ["void*".toJS, "bool".toJS, "bool".toJS, "bool".toJS].toJS, + [_viewer!, msaa.toJS, fxaa.toJS, taa.toJS].toJS, + null); + } + + @override + Future setBackgroundImage(String path, {bool fillHeight = false}) { + // TODO: implement setBackgroundImage + throw UnimplementedError(); + } + + @override + Future setBackgroundImagePosition(double x, double y, {bool clamp = false}) { + // TODO: implement setBackgroundImagePosition + throw UnimplementedError(); + } + + @override + Future setBloom(double bloom) { + // TODO: implement setBloom + throw UnimplementedError(); + } + + @override + Future setBoneTransform( + FilamentEntity entity, int boneIndex, Matrix4 transform, + {int skinIndex = 0}) { + // TODO: implement setBoneTransform + throw UnimplementedError(); + } + + @override + Future setCamera(FilamentEntity entity, String? name) { + // TODO: implement setCamera + throw UnimplementedError(); + } + + @override + Future setCameraCulling(double near, double far) { + // TODO: implement setCameraCulling + throw UnimplementedError(); + } + + @override + Future setCameraExposure( + double aperture, double shutterSpeed, double sensitivity) { + // TODO: implement setCameraExposure + throw UnimplementedError(); + } + + @override + Future setCameraFocalLength(double focalLength) { + // TODO: implement setCameraFocalLength + throw UnimplementedError(); + } + + @override + Future setCameraFocusDistance(double focusDistance) { + // TODO: implement setCameraFocusDistance + throw UnimplementedError(); + } + + @override + Future setCameraFov(double degrees, double width, double height) { + // TODO: implement setCameraFov + throw UnimplementedError(); + } + + @override + Future setCameraManipulatorOptions( + {ManipulatorMode mode = ManipulatorMode.ORBIT, + double orbitSpeedX = 0.01, + double orbitSpeedY = 0.01, + double zoomSpeed = 0.01}) { + // TODO: implement setCameraManipulatorOptions + throw UnimplementedError(); + } + + @override + Future setCameraModelMatrix(List matrix) { + // TODO: implement setCameraModelMatrix + throw UnimplementedError(); + } + + @override + Future setCameraPosition(double x, double y, double z) async { + _module.ccall( + "set_camera_position", + "void", + ["void*".toJS, "float".toJS, "float".toJS, "float".toJS].toJS, + [_viewer!, x.toJS, y.toJS, z.toJS].toJS, + null); + } + + @override + Future setCameraRotation(Quaternion quaternion) async { + _module.ccall( + "set_camera_rotation", + "void", + ["void*".toJS, "float".toJS, "float".toJS, "float".toJS, "float".toJS] + .toJS, + [ + _viewer!, + quaternion.w.toJS, + quaternion.x.toJS, + quaternion.y.toJS, + quaternion.z.toJS + ].toJS, + null); + } + + @override + Future setFrameRate(int framerate) { + // TODO: implement setFrameRate + throw UnimplementedError(); + } + + @override + Future setMainCamera() { + // TODO: implement setMainCamera + throw UnimplementedError(); + } + + @override + Future setMaterialColor(FilamentEntity entity, String meshName, + int materialIndex, double r, double g, double b, double a) { + // TODO: implement setMaterialColor + throw UnimplementedError(); + } + + // @override + // Future setMorphAnimationData( + // FilamentEntity entity, MorphAnimationData animation, + // {List? targetMeshNames}) async { + // final morphTargetNames = await getMorphTargetNames(entity, entity); + + // // We need to create a JS array for the morph indices and morph data + // final numFrames = animation.numFrames; + // final numMorphTargets = morphTargetNames.length; + // final numBytes = numFrames * numMorphTargets * 4; + // final floatPtr = _module._malloc(numBytes); + // final morphIndicesPtr = _module._malloc(numFrames * 4); + + // // Extract the morph data for the target morph targets + // final morphData = animation.extract(morphTargets: targetMeshNames); + + // // Create a list of morph indices based on the target morph targets + // final morphIndices = targetMeshNames != null + // ? animation._getMorphTargetIndices(targetMeshNames) + // : List.generate(morphTargetNames.length, (i) => i); + // final morphIndicesList = td.Int32List.fromList(morphIndices); + + // // Set the morph data and indices into the JS arrays + // _module.writeArrayToMemory(morphData.buffer.asUint8List(morphData.offsetInBytes).toJS, floatPtr); + // _module.writeArrayToMemory(morphIndicesList.buffer.asUint8List(morphData.offsetInBytes).toJS, morphIndicesPtr); + + // // Set the morph animation data + // _module.ccall( + // "set_morph_animation", + // "bool", + // [ + // "void*".toJS, + // "int".toJS, + // "float*".toJS, + // "int*".toJS, + // "int".toJS, + // "int".toJS, + // "float".toJS + // ].toJS, + // [ + // _sceneManager!, + // entity.toJS, + // floatPtr, + // morphIndicesPtr, + // numMorphTargets.toJS, + // numFrames.toJS, + // animation.frameLengthInMs.toJS + // ].toJS, + // null); + + // // Free the memory allocated for the JS arrays + // _module._free(floatPtr); + // _module._free(morphIndicesPtr); + // } + + @override + Future setMorphAnimationData( + FilamentEntity entity, MorphAnimationData animation, + {List? targetMeshNames, bool useNextEntity = false}) async { + var meshNames = await getChildEntityNames(entity, renderableOnly: false); + if (targetMeshNames != null) { + for (final targetMeshName in targetMeshNames) { + if (!meshNames.contains(targetMeshName)) { + throw Exception( + "Error: mesh ${targetMeshName} does not exist under the specified entity. Available meshes : ${meshNames}"); + } + } + } + + var meshEntities = await getChildEntities(entity, false); + + // Entities are not guaranteed to have the same morph targets (or share the same order), + // either from each other, or from those specified in [animation]. + // We therefore set morph targets separately for each mesh. + // For each mesh, allocate enough memory to hold FxM 32-bit floats + // (where F is the number of Frames, and M is the number of morph targets in the mesh). + // we call [extract] on [animation] to return frame data only for morph targets that present in both the mesh and the animation + for (int i = 0; i < meshNames.length; i++) { + var meshName = meshNames[i]; + var meshEntity = meshEntities[i]; + + if (targetMeshNames?.contains(meshName) == false) { + // print("Skipping $meshName, not contained in target"); + continue; + } + + if (useNextEntity) meshEntity += 1; + + var meshMorphTargets = await getMorphTargetNames(entity, meshEntity); + + print("Got mesh morph targets ${meshMorphTargets}"); + + var intersection = animation.morphTargets + .toSet() + .intersection(meshMorphTargets.toSet()) + .toList(); + + if (intersection.isEmpty) { + throw Exception( + """No morph targets specified in animation are present on mesh $meshName. + If you weren't intending to animate every mesh, specify [targetMeshNames] when invoking this method. + Animation morph targets: ${animation.morphTargets}\n + Mesh morph targets ${meshMorphTargets} + Child meshes: ${meshNames}"""); + } + + var indices = + intersection.map((m) => meshMorphTargets.indexOf(m)).toList(); + + var frameData = animation.extract(morphTargets: intersection); + + assert(frameData.length == animation.numFrames * intersection.length); + + // Allocate memory in WASM for the morph data + var dataPtr = _module._malloc(frameData.length * 4) as JSNumber; + + // Create a Float32List to copy the morph data to + var dataList = td.Float32List.fromList(frameData); + + // Copy the morph data to WASM + _module.writeArrayToMemory( + dataList.buffer.asUint8List(dataList.offsetInBytes).toJS, dataPtr); + + // Allocate memory in WASM for the morph indices + var idxPtr = _module._malloc(indices.length * 4) as JSNumber; + + // Create an Int32List to copy the morph indices to + var idxList = td.Int32List.fromList(indices); + + // Copy the morph indices to WASM + _module.writeArrayToMemory( + idxList.buffer.asUint8List(idxList.offsetInBytes).toJS, idxPtr); + + var result = _module.ccall( + "set_morph_animation", + "bool", + [ + "void*".toJS, + "int".toJS, + "float*".toJS, + "int*".toJS, + "int".toJS, + "int".toJS, + "float".toJS + ].toJS, + [ + _sceneManager!, + meshEntity.toJS, + dataPtr, + idxPtr, + indices.length.toJS, + animation.numFrames.toJS, + animation.frameLengthInMs.toJS + ].toJS, + null) as JSBoolean; + + // Free the memory allocated in WASM + _module._free(dataPtr); + _module._free(idxPtr); + + if (!result.toDart) { + throw Exception("Failed to set morph animation data for ${meshName}"); + } + } + } + + @override + Future setMorphTargetWeights(FilamentEntity entity, List weights) { + // TODO: implement setMorphTargetWeights + throw UnimplementedError(); + } + + @override + Future setParent(FilamentEntity child, FilamentEntity parent) { + // TODO: implement setParent + throw UnimplementedError(); + } + + @override + Future setPosition( + FilamentEntity entity, double x, double y, double z) async { + _module.ccall( + "set_position", + "void", + ["void*".toJS, "int".toJS, "float".toJS, "float".toJS, "float".toJS] + .toJS, + [_sceneManager!, entity.toJS, x.toJS, y.toJS, z.toJS].toJS, + null); + } + + @override + Future setPostProcessing(bool enabled) async { + _module.ccall("set_post_processing", "void", + ["void*".toJS, "bool".toJS].toJS, [_viewer!, enabled.toJS].toJS, null); + } + + @override + Future setPriority(FilamentEntity entityId, int priority) { + // TODO: implement setPriority + throw UnimplementedError(); + } + + @override + Future setRecording(bool recording) { + // TODO: implement setRecording + throw UnimplementedError(); + } + + @override + Future setRecordingOutputDirectory(String outputDirectory) { + // TODO: implement setRecordingOutputDirectory + throw UnimplementedError(); + } + + @override + Future setRendering(bool render) { + // TODO: implement setRendering + throw UnimplementedError(); + } + + @override + Future setRotation( + FilamentEntity entity, double rads, double x, double y, double z) async { + var quaternion = Quaternion.axisAngle(Vector3(x, y, z), rads); + _module.ccall( + "set_rotation", + "void", + [ + "void*".toJS, + "int".toJS, + "float".toJS, + "float".toJS, + "float".toJS, + "float".toJS + ].toJS, + [ + _sceneManager!, + entity.toJS, + quaternion.radians.toJS, + quaternion.x.toJS, + quaternion.y.toJS, + quaternion.z.toJS, + quaternion.w.toJS + ].toJS, + null); + } + + @override + Future setRotationQuat(FilamentEntity entity, Quaternion rotation) async { + _module.ccall( + "set_rotation", + "void", + [ + "void*".toJS, + "int".toJS, + "float".toJS, + "float".toJS, + "float".toJS, + "float".toJS + ].toJS, + [ + _sceneManager!, + entity.toJS, + rotation.radians.toJS, + rotation.x.toJS, + rotation.y.toJS, + rotation.z.toJS, + rotation.w.toJS + ].toJS, + null); + } + + @override + Future setScale(FilamentEntity entity, double scale) { + // TODO: implement setScale + throw UnimplementedError(); + } + + @override + Future setToneMapping(ToneMapper mapper) { + // TODO: implement setToneMapping + throw UnimplementedError(); + } + + @override + Future setTransform(FilamentEntity entity, Matrix4 transform) { + // TODO: implement setTransform + throw UnimplementedError(); + } + + @override + Future setViewFrustumCulling(bool enabled) { + // TODO: implement setViewFrustumCulling + throw UnimplementedError(); + } + + @override + Future stopAnimation(FilamentEntity entity, int animationIndex) { + // TODO: implement stopAnimation + throw UnimplementedError(); + } + + @override + Future stopAnimationByName(FilamentEntity entity, String name) { + // TODO: implement stopAnimationByName + throw UnimplementedError(); + } + + @override + Future testCollisions(FilamentEntity entity) { + // TODO: implement testCollisions + throw UnimplementedError(); + } + + @override + Future transformToUnitCube(FilamentEntity entity) { + // TODO: implement transformToUnitCube + throw UnimplementedError(); + } + + @override + Future updateBoneMatrices(FilamentEntity entity) { + // TODO: implement updateBoneMatrices + throw UnimplementedError(); + } + + @override + Future zoomBegin() { + // TODO: implement zoomBegin + throw UnimplementedError(); + } + + @override + Future zoomEnd() { + // TODO: implement zoomEnd + throw UnimplementedError(); + } + + @override + Future zoomUpdate(double x, double y, double z) { + // TODO: implement zoomUpdate + throw UnimplementedError(); + } +} diff --git a/dart_filament/lib/dart_filament/compatibility/web/interop/dart_filament_js_extension_type.dart b/dart_filament/lib/dart_filament/compatibility/web/interop/shims/abstract_filament_viewer_js_shim.dart similarity index 97% rename from dart_filament/lib/dart_filament/compatibility/web/interop/dart_filament_js_extension_type.dart rename to dart_filament/lib/dart_filament/compatibility/web/interop/shims/abstract_filament_viewer_js_shim.dart index e7f265e9..2332477b 100644 --- a/dart_filament/lib/dart_filament/compatibility/web/interop/dart_filament_js_extension_type.dart +++ b/dart_filament/lib/dart_filament/compatibility/web/interop/shims/abstract_filament_viewer_js_shim.dart @@ -2,10 +2,15 @@ library flutter_filament_js; import 'dart:js_interop'; - import 'package:dart_filament/dart_filament/entities/filament_entity.dart'; +import 'package:dart_filament/dart_filament/abstract_filament_viewer.dart'; -extension type DartFilamentJSShim(JSObject _) implements JSObject { +/// +/// 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 get initialized; @@ -401,3 +406,4 @@ extension type DartFilamentJSShim(JSObject _) implements JSObject { external JSPromise setBoneTransform( FilamentEntity entity, int boneIndex, JSArray transform, int skinIndex); } + diff --git a/dart_filament/lib/dart_filament/compatibility/web/interop/shims/dart_filament_api_js_shim.dart b/dart_filament/lib/dart_filament/compatibility/web/interop/shims/dart_filament_api_js_shim.dart new file mode 100644 index 00000000..718327fd --- /dev/null +++ b/dart_filament/lib/dart_filament/compatibility/web/interop/shims/dart_filament_api_js_shim.dart @@ -0,0 +1,403 @@ +@JS() +library flutter_filament_js; + +import 'dart:js_interop'; +import 'package:dart_filament/dart_filament/entities/filament_entity.dart'; +import 'package:dart_filament/dart_filament/abstract_filament_viewer.dart'; + +/// +/// An extension type on [JSObject] that represents a +/// Javascript shim implementation for the [AbstractFilamentViewer] interface. +/// +extension type DartFilamentAPIJSShim(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 rotationMatrix); + + @JS('removeIbl') + external JSPromise removeIbl(); + + @JS('addLight') + external JSPromise 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 loadGlb(String path, int numInstances); + + @JS('createInstance') + external JSPromise createInstance(FilamentEntity entity); + + @JS('getInstanceCount') + external JSPromise getInstanceCount(FilamentEntity entity); + + @JS('getInstances') + external JSPromise> getInstances(FilamentEntity entity); + + @JS('loadGltf') + external JSPromise 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 weights); + + @JS('getMorphTargetNames') + external JSPromise> getMorphTargetNames( + FilamentEntity entity, FilamentEntity childEntity); + + @JS('getBoneNames') + external JSPromise> getBoneNames( + FilamentEntity entity, int skinIndex); + + @JS('getAnimationNames') + external JSPromise> getAnimationNames( + FilamentEntity entity); + + @JS('getAnimationDuration') + external JSPromise getAnimationDuration( + FilamentEntity entity, int animationIndex); + + @JS('setMorphAnimationData') + external JSPromise setMorphAnimationData( + FilamentEntity entity, + JSArray> animation, + JSArray morphTargets, + JSArray? targetMeshNames, + double frameLengthInMs); + + @JS('resetBones') + external JSPromise resetBones(FilamentEntity entity); + + @JS('addBoneAnimation') + external JSPromise addBoneAnimation( + FilamentEntity entity, + JSArray bones, + JSArray>> 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 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 getCameraCullingNear(); + + @JS('getCameraCullingFar') + external JSPromise getCameraCullingFar(); + + @JS('setCameraFocusDistance') + external JSPromise setCameraFocusDistance(double focusDistance); + + @JS('getCameraPosition') + external JSPromise> getCameraPosition(); + + @JS('getCameraModelMatrix') + external JSPromise> getCameraModelMatrix(); + + @JS('getCameraViewMatrix') + external JSPromise> getCameraViewMatrix(); + + @JS('getCameraProjectionMatrix') + external JSPromise> getCameraProjectionMatrix(); + + @JS('getCameraCullingProjectionMatrix') + external JSPromise> getCameraCullingProjectionMatrix(); + + @JS('getCameraFrustum') + external JSPromise getCameraFrustum(); + + @JS('setCameraPosition') + external JSPromise setCameraPosition(double x, double y, double z); + + @JS('getCameraRotation') + external JSPromise> 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 quaternion); + + @JS('setCameraModelMatrix') + external JSPromise setCameraModelMatrix(JSArray 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 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 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> getChildEntities( + FilamentEntity parent, bool renderableOnly); + + @JS('getChildEntity') + external JSPromise getChildEntity( + FilamentEntity parent, String childName); + + @JS('getChildEntityNames') + external JSPromise> 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 createGeometry(JSArray vertices, + JSArray indices, String? materialPath, int primitiveType); + + @JS('setParent') + external JSPromise setParent(FilamentEntity child, FilamentEntity parent); + + @JS('getParent') + external JSPromise getParent(FilamentEntity child); + + @JS('getParent') + external JSPromise 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> getLocalTransform( + FilamentEntity entity); + + @JS('getWorldTransform') + external JSPromise> getWorldTransform( + FilamentEntity entity); + + @JS('updateBoneMatrices') + external JSPromise updateBoneMatrices(FilamentEntity entity); + + @JS('setTransform') + external JSPromise setTransform( + FilamentEntity entity, JSArray transform); + + @JS('setBoneTransform') + external JSPromise setBoneTransform( + FilamentEntity entity, int boneIndex, JSArray transform, int skinIndex); +} +