Compare commits

...

352 Commits

Author SHA1 Message Date
Nick Fisher
cb517c907d chore(release): publish packages
- thermion_dart@0.2.1-dev.0.0.8
 - thermion_flutter_web@0.1.0+9
 - thermion_flutter@0.2.1-dev.7
 - thermion_flutter_platform_interface@0.2.1-dev.7
 - thermion_flutter_ffi@0.2.1-dev.7
2024-10-14 09:36:55 +08:00
Nick Fisher
a6f5e59cbb fix: move ThermionWin32.h to include 2024-10-14 09:36:28 +08:00
Nick Fisher
9420143a36 chore(release): publish packages
- thermion_dart@0.2.1-dev.0.0.7
 - thermion_flutter_web@0.1.0+8
 - thermion_flutter@0.2.1-dev.6
 - thermion_flutter_platform_interface@0.2.1-dev.6
 - thermion_flutter_ffi@0.2.1-dev.6
2024-10-14 09:25:59 +08:00
Nick Fisher
eb8835b63a Merge pull request #69 from nmfisher/feature/multiple_swapchains
Support multiple views/widgets/swapchains
2024-10-14 12:05:21 +11:00
Nick Fisher
f9468db266 Windows embedder fixes 2024-10-14 11:23:56 +11:00
Nick Fisher
1135ba054c cleanup 2024-10-12 15:07:14 +11:00
Nick Fisher
8f7509a23f cleanup 2024-10-12 15:06:57 +11:00
Nick Fisher
cba9ee98ad (flutter) set view renderable on Windows 2024-10-12 14:56:04 +11:00
Nick Fisher
0ec0fef8f3 move standalone Windows file to extras 2024-10-12 14:55:41 +11:00
Nick Fisher
a348562f56 cleanup 2024-10-12 14:36:51 +11:00
Nick Fisher
392a606bbc don't call endFrame() if no views were rendered 2024-10-12 14:36:05 +11:00
Nick Fisher
440bed4485 add files to run standalone Windows 2024-10-12 14:34:42 +11:00
Nick Fisher
a321966e5b fix Windows build.dart to avoid native_assets fork; add implementations for ThermionFlutterWindows 2024-10-12 02:14:37 +11:00
Nick Fisher
f180c1018f mobile gesture handler 2024-10-11 15:37:03 +08:00
Nick Fisher
f267aa6dc6 chore(release): publish packages
- thermion_dart@0.2.1-dev.0.0.6
 - thermion_flutter_web@0.1.0+7
 - thermion_flutter_platform_interface@0.2.1-dev.5
 - thermion_flutter@0.2.1-dev.5
 - thermion_flutter_ffi@0.2.1-dev.5
2024-10-10 20:58:19 +08:00
Nick Fisher
1c74e83c2c add native_assets_cli to dependencies 2024-10-10 20:57:50 +08:00
Nick Fisher
099a895eb6 chore(release): publish packages
- thermion_dart@0.2.1-dev.0.0.5
 - thermion_flutter_platform_interface@0.2.1-dev.4
 - thermion_flutter_web@0.1.0+6
 - thermion_flutter@0.2.1-dev.4
 - thermion_flutter_ffi@0.2.1-dev.4
2024-10-10 20:49:40 +08:00
Nick Fisher
3b810f84da add animation tests 2024-10-10 20:49:08 +08:00
Nick Fisher
10f2c7d36b update cube test asset with morph targets 2024-10-10 20:49:01 +08:00
Nick Fisher
5b849638de update cube test asset with morph targets 2024-10-10 20:48:54 +08:00
Nick Fisher
857fd6f782 update tests 2024-10-10 20:48:41 +08:00
Nick Fisher
29edec63ab bump animation_tools_dart dependency 2024-10-10 20:47:45 +08:00
Nick Fisher
c6afc4756a bump animation_tools_dart dependency 2024-10-10 20:47:33 +08:00
Nick Fisher
365657cf88 add pick to view tests 2024-10-10 20:47:17 +08:00
Nick Fisher
5441dedcf4 add fall-through for InputAction.ZOOM for Third Person camera delegate 2024-10-10 20:47:00 +08:00
Nick Fisher
254b6d8af2 move zoom delta to InputAction.ZOOM for FreeFlight rotation delegate 2024-10-10 20:46:34 +08:00
Nick Fisher
1459aea5cf move zoom delta to InputAction.ZOOM for FixedOrbit rotation delegate 2024-10-10 20:46:16 +08:00
Nick Fisher
80d8525671 pass through sensitivity options in DelegateGestureHandler and implement cameraUpdated 2024-10-10 20:45:33 +08:00
Nick Fisher
389a165ed3 rearrange package folders 2024-10-10 20:44:55 +08:00
Nick Fisher
d8f309d21b add stubbed methods 2024-10-10 20:44:39 +08:00
Nick Fisher
ee983ddfaa add InputAction.ZOOM 2024-10-10 20:43:58 +08:00
Nick Fisher
c1cdd37e9d add DefaultPickDelegate 2024-10-10 20:43:31 +08:00
Nick Fisher
646f05933d use condition_variable for render thread requestFrame as well as tasks 2024-10-10 20:43:04 +08:00
Nick Fisher
3f854a7f27 rearrange utils dir 2024-10-10 14:59:11 +08:00
Nick Fisher
740dbea8bd add Axis class and rearrange utils dir 2024-10-10 14:58:50 +08:00
Nick Fisher
95a44936ac add option for async loading glTF resources 2024-10-10 14:58:09 +08:00
Nick Fisher
9deafc7371 return View when picking, use uint32_t for morphIndices 2024-10-10 08:42:16 +08:00
Nick Fisher
c1af7e374d set bloom enabled by default, but 0 strength 2024-10-10 08:39:48 +08:00
Nick Fisher
cd71db72be chore(release): publish packages
- thermion_dart@0.2.1-dev.0.0.4
 - thermion_flutter_web@0.1.0+5
 - thermion_flutter@0.2.1-dev.3
 - thermion_flutter_platform_interface@0.2.1-dev.3
 - thermion_flutter_ffi@0.2.1-dev.3
2024-10-02 17:59:56 +08:00
Nick Fisher
a9d90f966b chore: make pub.dev happy 2024-10-02 17:59:34 +08:00
Nick Fisher
1a323ca551 chore(release): publish packages
- thermion_dart@0.2.1-dev.0.0.3
 - thermion_flutter_web@0.1.0+4
 - thermion_flutter@0.2.1-dev.2
 - thermion_flutter_platform_interface@0.2.1-dev.2
 - thermion_flutter_ffi@0.2.1-dev.2
2024-10-02 17:58:24 +08:00
Nick Fisher
2d85e191bc chore: make pub.dev happy 2024-10-02 17:58:08 +08:00
Nick Fisher
1b971a859c chore(release): publish packages
- thermion_dart@0.2.1-dev.0.0.2
 - thermion_flutter_web@0.1.0+3
 - thermion_flutter_ffi@0.2.1-dev.1
 - thermion_flutter@0.2.1-dev.1
 - thermion_flutter_platform_interface@0.2.1-dev.1
2024-10-02 17:56:55 +08:00
Nick Fisher
78b697d1c2 chore: make pub.dev happy 2024-10-02 17:56:37 +08:00
Nick Fisher
9da2ce6672 chore(release): publish packages
- thermion_dart@0.2.1-dev.0.0.1
 - thermion_flutter_web@0.1.0+2
 - thermion_flutter@0.2.1-dev.0
 - thermion_flutter_platform_interface@0.2.1-dev.0
 - thermion_flutter_ffi@0.2.1-dev.0
2024-10-02 17:55:23 +08:00
Nick Fisher
39fabd501d chore: move swift bindings to test folder to make pub.dev happy 2024-10-02 17:53:31 +08:00
Nick Fisher
3f1867dd6f chore(release): publish packages
- thermion_dart@0.2.1-dev.0.0.0
 - thermion_flutter@0.2.1-dev.0.0.0
 - thermion_flutter_ffi@0.2.1-dev.0.0.0
 - thermion_flutter_platform_interface@0.2.1-dev.0.0.0
 - thermion_flutter_web@0.1.0+1
2024-10-02 17:46:54 +08:00
Nick Fisher
68d29041b0 chore(release): publish packages
- thermion_dart@0.2.0
 - thermion_flutter@0.2.0
 - thermion_flutter_ffi@0.2.0
 - thermion_flutter_platform_interface@0.2.0
 - thermion_flutter_web@0.1.0
2024-10-02 17:41:13 +08:00
Nick Fisher
b300e86962 chore(release): publish packages
- thermion_dart@0.2.0-dev.8.0.0
 - thermion_flutter@0.2.0-dev.8.0.0
 - thermion_flutter_ffi@0.2.0-dev.8.0.0
 - thermion_flutter_platform_interface@0.2.0-dev.8.0.0
 - thermion_flutter_web@0.1.0-dev.8.0.0
2024-10-02 16:50:41 +08:00
Nick Fisher
562ecf2ee5 feat: camera and resizing improvements 2024-10-02 16:47:55 +08:00
Nick Fisher
d294938a2c chore: remove EMSCRIPTEN ifdefs in *RenderThread methods (no longer used) 2024-09-30 18:24:05 +08:00
Nick Fisher
027cf23069 chore: cleanup 2024-09-30 18:20:30 +08:00
Nick Fisher
c4598637bb feat: support multiple ThermionWidget on Android 2024-09-30 18:20:05 +08:00
Nick Fisher
8a94b6a334 feat: use imported texture on iOS 2024-09-30 14:51:11 +08:00
Nick Fisher
c80c163212 feat: working implementation of multiple widgets on macos 2024-09-30 13:45:57 +08:00
Nick Fisher
921a994eb6 refactor: continual refactor to support multiple render targets 2024-09-28 18:28:05 +08:00
Nick Fisher
65e60da288 chore: upgrade Swift interop gen 2024-09-28 17:59:05 +08:00
Nick Fisher
661185083e chore: refactoring and cleanup for Gizmo 2024-09-28 13:24:28 +08:00
Nick Fisher
a2a26555e2 feat: more work on multiple views/swapchains 2024-09-28 11:19:06 +08:00
Nick Fisher
7f11250b79 refactor!: refactor to support multiple Views/Render Targets 2024-09-27 23:16:01 +08:00
Nick Fisher
a6d2f2ecf9 feat!: big refactor to support multiple swapchains 2024-09-27 18:39:20 +08:00
Nick Fisher
399d447eec chore: don't keep superfluous handle to rtDepth and rtColor 2024-09-27 16:40:30 +08:00
Nick Fisher
fb6204e47c chore: cleanup 2024-09-27 15:19:53 +08:00
Nick Fisher
566856c8fb chore: update bindings 2024-09-27 15:13:45 +08:00
Nick Fisher
d29dd207b6 feat: add rendering check to ThermionWidget ticker 2024-09-27 15:13:33 +08:00
Nick Fisher
e20489900d fix!: replace queuePosition/Rotation with queueTransforms 2024-09-27 15:12:43 +08:00
Nick Fisher
567a268ded fix!: replace queuePosition/Rotation with queueTransforms 2024-09-27 15:12:33 +08:00
Nick Fisher
6a57d242f9 feat: add setParameterFloat2 method 2024-09-27 15:12:14 +08:00
Nick Fisher
41e0851d70 feat: add setParameterFloat2 method 2024-09-27 15:12:05 +08:00
Nick Fisher
95b378348c refactor!: remove RenderThread methods no longer needed 2024-09-27 15:11:47 +08:00
Nick Fisher
1309bf7c6e fix!: replace queuePosition/Rotation with queueTransforms 2024-09-27 15:10:13 +08:00
Nick Fisher
2531b507b0 fix!: replace queuePosition/Rotation with queueTransforms 2024-09-27 15:09:49 +08:00
Nick Fisher
16dc0419e2 feat: add uvScale to unlit material 2024-09-27 15:08:30 +08:00
Nick Fisher
4e7ec6bfb4 feat: add ThirdPersonCameraDelegate 2024-09-27 15:06:51 +08:00
Nick Fisher
035ad48fe4 feat!: set baseColorIndex to -1 by default in unlit materialss 2024-09-27 15:06:32 +08:00
Nick Fisher
ff7c582157 chore: update tests 2024-09-27 15:06:02 +08:00
Nick Fisher
0876a91e17 add ThirdPersonCameraDelegate 2024-09-27 15:05:41 +08:00
Nick Fisher
a2d3c1d73e chore: getter for native camera view matrix 2024-09-26 18:49:32 +08:00
Nick Fisher
1a721deee6 more camera work 2024-09-26 18:41:34 +08:00
Nick Fisher
4355d9c83f more camera work 2024-09-26 16:51:14 +08:00
Nick Fisher
239891c400 chore: rearrange library/export structure 2024-09-26 16:35:33 +08:00
Nick Fisher
ac10aa0a1e chore: camera transform tests 2024-09-26 15:56:54 +08:00
Nick Fisher
7ecf414a47 feat: set camera model matrix directly 2024-09-26 15:52:50 +08:00
Nick Fisher
87c96d06a4 chore: rearrange library dirs, gesture handler improvements 2024-09-26 15:52:23 +08:00
Nick Fisher
26a8d9c6b5 chore: import cleanup 2024-09-26 10:48:40 +08:00
Nick Fisher
947a77f619 chore: rearrange flutter gesture/widget directory structure 2024-09-26 10:46:52 +08:00
Nick Fisher
c850513b7f chore: rearrange flutter gesture/widget directory structure 2024-09-26 10:46:10 +08:00
Nick Fisher
c47d827139 chore: rename thermion_flutter src dir 2024-09-26 10:44:47 +08:00
Nick Fisher
a2684ae47d renaming/refactoring and allow creating new cameras 2024-09-25 23:56:25 +08:00
Nick Fisher
2b1339b560 feat: expose more camera methods 2024-09-25 23:09:27 +08:00
Nick Fisher
59aec2bcc9 chore(release): publish packages
- thermion_dart@0.2.0-dev.7.0
 - thermion_flutter_web@0.1.0-dev.7.0
 - thermion_flutter@0.2.0-dev.7.0
 - thermion_flutter_platform_interface@0.2.0-dev.7.0
 - thermion_flutter_ffi@0.2.0-dev.7.0
