Compare commits

..

8 Commits

Author SHA1 Message Date
Nick Fisher
f8c6c33269 chore(release): publish packages
- thermion_dart@0.1.2
 - thermion_flutter_ffi@0.1.0+10
 - thermion_flutter_web@0.0.2
 - thermion_flutter@0.1.1+11
 - thermion_flutter_platform_interface@0.1.0+10
2024-07-11 19:00:36 +08:00
Nick Fisher
6e5627f90e Merge pull request #62 from nmfisher/bugfix/windows-native-assets
Native-assets build error on Windows when targeting Android
2024-07-11 20:59:37 +10:00
Nick Fisher
1921007fc6 fix: manually remove leading slash for compiler path on Windows when building for Android 2024-07-11 20:51:48 +10:00
Nick Fisher
7a362411b1 Merge pull request #56 from payl-ampa/patch-2
docs: correct link to windows page in quickstart page
2024-07-05 10:26:21 +10:00
Paul Ampadu
e95dfe4671 Update quickstart.mdx 2024-07-04 10:59:27 -07:00
Nick Fisher
e7b79bb6b9 fix: web/JS bool checks need to compare to int 2024-07-04 21:49:13 +08:00
Nick Fisher
59bc309f5b feat: allow passing assetPathPrefix to ThermionViewerWasm to account for Flutter build asset paths 2024-07-04 16:29:56 +08:00
Nick Fisher
d2fb40f317 fix: shadow JS<->WASM bridge methods 2024-07-04 16:29:08 +08:00
233 changed files with 151129 additions and 54934 deletions

5
.gitattributes vendored
View File

@@ -36,7 +36,7 @@ thermion_dart/native/lib/macos/debug/libmatlang.a filter=lfs diff=lfs merge=lfs
thermion_dart/native/lib/macos/debug/libibl.a filter=lfs diff=lfs merge=lfs -text
thermion_dart/native/lib/macos/debug/libmikktspace.a filter=lfs diff=lfs merge=lfs -text
thermion_dart/native/lib/macos/libmatdbg_combined.a filter=lfs diff=lfs merge=lfs -text
thermion_dart/native/lib/macos/swift/ThermionTexture.h filter=lfs diff=lfs merge=lfs -text
thermion_dart/native/lib/macos/swift/ThermionDartTexture.h filter=lfs diff=lfs merge=lfs -text
thermion_dart/native/lib/macos/debug/libfilamat_combined.a filter=lfs diff=lfs merge=lfs -text
thermion_dart/native/lib/macos/debug/libfilament.a filter=lfs diff=lfs merge=lfs -text
thermion_dart/native/lib/macos/libibl.a filter=lfs diff=lfs merge=lfs -text
@@ -153,7 +153,10 @@ thermion_dart/native/lib/ios/libibl-lite.a filter=lfs diff=lfs merge=lfs -text
thermion_dart/native/lib/ios/libimageio.a filter=lfs diff=lfs merge=lfs -text
materials/Makefile filter=lfs diff=lfs merge=lfs -text
materials/gizmo.filamat filter=lfs diff=lfs merge=lfs -text
materials/gizmo.mat filter=lfs diff=lfs merge=lfs -text
materials/image.filamat filter=lfs diff=lfs merge=lfs -text
materials/image.mat filter=lfs diff=lfs merge=lfs -text
materials/unlit_fade.mat filter=lfs diff=lfs merge=lfs -text
materials/unlit_opaque.mat filter=lfs diff=lfs merge=lfs -text
thermion_dart/native/lib/android/arm64-v8a/libktxreader.a filter=lfs diff=lfs merge=lfs -text
thermion_dart/native/lib/android/armeabi-v7a/libcamutils.a filter=lfs diff=lfs merge=lfs -text

View File

