Compare commits
352 Commits
thermion_d
...
thermion_f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb517c907d | ||
|
|
a6f5e59cbb | ||
|
|
9420143a36 | ||
|
|
eb8835b63a | ||
|
|
f9468db266 | ||
|
|
1135ba054c | ||
|
|
8f7509a23f | ||
|
|
cba9ee98ad | ||
|
|
0ec0fef8f3 | ||
|
|
a348562f56 | ||
|
|
392a606bbc | ||
|
|
440bed4485 | ||
|
|
a321966e5b | ||
|
|
f180c1018f | ||
|
|
f267aa6dc6 | ||
|
|
1c74e83c2c | ||
|
|
099a895eb6 | ||
|
|
3b810f84da | ||
|
|
10f2c7d36b | ||
|
|
5b849638de | ||
|
|
857fd6f782 | ||
|
|
29edec63ab | ||
|
|
c6afc4756a | ||
|
|
365657cf88 | ||
|
|
5441dedcf4 | ||
|
|
254b6d8af2 | ||
|
|
1459aea5cf | ||
|
|
80d8525671 | ||
|
|
389a165ed3 | ||
|
|
d8f309d21b | ||
|
|
ee983ddfaa | ||
|
|
c1cdd37e9d | ||
|
|
646f05933d | ||
|
|
3f854a7f27 | ||
|
|
740dbea8bd | ||
|
|
95a44936ac | ||
|
|
9deafc7371 | ||
|
|
c1af7e374d | ||
|
|
cd71db72be | ||
|
|
a9d90f966b | ||
|
|
1a323ca551 | ||
|
|
2d85e191bc | ||
|
|
1b971a859c | ||
|
|
78b697d1c2 | ||
|
|
9da2ce6672 | ||
|
|
39fabd501d | ||
|
|
3f1867dd6f | ||
|
|
68d29041b0 | ||
|
|
b300e86962 | ||
|
|
562ecf2ee5 | ||
|
|
d294938a2c | ||
|
|
027cf23069 | ||
|
|
c4598637bb | ||
|
|
8a94b6a334 | ||
|
|
c80c163212 | ||
|
|
921a994eb6 | ||
|
|
65e60da288 | ||
|
|
661185083e | ||
|
|
a2a26555e2 | ||
|
|
7f11250b79 | ||
|
|
a6d2f2ecf9 | ||
|
|
399d447eec | ||
|
|
fb6204e47c | ||
|
|
566856c8fb | ||
|
|
d29dd207b6 | ||
|
|
e20489900d | ||
|
|
567a268ded | ||
|
|
6a57d242f9 | ||
|
|
41e0851d70 | ||
|
|
95b378348c | ||
|
|
1309bf7c6e | ||
|
|
2531b507b0 | ||
|
|
16dc0419e2 | ||
|
|
4e7ec6bfb4 | ||
|
|
035ad48fe4 | ||
|
|
ff7c582157 | ||
|
|
0876a91e17 | ||
|
|
a2d3c1d73e | ||
|
|
1a721deee6 | ||
|
|
4355d9c83f | ||
|
|
239891c400 | ||
|
|
ac10aa0a1e | ||
|
|
7ecf414a47 | ||
|
|
87c96d06a4 | ||
|
|
26a8d9c6b5 | ||
|
|
947a77f619 | ||
|
|
c850513b7f | ||
|
|
c47d827139 | ||
|
|
a2684ae47d | ||
|
|
2b1339b560 | ||
|
|
59aec2bcc9 | ||
|
|
61d1581b96 | ||
|
|
8e47332ce8 | ||
|
|
ffe8bee98b | ||
|
|
1e07486017 | ||
|
|
58da196876 | ||
|
|
78af155a6d | ||
|
|
d7e1b3d7ba | ||
|
|
877eae35cd | ||
|
|
87846b68de | ||
|
|
6581ee8a2a | ||
|
|
951064e657 | ||
|
|
68ebf945a5 | ||
|
|
153817e859 | ||
|
|
ba0bc54fa7 | ||
|
|
9b99975017 | ||
|
|
e6a6862ba4 | ||
|
|
b7d3e9191a | ||
|
|
b461b2c5db | ||
|
|
8f7f48bcaa | ||
|
|
7dc8e394f7 | ||
|
|
f5c8faa5c3 | ||
|
|
c84b4010d1 | ||
|
|
b720c1294f | ||
|
|
d442ab5ce6 | ||
|
|
61fdf300f4 | ||
|
|
4dabca9160 | ||
|
|
85d6946645 | ||
|
|
4b1d8ce729 | ||
|
|
c52b0084ce | ||
|
|
8db725d8bd | ||
|
|
c67d1cbbc4 | ||
|
|
3ea4062e33 | ||
|
|
65e99b9212 | ||
|
|
e83193ba0d | ||
|
|
835338ef63 | ||
|
|
57872d2e40 | ||
|
|
b5a7996769 | ||
|
|
ddbb4ec5c6 | ||
|
|
7cb3080596 | ||
|
|
f6c91294aa | ||
|
|
412d333525 | ||
|
|
378dede02d | ||
|
|
0e3db2635f | ||
|
|
77147cbafd | ||
|
|
43e5fd7766 | ||
|
|
fc3ca3d6b3 | ||
|
|
51f52bb71b | ||
|
|
033c3f632d | ||
|
|
6d862ef36a | ||
|
|
f816274fb9 | ||
|
|
0816286696 | ||
|
|
b421df5e2f | ||
|
|
f6a136643d | ||
|
|
b5e278183a | ||
|
|
8e85042e37 | ||
|
|
f943756624 | ||
|
|
92814aed56 | ||
|
|
e8a1b976e1 | ||
|
|
ef48dbce30 | ||
|
|
196cc6b980 | ||
|
|
1788c74d4c | ||
|
|
74e808d1dc | ||
|
|
72dacc5b21 | ||
|
|
ebdaf65b89 | ||
|
|
fa43149c98 | ||
|
|
e8ae7193ee | ||
|
|
aa21c0fb76 | ||
|
|
10826466a4 | ||
|
|
3b2d7d8c47 | ||
|
|
212443cb8e | ||
|
|
523141d54b | ||
|
|
d8a0859f16 | ||
|
|
c70cc9abb5 | ||
|
|
66b626e605 | ||
|
|
4b28119318 | ||
|
|
666506aed0 | ||
|
|
a55f63a428 | ||
|
|
fb441e151c | ||
|
|
82d85386c0 | ||
|
|
242b2f6faa | ||
|
|
31d31dd583 | ||
|
|
f6077012b1 | ||
|
|
18bb45dcd8 | ||
|
|
10b919e4f4 | ||
|
|
98cedf821c | ||
|
|
729f72e768 | ||
|
|
d01861e949 | ||
|
|
adec48f253 | ||
|
|
27a8ce18d5 | ||
|
|
4b740a9f5a | ||
|
|
0b34b4546e | ||
|
|
c17919cd97 | ||
|
|
462f1f02bf | ||
|
|
ddc433a126 | ||
|
|
ab649e860d | ||
|
|
676ddc3773 | ||
|
|
7d2cf3f91b | ||
|
|
492d41d756 | ||
|
|
191c2fd709 | ||
|
|
027fc9ae04 | ||
|
|
b73d0e1e96 | ||
|
|
315f2b63b9 | ||
|
|
ad205679cb | ||
|
|
6ef8d19e94 | ||
|
|
6b0f25ca59 | ||
|
|
820d341f67 | ||
|
|
70f904d54c | ||
|
|
aee607908d | ||
|
|
c99c57e24d | ||
|
|
00d75be479 | ||
|
|
3a9bd31919 | ||
|
|
51be1bce39 | ||
|
|
822b8e14c1 | ||
|
|
2ab30a7933 | ||
|
|
1be8a5e862 | ||
|
|
98d61fa1b3 | ||
|
|
5b3d16a316 | ||
|
|
b6863828b4 | ||
|
|
98fefd0e52 | ||
|
|
d476d78e2b | ||
|
|
98113fb79f | ||
|
|
90827ff012 | ||
|
|
44078ba2e0 | ||
|
|
866219ee2e | ||
|
|
d785bd6b7e | ||
|
|
3e4e6653a8 | ||
|
|
8d250e2664 | ||
|
|
d455cea29e | ||
|
|
374e8eb910 | ||
|
|
833fc74b4c | ||
|
|
62417bfebd | ||
|
|
abc9ecbeee | ||
|
|
a1f71ab459 | ||
|
|
d123929fb4 | ||
|
|
141827c59c | ||
|
|
9fbcc9edaf | ||
|
|
83d9c5be30 | ||
|
|
6aadbbc3d0 | ||
|
|
d5bc865cf4 | ||
|
|
d766733489 | ||
|
|
f51c640d17 | ||
|
|
66f10b598a | ||
|
|
59957650aa | ||
|
|
d43fbd8964 | ||
|
|
f5de4349bf | ||
|
|
f67e1a021d | ||
|
|
33f2c5fbf7 | ||
|
|
6a7bde930d | ||
|
|
b827a2142b | ||
|
|
8bd2416bad | ||
|
|
3684eb248c | ||
|
|
9077632d1b | ||
|
|
d40261ae29 | ||
|
|
77dbb574c7 | ||
|
|
89a660144e | ||
|
|
7554af5d41 | ||
|
|
4b742fea2d | ||
|
|
a6c6cff8b6 | ||
|
|
5813753ef9 | ||
|
|
cf0dad2631 | ||
|
|
cfddb99a8b | ||
|
|
845d5bf223 | ||
|
|
dbbd972909 | ||
|
|
aba3ba24af | ||
|
|
81c27dde9f | ||
|
|
476b552fd0 | ||
|
|
5c4d5d4b9d | ||
|
|
ae5ef2c286 | ||
|
|
b0f3c8a087 | ||
|
|
43fc7ffc65 | ||
|
|
ee24fca20e | ||
|
|
a00fdbe042 | ||
|
|
c2eb28a8f5 | ||
|
|
4c6c20f3de | ||
|
|
b2ae8135c6 | ||
|
|
aecde97200 | ||
|
|
331caccc22 | ||
|
|
63e2b74bb1 | ||
|
|
433f03161c | ||
|
|
6ec84b6249 | ||
|
|
29b6a48816 | ||
|
|
00755fd417 | ||
|
|
9c5156e41a | ||
|
|
226c45ee2e | ||
|
|
e04390b2fe | ||
|
|
9a87eb4d9b | ||
|
|
2284d9d081 | ||
|
|
cf61369a8d | ||
|
|
4e14bd2396 | ||
|
|
0798d5c071 | ||
|
|
80d5b1d23f | ||
|
|
0bd87288d1 | ||
|
|
51e06c2eb9 | ||
|
|
b29663923d | ||
|
|
f57a323cda | ||
|
|
03ffe85113 | ||
|
|
4edc8aa85b | ||
|
|
04ecb4d56f | ||
|
|
0ac0a92024 | ||
|
|
ae1e14ddb7 | ||
|
|
aa246ab63a | ||
|
|
d4350d7d99 | ||
|
|
f07fe6084a | ||
|
|
4e29055a20 | ||
|
|
6d0c06a853 | ||
|
|
abe6e1fcb8 | ||
|
|
683105c4f7 | ||
|
|
6f2331582c | ||
|
|
f0f97e310c | ||
|
|
2331f2c31a | ||
|
|
704b7f6734 | ||
|
|
7ac7ae43ab | ||
|
|
d52b23d6b5 | ||
|
|
8b17916cd9 | ||
|
|
4a0f4e3ac8 | ||
|
|
85116f43a2 | ||
|
|
3ecb8920ea | ||
|
|
8923d97129 | ||
|
|
375e1cc887 | ||
|
|
78dcbc8bb9 | ||
|
|
a3f7b98bf7 | ||
|
|
12b61e8767 | ||
|
|
0e3b014c2c | ||
|
|
10db8c39f3 | ||
|
|
371bcb3706 | ||
|
|
714c575409 | ||
|
|
c0941e3b7f | ||
|
|
026acb7467 | ||
|
|
3c05cc6a43 | ||
|
|
f1a2926bdf | ||
|
|
88e8a138ac | ||
|
|
53d0301828 | ||
|
|
b10fec1963 | ||
|
|
08e1eb7778 | ||
|
|
d6713c090c | ||
|
|
497ecbf881 | ||
|
|
6f7d03737e | ||
|
|
ad60c6bbe1 | ||
|
|
92fdda722b | ||
|
|
3cc876f972 | ||
|
|
7d1e706045 | ||
|
|
11756fcedd | ||
|
|
2e1f2cd56d | ||
|
|
c08611b2c3 | ||
|
|
f87f89427d | ||
|
|
c3319ebbf3 | ||
|
|
f7b765c5d0 | ||
|
|
9512b74008 | ||
|
|
5a3517f953 | ||
|
|
731c4981c9 | ||
|
|
8f20a8a859 | ||
|
|
7693a0fe14 | ||
|
|
98c3676fdf | ||
|
|
d7664a9746 | ||
|
|
d868fd6970 | ||
|
|
300396b97e | ||
|
|
1d8faa70a1 | ||
|
|
1051b1bb0e | ||
|
|
c367824041 | ||
|
|
81cedf0d43 | ||
|
|
16c6203b97 |
5
.gitattributes
vendored
5
.gitattributes
vendored
@@ -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
|
||||
|
||||
476
CHANGELOG.md
476
CHANGELOG.md
@@ -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.
|
||||
|
||||
12
Makefile
12
Makefile
@@ -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: ;
|
||||
|
||||
@@ -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`:
|
||||
|
||||
```
|
||||
|
||||
@@ -6,6 +6,13 @@ A custom DartPad that lets you experiment with Thermion from your browser (curre
|
||||
|
||||
[](https://dartpad.thermion.dev)
|
||||
|
||||
## mixreel (Flutter/Web)
|
||||
|
||||
Create 3D worlds and translate to AI video.
|
||||
|
||||
[](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)).
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:14a484a441dacbc49ce6b8b189a8900a7bb2ab2072731a3a493676aa046b5888
|
||||
size 1009
|
||||
@@ -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
53
materials/grid.mat
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
42
materials/unlit.mat
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4abb9e737d20956cced366af6a54dd31ab17cc8d0d1773f02c57a5712760fe10
|
||||
size 7385
|
||||
@@ -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.
|
||||
|
||||
30
thermion_dart/extras/windows/CMakeLists.txt
Normal file
30
thermion_dart/extras/windows/CMakeLists.txt
Normal 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
|
||||
)
|
||||
|
||||
244
thermion_dart/extras/windows/thermion_window.cpp
Normal file
244
thermion_dart/extras/windows/thermion_window.cpp
Normal 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
|
||||
57
thermion_dart/extras/windows/thermion_window.h
Normal file
57
thermion_dart/extras/windows/thermion_window.h
Normal 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
|
||||
@@ -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:
|
||||
- '.*'
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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,
|
||||
|
||||
7
thermion_dart/lib/src/input/input.dart
Normal file
7
thermion_dart/lib/src/input/input.dart
Normal 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';
|
||||
282
thermion_dart/lib/src/input/src/delegate_gesture_handler.dart
Normal file
282
thermion_dart/lib/src/input/src/delegate_gesture_handler.dart
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
27
thermion_dart/lib/src/input/src/delegates.dart
Normal file
27
thermion_dart/lib/src/input/src/delegates.dart
Normal 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);
|
||||
}
|
||||
@@ -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();
|
||||
// }
|
||||
// }
|
||||
@@ -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);
|
||||
// }
|
||||
// }
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
// }
|
||||
// }
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
49
thermion_dart/lib/src/input/src/input_handler.dart
Normal file
49
thermion_dart/lib/src/input/src/input_handler.dart
Normal 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);
|
||||
}
|
||||
234
thermion_dart/lib/src/scene.dart
Normal file
234
thermion_dart/lib/src/scene.dart
Normal 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'],
|
||||
);
|
||||
}
|
||||
}
|
||||
44
thermion_dart/lib/src/utils/src/axis.dart
Normal file
44
thermion_dart/lib/src/utils/src/axis.dart
Normal 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);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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>{};
|
||||
334
thermion_dart/lib/src/utils/src/geometry.dart
Normal file
334
thermion_dart/lib/src/utils/src/geometry.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
127
thermion_dart/lib/src/utils/src/gizmo.dart
Normal file
127
thermion_dart/lib/src/utils/src/gizmo.dart
Normal 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);
|
||||
}
|
||||
38
thermion_dart/lib/src/utils/src/matrix.dart
Normal file
38
thermion_dart/lib/src/utils/src/matrix.dart
Normal 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;
|
||||
}
|
||||
4
thermion_dart/lib/src/utils/utils.dart
Normal file
4
thermion_dart/lib/src/utils/utils.dart
Normal file
@@ -0,0 +1,4 @@
|
||||
library;
|
||||
|
||||
export 'src/geometry.dart';
|
||||
export 'src/axis.dart';
|
||||
85
thermion_dart/lib/src/viewer/src/events.dart
Normal file
85
thermion_dart/lib/src/viewer/src/events.dart
Normal 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!;
|
||||
}
|
||||
}
|
||||
@@ -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 {}
|
||||
91
thermion_dart/lib/src/viewer/src/ffi/src/ffi_camera.dart
Normal file
91
thermion_dart/lib/src/viewer/src/ffi/src/ffi_camera.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
50
thermion_dart/lib/src/viewer/src/ffi/src/ffi_gizmo.dart
Normal file
50
thermion_dart/lib/src/viewer/src/ffi/src/ffi_gizmo.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
69
thermion_dart/lib/src/viewer/src/ffi/src/ffi_view.dart
Normal file
69
thermion_dart/lib/src/viewer/src/ffi/src/ffi_view.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
2332
thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart
Normal file
2332
thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
library;
|
||||
|
||||
export 'src/thermion_viewer_ffi.dart' show ThermionViewerFFI;
|
||||
30
thermion_dart/lib/src/viewer/src/shared_types/camera.dart
Normal file
30
thermion_dart/lib/src/viewer/src/shared_types/camera.dart
Normal 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();
|
||||
}
|
||||
11
thermion_dart/lib/src/viewer/src/shared_types/entities.dart
Normal file
11
thermion_dart/lib/src/viewer/src/shared_types/entities.dart
Normal 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;
|
||||
|
||||
|
||||
32
thermion_dart/lib/src/viewer/src/shared_types/geometry.dart
Normal file
32
thermion_dart/lib/src/viewer/src/shared_types/geometry.dart
Normal 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;
|
||||
}
|
||||
6
thermion_dart/lib/src/viewer/src/shared_types/gltf.dart
Normal file
6
thermion_dart/lib/src/viewer/src/shared_types/gltf.dart
Normal file
@@ -0,0 +1,6 @@
|
||||
class GLTF {
|
||||
final String uri;
|
||||
final int numInstances;
|
||||
|
||||
GLTF(this.uri, this.numInstances);
|
||||
}
|
||||
7
thermion_dart/lib/src/viewer/src/shared_types/light.dart
Normal file
7
thermion_dart/lib/src/viewer/src/shared_types/light.dart
Normal 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,
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
@@ -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 }
|
||||
@@ -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 }
|
||||
@@ -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;
|
||||
10
thermion_dart/lib/src/viewer/src/shared_types/primitive.dart
Normal file
10
thermion_dart/lib/src/viewer/src/shared_types/primitive.dart
Normal 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
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
abstract class RenderTarget {
|
||||
Future destroy();
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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';
|
||||
@@ -0,0 +1,3 @@
|
||||
abstract class SwapChain {
|
||||
Future destroy();
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
abstract class ThermionTexture {
|
||||
|
||||
}
|
||||
@@ -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});
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
enum ToneMapper { ACES, FILMIC, LINEAR }
|
||||
30
thermion_dart/lib/src/viewer/src/shared_types/view.dart
Normal file
30
thermion_dart/lib/src/viewer/src/shared_types/view.dart
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
// }
|
||||
// }
|
||||
1107
thermion_dart/lib/src/viewer/src/web_js/src/thermion_viewer_js.dart
Normal file
1107
thermion_dart/lib/src/viewer/src/web_js/src/thermion_viewer_js.dart
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
@@ -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';
|
||||
77
thermion_dart/lib/src/viewer/src/web_wasm/src/camera.dart
Normal file
77
thermion_dart/lib/src/viewer/src/web_wasm/src/camera.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -0,0 +1,3 @@
|
||||
library;
|
||||
|
||||
export 'src/thermion_viewer_wasm.dart' show ThermionViewerWasm;
|
||||
7
thermion_dart/lib/src/viewer/viewer.dart
Normal file
7
thermion_dart/lib/src/viewer/viewer.dart
Normal 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';
|
||||
@@ -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';
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export 'native/compatibility.dart';
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import 'dart:js_interop';
|
||||
|
||||
@JS()
|
||||
external JSArray createIntCallback();
|
||||
|
||||
@JS()
|
||||
external JSArray createBoolCallback();
|
||||
|
||||
@JS()
|
||||
external JSArray createVoidPointerCallback();
|
||||
|
||||
@JS()
|
||||
external JSArray createVoidCallback();
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
|
||||
|
||||
|
||||
@@ -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 {}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
110
thermion_dart/native/include/APIBoundaryTypes.h
Normal file
110
thermion_dart/native/include/APIBoundaryTypes.h
Normal 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
|
||||
59
thermion_dart/native/include/CustomGeometry.hpp
Normal file
59
thermion_dart/native/include/CustomGeometry.hpp
Normal 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();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
111
thermion_dart/native/include/Gizmo.hpp
Normal file
111
thermion_dart/native/include/Gizmo.hpp
Normal 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;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
52
thermion_dart/native/include/GridOverlay.hpp
Normal file
52
thermion_dart/native/include/GridOverlay.hpp
Normal 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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
using namespace std::chrono_literals;
|
||||
#endif
|
||||
|
||||
namespace thermion_filament
|
||||
namespace thermion
|
||||
{
|
||||
|
||||
struct ResourceLoaderWrapperImpl : public ResourceLoaderWrapper
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user