2024-09-25 22:35:46 +08:00
Nick Fisher
61d1581b96 fix!: fix min SDK for thermion_dart 2024-09-25 22:35:30 +08:00
Nick Fisher
8e47332ce8 chore(release): publish packages
- thermion_dart@0.2.0-dev.6.0
 - thermion_flutter_web@0.1.0-dev.6.0
 - thermion_flutter@0.2.0-dev.6.0
 - thermion_flutter_platform_interface@0.2.0-dev.6.0
 - thermion_flutter_ffi@0.2.0-dev.6.0
2024-09-25 21:58:28 +08:00
Nick Fisher
ffe8bee98b chore!: cleanup deleted export 2024-09-25 21:58:19 +08:00
Nick Fisher
1e07486017 chore(release): publish packages
- thermion_flutter@0.2.0-dev.6.0
2024-09-25 21:57:38 +08:00
Nick Fisher
58da196876 chore!: remove superseded HardwareKeyboard* classes 2024-09-25 21:56:16 +08:00
Nick Fisher
78af155a6d chore(release): publish packages
- thermion_dart@0.2.0-dev.5.0
 - thermion_flutter_web@0.1.0-dev.5.0
 - thermion_flutter@0.2.0-dev.5.0
 - thermion_flutter_platform_interface@0.2.0-dev.5.0
 - thermion_flutter_ffi@0.2.0-dev.5.0
2024-09-25 21:52:58 +08:00
Nick Fisher
d7e1b3d7ba chore!: remove EntityTransformController (requires replacement) 2024-09-25 21:52:42 +08:00
Nick Fisher
877eae35cd chore(release): publish packages
- thermion_dart@0.2.0-dev.4.0
 - thermion_flutter_web@0.1.0-dev.4.0
 - thermion_flutter@0.2.0-dev.4.0
 - thermion_flutter_platform_interface@0.2.0-dev.4.0
 - thermion_flutter_ffi@0.2.0-dev.4.0
2024-09-25 21:51:19 +08:00
Nick Fisher
87846b68de fix!: (web/wasm) free pick callbacks on dispose 2024-09-25 21:50:31 +08:00
Nick Fisher
6581ee8a2a chore!: restructure viewer folders as libraries to only export the public interface 2024-09-25 21:49:15 +08:00
Nick Fisher
951064e657 chore(release): publish packages
- thermion_flutter@0.2.0-dev.3.0
2024-09-25 21:20:54 +08:00
Nick Fisher
68ebf945a5 chore: add path to dev deps 2024-09-25 21:20:34 +08:00
Nick Fisher
153817e859 chore!: (flutter) cleanup for pub.dev publishing 2024-09-25 21:20:04 +08:00
Nick Fisher
ba0bc54fa7 fix!: remove EntityControllerMouseWidget (replace with GestureHandler) 2024-09-25 21:19:41 +08:00
Nick Fisher
9b99975017 chore(release): publish packages
- thermion_flutter@0.2.0-dev.2.0
2024-09-25 21:18:00 +08:00
Nick Fisher
e6a6862ba4 chore: (flutter) web packaging fixes (use logger) 2024-09-25 21:17:38 +08:00
Nick Fisher
b7d3e9191a chore: (flutter) web packaging fixes 2024-09-25 21:17:20 +08:00
Nick Fisher
b461b2c5db chore: (flutter) web packaging fixes 2024-09-25 21:17:09 +08:00
Nick Fisher
8f7f48bcaa chore: (flutter) add package:web to flutter deps 2024-09-25 21:16:48 +08:00
Nick Fisher
7dc8e394f7 chore!: remove EntityListWidget - will replace with new Scene 2024-09-25 21:16:24 +08:00
Nick Fisher
f5c8faa5c3 chore(release): publish packages
- thermion_dart@0.2.0-dev.1.0
 - thermion_flutter@0.2.0-dev.1.0
 - thermion_flutter_ffi@0.2.0-dev.1.0
 - thermion_flutter_platform_interface@0.2.0-dev.1.0
 - thermion_flutter_web@0.1.0-dev.1.0