@@ -3,376 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## 2024-10-02
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`thermion_dart` - `v0.2.1-dev.0.0.3`](#thermion_dart---v021-dev003)
- [`thermion_flutter_web` - `v0.1.0+4`](#thermion_flutter_web---v0104)
- [`thermion_flutter` - `v0.2.1-dev.2`](#thermion_flutter---v021-dev2)
- [`thermion_flutter_platform_interface` - `v0.2.1-dev.2`](#thermion_flutter_platform_interface---v021-dev2)
- [`thermion_flutter_ffi` - `v0.2.1-dev.2`](#thermion_flutter_ffi---v021-dev2)
Packages with dependency updates only:
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
- `thermion_flutter_web` - `v0.1.0+4`
- `thermion_flutter` - `v0.2.1-dev.2`
- `thermion_flutter_platform_interface` - `v0.2.1-dev.2`
- `thermion_flutter_ffi` - `v0.2.1-dev.2`
---
#### `thermion_dart` - `v0.2.1-dev.0.0.3`
- Bump "thermion_dart" to `0.2.1-dev.0.0.3`.
## 2024-10-02
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`thermion_dart` - `v0.2.1-dev.0.0.2`](#thermion_dart---v021-dev002)
- [`thermion_flutter_web` - `v0.1.0+3`](#thermion_flutter_web---v0103)
- [`thermion_flutter_ffi` - `v0.2.1-dev.1`](#thermion_flutter_ffi---v021-dev1)
- [`thermion_flutter` - `v0.2.1-dev.1`](#thermion_flutter---v021-dev1)
- [`thermion_flutter_platform_interface` - `v0.2.1-dev.1`](#thermion_flutter_platform_interface---v021-dev1)
Packages with dependency updates only:
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
- `thermion_flutter_web` - `v0.1.0+3`
- `thermion_flutter_ffi` - `v0.2.1-dev.1`
- `thermion_flutter` - `v0.2.1-dev.1`
- `thermion_flutter_platform_interface` - `v0.2.1-dev.1`
---
#### `thermion_dart` - `v0.2.1-dev.0.0.2`
## 2024-10-02
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`thermion_dart` - `v0.2.1-dev.0.0.1`](#thermion_dart---v021-dev001)
- [`thermion_flutter_web` - `v0.1.0+2`](#thermion_flutter_web---v0102)
- [`thermion_flutter` - `v0.2.1-dev.0`](#thermion_flutter---v021-dev0)
- [`thermion_flutter_platform_interface` - `v0.2.1-dev.0`](#thermion_flutter_platform_interface---v021-dev0)
- [`thermion_flutter_ffi` - `v0.2.1-dev.0`](#thermion_flutter_ffi---v021-dev0)
Packages with dependency updates only:
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
- `thermion_flutter_web` - `v0.1.0+2`
- `thermion_flutter` - `v0.2.1-dev.0`
- `thermion_flutter_platform_interface` - `v0.2.1-dev.0`
- `thermion_flutter_ffi` - `v0.2.1-dev.0`
---
#### `thermion_dart` - `v0.2.1-dev.0.0.1`
## 2024-10-02
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`thermion_dart` - `v0.2.1-dev.0.0.0`](#thermion_dart---v021-dev000)
- [`thermion_flutter` - `v0.2.1-dev.0.0.0`](#thermion_flutter---v021-dev000)
- [`thermion_flutter_ffi` - `v0.2.1-dev.0.0.0`](#thermion_flutter_ffi---v021-dev000)
- [`thermion_flutter_platform_interface` - `v0.2.1-dev.0.0.0`](#thermion_flutter_platform_interface---v021-dev000)
- [`thermion_flutter_web` - `v0.1.0+1`](#thermion_flutter_web---v0101)
Packages with dependency updates only:
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
- `thermion_flutter_web` - `v0.1.0+1`
---
#### `thermion_dart` - `v0.2.1-dev.0.0.0`
- y
#### `thermion_flutter` - `v0.2.1-dev.0.0.0`
- y
#### `thermion_flutter_ffi` - `v0.2.1-dev.0.0.0`
- y
#### `thermion_flutter_platform_interface` - `v0.2.1-dev.0.0.0`
- y
## 2024-10-02
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`thermion_dart` - `v0.2.0`](#thermion_dart---v020)
- [`thermion_flutter` - `v0.2.0`](#thermion_flutter---v020)
- [`thermion_flutter_ffi` - `v0.2.0`](#thermion_flutter_ffi---v020)
- [`thermion_flutter_platform_interface` - `v0.2.0`](#thermion_flutter_platform_interface---v020)
- [`thermion_flutter_web` - `v0.1.0`](#thermion_flutter_web---v010)
Packages graduated to a stable release (see pre-releases prior to the stable version for changelog entries):
- `thermion_dart` - `v0.2.0`
- `thermion_flutter` - `v0.2.0`
- `thermion_flutter_ffi` - `v0.2.0`
- `thermion_flutter_platform_interface` - `v0.2.0`
- `thermion_flutter_web` - `v0.1.0`
---
#### `thermion_dart` - `v0.2.0`
#### `thermion_flutter` - `v0.2.0`
#### `thermion_flutter_ffi` - `v0.2.0`
#### `thermion_flutter_platform_interface` - `v0.2.0`
#### `thermion_flutter_web` - `v0.1.0`
## 2024-10-02
### Changes
---
Packages with breaking changes:
- [`thermion_dart` - `v0.2.0-dev.8.0.0`](#thermion_dart---v020-dev800)
- [`thermion_flutter_ffi` - `v0.2.0-dev.8.0.0`](#thermion_flutter_ffi---v020-dev800)
Packages with other changes:
- [`thermion_flutter` - `v0.2.0-dev.8.0.0`](#thermion_flutter---v020-dev800)
- [`thermion_flutter_platform_interface` - `v0.2.0-dev.8.0.0`](#thermion_flutter_platform_interface---v020-dev800)
- [`thermion_flutter_web` - `v0.1.0-dev.8.0.0`](#thermion_flutter_web---v010-dev800)
Packages with dependency updates only:
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
- `thermion_flutter_web` - `v0.1.0-dev.8.0.0`
---
#### `thermion_dart` - `v0.2.0-dev.8.0.0`
- **REFACTOR**: continual refactor to support multiple render targets.
- **FEAT**: camera and resizing improvements.
- **FEAT**: support multiple ThermionWidget on Android.
- **FEAT**: use imported texture on iOS.
- **FEAT**: working implementation of multiple widgets on macos.
- **FEAT**: more work on multiple views/swapchains.
- **FEAT**: add setParameterFloat2 method.
- **FEAT**: add setParameterFloat2 method.
- **FEAT**: add uvScale to unlit material.
- **FEAT**: add ThirdPersonCameraDelegate.
- **FEAT**: set camera model matrix directly.
- **FEAT**: expose more camera methods.
- **BREAKING** **REFACTOR**: refactor to support multiple Views/Render Targets.
- **BREAKING** **REFACTOR**: remove RenderThread methods no longer needed.
- **BREAKING** **FIX**: replace queuePosition/Rotation with queueTransforms.
- **BREAKING** **FIX**: replace queuePosition/Rotation with queueTransforms.
- **BREAKING** **FIX**: replace queuePosition/Rotation with queueTransforms.
- **BREAKING** **FIX**: replace queuePosition/Rotation with queueTransforms.
- **BREAKING** **FEAT**: big refactor to support multiple swapchains.
- **BREAKING** **FEAT**: set baseColorIndex to -1 by default in unlit materialss.
#### `thermion_flutter_ffi` - `v0.2.0-dev.8.0.0`
- **REFACTOR**: continual refactor to support multiple render targets.
- **FEAT**: support multiple ThermionWidget on Android.
- **FEAT**: use imported texture on iOS.
- **FEAT**: working implementation of multiple widgets on macos.
- **BREAKING** **REFACTOR**: refactor to support multiple Views/Render Targets.
- **BREAKING** **FEAT**: big refactor to support multiple swapchains.
#### `thermion_flutter` - `v0.2.0-dev.8.0.0`
- **REFACTOR**: continual refactor to support multiple render targets.
- **FEAT**: camera and resizing improvements.
- **FEAT**: support multiple ThermionWidget on Android.
- **FEAT**: use imported texture on iOS.
- **FEAT**: working implementation of multiple widgets on macos.
- **FEAT**: add rendering check to ThermionWidget ticker.
#### `thermion_flutter_platform_interface` - `v0.2.0-dev.8.0.0`
- **REFACTOR**: continual refactor to support multiple render targets.
- **FEAT**: support multiple ThermionWidget on Android.
- **FEAT**: working implementation of multiple widgets on macos.
# Change Log
v0.2.0
- **BREAKING** Dart SDK 3.6.0 required
- **BREAKING** Libraries have been restructured so you should only need to import `package:thermion_dart/thermion_dart.dart`, `package:thermion_flutter/thermion_flutter.dart`
- **BREAKING** The former GestureDetector widgets and EntityControllerMouseWidget/EntityTransformController have been removed and replaced with ThermionListenerWidget. that accepts an InputHandler.
- **BREAKING** The former debugging widgets and Scene class have been removed.
- **REFACTOR** The creation of the main camera has been refactored; the default projection & near/far planes should not have changed, but pay close attention.
- **REFACTOR**: add methods for create/destroy material instance, add priority/layer to load_glb_from_buffer.
- **FEAT**: Translation gizmo, stencil highlight & overlays.
- **FEAT**: new setLightDirection and setLightPosition.
- **FEAT**: move HighlightOverlay to nested class, move createGeometry to SceneManager, add queueRelativePositionUpdateFromViewportVector.
- **FEAT**: move createGeometry to SceneManager, add queueRelativePositionUpdateFromViewportVector and removeStencilHighlight.
- **FEAT**: add setGizmoVisibility/pickGizmo methods to ThermionViewer.
- **FEAT**: remove gizmo view references, exclude gizmo entities from picking, add createIbl.
- **FEAT**: set stencil highlight on gizmo attach.
- **FEAT**: add getAncestor method.
- **FEAT**: expose API methods for create_ibl, pick/set gizmo visibility.
- **FEAT**: create transparent overlay for gizmo for easier picking.
- **FEAT**: rescale gizmo based on distance from camera.
- **FEAT**: rescale gizmo based on distance from camera.
- **FEAT**: add getAncestor method.
- **FEAT**: add startOffset parameter to gltf playAnimation.
- **FEAT**: layers, grid.
- **FEAT**: layers, grid.
- **FEAT**: ignore grid overlay and gizmo center when picking, implement highlighting.
- **FEAT**: SceneManager updates (setLayer, add grid, queueRelativePositionUpdateWorld.
- **FEAT**: expose set_layer_enabled, get_camera_fov and queue_relative_position_updateg_world_axis to ThermionDartApi.h.
- **FEAT**: add getCameraFov to FilamentViewer.
- **FEAT**: add new grid overlay files to web CmakeLists.
- **FEAT**: re-implement (native) Gizmo class, expose preserveScaling parameter for setParent, add methods for getting viewport bounding box from renderable entity.
- **FEAT**: expose setLayerEnabled, viewportDimensions and getCameraFov on ThermionView.
- **FEAT**: download WASM module directly on web (no need to embed in index.html any more) and expose updateViewportAndCameraProjection.
- **FEAT**: add capture() function and expose viewportDimensions on ThermionViewer (allows easier saving of captured images to PNG).
- **FEAT**: (web) allow table growth in emscripten module for passing C-style callback function pointers.
- **FEAT**: (web) add capture() method and missing camera navigation controls.
- **FEAT**: createIbl.
- **BREAKING** **FEAT**: (web) (flutter) create canvas when createViewer is called (no longer need to manually add canvas element to web HTML).
- **BREAKING** **FEAT**: update web/http dependencies.
- **FIX**: (flutter) pass ThermionFlutterOptions to ThermionWidget, use dpr for resizeTexture, delete unnecessary TransparencyPainter class.
- **FIX**: (flutter/web) use window.devicePixelRatio for viewport.
- **FIX**: (flutter) desktop gesture detector changes for new Gizmo methods.
- **FEAT**: allow passing null options to ThermionWidget.
- **FEAT**: (flutter) (web) if importCanvasAsWidget is false, render transparency.
- **FEAT**: add createViewerWithOptions to ThermionFlutterPlugin and mark createViewer as deprecated.
- **FEAT**: add createViewerWithOptions to ThermionFlutterPlugin and mark createViewer as deprecated.
- **FEAT**: highlight gizmo on hover.
- **BREAKING** **FIX**: (flutter) pass pixelRatio to createTexture.
- **BREAKING** **FIX**: (flutter) pass pixelRatio to createTexture.
- **BREAKING** **FEAT**: (web) (flutter) create canvas when createViewer is called (no longer need to manually add canvas element to web HTML).
- **BREAKING** **FEAT**: resize canvas on web.
- **BREAKING** **CHORE**: rename controller to viewer in gesture detector widgets.
- **FEAT**: (flutter) move DPR calculation to resizeTexture and add createViewerWithOptions method to ThermionFlutterFFI.
- **BREAKING** **FIX**: (flutter) pass pixelRatio to createTexture.
- **FEAT**: add createViewerWithOptions to ThermionFlutterPlugin and mark createViewer as deprecated.
- **FEAT**: add ThermionFlutterOptions classes, rename interface parameter for offsetTop and ensure pixelRatio is passed to resizeTexture.
- **BREAKING** **FIX**: (flutter) pass pixelRatio to createTexture.
- **FIX**: (flutter/web) use window.devicePixelRatio for viewport.
- **FEAT**: (flutter) (web) use options to determine whether to create canvas, and set fixed position + offset.
- **FEAT**: add ThermionFlutterOptions classes, rename interface parameter for offsetTop and ensure pixelRatio is passed to resizeTexture.
- **BREAKING** **FEAT**: (flutter) (web) upgrade package:web dep to 1.0.0.
- **BREAKING** **FEAT**: (web) (flutter) create canvas when createViewer is called (no longer need to manually add canvas element to web HTML).
- **BREAKING** **FEAT**: resize canvas on web.
## v0.1.3
- **FIX**: manually remove leading slash for compiler path on Windows when building for Android.
- **FIX**: web/JS bool checks need to compare to int.
- **FIX**: shadow JS<->WASM bridge methods.
- **FIX**: manually remove leading slash for compiler path on Windows when building for Android.
- **FIX**: web/JS bool checks need to compare to int.
- **FIX**: shadow JS<->WASM bridge methods.
- **FEAT**: add clearMorphAnimationData function.
- **FEAT**: allow passing assetPathPrefix to ThermionViewerWasm to account for Flutter build asset paths.
- **FEAT**: allow passing assetPathPrefix to ThermionViewerWasm to account for Flutter build asset paths.
#### `thermion_flutter_ffi` - `v0.1.0+12`
- **FIX**: add logging dependency.
- **FIX**: web/JS bool checks need to compare to int.
- **FIX**: add logging dependency.
- **FIX**: web/JS bool checks need to compare to int.
#### `thermion_flutter_web` - `v0.0.3`
- **FEAT**: allow passing assetPathPrefix to ThermionViewerWasm to account for Flutter build asset paths.
- **FEAT**: allow passing assetPathPrefix to ThermionViewerWasm to account for Flutter build asset paths.
## 2024-07-11
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`thermion_flutter_ffi` - `v0.1.0+11`](#thermion_flutter_ffi---v01011)
- [`thermion_flutter` - `v0.1.1+12`](#thermion_flutter---v01112)
Packages with dependency updates only:
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
- `thermion_flutter` - `v0.1.1+12`
---
#### `thermion_flutter_ffi` - `v0.1.0+11`
- **FIX**: add logging dependency.
## 2024-07-11
### Changes

View File

@@ -12,7 +12,6 @@ flutter-example-web: dart-web-clean dart-web
flutter-example-macos:
cd thermion_flutter_federated/thermion_flutter/example/web && flutter run -d macos
swift-bindings:
swiftc -c thermion_flutter/thermion_flutter/macos/Classes/ThermionTexture.swift -module-name swift_module -emit-objc-header-path thermion_dart/native/include/generated/ThermionTextureSwiftObjCAPI.h -emit-library -o thermion_dart/test/libThermionTextureSwift.dylib
cd thermion_dart/ && dart --enable-experiment=native-assets run ffigen --config ffigen/swift.yaml
bindings:
cd thermion_dart/ && dart --enable-experiment=native-assets run ffigen --config ffigen/native.yaml
@@ -23,13 +22,10 @@ bindings:
#
materials: FORCE
@echo "Using Filament build from ${FILAMENT_PATH}"
@for material in unlit image gizmo grid; do \
${FILAMENT_PATH}/matc -a opengl -a metal -o materials/$$material.filamat materials/$$material.mat; \
$(FILAMENT_PATH)/resgen -c -p $$material -x thermion_dart/native/include/material/ materials/$$material.filamat; \
echo '#include "'$$material'.h"' | cat - thermion_dart/native/include/material/$$material.c > thermion_dart/native/include/material/$$material.c.new; \
mv thermion_dart/native/include/material/$$material.c.new thermion_dart/native/include/material/$$material.c; \
done
${FILAMENT_PATH}/matc -a opengl -a metal -o materials/image.filamat materials/image.mat
$(FILAMENT_PATH)/resgen -c -p image -x ios/include/material/ materials/image.filamat
$(FILAMENT_PATH)/matc -a opengl -a metal -o materials/gizmo.filamat materials/gizmo.mat
$(FILAMENT_PATH)/resgen -c -p gizmo -x ios/include/material/ materials/gizmo.filamat
#rm materials/*.filamat
FORCE: ;

View File

@@ -12,7 +12,7 @@
],
["Misc.", [
["Playground", "https://dartpad.thermion.dev"],
["Showcase", "/showcase"],
["Examples", "/examples"],
["Windows", "/windows"],
["Android", "/android"],
["Contributing", "/contributing"],

View File

@@ -6,13 +6,6 @@ A custom DartPad that lets you experiment with Thermion from your browser (curre
[![Screenshot of Thermion Dartpad](images/dartpad.thermion.dev_.png)](https://dartpad.thermion.dev)
## mixreel (Flutter/Web)
Create 3D worlds and translate to AI video.
[![Screenshot of the mixreeel app](images/ixlabs.app_app.png)](https://mixreel.ai)
## Nick Fisher
My personal website, where I create an interactive clone of myself with Avaturn & Cartesia (no Flutter, made with Thermion and the [Jaspr Dart UI framework](https://github.com/schultek/jaspr)).

3
materials/Makefile Normal file
View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:14a484a441dacbc49ce6b8b189a8900a7bb2ab2072731a3a493676aa046b5888
size 1009

View File

@@ -1,50 +1,3 @@
material {
name : Gizmo,
parameters : [
{
type : mat4,
name : transform,
precision : high
},
{
type : float4,
name : color,
precision : low
}
],
depthWrite : true,
depthCulling : false,
shadingModel : unlit,
blending: transparent,
variantFilter : [ skinning, shadowReceiver, vsm ],
culling: none,
instanced: false,
vertexDomain: object
}
vertex {
void materialVertex(inout MaterialVertexInputs material) {
// we want to ensure the gizmo has the same size (in screen-space), no matter the distance from the camera
// we do this by scaling the model-space vertex positions by the distance from the camera
vec4 modelSpace = getPosition();
vec4 worldSpace = getWorldFromModelMatrix() * modelSpace;
vec4 viewSpace = getViewFromWorldMatrix() * worldSpace;
float distanceFromCamera = length(viewSpace.xyz);
modelSpace.xyz *= (distanceFromCamera / 4.0f); // divide by 4 so that the size is equivalent to the camera being 4 world-space units away from the (unscaled) gizmo
worldSpace = getWorldFromModelMatrix() * modelSpace;
material.worldPosition = worldSpace;
//vec4 clipSpace = getClipFromWorldMatrix() * worldSpace;
//clipSpace.z = 0.99f;
//material.worldPosition = getWorldFromClipMatrix() * clipSpace;
}
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.baseColor = materialParams.color;
}
}
version https://git-lfs.github.com/spec/v1
oid sha256:3158461d081f058dcb9582ce19cc2daedc73abbe758ba5094c94df89028d8c4d
size 981

View File

@@ -1,53 +0,0 @@
material {
name : Grid,
parameters : [
{
type : float,
name : maxDistance
},
{
type : float3,
name : color
}
],
depthWrite : true,
depthCulling : false,
shadingModel : unlit,
blending: transparent,
variantFilter : [ skinning, shadowReceiver, vsm ],
culling: none,
instanced: false,
vertexDomain: object
}
vertex {
void materialVertex(inout MaterialVertexInputs material) {
material.worldPosition = getWorldFromModelMatrix() * getPosition();
}
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
// Convert world position to view space
float4 viewPos = getViewFromWorldMatrix() * float4(getWorldPosition(), 1.0);
// Calculate distance in view space (camera is at 0,0,0 in view space)
float distance = length(viewPos.xz);
// Discard fragment if it's too far from the camera
if (distance > materialParams.maxDistance) {
material.baseColor = float4(0.0);
} else {
material.baseColor = float4(materialParams.color, 1.0);
// Optional: fade out as we approach maxDistance
float fadeStart = materialParams.maxDistance * 0.8;
if (distance > fadeStart) {
float fade = 1.0 - (distance - fadeStart) / (materialParams.maxDistance - fadeStart);
material.baseColor.a = fade;
}
}
}
}

View File

@@ -1,52 +1,3 @@
material {
name : Image,
parameters : [
{
type : sampler2d,
name : image
},
{
type : mat4,
name : transform,
precision : high
},
{
type : float4,
name : backgroundColor
},
{
type : int,
name : showImage
}
],
variables : [
imageUV
],
vertexDomain : device,
depthWrite : false,
shadingModel : unlit,
variantFilter : [ skinning, shadowReceiver, vsm ],
culling: none
}
vertex {
void materialVertex(inout MaterialVertexInputs material) {
material.imageUV.st = getPosition().st * 0.5 + 0.5;
}
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
highp vec2 uv = (materialParams.transform * vec4(saturate(variable_imageUV.st), 1.0, 1.0)).st;
if (materialParams.showImage == 0 || uv.s > 1.0 || uv.s < 0.0 || uv.t < 0.0 || uv.t > 1.0) {
material.baseColor = materialParams.backgroundColor;
} else {
uv.t = 1.0 - uv.t;
vec4 color = max(texture(materialParams_image, uv.st), 0.0);
color.rgb *= color.a;
// Manual, pre-multiplied srcOver with opaque destination optimization
material.baseColor.rgb = color.rgb + materialParams.backgroundColor.rgb * (1.0 - color.a);
}
}
}
version https://git-lfs.github.com/spec/v1
oid sha256:273059c97f96c6848807914d937583864445b51d8e0f1cd98c3e4e0e4bd9f411
size 1451

View File

@@ -1,42 +0,0 @@
material {
name : unlit,
requires : [ uv0 ],
parameters : [
{
type : sampler2d,
name : baseColorMap
},
{
type : float4,
name : baseColorFactor
},
{
type : int,
name : baseColorIndex
},
{
type : float2,
name : uvScale
}
],
depthWrite : true,
depthCulling : true,
shadingModel : unlit,
blending: opaque,
culling: none,
instanced: false,
vertexDomain: object,
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.baseColor = materialParams.baseColorFactor;
if (materialParams.baseColorIndex > -1) {
highp float2 uv = getUV0();
uv.y = 1.0 - uv.y;
material.baseColor *= texture(materialParams_baseColorMap, uv);
}
}
}

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4abb9e737d20956cced366af6a54dd31ab17cc8d0d1773f02c57a5712760fe10
size 7385

View File

@@ -1,133 +1,3 @@
## 0.2.1-dev.0.0.3
- Bump "thermion_dart" to `0.2.1-dev.0.0.3`.
## 0.2.1-dev.0.0.2
## 0.2.1-dev.0.0.1
## 0.2.1-dev.0.0.0
- y
## 0.2.0
- Graduate package to a stable release. See pre-releases prior to this version for changelog entries.
## 0.2.0-dev.8.0.0
> Note: This release has breaking changes.
- **REFACTOR**: continual refactor to support multiple render targets.
- **FEAT**: camera and resizing improvements.
- **FEAT**: support multiple ThermionWidget on Android.
- **FEAT**: use imported texture on iOS.
- **FEAT**: working implementation of multiple widgets on macos.
- **FEAT**: more work on multiple views/swapchains.
- **FEAT**: add setParameterFloat2 method.
- **FEAT**: add setParameterFloat2 method.
- **FEAT**: add uvScale to unlit material.
- **FEAT**: add ThirdPersonCameraDelegate.
- **FEAT**: set camera model matrix directly.
- **FEAT**: expose more camera methods.
- **BREAKING** **REFACTOR**: refactor to support multiple Views/Render Targets.
- **BREAKING** **REFACTOR**: remove RenderThread methods no longer needed.
- **BREAKING** **FIX**: replace queuePosition/Rotation with queueTransforms.
- **BREAKING** **FIX**: replace queuePosition/Rotation with queueTransforms.
- **BREAKING** **FIX**: replace queuePosition/Rotation with queueTransforms.
- **BREAKING** **FIX**: replace queuePosition/Rotation with queueTransforms.
- **BREAKING** **FEAT**: big refactor to support multiple swapchains.
- **BREAKING** **FEAT**: set baseColorIndex to -1 by default in unlit materialss.
## 0.2.0-dev.7.0
> Note: This release has breaking changes.
- **BREAKING** **FIX**: fix min SDK for thermion_dart.
## 0.2.0-dev.6.0
> Note: This release has breaking changes.
- **BREAKING** **CHORE**: cleanup deleted export.
## 0.2.0-dev.5.0
> Note: This release has breaking changes.
- **BREAKING** **CHORE**: remove EntityTransformController (requires replacement).
## 0.2.0-dev.4.0
> Note: This release has breaking changes.
- **BREAKING** **FIX**: (web/wasm) free pick callbacks on dispose.
- **BREAKING** **CHORE**: restructure viewer folders as libraries to only export the public interface.
## 0.2.0-dev.1.0
> Note: This release has breaking changes.
- **REFACTOR**: native types.
- **REFACTOR**: native types.
- **REFACTOR**: move native types to own header, add methods for create/destroy material instance, add priority/layer to load_glb_from_buffer.
- **REFACTOR**: Dart types.
- **FIX**: (web) add emscripten guards for flushAndWait call when swapchain destroyed.
- **FIX**: ignore pick results directly on axis.
- **FIX**: properly destroy entities/material/etc in Gizmo on destruction, remove custom scene creation logic.
- **FIX**: add check for nan NDC coordinates for viewport translation.
- **FIX**: (wasm) use correct coords for pick, free memory correctly, keep pixelratio copy.
- **FIX**: add more nan checks for gizmo manipulation.
- **FIX**: emscripten export visibility for add_light.
- **FIX**: add Fence to capture() and set stencil buffer by default.
- **FEAT**: add removeStencilHighlight, queuePositionUpdateFromViewportCoords to ThermionViewer.
- **FEAT**: add removeStencilHighlight, accept color param for setStencilHighlight, queuePositionUpdateFromViewportCoords to ThermionDartApi.
- **FEAT**: add flag for keepData for gltf instancing, add highlightScene, add stencilHighlight method.
- **FEAT**: grid uses own material.
- **FEAT**: parent the cloned entity instance when setting stencil highlight.
- **FEAT**: add grid material.
- **FEAT**: expose setLightDirection and setLightPosition.
- **FEAT**: move HighlightOverlay to nested class, move createGeometry to SceneManager, add queueRelativePositionUpdateFromViewportVector.
- **FEAT**: move createGeometry to SceneManager, add queueRelativePositionUpdateFromViewportVector and removeStencilHighlight.
- **FEAT**: add setGizmoVisibility/pickGizmo methods to ThermionViewer.
- **FEAT**: remove gizmo view references, exclude gizmo entities from picking, add createIbl.
- **FEAT**: set stencil highlight on gizmo attach.
- **FEAT**: add getAncestor method.
- **FEAT**: expose API methods for create_ibl, pick/set gizmo visibility.
- **FEAT**: create transparent overlay for gizmo for easier picking.
- **FEAT**: rescale gizmo based on distance from camera.
- **FEAT**: rescale gizmo based on distance from camera.
- **FEAT**: add getAncestor method.
- **FEAT**: add startOffset parameter to gltf playAnimation.
- **FEAT**: layers, grid.
- **FEAT**: layers, grid.
- **FEAT**: ignore grid overlay and gizmo center when picking, implement highlighting.
- **FEAT**: SceneManager updates (setLayer, add grid, queueRelativePositionUpdateWorld.
- **FEAT**: expose set_layer_enabled, get_camera_fov and queue_relative_position_updateg_world_axis to ThermionDartApi.h.
- **FEAT**: add getCameraFov to FilamentViewer.
- **FEAT**: add new grid overlay files to web CmakeLists.
- **FEAT**: re-implement (native) Gizmo class, expose preserveScaling parameter for setParent, add methods for getting viewport bounding box from renderable entity.
- **FEAT**: expose setLayerEnabled, viewportDimensions and getCameraFov on ThermionView.
- **FEAT**: download WASM module directly on web (no need to embed in index.html any more) and expose updateViewportAndCameraProjection.
- **FEAT**: add capture() function and expose viewportDimensions on ThermionViewer (allows easier saving of captured images to PNG).
- **FEAT**: (web) allow table growth in emscripten module for passing C-style callback function pointers.
- **FEAT**: (web) add capture() method and missing camera navigation controls.
- **FEAT**: createIbl.
- **BREAKING** **FEAT**: (web) (flutter) create canvas when createViewer is called (no longer need to manually add canvas element to web HTML).
- **BREAKING** **FEAT**: update web/http dependencies.
## 0.1.3
- **FIX**: manually remove leading slash for compiler path on Windows when building for Android.
- **FIX**: web/JS bool checks need to compare to int.
- **FIX**: shadow JS<->WASM bridge methods.
- **FIX**: manually remove leading slash for compiler path on Windows when building for Android.
- **FIX**: web/JS bool checks need to compare to int.
- **FIX**: shadow JS<->WASM bridge methods.
- **FEAT**: add clearMorphAnimationData function.
- **FEAT**: allow passing assetPathPrefix to ThermionViewerWasm to account for Flutter build asset paths.
- **FEAT**: allow passing assetPathPrefix to ThermionViewerWasm to account for Flutter build asset paths.
## 0.1.2
- **FIX**: manually remove leading slash for compiler path on Windows when building for Android.

View File

@@ -1 +0,0 @@
For an example with both pure Dart and Flutter, see the [example repository](https://github.com/nmfisher/thermion_examples).

View File

@@ -1,25 +1,13 @@
output: '../lib/src/viewer/src/ffi/src/thermion_dart.g.dart'
output: '../lib/thermion_dart/compatibility/native/thermion_dart.g.dart'
headers:
entry-points:
- '../native/include/ThermionDartRenderThreadApi.h'
- '../native/include/ThermionDartFFIApi.h'
- '../native/include/ThermionDartApi.h'
- '../native/include/TView.h'
- '../native/include/TCamera.h'
- '../native/include/TGizmo.h'
- '../native/include/ResourceBuffer.h'
include-directives:
- '../native/include/ThermionDartRenderThreadApi.h'
- '../native/include/ThermionDartFFIApi.h'
- '../native/include/ThermionDartApi.h'
- '../native/include/TView.h'
- '../native/include/TCamera.h'
- '../native/include/TGizmo.h'
- '../native/include/ResourceBuffer.h'
- '../native/include/APIBoundaryTypes.h'
ffi-native:
assetId: package:thermion_dart/thermion_dart.dart
ignore-source-errors: true
functions:
leaf:
include:
- '.*'

View File

@@ -1,15 +1,15 @@
name: ThermionTextureSwift
description: Bindings for ThermionTexture.
name: ThermionDartTexture
description: Bindings for ThermionDartTexture.
language: objc
output: '../lib/src/swift/swift_bindings.g.dart'
output: 'lib/thermion_dart/swift/swift_bindings.g.dart'
exclude-all-by-default: true
objc-interfaces:
include:
- 'ThermionTextureSwift'
- 'ThermionDartTexture'
module:
'ThermionTextureSwift': 'swift_module'
'ThermionDartTexture': 'thermion_dart_texture'
headers:
entry-points:
- '../native/include/generated/ThermionTextureSwiftObjCAPI.h'
- 'native/lib/macos/swift/ThermionDartTexture.h'
preamble: |
// ignore_for_file: camel_case_types, non_constant_identifier_names, unused_element, unused_field, return_of_invalid_type, void_checks, annotate_overrides, no_leading_underscores_for_local_identifiers, library_private_types_in_public_apia

View File

@@ -60,8 +60,6 @@ void main(List<String> args) async {
sources.addAll([
"${config.packageRoot.toFilePath()}/native/include/material/gizmo.c",
"${config.packageRoot.toFilePath()}/native/include/material/image.c",
"${config.packageRoot.toFilePath()}/native/include/material/grid.c",
"${config.packageRoot.toFilePath()}/native/include/material/unlit.c",
]);
var libs = [
@@ -78,6 +76,7 @@ void main(List<String> args) async {
"image",
"imageio",
"tinyexr",
"gltfio_core",
"filaflat",
"dracodec",
"ibl",

View File

@@ -1,6 +0,0 @@
library;
export 'src/input_handler.dart';
export 'src/delegates.dart';
export 'src/delegate_gesture_handler.dart';
export 'src/implementations/third_person_camera_delegate.dart';

View File

@@ -1,253 +0,0 @@
import 'dart:async';
import 'package:logging/logging.dart';
import 'package:thermion_dart/thermion_dart.dart';
import 'package:vector_math/vector_math_64.dart';
import 'implementations/fixed_orbit_camera_rotation_delegate.dart';
import 'implementations/free_flight_camera_delegate.dart';
class DelegateInputHandler implements InputHandler {
final ThermionViewer viewer;
Stream<List<InputType>> get gestures => _gesturesController.stream;
final _gesturesController = StreamController<List<InputType>>.broadcast();
final _logger = Logger("DelegateInputHandler");
InputHandlerDelegate? transformDelegate;
PickDelegate? pickDelegate;
final Set<PhysicalKey> _pressedKeys = {};
final _inputDeltas = <InputType, Vector3>{};
Map<InputType, InputAction> _actions = {
InputType.LMB_HOLD_AND_MOVE: InputAction.TRANSLATE,
InputType.MMB_HOLD_AND_MOVE: InputAction.ROTATE,
InputType.SCROLLWHEEL: InputAction.TRANSLATE,
InputType.POINTER_MOVE: InputAction.NONE,
InputType.KEYDOWN_W: InputAction.TRANSLATE,
InputType.KEYDOWN_S: InputAction.TRANSLATE,
InputType.KEYDOWN_A: InputAction.TRANSLATE,
InputType.KEYDOWN_D: InputAction.TRANSLATE,
};
final _axes = <InputType, Matrix3>{};
void setTransformForAction(InputType inputType, Matrix3 transform) {
_axes[inputType] = transform;
}
DelegateInputHandler({
required this.viewer,
required this.transformDelegate,
this.pickDelegate,
Map<InputType, InputAction>? actions,
}) {
if (actions != null) {
_actions = actions;
}
for (var gestureType in InputType.values) {
_inputDeltas[gestureType] = Vector3.zero();
}
viewer.registerRequestFrameHook(process);
}
factory DelegateInputHandler.fixedOrbit(ThermionViewer viewer,
{double minimumDistance = 10.0,
Future<double?> Function(Vector3)? getDistanceToTarget,
ThermionEntity? entity,
PickDelegate? pickDelegate}) =>
DelegateInputHandler(
viewer: viewer,
pickDelegate: pickDelegate,
transformDelegate: FixedOrbitRotateInputHandlerDelegate(viewer,
getDistanceToTarget: getDistanceToTarget,
minimumDistance: minimumDistance),
actions: {
InputType.MMB_HOLD_AND_MOVE: InputAction.ROTATE,
InputType.SCROLLWHEEL: InputAction.TRANSLATE
});
factory DelegateInputHandler.flight(ThermionViewer viewer,
{PickDelegate? pickDelegate,
bool freeLook = false,
double? clampY,
ThermionEntity? entity}) =>
DelegateInputHandler(
viewer: viewer,
pickDelegate: pickDelegate,
transformDelegate: FreeFlightInputHandlerDelegate(viewer,
clampY: clampY, entity: entity),
actions: {
InputType.MMB_HOLD_AND_MOVE: InputAction.ROTATE,
InputType.SCROLLWHEEL: InputAction.TRANSLATE,
InputType.LMB_HOLD_AND_MOVE: InputAction.TRANSLATE,
InputType.KEYDOWN_A: InputAction.TRANSLATE,
InputType.KEYDOWN_W: InputAction.TRANSLATE,
InputType.KEYDOWN_S: InputAction.TRANSLATE,
InputType.KEYDOWN_D: InputAction.TRANSLATE,
if (freeLook) InputType.POINTER_MOVE: InputAction.ROTATE,
});
bool _processing = false;
Future<void> process() async {
_processing = true;
for (var gestureType in _inputDeltas.keys) {
var vector = _inputDeltas[gestureType]!;
var action = _actions[gestureType];
if (action == null) {
continue;
}
final transform = _axes[gestureType];
if (transform != null) {
vector = transform * vector;
}
await transformDelegate?.queue(action, vector);
}
final keyTypes = <InputType>[];
for (final key in _pressedKeys) {
InputAction? keyAction;
InputType? keyType = null;
Vector3? vector;
switch (key) {
case PhysicalKey.W:
keyType = InputType.KEYDOWN_W;
vector = Vector3(0, 0, -1);
break;
case PhysicalKey.A:
keyType = InputType.KEYDOWN_A;
vector = Vector3(-1, 0, 0);
break;
case PhysicalKey.S:
keyType = InputType.KEYDOWN_S;
vector = Vector3(0, 0, 1);
break;
case PhysicalKey.D:
keyType = InputType.KEYDOWN_D;
vector = Vector3(1, 0, 0);
break;
}
if (keyType != null) {
keyAction = _actions[keyType];
if (keyAction != null) {
var transform = _axes[keyAction];
if (transform != null) {
vector = transform * vector;
}
transformDelegate?.queue(keyAction, vector!);
keyTypes.add(keyType);
}
}
}
await transformDelegate?.execute();
var updates = _inputDeltas.keys.followedBy(keyTypes).toList();
if(updates.isNotEmpty) {
_gesturesController.add(updates);
}
_inputDeltas.clear();
_processing = false;
}
@override
Future<void> onPointerDown(Vector2 localPosition, bool isMiddle) async {
if (!isMiddle) {
final action = _actions[InputType.LMB_DOWN];
switch (action) {
case InputAction.PICK:
pickDelegate?.pick(localPosition);
default:
// noop
}
}
}
@override
Future<void> onPointerMove(
Vector2 localPosition, Vector2 delta, bool isMiddle) async {
if (_processing) {
return;
}
if (isMiddle) {
_inputDeltas[InputType.MMB_HOLD_AND_MOVE] =
(_inputDeltas[InputType.MMB_HOLD_AND_MOVE] ?? Vector3.zero()) +
Vector3(delta.x, delta.y, 0.0);
} else {
_inputDeltas[InputType.LMB_HOLD_AND_MOVE] =
(_inputDeltas[InputType.LMB_HOLD_AND_MOVE] ?? Vector3.zero()) +
Vector3(delta.x, delta.y, 0.0);
}
}
@override
Future<void> onPointerUp(bool isMiddle) async {}
@override
Future<void> onPointerHover(Vector2 localPosition, Vector2 delta) async {
if (_processing) {
return;
}
_inputDeltas[InputType.POINTER_MOVE] =
(_inputDeltas[InputType.POINTER_MOVE] ?? Vector3.zero()) +
Vector3(delta.x, delta.y, 0.0);
}
@override
Future<void> onPointerScroll(
Vector2 localPosition, double scrollDelta) async {
if (_processing) {
return;
}
try {
_inputDeltas[InputType.SCROLLWHEEL] =
(_inputDeltas[InputType.SCROLLWHEEL] ?? Vector3.zero()) +
Vector3(0, 0, scrollDelta > 0 ? 1 : -1);
} catch (e) {
_logger.warning("Error during scroll accumulation: $e");
}
}
@override
Future dispose() async {
viewer.unregisterRequestFrameHook(process);
}
@override
Future<bool> get initialized => viewer.initialized;
@override
Future<void> onScaleEnd() async {}
@override
Future<void> onScaleStart() async {}
@override
Future<void> onScaleUpdate() async {}
@override
void setActionForType(InputType gestureType, InputAction gestureAction) {
_actions[gestureType] = gestureAction;
}
@override
InputAction? getActionForType(InputType gestureType) {
return _actions[gestureType];
}
void keyDown(PhysicalKey key) {
_pressedKeys.add(key);
}
void keyUp(PhysicalKey key) {
_pressedKeys.remove(key);
}
}

View File

@@ -1,27 +0,0 @@
import 'package:vector_math/vector_math_64.dart';
import 'input_handler.dart';
abstract class InputHandlerDelegate {
Future queue(InputAction action, Vector3? delta);
Future execute();
}
abstract class VelocityDelegate {
Vector2? get velocity;
void updateVelocity(Vector2 delta);
void startDeceleration();
void stopDeceleration();
void dispose() {
stopDeceleration();
}
}
abstract class PickDelegate {
const PickDelegate();
void pick(Vector2 location);
}

View File

@@ -1,87 +0,0 @@
// import 'dart:async';
// import 'dart:ui';
// import 'package:flutter/services.dart';
// import 'package:thermion_dart/thermion_dart/thermion_viewer.dart';
// import 'package:thermion_dart/thermion_dart/input/delegates.dart';
// import 'package:vector_math/vector_math_64.dart';
// class DefaultKeyboardCameraFlightDelegate
// {
// final ThermionViewer viewer;
// static const double _panSensitivity = 0.005;
// static const double _keyMoveSensitivity = 0.1;
// final Map<PhysicalKeyboardKey, bool> _pressedKeys = {};
// Timer? _moveTimer;
// DefaultKeyboardCameraFlightDelegate(this.viewer) {
// _startMoveLoop();
// }
// @override
// Future<void> panCamera(Offset delta, Vector2? velocity) async {
// double deltaX = delta.dx;
// double deltaY = delta.dy;
// deltaX *= _panSensitivity * viewer.pixelRatio;
// deltaY *= _panSensitivity * viewer.pixelRatio;
// await _moveCamera(deltaX, deltaY, 0);
// }
// @override
// Future<void> onKeypress(PhysicalKeyboardKey key) async {
// _pressedKeys[key] = true;
// }
// // New method to handle key release
// Future<void> onKeyRelease(PhysicalKeyboardKey key) async {
// _pressedKeys.remove(key);
// }
// void _startMoveLoop() {
// _moveTimer = Timer.periodic(
// Duration(milliseconds: 16), (_) => _processKeyboardInput());
// }
// Future<void> _processKeyboardInput() async {
// double dx = 0, dy = 0, dz = 0;
// if (_pressedKeys[PhysicalKeyboardKey.keyW] == true)
// dz += _keyMoveSensitivity;
// if (_pressedKeys[PhysicalKeyboardKey.keyS] == true)
// dz -= _keyMoveSensitivity;
// if (_pressedKeys[PhysicalKeyboardKey.keyA] == true)
// dx -= _keyMoveSensitivity;
// if (_pressedKeys[PhysicalKeyboardKey.keyD] == true)
// dx += _keyMoveSensitivity;
// if (dx != 0 || dy != 0 || dz != 0) {
// await _moveCamera(dx, dy, dz);
// }
// // Removed _pressedKeys.clear(); from here
// }
// Future<void> _moveCamera(double dx, double dy, double dz) async {
// Matrix4 currentModelMatrix = await viewer.getCameraModelMatrix();
// Vector3 currentPosition = currentModelMatrix.getTranslation();
// Quaternion currentRotation =
// Quaternion.fromRotation(currentModelMatrix.getRotation());
// Vector3 forward = Vector3(0, 0, -1)..applyQuaternion(currentRotation);
// Vector3 right = Vector3(1, 0, 0)..applyQuaternion(currentRotation);
// Vector3 up = Vector3(0, 1, 0)..applyQuaternion(currentRotation);
// Vector3 moveOffset = right * dx + up * dy + forward * dz;
// Vector3 newPosition = currentPosition + moveOffset;
// Matrix4 newModelMatrix =
// Matrix4.compose(newPosition, currentRotation, Vector3(1, 1, 1));
// await viewer.setCameraModelMatrix4(newModelMatrix);
// }
// void dispose() {
// _moveTimer?.cancel();
// }
// }

View File

@@ -1,35 +0,0 @@
// import 'dart:ui';
// import 'package:thermion_dart/thermion_dart/thermion_viewer.dart';
// import 'package:thermion_flutter/thermion/widgets/camera/gestures/v2/delegates.dart';
// import 'package:vector_math/vector_math_64.dart';
// class DefaultPanInputHandlerDelegate implements PanInputHandlerDelegate {
// final ThermionViewer viewer;
// static const double _panSensitivity = 0.005;
// DefaultPanInputHandlerDelegate(this.viewer);
// static const double _panSensitivity = 0.005;
// @override
// Future<void> panCamera(Offset delta, Vector2? velocity) async {
// double deltaX = delta.dx;
// double deltaY = delta.dy;
// deltaX *= _panSensitivity * viewer.pixelRatio;
// deltaY *= _panSensitivity * viewer.pixelRatio;
// Matrix4 currentModelMatrix = await viewer.getCameraModelMatrix();
// Vector3 currentPosition = currentModelMatrix.getTranslation();
// Quaternion currentRotation = Quaternion.fromRotation(currentModelMatrix.getRotation());
// Vector3 right = Vector3(1, 0, 0)..applyQuaternion(currentRotation);
// Vector3 up = Vector3(0, 1, 0)..applyQuaternion(currentRotation);
// Vector3 panOffset = right * -deltaX + up * deltaY;
// Vector3 newPosition = currentPosition + panOffset;
// Matrix4 newModelMatrix = Matrix4.compose(newPosition, currentRotation, Vector3(1, 1, 1));
// await viewer.setCameraModelMatrix4(newModelMatrix);
// }
// }

View File

@@ -1,183 +0,0 @@
import 'dart:async';
import 'package:vector_math/vector_math_64.dart';
import '../../../viewer/src/shared_types/camera.dart';
import '../../../viewer/viewer.dart';
import '../../input.dart';
import '../input_handler.dart';
import 'dart:math';
class FixedOrbitRotateInputHandlerDelegate implements InputHandlerDelegate {
final ThermionViewer viewer;
late Future<Camera> _camera;
final double minimumDistance;
Future<double?> Function(Vector3)? getDistanceToTarget;
Vector2 _queuedRotationDelta = Vector2.zero();
double _queuedZoomDelta = 0.0;
static final _up = Vector3(0, 1, 0);
Timer? _updateTimer;
FixedOrbitRotateInputHandlerDelegate(
this.viewer, {
this.getDistanceToTarget,
this.minimumDistance = 10.0,
}) {
_camera = viewer.getMainCamera().then((Camera cam) async {
var viewMatrix = makeViewMatrix(Vector3(0.0, 0, -minimumDistance),
Vector3.zero(), Vector3(0.0, 1.0, 0.0));
viewMatrix.invert();
await cam.setTransform(viewMatrix);
return cam;
});
}
void dispose() {
_updateTimer?.cancel();
}
@override
Future<void> queue(InputAction action, Vector3? delta) async {
if (delta == null) return;
switch (action) {
case InputAction.ROTATE:
_queuedRotationDelta += Vector2(delta.x, delta.y);
break;
case InputAction.TRANSLATE:
_queuedZoomDelta += delta!.z;
break;
case InputAction.PICK:
// Assuming PICK is used for zoom in this context
_queuedZoomDelta += delta.z;
break;
case InputAction.NONE:
// Do nothing
break;
}
}
bool _executing = false;
@override
Future<void> execute() async {
if (_queuedRotationDelta.length2 == 0.0 && _queuedZoomDelta == 0.0) {
return;
}
if (_executing) {
return;
}
_executing = true;
final view = await viewer.getViewAt(0);
final viewport = await view.getViewport();
var viewMatrix = await viewer.getCameraViewMatrix();
var modelMatrix = await viewer.getCameraModelMatrix();
var projectionMatrix = await viewer.getCameraProjectionMatrix();
var inverseProjectionMatrix = projectionMatrix.clone()..invert();
Vector3 currentPosition = modelMatrix.getTranslation();
Vector3 forward = -currentPosition.normalized();
if (forward.length == 0) {
forward = Vector3(0, 0, -1);
currentPosition = Vector3(0, 0, minimumDistance);
}
Vector3 right = _up.cross(forward).normalized();
Vector3 up = forward.cross(right);
// Calculate the point where the camera forward ray intersects with the
// surface of the target sphere
var distanceToTarget =
(await getDistanceToTarget?.call(currentPosition)) ?? 0;
Vector3 intersection =
(-forward).scaled(currentPosition.length - distanceToTarget);
final intersectionInViewSpace = viewMatrix *
Vector4(intersection.x, intersection.y, intersection.z, 1.0);
final intersectionInClipSpace = projectionMatrix * intersectionInViewSpace;
final intersectionInNdcSpace =
intersectionInClipSpace / intersectionInClipSpace.w;
// Calculate new camera position based on rotation
final ndcX = 2 * ((-_queuedRotationDelta.x) / viewport.width);
final ndcY = 2 * ((_queuedRotationDelta.y) / viewport.height);
final ndc = Vector4(ndcX, ndcY, intersectionInNdcSpace.z, 1.0);
var clipSpace = Vector4(
ndc.x * intersectionInClipSpace.w,
ndcY * intersectionInClipSpace.w,
ndc.z * intersectionInClipSpace.w,
intersectionInClipSpace.w);
Vector4 cameraSpace = inverseProjectionMatrix * clipSpace;
Vector4 worldSpace = modelMatrix * cameraSpace;
var worldSpace3 = worldSpace.xyz.normalized() * currentPosition.length;
currentPosition = worldSpace3;
// Zoom
if (_queuedZoomDelta != 0.0) {
var distToIntersection =
(currentPosition - intersection).length - minimumDistance;
// if we somehow overshot the minimum distance, reset the camera to the minimum distance
if (distToIntersection < 0) {
currentPosition +=
(intersection.normalized().scaled(-distToIntersection * 10));
} else {
bool zoomingOut = _queuedZoomDelta > 0;
late Vector3 offset;
// when zooming, we don't always use fractions of the distance from
// the camera to the target (this is due to float precision issues at
// large distances, and also it doesn't work well for UI).
// if we're zooming out and the distance is less than 10m, we zoom out by 1 unit
if (zoomingOut) {
if (distToIntersection < 10) {
offset = intersection.normalized();
} else {
offset = intersection.normalized().scaled(distToIntersection / 10);
}
// if we're zooming in and the distance is less than 5m, zoom in by 1/2 the distance,
// otherwise 1/10 of the distance each time
} else {
if (distToIntersection < 5) {
offset = intersection.normalized().scaled(-distToIntersection / 2);
} else {
offset = intersection.normalized().scaled(-distToIntersection / 10);
}
if (offset.length > distToIntersection) {
offset = Vector3.zero();
}
}
currentPosition += offset;
}
}
// Calculate view matrix
forward = -currentPosition.normalized();
right = _up.cross(forward).normalized();
up = forward.cross(right);
Matrix4 newViewMatrix = makeViewMatrix(currentPosition, Vector3.zero(), up);
newViewMatrix.invert();
// Set the camera model matrix
var camera = await _camera;
await camera.setModelMatrix(newViewMatrix);
// Reset queued deltas
_queuedRotationDelta = Vector2.zero();
_queuedZoomDelta = 0.0;
_executing = false;
}
}

View File

@@ -1,184 +0,0 @@
import 'dart:async';
import 'package:vector_math/vector_math_64.dart';
import '../../../viewer/viewer.dart';
import '../delegates.dart';
import '../input_handler.dart';
class FreeFlightInputHandlerDelegate implements InputHandlerDelegate {
final ThermionViewer viewer;
late Future<ThermionEntity> entity;
final Vector3? minBounds;
final Vector3? maxBounds;
final double rotationSensitivity;
final double movementSensitivity;
final double zoomSensitivity;
final double panSensitivity;
final double? clampY;
static final _up = Vector3(0, 1, 0);
static final _forward = Vector3(0, 0, -1);
static final Vector3 _right = Vector3(1, 0, 0);
Vector2 _queuedRotationDelta = Vector2.zero();
Vector2 _queuedPanDelta = Vector2.zero();
double _queuedZoomDelta = 0.0;
Vector3 _queuedMoveDelta = Vector3.zero();
FreeFlightInputHandlerDelegate(this.viewer,
{this.minBounds,
this.maxBounds,
this.rotationSensitivity = 0.001,
this.movementSensitivity = 0.1,
this.zoomSensitivity = 0.1,
this.panSensitivity = 0.1,
this.clampY,
ThermionEntity? entity}) {
if (entity != null) {
this.entity = Future.value(entity);
} else {
this.entity = viewer.getMainCameraEntity();
}
}
@override
Future<void> queue(InputAction action, Vector3? delta) async {
if (delta == null) return;
switch (action) {
case InputAction.ROTATE:
_queuedRotationDelta += Vector2(delta.x, delta.y);
break;
case InputAction.TRANSLATE:
_queuedPanDelta += Vector2(delta.x, delta.y);
_queuedZoomDelta += delta.z;
break;
case InputAction.PICK:
_queuedZoomDelta += delta.z;
break;
case InputAction.NONE:
break;
}
}
bool _executing = false;
@override
Future<void> execute() async {
if (_executing) {
return;
}
_executing = true;
if (_queuedRotationDelta.length2 == 0.0 &&
_queuedPanDelta.length2 == 0.0 &&
_queuedZoomDelta == 0.0 &&
_queuedMoveDelta.length2 == 0.0) {
_executing = false;
return;
}
final activeCamera = await viewer.getActiveCamera();
Matrix4 currentViewMatrix = await activeCamera.getViewMatrix();
Matrix4 currentTransform = await viewer.getLocalTransform(await entity);
Vector3 currentPosition = currentTransform.getTranslation();
Quaternion currentRotation =
Quaternion.fromRotation(currentTransform.getRotation());
// Calculate relative transform
Matrix4 relativeTransform = Matrix4.identity();
Vector3 relativeTranslation = Vector3.zero();
Quaternion relativeRotation = Quaternion.identity();
// Apply rotation
if (_queuedRotationDelta.length2 > 0.0) {
double deltaX =
_queuedRotationDelta.x * rotationSensitivity;
double deltaY =
_queuedRotationDelta.y * rotationSensitivity;
Quaternion yawRotation = Quaternion.axisAngle(_up, -deltaX);
Quaternion pitchRotation = Quaternion.axisAngle(_right, -deltaY);
relativeRotation = pitchRotation * yawRotation;
_queuedRotationDelta = Vector2.zero();
}
// Apply pan
if (_queuedPanDelta.length2 > 0.0) {
Vector3 right = _right.clone()..applyQuaternion(currentRotation);
Vector3 up = _up.clone()..applyQuaternion(currentRotation);
double deltaX = _queuedPanDelta.x * panSensitivity;
double deltaY = _queuedPanDelta.y * panSensitivity;
relativeTranslation += right * deltaX + up * deltaY;
_queuedPanDelta = Vector2.zero();
}
// Apply zoom
if (_queuedZoomDelta != 0.0) {
Vector3 forward = _forward.clone()..applyQuaternion(currentRotation);
relativeTranslation += forward * -_queuedZoomDelta * zoomSensitivity;
_queuedZoomDelta = 0.0;
}
// Apply queued movement
if (_queuedMoveDelta.length2 > 0.0) {
Vector3 forward = _forward.clone()..applyQuaternion(currentRotation);
Vector3 right = _right.clone()..applyQuaternion(currentRotation);
Vector3 up = _up.clone()..applyQuaternion(currentRotation);
relativeTranslation += right * _queuedMoveDelta.x +
up * _queuedMoveDelta.y +
forward * _queuedMoveDelta.z;
_queuedMoveDelta = Vector3.zero();
}
// If the managed entity is not the active camera, we need to apply the rotation from the current camera model matrix
// to the entity's translation
if (await entity != activeCamera.getEntity()) {
Matrix4 modelMatrix = await activeCamera.getModelMatrix();
relativeTranslation = modelMatrix.getRotation() * relativeTranslation;
}
// Compose relative transform
relativeTransform = Matrix4.compose(
relativeTranslation, relativeRotation, Vector3(1, 1, 1));
// Apply relative transform to current transform
Matrix4 newTransform = currentTransform * relativeTransform;
// Extract new position and constrain it
Vector3 newPosition = newTransform.getTranslation();
newPosition = _constrainPosition(newPosition);
// Recompose final transform with constrained position
Matrix4 finalTransform = Matrix4.compose(newPosition,
Quaternion.fromRotation(newTransform.getRotation()), Vector3(1, 1, 1));
// Update camera
await viewer.setTransform(await entity, finalTransform);
_executing = false;
}
Vector3 _constrainPosition(Vector3 position) {
if (minBounds != null) {
position.x = position.x.clamp(minBounds!.x, double.infinity);
position.y = position.y.clamp(minBounds!.y, double.infinity);
position.z = position.z.clamp(minBounds!.z, double.infinity);
}
if (maxBounds != null) {
position.x = position.x.clamp(double.negativeInfinity, maxBounds!.x);
position.y = position.y.clamp(double.negativeInfinity, maxBounds!.y);
position.z = position.z.clamp(double.negativeInfinity, maxBounds!.z);
}
if (clampY != null) {
position.y = clampY!;
}
return position;
}
}

View File

@@ -1,230 +0,0 @@
// import 'dart:async';
// import 'package:flutter/gestures.dart';
// import 'package:flutter/services.dart';
// import 'package:logging/logging.dart';
// import 'package:thermion_dart/thermion_dart/entities/abstract_gizmo.dart';
// import 'package:thermion_dart/thermion_dart/thermion_viewer.dart';
// import 'dart:ui';
// import 'package:thermion_dart/thermion_dart/input/input_handler.dart';
// // Renamed implementation
// class PickingCameraGestureHandler implements InputHandler {
// final ThermionViewer viewer;
// final bool enableCamera;
// final bool enablePicking;
// final Logger _logger = Logger("PickingCameraGestureHandler");
// AbstractGizmo? _gizmo;
// Timer? _scrollTimer;
// ThermionEntity? _highlightedEntity;
// StreamSubscription<FilamentPickResult>? _pickResultSubscription;
// bool _gizmoAttached = false;
// PickingCameraGestureHandler({
// required this.viewer,
// this.enableCamera = true,
// this.enablePicking = true,
// }) {
// try {
// _gizmo = viewer.gizmo;
// } catch (err) {
// _logger.warning(
// "Failed to get gizmo. If you are running on WASM, this is expected");
// }
// _pickResultSubscription = viewer.pickResult.listen(_onPickResult);
// // Add keyboard listener
// RawKeyboard.instance.addListener(_handleKeyEvent);
// }
// @override
// ThermionGestureState get currentState => _currentState;
// void _handleKeyEvent(RawKeyEvent event) {
// if (event is RawKeyDownEvent &&
// event.logicalKey == LogicalKeyboardKey.escape) {
// _resetToNullState();
// }
// }
// void _resetToNullState() async {
// _currentState = ThermionGestureState.NULL;
// if (_highlightedEntity != null) {
// await viewer.removeStencilHighlight(_highlightedEntity!);
// _highlightedEntity = null;
// }
// }
// void _onPickResult(FilamentPickResult result) async {
// var targetEntity = await viewer.getAncestor(result.entity) ?? result.entity;
// if (_highlightedEntity != targetEntity) {
// if (_highlightedEntity != null) {
// await viewer.removeStencilHighlight(_highlightedEntity!);
// }
// _highlightedEntity = targetEntity;
// if (_highlightedEntity != null) {
// await viewer.setStencilHighlight(_highlightedEntity!);
// _gizmo?.attach(_highlightedEntity!);
// }
// }
// }
// @override
// Future<void> onPointerHover(Offset localPosition, Offset delta) async {
// if (_gizmoAttached) {
// _gizmo?.checkHover(localPosition.dx, localPosition.dy);
// }
// if (_highlightedEntity != null) {
// await viewer.queuePositionUpdateFromViewportCoords(
// _highlightedEntity!,
// localPosition.dx,
// localPosition.dy,
// );
// }
// }
// @override
// Future<void> onPointerScroll(Offset localPosition, double scrollDelta) async {
// if (!enableCamera) {
// return;
// }
// if (_currentState == ThermionGestureState.NULL ||
// _currentState == ThermionGestureState.ZOOMING) {
// await _zoom(localPosition, scrollDelta);
// }
// }
// @override
// Future<void> onPointerDown(Offset localPosition, int buttons) async {
// if (_highlightedEntity != null) {
// _resetToNullState();
// return;
// }
// if (enablePicking && buttons != kMiddleMouseButton) {
// viewer.pick(localPosition.dx.toInt(), localPosition.dy.toInt());
// }
// if (buttons == kMiddleMouseButton && enableCamera) {
// await viewer.rotateStart(localPosition.dx, localPosition.dy);
// _currentState = ThermionGestureState.ROTATING;
// } else if (buttons == kPrimaryMouseButton && enableCamera) {
// await viewer.panStart(localPosition.dx, localPosition.dy);
// _currentState = ThermionGestureState.PANNING;
// }
// }
// @override
// Future<void> onPointerMove(
// Offset localPosition, Offset delta, int buttons) async {
// if (_highlightedEntity != null) {
// await _handleEntityHighlightedMove(localPosition);
// return;
// }
// switch (_currentState) {
// case ThermionGestureState.NULL:
// break;
// case ThermionGestureState.ROTATING:
// if (enableCamera) {
// await viewer.rotateUpdate(localPosition.dx, localPosition.dy);
// }
// break;
// case ThermionGestureState.PANNING:
// if (enableCamera) {
// await viewer.panUpdate(localPosition.dx, localPosition.dy);
// }
// break;
// case ThermionGestureState.ZOOMING:
// // ignore
// break;
// }
// }
// @override
// Future<void> onPointerUp(int buttons) async {
// switch (_currentState) {
// case ThermionGestureState.ROTATING:
// await viewer.rotateEnd();
// _currentState = ThermionGestureState.NULL;
// break;
// case ThermionGestureState.PANNING:
// await viewer.panEnd();
// _currentState = ThermionGestureState.NULL;
// break;
// default:
// break;
// }
// }
// Future<void> _handleEntityHighlightedMove(Offset localPosition) async {
// if (_highlightedEntity != null) {
// await viewer.queuePositionUpdateFromViewportCoords(
// _highlightedEntity!,
// localPosition.dx,
// localPosition.dy,
// );
// }
// }
// Future<void> _zoom(Offset localPosition, double scrollDelta) async {
// _scrollTimer?.cancel();
// _currentState = ThermionGestureState.ZOOMING;
// await viewer.zoomBegin();
// await viewer.zoomUpdate(
// localPosition.dx, localPosition.dy, scrollDelta > 0 ? 1 : -1);
// _scrollTimer = Timer(const Duration(milliseconds: 100), () async {
// await viewer.zoomEnd();
// _currentState = ThermionGestureState.NULL;
// });
// }
// @override
// void dispose() {
// _pickResultSubscription?.cancel();
// if (_highlightedEntity != null) {
// viewer.removeStencilHighlight(_highlightedEntity!);
// }
// RawKeyboard.instance.removeListener(_handleKeyEvent);
// }
// @override
// Future<bool> get initialized => viewer.initialized;
// @override
// InputAction getActionForType(InputType type) {
// // TODO: implement getActionForType
// throw UnimplementedError();
// }
// @override
// Future<void> onScaleEnd() {
// // TODO: implement onScaleEnd
// throw UnimplementedError();
// }
// @override
// Future<void> onScaleStart() {
// // TODO: implement onScaleStart
// throw UnimplementedError();
// }
// @override
// Future<void> onScaleUpdate() {
// // TODO: implement onScaleUpdate
// throw UnimplementedError();
// }
// @override
// void setActionForType(InputType type, InputAction action) {
// // TODO: implement setActionForType
// }
// }

View File

@@ -1,120 +0,0 @@
import 'dart:async';
import 'dart:math';
import 'package:vector_math/vector_math_64.dart';
import '../../../viewer/viewer.dart';
import '../delegates.dart';
import '../input_handler.dart';
class OverTheShoulderCameraDelegate implements InputHandlerDelegate {
final ThermionViewer viewer;
late ThermionEntity player;
late Camera camera;
final double rotationSensitivity;
final double movementSensitivity;
final double zoomSensitivity;
final double panSensitivity;
final double? clampY;
static final _up = Vector3(0, 1, 0);
static final _forward = Vector3(0, 0, -1);
static final Vector3 _right = Vector3(1, 0, 0);
Vector2 _queuedRotationDelta = Vector2.zero();
double _queuedZoomDelta = 0.0;
Vector3 _queuedMoveDelta = Vector3.zero();
final cameraPosition = Vector3(-0.5, 2.5, -3);
final cameraUp = Vector3(0, 1, 0);
var cameraLookAt = Vector3(0, 0.5, 3);
final void Function(Matrix4 transform)? onUpdate;
OverTheShoulderCameraDelegate(this.viewer, this.player, this.camera,
{this.rotationSensitivity = 0.001,
this.movementSensitivity = 0.1,
this.zoomSensitivity = 0.1,
this.panSensitivity = 0.1,
this.clampY,
ThermionEntity? entity,
this.onUpdate}) {}
@override
Future<void> queue(InputAction action, Vector3? delta) async {
if (delta == null) return;
switch (action) {
case InputAction.ROTATE:
_queuedRotationDelta += Vector2(delta.x, delta.y);
break;
case InputAction.TRANSLATE:
_queuedMoveDelta += delta;
break;
case InputAction.PICK:
_queuedZoomDelta += delta.z;
break;
case InputAction.NONE:
break;
}
}
static bool _executing = false;
static bool get executing => _executing;
@override
Future<void> execute() async {
if (_executing) {
return;
}
_executing = true;
if (_queuedRotationDelta.length2 == 0.0 &&
_queuedZoomDelta == 0.0 &&
_queuedMoveDelta.length2 == 0.0) {
_executing = false;
return;
}
Matrix4 currentPlayerTransform = await viewer.getWorldTransform(player);
// first we need to convert the move vector to player space
var newTransform =
Matrix4.translation(_queuedMoveDelta * movementSensitivity);
_queuedMoveDelta = Vector3.zero();
Matrix4 newPlayerTransform = newTransform * currentPlayerTransform;
await viewer.setTransform(player, newPlayerTransform);
if (_queuedZoomDelta != 0.0) {
// Ignore zoom
}
var inverted = newPlayerTransform.clone()..invert();
// camera is always looking at -Z, whereas models generally face towards +Z
if (_queuedRotationDelta.length2 > 0.0) {
double deltaX =
_queuedRotationDelta.x * rotationSensitivity;
double deltaY =
_queuedRotationDelta.y * rotationSensitivity;
cameraLookAt = Matrix4.rotationY(-deltaX) *
Matrix4.rotationX(-deltaY) *
cameraLookAt;
_queuedRotationDelta = Vector2.zero();
}
var newCameraViewMatrix =
makeViewMatrix(cameraPosition, cameraLookAt, cameraUp);
newCameraViewMatrix.invert();
var newCameraTransform = newPlayerTransform * newCameraViewMatrix;
await camera.setTransform(newCameraTransform);
await viewer.queueTransformUpdates(
[camera.getEntity(), player], [newCameraTransform, newPlayerTransform]);
onUpdate?.call(newPlayerTransform);
_executing = false;
}
}

View File

@@ -1,49 +0,0 @@
import 'dart:async';
import 'package:vector_math/vector_math_64.dart';
enum InputType {
LMB_DOWN,
LMB_HOLD_AND_MOVE,
LMB_UP,
LMB_HOVER,
MMB_DOWN,
MMB_HOLD_AND_MOVE,
MMB_UP,
MMB_HOVER,
SCALE1,
SCALE2,
SCROLLWHEEL,
POINTER_MOVE,
KEYDOWN_W,
KEYDOWN_A,
KEYDOWN_S,
KEYDOWN_D,
}
enum PhysicalKey { W, A, S, D }
enum InputAction { TRANSLATE, ROTATE, PICK, NONE }
abstract class InputHandler {
Stream<List<InputType>> get gestures;
Future<void> onPointerHover(Vector2 localPosition, Vector2 delta);
Future<void> onPointerScroll(Vector2 localPosition, double scrollDelta);
Future<void> onPointerDown(Vector2 localPosition, bool isMiddle);
Future<void> onPointerMove(
Vector2 localPosition, Vector2 delta, bool isMiddle);
Future<void> onPointerUp(bool isMiddle);
Future<void> onScaleStart();
Future<void> onScaleUpdate();
Future<void> onScaleEnd();
Future<bool> get initialized;
Future dispose();
void setActionForType(InputType gestureType, InputAction gestureAction);
InputAction? getActionForType(InputType gestureType);
void keyDown(PhysicalKey key);
void keyUp(PhysicalKey key);
}

View File

@@ -1,234 +0,0 @@
import 'dart:convert';
import 'package:vector_math/vector_math_64.dart';
import '../thermion_dart.dart';
class SceneV2 {
final Map<String, AssetInfo> assets;
final List<LightInfo> lights;
List<CameraInfo> cameras;
final List<EntityInfo> entities;
EnvironmentInfo? environment;
SceneV2({
Map<String, AssetInfo>? assets,
List<LightInfo>? lights,
List<CameraInfo>? cameras,
List<EntityInfo>? entities,
this.environment,
}) : assets = assets ?? {},
lights = lights ?? [],
cameras = cameras ?? [],
entities = entities ?? [];
void addAsset(String uri, AssetType type) {
assets[uri] = AssetInfo(uri: uri, type: type);
}
void addLight(LightInfo light) {
lights.add(light);
}
void clearAssets() {
assets.clear();
}
void clearLights() {
lights.clear();
}
void setCamera(Matrix4 modelMatrix, Matrix4 projectionMatrix) {
var camera = cameras.firstWhere((cam) => cam.isActive);
camera.modelMatrix = modelMatrix;
camera.projectionMatrix = projectionMatrix;
}
void addEntity(String assetUri, Matrix4 transform) {
if (assets.containsKey(assetUri)) {
entities.add(EntityInfo(assetUri: assetUri, transform: transform));
} else {
throw Exception('Asset not found: $assetUri');
}
}
void setEnvironment(String? skyboxUri, String? iblUri) {
environment = EnvironmentInfo(skyboxUri: skyboxUri, iblUri: iblUri);
}
Map<String, dynamic> toJson() => {
'assets': assets.map((key, value) => MapEntry(key, value.toJson())),
'lights': lights.map((light) => light.toJson()).toList(),
'cameras': cameras.map((camera) => camera.toJson()),
'entities': entities.map((entity) => entity.toJson()).toList(),
'environment': environment?.toJson(),
};
String toJsonString() => jsonEncode(toJson());
static SceneV2 fromJson(Map<String, dynamic> json) {
return SceneV2(
assets: (json['assets'] as Map<String, dynamic>).map(
(key, value) => MapEntry(key, AssetInfo.fromJson(value)),
),
lights: (json['lights'] as List)
.map((light) => LightInfo.fromJson(light))
.toList(),
cameras: json['cameras'].map((camera) => CameraInfo.fromJson),
entities: (json['entities'] as List)
.map((entity) => EntityInfo.fromJson(entity))
.toList(),
environment: json['environment'] != null
? EnvironmentInfo.fromJson(json['environment'])
: null,
);
}
static SceneV2 fromJsonString(String jsonString) =>
fromJson(jsonDecode(jsonString));
}
class AssetInfo {
final String uri;
final AssetType type;
AssetInfo({required this.uri, required this.type});
Map<String, dynamic> toJson() => {
'uri': uri,
'type': type.toString().split('.').last,
};
static AssetInfo fromJson(Map<String, dynamic> json) {
return AssetInfo(
uri: json['uri'],
type: AssetType.values.firstWhere(
(e) => e.toString().split('.').last == json['type'],
orElse: () => AssetType.glb),
);
}
}
enum AssetType { glb, gltf, geometryPrimitive }
class LightInfo {
final LightType type;
final Vector3 position;
final Vector3 direction;
final Color color;
final double intensity;
LightInfo({
required this.type,
required this.position,
required this.direction,
required this.color,
required this.intensity,
});
Map<String, dynamic> toJson() => {
'type': type.toString().split('.').last,
'position': [position.x, position.y, position.z],
'direction': [direction.x, direction.y, direction.z],
'color': color.toJson(),
'intensity': intensity,
};
static LightInfo fromJson(Map<String, dynamic> json) {
return LightInfo(
type: LightType.values.firstWhere((e) => e.name == json['type']),
position: Vector3.array(json['position'].cast<double>()),
direction: Vector3.array(json['direction'].cast<double>()),
color: Color.fromJson(json['color']),
intensity: json['intensity'],
);
}
}
class CameraInfo {
final bool isActive;
Matrix4 modelMatrix;
Matrix4 projectionMatrix;
CameraInfo(
{required this.isActive,
required this.modelMatrix,
required this.projectionMatrix});
Map<String, dynamic> toJson() => {
'modelMatrix': modelMatrix.storage,
'projectionMatrix': projectionMatrix.storage,
'isActive': isActive,
};
static CameraInfo fromJson(Map<String, dynamic> json) {
return CameraInfo(
modelMatrix:
Matrix4.fromFloat64List(json['modelMatrix'].cast<double>()),
projectionMatrix:
Matrix4.fromFloat64List(json['modelMatrix'].cast<double>()),
isActive: json["isActive"]);
}
}
class EntityInfo {
final String assetUri;
final Matrix4 transform;
EntityInfo({required this.assetUri, required this.transform});
Map<String, dynamic> toJson() => {
'assetUri': assetUri,
'transform': transform.storage,
};
static EntityInfo fromJson(Map<String, dynamic> json) {
return EntityInfo(
assetUri: json['assetUri'],
transform: Matrix4.fromList(List<double>.from(json['transform'])),
);
}
}
class EnvironmentInfo {
final String? skyboxUri;
final String? iblUri;
EnvironmentInfo({this.skyboxUri, this.iblUri});
Map<String, dynamic> toJson() => {
'skyboxUri': skyboxUri,
'iblUri': iblUri,
};
static EnvironmentInfo fromJson(Map<String, dynamic> json) {
return EnvironmentInfo(
skyboxUri: json['skyboxUri'],
iblUri: json['iblUri'],
);
}
}
class Color {
final double r;
final double g;
final double b;
final double a;
Color({required this.r, required this.g, required this.b, this.a = 1.0});
Map<String, dynamic> toJson() => {
'r': r,
'g': g,
'b': b,
'a': a,
};
static Color fromJson(Map<String, dynamic> json) {
return Color(
r: json['r'],
g: json['g'],
b: json['b'],
a: json['a'],
);
}
}

View File

@@ -1,334 +0,0 @@
import 'dart:math';
import 'dart:typed_data';
import '../../thermion_dart.dart';
class GeometryHelper {
static Geometry sphere({bool normals = true, bool uvs = true}) {
int latitudeBands = 20;
int longitudeBands = 20;
List<double> verticesList = [];
List<double> normalsList = [];
List<double> uvsList = [];
List<int> indices = [];
for (int latNumber = 0; latNumber <= latitudeBands; latNumber++) {
double theta = latNumber * pi / latitudeBands;
double sinTheta = sin(theta);
double cosTheta = cos(theta);
for (int longNumber = 0; longNumber <= longitudeBands; longNumber++) {
double phi = longNumber * 2 * pi / longitudeBands;
double sinPhi = sin(phi);
double cosPhi = cos(phi);
double x = cosPhi * sinTheta;
double y = cosTheta;
double z = sinPhi * sinTheta;
verticesList.addAll([x, y, z]);
normalsList.addAll([x, y, z]);
uvsList.addAll([longNumber / longitudeBands, latNumber / latitudeBands]);
}
}
for (int latNumber = 0; latNumber < latitudeBands; latNumber++) {
for (int longNumber = 0; longNumber < longitudeBands; longNumber++) {
int first = (latNumber * (longitudeBands + 1)) + longNumber;
int second = first + longitudeBands + 1;
indices.addAll([first, second, first + 1, second, second + 1, first + 1]);
}
}
Float32List vertices = Float32List.fromList(verticesList);
Float32List? _normals = normals ? Float32List.fromList(normalsList) : null;
Float32List? _uvs = uvs ? Float32List.fromList(uvsList) : null;
return Geometry(vertices, indices, normals: _normals, uvs: _uvs);
}
static Geometry cube({bool normals = true, bool uvs = true}) {
final vertices = Float32List.fromList([
// Front face
-1, -1, 1,
1, -1, 1,
1, 1, 1,
-1, 1, 1,
// Back face
-1, -1, -1,
-1, 1, -1,
1, 1, -1,
1, -1, -1,
// Top face
-1, 1, -1,
-1, 1, 1,
1, 1, 1,
1, 1, -1,
// Bottom face
-1, -1, -1,
1, -1, -1,
1, -1, 1,
-1, -1, 1,
// Right face
1, -1, -1,
1, 1, -1,
1, 1, 1,
1, -1, 1,
// Left face
-1, -1, -1,
-1, -1, 1,
-1, 1, 1,
-1, 1, -1,
]);
final _normals = normals ? Float32List.fromList([
// Front face
0, 0, 1,
0, 0, 1,
0, 0, 1,
0, 0, 1,
// Back face
0, 0, -1,
0, 0, -1,
0, 0, -1,
0, 0, -1,
// Top face
0, 1, 0,
0, 1, 0,
0, 1, 0,
0, 1, 0,
// Bottom face
0, -1, 0,
0, -1, 0,
0, -1, 0,
0, -1, 0,
// Right face
1, 0, 0,
1, 0, 0,
1, 0, 0,
1, 0, 0,
// Left face
-1, 0, 0,
-1, 0, 0,
-1, 0, 0,
-1, 0, 0,
]) : null;
final _uvs = uvs ? Float32List.fromList([
// Front face
1/3, 1/3,
2/3, 1/3,
2/3, 2/3,
1/3, 2/3,
// Back face
2/3, 2/3,
2/3, 1,
1, 1,
1, 2/3,
// Top face
1/3, 0,
1/3, 1/3,
2/3, 1/3,
2/3, 0,
// Bottom face
1/3, 2/3,
2/3, 2/3,
2/3, 1,
1/3, 1,
// Right face
2/3, 1/3,
2/3, 2/3,
1, 2/3,
1, 1/3,
// Left face
0, 1/3,
1/3, 1/3,
1/3, 2/3,
0, 2/3,
]) : null;
final indices = [
// Front face
0, 1, 2, 0, 2, 3,
// Back face
4, 5, 6, 4, 6, 7,
// Top face
8, 9, 10, 8, 10, 11,
// Bottom face
12, 13, 14, 12, 14, 15,
// Right face
16, 17, 18, 16, 18, 19,
// Left face
20, 21, 22, 20, 22, 23
];
return Geometry(vertices, indices, normals: _normals, uvs: _uvs);
}
static Geometry cylinder({double radius = 1.0, double length = 1.0, bool normals = true, bool uvs = true }) {
int segments = 32;
List<double> verticesList = [];
List<double> normalsList = [];
List<double> uvsList = [];
List<int> indices = [];
// Create vertices, normals, and UVs
for (int i = 0; i <= segments; i++) {
double theta = i * 2 * pi / segments;
double x = radius * cos(theta);
double z = radius * sin(theta);
// Top circle
verticesList.addAll([x, length / 2, z]);
normalsList.addAll([x / radius, 0, z / radius]);
uvsList.addAll([i / segments, 1]);
// Bottom circle
verticesList.addAll([x, -length / 2, z]);
normalsList.addAll([x / radius, 0, z / radius]);
uvsList.addAll([i / segments, 0]);
}
// Create indices
for (int i = 0; i < segments; i++) {
int topFirst = i * 2;
int topSecond = (i + 1) * 2;
int bottomFirst = topFirst + 1;
int bottomSecond = topSecond + 1;
// Top face (counter-clockwise)
indices.addAll([segments * 2, topSecond, topFirst]);
// Bottom face (counter-clockwise when viewed from below)
indices.addAll([segments * 2 + 1, bottomFirst, bottomSecond]);
// Side faces (counter-clockwise)
indices.addAll([topFirst, bottomFirst, topSecond]);
indices.addAll([bottomFirst, bottomSecond, topSecond]);
}
// Add center vertices, normals, and UVs for top and bottom faces
verticesList.addAll([0, length / 2, 0]); // Top center
normalsList.addAll([0, 1, 0]);
uvsList.addAll([0.5, 0.5]); // Center of top face
verticesList.addAll([0, -length / 2, 0]); // Bottom center
normalsList.addAll([0, -1, 0]);
uvsList.addAll([0.5, 0.5]); // Center of bottom face
// Add top and bottom face normals and UVs
for (int i = 0; i <= segments; i++) {
normalsList.addAll([0, 1, 0]); // Top face normal
normalsList.addAll([0, -1, 0]); // Bottom face normal
double u = 0.5 + 0.5 * cos(i * 2 * pi / segments);
double v = 0.5 + 0.5 * sin(i * 2 * pi / segments);
uvsList.addAll([u, v]); // Top face UV
uvsList.addAll([u, v]); // Bottom face UV
}
Float32List vertices = Float32List.fromList(verticesList);
Float32List? _normals = normals ? Float32List.fromList(normalsList) : null;
Float32List? _uvs = uvs ? Float32List.fromList(uvsList) : null;
return Geometry(vertices, indices, normals: _normals, uvs: _uvs);
}
static Geometry conic({double radius = 1.0, double length = 1.0, bool normals = true, bool uvs = true}) {
int segments = 32;
List<double> verticesList = [];
List<double> normalsList = [];
List<double> uvsList = [];
List<int> indices = [];
// Create vertices, normals, and UVs
for (int i = 0; i <= segments; i++) {
double theta = i * 2 * pi / segments;
double x = radius * cos(theta);
double z = radius * sin(theta);
// Base circle
verticesList.addAll([x, 0, z]);
// Calculate normal for the side
double nx = x / sqrt(x * x + length * length);
double nz = z / sqrt(z * z + length * length);
double ny = radius / sqrt(radius * radius + length * length);
normalsList.addAll([nx, ny, nz]);
// UV coordinates
uvsList.addAll([i / segments, 0]);
}
// Apex
verticesList.addAll([0, length, 0]);
normalsList.addAll([0, 1, 0]); // Normal at apex points straight up
uvsList.addAll([0.5, 1]); // UV for apex
// Create indices
for (int i = 0; i < segments; i++) {
// Base face (fixed to counterclockwise)
indices.addAll([segments + 1, i + 1, i]);
// Side faces (already correct)
indices.addAll([i, segments, i + 1]);
}
// Add base face normals and UVs
for (int i = 0; i <= segments; i++) {
normalsList.addAll([0, -1, 0]); // Base face normal
double u = 0.5 + 0.5 * cos(i * 2 * pi / segments);
double v = 0.5 + 0.5 * sin(i * 2 * pi / segments);
uvsList.addAll([u, v]); // Base face UV
}
Float32List vertices = Float32List.fromList(verticesList);
Float32List? _normals = normals ? Float32List.fromList(normalsList) : null;
Float32List? _uvs = uvs ? Float32List.fromList(uvsList) : null;
return Geometry(vertices, indices, normals: _normals, uvs: _uvs);
}
static Geometry plane({double width = 1.0, double height = 1.0, bool normals = true, bool uvs = true}) {
Float32List vertices = Float32List.fromList([
-width / 2, 0, -height / 2,
width / 2, 0, -height / 2,
width / 2, 0, height / 2,
-width / 2, 0, height / 2,
]);
Float32List? _normals = normals ? Float32List.fromList([
0, 1, 0,
0, 1, 0,
0, 1, 0,
0, 1, 0,
]) : null;
Float32List? _uvs = uvs ? Float32List.fromList([
0, 0,
1, 0,
1, 1,
0, 1,
]) : null;
List<int> indices = [
0, 2, 1,
0, 3, 2,
];
return Geometry(vertices, indices, normals: _normals, uvs: _uvs);
}
}

View File

@@ -1,127 +0,0 @@
import 'dart:async';
import 'package:thermion_dart/src/viewer/viewer.dart';
import 'package:vector_math/vector_math_64.dart';
abstract class Gizmo {
bool get isVisible;
bool get isHovered;
void reset();
Future attach(ThermionEntity entity);
Future detach();
Stream<Aabb2> get boundingBox;
void checkHover(int x, int y);
}
abstract class BaseGizmo extends Gizmo {
final ThermionEntity x;
final ThermionEntity y;
final ThermionEntity z;
final ThermionEntity center;
ThermionEntity? _activeAxis;
ThermionEntity? _activeEntity;
ThermionViewer viewer;
bool _visible = false;
bool get isVisible => _visible;
bool _isHovered = false;
bool get isHovered => _isHovered;
final Set<ThermionEntity> ignore;
Stream<Aabb2> get boundingBox => _boundingBoxController.stream;
final _boundingBoxController = StreamController<Aabb2>.broadcast();
BaseGizmo({required this.x, required this.y, required this.z, required this.center, required this.viewer,
this.ignore = const <ThermionEntity>{}}) {
onPick(_onGizmoPickResult);
}
final _stopwatch = Stopwatch();
double _transX = 0.0;
double _transY = 0.0;
Future translate(double transX, double transY) async {
if (!_stopwatch.isRunning) {
_stopwatch.start();
}
_transX += transX;
_transY += transY;
if (_stopwatch.elapsedMilliseconds < 16) {
return;
}
final axis = Vector3(_activeAxis == x ? 1.0 : 0.0,
_activeAxis == y ? 1.0 : 0.0, _activeAxis == z ? 1.0 : 0.0);
await viewer.queueRelativePositionUpdateWorldAxis(
_activeEntity!,
_transX,
-_transY, // flip the sign because "up" in NDC Y axis is positive, but negative in Flutter
axis.x,
axis.y,
axis.z);
_transX = 0;
_transY = 0;
_stopwatch.reset();
}
void reset() {
_activeAxis = null;
}
void _onGizmoPickResult(FilamentPickResult result) async {
if (result.entity == x || result.entity == y || result.entity == z) {
_activeAxis = result.entity;
_isHovered = true;
} else if (result.entity == 0) {
_activeAxis = null;
_isHovered = false;
} else {
throw Exception("Unexpected gizmo pick result");
}
}
Future attach(ThermionEntity entity) async {
_activeAxis = null;
if (entity == _activeEntity) {
return;
}
if (entity == center) {
_activeEntity = null;
return;
}
_visible = true;
if (_activeEntity != null) {
await viewer.removeStencilHighlight(_activeEntity!);
}
_activeEntity = entity;
await viewer.setParent(center, entity, preserveScaling: false);
_boundingBoxController.sink.add(await viewer.getViewportBoundingBox(x));
}
Future detach() async {
await setVisibility(false);
}
@override
void checkHover(int x, int y) {
pick(x, y);
}
Future pick(int x, int y);
Future setVisibility(bool visible);
void onPick(void Function(PickResult result) callback);
}

View File

@@ -1,38 +0,0 @@
// Helper function to convert double4x4 to Matrix4
import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_dart.g.dart';
import 'package:vector_math/vector_math_64.dart';
import 'dart:ffi';
Matrix4 double4x4ToMatrix4(double4x4 mat) {
return Matrix4.fromList([
mat.col1[0],
mat.col1[1],
mat.col1[2],
mat.col1[3],
mat.col2[0],
mat.col2[1],
mat.col2[2],
mat.col2[3],
mat.col3[0],
mat.col3[1],
mat.col3[2],
mat.col3[3],
mat.col4[0],
mat.col4[1],
mat.col4[2],
mat.col4[3],
]);
}
double4x4 matrix4ToDouble4x4(Matrix4 mat) {
final out = Struct.create<double4x4>();
for (int i = 0; i < 4; i++) {
out.col1[i] = mat.storage[i];
out.col2[i] = mat.storage[i + 4];
out.col3[i] = mat.storage[i + 8];
out.col4[i] = mat.storage[i + 12];
}
return out;
}

View File

@@ -1,85 +0,0 @@
import 'shared_types/shared_types.dart';
///
/// To ensure we can easily store/recreate a particular, [ThermionViewer] will raise an event whenever an
/// entity is added/removed.
///
enum EventType { EntityAdded, EntityRemoved, EntityHidden, EntityRevealed, ClearLights }
///
/// An "entity added" event must provide sufficient detail to enable that asset to be reloaded in future.
/// This requires a bit more legwork because entities may be lights (direct/indirect), geometry or gltf.
///
enum EntityType { Geometry, Gltf, DirectLight, IBL }
class SceneUpdateEvent {
late final ThermionEntity? entity;
late final EventType eventType;
EntityType get addedEntityType {
if (_directLight != null) {
return EntityType.DirectLight;
} else if (_ibl != null) {
return EntityType.IBL;
} else if (_gltf != null) {
return EntityType.Gltf;
} else if (_geometry != null) {
return EntityType.Geometry;
} else {
throw Exception("Unknown entity type");
}
}
DirectLight? _directLight;
IBL? _ibl;
GLTF? _gltf;
Geometry? _geometry;
SceneUpdateEvent.remove(this.entity) {
this.eventType = EventType.EntityRemoved;
}
SceneUpdateEvent.reveal(this.entity) {
this.eventType = EventType.EntityRevealed;
}
SceneUpdateEvent.hide(this.entity) {
this.eventType = EventType.EntityHidden;
}
SceneUpdateEvent.addDirectLight(this.entity, this._directLight) {
this.eventType = EventType.EntityAdded;
}
SceneUpdateEvent.addIbl(this.entity, this._ibl) {
this.eventType = EventType.EntityAdded;
}
SceneUpdateEvent.addGltf(this.entity, this._gltf) {
this.eventType = EventType.EntityAdded;
}
SceneUpdateEvent.addGeometry(this.entity, this._geometry) {
this.eventType = EventType.EntityAdded;
}
SceneUpdateEvent.clearLights() {
this.eventType = EventType.ClearLights;
}
DirectLight getDirectLight() {
return _directLight!;
}
IBL getAsIBL() {
return _ibl!;
}
GLTF getAsGLTF() {
return _gltf!;
}
Geometry getAsGeometry() {
return _geometry!;
}
}

View File

@@ -1,91 +0,0 @@
import 'dart:ffi';
import 'package:vector_math/vector_math_64.dart';
import '../../../../utils/matrix.dart';
import '../../thermion_viewer_base.dart';
import 'thermion_dart.g.dart' as g;
class FFICamera extends Camera {
final Pointer<g.TCamera> camera;
final Pointer<g.TEngine> engine;
late ThermionEntity _entity;
FFICamera(this.camera, this.engine) {
_entity = g.Camera_getEntity(camera);
}
@override
Future setProjectionMatrixWithCulling(
Matrix4 projectionMatrix, double near, double far) async {
g.Camera_setCustomProjectionWithCulling(
camera, matrix4ToDouble4x4(projectionMatrix), near, far);
}
Future<Matrix4> getModelMatrix() async {
return double4x4ToMatrix4(g.Camera_getModelMatrix(camera));
}
@override
Future setTransform(Matrix4 transform) async {
var entity = g.Camera_getEntity(camera);
g.Engine_setTransform(engine, entity, matrix4ToDouble4x4(transform));
}
@override
Future setLensProjection(
{double near = kNear,
double far = kFar,
double aspect = 1.0,
double focalLength = kFocalLength}) async {
g.Camera_setLensProjection(camera, near, far, aspect, focalLength);
}
@override
ThermionEntity getEntity() {
return _entity;
}
@override
Future setModelMatrix(Matrix4 matrix) async {
g.Camera_setModelMatrix(camera, matrix4ToDouble4x4(matrix));
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is FFICamera &&
runtimeType == other.runtimeType &&
camera == other.camera;
@override
int get hashCode => camera.hashCode;
@override
Future<double> getCullingFar() async {
return g.Camera_getCullingFar(camera);
}
@override
Future<double> getNear() async {
return g.Camera_getNear(camera);
}
@override
Future<double> getFocalLength() async {
return g.Camera_getFocalLength(camera);
}
@override
Future<Matrix4> getViewMatrix() async {
return double4x4ToMatrix4(g.Camera_getViewMatrix(camera));
}
@override
Future setProjection(Projection projection, double left, double right,
double bottom, double top, double near, double far) async {
var pType = g.Projection.values[projection.index];
g.Camera_setProjection(camera, pType, left,
right, bottom, top, near, far);
}
}

View File

@@ -1,50 +0,0 @@
import 'dart:async';
import 'dart:ffi';
import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_dart.g.dart';
import '../../../../utils/gizmo.dart';
import '../../../viewer.dart';
class FFIGizmo extends BaseGizmo {
Pointer<TGizmo> pointer;
late NativeCallable<GizmoPickCallbackFunction> _nativeCallback;
FFIGizmo(
this.pointer, ThermionViewer viewer) : super(x: 0, y: 0, z: 0, center: 0, viewer: viewer) {
_nativeCallback =
NativeCallable<GizmoPickCallbackFunction>.listener(_onPickResult);
}
///
/// The result(s) of calling [pickGizmo] (see below).
///
// Stream<PickResult> get onPick => _pickResultController.stream;
// final _pickResultController = StreamController<PickResult>.broadcast();
void Function(PickResult)? _callback;
void onPick(void Function(PickResult) callback) {
_callback = callback;
}
void _onPickResult(DartEntityId entityId, int x, int y, Pointer<TView> view) {
_callback?.call((entity: entityId, x: x, y: y));
}
///
/// Used to test whether a Gizmo is 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 [gizmoPickResult] 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 ThermionWidget).
///
@override
Future pick(int x, int y) async {
Gizmo_pick(pointer, x.toInt(), y, _nativeCallback.nativeFunction);
}
@override
Future setVisibility(bool visible) async {
Gizmo_setVisibility(pointer, visible);
}
}

View File

@@ -1,69 +0,0 @@
import 'dart:ffi';
import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_dart.g.dart';
import 'package:thermion_dart/src/viewer/src/shared_types/shared_types.dart';
import 'ffi_camera.dart';
import 'thermion_viewer_ffi.dart';
class FFIView extends View {
final Pointer<TView> view;
final Pointer<TViewer> viewer;
FFIView(this.view, this.viewer);
@override
Future updateViewport(int width, int height) async {
View_updateViewport(view, width, height);
}
@override
Future setRenderTarget(covariant FFIRenderTarget renderTarget) async {
View_setRenderTarget(view, renderTarget.renderTarget);
}
@override
Future setCamera(FFICamera camera) async {
View_setCamera(view, camera.camera);
}
@override
Future<Viewport> getViewport() async {
TViewport vp = View_getViewport(view);
return Viewport(vp.left, vp.bottom, vp.width, vp.height);
}
@override
Camera getCamera() {
final engine = Viewer_getEngine(viewer);
return FFICamera(View_getCamera(view), engine);
}
@override
Future setAntiAliasing(bool msaa, bool fxaa, bool taa) async {
View_setAntiAliasing(view, msaa, fxaa, taa);
}
@override
Future setPostProcessing(bool enabled) async {
View_setPostProcessing(view, enabled);
}
Future setRenderable(bool renderable, FFISwapChain swapChain) async {
Viewer_setViewRenderable(viewer, swapChain.swapChain, view, renderable);
}
@override
Future setFrustumCullingEnabled(bool enabled) async {
View_setFrustumCullingEnabled(view, enabled);
}
@override
Future setBloom(double strength) async {
View_setBloom(view, strength);
}
@override
Future setToneMapper(ToneMapper mapper) async {
final engine = await Viewer_getEngine(viewer);
View_setToneMappingRenderThread(view, engine, mapper.index);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +0,0 @@
library;
export 'src/thermion_viewer_ffi.dart' show ThermionViewerFFI;

View File

@@ -1,30 +0,0 @@
import 'package:vector_math/vector_math_64.dart';
import '../thermion_viewer_base.dart';
enum Projection { Perspective, Orthographic }
abstract class Camera {
Future setProjection(Projection projection, double left, double right,
double bottom, double top, double near, double far);
Future setProjectionMatrixWithCulling(
Matrix4 projectionMatrix, double near, double far);
Future setLensProjection(
{double near = kNear,
double far = kFar,
double aspect = 1.0,
double focalLength = kFocalLength});
Future<Matrix4> getViewMatrix();
Future<Matrix4> getModelMatrix();
Future setModelMatrix(Matrix4 matrix);
ThermionEntity getEntity();
Future setTransform(Matrix4 transform);
Future<double> getNear();
Future<double> getCullingFar();
Future<double> getFocalLength();
}

View File

@@ -1,11 +0,0 @@
library;
export 'geometry.dart';
export 'gltf.dart';
export 'light_options.dart';
// a handle that can be safely passed back to the rendering layer to manipulate an Entity
typedef ThermionEntity = int;

View File

@@ -1,32 +0,0 @@
import 'dart:typed_data';
import '../../viewer.dart';
class Geometry {
final Float32List vertices;
final Uint16List indices;
final Float32List normals;
final Float32List uvs;
final PrimitiveType primitiveType;
Geometry(
this.vertices,
List<int> indices, {
Float32List? normals,
Float32List? uvs,
this.primitiveType = PrimitiveType.TRIANGLES,
}) : indices = Uint16List.fromList(indices),
normals = normals ?? Float32List(0),
uvs = uvs ?? Float32List(0) {
assert(this.uvs.length == 0 || this.uvs.length == (vertices.length ~/ 3) * 2);
}
void scale(double factor) {
for (int i = 0; i < vertices.length; i++) {
vertices[i] = vertices[i] * factor;
}
}
bool get hasNormals => normals.isNotEmpty;
bool get hasUVs => uvs.isNotEmpty;
}

View File

@@ -1,6 +0,0 @@
class GLTF {
final String uri;
final int numInstances;
GLTF(this.uri, this.numInstances);
}

View File

@@ -1,7 +0,0 @@
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,
}

View File

@@ -1,98 +0,0 @@
import 'dart:math';
import 'package:vector_math/vector_math_64.dart' as v;
import 'package:vector_math/vector_math_64.dart';
import 'light.dart';
class IBL {
String? iblPath;
final double iblIntensity;
IBL(this.iblIntensity);
}
class DirectLight {
final LightType type;
final double color;
final double intensity;
final bool castShadows;
late final v.Vector3 position;
late final v.Vector3 direction;
final double falloffRadius;
final double spotLightConeInner;
final double spotLightConeOuter;
final double sunAngularRadius;
final double sunHaloSize;
final double sunHaloFallof;
DirectLight({
required this.type,
required this.color,
required this.intensity,
this.castShadows = false,
required this.direction,
required this.position,
this.falloffRadius = 1.0,
this.spotLightConeInner = pi / 8,
this.spotLightConeOuter = pi / 4,
this.sunAngularRadius = 0.545,
this.sunHaloSize = 10.0,
this.sunHaloFallof = 80.0,
});
DirectLight.point({
double color = 6500,
double intensity = 100000,
bool castShadows = false,
Vector3? position,
double falloffRadius = 1.0,
}) : this(
type: LightType.POINT,
color: color,
intensity: intensity,
castShadows: castShadows,
position: position ?? Vector3(0, 1, 0),
direction: Vector3.zero(),
falloffRadius: falloffRadius,
);
DirectLight.sun({
double color = 6500,
double intensity = 100000,
bool castShadows = true,
Vector3? direction,
double sunAngularRadius = 0.545,
double sunHaloSize = 10.0,
double sunHaloFalloff = 80.0,
}) : this(
type: LightType.DIRECTIONAL,
color: color,
intensity: intensity,
castShadows: castShadows,
position: Vector3(0, 0, 0),
direction: direction ?? Vector3(0, -1, 0),
sunAngularRadius: sunAngularRadius,
sunHaloSize: sunHaloSize,
sunHaloFallof: sunHaloFalloff,
);
DirectLight.spot({
double color = 6500,
double intensity = 100000,
bool castShadows = true,
Vector3? position,
Vector3? direction,
double falloffRadius = 1.0,
double spotLightConeInner = pi / 8,
double spotLightConeOuter = pi / 4,
}) : this(
type: LightType.SPOT,
color: color,
intensity: intensity,
castShadows: castShadows,
position: position ?? Vector3(0, 1, 0),
direction: direction ?? Vector3(0, -1, 0),
falloffRadius: falloffRadius,
spotLightConeInner: spotLightConeInner,
spotLightConeOuter: spotLightConeOuter,
);
}

View File

@@ -1,4 +0,0 @@
// see filament Manipulator.h for more details
@Deprecated(
"This is used the native pointer manipulator Prefer InputHandler instead")
enum ManipulatorMode { ORBIT, MAP, FREE_FLIGHT }

View File

@@ -1,7 +0,0 @@
abstract class MaterialInstance {
Future setDepthWriteEnabled(bool enabled);
Future setDepthCullingEnabled(bool enabled);
Future setParameterFloat2(String name, double x, double y);
}
enum AlphaMode { OPAQUE, MASK, BLEND }

View File

@@ -1,5 +0,0 @@
// "picking" means clicking/tapping on the viewport, and unprojecting the X/Y coordinate to determine whether any renderable entities were present at those coordinates.
import '../../viewer.dart';
typedef FilamentPickResult = ({ThermionEntity entity, int x, int y});
typedef PickResult = FilamentPickResult;

View File

@@ -1,10 +0,0 @@
// 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
}

View File

@@ -1,3 +0,0 @@
abstract class RenderTarget {
Future destroy();
}

View File

@@ -1,6 +0,0 @@
enum ShadowType {
PCF, //!< percentage-closer filtered shadows (default)
VSM, //!< variance shadows
DPCF, //!< PCF with contact hardening simulation
PCSS, //!< PCF with soft shadows and contact hardening
}

View File

@@ -1,16 +0,0 @@
library shared_types;
export 'swap_chain.dart';
export 'view.dart';
export 'render_target.dart';
export 'camera.dart';
export 'material.dart';
export 'texture.dart';
export 'entities.dart';
export 'light.dart';
export 'shadow.dart';
export 'manipulator.dart';
export 'pick_result.dart';
export 'primitive.dart';
export 'texture_details.dart';
export 'tone_mapper.dart';

View File

@@ -1,3 +0,0 @@
abstract class SwapChain {
Future destroy();
}

View File

@@ -1,3 +0,0 @@
abstract class ThermionTexture {
}

View File

@@ -1,14 +0,0 @@
///
/// This represents the backing "surface" that we render into.
/// "Texture" here is a misnomer as it is only a render target texture on certain platforms.
///
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});
}

View File

@@ -1 +0,0 @@
enum ToneMapper { ACES, FILMIC, LINEAR }

View File

@@ -1,30 +0,0 @@
import 'package:thermion_dart/thermion_dart.dart';
import 'swap_chain.dart';
///
/// The viewport currently attached to a [View].
///
/// The dimensions here are guaranteed to be in physical pixels.
///
class Viewport {
final int left;
final int bottom;
final int width;
final int height;
Viewport(this.left, this.bottom, this.width, this.height);
}
abstract class View {
Future<Viewport> getViewport();
Future updateViewport(int width, int height);
Future setRenderTarget(covariant RenderTarget? renderTarget);
Future setCamera(covariant Camera camera);
Camera getCamera();
Future setPostProcessing(bool enabled);
Future setAntiAliasing(bool msaa, bool fxaa, bool taa);
Future setRenderable(bool renderable, covariant SwapChain swapChain);
Future setFrustumCullingEnabled(bool enabled);
Future setToneMapper(ToneMapper mapper);
Future setBloom(double strength);
}

View File

@@ -1,759 +0,0 @@
// @JS()
// library thermion_flutter_js;
// import 'dart:js_interop';
// import 'package:logging/logging.dart';
// import 'package:vector_math/vector_math_64.dart' as v64;
// import 'package:animation_tools_dart/animation_tools_dart.dart';
// import 'dart:js_interop_unsafe';
// import 'package:vector_math/vector_math_64.dart';
// import '../../../viewer.dart';
// import 'thermion_viewer_js_shim.dart';
// ///
// /// A (Dart) class that wraps a (Dart) instance of [ThermionViewer],
// /// but exported to JS by binding to a global property.
// /// This is effectively an implementation of [ThermionViewerJSShim];
// /// allowing users to interact with an instance of [ThermionViewer]
// /// (presumably compiled to WASM) from any Javascript context (including
// /// the browser console).
// ///
// @JSExport()
// class ThermionViewerJSDartBridge {
// final _logger = Logger("ThermionViewerJSDartBridge");
// final ThermionViewer viewer;
// ThermionViewerJSDartBridge(this.viewer);
// void bind({String globalPropertyName = "thermionViewer"}) {
// var wrapper = createJSInteropWrapper<ThermionViewerJSDartBridge>(this)
// as ThermionViewerJSShim;
// 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<JSUint8Array> capture() {
// return viewer.capture().then((captured) => captured.toJS).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) {
// _logger.info("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(ThermionEntity light) => viewer.removeLight(light).toJS;
// @JSExport()
// JSPromise clearLights() => viewer.clearLights().toJS;
// @JSExport()
// JSPromise<JSNumber> loadGlb(String path, {int numInstances = 1}) {
// _logger.info("Loading GLB from path $path with numInstances $numInstances");
// return viewer
// .loadGlb(path, numInstances: numInstances)
// .then((entity) => entity.toJS)
// .catchError((err) {
// _logger.info("Error: $err");
// }).toJS;
// }
// @JSExport()
// JSPromise<JSNumber> createInstance(ThermionEntity entity) {
// return viewer.createInstance(entity).then((instance) => instance.toJS).toJS;
// }
// @JSExport()
// JSPromise<JSNumber> getInstanceCount(ThermionEntity entity) =>
// viewer.getInstanceCount(entity).then((v) => v.toJS).toJS;
// @JSExport()
// JSPromise<JSArray<JSNumber>> getInstances(ThermionEntity entity) {
// return viewer
// .getInstances(entity)
// .then((instances) =>
// instances.map((instance) => instance.toJS).toList().toJS)
// .toJS;
// }
// @JSExport()
// JSPromise<JSNumber> loadGltf(String path, String relativeResourcePath,
// {bool keepData = false}) {
// return viewer
// .loadGltf(path, relativeResourcePath, keepData: keepData)
// .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(
// ThermionEntity entity, JSArray<JSNumber> weights) {
// var dartWeights = weights.toDart.map((w) => w.toDartDouble).toList();
// return viewer.setMorphTargetWeights(entity, dartWeights).toJS;
// }
// @JSExport()
// JSPromise<JSArray<JSString>> getMorphTargetNames(
// ThermionEntity entity, ThermionEntity childEntity) {
// var morphTargetNames = viewer
// .getMorphTargetNames(entity, childEntity)
// .then((v) => v.map((s) => s.toJS).toList().toJS);
// return morphTargetNames.toJS;
// }
// @JSExport()
// JSPromise<JSArray<JSString>> getBoneNames(
// ThermionEntity entity, int skinIndex) {
// return viewer
// .getBoneNames(entity, skinIndex: skinIndex)
// .then((v) => v.map((s) => s.toJS).toList().toJS)
// .toJS;
// }
// @JSExport()
// JSPromise<JSArray<JSString>> getAnimationNames(ThermionEntity entity) =>
// viewer
// .getAnimationNames(entity)
// .then((v) => v.map((s) => s.toJS).toList().toJS)
// .toJS;
// @JSExport()
// JSPromise<JSNumber> getAnimationDuration(
// ThermionEntity entity, int animationIndex) =>
// viewer
// .getAnimationDuration(entity, animationIndex)
// .then((v) => v.toJS)
// .toJS;
// @JSExport()
// void clearMorphAnimationData(ThermionEntity entity) {
// viewer.clearMorphAnimationData(entity);
// }
// @JSExport()
// JSPromise setMorphAnimationData(
// ThermionEntity 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) {
// _logger.severe("ERROR SETTING MORPH ANIMATION DATA : $err\n$st");
// return null;
// });
// return result.toJS;
// } catch (err, st) {
// _logger.severe(err);
// _logger.severe(st);
// rethrow;
// }
// }
// @JSExport()
// JSPromise resetBones(ThermionEntity entity) => viewer.resetBones(entity).toJS;
// @JSExport()
// JSPromise addBoneAnimation(
// ThermionEntity 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(ThermionEntity 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(ThermionEntity entity, int index,
// {bool loop = false,
// bool reverse = false,
// bool replaceActive = true,
// double crossfade = 0.0,
// double startOffset = 0.0}) =>
// viewer
// .playAnimation(entity, index,
// loop: loop,
// reverse: reverse,
// replaceActive: replaceActive,
// crossfade: crossfade,
// startOffset: startOffset)
// .toJS;
// @JSExport()
// JSPromise playAnimationByName(ThermionEntity 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(
// ThermionEntity entity, int index, int animationFrame) =>
// viewer
// .setAnimationFrame(
// entity,
// index,
// animationFrame,
// )
// .toJS;
// @JSExport()
// JSPromise stopAnimation(ThermionEntity entity, int animationIndex) =>
// viewer.stopAnimation(entity, animationIndex).toJS;
// @JSExport()
// JSPromise stopAnimationByName(ThermionEntity entity, String name) =>
// viewer.stopAnimationByName(entity, name).toJS;
// @JSExport()
// JSPromise setCamera(ThermionEntity entity, String? name) =>
// viewer.setCamera(entity, name).toJS;
// @JSExport()
// JSPromise setMainCamera() => viewer.setMainCamera().toJS;
// @JSExport()
// JSPromise<JSNumber> getMainCamera() {
// throw UnimplementedError("TODO");
// // return viewer.getMainCamera().then((camera) => camera.toJS).toJS;
// }
// @JSExport()
// JSPromise setParent(
// ThermionEntity child, ThermionEntity parent, bool preserveScaling) {
// return viewer
// .setParent(child, parent, preserveScaling: preserveScaling)
// .toJS;
// }
// @JSExport()
// JSPromise setCameraFov(double degrees, bool horizontal) =>
// viewer.setCameraFov(degrees, horizontal: horizontal).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(ThermionEntity 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(ThermionEntity 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(ThermionEntity entity) =>
// viewer.transformToUnitCube(entity).toJS;
// @JSExport()
// JSPromise setPosition(ThermionEntity entity, double x, double y, double z) =>
// viewer.setPosition(entity, x, y, z).toJS;
// @JSExport()
// JSPromise setScale(ThermionEntity entity, double scale) =>
// viewer.setScale(entity, scale).toJS;
// @JSExport()
// JSPromise setRotation(
// ThermionEntity entity, double rads, double x, double y, double z) =>
// viewer.setRotation(entity, rads, x, y, z).toJS;
// @JSExport()
// JSPromise queuePositionUpdate(
// ThermionEntity entity, double x, double y, double z, bool relative) =>
// viewer
// .queuePositionUpdate(
// entity,
// x,
// y,
// z,
// relative: relative,
// )
// .toJS;
// @JSExport()
// JSPromise queueRotationUpdate(ThermionEntity entity, double rads, double x,
// double y, double z, bool relative) =>
// viewer
// .queueRotationUpdate(
// entity,
// rads,
// x,
// y,
// z,
// relative: relative,
// )
// .toJS;
// @JSExport()
// JSPromise queueRotationUpdateQuat(
// ThermionEntity 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(
// ThermionEntity entity, JSArray<JSNumber> rotation) =>
// throw UnimplementedError();
// @JSExport()
// JSPromise reveal(ThermionEntity entity, String? meshName) =>
// viewer.reveal(entity, meshName).toJS;
// @JSExport()
// JSPromise hide(ThermionEntity entity, String? meshName) =>
// viewer.hide(entity, meshName).toJS;
// @JSExport()
// void pick(int x, int y) => viewer.pick(x, y);
// @JSExport()
// String? getNameForEntity(ThermionEntity 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(
// ThermionEntity parent, bool renderableOnly) {
// return viewer
// .getChildEntities(
// parent,
// renderableOnly,
// )
// .then((entities) => entities.map((entity) => entity.toJS).toList().toJS)
// .onError((e, st) async {
// _logger.severe("Error : $e\n$st");
// return <JSNumber>[].toJS;
// }).toJS;
// }
// @JSExport()
// JSPromise<JSNumber> getChildEntity(ThermionEntity parent, String childName) {
// return viewer
// .getChildEntity(
// parent,
// childName,
// )
// .then((entity) => entity.toJS)
// .onError((e, st) async {
// _logger.severe("Error getChildEntity : $e\n$st");
// return 0.toJS;
// }).toJS;
// }
// @JSExport()
// JSPromise<JSArray<JSString>> getChildEntityNames(
// ThermionEntity 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(ThermionEntity entity) =>
// viewer.addAnimationComponent(entity).toJS;
// @JSExport()
// JSPromise removeAnimationComponent(ThermionEntity entity) =>
// viewer.removeAnimationComponent(entity).toJS;
// @JSExport()
// JSPromise getParent(ThermionEntity entity) =>
// viewer.removeAnimationComponent(entity).toJS;
// @JSExport()
// JSPromise getBone(ThermionEntity entity, int boneIndex, int skinIndex) =>
// viewer.getBone(entity, boneIndex, skinIndex: skinIndex).toJS;
// @JSExport()
// JSPromise<JSArray<JSNumber>> getLocalTransform(ThermionEntity entity) {
// return viewer
// .getLocalTransform(entity)
// .then((t) => t.storage.map((v) => v.toJS).toList().toJS)
// .toJS;
// }
// @JSExport()
// JSPromise<JSArray<JSNumber>> getWorldTransform(ThermionEntity entity) {
// return viewer
// .getWorldTransform(entity)
// .then((t) => t.storage.map((v) => v.toJS).toList().toJS)
// .toJS;
// }
// @JSExport()
// JSPromise setTransform(ThermionEntity entity, JSArray<JSNumber> transform) {
// return viewer
// .setTransform(
// entity,
// Matrix4.fromList(
// transform.toDart.map((v) => v.toDartDouble).toList()))
// .toJS;
// }
// @JSExport()
// JSPromise updateBoneMatrices(ThermionEntity entity) {
// return viewer.updateBoneMatrices(entity).toJS;
// }
// @JSExport()
// JSPromise setBoneTransform(ThermionEntity 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(ThermionEntity entity,
// {JSFunction? callback, bool affectsTransform = false}) {
// throw UnimplementedError();
// }
// @JSExport()
// JSPromise setShadowsEnabled(bool enabled) {
// return viewer.setShadowsEnabled(enabled).toJS;
// }
// @JSExport()
// JSPromise setShadowType(int shadowType) {
// return viewer.setShadowType(ShadowType.values[shadowType]).toJS;
// }
// @JSExport()
// JSPromise setSoftShadowOptions(
// double penumbraScale, double penumbraRatioScale) {
// return viewer.setSoftShadowOptions(penumbraScale, penumbraRatioScale).toJS;
// }
// }

View File

@@ -1,5 +0,0 @@
library;
export 'src/thermion_viewer_dart_bridge.dart';
export 'src/thermion_viewer_js_shim.dart';
export 'src/thermion_viewer_js.dart';

View File

@@ -1,78 +0,0 @@
import 'package:vector_math/vector_math_64.dart';
import '../../shared_types/camera.dart';
import '../../thermion_viewer_base.dart';
class ThermionWasmCamera extends Camera {
final int pointer;
ThermionWasmCamera(this.pointer);
@override
Future setProjectionMatrixWithCulling(
Matrix4 projectionMatrix, double near, double far) {
// TODO: implement setProjectionMatrixWithCulling
throw UnimplementedError();
}
@override
Future<Matrix4> getModelMatrix() {
// TODO: implement getModelMatrix
throw UnimplementedError();
}
@override
Future setLensProjection({double near = kNear, double far = kFar, double aspect = 1.0, double focalLength = kFocalLength}) {
// TODO: implement setLensProjection
throw UnimplementedError();
}
@override
Future setTransform(Matrix4 transform) {
// TODO: implement setTransform
throw UnimplementedError();
}
@override
ThermionEntity getEntity() {
// TODO: implement getEntity
throw UnimplementedError();
}
@override
Future setModelMatrix(Matrix4 matrix) {
// TODO: implement setModelMatrix
throw UnimplementedError();
}
@override
Future<double> getCullingFar() {
// TODO: implement getCullingFar
throw UnimplementedError();
}
@override
Future<double> getFocalLength() {
// TODO: implement getFocalLength
throw UnimplementedError();
}
@override
Future<double> getNear() {
// TODO: implement getNear
throw UnimplementedError();
}
@override
Future<Matrix4> getViewMatrix() {
// TODO: implement getViewMatrix
throw UnimplementedError();
}
@override
Future setProjection(Projection projection, double left, double right, double bottom, double top, double near, double far) {
// TODO: implement setProjection
throw UnimplementedError();
}
}

View File

@@ -1,24 +0,0 @@
import '../../../viewer.dart';
class ThermionWasmMaterialInstance extends MaterialInstance {
final int pointer;
ThermionWasmMaterialInstance(this.pointer);
@override
Future setDepthCullingEnabled(bool enabled) {
// TODO: implement setDepthCullingEnabled
throw UnimplementedError();
}
@override
Future setDepthWriteEnabled(bool enabled) {
// TODO: implement setDepthWriteEnabled
throw UnimplementedError();
}
@override
Future setParameterFloat2(String name, double x, double y) {
// TODO: implement setParameterFloat2
throw UnimplementedError();
}
}

View File

@@ -1,3 +0,0 @@
library;
export 'src/thermion_viewer_wasm.dart' show ThermionViewerWasm;

View File

@@ -1,7 +0,0 @@
library thermion_viewer;
export 'src/thermion_viewer_base.dart';
export 'src/thermion_viewer_stub.dart'
if (dart.library.io) 'src/ffi/thermion_viewer_ffi.dart'
if (dart.library.js_interop) 'src/web_wasm/thermion_viewer_web_wasm.dart';
export 'src/shared_types/shared_types.dart';

View File

@@ -1,5 +1,8 @@
library filament_dart;
export 'src/viewer/viewer.dart';
export 'src/input/input.dart';
export 'src/utils/geometry.dart';
export 'thermion_dart/thermion_viewer.dart';
export 'thermion_dart/thermion_viewer_stub.dart'
if (dart.library.io) 'thermion_dart/thermion_viewer_ffi.dart'
if (dart.library.js_interop)'thermion_dart/compatibility/web/interop/thermion_viewer_wasm.dart';
export 'thermion_dart/entities/entity_transform_controller.dart';

View File

@@ -0,0 +1 @@
export 'native/compatibility.dart';

View File

@@ -8,11 +8,6 @@ export 'thermion_dart.g.dart';
final allocator = calloc;
void using(Pointer ptr, Future Function(Pointer ptr) function) async {
await function.call(ptr);
allocator.free(ptr);
}
Future<void> withVoidCallback(
Function(Pointer<NativeFunction<Void Function()>>) func) async {
final completer = Completer();
@@ -26,20 +21,20 @@ Future<void> withVoidCallback(
nativeCallable.close();
}
Future<Pointer<T>> withPointerCallback<T extends NativeType>(
Function(Pointer<NativeFunction<Void Function(Pointer<T>)>>)
Future<int> withVoidPointerCallback(
Function(Pointer<NativeFunction<Void Function(Pointer<Void>)>>)
func) async {
final completer = Completer<Pointer<T>>();
final completer = Completer<Pointer<Void>>();
// ignore: prefer_function_declarations_over_variables
void Function(Pointer<NativeType>) callback = (Pointer<NativeType> ptr) {
completer.complete(ptr.cast<T>());
void Function(Pointer<Void>) callback = (Pointer<Void> ptr) {
completer.complete(ptr);
};
final nativeCallable =
NativeCallable<Void Function(Pointer<NativeType>)>.listener(callback);
NativeCallable<Void Function(Pointer<Void>)>.listener(callback);
func.call(nativeCallable.nativeFunction);
var ptr = await completer.future;
nativeCallable.close();
return ptr;
return ptr.address;
}
Future<bool> withBoolCallback(
@@ -87,3 +82,4 @@ Future<String> withCharPtrCallback(
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/ffi/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/ffi/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();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,741 @@
@JS()
library thermion_flutter_js;
import 'dart:js_interop';
import 'package:logging/logging.dart';
import 'package:thermion_dart/thermion_dart/compatibility/web/interop/thermion_viewer_js_shim.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/thermion_viewer.dart';
import 'dart:js_interop_unsafe';
import 'package:vector_math/vector_math_64.dart';
///
/// A (Dart) class that wraps a (Dart) instance of [ThermionViewer],
/// but exported to JS by binding to a global property.
/// This is effectively an implementation of [ThermionViewerJSShim];
/// allowing users to interact with an instance of [ThermionViewer]
/// (presumably compiled to WASM) from any Javascript context (including
/// the browser console).
///
@JSExport()
class ThermionViewerJSDartBridge {
final _logger = Logger("ThermionViewerJSDartBridge");
final ThermionViewer viewer;
ThermionViewerJSDartBridge(this.viewer);
void bind({String globalPropertyName = "thermionViewer"}) {
var wrapper = createJSInteropWrapper<ThermionViewerJSDartBridge>(this)
as ThermionViewerJSShim;
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) {
_logger.info("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(ThermionEntity light) => viewer.removeLight(light).toJS;
@JSExport()
JSPromise clearLights() => viewer.clearLights().toJS;
@JSExport()
JSPromise<JSNumber> loadGlb(String path, {int numInstances = 1}) {
_logger.info("Loading GLB from path $path with numInstances $numInstances");
return viewer
.loadGlb(path, numInstances: numInstances)
.then((entity) => entity.toJS)
.catchError((err) {
_logger.info("Error: $err");
}).toJS;
}
@JSExport()
JSPromise<JSNumber> createInstance(ThermionEntity entity) {
return viewer.createInstance(entity).then((instance) => instance.toJS).toJS;
}
@JSExport()
JSPromise<JSNumber> getInstanceCount(ThermionEntity entity) =>
viewer.getInstanceCount(entity).then((v) => v.toJS).toJS;
@JSExport()
JSPromise<JSArray<JSNumber>> getInstances(ThermionEntity 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(
ThermionEntity entity, JSArray<JSNumber> weights) {
var dartWeights = weights.toDart.map((w) => w.toDartDouble).toList();
return viewer.setMorphTargetWeights(entity, dartWeights).toJS;
}
@JSExport()
JSPromise<JSArray<JSString>> getMorphTargetNames(
ThermionEntity entity, ThermionEntity childEntity) {
var morphTargetNames = viewer
.getMorphTargetNames(entity, childEntity)
.then((v) => v.map((s) => s.toJS).toList().toJS);
return morphTargetNames.toJS;
}
@JSExport()
JSPromise<JSArray<JSString>> getBoneNames(
ThermionEntity entity, int skinIndex) {
return viewer
.getBoneNames(entity, skinIndex: skinIndex)
.then((v) => v.map((s) => s.toJS).toList().toJS)
.toJS;
}
@JSExport()
JSPromise<JSArray<JSString>> getAnimationNames(ThermionEntity entity) =>
viewer
.getAnimationNames(entity)
.then((v) => v.map((s) => s.toJS).toList().toJS)
.toJS;
@JSExport()
JSPromise<JSNumber> getAnimationDuration(
ThermionEntity entity, int animationIndex) =>
viewer
.getAnimationDuration(entity, animationIndex)
.then((v) => v.toJS)
.toJS;
@JSExport()
JSPromise setMorphAnimationData(
ThermionEntity 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) {
_logger.severe("ERROR SETTING MORPH ANIMATION DATA : $err\n$st");
return null;
});
return result.toJS;
} catch (err, st) {
_logger.severe(err);
_logger.severe(st);
rethrow;
}
}
@JSExport()
JSPromise resetBones(ThermionEntity entity) => viewer.resetBones(entity).toJS;
@JSExport()
JSPromise addBoneAnimation(
ThermionEntity 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(ThermionEntity 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(ThermionEntity 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(ThermionEntity 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(
ThermionEntity entity, int index, int animationFrame) =>
viewer
.setAnimationFrame(
entity,
index,
animationFrame,
)
.toJS;
@JSExport()
JSPromise stopAnimation(ThermionEntity entity, int animationIndex) =>
viewer.stopAnimation(entity, animationIndex).toJS;
@JSExport()
JSPromise stopAnimationByName(ThermionEntity entity, String name) =>
viewer.stopAnimationByName(entity, name).toJS;
@JSExport()
JSPromise setCamera(ThermionEntity 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(ThermionEntity 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(ThermionEntity 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(ThermionEntity entity) =>
viewer.transformToUnitCube(entity).toJS;
@JSExport()
JSPromise setPosition(ThermionEntity entity, double x, double y, double z) =>
viewer.setPosition(entity, x, y, z).toJS;
@JSExport()
JSPromise setScale(ThermionEntity entity, double scale) =>
viewer.setScale(entity, scale).toJS;
@JSExport()
JSPromise setRotation(
ThermionEntity entity, double rads, double x, double y, double z) =>
viewer.setRotation(entity, rads, x, y, z).toJS;
@JSExport()
JSPromise queuePositionUpdate(
ThermionEntity entity, double x, double y, double z, bool relative) =>
viewer
.queuePositionUpdate(
entity,
x,
y,
z,
relative: relative,
)
.toJS;
@JSExport()
JSPromise queueRotationUpdate(ThermionEntity entity, double rads, double x,
double y, double z, bool relative) =>
viewer
.queueRotationUpdate(
entity,
rads,
x,
y,
z,
relative: relative,
)
.toJS;
@JSExport()
JSPromise queueRotationUpdateQuat(
ThermionEntity 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(
ThermionEntity entity, JSArray<JSNumber> rotation) =>
throw UnimplementedError();
@JSExport()
JSPromise reveal(ThermionEntity entity, String? meshName) =>
viewer.reveal(entity, meshName).toJS;
@JSExport()
JSPromise hide(ThermionEntity entity, String? meshName) =>
viewer.hide(entity, meshName).toJS;
@JSExport()
void pick(int x, int y) => viewer.pick(x, y);
@JSExport()
String? getNameForEntity(ThermionEntity 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(
ThermionEntity parent, bool renderableOnly) {
return viewer
.getChildEntities(
parent,
renderableOnly,
)
.then((entities) => entities.map((entity) => entity.toJS).toList().toJS)
.onError((e, st) async {
_logger.severe("Error : $e\n$st");
return <JSNumber>[].toJS;
}).toJS;
}
@JSExport()
JSPromise<JSNumber> getChildEntity(ThermionEntity parent, String childName) {
return viewer
.getChildEntity(
parent,
childName,
)
.then((entity) => entity.toJS)
.onError((e, st) async {
_logger.severe("Error getChildEntity : $e\n$st");
return 0.toJS;
}).toJS;
}
@JSExport()
JSPromise<JSArray<JSString>> getChildEntityNames(
ThermionEntity 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(ThermionEntity entity) =>
viewer.addAnimationComponent(entity).toJS;
@JSExport()
JSPromise removeAnimationComponent(ThermionEntity entity) =>
viewer.removeAnimationComponent(entity).toJS;
@JSExport()
JSPromise getParent(ThermionEntity entity) =>
viewer.removeAnimationComponent(entity).toJS;
@JSExport()
JSPromise getBone(ThermionEntity entity, int boneIndex, int skinIndex) =>
viewer.getBone(entity, boneIndex, skinIndex: skinIndex).toJS;
@JSExport()
JSPromise<JSArray<JSNumber>> getLocalTransform(ThermionEntity entity) {
return viewer
.getLocalTransform(entity)
.then((t) => t.storage.map((v) => v.toJS).toList().toJS)
.toJS;
}
@JSExport()
JSPromise<JSArray<JSNumber>> getWorldTransform(ThermionEntity entity) {
return viewer
.getWorldTransform(entity)
.then((t) => t.storage.map((v) => v.toJS).toList().toJS)
.toJS;
}
@JSExport()
JSPromise setTransform(ThermionEntity entity, JSArray<JSNumber> transform) {
return viewer
.setTransform(
entity,
Matrix4.fromList(
transform.toDart.map((v) => v.toDartDouble).toList()))
.toJS;
}
@JSExport()
JSPromise updateBoneMatrices(ThermionEntity entity) {
return viewer.updateBoneMatrices(entity).toJS;
}
@JSExport()
JSPromise setBoneTransform(ThermionEntity 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(ThermionEntity entity,
{JSFunction? callback, bool affectsTransform = false}) {
throw UnimplementedError();
}
@JSExport()
JSPromise setShadowsEnabled(bool enabled) {
return viewer.setShadowsEnabled(enabled).toJS;
}
@JSExport()
JSPromise setShadowType(int shadowType) {
return viewer.setShadowType(ShadowType.values[shadowType]).toJS;
}
@JSExport()
JSPromise setSoftShadowOptions(
double penumbraScale, double penumbraRatioScale) {
return viewer.setSoftShadowOptions(penumbraScale, penumbraRatioScale).toJS;
}
}

View File

@@ -0,0 +1,846 @@
import 'dart:js_interop';
import 'dart:js_interop_unsafe';
import 'dart:math';
import 'package:animation_tools_dart/animation_tools_dart.dart';
import 'package:logging/logging.dart';
import 'package:thermion_dart/thermion_dart/scene.dart';
import 'package:thermion_dart/thermion_dart/thermion_viewer.dart';
import 'package:thermion_dart/thermion_dart/scene_impl.dart';
import 'package:vector_math/vector_math_64.dart';
import 'thermion_viewer_js_shim.dart';
///
/// An [ThermionViewer] implementation that forwards calls to
/// a corresponding Javascript shim implementation (see [ThermionViewerJSShim]).
///
class ThermionViewerJS implements ThermionViewer {
final _logger = Logger("ThermionViewerJS");
late final ThermionViewerJSShim _shim;
ThermionViewerJS.fromGlobalProperty(String globalPropertyName) {
this._shim = globalContext.getProperty(globalPropertyName.toJS)
as ThermionViewerJSShim;
}
ThermionViewerJS(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;
for (final callback in _onDispose) {
callback.call();
}
}
@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<ThermionEntity> 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(ThermionEntity light) async {
await _shim.removeLight(light).toDart;
}
@override
Future<void> clearLights() async {
await _shim.clearLights().toDart;
}
@override
Future<ThermionEntity> loadGlb(String path, {int numInstances = 1}) async {
var entity = (await _shim.loadGlb(path, numInstances).toDart).toDartInt;
scene.registerEntity(entity);
return entity;
}
@override
Future<ThermionEntity> createInstance(ThermionEntity entity) async {
return (await _shim.createInstance(entity).toDart).toDartInt;
}
@override
Future<int> getInstanceCount(ThermionEntity entity) async {
return (await _shim.getInstanceCount(entity).toDart).toDartInt;
}
@override
Future<List<ThermionEntity>> getInstances(ThermionEntity entity) async {
throw UnimplementedError();
// final List<JSObject> jsInstances =
// await _shim.getInstances(entity).toDart;
// return jsInstances
// .map((js) => ThermionEntity._fromJSObject(js))
// .toList()
// .toDart;
}
@override
Future<ThermionEntity> loadGltf(String path, String relativeResourcePath,
{bool force = false}) async {
throw UnimplementedError();
// final ThermionEntity jsEntity = await _shim
// .loadGltf(path, relativeResourcePath, force: force)
// .toDart;
// return ThermionEntity._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(
ThermionEntity 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(
ThermionEntity entity, ThermionEntity childEntity) async {
var result = await _shim.getMorphTargetNames(entity, childEntity).toDart;
return result.toDart.map((r) => r.toDart).toList();
}
@override
Future<List<String>> getAnimationNames(ThermionEntity entity) async {
var names = (await (_shim.getAnimationNames(entity).toDart))
.toDart
.map((x) => x.toDart)
.toList();
return names;
}
@override
Future<double> getAnimationDuration(
ThermionEntity entity, int animationIndex) async {
return (await _shim.getAnimationDuration(entity, animationIndex).toDart)
.toDartDouble;
}
@override
Future<void> setMorphAnimationData(
ThermionEntity 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) {
_logger.severe(err);
_logger.severe(st);
rethrow;
}
}
@override
Future<void> resetBones(ThermionEntity entity) async {
await _shim.resetBones(entity).toDart;
}
@override
Future<void> addBoneAnimation(
ThermionEntity 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(ThermionEntity 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(ThermionEntity 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(ThermionEntity 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(
ThermionEntity entity, int index, int animationFrame) async {
await _shim.setAnimationFrame(entity, index, animationFrame).toDart;
}
@override
Future<void> stopAnimation(ThermionEntity entity, int animationIndex) async {
await _shim.stopAnimation(entity, animationIndex).toDart;
}
@override
Future<void> stopAnimationByName(ThermionEntity entity, String name) async {
await _shim.stopAnimationByName(entity, name).toDart;
}
@override
Future<void> setCamera(ThermionEntity entity, String? name) async {
await _shim.setCamera(entity, name).toDart;
}
@override
Future<void> setMainCamera() async {
await _shim.setMainCamera().toDart;
}
@override
Future<ThermionEntity> getMainCamera() async {
throw UnimplementedError();
// final ThermionEntity jsEntity = await _shim.getMainCamera().toDart;
// return ThermionEntity._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(ThermionEntity 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(ThermionEntity 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(ThermionEntity entity) async {
await _shim.transformToUnitCube(entity).toDart;
}
@override
Future<void> setPosition(
ThermionEntity entity, double x, double y, double z) async {
await _shim.setPosition(entity, x, y, z).toDart;
}
@override
Future<void> setScale(ThermionEntity entity, double scale) async {
await _shim.setScale(entity, scale).toDart;
}
@override
Future<void> setRotation(
ThermionEntity entity, double rads, double x, double y, double z) async {
await _shim.setRotation(entity, rads, x, y, z).toDart;
}
@override
Future<void> queuePositionUpdate(
ThermionEntity entity, double x, double y, double z,
{bool relative = false}) async {
await _shim.queuePositionUpdate(entity, x, y, z, relative).toDart;
}
@override
Future<void> queueRotationUpdate(
ThermionEntity 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(ThermionEntity 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(
ThermionEntity entity, Quaternion rotation) async {
throw UnimplementedError();
// final JSQuaternion jsRotation = rotation.toJSQuaternion().toDart;
// await _shim.setRotationQuat(entity, jsRotation).toDart;
}
@override
Future<void> reveal(ThermionEntity entity, String? meshName) async {
throw UnimplementedError();
// await _shim.reveal(entity, meshName).toDart;
}
@override
Future<void> hide(ThermionEntity 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(ThermionEntity 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<ThermionEntity>> getChildEntities(
ThermionEntity parent, bool renderableOnly) async {
final children =
await _shim.getChildEntities(parent, renderableOnly).toDart;
return children.toDart
.map((js) => js.toDartInt)
.cast<ThermionEntity>()
.toList();
}
@override
Future<ThermionEntity> getChildEntity(
ThermionEntity parent, String childName) async {
return (await _shim.getChildEntity(parent, childName).toDart).toDartInt;
}
@override
Future<List<String>> getChildEntityNames(ThermionEntity 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(ThermionEntity entity) async {
await _shim.addAnimationComponent(entity).toDart;
}
@override
Future<void> addCollisionComponent(ThermionEntity 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(ThermionEntity entity) async {
await _shim.removeCollisionComponent(entity).toDart;
}
@override
Future<ThermionEntity> createGeometry(
List<double> vertices, List<int> indices,
{String? materialPath,
PrimitiveType primitiveType = PrimitiveType.TRIANGLES}) async {
throw UnimplementedError();
// final ThermionEntity jsEntity = await _shim
// .createGeometry(vertices, indices,
// materialPath: materialPath, primitiveType: primitiveType.index)
// .toDart;
// return ThermionEntity._fromJSObject(jsEntity).toDart;
}
@override
Future<void> setParent(ThermionEntity child, ThermionEntity parent) async {
await _shim.setParent(child, parent).toDart;
}
@override
Future<void> testCollisions(ThermionEntity entity) async {
await _shim.testCollisions(entity).toDart;
}
@override
Future<void> setPriority(ThermionEntity 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(ThermionEntity entity,
{int skinIndex = 0}) async {
var result = await _shim.getBoneNames(entity, skinIndex).toDart;
return result.toDart.map((n) => n.toDart).toList();
}
@override
Future<ThermionEntity> getBone(ThermionEntity entity, int boneIndex,
{int skinIndex = 0}) async {
var result = await _shim.getBone(entity, boneIndex, skinIndex).toDart;
return result.toDartInt;
}
@override
Future<Matrix4> getInverseBindMatrix(ThermionEntity parent, int boneIndex,
{int skinIndex = 0}) {
// TODO: implement getInverseBindMatrix
throw UnimplementedError();
}
@override
Future<Matrix4> getLocalTransform(ThermionEntity entity) async {
var result = await _shim.getLocalTransform(entity).toDart;
return Matrix4.fromList(result.toDart.map((v) => v.toDartDouble).toList());
}
@override
Future<ThermionEntity?> getParent(ThermionEntity child) async {
var result = await _shim.getParent(child).toDart;
return result.toDartInt;
}
@override
Future<Matrix4> getWorldTransform(ThermionEntity entity) async {
var result = await _shim.getLocalTransform(entity).toDart;
return Matrix4.fromList(result.toDart.map((v) => v.toDartDouble).toList());
}
@override
Future removeAnimationComponent(ThermionEntity entity) {
return _shim.removeAnimationComponent(entity).toDart;
}
@override
Future setBoneTransform(
ThermionEntity 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(ThermionEntity entity, Matrix4 transform) {
return _shim
.setTransform(
entity, transform.storage.map((v) => v.toJS).toList().toJS)
.toDart;
}
@override
Future updateBoneMatrices(ThermionEntity entity) {
return _shim.updateBoneMatrices(entity).toDart;
}
final _onDispose = <Future Function()>[];
///
///
///
void onDispose(Future Function() callback) {
_onDispose.add(callback);
}
@override
Future setShadowType(ShadowType shadowType) {
return _shim.setShadowType(shadowType.index).toDart;
}
@override
Future setShadowsEnabled(bool enabled) {
return _shim.setShadowsEnabled(enabled).toDart;
}
@override
Future setSoftShadowOptions(double penumbraScale, double penumbraRatioScale) {
return _shim.setSoftShadowOptions(penumbraScale, penumbraRatioScale).toDart;
}
}

View File

@@ -3,13 +3,14 @@ library thermion_flutter_js;
import 'dart:js_interop';
import '../../shared_types/shared_types.dart';
import 'package:thermion_dart/thermion_dart/thermion_viewer.dart';
///
/// An extension type on [JSObject] that represents a
/// Javascript shim implementation of the [ThermionViewer] interface.
///
extension type ThermionViewerJSShim(JSObject _) implements JSObject {
@JS('initialized')
external JSPromise<JSBoolean> get initialized;
@@ -22,9 +23,6 @@ extension type ThermionViewerJSShim(JSObject _) implements JSObject {
@JS('render')
external JSPromise render();
@JS('capture')
external JSPromise<JSUint8Array> capture();
@JS('setFrameRate')
external JSPromise setFrameRate(int framerate);
@@ -138,9 +136,6 @@ extension type ThermionViewerJSShim(JSObject _) implements JSObject {
external JSPromise<JSNumber> getAnimationDuration(
ThermionEntity entity, int animationIndex);
@JS('clearMorphAnimationData')
external void clearMorphAnimationData(ThermionEntity entity);
@JS('setMorphAnimationData')
external JSPromise setMorphAnimationData(
ThermionEntity entity,
@@ -187,7 +182,6 @@ extension type ThermionViewerJSShim(JSObject _) implements JSObject {
bool reverse,
bool replaceActive,
double crossfade,
double startOffset,
);
@JS('playAnimationByName')
@@ -220,7 +214,7 @@ extension type ThermionViewerJSShim(JSObject _) implements JSObject {
external JSPromise<JSNumber> getMainCamera();
@JS('setCameraFov')
external JSPromise setCameraFov(double degrees, bool horizontal);
external JSPromise setCameraFov(double degrees, double width, double height);
@JS('setToneMapping')
external JSPromise setToneMapping(int mapper);
@@ -378,7 +372,7 @@ extension type ThermionViewerJSShim(JSObject _) implements JSObject {
JSArray<JSNumber> indices, String? materialPath, int primitiveType);
@JS('setParent')
external JSPromise setParent(ThermionEntity child, ThermionEntity parent, bool preserveScaling);
external JSPromise setParent(ThermionEntity child, ThermionEntity parent);
@JS('getParent')
external JSPromise<JSNumber> getParent(ThermionEntity child);
@@ -409,16 +403,19 @@ extension type ThermionViewerJSShim(JSObject _) implements JSObject {
ThermionEntity entity, JSArray<JSNumber> transform);
@JS('setBoneTransform')
external JSPromise setBoneTransform(ThermionEntity entity, int boneIndex,
JSArray<JSNumber> transform, int skinIndex);
external JSPromise setBoneTransform(
ThermionEntity entity, int boneIndex, JSArray<JSNumber> transform, int skinIndex);
@JS('setShadowsEnabled')
external JSPromise setShadowsEnabled(bool enabled);
external JSPromise setShadowsEnabled(
bool enabled);
@JS('setShadowType')
external JSPromise setShadowType(int shadowType);
external JSPromise setShadowType(
int shadowType);
@JS('setSoftShadowOptions')
external JSPromise setSoftShadowOptions(
double penumbraScale, double penumbraRatioScale);
}

View File

@@ -0,0 +1,3 @@

View File

@@ -0,0 +1,179 @@
import 'dart:async';
import 'dart:math';
import 'package:thermion_dart/thermion_dart/thermion_viewer.dart';
import 'package:vector_math/vector_math_64.dart' as v;
class EntityTransformController {
final ThermionViewer controller;
final ThermionEntity _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,79 @@
import 'package:vector_math/vector_math_64.dart';
import '../thermion_viewer.dart';
class Gizmo extends AbstractGizmo {
final ThermionEntity x;
Vector3 _x = Vector3(0.1, 0, 0);
final ThermionEntity y;
Vector3 _y = Vector3(0.0, 0.1, 0);
final ThermionEntity z;
Vector3 _z = Vector3(0.0, 0.0, 0.1);
final ThermionViewer controller;
ThermionEntity? _activeAxis;
ThermionEntity? _activeEntity;
bool get isActive => _activeAxis != null;
final Set<ThermionEntity> ignore;
Gizmo(this.x, this.y, this.z, this.controller,
{this.ignore = const <ThermionEntity>{}}) {
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(ThermionEntity 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);
}
}

View File

@@ -0,0 +1,48 @@
import 'package:thermion_dart/thermion_dart/thermion_viewer.dart';
import 'dart:async';
///
/// 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);
ThermionEntity? selected;
///
/// A Stream updated whenever an entity is added/removed from the scene.
///
Stream<bool> get onUpdated;
///
/// A Stream containing every ThermionEntity 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 ThermionEntity returned from those methods.
///
Stream<ThermionEntity> get onLoad;
///
/// A Stream containing every ThermionEntity removed from the scene (i.e. via [removeEntity], [clearEntities], [removeLight] or [clearLights]).
Stream<ThermionEntity> get onUnload;
///
/// Lists all light entities currently loaded (not necessarily active in the scene). Does not account for instances.
///
Iterable<ThermionEntity> listLights();
///
/// Lists all entities currently loaded (not necessarily active in the scene). Does not account for instances.
///
Iterable<ThermionEntity> listEntities();
///
/// Attach the gizmo to the specified entity.
///
void select(ThermionEntity entity);
///
///
///
void registerEntity(ThermionEntity entity);
}

View File

@@ -0,0 +1,125 @@
import 'dart:async';
import 'package:thermion_dart/thermion_dart/scene.dart';
import 'thermion_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 {
ThermionViewer controller;
SceneImpl(this.controller);
@override
ThermionEntity? selected;
final _onUpdatedController = StreamController<bool>.broadcast();
@override
Stream<bool> get onUpdated => _onUpdatedController.stream;
final _onLoadController = StreamController<ThermionEntity>.broadcast();
@override
Stream<ThermionEntity> get onLoad => _onLoadController.stream;
final _onUnloadController = StreamController<ThermionEntity>.broadcast();
@override
Stream<ThermionEntity> get onUnload => _onUnloadController.stream;
final _lights = <ThermionEntity>{};
final _entities = <ThermionEntity>{};
void registerLight(ThermionEntity entity) {
_lights.add(entity);
_onLoadController.sink.add(entity);
_onUpdatedController.add(true);
}
void unregisterLight(ThermionEntity 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(ThermionEntity 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(ThermionEntity 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<ThermionEntity> listLights() {
return _lights;
}
@override
Iterable<ThermionEntity> listEntities() {
return _entities;
}
void registerSelected(ThermionEntity entity) {
selected = entity;
_onUpdatedController.add(true);
}
void unregisterSelected() {
selected = null;
_onUpdatedController.add(true);
}
@override
void select(ThermionEntity entity) {
selected = entity;
controller.gizmo?.attach(entity);
_onUpdatedController.add(true);
}
Future dispose() async {
await _onLoadController.close();
await _onUnloadController.close();
await _onUpdatedController.close();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,24 +1,60 @@
import 'package:thermion_dart/src/viewer/src/events.dart';
import '../../utils/gizmo.dart';
import 'shared_types/shared_types.dart';
export 'shared_types/shared_types.dart';
import 'dart:math';
import 'dart:typed_data';
import 'package:thermion_dart/thermion_dart/scene.dart';
import 'package:vector_math/vector_math_64.dart';
import 'dart:async';
import 'package:animation_tools_dart/animation_tools_dart.dart';
import 'shared_types/view.dart';
// a handle that can be safely passed back to the rendering layer to manipulate an Entity
typedef ThermionEntity = int;
const double kNear = 0.05;
const double kFar = 1000.0;
const double kFocalLength = 28.0;
// "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 = ({ThermionEntity 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,
}
enum ShadowType {
PCF, //!< percentage-closer filtered shadows (default)
VSM, //!< variance shadows
DPCF, //!< PCF with contact hardening simulation
PCSS, //!< PCF with soft shadows and contact hardening
}
// 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 ThermionViewer {
///
/// A Future that resolves when the underlying rendering context has been successfully created.
///
Future<bool> get initialized;
///
@@ -28,11 +64,6 @@ abstract class ThermionViewer {
///
Stream<FilamentPickResult> get pickResult;
///
/// A Stream containing entities added/removed to/from to the scene.
///
Stream<SceneUpdateEvent> get sceneUpdated;
///
/// Whether the controller is currently rendering at [framerate].
///
@@ -44,51 +75,9 @@ abstract class ThermionViewer {
Future setRendering(bool render);
///
/// Render a single frame immediately.
/// Render a single frame.
///
Future render({covariant SwapChain? swapChain});
///
/// Requests a single frame to be rendered. This is only intended to be used internally.
///
Future requestFrame();
///
/// Render a single frame and copy the pixel buffer to [out].
///
Future<Uint8List> capture({covariant SwapChain? swapChain,
covariant View? view, covariant RenderTarget? renderTarget});
///
///
///
Future<SwapChain> createHeadlessSwapChain(int width, int height);
///
///
///
Future<SwapChain> createSwapChain(int handle);
///
///
///
Future<RenderTarget> createRenderTarget(
int width, int height, int textureHandle);
///
///
///
Future setRenderTarget(covariant RenderTarget renderTarget);
///
///
///
Future<View> createView();
///
///
///
Future<View> getViewAt(int index);
Future render();
///
/// Sets the framerate for continuous rendering when [setRendering] is enabled.
@@ -135,17 +124,11 @@ abstract class ThermionViewer {
Future removeSkybox();
///
/// Creates an indirect light by loading the reflections/irradiance from the KTX file.
/// Only one indirect light can be active at any given time; if an indirect light has already been loaded, it will be replaced.
/// 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});
///
/// Creates a indirect light with the given color.
/// Only one indirect light can be active at any given time; if an indirect light has already been loaded, it will be replaced.
///
Future createIbl(double r, double g, double b, double intensity);
///
/// Rotates the IBL & skybox.
///
@@ -162,8 +145,6 @@ abstract class ThermionViewer {
/// Note that [sunAngularRadius] is in degrees,
/// whereas [spotLightConeInner] and [spotLightConeOuter] are in radians
///
@Deprecated(
"This will be removed in future versions. Use addDirectLight instead.")
Future<ThermionEntity> addLight(
LightType type,
double colour,
@@ -182,17 +163,6 @@ abstract class ThermionViewer {
double sunHaloFallof = 80.0,
bool castShadows = true});
///
/// Adds a direct light to the scene.
/// See LightManager.h for details
/// Note that [sunAngularRadius] is in degrees,
/// whereas [spotLightConeInner] and [spotLightConeOuter] are in radians
///
Future<ThermionEntity> addDirectLight(DirectLight light);
///
/// Remove a light from the scene.
///
Future removeLight(ThermionEntity light);
///
@@ -202,24 +172,8 @@ abstract class ThermionViewer {
///
/// Load the .glb asset at the given path and insert into the scene.
/// Specify [numInstances] to create multiple instances (this is more efficient than dynamically instantating at a later time). You can then retrieve the created instances with [getInstances].
/// If you want to be able to call [createInstance] at a later time, you must pass true for [keepData].
/// If [keepData] is false, the source glTF data will be released and [createInstance] will throw an exception.
///
Future<ThermionEntity> loadGlb(String path,
{int numInstances = 1, bool keepData = false});
///
/// Load the .glb asset from the specified buffer and insert into the scene.
/// Specify [numInstances] to create multiple instances (this is more efficient than dynamically instantating at a later time). You can then retrieve the created instances with [getInstances].
/// If you want to be able to call [createInstance] at a later time, you must pass true for [keepData].
/// If [keepData] is false, the source glTF data will be released and [createInstance] will throw an exception.
///
Future<ThermionEntity> loadGlbFromBuffer(Uint8List data,
{int numInstances = 1,
bool keepData = false,
int priority = 4,
int layer = 0});
Future<ThermionEntity> loadGlb(String path, {int numInstances = 1});
///
/// Create a new instance of [entity].
@@ -241,10 +195,38 @@ abstract class ThermionViewer {
/// [relativeResourcePath] is the folder path where the glTF resources are stored;
/// this is usually the parent directory of the .gltf file itself.
///
/// See [loadGlb] for an explanation of [keepData].
///
Future<ThermionEntity> loadGltf(String path, String relativeResourcePath,
{bool keepData = false});
{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].
@@ -287,11 +269,6 @@ abstract class ThermionViewer {
ThermionEntity entity, MorphAnimationData animation,
{List<String>? targetMeshNames});
///
/// Clear all current morph animations for [entity].
///
Future clearMorphAnimationData(ThermionEntity entity);
///
/// Resets all bones in the given entity to their rest pose.
/// This should be done before every call to addBoneAnimation.
@@ -352,13 +329,6 @@ abstract class ThermionViewer {
///
Future setTransform(ThermionEntity entity, Matrix4 transform);
///
/// Sets multiple transforms (relative to parent) simultaneously for [entity].
/// Uses mutex to ensure that transform updates aren't split across frames.
///
Future queueTransformUpdates(
List<ThermionEntity> entities, List<Matrix4> transforms);
///
/// Updates the bone matrices for [entity] (which must be the ThermionEntity
/// returned by [loadGlb/loadGltf]).
@@ -388,6 +358,21 @@ abstract class ThermionViewer {
///
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.
///
@@ -395,8 +380,7 @@ abstract class ThermionViewer {
{bool loop = false,
bool reverse = false,
bool replaceActive = true,
double crossfade = 0.0,
double startOffset = 0.0});
double crossfade = 0.0});
///
/// Schedules the glTF animation at [index] in [entity] to start playing on the next frame.
@@ -424,25 +408,14 @@ abstract class ThermionViewer {
Future setMainCamera();
///
/// Returns the entity associated with the main camera. You probably never need this; use getMainCamera instead.
/// Returns the entity associated with the main camera.
///
Future<ThermionEntity> getMainCameraEntity();
Future<ThermionEntity> getMainCamera();
///
/// Returns the entity associated with the main camera. You probably never need this; use getMainCamera instead.
/// Sets the current scene camera to the glTF camera under [name] in [entity].
///
Future<Camera> getMainCamera();
///
/// Sets the horizontal field of view (if [horizontal] is true) or vertical field of view for the currently active camera to [degrees].
/// The aspect ratio of the current viewport is used.
///
Future setCameraFov(double degrees, {bool horizontal = true});
///
/// Gets the field of view (in degrees).
///
Future<double> getCameraFov(bool horizontal);
Future setCameraFov(double degrees, double width, double height);
///
/// Sets the tone mapping (requires postprocessing).
@@ -465,16 +438,10 @@ abstract class ThermionViewer {
Future setCameraCulling(double near, double far);
///
/// Get the distance (in world units) to the near plane for the active camera.
/// Get the distance (in world units) to the near culling plane for the active camera.
///
@Deprecated("Use getCameraNear")
Future<double> getCameraCullingNear();
///
/// Get the distance (in world units) to the near plane for the active camera.
///
Future<double> getCameraNear();
///
/// Get the distance (in world units) to the far culling plane for the active camera.
///
@@ -532,7 +499,7 @@ abstract class ThermionViewer {
Future moveCameraToAsset(ThermionEntity entity);
///
/// Enables/disables frustum culling.
/// 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);
@@ -543,70 +510,31 @@ abstract class ThermionViewer {
double aperture, double shutterSpeed, double sensitivity);
///
/// Rotate the camera by [rads] around the given axis.
/// 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.
///
@Deprecated("Will be superseded by setCameraModelMatrix4")
Future setCameraModelMatrix(List<double> matrix);
///
/// Sets the camera model matrix.
///
Future setCameraModelMatrix4(Matrix4 matrix);
///
/// Sets the `baseColorFactor` property for the material at index [materialIndex] in [entity] under node [meshName] to [color].
///
@Deprecated("Use setMaterialPropertyFloat4 instead")
Future setMaterialColor(ThermionEntity entity, String meshName,
int materialIndex, double r, double g, double b, double a);
///
/// Sets the material property [propertyName] under material [materialIndex] for [entity] to [value].
/// [entity] must have a Renderable attached.
///
Future setMaterialPropertyFloat4(ThermionEntity entity, String propertyName,
int materialIndex, double f1, double f2, double f3, double f4);
///
/// Sets the material property [propertyName] under material [materialIndex] for [entity] to [value].
/// [entity] must have a Renderable attached.
///
Future setMaterialPropertyFloat(ThermionEntity entity, String propertyName,
int materialIndex, double value);
///
/// Sets the material property [propertyName] under material [materialIndex] for [entity] to [value].
/// [entity] must have a Renderable attached.
///
Future setMaterialPropertyInt(
ThermionEntity entity, String propertyName, int materialIndex, int value);
///
/// Scale [entity] to fit within the unit cube.
///
Future transformToUnitCube(ThermionEntity entity);
///
/// Directly sets the world space position for [entity] to the given coordinates.
/// Directly sets the world space position for [entity] to the given coordinates, skipping all collision detection.
///
Future setPosition(ThermionEntity entity, double x, double y, double z);
///
/// Set the world space position for [lightEntity] to the given coordinates.
///
Future setLightPosition(
ThermionEntity lightEntity, double x, double y, double z);
///
/// Sets the world space direction for [lightEntity] to the given vector.
///
Future setLightDirection(ThermionEntity lightEntity, Vector3 direction);
///
/// Directly sets the scale for [entity], skipping all collision detection.
///
@@ -619,16 +547,26 @@ abstract class ThermionViewer {
ThermionEntity entity, double rads, double x, double y, double z);
///
/// TODO
/// 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 queuePositionUpdateFromViewportCoords(
ThermionEntity entity, double x, double y);
Future queuePositionUpdate(
ThermionEntity entity, double x, double y, double z,
{bool relative = false});
///
/// TODO
/// 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 queueRelativePositionUpdateWorldAxis(ThermionEntity entity,
double viewportX, double viewportY, double x, double y, double z);
Future queueRotationUpdate(
ThermionEntity entity, double rads, double x, double y, double z,
{bool relative = false});
///
/// Same as [queueRotationUpdate].
///
Future queueRotationUpdateQuat(ThermionEntity entity, Quaternion quat,
{bool relative = false});
///
/// Enable/disable postprocessing (disabled by default).
@@ -684,6 +622,16 @@ abstract class ThermionViewer {
///
String? getNameForEntity(ThermionEntity 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].
///
@@ -703,6 +651,17 @@ abstract class ThermionViewer {
Future<List<String>> getChildEntityNames(ThermionEntity 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.
@@ -730,26 +689,20 @@ abstract class ThermionViewer {
///
/// Creates a (renderable) entity with the specified geometry and adds to the scene.
/// If [keepData] is true, the source data will not be released.
///
Future createGeometry(Geometry geometry,
{MaterialInstance? materialInstance, bool keepData = false});
Future createGeometry(List<double> vertices, List<int> indices,
{String? materialPath,
PrimitiveType primitiveType = PrimitiveType.TRIANGLES});
///
/// Gets the parent entity of [entity]. Returns null if the entity has no parent.
/// Gets the parent transform of [child].
///
Future<ThermionEntity?> getParent(ThermionEntity entity);
///
/// Gets the ancestor (ultimate parent) entity of [entity]. Returns null if the entity has no parent.
///
Future<ThermionEntity?> getAncestor(ThermionEntity entity);
Future<ThermionEntity?> getParent(ThermionEntity child);
///
/// Sets the parent transform of [child] to [parent].
///
Future setParent(ThermionEntity child, ThermionEntity parent,
{bool preserveScaling});
Future setParent(ThermionEntity child, ThermionEntity parent);
///
/// Test all collidable entities against this entity to see if any have collided.
@@ -763,150 +716,30 @@ abstract class ThermionViewer {
Future setPriority(ThermionEntity entityId, int priority);
///
/// The gizmo for translating/rotating objects. Only one gizmo can be active for a given view.
/// The Scene holds all loaded entities/lights.
///
Future<Gizmo> createGizmo(covariant View view);
Scene get scene;
///
///
///
AbstractGizmo? get gizmo;
///
/// Register a callback to be invoked when this viewer is disposed.
///
void onDispose(Future Function() callback);
///
/// Gets the 2D bounding box (in viewport coordinates) for the given entity.
///
Future<Aabb2> getViewportBoundingBox(ThermionEntity entity);
///
/// Filament assigns renderables to a numeric layer.
/// We place all scene assets in layer 0 (enabled by default), gizmos in layer 1 (enabled by default), world grid in layer 2 (disabled by default).
/// Use this method to toggle visibility of the respective layer.
///
Future setLayerVisibility(int layer, bool visible);
///
/// Assigns [entity] to visibility layer [layer].
///
Future setVisibilityLayer(ThermionEntity entity, int layer);
///
/// Renders an outline around [entity] with the given color.
///
Future setStencilHighlight(ThermionEntity entity,
{double r = 1.0, double g = 0.0, double b = 0.0});
///
/// Removes the outline around [entity]. Noop if there was no highlight.
///
Future removeStencilHighlight(ThermionEntity entity);
///
/// Decodes the specified image data and creates a texture.
///
Future<ThermionTexture> createTexture(Uint8List data);
///
///
///
Future applyTexture(covariant ThermionTexture texture, ThermionEntity entity,
{int materialIndex = 0, String parameterName = "baseColorMap"});
///
///
///
Future destroyTexture(covariant ThermionTexture texture);
///
///
///
Future<MaterialInstance> createUbershaderMaterialInstance({
bool doubleSided = false,
bool unlit = false,
bool hasVertexColors = false,
bool hasBaseColorTexture = false,
bool hasNormalTexture = false,
bool hasOcclusionTexture = false,
bool hasEmissiveTexture = false,
bool useSpecularGlossiness = false,
AlphaMode alphaMode = AlphaMode.OPAQUE,
bool enableDiagnostics = false,
bool hasMetallicRoughnessTexture = false,
int metallicRoughnessUV = 0,
int baseColorUV = 0,
bool hasClearCoatTexture = false,
int clearCoatUV = 0,
bool hasClearCoatRoughnessTexture = false,
int clearCoatRoughnessUV = 0,
bool hasClearCoatNormalTexture = false,
int clearCoatNormalUV = 0,
bool hasClearCoat = false,
bool hasTransmission = false,
bool hasTextureTransforms = false,
int emissiveUV = 0,
int aoUV = 0,
int normalUV = 0,
bool hasTransmissionTexture = false,
int transmissionUV = 0,
bool hasSheenColorTexture = false,
int sheenColorUV = 0,
bool hasSheenRoughnessTexture = false,
int sheenRoughnessUV = 0,
bool hasVolumeThicknessTexture = false,
int volumeThicknessUV = 0,
bool hasSheen = false,
bool hasIOR = false,
bool hasVolume = false,
});
///
///
///
Future destroyMaterialInstance(covariant MaterialInstance materialInstance);
///
///
///
Future<MaterialInstance> createUnlitMaterialInstance();
///
///
///
Future<MaterialInstance?> getMaterialInstanceAt(
ThermionEntity entity, int index);
///
///
///
Future<Camera> createCamera();
///
///
///
Future setActiveCamera(covariant Camera camera);
///
///
///
Future<Camera> getActiveCamera();
///
///
///
Future registerRequestFrameHook(Future Function() hook);
///
///
///
Future unregisterRequestFrameHook(Future Function() hook);
///
///
///
int getCameraCount();
///
/// Returns the camera specified by the given index. Note that the camera at
/// index 0 is always the main camera; this cannot be destroyed.
///
Camera getCameraAt(int index);
}
abstract class AbstractGizmo {
bool get isActive;
void translate(double transX, double transY);
void reset();
void attach(ThermionEntity entity);
void detach();
}

View File

@@ -1,15 +1,12 @@
import 'dart:math';
import 'dart:typed_data';
import 'package:thermion_dart/src/utils/gizmo.dart';
import 'package:thermion_dart/src/viewer/src/shared_types/swap_chain.dart';
import 'package:thermion_dart/src/viewer/src/shared_types/view.dart';
import 'package:thermion_dart/thermion_dart.dart';
import 'package:thermion_dart/thermion_dart/scene.dart';
import 'package:thermion_dart/thermion_dart/thermion_viewer.dart';
import 'package:vector_math/vector_math_64.dart';
import 'dart:async';
import 'package:animation_tools_dart/animation_tools_dart.dart';
import 'events.dart';
import 'shared_types/camera.dart';
typedef ThermionViewerImpl = ThermionViewerStub;
class ThermionViewerStub extends ThermionViewer {
@override
@@ -76,6 +73,13 @@ class ThermionViewerStub extends ThermionViewer {
throw UnimplementedError();
}
@override
Future createGeometry(List<double> vertices, List<int> indices,
{String? materialPath,
PrimitiveType primitiveType = PrimitiveType.TRIANGLES}) {
// TODO: implement createGeometry
throw UnimplementedError();
}
@override
Future<ThermionEntity> createInstance(ThermionEntity entity) {
@@ -216,6 +220,12 @@ class ThermionViewerStub extends ThermionViewer {
throw UnimplementedError();
}
@override
Future<ThermionEntity> getMainCamera() {
// TODO: implement getMainCamera
throw UnimplementedError();
}
@override
Future<List<String>> getMorphTargetNames(
ThermionEntity entity, ThermionEntity childEntity) {
@@ -241,6 +251,10 @@ class ThermionViewerStub extends ThermionViewer {
throw UnimplementedError();
}
@override
// TODO: implement gizmo
AbstractGizmo? get gizmo => throw UnimplementedError();
@override
Future hide(ThermionEntity entity, String? meshName) {
// TODO: implement hide
@@ -251,6 +265,19 @@ class ThermionViewerStub extends ThermionViewer {
// TODO: implement initialized
Future<bool> get initialized => throw UnimplementedError();
@override
Future<ThermionEntity> loadGlb(String path, {int numInstances = 1}) {
// TODO: implement loadGlb
throw UnimplementedError();
}
@override
Future<ThermionEntity> loadGltf(String path, String relativeResourcePath,
{bool force = false}) {
// TODO: implement loadGltf
throw UnimplementedError();
}
@override
Future loadIbl(String lightingPath, {double intensity = 30000}) {
// TODO: implement loadIbl
@@ -306,8 +333,7 @@ class ThermionViewerStub extends ThermionViewer {
{bool loop = false,
bool reverse = false,
bool replaceActive = true,
double crossfade = 0.0,
double startOffset=0.0}) {
double crossfade = 0.0}) {
// TODO: implement playAnimation
throw UnimplementedError();
}
@@ -381,6 +407,11 @@ class ThermionViewerStub extends ThermionViewer {
throw UnimplementedError();
}
@override
Future render() {
// TODO: implement render
throw UnimplementedError();
}
@override
// TODO: implement rendering
@@ -422,6 +453,10 @@ class ThermionViewerStub extends ThermionViewer {
throw UnimplementedError();
}
@override
// TODO: implement scene
Scene get scene => throw UnimplementedError();
@override
Future setAnimationFrame(
ThermionEntity entity, int index, int animationFrame) {
@@ -499,7 +534,7 @@ class ThermionViewerStub extends ThermionViewer {
}
@override
Future setCameraFov(double degrees, {bool horizontal=true}) {
Future setCameraFov(double degrees, double width, double height) {
// TODO: implement setCameraFov
throw UnimplementedError();
}
@@ -551,11 +586,6 @@ class ThermionViewerStub extends ThermionViewer {
throw UnimplementedError();
}
@override
Future clearMorphAnimationData(ThermionEntity entity) {
throw UnimplementedError();
}
@override
Future setMorphAnimationData(
ThermionEntity entity, MorphAnimationData animation,
@@ -571,7 +601,7 @@ class ThermionViewerStub extends ThermionViewer {
}
@override
Future setParent(ThermionEntity child, ThermionEntity parent, { bool preserveScaling = false}) {
Future setParent(ThermionEntity child, ThermionEntity parent) {
// TODO: implement setParent
throw UnimplementedError();
}
@@ -714,352 +744,4 @@ class ThermionViewerStub extends ThermionViewer {
// TODO: implement setSoftShadowOptions
throw UnimplementedError();
}
@override
Future<Aabb2> getBoundingBox(ThermionEntity entity) {
// TODO: implement getBoundingBox
throw UnimplementedError();
}
@override
Future<double> getCameraFov(bool horizontal) {
// TODO: implement getCameraFov
throw UnimplementedError();
}
@override
Future queueRelativePositionUpdateWorldAxis(ThermionEntity entity, double viewportX, double viewportY, double x, double y, double z) {
// TODO: implement queueRelativePositionUpdateWorldAxis
throw UnimplementedError();
}
@override
Future setLayerEnabled(int layer, bool enabled) {
// TODO: implement setLayerEnabled
throw UnimplementedError();
}
@override
Future createIbl(double r, double g, double b, double intensity) {
// TODO: implement createIbl
throw UnimplementedError();
}
@override
// TODO: implement gizmoPickResult
Stream<FilamentPickResult> get gizmoPickResult => throw UnimplementedError();
@override
void pickGizmo(int x, int y) {
// TODO: implement pickGizmo
}
@override
Future setGizmoVisibility(bool visible) {
// TODO: implement setGizmoVisibility
throw UnimplementedError();
}
@override
Future<ThermionEntity?> getAncestor(ThermionEntity entity) {
// TODO: implement getAncestor
throw UnimplementedError();
}
@override
Future queuePositionUpdateFromViewportCoords(ThermionEntity entity, double x, double y) {
// TODO: implement queuePositionUpdateFromViewportCoords
throw UnimplementedError();
}
@override
Future removeStencilHighlight(ThermionEntity entity) {
// TODO: implement removeStencilHighlight
throw UnimplementedError();
}
@override
Future setLightDirection(ThermionEntity lightEntity, Vector3 direction) {
// TODO: implement setLightDirection
throw UnimplementedError();
}
@override
Future setLightPosition(ThermionEntity lightEntity, double x, double y, double z) {
// TODO: implement setLightPosition
throw UnimplementedError();
}
@override
Future setStencilHighlight(ThermionEntity entity, {double r = 1.0, double g = 0.0, double b = 0.0}) {
// TODO: implement setStencilHighlight
throw UnimplementedError();
}
@override
Future<double> getCameraNear() {
// TODO: implement getCameraNear
throw UnimplementedError();
}
@override
Future<Aabb2> getViewportBoundingBox(ThermionEntity entity) {
// TODO: implement getViewportBoundingBox
throw UnimplementedError();
}
@override
Future setCameraModelMatrix4(Matrix4 matrix) {
// TODO: implement setCameraModelMatrix4
throw UnimplementedError();
}
@override
Future<ThermionEntity> loadGlb(String path, {int numInstances = 1, bool keepData = false}) {
// TODO: implement loadGlb
throw UnimplementedError();
}
@override
Future<ThermionEntity> loadGltf(String path, String relativeResourcePath, {bool keepData = false}) {
// TODO: implement loadGltf
throw UnimplementedError();
}
@override
Future setMaterialPropertyFloat(ThermionEntity entity, String propertyName, int materialIndex, double value) {
// TODO: implement setMaterialPropertyFloat
throw UnimplementedError();
}
@override
Future setMaterialPropertyFloat4(ThermionEntity entity, String propertyName, int materialIndex, double f1, double f2, double f3, double f4) {
// TODO: implement setMaterialPropertyFloat4
throw UnimplementedError();
}
@override
// TODO: implement sceneUpdated
Stream<SceneUpdateEvent> get sceneUpdated => throw UnimplementedError();
@override
Future<ThermionEntity> addDirectLight(DirectLight light) {
// TODO: implement addDirectLight
throw UnimplementedError();
}
@override
Future applyTexture(covariant ThermionTexture texture, ThermionEntity entity, {int materialIndex = 0, String parameterName = "baseColorMap"}) {
// TODO: implement applyTexture
throw UnimplementedError();
}
@override
Future<ThermionTexture> createTexture(Uint8List data) {
// TODO: implement createTexture
throw UnimplementedError();
}
@override
Future<MaterialInstance> createUbershaderMaterialInstance({bool doubleSided = false, bool unlit = false, bool hasVertexColors = false, bool hasBaseColorTexture = false, bool hasNormalTexture = false, bool hasOcclusionTexture = false, bool hasEmissiveTexture = false, bool useSpecularGlossiness = false, AlphaMode alphaMode = AlphaMode.OPAQUE, bool enableDiagnostics = false, bool hasMetallicRoughnessTexture = false, int metallicRoughnessUV = 0, int baseColorUV = 0, bool hasClearCoatTexture = false, int clearCoatUV = 0, bool hasClearCoatRoughnessTexture = false, int clearCoatRoughnessUV = 0, bool hasClearCoatNormalTexture = false, int clearCoatNormalUV = 0, bool hasClearCoat = false, bool hasTransmission = false, bool hasTextureTransforms = false, int emissiveUV = 0, int aoUV = 0, int normalUV = 0, bool hasTransmissionTexture = false, int transmissionUV = 0, bool hasSheenColorTexture = false, int sheenColorUV = 0, bool hasSheenRoughnessTexture = false, int sheenRoughnessUV = 0, bool hasVolumeThicknessTexture = false, int volumeThicknessUV = 0, bool hasSheen = false, bool hasIOR = false, bool hasVolume = false}) {
// TODO: implement createUbershaderMaterialInstance
throw UnimplementedError();
}
@override
Future<MaterialInstance> createUnlitMaterialInstance() {
// TODO: implement createUnlitMaterialInstance
throw UnimplementedError();
}
@override
Future destroyMaterialInstance(covariant MaterialInstance materialInstance) {
// TODO: implement destroyMaterialInstance
throw UnimplementedError();
}
@override
Future destroyTexture(covariant ThermionTexture texture) {
// TODO: implement destroyTexture
throw UnimplementedError();
}
@override
Future createGeometry(Geometry geometry, {MaterialInstance? materialInstance, bool keepData = false}) {
// TODO: implement createGeometry
throw UnimplementedError();
}
@override
Future<ThermionEntity> loadGlbFromBuffer(Uint8List data, {int numInstances = 1, bool keepData = false, int priority = 4, int layer = 0}) {
// TODO: implement loadGlbFromBuffer
throw UnimplementedError();
}
@override
Future setMaterialPropertyInt(ThermionEntity entity, String propertyName, int materialIndex, int value) {
// TODO: implement setMaterialPropertyInt
throw UnimplementedError();
}
@override
Future<MaterialInstance?> getMaterialInstanceAt(ThermionEntity entity, int index) {
// TODO: implement getMaterialInstanceAt
throw UnimplementedError();
}
@override
Future setLayerVisibility(int layer, bool visible) {
// TODO: implement setLayerVisibility
throw UnimplementedError();
}
@override
Future setMaterialDepthWrite(ThermionEntity entity, int materialIndex, bool enabled) {
// TODO: implement setMaterialDepthWrite
throw UnimplementedError();
}
@override
Future setVisibilityLayer(ThermionEntity entity, int layer) {
// TODO: implement setVisibilityLayer
throw UnimplementedError();
}
@override
Future requestFrame() {
throw UnimplementedError();
}
@override
Future setCameraLensProjection({double near = kNear, double far = kFar, double? aspect, double focalLength = kFocalLength}) {
// TODO: implement setCameraLensProjection
throw UnimplementedError();
}
@override
Future<ThermionEntity> getMainCameraEntity() {
// TODO: implement getMainCameraEntity
throw UnimplementedError();
}
@override
Future<Camera> getMainCamera() {
// TODO: implement getMainCamera
throw UnimplementedError();
}
@override
Future<Camera> createCamera() {
// TODO: implement createCamera
throw UnimplementedError();
}
@override
Future registerRenderHook(Future Function() hook) {
// TODO: implement registerRenderHook
throw UnimplementedError();
}
@override
Future setActiveCamera(covariant Camera camera) {
// TODO: implement setActiveCamera
throw UnimplementedError();
}
@override
Future registerRequestFrameHook(Future Function() hook) {
// TODO: implement registerRequestFrameHook
throw UnimplementedError();
}
@override
Future unregisterRequestFrameHook(Future Function() hook) {
// TODO: implement unregisterRequestFrameHook
throw UnimplementedError();
}
@override
Camera getCameraAt(int index) {
// TODO: implement getCameraAt
throw UnimplementedError();
}
@override
int getCameraCount() {
// TODO: implement getCameraCount
throw UnimplementedError();
}
@override
Future queueTransformUpdates(List<ThermionEntity> entities, List<Matrix4> transforms) {
// TODO: implement queueTransformUpdates
throw UnimplementedError();
}
@override
Future<RenderTarget> createRenderTarget(int width, int height, int textureHandle) {
// TODO: implement createRenderTarget
throw UnimplementedError();
}
@override
Future setRenderTarget(covariant RenderTarget renderTarget) {
// TODO: implement setRenderTarget
throw UnimplementedError();
}
@override
Future<View> createView() {
// TODO: implement createView
throw UnimplementedError();
}
@override
Future<View> getViewAt(int index) {
// TODO: implement getViewAt
throw UnimplementedError();
}
@override
Future<Gizmo> createGizmo(covariant View view) {
// TODO: implement createGizmo
throw UnimplementedError();
}
@override
Future<SwapChain> createHeadlessSwapChain(int width, int height) {
// TODO: implement createHeadlessSwapChain
throw UnimplementedError();
}
@override
Future<Uint8List> capture({covariant SwapChain? swapChain, covariant View? view, covariant RenderTarget? renderTarget}) {
// TODO: implement capture
throw UnimplementedError();
}
@override
Future<SwapChain> createSwapChain(handle) {
// TODO: implement createSwapChain
throw UnimplementedError();
}
@override
Future render({covariant SwapChain? swapChain}) {
// TODO: implement render
throw UnimplementedError();
}
@override
Future<Camera> getActiveCamera() {
// TODO: implement getActiveCamera
throw UnimplementedError();
}
}

View File

@@ -1,9 +1,6 @@
import 'dart:ffi';
import 'dart:io';
import 'package:ffi/ffi.dart';
import '../viewer/src/ffi/src/thermion_dart.g.dart';
import '../compatibility/compatibility.dart';
class DartResourceLoader {
static final _assets = <int, Pointer>{};

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);
}

View File

@@ -1,110 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdint.h>
typedef int32_t EntityId;
typedef struct TCamera TCamera;
typedef struct TMaterialInstance TMaterialInstance;
typedef struct TEngine TEngine;
typedef struct TEntityManager TEntityManager;
typedef struct TViewer TViewer;
typedef struct TSceneManager TSceneManager;
typedef struct TRenderTarget TRenderTarget;
typedef struct TSwapChain TSwapChain;
typedef struct TView TView;
typedef struct TGizmo TGizmo;
typedef struct TScene TScene;
struct TMaterialKey {
bool doubleSided = 1;
bool unlit = 1;
bool hasVertexColors = 1;
bool hasBaseColorTexture = 1;
bool hasNormalTexture = 1;
bool hasOcclusionTexture = 1;
bool hasEmissiveTexture = 1;
bool useSpecularGlossiness = 1;
int alphaMode = 4;
bool enableDiagnostics = 4;
union {
#ifdef __cplusplus
struct {
bool hasMetallicRoughnessTexture;
uint8_t metallicRoughnessUV;
};
struct {
bool hasSpecularGlossinessTexture;
uint8_t specularGlossinessUV;
};
#else
struct {
bool hasMetallicRoughnessTexture = 1;
uint8_t metallicRoughnessUV = 7;
};
struct {
bool hasSpecularGlossinessTexture = 1;
uint8_t specularGlossinessUV = 7;
};
#endif
};
uint8_t baseColorUV;
// -- 32 bit boundary --
bool hasClearCoatTexture = 1;
uint8_t clearCoatUV = 7;
bool hasClearCoatRoughnessTexture = 1;
uint8_t clearCoatRoughnessUV = 7;
bool hasClearCoatNormalTexture = 1;
uint8_t clearCoatNormalUV = 7;
bool hasClearCoat = 1;
bool hasTransmission = 1;
bool hasTextureTransforms = 6;
// -- 32 bit boundary --
uint8_t emissiveUV;
uint8_t aoUV;
uint8_t normalUV;
bool hasTransmissionTexture = 1;
uint8_t transmissionUV = 7;
// -- 32 bit boundary --
bool hasSheenColorTexture = 1;
uint8_t sheenColorUV = 7;
bool hasSheenRoughnessTexture = 1;
uint8_t sheenRoughnessUV = 7;
bool hasVolumeThicknessTexture = 1;
uint8_t volumeThicknessUV = 7;
bool hasSheen = 1;
bool hasIOR = 1;
bool hasVolume = 1;
} ;
typedef struct TMaterialKey TMaterialKey;
typedef struct {
double x;
double y;
double z;
double w;
} double4;
typedef struct {
double col1[4];
double col2[4];
double col3[4];
double col4[4];
} double4x4;
struct Aabb2 {
float minX;
float minY;
float maxX;
float maxY;
};
typedef struct Aabb2 Aabb2;
#ifdef __cplusplus
}
#endif

View File

@@ -1,59 +0,0 @@
#pragma once
#include <stddef.h>
#include <filament/Engine.h>
#include <filament/VertexBuffer.h>
#include <filament/IndexBuffer.h>
#include <filament/TransformManager.h>
#include <filament/Texture.h>
#include <filament/RenderableManager.h>
#include <filament/Viewport.h>
#include <filament/Frustum.h>
namespace thermion
{
using namespace filament;
// CustomGeometry.h
class CustomGeometry {
public:
CustomGeometry(
float* vertices,
uint32_t numVertices,
float* normals,
uint32_t numNormals,
float *uvs,
uint32_t numUvs,
uint16_t* indices,
uint32_t numIndices,
RenderableManager::PrimitiveType primitiveType,
Engine* engine);
~CustomGeometry();
VertexBuffer* vertexBuffer() const;
IndexBuffer* indexBuffer() const;
Box getBoundingBox() const;
float* vertices = nullptr;
float* normals = nullptr;
float *uvs = nullptr;
uint32_t numVertices = 0;
uint16_t* indices = 0;
uint32_t numIndices = 0;
Box boundingBox;
RenderableManager::PrimitiveType primitiveType;
private:
Engine* _engine;
bool _vertexBufferFreed = false;
bool _indexBufferFreed = false;
void computeBoundingBox();
};
}

View File

@@ -36,15 +36,24 @@
#include "SceneManager.hpp"
#include "ThreadPool.hpp"
namespace thermion
namespace thermion_filament
{
typedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point_t;
using namespace std::chrono;
using namespace filament;
using namespace filament::math;
using namespace gltfio;
using namespace camutils;
enum ToneMapping
{
ACES,
FILMIC,
LINEAR
};
class FilamentViewer
{
@@ -54,54 +63,69 @@ namespace thermion
FilamentViewer(const void *context, const ResourceLoaderWrapperImpl *const resourceLoaderWrapper, void *const platform = nullptr, const char *uberArchivePath = nullptr);
~FilamentViewer();
View* createView();
View* getViewAt(int index);
void setToneMapping(ToneMapping toneMapping);
void setBloom(float strength);
void loadSkybox(const char *const skyboxUri);
void removeSkybox();
void loadIbl(const char *const iblUri, float intensity);
void removeIbl();
void rotateIbl(const math::mat3f &matrix);
void createIbl(float r, float g, float b, float intensity);
void removeEntity(EntityId asset);
void clearEntities();
void updateViewportAndCameraProjection(int height, int width, float scaleFactor);
void render(
uint64_t frameTimeInNanos
);
uint64_t frameTimeInNanos,
void *pixelBuffer,
void (*callback)(void *buf, size_t size, void *data),
void *data);
void setFrameInterval(float interval);
void setMainCamera(View *view);
bool setCamera(EntityId asset, const char *nodeName);
void setMainCamera();
EntityId getMainCamera();
Camera* getCamera(EntityId entity);
void setCameraFov(double fovDegrees, double aspect);
float getCameraFov(bool horizontal);
void setCameraFov(double fovDegrees, bool horizontal);
void createSwapChain(const void *surface, uint32_t width, uint32_t height);
void destroySwapChain();
SwapChain* createSwapChain(const void *surface);
SwapChain* createSwapChain(uint32_t width, uint32_t height);
void destroySwapChain(SwapChain* swapChain);
RenderTarget* createRenderTarget(intptr_t textureId, uint32_t width, uint32_t height);
void destroyRenderTarget(RenderTarget* renderTarget);
void createRenderTarget(intptr_t textureId, uint32_t width, uint32_t height);
Renderer *getRenderer();
std::map<SwapChain*, std::vector<View*>> _renderable;
void setRenderable(View* view, SwapChain* swapChain, bool renderable);
void setBackgroundColor(const float r, const float g, const float b, const float a);
void setBackgroundImage(const char *resourcePath, bool fillHeight, uint32_t width, uint32_t height);
void setBackgroundImage(const char *resourcePath, bool fillHeight);
void clearBackgroundImage();
void setBackgroundImagePosition(float x, float y, bool clamp, uint32_t width, uint32_t height);
void setBackgroundImagePosition(float x, float y, bool clamp);
void pick(View *view, uint32_t x, uint32_t y, void (*callback)(EntityId entityId, int x, int y));
Engine* getEngine() {
return _engine;
}
// Camera methods
void moveCameraToAsset(EntityId entityId);
void setViewFrustumCulling(bool enabled);
void setCameraExposure(float aperture, float shutterSpeed, float sensitivity);
void setCameraPosition(float x, float y, float z);
void setCameraRotation(float w, float x, float y, float z);
const math::mat4 getCameraModelMatrix();
const math::mat4 getCameraViewMatrix();
const math::mat4 getCameraProjectionMatrix();
const math::mat4 getCameraCullingProjectionMatrix();
const filament::Frustum getCameraFrustum();
void setCameraModelMatrix(const float *const matrix);
void setCameraProjectionMatrix(const double *const matrix, double near, double far);
void setCameraFocalLength(float focalLength);
void setCameraCulling(double near, double far);
double getCameraCullingNear();
double getCameraCullingFar();
void setCameraFocusDistance(float focusDistance);
void setCameraManipulatorOptions(filament::camutils::Mode mode, double orbitSpeedX, double orbitSpeedY, double zoomSpeed);
void grabBegin(float x, float y, bool pan);
void grabUpdate(float x, float y);
void grabEnd();
void scrollBegin();
void scrollUpdate(float x, float y, float delta);
void scrollEnd();
void pick(uint32_t x, uint32_t y, void (*callback)(EntityId entityId, int x, int y));
EntityId addLight(
LightManager::Type t,
@@ -120,58 +144,75 @@ namespace thermion
float sunHaloSize,
float sunHaloFallof,
bool shadows);
void setLightPosition(EntityId entityId, float x, float y, float z);
void setLightDirection(EntityId entityId, float x, float y, float z);
void removeLight(EntityId entityId);
void clearLights();
void setPostProcessing(bool enabled);
void capture(View* view, uint8_t *out, bool useFence, SwapChain* swapChain, void (*onComplete)());
void capture(View* view, uint8_t *out, bool useFence, SwapChain* swapChain, RenderTarget* renderTarget, void (*onComplete)());
void setRecording(bool recording);
void setRecordingOutputDirectory(const char *path);
void setAntiAliasing(bool msaaEnabled, bool fxaaEnabled, bool taaEnabled);
void setDepthOfField();
void setShadowsEnabled(bool enabled);
void setShadowType(ShadowType shadowType);
void setSoftShadowOptions( float penumbraScale, float penumbraRatioScale);
EntityId createGeometry(float *vertices, uint32_t numVertices, uint16_t *indices, uint32_t numIndices, filament::RenderableManager::PrimitiveType primitiveType = RenderableManager::PrimitiveType::TRIANGLES, const char *materialPath = nullptr);
SceneManager *const getSceneManager()
{
return (SceneManager *const)_sceneManager;
}
SwapChain* getSwapChainAt(int index) {
if(index < _swapChains.size()) {
return _swapChains[index];
}
Log("Error: index %d is greater than available swapchains", index);
return nullptr;
}
void unprojectTexture(EntityId entity, uint8_t* input, uint32_t inputWidth, uint32_t inputHeight, uint8_t* out, uint32_t outWidth, uint32_t outHeight);
private:
const ResourceLoaderWrapperImpl *const _resourceLoaderWrapper;
void* _context = nullptr;
Scene *_scene = nullptr;
View *_view = nullptr;
Engine *_engine = nullptr;
thermion::ThreadPool *_tp = nullptr;
thermion_filament::ThreadPool *_tp = nullptr;
Renderer *_renderer = nullptr;
SceneManager *_sceneManager = nullptr;
std::vector<RenderTarget*> _renderTargets;
std::vector<SwapChain*> _swapChains;
std::vector<View*> _views;
RenderTarget *_rt = nullptr;
Texture *_rtColor = nullptr;
Texture *_rtDepth = nullptr;
std::mutex _renderMutex; // mutex to ensure thread safety when removing assets
SwapChain *_swapChain = nullptr;
SceneManager *_sceneManager = nullptr;
std::mutex mtx; // mutex to ensure thread safety when removing assets
std::vector<utils::Entity> _lights;
Texture *_skyboxTexture = nullptr;
Skybox *_skybox = nullptr;
Texture *_iblTexture = nullptr;
IndirectLight *_indirectLight = nullptr;
bool _recomputeAabb = false;
bool _actualSize = false;
float _frameInterval = 1000.0 / 60.0;
// Camera properties
Camera *_mainCamera = nullptr; // the default camera added to every scene. If you want the *active* camera, access via View.
float _cameraFocalLength = 28.0f;
float _cameraFocusDistance = 0.0f;
Manipulator<double> *_manipulator = nullptr;
filament::camutils::Mode _manipulatorMode = filament::camutils::Mode::ORBIT;
double _orbitSpeedX = 0.01;
double _orbitSpeedY = 0.01;
double _zoomSpeed = 0.01;
math::mat4f _cameraPosition;
math::mat4f _cameraRotation;
void _createManipulator();
double _near = 0.05;
double _far = 1000.0;
ColorGrading *colorGrading = nullptr;
// background image properties
uint32_t _imageHeight = 0;
uint32_t _imageWidth = 0;
filament::math::mat4f _imageScale;
mat4f _imageScale;
Texture *_imageTexture = nullptr;
Texture *_dummyImageTexture = nullptr;
utils::Entity _imageEntity;
@@ -186,10 +227,12 @@ namespace thermion
void savePng(void *data, size_t size, int frameNumber);
void createBackgroundImage();
time_point_t _recordingStartTime = std::chrono::high_resolution_clock::now();
time_point_t _fpsCounterStartTime = std::chrono::high_resolution_clock::now();
bool _recording = false;
std::string _recordingOutputDirectory = std::string("/tmp");
std::mutex _recordingMutex;
std::mutex _imageMutex;
double _cumulativeAnimationUpdateTime = 0;
int _frameCount = 0;

View File

@@ -1,111 +0,0 @@
#pragma once
#include <utils/Entity.h>
#include <filament/Engine.h>
#include <filament/Material.h>
#include <filament/MaterialInstance.h>
#include <filament/Scene.h>
#include <filament/Camera.h>
#include <filament/View.h>
#include <filament/Viewport.h>
#include <gltfio/AssetLoader.h>
#include <gltfio/FilamentAsset.h>
#include <gltfio/FilamentInstance.h>
#include <gltfio/ResourceLoader.h>
#include <filament/IndexBuffer.h>
#include <filament/InstanceBuffer.h>
#include "material/gizmo.h"
#include "ThermionDartApi.h"
namespace thermion {
using namespace filament;
using namespace utils;
class Gizmo {
enum Axis { X, Y, Z};
public:
Gizmo(Engine *engine, View *view, Scene *scene);
~Gizmo();
typedef void (*PickCallback)(EntityId entityId, uint32_t x, uint32_t y, View *view);
Entity x() {
return _entities[0];
};
Entity y() {
return _entities[1];
};
Entity z() {
return _entities[2];
};
Entity center() {
return _entities[3];
};
bool isActive() {
return _isActive;
}
void pick(uint32_t x, uint32_t y, PickCallback callback);
bool isGizmoEntity(Entity entity);
void setVisibility(bool visible);
private:
class PickCallbackHandler {
public:
PickCallbackHandler(Gizmo* gizmo, PickCallback callback) : _gizmo(gizmo), _callback(callback) {};
void handle(filament::View::PickingQueryResult const &result) {
auto x = static_cast<int32_t>(result.fragCoords.x);
auto y= static_cast<int32_t>(result.fragCoords.y);
for(int i = 0; i < 7; i++) {
if(_gizmo->_entities[i] == result.renderable) {
if(i < 4) {
return;
}
_gizmo->highlight(_gizmo->_entities[i - 4]);
_callback(Entity::smuggle(_gizmo->_entities[i - 4]), x, y, _gizmo->_view);
return;
}
}
_gizmo->unhighlight();
_callback(0, x, y, _gizmo->_view);
delete(this);
}
private:
Gizmo* _gizmo;
PickCallback _callback;
};
void createTransparentRectangles();
void highlight(Entity entity);
void unhighlight();
Engine *_engine;
Scene *_scene;
View *_view;
utils::Entity _entities[7] = { utils::Entity(), utils::Entity(), utils::Entity(), utils::Entity(), utils::Entity(), utils::Entity(), utils::Entity() };
Material* _material;
MaterialInstance* _materialInstances[7];
math::float4 inactiveColors[3] {
math::float4 { 1.0f, 0.0f, 0.0f, 0.5f },
math::float4 { 0.0f, 1.0f, 0.0f, 0.5f },
math::float4 { 0.0f, 0.0f, 1.0f, 0.5f },
};
math::float4 activeColors[3] {
math::float4 { 1.0f, 0.0f, 0.0f, 1.0f },
math::float4 { 0.0f, 1.0f, 0.0f, 1.0f },
math::float4 { 0.0f, 0.0f, 1.0f, 1.0f },
};
bool _isActive = true;
};
}

View File

@@ -1,52 +0,0 @@
#pragma once
#include <vector>
#include <utils/Entity.h>
#include <filament/Engine.h>
#include <filament/Material.h>
#include <filament/MaterialInstance.h>
#include <filament/Scene.h>
#include <filament/Camera.h>
#include <filament/View.h>
#include <filament/Viewport.h>
#include <gltfio/AssetLoader.h>
#include <gltfio/FilamentAsset.h>
#include <gltfio/FilamentInstance.h>
#include <gltfio/ResourceLoader.h>
#include <filament/IndexBuffer.h>
#include <filament/InstanceBuffer.h>
#include "material/gizmo.h"
namespace thermion {
using namespace filament;
using namespace utils;
class GridOverlay {
public:
GridOverlay(Engine& engine);
void destroy();
utils::Entity sphere() {
return _sphereEntity;
}
utils::Entity grid() {
return _gridEntity;
}
private:
Engine &_engine;
utils::Entity _gridEntity;
utils::Entity _sphereEntity;
Material* _material;
MaterialInstance* _materialInstance;
MaterialInstance* _sphereMaterialInstance;
};
}

View File

@@ -8,7 +8,7 @@
using namespace std::chrono_literals;
#endif
namespace thermion
namespace thermion_filament
{
struct ResourceLoaderWrapperImpl : public ResourceLoaderWrapper

View File

@@ -4,11 +4,8 @@
#include <vector>
#include <memory>
#include <map>
#include <set>
#include <filament/Scene.h>
#include <filament/Camera.h>
#include <filament/View.h>
#include <gltfio/AssetLoader.h>
#include <gltfio/FilamentAsset.h>
@@ -17,20 +14,16 @@
#include <filament/IndexBuffer.h>
#include <filament/InstanceBuffer.h>
#include <utils/NameComponentManager.h>
#include "CustomGeometry.hpp"
#include "APIBoundaryTypes.h"
#include "GridOverlay.hpp"
#include "material/gizmo.h"
#include "utils/NameComponentManager.h"
#include "ResourceBuffer.hpp"
#include "components/CollisionComponentManager.hpp"
#include "components/AnimationComponentManager.hpp"
#include "tsl/robin_map.h"
namespace thermion
namespace thermion_filament
{
typedef int32_t EntityId;
@@ -44,46 +37,13 @@ namespace thermion
class SceneManager
{
public:
SceneManager(
const ResourceLoaderWrapperImpl *const loader,
SceneManager(const ResourceLoaderWrapperImpl *const loader,
Engine *engine,
Scene *scene,
const char *uberArchivePath,
Camera* mainCamera);
const char *uberArchivePath);
~SceneManager();
enum LAYERS {
DEFAULT_ASSETS = 0,
BACKGROUND = 6,
OVERLAY = 7,
};
class HighlightOverlay {
public:
HighlightOverlay(EntityId id, SceneManager* const sceneManager, Engine* const engine, float r, float g, float b);
~HighlightOverlay();
bool isValid() {
return !_entity.isNull();
}
private:
MaterialInstance* _highlightMaterialInstance = nullptr;
bool _isGeometryEntity = false;
bool _isGltfAsset = false;
FilamentInstance* _newInstance = nullptr;
Entity _entity;
Engine* const _engine;
SceneManager* const _sceneManager;
};
////
/// @brief Load the glTF file from the specified path and adds all entities to the scene.
/// @param uri the path to the asset. Should be either asset:// (representing a Flutter asset), or file:// (representing a filesystem file).
/// @param relativeResourcePath the (relative) path to the asset's resources.
/// @return the glTF entity.
///
EntityId loadGltf(const char *uri, const char *relativeResourcePath, bool keepData = false);
EntityId loadGltf(const char *uri, const char *relativeResourcePath);
////
/// @brief Load the GLB from the specified path, optionally creating multiple instances.
@@ -91,8 +51,8 @@ namespace thermion
/// @param numInstances the number of instances to create.
/// @return an Entity representing the FilamentAsset associated with the loaded FilamentAsset.
///
EntityId loadGlb(const char *uri, int numInstances, bool keepData);
EntityId loadGlbFromBuffer(const uint8_t *data, size_t length, int numInstances = 1, bool keepData = false, int priority = 4, int layer = 0);
EntityId loadGlb(const char *uri, int numInstances);
EntityId loadGlbFromBuffer(const uint8_t *data, size_t length, int numInstances = 1);
EntityId createInstance(EntityId entityId);
void remove(EntityId entity);
@@ -107,11 +67,8 @@ namespace thermion
void setScale(EntityId e, float scale);
void setPosition(EntityId e, float x, float y, float z);
void setRotation(EntityId e, float rads, float x, float y, float z, float w);
void queueTransformUpdates(EntityId* entities, math::mat4* transforms, int numEntities);
void queueRelativePositionUpdateWorldAxis(EntityId entity, float viewportCoordX, float viewportCoordY, float x, float y, float z);
void queueRelativePositionUpdateFromViewportVector(View* view, EntityId entityId, float viewportCoordX, float viewportCoordY);
void queuePositionUpdate(EntityId e, float x, float y, float z, bool relative);
void queueRotationUpdate(EntityId e, float rads, float x, float y, float z, float w, bool relative);
const utils::Entity *getCameraEntities(EntityId e);
size_t getCameraEntityCount(EntityId e);
const utils::Entity *getLightEntities(EntityId e) noexcept;
@@ -130,9 +87,6 @@ namespace thermion
int numFrames,
float frameLengthInMs);
void clearMorphAnimationBuffer(
EntityId entityId);
bool setMorphTargetWeights(EntityId entityId, const float *const weights, int count);
math::mat4f getLocalTransform(EntityId entityId);
@@ -170,19 +124,12 @@ namespace thermion
std::unique_ptr<std::vector<math::mat4f>> getBoneRestTranforms(EntityId entityId, int skinIndex);
void resetBones(EntityId entityId);
bool setTransform(EntityId entityId, math::mat4f transform);
bool setTransform(EntityId entityId, math::mat4 transform);
bool updateBoneMatrices(EntityId entityId);
void playAnimation(EntityId e, int index, bool loop, bool reverse, bool replaceActive, float crossfade = 0.3f, float startOffset = 0.0f);
void playAnimation(EntityId e, int index, bool loop, bool reverse, bool replaceActive, float crossfade = 0.3f);
void stopAnimation(EntityId e, int index);
void setMorphTargetWeights(const char *const entityName, float *weights, int count);
Texture* createTexture(const uint8_t* data, size_t length, const char* name);
bool applyTexture(EntityId entityId, Texture *texture, const char* slotName, int materialIndex);
void destroyTexture(Texture* texture);
void loadTexture(EntityId entity, const char *resourcePath, int renderableIndex);
void setAnimationFrame(EntityId entity, int animationIndex, int animationFrame);
bool hide(EntityId entity, const char *meshName);
bool reveal(EntityId entity, const char *meshName);
@@ -196,21 +143,10 @@ namespace thermion
void addCollisionComponent(EntityId entity, void (*onCollisionCallback)(const EntityId entityId1, const EntityId entityId2), bool affectsCollidingTransform);
void removeCollisionComponent(EntityId entityId);
EntityId getParent(EntityId child);
EntityId getAncestor(EntityId child);
void setParent(EntityId child, EntityId parent, bool preserveScaling);
void setParent(EntityId child, EntityId parent);
bool addAnimationComponent(EntityId entity);
void removeAnimationComponent(EntityId entity);
/// @brief renders an outline around the specified entity.
///
///
void setStencilHighlight(EntityId entity, float r, float g, float b);
/// @brief removes the outline around the specified entity.
///
///
void removeStencilHighlight(EntityId entity);
/// @brief returns the number of instances of the FilamentAsset represented by the given entity.
/// @param entityId
/// @return the number of instances
@@ -225,101 +161,24 @@ namespace thermion
///
void setPriority(EntityId entity, int priority);
/// @brief returns the 2D min/max viewport coordinates of the bounding box for the specified enitty;
/// @param out a pointer large enough to store four floats (the min/max coordinates of the bounding box)
/// @brief returns the gizmo entity, used to manipulate the global transform for entities
/// @param out a pointer to an array of three EntityId {x, y, z}
/// @return
///
Aabb2 getBoundingBox(View* view, EntityId entity);
///
/// Creates an entity with the specified geometry/material/normals and adds to the scene.
/// If [keepData] is true, stores
///
EntityId createGeometry(
float *vertices,
uint32_t numVertices,
float *normals,
uint32_t numNormals,
float *uvs,
uint32_t numUvs,
uint16_t *indices,
uint32_t numIndices,
filament::RenderableManager::PrimitiveType primitiveType = RenderableManager::PrimitiveType::TRIANGLES,
MaterialInstance* materialInstance = nullptr,
bool keepData = false
);
void getGizmo(EntityId *out);
friend class FilamentViewer;
gltfio::MaterialProvider * const unlitMaterialProvider() {
return _unlitMaterialProvider;
}
bool isGeometryEntity(EntityId entity) {
return _geometry.find(entity) != _geometry.end();
}
const CustomGeometry* const getGeometry(EntityId entityId) {
return _geometry[entityId].get();
}
bool isGltfAsset(EntityId entity) {
return getAssetByEntityId(entity) != nullptr;
}
gltfio::FilamentInstance *getInstanceByEntityId(EntityId entityId);
gltfio::FilamentAsset *getAssetByEntityId(EntityId entityId);
gltfio::FilamentInstance *createGltfAssetInstance(FilamentAsset* asset) {
return _assetLoader->createInstance(asset);
}
MaterialInstance* getMaterialInstanceAt(EntityId entityId, int materialIndex);
void setMaterialProperty(EntityId entity, int materialIndex, const char* property, float value);
void setMaterialProperty(EntityId entity, int materialIndex, const char* property, int32_t value);
void setMaterialProperty(EntityId entityId, int materialIndex, const char* property, filament::math::float4& value);
MaterialInstance* createUbershaderMaterialInstance(MaterialKey key);
void destroy(MaterialInstance* materialInstance);
gltfio::MaterialProvider* getUbershaderProvider() {
return _ubershaderProvider;
}
MaterialInstance* createUnlitMaterialInstance();
void setVisibilityLayer(EntityId entityId, int layer);
Camera* createCamera();
void destroyCamera(Camera* camera);
size_t getCameraCount();
Camera* getCameraAt(size_t index);
bool isGizmoEntity(utils::Entity entity);
Scene* getScene() {
return _scene;
}
private:
gltfio::AssetLoader *_assetLoader = nullptr;
const ResourceLoaderWrapperImpl *const _resourceLoaderWrapper;
Engine *_engine = nullptr;
Scene *_scene = nullptr;
Camera* _mainCamera;
Engine *_engine;
Scene *_scene;
gltfio::MaterialProvider *_ubershaderProvider = nullptr;
gltfio::MaterialProvider *_unlitMaterialProvider = nullptr;
gltfio::ResourceLoader *_gltfResourceLoader = nullptr;
gltfio::TextureProvider *_stbDecoder = nullptr;
gltfio::TextureProvider *_ktxDecoder = nullptr;
std::mutex _mutex;
std::mutex _stencilMutex;
std::vector<MaterialInstance*> _materialInstances;
utils::NameComponentManager *_ncm;
@@ -328,21 +187,22 @@ namespace thermion
gltfio::FilamentInstance *>
_instances;
tsl::robin_map<EntityId, gltfio::FilamentAsset *> _assets;
tsl::robin_map<EntityId, unique_ptr<CustomGeometry>> _geometry;
tsl::robin_map<EntityId, unique_ptr<HighlightOverlay>> _highlighted;
tsl::robin_map<EntityId, math::mat4> _transformUpdates;
std::set<Texture*> _textures;
std::vector<Camera*> _cameras;
tsl::robin_map<EntityId, std::tuple<math::float3, bool, math::quatf, bool, float>> _transformUpdates;
AnimationComponentManager *_animationComponentManager = nullptr;
CollisionComponentManager *_collisionComponentManager = nullptr;
gltfio::FilamentInstance *getInstanceByEntityId(EntityId entityId);
gltfio::FilamentAsset *getAssetByEntityId(EntityId entityId);
utils::Entity findEntityByName(
const gltfio::FilamentInstance *instance,
const char *entityName);
GridOverlay* _gridOverlay = nullptr;
EntityId addGizmo();
utils::Entity _gizmo[3];
Material* _gizmoMaterial;
MaterialInstance* _gizmoMaterialInstances[3];
};
}

View File

@@ -5,7 +5,7 @@
#include <cassert>
#include <cstring>
namespace thermion {
namespace thermion_filament {
//

View File

@@ -1,50 +0,0 @@
#pragma once
#ifdef __cplusplus
namespace thermion {
extern "C"
{
#endif
#include "ThermionDartApi.h"
enum Projection {
Perspective,
Orthographic
};
// Camera methods
EMSCRIPTEN_KEEPALIVE void set_camera_exposure(TCamera *camera, float aperture, float shutterSpeed, float sensitivity);
EMSCRIPTEN_KEEPALIVE void set_camera_model_matrix(TCamera *camera, double4x4 matrix);
EMSCRIPTEN_KEEPALIVE TCamera *get_camera(TViewer *viewer, EntityId entity);
EMSCRIPTEN_KEEPALIVE double4x4 get_camera_model_matrix(TCamera *const camera);
EMSCRIPTEN_KEEPALIVE double4x4 get_camera_view_matrix(TCamera *const camera);
EMSCRIPTEN_KEEPALIVE double4x4 get_camera_projection_matrix(TCamera *const camera);
EMSCRIPTEN_KEEPALIVE double4x4 get_camera_culling_projection_matrix(TCamera *const camera);
EMSCRIPTEN_KEEPALIVE const double *const get_camera_frustum(TCamera *const camera);
EMSCRIPTEN_KEEPALIVE void set_camera_projection_matrix(TCamera *camera, double4x4 matrix, double near, double far);
EMSCRIPTEN_KEEPALIVE void set_camera_projection_from_fov(TCamera *camera, double fovInDegrees, double aspect, double near, double far, bool horizontal);
EMSCRIPTEN_KEEPALIVE double get_camera_focal_length(TCamera *const camera);
EMSCRIPTEN_KEEPALIVE double Camera_getFocalLength(TCamera *const camera);
EMSCRIPTEN_KEEPALIVE double Camera_getNear(TCamera *const camera);
EMSCRIPTEN_KEEPALIVE double Camera_getCullingFar(TCamera *const camera);
EMSCRIPTEN_KEEPALIVE double4x4 Camera_getViewMatrix(TCamera *const camera);
EMSCRIPTEN_KEEPALIVE double4x4 Camera_getModelMatrix(TCamera* camera);
EMSCRIPTEN_KEEPALIVE double get_camera_near(TCamera *camera);
EMSCRIPTEN_KEEPALIVE double get_camera_culling_far(TCamera *camera);
EMSCRIPTEN_KEEPALIVE float get_camera_fov(TCamera *camera, bool horizontal);
EMSCRIPTEN_KEEPALIVE void set_camera_focus_distance(TCamera *camera, float focusDistance);
EMSCRIPTEN_KEEPALIVE void Camera_setCustomProjectionWithCulling(TCamera* camera, double4x4 projectionMatrix, double near, double far);
EMSCRIPTEN_KEEPALIVE void Camera_setModelMatrix(TCamera* camera, double4x4 modelMatrix);
EMSCRIPTEN_KEEPALIVE void Camera_setLensProjection(TCamera *camera, double near, double far, double aspect, double focalLength);
EMSCRIPTEN_KEEPALIVE EntityId Camera_getEntity(TCamera* camera);
EMSCRIPTEN_KEEPALIVE void Camera_setProjection(TCamera *const tCamera, Projection projection, double left, double right,
double bottom, double top,
double near, double far);
#ifdef __cplusplus
}
}
#endif

View File

@@ -1,19 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#include "ThermionDartApi.h"
#include "TGizmo.h"
typedef void (*GizmoPickCallback)(EntityId entityId, uint32_t x, uint32_t y, TView* view);
EMSCRIPTEN_KEEPALIVE TGizmo* Gizmo_new(TEngine *tEngine, TView *tView, TScene *tScene);
EMSCRIPTEN_KEEPALIVE void Gizmo_pick(TGizmo *tGizmo, uint32_t x, uint32_t y, GizmoPickCallback callback);
EMSCRIPTEN_KEEPALIVE void Gizmo_setVisibility(TGizmo *tGizmo, bool visible);
#ifdef __cplusplus
}
#endif

View File

@@ -1,49 +0,0 @@
#pragma once
#ifdef __cplusplus
namespace thermion {
extern "C"
{
#endif
#include "ThermionDartApi.h"
struct TViewport {
int32_t left;
int32_t bottom;
uint32_t width;
uint32_t height;
};
typedef struct TViewport TViewport;
enum ToneMapping
{
ACES,
FILMIC,
LINEAR
};
// View
EMSCRIPTEN_KEEPALIVE TViewport View_getViewport(TView *view);
EMSCRIPTEN_KEEPALIVE void View_updateViewport(TView *view, uint32_t width, uint32_t height);
EMSCRIPTEN_KEEPALIVE void View_setRenderTarget(TView *view, TRenderTarget *renderTarget);
EMSCRIPTEN_KEEPALIVE void View_setFrustumCullingEnabled(TView *view, bool enabled);
EMSCRIPTEN_KEEPALIVE void View_updateViewport(TView* tView, uint32_t width, uint32_t height);
EMSCRIPTEN_KEEPALIVE void View_setRenderTarget(TView* tView, TRenderTarget* tRenderTarget);
EMSCRIPTEN_KEEPALIVE void View_setFrustumCullingEnabled(TView* tView, bool enabled);
EMSCRIPTEN_KEEPALIVE void View_setPostProcessing(TView* tView, bool enabled);
EMSCRIPTEN_KEEPALIVE void View_setShadowsEnabled(TView* tView, bool enabled);
EMSCRIPTEN_KEEPALIVE void View_setShadowType(TView* tView, int shadowType);
EMSCRIPTEN_KEEPALIVE void View_setSoftShadowOptions(TView* tView, float penumbraScale, float penumbraRatioScale);
EMSCRIPTEN_KEEPALIVE void View_setBloom(TView* tView, float strength);
EMSCRIPTEN_KEEPALIVE void View_setToneMapping(TView* tView, TEngine* tEngine, ToneMapping toneMapping);
EMSCRIPTEN_KEEPALIVE void View_setAntiAliasing(TView *tView, bool msaa, bool fxaa, bool taa);
EMSCRIPTEN_KEEPALIVE void View_setLayerEnabled(TView *tView, int layer, bool visible);
EMSCRIPTEN_KEEPALIVE void View_setCamera(TView *tView, TCamera *tCamera);
EMSCRIPTEN_KEEPALIVE TScene* View_getScene(TView *tView);
EMSCRIPTEN_KEEPALIVE TCamera* View_getCamera(TView *tView);
#ifdef __cplusplus
}
}
#endif

View File

@@ -1,27 +0,0 @@
#pragma once
#include <math/mat4.h>
#include "APIBoundaryTypes.h"
namespace thermion {
// Helper function to convert filament::math::mat4 to double4x4
static double4x4 convert_mat4_to_double4x4(const filament::math::mat4 &mat)
{
return double4x4{
{mat[0][0], mat[0][1], mat[0][2], mat[0][3]},
{mat[1][0], mat[1][1], mat[1][2], mat[1][3]},
{mat[2][0], mat[2][1], mat[2][2], mat[2][3]},
{mat[3][0], mat[3][1], mat[3][2], mat[3][3]},
};
}
// Helper function to convert double4x4 to filament::math::mat4
static filament::math::mat4 convert_double4x4_to_mat4(const double4x4 &d_mat)
{
return filament::math::mat4{
filament::math::float4{float(d_mat.col1[0]), float(d_mat.col1[1]), float(d_mat.col1[2]), float(d_mat.col1[3])},
filament::math::float4{float(d_mat.col2[0]), float(d_mat.col2[1]), float(d_mat.col2[2]), float(d_mat.col2[3])},
filament::math::float4{float(d_mat.col3[0]), float(d_mat.col3[1]), float(d_mat.col3[2]), float(d_mat.col3[3])},
filament::math::float4{float(d_mat.col4[0]), float(d_mat.col4[1]), float(d_mat.col4[2]), float(d_mat.col4[3])}};
}
}

Some files were not shown because too many files have changed in this diff Show More