2024-09-25 21:06:28 +08:00
Nick Fisher
c84b4010d1 Merge pull request #68 from nmfisher/improvements
Improvements
2024-09-25 21:02:53 +08:00
Nick Fisher
b720c1294f rename Android plugin class in pubspec.yaml 2024-09-25 19:27:38 +08:00
Nick Fisher
d442ab5ce6 add namespace for build.gradle 2024-09-25 19:27:19 +08:00
Nick Fisher
61fdf300f4 add namespace for AndroidManifest.xml 2024-09-25 19:27:04 +08:00
Nick Fisher
4dabca9160 rename Android package/paths 2024-09-25 19:26:51 +08:00
Nick Fisher
85d6946645 (flutter) provide nicer implementation of FixedOrbitCameraRotationDelegate 2024-09-25 19:26:19 +08:00
Nick Fisher
4b1d8ce729 (flutter) update FreeFlight camera delegate 2024-09-24 14:24:29 +08:00
Nick Fisher
c52b0084ce add PickDelegate to DelegateGestureHandler 2024-09-24 14:21:13 +08:00
Nick Fisher
8db725d8bd add PICK_ENTITY to GestureAction 2024-09-24 14:20:49 +08:00
Nick Fisher
c67d1cbbc4 add background color tests 2024-09-24 14:20:33 +08:00
Nick Fisher
3ea4062e33 normalize direction in addLight 2024-09-24 14:20:14 +08:00
Nick Fisher
65e99b9212 set clearOptions to true 2024-09-23 13:51:18 +08:00
Nick Fisher
e83193ba0d introduce stronger native typing, camera projection/culling methods, update tests 2024-09-21 11:36:41 +08:00
Nick Fisher
835338ef63 (flutter) use scheduleFrameCallback to invoke requestFrame to match Flutter/vsync 2024-09-21 10:22:49 +08:00
Nick Fisher
57872d2e40 rename from *FFI to *RenderThread, return bool from render() to check frame timings, update render loop to wait on condition variable, add requestFrame() method 2024-09-21 10:21:46 +08:00
Nick Fisher
b5a7996769 add Dart Camera type 2024-09-21 10:18:36 +08:00
Nick Fisher
ddbb4ec5c6 rename CameraPtr to TCamera and use named arguments for setCameraLensProjection 2024-09-20 18:31:20 +08:00
Nick Fisher
7cb3080596 move MaterialInstance methods to own class 2024-09-20 18:18:08 +08:00
Nick Fisher
f6c91294aa rename setLayerEnabled to setLayerVisibility, add setVisibilityLayer method 2024-09-20 17:34:17 +08:00
Nick Fisher
412d333525 add PickDelegate 2024-09-20 14:54:18 +08:00
Nick Fisher
378dede02d add ThermionPickResult typedef 2024-09-20 14:53:48 +08:00
Nick Fisher
0e3db2635f update bindings 2024-09-20 14:53:34 +08:00
Nick Fisher
77147cbafd add setMaterialDepthWrite method 2024-09-20 14:53:16 +08:00
Nick Fisher
43e5fd7766 add setMaterialDepthWrite method 2024-09-20 14:53:06 +08:00
Nick Fisher
fc3ca3d6b3 update tests 2024-09-20 14:08:47 +08:00
Nick Fisher
51f52bb71b update stub 2024-09-20 14:08:26 +08:00
Nick Fisher
033c3f632d add createUnlitMaterialInstance, setMaterialPropertyInt methods to viewer interface 2024-09-20 14:08:16 +08:00
Nick Fisher
6d862ef36a update bindings 2024-09-20 14:07:37 +08:00
Nick Fisher
f816274fb9 replace some async methods with futures, fix setMaterialProperty4, update unproject, setMaterialPropertyInt, createUnlitMaterialInstance 2024-09-20 14:07:09 +08:00
Nick Fisher
0816286696 export geometry from thermion_dart 2024-09-20 14:05:14 +08:00
Nick Fisher
b421df5e2f remove image.mat from LFS 2024-09-20 14:04:59 +08:00
Nick Fisher
f6a136643d remove image.mat from LFS 2024-09-20 14:04:43 +08:00
Nick Fisher
b5e278183a update built unlit material 2024-09-20 14:04:27 +08:00
Nick Fisher
8e85042e37 remove old default paramsf rom UnlitMaterialProvider 2024-09-20 14:04:11 +08:00
Nick Fisher
f943756624 add setMaterialProperty for int, add create_unlit_material_instance, use double4 instead of float4 2024-09-20 14:03:50 +08:00
Nick Fisher
92814aed56 add setMaterialProperty for int, add create_unlit_material_instance 2024-09-20 14:03:29 +08:00
Nick Fisher
e8a1b976e1 set default layer for loadGlb to 0,. add setMaterialProperty for int, add SceneManager::createUnlitMaterialInstance 2024-09-20 14:02:30 +08:00
Nick Fisher
ef48dbce30 add base color/texture to unlit 2024-09-20 14:01:59 +08:00
Nick Fisher
196cc6b980 set default layer for loadGlb to 0,. add setMaterialProperty for int 2024-09-20 14:01:41 +08:00
Nick Fisher
1788c74d4c change float4 to double4 2024-09-20 14:01:02 +08:00
Nick Fisher
74e808d1dc update tests 2024-09-19 21:10:42 +08:00
Nick Fisher
72dacc5b21 update unproject texture to accept input texture 2024-09-19 21:10:28 +08:00
Nick Fisher
ebdaf65b89 update unproject texture to accept input texture 2024-09-19 21:10:22 +08:00
Nick Fisher
fa43149c98 update unproject texture to accept input texture 2024-09-19 21:10:10 +08:00
Nick Fisher
e8ae7193ee update unproject texture to accept input texture 2024-09-19 21:09:23 +08:00
Nick Fisher
aa21c0fb76 don't call clearBackgroundImage when setBackgroundImage is called (would deadlock) 2024-09-19 21:08:55 +08:00
Nick Fisher
10826466a4 set default material for geometry to baseColorIndex 0 2024-09-19 17:08:06 +08:00
Nick Fisher
3b2d7d8c47 depth pre-pass when projecting texture 2024-09-19 16:59:01 +08:00
Nick Fisher
212443cb8e add spot constructor 2024-09-19 13:44:17 +08:00
Nick Fisher
523141d54b add test cube texture image 2024-09-19 13:02:51 +08:00
Nick Fisher
d8a0859f16 update test 2024-09-19 13:00:40 +08:00
Nick Fisher
c70cc9abb5 add image methods to test helper 2024-09-19 12:53:44 +08:00
Nick Fisher
66b626e605 update showcase 2024-09-19 12:53:30 +08:00
Nick Fisher
4b28119318 test updates 2024-09-19 12:52:27 +08:00
Nick Fisher
666506aed0 widget updates 2024-09-19 12:52:16 +08:00
Nick Fisher
a55f63a428 increase min Dart SDK to 3.5 for .address FFI accessors 2024-09-19 12:51:30 +08:00
Nick Fisher
fb441e151c update ffigen with new headers 2024-09-19 12:51:09 +08:00
Nick Fisher
82d85386c0 fixes for createMaterialInstance 2024-09-19 12:50:57 +08:00
Nick Fisher
242b2f6faa gesture detector fixes 2024-09-19 09:19:23 +08:00
Nick Fisher
31d31dd583 ThermionViewer: add create/destroy materialinstance, add MaterialInstance property to createGeometry, priority/layer to loadGlbFromBuffer 2024-09-19 09:17:45 +08:00
Nick Fisher
f6077012b1 widget cleanup 2024-09-19 09:16:45 +08:00
Nick Fisher
18bb45dcd8 use TMaterialInstance for FFI API, add priority/layer params to load_glb_from_buffer 2024-09-19 09:16:06 +08:00
Nick Fisher
10b919e4f4 add priority/layer to FFI viewer, use struct for model matrix, add createUbershaderMaterialInstance and destroyMaterialInstance 2024-09-19 09:15:31 +08:00
Nick Fisher
98cedf821c update bindings 2024-09-19 09:14:10 +08:00
Nick Fisher
729f72e768 use SceneManager::LAYERS enum for Overlay, set priority/layer in loadGlbFromBuffer, add create/destroy material instance 2024-09-19 09:12:57 +08:00
Nick Fisher
d01861e949 use SceneManager::LAYERS enum for Overlay 2024-09-19 09:12:16 +08:00
Nick Fisher
adec48f253 use SceneManager::LAYERS enum for gizmo 2024-09-19 09:12:02 +08:00
Nick Fisher
27a8ce18d5 add priority/layer to load_glb_from_buffer 2024-09-19 09:11:40 +08:00
Nick Fisher
4b740a9f5a native types, add create/destroy material instance, add SceneManager::LAYERS enum 2024-09-19 09:11:20 +08:00
Nick Fisher
0b34b4546e refactor: native types 2024-09-19 09:10:24 +08:00
Nick Fisher
c17919cd97 refactor: native types 2024-09-19 09:09:42 +08:00
Nick Fisher
462f1f02bf refactor: move native types to own header, add methods for create/destroy material instance, add priority/layer to load_glb_from_buffer 2024-09-19 09:09:16 +08:00
Nick Fisher
ddc433a126 refactor: Dart types 2024-09-19 09:07:35 +08:00
Nick Fisher
ab649e860d update gitattr 2024-09-16 20:51:27 +08:00
Nick Fisher
676ddc3773 add texture methods (including unproject) 2024-09-16 20:51:14 +08:00
Nick Fisher
7d2cf3f91b don't store gizmo material in LFS 2024-09-16 11:25:21 +08:00
Nick Fisher
492d41d756 test update 2024-09-16 11:24:24 +08:00
Nick Fisher
191c2fd709 restructure viewer/types/helper folders, remove old WASM/web FFI interop, add SceneUpdated stream 2024-09-16 11:08:27 +08:00
Nick Fisher
027fc9ae04 update tests 2024-09-16 11:08:19 +08:00
Nick Fisher
b73d0e1e96 restructure viewer/types/helper folders, remove old WASM/web FFI interop, add SceneUpdated stream 2024-09-16 11:07:54 +08:00
Nick Fisher
315f2b63b9 gesture handler & delegate improvements 2024-09-13 18:32:02 +08:00
Nick Fisher
ad205679cb allow setting material property by name 2024-09-13 15:27:46 +08:00
Nick Fisher
6ef8d19e94 (flutter) export delegate gesture handler 2024-09-13 15:27:20 +08:00
Nick Fisher
6b0f25ca59 remove using namespace filament* 2024-09-13 15:27:01 +08:00
Nick Fisher
820d341f67 update bindings 2024-09-13 15:26:44 +08:00
Nick Fisher
70f904d54c allow setting material property by name 2024-09-13 15:20:45 +08:00
Nick Fisher
aee607908d remove using namespace filament* 2024-09-13 15:18:01 +08:00
Nick Fisher
c99c57e24d geometry receives/casts shadows by default 2024-09-13 14:52:20 +08:00
Nick Fisher
00d75be479 gesture handler improvements 2024-09-13 14:25:10 +08:00
Nick Fisher
3a9bd31919 remove superseded desktop/mobile gesture detector widget 2024-09-13 13:47:26 +08:00
Nick Fisher
51be1bce39 add pan camera implementation and fix velocity timer 2024-09-13 13:46:41 +08:00
Nick Fisher
822b8e14c1 add delegate-based implementations for gesture handlers 2024-09-13 13:30:00 +08:00
Nick Fisher
2ab30a7933 remove double sided from HighlightOverlay material 2024-09-13 11:09:09 +08:00
Nick Fisher
1be8a5e862 adjust gizmo size 2024-09-13 11:08:49 +08:00
Nick Fisher
98d61fa1b3 fix setCameraModelMatrix4 2024-09-13 10:36:24 +08:00
Nick Fisher
5b3d16a316 update bindings 2024-09-13 10:36:07 +08:00
Nick Fisher
b6863828b4 initialize viewportDimensions to (0,0) in ThermionViewer 2024-09-13 10:35:57 +08:00
Nick Fisher
98fefd0e52 fix winding order in GeometryHelper 2024-09-13 10:35:33 +08:00
Nick Fisher
d476d78e2b reduce size of gizmo 2024-09-13 10:35:13 +08:00
Nick Fisher
98113fb79f remove logging 2024-09-13 10:34:59 +08:00
Nick Fisher
90827ff012 culling fixes for HighlightOverlay 2024-09-13 10:34:47 +08:00
Nick Fisher
44078ba2e0 add v2 gesture handlers 2024-09-13 10:34:23 +08:00
Nick Fisher
866219ee2e add v2 gesture handlers 2024-09-13 10:34:12 +08:00
Nick Fisher
d785bd6b7e chore!: rename controller to viewer in gesture detector widgets 2024-09-12 08:54:07 +08:00
Nick Fisher
3e4e6653a8 update material building in Makefile 2024-09-11 23:09:56 +08:00
Nick Fisher
8d250e2664 update test 2024-09-11 23:09:41 +08:00
Nick Fisher
d455cea29e use opaque CameraPtr to set camera matrices/properties/etc 2024-09-11 23:09:27 +08:00
Nick Fisher
374e8eb910 rename getBoundingBox to getViewportBoundingBox and add camera methods 2024-09-11 23:08:07 +08:00
Nick Fisher
833fc74b4c rename getBoundingBox to getViewportBoundingBox 2024-09-11 23:07:47 +08:00
Nick Fisher
62417bfebd explicitly mark type of Camera for Gizmo 2024-09-11 23:07:29 +08:00
Nick Fisher
abc9ecbeee use opaque CameraPtr to set camera matrices/properties/etc 2024-09-11 23:07:03 +08:00
Nick Fisher
a1f71ab459 remove camera methods from FilamentViewer 2024-09-11 23:06:32 +08:00
Nick Fisher
d123929fb4 remove camera methods from FilamentViewer 2024-09-11 23:06:06 +08:00
Nick Fisher
141827c59c use opaque CameraPtr to set camera matrices/properties/etc 2024-09-11 23:05:40 +08:00
Nick Fisher
9fbcc9edaf mark all ffigen functions as leaf 2024-09-11 23:04:57 +08:00
Nick Fisher
83d9c5be30 remove update_viewport_and_camera_projection_ffi 2024-09-11 23:04:36 +08:00
Nick Fisher
6aadbbc3d0 remove update_viewport_and_camera_projection_ffi 2024-09-11 23:04:27 +08:00
Nick Fisher
d5bc865cf4 add matrix helper 2024-09-11 23:04:02 +08:00
Nick Fisher
d766733489 update stubbed ThermionViewer methods 2024-09-11 23:03:53 +08:00
Nick Fisher
f51c640d17 remove unused get_camera_position method 2024-09-11 20:01:21 +08:00
Nick Fisher
66f10b598a update stub ThermionViewer 2024-09-11 19:59:43 +08:00
Nick Fisher
59957650aa update bindings 2024-09-11 19:59:33 +08:00
Nick Fisher
d43fbd8964 add loadGlbFromBuffer implementation to ThermionViewerFFI 2024-09-11 19:59:25 +08:00
Nick Fisher
f5de4349bf add createGeometryWithNormals to SceneManager 2024-09-11 18:09:42 +08:00
Nick Fisher
f67e1a021d add createGeometryWithNormals to SceneManager 2024-09-11 18:09:14 +08:00
Nick Fisher
33f2c5fbf7 add normals to CustomGeometry implementation 2024-09-11 18:08:48 +08:00
Nick Fisher
6a7bde930d add normals to CustomGeometry interface 2024-09-11 18:08:35 +08:00
Nick Fisher
b827a2142b HighlightOverlay fixes 2024-09-11 18:08:13 +08:00
Nick Fisher
8bd2416bad don't set stencil highlight in Gizmo 2024-09-11 18:07:38 +08:00
Nick Fisher
3684eb248c add create_geometry_with_normals method 2024-09-11 18:07:17 +08:00
Nick Fisher
9077632d1b add create_geometry_with_normals method 2024-09-11 18:07:07 +08:00
Nick Fisher
d40261ae29 add create_geometry_with_normals_ffi method, switch load_glb_from_buffer type to uint8_t for Dart leaf compat and add keepData param 2024-09-11 18:06:50 +08:00
Nick Fisher
77dbb574c7 add create_geometry_with_normals_ffi method, switch load_glb_from_buffer type to uint8_t for Dart leaf compat and add keepData param 2024-09-11 18:06:31 +08:00
Nick Fisher
89a660144e update test helpers 2024-09-11 18:05:36 +08:00
Nick Fisher
7554af5d41 (flutter) add experimental GestureHandler widget and decouple from ThermionGestureDetectorDesktop 2024-09-11 18:05:24 +08:00
Nick Fisher
4b742fea2d (flutter) add experimental GestureHandler widget and decouple from ThermionGestureDetectorDesktop 2024-09-11 18:05:18 +08:00
Nick Fisher
a6c6cff8b6 add loadGlbFromBuffer method and normals param to ThermionViewer 2024-09-11 18:04:36 +08:00
Nick Fisher
5813753ef9 (flutter) set enablePicking to false by default in ThermionGestureDetector 2024-09-11 18:03:52 +08:00
Nick Fisher
cf0dad2631 (flutter) add const FlutterWebOptions 2024-09-11 18:03:29 +08:00
Nick Fisher
cfddb99a8b (flutter) add CameraOrientationWidget 2024-09-11 18:01:45 +08:00
Nick Fisher
845d5bf223 (flutter) add const constructor for flutter options 2024-09-11 18:01:33 +08:00
Nick Fisher
dbbd972909 add GeometryHelper 2024-09-11 17:54:21 +08:00
Nick Fisher
aba3ba24af (flutter) add CameraOrientationWidget 2024-09-11 17:54:08 +08:00
Nick Fisher
81c27dde9f feat: parent the cloned entity instance when setting stencil highlight 2024-09-08 13:55:25 +08:00
Nick Fisher
476b552fd0 feat: add getAncestor method 2024-09-08 13:52:33 +08:00
Nick Fisher
5c4d5d4b9d feat: add getAncestor method 2024-09-08 13:52:04 +08:00
Nick Fisher
ae5ef2c286 update bindings 2024-09-07 18:01:39 +08:00
Nick Fisher
b0f3c8a087 feat: set stencil highlight on gizmo attach 2024-09-07 18:01:30 +08:00
Nick Fisher
43fc7ffc65 update tests 2024-09-07 18:01:03 +08:00
Nick Fisher
ee24fca20e feat: move createGeometry to SceneManager, add queueRelativePositionUpdateFromViewportVector and removeStencilHighlight 2024-09-07 18:00:50 +08:00
Nick Fisher
a00fdbe042 chore: remove createGeometry method from FilamentViewer, set default view blend mode 2024-09-07 17:59:55 +08:00
Nick Fisher
c2eb28a8f5 fix: properly destroy entities/material/etc in Gizmo on destruction, remove custom scene creation logic 2024-09-07 17:59:03 +08:00
Nick Fisher
4c6c20f3de feat: move HighlightOverlay to nested class, move createGeometry to SceneManager, add queueRelativePositionUpdateFromViewportVector 2024-09-07 17:57:38 +08:00
Nick Fisher
b2ae8135c6 feat: add removeStencilHighlight, accept color param for setStencilHighlight, queuePositionUpdateFromViewportCoords to ThermionDartApi 2024-09-07 17:55:40 +08:00
Nick Fisher
aecde97200 feat: add removeStencilHighlight, queuePositionUpdateFromViewportCoords to ThermionViewer 2024-09-07 17:54:42 +08:00
Nick Fisher
331caccc22 chore: delete old unlit_opaque material 2024-09-07 17:54:12 +08:00
Nick Fisher
63e2b74bb1 chore: rebuild unlit material 2024-09-07 17:53:55 +08:00
Nick Fisher
433f03161c feat: add scale parameter to unlit material 2024-09-07 17:53:41 +08:00
Nick Fisher
6ec84b6249 fix: add Fence to capture() and set stencil buffer by default 2024-09-06 13:03:51 +08:00
Nick Fisher
29b6a48816 test: add test cube.glb 2024-09-06 12:44:54 +08:00
Nick Fisher
00755fd417 chore: remove old materials Makefile 2024-09-06 12:44:40 +08:00
Nick Fisher
9c5156e41a feat: add flag for keepData for gltf instancing, add highlightScene, add stencilHighlight method 2024-09-06 12:36:16 +08:00
Nick Fisher
226c45ee2e chore: minor widget fixes 2024-09-05 22:22:25 +08:00
Nick Fisher
e04390b2fe feat: add grid material 2024-09-05 22:21:19 +08:00
Nick Fisher
9a87eb4d9b feat: allow passing null options to ThermionWidget 2024-09-05 22:21:09 +08:00
Nick Fisher
2284d9d081 feat: grid uses own material 2024-09-05 22:20:27 +08:00
Nick Fisher
cf61369a8d chore: clean up old commented code 2024-09-05 22:20:11 +08:00
Nick Fisher
4e14bd2396 fix: ignore pick results directly on axis 2024-09-05 22:19:54 +08:00
Nick Fisher
0798d5c071 feat: add grid material 2024-09-05 22:19:27 +08:00
Nick Fisher
80d5b1d23f feat: expose setLightDirection and setLightPosition 2024-09-05 22:18:54 +08:00
Nick Fisher
0bd87288d1 chore: update binding 2024-09-05 17:53:24 +08:00
Nick Fisher
51e06c2eb9 chore: (js) stub missing methods 2024-09-05 17:52:53 +08:00
Nick Fisher
b29663923d chore: (wasm) add missing interop methods 2024-09-05 17:52:40 +08:00
Nick Fisher
f57a323cda feat: (flutter) (web) if importCanvasAsWidget is false, render transparency 2024-09-05 17:52:19 +08:00
Nick Fisher
03ffe85113 chore: stub ThermionWidget methods 2024-09-05 17:51:36 +08:00
Nick Fisher
4edc8aa85b feat: (flutter) move DPR calculation to resizeTexture and add createViewerWithOptions method to ThermionFlutterFFI 2024-09-05 17:51:22 +08:00
Nick Fisher
04ecb4d56f fix: (flutter) pass ThermionFlutterOptions to ThermionWidget, use dpr for resizeTexture, delete unnecessary TransparencyPainter class 2024-09-05 17:49:02 +08:00
Nick Fisher
0ac0a92024 feat: add createViewerWithOptions to ThermionFlutterPlugin and mark createViewer as deprecated 2024-09-05 17:47:54 +08:00
Nick Fisher
ae1e14ddb7 feat: add createViewerWithOptions to ThermionFlutterPlugin and mark createViewer as deprecated 2024-09-05 17:47:41 +08:00
Nick Fisher
aa246ab63a feat: (flutter) (web) use options to determine whether to create canvas, and set fixed position + offset 2024-09-05 17:45:49 +08:00
Nick Fisher
d4350d7d99 test: update viewport gizmo test 2024-09-05 17:44:18 +08:00
Nick Fisher
f07fe6084a chore: (flutter) export platform interface from thermion_flutter package 2024-09-05 17:43:58 +08:00
Nick Fisher
4e29055a20 chore: stub new methods 2024-09-05 17:43:35 +08:00
Nick Fisher
6d0c06a853 doc: pixelRatio 2024-09-05 17:43:19 +08:00
Nick Fisher
abe6e1fcb8 feat: add ThermionFlutterOptions classes, rename interface parameter for offsetTop and ensure pixelRatio is passed to resizeTexture 2024-09-05 17:43:04 +08:00
Nick Fisher
683105c4f7 fix: emscripten export visibility for add_light 2024-09-05 17:42:17 +08:00
Nick Fisher
6f2331582c chore: use float instead of float32_t in FilamentViewer 2024-09-05 17:41:55 +08:00
Nick Fisher
f0f97e310c chore: add nested PickCallbackHandler to Gizmo 2024-09-05 17:41:24 +08:00
Nick Fisher
2331f2c31a chore: update Makefile to add missing headers to resgen files 2024-08-27 21:51:29 +08:00
Nick Fisher
704b7f6734 fix: (flutter/web) use window.devicePixelRatio for viewport 2024-08-27 21:51:09 +08:00
Nick Fisher
7ac7ae43ab fix: (wasm) use correct coords for pick, free memory correctly, keep pixelratio copy 2024-08-27 21:50:07 +08:00
Nick Fisher
d52b23d6b5 fix: (flutter) desktop gesture detector changes for new Gizmo methods 2024-08-27 21:49:28 +08:00
Nick Fisher
8b17916cd9 chore: Dart Gizmo class cleanup 2024-08-27 21:49:04 +08:00
Nick Fisher
4a0f4e3ac8 feat: add setGizmoVisibility/pickGizmo methods to ThermionViewer 2024-08-27 21:48:27 +08:00
Nick Fisher
85116f43a2 feat: remove gizmo view references, exclude gizmo entities from picking, add createIbl 2024-08-27 21:47:56 +08:00
Nick Fisher
3ecb8920ea feat: createIbl 2024-08-27 21:47:02 +08:00
Nick Fisher
8923d97129 chore: init SceneManager pointers to nullptr 2024-08-27 21:46:47 +08:00
Nick Fisher
375e1cc887 fix: add more nan checks for gizmo manipulation 2024-08-27 21:46:21 +08:00
Nick Fisher
78dcbc8bb9 feat: expose API methods for create_ibl, pick/set gizmo visibility 2024-08-27 21:45:49 +08:00
Nick Fisher
a3f7b98bf7 feat: create transparent overlay for gizmo for easier picking 2024-08-27 21:45:19 +08:00
Nick Fisher
12b61e8767 feat: rescale gizmo based on distance from camera 2024-08-27 16:54:40 +08:00
Nick Fisher
0e3b014c2c feat: rescale gizmo based on distance from camera 2024-08-27 16:50:54 +08:00
Nick Fisher
10db8c39f3 fix: add check for nan NDC coordinates for viewport translation 2024-08-25 21:54:08 +08:00
Nick Fisher
371bcb3706 fix stub methods 2024-08-24 16:28:12 +08:00
Nick Fisher
714c575409 add implementations to JS bridge/shim classes 2024-08-24 16:26:05 +08:00
Nick Fisher
c0941e3b7f add missing methods to wasm viewer 2024-08-24 16:25:37 +08:00
Nick Fisher
026acb7467 feat: highlight gizmo on hover 2024-08-24 16:25:17 +08:00
Nick Fisher
3c05cc6a43 add implementations to FFI viewer 2024-08-24 16:24:59 +08:00
Nick Fisher
f1a2926bdf fix!: (flutter) pass pixelRatio to createTexture 2024-08-24 16:24:06 +08:00
Nick Fisher
88e8a138ac (flutter) (web) initialize viewer to 1x1 then resize on createTexture 2024-08-24 16:23:54 +08:00
Nick Fisher
53d0301828 feat!: (flutter) (web) upgrade package:web dep to 1.0.0 2024-08-24 16:23:00 +08:00
Nick Fisher
b10fec1963 fix!: (flutter) pass pixelRatio to createTexture 2024-08-24 16:22:31 +08:00
Nick Fisher
08e1eb7778 feat: expose setLayerEnabled, viewportDimensions and getCameraFov on ThermionView 2024-08-24 16:22:12 +08:00
Nick Fisher
d6713c090c stub out new methods 2024-08-24 16:21:35 +08:00
Nick Fisher
497ecbf881 fix!: (flutter) pass pixelRatio to createTexture 2024-08-24 16:20:06 +08:00
Nick Fisher
6f7d03737e fix!: (flutter) pass pixelRatio to createTexture 2024-08-24 16:19:50 +08:00
Nick Fisher
ad60c6bbe1 chore: update bindings 2024-08-24 16:19:07 +08:00
Nick Fisher
92fdda722b chore: add viewport test 2024-08-24 16:18:53 +08:00
Nick Fisher
3cc876f972 feat: layers, grid 2024-08-24 16:18:23 +08:00
Nick Fisher
7d1e706045 feat: layers, grid 2024-08-24 16:18:12 +08:00
Nick Fisher
11756fcedd feat: ignore grid overlay and gizmo center when picking, implement highlighting 2024-08-24 16:17:34 +08:00
Nick Fisher
2e1f2cd56d feat: SceneManager updates (setLayer, add grid, queueRelativePositionUpdateWorld 2024-08-24 16:02:54 +08:00
Nick Fisher
c08611b2c3 feat: expose set_layer_enabled, get_camera_fov and queue_relative_position_updateg_world_axis to ThermionDartApi.h 2024-08-24 16:02:06 +08:00
Nick Fisher
f87f89427d feat: add getCameraFov to FilamentViewer 2024-08-24 16:01:03 +08:00
Nick Fisher
c3319ebbf3 feat: add new grid overlay files to web CmakeLists 2024-08-24 16:00:15 +08:00
Nick Fisher
f7b765c5d0 chore: rebuild materials 2024-08-24 15:59:55 +08:00
Nick Fisher
9512b74008 fix: update material output path in Makefile 2024-08-24 15:59:36 +08:00
Nick Fisher
5a3517f953 add grid overlay 2024-08-24 15:59:04 +08:00
Nick Fisher
731c4981c9 use inactive/active color gizmo 2024-08-24 15:58:49 +08:00
Nick Fisher
8f20a8a859 chore: don't use shader to overlay gizmo 2024-08-24 15:21:36 +08:00
Nick Fisher
7693a0fe14 feat: re-implement (native) Gizmo class, expose preserveScaling parameter for setParent, add methods for getting viewport bounding box from renderable entity 2024-08-22 18:04:06 +08:00
Nick Fisher
98c3676fdf fix: (web) add emscripten guards for flushAndWait call when swapchain destroyed 2024-08-21 17:18:37 +08:00
Nick Fisher
d7664a9746 feat!: (web) (flutter) create canvas when createViewer is called (no longer need to manually add canvas element to web HTML) 2024-08-21 17:17:58 +08:00
Nick Fisher
d868fd6970 feat: add capture() function and expose viewportDimensions on ThermionViewer (allows easier saving of captured images to PNG) 2024-08-21 14:33:48 +08:00
Nick Fisher
300396b97e feat: (web) allow table growth in emscripten module for passing C-style callback function pointers 2024-08-21 14:30:17 +08:00
Nick Fisher
1d8faa70a1 feat: (web) add capture() method and missing camera navigation controls 2024-08-21 14:29:40 +08:00
Nick Fisher
1051b1bb0e feat!: resize canvas on web 2024-08-17 11:43:43 +08:00
Nick Fisher
c367824041 feat!: update web/http dependencies 2024-08-17 11:43:03 +08:00
Nick Fisher
81cedf0d43 feat: download WASM module directly on web (no need to embed in index.html any more) and expose updateViewportAndCameraProjection 2024-08-17 11:42:25 +08:00
Nick Fisher
16c6203b97 feat: add startOffset parameter to gltf playAnimation 2024-07-29 16:13:45 +08:00
246 changed files with 55844 additions and 151263 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/ThermionDartTexture.h 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/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,10 +153,7 @@ 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,7 +3,7 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## 2024-07-23
## 2024-10-14
### Changes
@@ -15,23 +15,481 @@ Packages with breaking changes:
Packages with other changes:
- [`thermion_dart` - `v0.1.3`](#thermion_dart---v013)
- [`thermion_flutter_ffi` - `v0.1.0+12`](#thermion_flutter_ffi---v01012)
- [`thermion_flutter_web` - `v0.0.3`](#thermion_flutter_web---v003)
- [`thermion_flutter_platform_interface` - `v0.1.0+11`](#thermion_flutter_platform_interface---v01011)
- [`thermion_flutter` - `v0.1.1+13`](#thermion_flutter---v01113)
- [`thermion_dart` - `v0.2.1-dev.0.0.8`](#thermion_dart---v021-dev008)
- [`thermion_flutter_web` - `v0.1.0+9`](#thermion_flutter_web---v0109)
- [`thermion_flutter` - `v0.2.1-dev.7`](#thermion_flutter---v021-dev7)
- [`thermion_flutter_platform_interface` - `v0.2.1-dev.7`](#thermion_flutter_platform_interface---v021-dev7)
- [`thermion_flutter_ffi` - `v0.2.1-dev.7`](#thermion_flutter_ffi---v021-dev7)
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_platform_interface` - `v0.1.0+11`
- `thermion_flutter` - `v0.1.1+13`
- `thermion_flutter_web` - `v0.1.0+9`
- `thermion_flutter` - `v0.2.1-dev.7`
- `thermion_flutter_platform_interface` - `v0.2.1-dev.7`
- `thermion_flutter_ffi` - `v0.2.1-dev.7`
---
#### `thermion_dart` - `v0.1.3`
#### `thermion_dart` - `v0.2.1-dev.0.0.8`
- **FIX**: move ThermionWin32.h to include.
## 2024-10-14
### 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.7`](#thermion_dart---v021-dev007)
- [`thermion_flutter_web` - `v0.1.0+8`](#thermion_flutter_web---v0108)
- [`thermion_flutter` - `v0.2.1-dev.6`](#thermion_flutter---v021-dev6)
- [`thermion_flutter_platform_interface` - `v0.2.1-dev.6`](#thermion_flutter_platform_interface---v021-dev6)
- [`thermion_flutter_ffi` - `v0.2.1-dev.6`](#thermion_flutter_ffi---v021-dev6)
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+8`
- `thermion_flutter` - `v0.2.1-dev.6`
- `thermion_flutter_platform_interface` - `v0.2.1-dev.6`
- `thermion_flutter_ffi` - `v0.2.1-dev.6`
---
#### `thermion_dart` - `v0.2.1-dev.0.0.7`
- Bump "thermion_dart" to `0.2.1-dev.0.0.7`.
## 2024-10-10
### 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.6`](#thermion_dart---v021-dev006)
- [`thermion_flutter_web` - `v0.1.0+7`](#thermion_flutter_web---v0107)
- [`thermion_flutter_platform_interface` - `v0.2.1-dev.5`](#thermion_flutter_platform_interface---v021-dev5)
- [`thermion_flutter` - `v0.2.1-dev.5`](#thermion_flutter---v021-dev5)
- [`thermion_flutter_ffi` - `v0.2.1-dev.5`](#thermion_flutter_ffi---v021-dev5)
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+7`
- `thermion_flutter_platform_interface` - `v0.2.1-dev.5`
- `thermion_flutter` - `v0.2.1-dev.5`
- `thermion_flutter_ffi` - `v0.2.1-dev.5`
---
#### `thermion_dart` - `v0.2.1-dev.0.0.6`
- Bump "thermion_dart" to `0.2.1-dev.0.0.6`.
## 2024-10-10
### 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.5`](#thermion_dart---v021-dev005)
- [`thermion_flutter_platform_interface` - `v0.2.1-dev.4`](#thermion_flutter_platform_interface---v021-dev4)
- [`thermion_flutter_web` - `v0.1.0+6`](#thermion_flutter_web---v0106)
- [`thermion_flutter` - `v0.2.1-dev.4`](#thermion_flutter---v021-dev4)
- [`thermion_flutter_ffi` - `v0.2.1-dev.4`](#thermion_flutter_ffi---v021-dev4)
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_platform_interface` - `v0.2.1-dev.4`
- `thermion_flutter_web` - `v0.1.0+6`
- `thermion_flutter` - `v0.2.1-dev.4`
- `thermion_flutter_ffi` - `v0.2.1-dev.4`
---
#### `thermion_dart` - `v0.2.1-dev.0.0.5`
- Bump "thermion_dart" to `0.2.1-dev.0.0.5`.
## 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.4`](#thermion_dart---v021-dev004)
- [`thermion_flutter_web` - `v0.1.0+5`](#thermion_flutter_web---v0105)
- [`thermion_flutter` - `v0.2.1-dev.3`](#thermion_flutter---v021-dev3)
- [`thermion_flutter_platform_interface` - `v0.2.1-dev.3`](#thermion_flutter_platform_interface---v021-dev3)
- [`thermion_flutter_ffi` - `v0.2.1-dev.3`](#thermion_flutter_ffi---v021-dev3)
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+5`
- `thermion_flutter` - `v0.2.1-dev.3`
- `thermion_flutter_platform_interface` - `v0.2.1-dev.3`
- `thermion_flutter_ffi` - `v0.2.1-dev.3`
---
#### `thermion_dart` - `v0.2.1-dev.0.0.4`
## 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.

View File

@@ -12,6 +12,7 @@ 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
@@ -22,10 +23,13 @@ bindings:
#
materials: FORCE
@echo "Using Filament build from ${FILAMENT_PATH}"
${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
@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
#rm materials/*.filamat
FORCE: ;

View File

@@ -1,5 +1,19 @@
## Android
### Min SDK version
Thermion requires Android SDK version 22, so change your `app/android/build.gradle` to match this version or higher
```groovy
defaultConfig {
...
minSdk = 22
...
}
```
### Shrink/Minify Resources
In release mode, you must add the following to your `app/build.gradle`:
```

View File

@@ -6,6 +6,13 @@ 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)).

View File

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

View File

@@ -1,3 +1,50 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3158461d081f058dcb9582ce19cc2daedc73abbe758ba5094c94df89028d8c4d
size 981
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;
}
}

53
materials/grid.mat Normal file
View File

@@ -0,0 +1,53 @@
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,3 +1,52 @@
version https://git-lfs.github.com/spec/v1
oid sha256:273059c97f96c6848807914d937583864445b51d8e0f1cd98c3e4e0e4bd9f411
size 1451
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);
}
}
}

42
materials/unlit.mat Normal file
View File

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

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

View File

@@ -1,3 +1,139 @@
## 0.2.1-dev.0.0.8
- **FIX**: move ThermionWin32.h to include.
## 0.2.1-dev.0.0.7
- Bump "thermion_dart" to `0.2.1-dev.0.0.7`.
## 0.2.1-dev.0.0.6
- Bump "thermion_dart" to `0.2.1-dev.0.0.6`.
## 0.2.1-dev.0.0.5
- Bump "thermion_dart" to `0.2.1-dev.0.0.5`.
## 0.2.1-dev.0.0.4
## 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.

View File

@@ -0,0 +1,30 @@
cmake_minimum_required(VERSION 3.14)
set(PROJECT_NAME "thermion_windows")
project(${PROJECT_NAME} LANGUAGES C CXX)
cmake_policy(VERSION 3.14...3.25)
add_compile_definitions(WGL_USE_BACKING_WINDOW)
add_compile_definitions(UNICODE)
add_library(${PROJECT_NAME} SHARED
"thermion_window.cpp"
)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20)
target_include_directories(${PROJECT_NAME} INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/include"
"${CMAKE_SOURCE_DIR}/../../../../thermion_dart/native/include/filament"
"${CMAKE_SOURCE_DIR}/../../../../thermion_dart/native/include"
${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_link_libraries(${PROJECT_NAME} PRIVATE
Shlwapi
opengl32
dwmapi
comctl32
)

View File

@@ -0,0 +1,244 @@
#include "thermion_window.h"
#include <cstdint>
#include <iostream>
#include <chrono>
#include <thread>
#include <Windows.h>
#include <dwmapi.h>
#include <ShObjIdl.h>
#pragma comment(lib, "dwmapi.lib")
#pragma comment(lib, "comctl32.lib")
namespace thermion {
static constexpr auto kClassName = L"THERMION_WINDOW";
static constexpr auto kWindowName = L"thermion_window";
static bool was_window_hidden_due_to_minimize_ = false;
static WPARAM last_wm_size_wparam_ = SIZE_RESTORED;
uint64_t last_thread_time_ = 0;
static constexpr auto kNativeViewPositionAndShowDelay = 300;
typedef enum _WINDOWCOMPOSITIONATTRIB {
WCA_UNDEFINED = 0,
WCA_NCRENDERING_ENABLED = 1,
WCA_NCRENDERING_POLICY = 2,
WCA_TRANSITIONS_FORCEDISABLED = 3,
WCA_ALLOW_NCPAINT = 4,
WCA_CAPTION_BUTTON_BOUNDS = 5,
WCA_NONCLIENT_RTL_LAYOUT = 6,
WCA_FORCE_ICONIC_REPRESENTATION = 7,
WCA_EXTENDED_FRAME_BOUNDS = 8,
WCA_HAS_ICONIC_BITMAP = 9,
WCA_THEME_ATTRIBUTES = 10,
WCA_NCRENDERING_EXILED = 11,
WCA_NCADORNMENTINFO = 12,
WCA_EXCLUDED_FROM_LIVEPREVIEW = 13,
WCA_VIDEO_OVERLAY_ACTIVE = 14,
WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15,
WCA_DISALLOW_PEEK = 16,
WCA_CLOAK = 17,
WCA_CLOAKED = 18,
WCA_ACCENT_POLICY = 19,
WCA_FREEZE_REPRESENTATION = 20,
WCA_EVER_UNCLOAKED = 21,
WCA_VISUAL_OWNER = 22,
WCA_HOLOGRAPHIC = 23,
WCA_EXCLUDED_FROM_DDA = 24,
WCA_PASSIVEUPDATEMODE = 25,
WCA_USEDARKMODECOLORS = 26,
WCA_LAST = 27
} WINDOWCOMPOSITIONATTRIB;
typedef struct _WINDOWCOMPOSITIONATTRIBDATA {
WINDOWCOMPOSITIONATTRIB Attrib;
PVOID pvData;
SIZE_T cbData;
} WINDOWCOMPOSITIONATTRIBDATA;
typedef enum _ACCENT_STATE {
ACCENT_DISABLED = 0,
ACCENT_ENABLE_GRADIENT = 1,
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
ACCENT_ENABLE_BLURBEHIND = 3,
ACCENT_ENABLE_ACRYLICBLURBEHIND = 4,
ACCENT_ENABLE_HOSTBACKDROP = 5,
ACCENT_INVALID_STATE = 6
} ACCENT_STATE;
typedef struct _ACCENT_POLICY {
ACCENT_STATE AccentState;
DWORD AccentFlags;
DWORD GradientColor;
DWORD AnimationId;
} ACCENT_POLICY;
typedef BOOL(WINAPI* _GetWindowCompositionAttribute)(
HWND, WINDOWCOMPOSITIONATTRIBDATA*);
typedef BOOL(WINAPI* _SetWindowCompositionAttribute)(
HWND, WINDOWCOMPOSITIONATTRIBDATA*);
static _SetWindowCompositionAttribute g_set_window_composition_attribute = NULL;
static bool g_set_window_composition_attribute_initialized = false;
typedef LONG NTSTATUS, *PNTSTATUS;
#define STATUS_SUCCESS (0x00000000)
typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
RTL_OSVERSIONINFOW GetWindowsVersion() {
HMODULE hmodule = ::GetModuleHandleW(L"ntdll.dll");
if (hmodule) {
RtlGetVersionPtr rtl_get_version_ptr =
(RtlGetVersionPtr)::GetProcAddress(hmodule, "RtlGetVersion");
if (rtl_get_version_ptr != nullptr) {
RTL_OSVERSIONINFOW rovi = {0};
rovi.dwOSVersionInfoSize = sizeof(rovi);
if (STATUS_SUCCESS == rtl_get_version_ptr(&rovi)) {
return rovi;
}
}
}
RTL_OSVERSIONINFOW rovi = {0};
return rovi;
}
void SetWindowComposition(HWND window, int32_t accent_state,
int32_t gradient_color) {
// TODO: Look for a better available API.
if (GetWindowsVersion().dwBuildNumber >= 18362) {
if (!g_set_window_composition_attribute_initialized) {
auto user32 = ::GetModuleHandleA("user32.dll");
if (user32) {
g_set_window_composition_attribute =
reinterpret_cast<_SetWindowCompositionAttribute>(
::GetProcAddress(user32, "SetWindowCompositionAttribute"));
if (g_set_window_composition_attribute) {
g_set_window_composition_attribute_initialized = true;
}
}
}
ACCENT_POLICY accent = {static_cast<ACCENT_STATE>(accent_state), 2,
static_cast<DWORD>(gradient_color), 0};
WINDOWCOMPOSITIONATTRIBDATA data;
data.Attrib = WCA_ACCENT_POLICY;
data.pvData = &accent;
data.cbData = sizeof(accent);
g_set_window_composition_attribute(window, &data);
}
}
LRESULT CALLBACK FilamentWindowProc(HWND const window, UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
switch (message) {
std::cout << message <<std::endl;
case WM_MOUSEMOVE: {
TRACKMOUSEEVENT event;
event.cbSize = sizeof(event);
event.hwndTrack = window;
event.dwFlags = TME_HOVER;
event.dwHoverTime = 200;
auto user_data = ::GetWindowLongPtr(window, GWLP_USERDATA);
if (user_data) {
HWND flutterRootWindow = reinterpret_cast<HWND>(user_data);
::SetForegroundWindow(flutterRootWindow);
LONG ex_style = ::GetWindowLong(flutterRootWindow, GWL_EXSTYLE);
ex_style &= ~(WS_EX_TRANSPARENT | WS_EX_LAYERED);
::SetWindowLong(flutterRootWindow, GWL_EXSTYLE, ex_style);
}
break;
}
case WM_ERASEBKGND: {
HDC hdc = (HDC)wparam;
RECT rect;
GetClientRect(window, &rect);
// Get the ThermionWindow instance associated with this window
ThermionWindow* thermionWindow = reinterpret_cast<ThermionWindow*>(
GetWindowLongPtr(window, GWLP_USERDATA));
if (thermionWindow) {
HBRUSH brush = CreateSolidBrush(RGB(0, 0, 255));
FillRect(hdc, &rect, brush);
DeleteObject(brush);
}
break;
}
case WM_SIZE:
break;
case WM_MOVE:
case WM_MOVING:
case WM_ACTIVATE:
case WM_WINDOWPOSCHANGED: {
// NativeViewCore::GetInstance()->SetHitTestBehavior(0);
auto user_data = ::GetWindowLongPtr(window, GWLP_USERDATA);
if (user_data) {
HWND flutterRootWindow = reinterpret_cast<HWND>(user_data);
::SetForegroundWindow(flutterRootWindow);
// NativeViewCore::GetInstance()->SetHitTestBehavior(0);
LONG ex_style = ::GetWindowLong(flutterRootWindow, GWL_EXSTYLE);
ex_style &= ~(WS_EX_TRANSPARENT | WS_EX_LAYERED);
::SetWindowLong(flutterRootWindow, GWL_EXSTYLE, ex_style);
}
break;
}
default:
break;
}
return ::DefWindowProc(window, message, wparam, lparam);
}
ThermionWindow::ThermionWindow(int width,
int height,
int left,
int top) : _width(width), _height(height), _left(left), _top(top) {
// create the HWND for Filament
auto window_class = WNDCLASSEX{};
::SecureZeroMemory(&window_class, sizeof(window_class));
window_class.cbSize = sizeof(window_class);
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.lpfnWndProc = FilamentWindowProc;
window_class.hInstance = 0;
window_class.lpszClassName = kClassName;
window_class.hCursor = ::LoadCursorW(nullptr, IDC_ARROW);
window_class.hbrBackground = ::CreateSolidBrush(RGB(0,255,0));
::RegisterClassExW(&window_class);
_windowHandle = ::CreateWindow(kClassName, kWindowName, WS_OVERLAPPEDWINDOW,
0, 0, _width, _height, nullptr,
nullptr, GetModuleHandle(nullptr), nullptr);
// Disable DWM animations
auto disable_window_transitions = TRUE;
DwmSetWindowAttribute(_windowHandle, DWMWA_TRANSITIONS_FORCEDISABLED,
&disable_window_transitions,
sizeof(disable_window_transitions));
auto style = ::GetWindowLong(_windowHandle, GWL_STYLE);
style &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX |
WS_EX_APPWINDOW);
::SetWindowLong(_windowHandle, GWL_STYLE, style);
// // remove taskbar entry for the window we created
// ITaskbarList3* taskbar = nullptr;
// ::CoCreateInstance(CLSID_TaskbarList, 0, CLSCTX_INPROC_SERVER,
// IID_PPV_ARGS(&taskbar));
// taskbar->DeleteTab(_windowHandle);
// taskbar->Release();
::ShowWindow(_windowHandle, SW_SHOW);
UpdateWindow(_windowHandle);
}
void ThermionWindow::Resize(int width, int height, int left, int top) {
_width = width;
_height = height;
_left = left;
_top = top;
}
HWND ThermionWindow::GetHandle() { return _windowHandle; }
} // namespace thermion_flutter

View File

@@ -0,0 +1,57 @@
#pragma once
#ifdef _WIN32
#ifdef IS_DLL
#define EMSCRIPTEN_KEEPALIVE __declspec(dllimport)
#else
#define EMSCRIPTEN_KEEPALIVE __declspec(dllexport)
#endif
#include <cstdint>
#include <iostream>
#include <Windows.h>
namespace thermion {
///
/// Instantiating a ThermionWindow creates a HWND that can be passed
/// to Filament to create a swapchain.
///
///
class ThermionWindow {
public:
ThermionWindow(
int width,
int height,
int left,
int top);
HWND GetHandle();
void Resize(int width, int height, int left, int top);
private:
HWND _windowHandle;
uint32_t _width = 0;
uint32_t _height = 0;
uint32_t _left = 0;
uint32_t _top = 0;
};
static ThermionWindow* _window;
extern "C" {
EMSCRIPTEN_KEEPALIVE intptr_t create_thermion_window(int width, int height, int left, int top) {
_window = new ThermionWindow(width, height, left, top);
return (intptr_t)_window->GetHandle();
}
EMSCRIPTEN_KEEPALIVE void update() {
MSG msg;
if(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
}
#endif

View File

@@ -1,13 +1,25 @@
output: '../lib/thermion_dart/compatibility/native/thermion_dart.g.dart'
output: '../lib/src/viewer/src/ffi/src/thermion_dart.g.dart'
headers:
entry-points:
- '../native/include/ThermionDartFFIApi.h'
- '../native/include/ThermionDartRenderThreadApi.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/ThermionDartFFIApi.h'
- '../native/include/ThermionDartRenderThreadApi.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: ThermionDartTexture
description: Bindings for ThermionDartTexture.
name: ThermionTextureSwift
description: Bindings for ThermionTexture.
language: objc
output: 'lib/thermion_dart/swift/swift_bindings.g.dart'
output: '../lib/src/swift/swift_bindings.g.dart'
exclude-all-by-default: true
objc-interfaces:
include:
- 'ThermionDartTexture'
- 'ThermionTextureSwift'
module:
'ThermionDartTexture': 'thermion_dart_texture'
'ThermionTextureSwift': 'swift_module'
headers:
entry-points:
- 'native/lib/macos/swift/ThermionDartTexture.h'
- '../native/include/generated/ThermionTextureSwiftObjCAPI.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

@@ -32,19 +32,14 @@ void main(List<String> args) async {
final name = "thermion_dart.dart";
final libUri = config.outputDirectory
.resolve(config.targetOS.libraryFileName(name, linkMode));
output.addAssets(
[
NativeCodeAsset(
package: config.packageName,
name: name,
file: libUri,
linkMode: linkMode,
os: config.targetOS,
architecture: config.dryRun ? null : config.targetArchitecture,
)
],
linkInPackage: null,
);
output.addAsset(NativeCodeAsset(
package: config.packageName,
name: name,
file: libUri,
linkMode: linkMode,
os: config.targetOS,
architecture: config.dryRun ? null : config.targetArchitecture,
));
return;
}
@@ -58,8 +53,10 @@ void main(List<String> args) async {
.map((f) => f.path)
.toList();
sources.addAll([
"${config.packageRoot.toFilePath()}/native/include/material/gizmo.c",
"${config.packageRoot.toFilePath()}/native/include/material/gizmo_material.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 = [
@@ -76,7 +73,6 @@ void main(List<String> args) async {
"image",
"imageio",
"tinyexr",
"gltfio_core",
"filaflat",
"dracodec",
"ibl",
@@ -91,7 +87,6 @@ void main(List<String> args) async {
"basis_transcoder"
];
if (platform == "windows") {
libDir = Directory(libDir).uri.toFilePath();
libs = libs.map((lib) => "${libDir}${lib}.lib").toList();
@@ -117,7 +112,12 @@ void main(List<String> args) async {
defines["WIN32"] = "1";
defines["_DEBUG"] = "1";
defines["_DLL"] = "1";
flags.addAll(["/std:c++20", "/MDd"]);
flags.addAll([
"/std:c++20",
"/MDd",
"/VERBOSE",
...defines.keys.map((k) => "/D$k=${defines[k]}").toList()
]);
}
if (platform == "ios") {
@@ -148,16 +148,26 @@ void main(List<String> args) async {
name: packageName,
language: Language.cpp,
assetName: 'thermion_dart.dart',
sources: sources,
includes: ['native/include', 'native/include/filament'],
defines: defines,
sources: platform == "windows" ? [] : sources,
includes: platform == "windows"
? []
: ['native/include', 'native/include/filament'],
defines: platform == "windows" ? {} : defines,
flags: [
if (platform == "macos") '-mmacosx-version-min=13.0',
if (platform == "ios") '-mios-version-min=13.0',
...flags,
...frameworks,
...libs.map((lib) => "-l$lib"),
"-L$libDir",
if (platform != "windows") ...libs.map((lib) => "-l$lib"),
if (platform != "windows") "-L$libDir",
if (platform == "windows") ...[
"/I${config.packageRoot.toFilePath()}\\native\\include",
"/I${config.packageRoot.toFilePath()}native\\include\\filament",
...sources,
'/link',
"/LIBPATH:$libDir",
'/DLL',
]
],
dartBuildFiles: ['hook/build.dart'],
);
@@ -176,17 +186,24 @@ void main(List<String> args) async {
Architecture.ia32 => "i686-linux-android",
_ => throw FormatException('Invalid')
};
var compilerPath = config.cCompiler.compiler!.path;
if(Platform.isWindows && compilerPath.startsWith("/")) {
if (Platform.isWindows && compilerPath.startsWith("/")) {
compilerPath = compilerPath.substring(1);
}
var ndkRoot = File(compilerPath).parent.parent.uri.toFilePath(windows:true);
var stlPath =
File([ndkRoot, "sysroot", "usr", "lib", archExtension, "libc++_shared.so"].join(Platform.pathSeparator));
var ndkRoot =
File(compilerPath).parent.parent.uri.toFilePath(windows: true);
var stlPath = File([
ndkRoot,
"sysroot",
"usr",
"lib",
archExtension,
"libc++_shared.so"
].join(Platform.pathSeparator));
output.addAsset(NativeCodeAsset(
package: "thermion_dart",
name: "libc++_shared.so",
@@ -200,7 +217,7 @@ void main(List<String> args) async {
if (config.targetOS == "windows") {
output.addAsset(
NativeCodeAsset(
package: "thermion_dart",
package: config.packageName,
name: "thermion_dart.dll",
linkMode: DynamicLoadingBundled(),
os: config.targetOS,

View File

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

View File

@@ -0,0 +1,282 @@
import 'dart:async';
import 'dart:math';
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();
Stream get cameraUpdated => _cameraUpdatedController.stream;
final _cameraUpdatedController = StreamController.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.SCALE1: InputAction.TRANSLATE,
InputType.SCALE2: InputAction.ZOOM,
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 panSensitivity = 0.1,
double movementSensitivity = 0.1,
double rotateSensitivity = 0.01,
double? clampY,
ThermionEntity? entity}) =>
DelegateInputHandler(
viewer: viewer,
pickDelegate: pickDelegate,
transformDelegate: FreeFlightInputHandlerDelegate(viewer,
clampY: clampY,
entity: entity,
rotationSensitivity: rotateSensitivity,
panSensitivity: panSensitivity,
movementSensitivity: movementSensitivity),
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,
InputType.SCALE1: InputAction.TRANSLATE,
InputType.SCALE2: InputAction.ZOOM,
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;
}
// ignore: unnecessary_null_comparison
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);
_cameraUpdatedController.add(true);
}
_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
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);
}
@override
Future<void> onScaleEnd(int pointerCount) async {}
@override
Future<void> onScaleStart(Vector2 localPosition, int pointerCount) async {
// noop
}
@override
Future<void> onScaleUpdate(Vector2 focalPoint, Vector2 focalPointDelta,
double horizontalScale, double verticalScale, double scale, int pointerCount) async {
if (pointerCount == 1) {
_inputDeltas[InputType.SCALE1] =
Vector3(focalPointDelta.x, focalPointDelta.y, 0);
} else if (pointerCount == 2) {
_inputDeltas[InputType.SCALE2] =
Vector3(0, 0, max(horizontalScale, verticalScale));
} else {
throw UnimplementedError("Only pointerCount <= 2 supported");
}
}
}

View File

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

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

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

@@ -0,0 +1,13 @@
import 'package:thermion_dart/thermion_dart.dart';
import 'package:vector_math/vector_math_64.dart';
class DefaultPickDelegate extends PickDelegate {
final ThermionViewer viewer;
const DefaultPickDelegate(this.viewer);
@override
void pick(Vector2 location) {
viewer.pick(location.x.toInt(), location.y.toInt());
}
}

View File

@@ -0,0 +1,183 @@
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';
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:
break;
case InputAction.NONE:
// Do nothing
break;
case InputAction.ZOOM:
_queuedZoomDelta += delta.z;
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

@@ -0,0 +1,183 @@
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();
Vector3 _queuedTranslateDelta = Vector3.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:
_queuedTranslateDelta += delta;
break;
case InputAction.PICK:
_queuedZoomDelta += delta.z;
break;
case InputAction.NONE:
break;
case InputAction.ZOOM:
_queuedZoomDelta += delta.z;
break;
}
}
bool _executing = false;
@override
Future<void> execute() async {
if (_executing) {
return;
}
_executing = true;
if (_queuedRotationDelta.length2 == 0.0 &&
_queuedTranslateDelta.length2 == 0.0 &&
_queuedZoomDelta == 0.0 &&
_queuedMoveDelta.length2 == 0.0) {
_executing = false;
return;
}
final activeCamera = await viewer.getActiveCamera();
Matrix4 currentTransform = await viewer.getLocalTransform(await entity);
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 (_queuedTranslateDelta.length2 > 0.0) {
double deltaX = _queuedTranslateDelta.x * panSensitivity;
double deltaY = _queuedTranslateDelta.y * panSensitivity;
double deltaZ = -_queuedTranslateDelta.z * panSensitivity;
relativeTranslation += _right * deltaX + _up * deltaY + _forward * deltaZ;
_queuedTranslateDelta = Vector3.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) *
movementSensitivity;
_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

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

@@ -0,0 +1,122 @@
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;
case InputAction.ZOOM:
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

@@ -0,0 +1,49 @@
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, ZOOM, NONE }
abstract class InputHandler {
Stream get cameraUpdated;
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(Vector2 focalPoint, int pointerCount);
Future<void> onScaleUpdate(Vector2 focalPoint, Vector2 focalPointDelta, double horizontalScale, double verticalScale, double scale, int pointerCount);
Future<void> onScaleEnd(int pointerCount);
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

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

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

View File

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

View File

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

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

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

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

View File

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

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

View File

@@ -0,0 +1,91 @@
import 'dart:ffi';
import 'package:vector_math/vector_math_64.dart';
import '../../../../utils/src/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

@@ -0,0 +1,50 @@
import 'dart:async';
import 'dart:ffi';
import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_dart.g.dart';
import '../../../../utils/src/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

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

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

View File

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

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

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

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

View File

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

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

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

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

@@ -0,0 +1,5 @@
// "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

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

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

View File

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

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

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

View File

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

View File

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

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

View File

@@ -0,0 +1,30 @@
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,60 +1,24 @@
import 'dart:math';
import 'package:thermion_dart/src/viewer/src/events.dart';
import '../../utils/src/gizmo.dart';
import 'shared_types/shared_types.dart';
export 'shared_types/shared_types.dart';
import 'package:thermion_dart/thermion_dart/scene.dart';
import 'dart:math';
import 'dart:typed_data';
import 'package:vector_math/vector_math_64.dart';
import 'dart:async';
import 'package:animation_tools_dart/animation_tools_dart.dart';
// a handle that can be safely passed back to the rendering layer to manipulate an Entity
typedef ThermionEntity = int;
import 'shared_types/view.dart';
// "picking" means clicking/tapping on the viewport, and unprojecting the X/Y coordinate to determine whether any renderable entities were present at those coordinates.
typedef FilamentPickResult = ({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});
}
const double kNear = 0.05;
const double kFar = 1000.0;
const double kFocalLength = 28.0;
abstract class ThermionViewer {
///
/// A Future that resolves when the underlying rendering context has been successfully created.
///
Future<bool> get initialized;
///
@@ -64,6 +28,11 @@ 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].
///
@@ -75,9 +44,53 @@ abstract class ThermionViewer {
Future setRendering(bool render);
///
/// Render a single frame.
/// Render a single frame immediately.
///
Future render();
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);
///
/// Sets the framerate for continuous rendering when [setRendering] is enabled.
@@ -124,11 +137,17 @@ abstract class ThermionViewer {
Future removeSkybox();
///
/// Loads an image-based light from the specified path at the given intensity.
/// Only one IBL can be active at any given time; if an IBL has already been loaded, it will be replaced.
/// 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.
///
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.
///
@@ -145,6 +164,8 @@ 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,
@@ -163,6 +184,17 @@ 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);
///
@@ -172,8 +204,28 @@ 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});
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.
/// If [loadResourcesAsync] is true, resources (textures, materials, etc) will
/// be loaded asynchronously (so expect some material/texture pop-in);
///
///
Future<ThermionEntity> loadGlbFromBuffer(Uint8List data,
{int numInstances = 1,
bool keepData = false,
int priority = 4,
int layer = 0,
bool loadResourcesAsync});
///
/// Create a new instance of [entity].
@@ -195,38 +247,10 @@ 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 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();
{bool keepData = false});
///
/// Set the weights for all morph targets in [entity] to [weights].
@@ -270,10 +294,9 @@ abstract class ThermionViewer {
{List<String>? targetMeshNames});
///
/// Clear all current morph animations for [entity].
/// Clear all current morph animations for [entity].
///
Future clearMorphAnimationData(
ThermionEntity entity);
Future clearMorphAnimationData(ThermionEntity entity);
///
/// Resets all bones in the given entity to their rest pose.
@@ -335,6 +358,13 @@ 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]).
@@ -364,21 +394,6 @@ 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.
///
@@ -386,7 +401,8 @@ abstract class ThermionViewer {
{bool loop = false,
bool reverse = false,
bool replaceActive = true,
double crossfade = 0.0});
double crossfade = 0.0,
double startOffset = 0.0});
///
/// Schedules the glTF animation at [index] in [entity] to start playing on the next frame.
@@ -414,14 +430,25 @@ abstract class ThermionViewer {
Future setMainCamera();
///
/// Returns the entity associated with the main camera.
/// Returns the entity associated with the main camera. You probably never need this; use getMainCamera instead.
///
Future<ThermionEntity> getMainCamera();
Future<ThermionEntity> getMainCameraEntity();
///
/// Sets the current scene camera to the glTF camera under [name] in [entity].
/// Returns the entity associated with the main camera. You probably never need this; use getMainCamera instead.
///
Future setCameraFov(double degrees, double width, double height);
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);
///
/// Sets the tone mapping (requires postprocessing).
@@ -444,10 +471,16 @@ abstract class ThermionViewer {
Future setCameraCulling(double near, double far);
///
/// Get the distance (in world units) to the near culling plane for the active camera.
/// Get the distance (in world units) to the near 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.
///
@@ -505,7 +538,7 @@ abstract class ThermionViewer {
Future moveCameraToAsset(ThermionEntity entity);
///
/// Enables/disables frustum culling. Currently we don't expose a method for manipulating the camera projection/culling matrices so this is your only option to deal with unwanted near/far clipping.
/// Enables/disables frustum culling.
///
Future setViewFrustumCulling(bool enabled);
@@ -516,31 +549,70 @@ abstract class ThermionViewer {
double aperture, double shutterSpeed, double sensitivity);
///
/// Rotate the camera by [rads] around the given axis. Note this is not persistent - any viewport navigation will reset the camera transform.
/// Rotate the camera by [rads] around the given axis.
///
Future setCameraRotation(Quaternion quaternion);
///
/// Sets the camera model matrix.
///
@Deprecated("Will be superseded by setCameraModelMatrix4")
Future setCameraModelMatrix(List<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, skipping all collision detection.
/// Directly sets the world space position for [entity] to the given coordinates.
///
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.
///
@@ -553,26 +625,16 @@ abstract class ThermionViewer {
ThermionEntity entity, double rads, double x, double y, double z);
///
/// Queues an update to the worldspace position for [entity] to {x,y,z}.
/// The actual update will occur on the next frame, and will be subject to collision detection.
/// TODO
///
Future queuePositionUpdate(
ThermionEntity entity, double x, double y, double z,
{bool relative = false});
Future queuePositionUpdateFromViewportCoords(
ThermionEntity entity, double x, double y);
///
/// 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.
/// TODO
///
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});
Future queueRelativePositionUpdateWorldAxis(ThermionEntity entity,
double viewportX, double viewportY, double x, double y, double z);
///
/// Enable/disable postprocessing (disabled by default).
@@ -628,16 +690,6 @@ 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].
///
@@ -657,17 +709,6 @@ 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.
@@ -695,20 +736,26 @@ 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(List<double> vertices, List<int> indices,
{String? materialPath,
PrimitiveType primitiveType = PrimitiveType.TRIANGLES});
Future createGeometry(Geometry geometry,
{MaterialInstance? materialInstance, bool keepData = false});
///
/// Gets the parent transform of [child].
/// Gets the parent entity of [entity]. Returns null if the entity has no parent.
///
Future<ThermionEntity?> getParent(ThermionEntity 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);
///
/// Sets the parent transform of [child] to [parent].
///
Future setParent(ThermionEntity child, ThermionEntity parent);
Future setParent(ThermionEntity child, ThermionEntity parent,
{bool preserveScaling});
///
/// Test all collidable entities against this entity to see if any have collided.
@@ -722,30 +769,150 @@ abstract class ThermionViewer {
Future setPriority(ThermionEntity entityId, int priority);
///
/// The Scene holds all loaded entities/lights.
/// The gizmo for translating/rotating objects. Only one gizmo can be active for a given view.
///
Scene get scene;
///
///
///
AbstractGizmo? get gizmo;
Future<Gizmo> createGizmo(covariant View view);
///
/// Register a callback to be invoked when this viewer is disposed.
///
void onDispose(Future Function() callback);
}
abstract class AbstractGizmo {
bool get isActive;
void translate(double transX, double transY);
void reset();
void attach(ThermionEntity entity);
void detach();
///
/// 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);
}

View File

@@ -1,12 +1,15 @@
import 'dart:math';
import 'dart:typed_data';
import 'package:thermion_dart/thermion_dart/scene.dart';
import 'package:thermion_dart/thermion_dart/thermion_viewer.dart';
import 'package:thermion_dart/src/utils/src/gizmo.dart';
import 'package:thermion_dart/src/viewer/src/shared_types/swap_chain.dart';
import 'package:thermion_dart/src/viewer/src/shared_types/view.dart';
import 'package:thermion_dart/thermion_dart.dart';
import 'package:vector_math/vector_math_64.dart';
import 'dart:async';
import 'package:animation_tools_dart/animation_tools_dart.dart';
typedef ThermionViewerImpl = ThermionViewerStub;
import 'events.dart';
import 'shared_types/camera.dart';
class ThermionViewerStub extends ThermionViewer {
@override
@@ -73,13 +76,6 @@ 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) {
@@ -220,12 +216,6 @@ class ThermionViewerStub extends ThermionViewer {
throw UnimplementedError();
}
@override
Future<ThermionEntity> getMainCamera() {
// TODO: implement getMainCamera
throw UnimplementedError();
}
@override
Future<List<String>> getMorphTargetNames(
ThermionEntity entity, ThermionEntity childEntity) {
@@ -251,10 +241,6 @@ 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
@@ -265,19 +251,6 @@ 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
@@ -333,7 +306,8 @@ class ThermionViewerStub extends ThermionViewer {
{bool loop = false,
bool reverse = false,
bool replaceActive = true,
double crossfade = 0.0}) {
double crossfade = 0.0,
double startOffset=0.0}) {
// TODO: implement playAnimation
throw UnimplementedError();
}
@@ -407,12 +381,7 @@ class ThermionViewerStub extends ThermionViewer {
throw UnimplementedError();
}
@override
Future render() {
// TODO: implement render
throw UnimplementedError();
}
@override
// TODO: implement rendering
bool get rendering => throw UnimplementedError();
@@ -453,10 +422,6 @@ class ThermionViewerStub extends ThermionViewer {
throw UnimplementedError();
}
@override
// TODO: implement scene
Scene get scene => throw UnimplementedError();
@override
Future setAnimationFrame(
ThermionEntity entity, int index, int animationFrame) {
@@ -534,7 +499,7 @@ class ThermionViewerStub extends ThermionViewer {
}
@override
Future setCameraFov(double degrees, double width, double height) {
Future setCameraFov(double degrees, {bool horizontal=true}) {
// TODO: implement setCameraFov
throw UnimplementedError();
}
@@ -606,7 +571,7 @@ class ThermionViewerStub extends ThermionViewer {
}
@override
Future setParent(ThermionEntity child, ThermionEntity parent) {
Future setParent(ThermionEntity child, ThermionEntity parent, { bool preserveScaling = false}) {
// TODO: implement setParent
throw UnimplementedError();
}
@@ -749,4 +714,354 @@ 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 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();
}
@override
Future<ThermionEntity> loadGlbFromBuffer(Uint8List data, {int numInstances = 1, bool keepData = false, int priority = 4, int layer = 0, bool loadResourcesAsync= false}) {
// TODO: implement loadGlbFromBuffer
throw UnimplementedError();
}
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@ library thermion_flutter_js;
import 'dart:js_interop';
import 'package:thermion_dart/thermion_dart/thermion_viewer.dart';
import '../../shared_types/shared_types.dart';
///
/// An extension type on [JSObject] that represents a
@@ -22,6 +22,9 @@ extension type ThermionViewerJSShim(JSObject _) implements JSObject {
@JS('render')
external JSPromise render();
@JS('capture')
external JSPromise<JSUint8Array> capture();
@JS('setFrameRate')
external JSPromise setFrameRate(int framerate);
@@ -184,6 +187,7 @@ extension type ThermionViewerJSShim(JSObject _) implements JSObject {
bool reverse,
bool replaceActive,
double crossfade,
double startOffset,
);
@JS('playAnimationByName')
@@ -216,7 +220,7 @@ extension type ThermionViewerJSShim(JSObject _) implements JSObject {
external JSPromise<JSNumber> getMainCamera();
@JS('setCameraFov')
external JSPromise setCameraFov(double degrees, double width, double height);
external JSPromise setCameraFov(double degrees, bool horizontal);
@JS('setToneMapping')
external JSPromise setToneMapping(int mapper);
@@ -374,7 +378,7 @@ extension type ThermionViewerJSShim(JSObject _) implements JSObject {
JSArray<JSNumber> indices, String? materialPath, int primitiveType);
@JS('setParent')
external JSPromise setParent(ThermionEntity child, ThermionEntity parent);
external JSPromise setParent(ThermionEntity child, ThermionEntity parent, bool preserveScaling);
@JS('getParent')
external JSPromise<JSNumber> getParent(ThermionEntity child);

View File

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

View File

@@ -0,0 +1,77 @@
import 'package:vector_math/vector_math_64.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

@@ -0,0 +1,24 @@
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();
}
}

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -0,0 +1,7 @@
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,8 +1,5 @@
library filament_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';
export 'src/viewer/viewer.dart';
export 'src/input/input.dart';
export 'src/utils/utils.dart';

View File

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

View File

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

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

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

View File

@@ -1,746 +0,0 @@
@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()
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}) =>
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

@@ -1,851 +0,0 @@
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> clearMorphAnimationData(ThermionEntity entity) async {
_shim.clearMorphAnimationData(entity);
}
@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

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

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

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

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

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

@@ -0,0 +1,110 @@
#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 = true;
bool unlit = true;
bool hasVertexColors = true;
bool hasBaseColorTexture = true;
bool hasNormalTexture = true;
bool hasOcclusionTexture = true;
bool hasEmissiveTexture = true;
bool useSpecularGlossiness = true;
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 = true;
uint8_t metallicRoughnessUV = 7;
};
struct {
bool hasSpecularGlossinessTexture = true;
uint8_t specularGlossinessUV = 7;
};
#endif
};
uint8_t baseColorUV;
// -- 32 bit boundary --
bool hasClearCoatTexture = true;
uint8_t clearCoatUV = 7;
bool hasClearCoatRoughnessTexture = true;
uint8_t clearCoatRoughnessUV = 7;
bool hasClearCoatNormalTexture = true;
uint8_t clearCoatNormalUV = 7;
bool hasClearCoat = true;
bool hasTransmission = true;
bool hasTextureTransforms = 6;
// -- 32 bit boundary --
uint8_t emissiveUV;
uint8_t aoUV;
uint8_t normalUV;
bool hasTransmissionTexture = true;
uint8_t transmissionUV = 7;
// -- 32 bit boundary --
bool hasSheenColorTexture = true;
uint8_t sheenColorUV = 7;
bool hasSheenRoughnessTexture = true;
uint8_t sheenRoughnessUV = 7;
bool hasVolumeThicknessTexture = true;
uint8_t volumeThicknessUV = 7;
bool hasSheen = true;
bool hasIOR = true;
bool hasVolume = true;
} ;
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

@@ -0,0 +1,59 @@
#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,24 +36,15 @@
#include "SceneManager.hpp"
#include "ThreadPool.hpp"
namespace thermion_filament
namespace thermion
{
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
{
@@ -63,69 +54,54 @@ namespace thermion_filament
FilamentViewer(const void *context, const ResourceLoaderWrapperImpl *const resourceLoaderWrapper, void *const platform = nullptr, const char *uberArchivePath = nullptr);
~FilamentViewer();
void setToneMapping(ToneMapping toneMapping);
void setBloom(float strength);
View* createView();
View* getViewAt(int index);
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,
void *pixelBuffer,
void (*callback)(void *buf, size_t size, void *data),
void *data);
uint64_t frameTimeInNanos
);
void setFrameInterval(float interval);
bool setCamera(EntityId asset, const char *nodeName);
void setMainCamera();
void setMainCamera(View *view);
EntityId getMainCamera();
void setCameraFov(double fovDegrees, double aspect);
Camera* getCamera(EntityId entity);
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);
void createRenderTarget(intptr_t textureId, uint32_t width, uint32_t height);
RenderTarget* createRenderTarget(intptr_t textureId, uint32_t width, uint32_t height);
void destroyRenderTarget(RenderTarget* renderTarget);
Renderer *getRenderer();
void setBackgroundColor(const float r, const float g, const float b, const float a);
void setBackgroundImage(const char *resourcePath, bool fillHeight);
void clearBackgroundImage();
void setBackgroundImagePosition(float x, float y, bool clamp);
std::map<SwapChain*, std::vector<View*>> _renderable;
void setRenderable(View* view, SwapChain* swapChain, bool renderable);
// 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));
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 clearBackgroundImage();
void setBackgroundImagePosition(float x, float y, bool clamp, uint32_t width, uint32_t height);
void pick(View *view, uint32_t x, uint32_t y, void (*callback)(EntityId entityId, int x, int y, View *view));
Engine* getEngine() {
return _engine;
}
EntityId addLight(
LightManager::Type t,
@@ -144,75 +120,58 @@ namespace thermion_filament
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 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);
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)());
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_filament::ThreadPool *_tp = nullptr;
thermion::ThreadPool *_tp = nullptr;
Renderer *_renderer = nullptr;
RenderTarget *_rt = nullptr;
Texture *_rtColor = nullptr;
Texture *_rtDepth = nullptr;
SwapChain *_swapChain = nullptr;
SceneManager *_sceneManager = nullptr;
std::vector<RenderTarget*> _renderTargets;
std::vector<SwapChain*> _swapChains;
std::vector<View*> _views;
std::mutex mtx; // mutex to ensure thread safety when removing assets
std::mutex _renderMutex; // 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;
mat4f _imageScale;
filament::math::mat4f _imageScale;
Texture *_imageTexture = nullptr;
Texture *_dummyImageTexture = nullptr;
utils::Entity _imageEntity;
@@ -227,12 +186,10 @@ namespace thermion_filament
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

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

@@ -0,0 +1,52 @@
#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_filament
namespace thermion
{
struct ResourceLoaderWrapperImpl : public ResourceLoaderWrapper

View File

@@ -4,8 +4,11 @@
#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>
@@ -14,16 +17,20 @@
#include <filament/IndexBuffer.h>
#include <filament/InstanceBuffer.h>
#include <utils/NameComponentManager.h>
#include "material/gizmo.h"
#include "utils/NameComponentManager.h"
#include "CustomGeometry.hpp"
#include "APIBoundaryTypes.h"
#include "GridOverlay.hpp"
#include "ResourceBuffer.hpp"
#include "components/CollisionComponentManager.hpp"
#include "components/AnimationComponentManager.hpp"
#include "tsl/robin_map.h"
namespace thermion_filament
namespace thermion
{
typedef int32_t EntityId;
@@ -37,13 +44,46 @@ namespace thermion_filament
class SceneManager
{
public:
SceneManager(const ResourceLoaderWrapperImpl *const loader,
SceneManager(
const ResourceLoaderWrapperImpl *const loader,
Engine *engine,
Scene *scene,
const char *uberArchivePath);
const char *uberArchivePath,
Camera* mainCamera);
~SceneManager();
EntityId loadGltf(const char *uri, const char *relativeResourcePath);
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);
////
/// @brief Load the GLB from the specified path, optionally creating multiple instances.
@@ -51,8 +91,8 @@ namespace thermion_filament
/// @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);
EntityId loadGlbFromBuffer(const uint8_t *data, size_t length, int numInstances = 1);
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, bool loadResourcesAsync = false);
EntityId createInstance(EntityId entityId);
void remove(EntityId entity);
@@ -67,8 +107,11 @@ namespace thermion_filament
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 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);
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);
const utils::Entity *getCameraEntities(EntityId e);
size_t getCameraEntityCount(EntityId e);
const utils::Entity *getLightEntities(EntityId e) noexcept;
@@ -82,7 +125,7 @@ namespace thermion_filament
bool setMorphAnimationBuffer(
EntityId entityId,
const float *const morphData,
const int *const morphIndices,
const uint32_t *const morphIndices,
int numMorphTargets,
int numFrames,
float frameLengthInMs);
@@ -127,12 +170,19 @@ namespace thermion_filament
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);
void playAnimation(EntityId e, int index, bool loop, bool reverse, bool replaceActive, float crossfade = 0.3f, float startOffset = 0.0f);
void stopAnimation(EntityId e, int index);
void setMorphTargetWeights(const char *const entityName, float *weights, int count);
void loadTexture(EntityId entity, const char *resourcePath, int renderableIndex);
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 setAnimationFrame(EntityId entity, int animationIndex, int animationFrame);
bool hide(EntityId entity, const char *meshName);
bool reveal(EntityId entity, const char *meshName);
@@ -146,10 +196,21 @@ namespace thermion_filament
void addCollisionComponent(EntityId entity, void (*onCollisionCallback)(const EntityId entityId1, const EntityId entityId2), bool affectsCollidingTransform);
void removeCollisionComponent(EntityId entityId);
EntityId getParent(EntityId child);
void setParent(EntityId child, EntityId parent);
EntityId getAncestor(EntityId child);
void setParent(EntityId child, EntityId parent, bool preserveScaling);
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
@@ -164,24 +225,101 @@ namespace thermion_filament
///
void setPriority(EntityId entity, int priority);
/// @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}
/// @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)
/// @return
///
void getGizmo(EntityId *out);
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
);
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;
Scene *_scene;
Engine *_engine = nullptr;
Scene *_scene = nullptr;
Camera* _mainCamera;
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;
@@ -190,22 +328,21 @@ namespace thermion_filament
gltfio::FilamentInstance *>
_instances;
tsl::robin_map<EntityId, gltfio::FilamentAsset *> _assets;
tsl::robin_map<EntityId, std::tuple<math::float3, bool, math::quatf, bool, float>> _transformUpdates;
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;
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);
EntityId addGizmo();
utils::Entity _gizmo[3];
Material* _gizmoMaterial;
MaterialInstance* _gizmoMaterialInstances[3];
GridOverlay* _gridOverlay = nullptr;
};
}

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