Compare commits
844 Commits
thermion_d
...
thermion_f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d072594b5 | ||
|
|
75590c5dde | ||
|
|
b52d47bb41 | ||
|
|
397c575631 | ||
|
|
ddc5af376a | ||
|
|
77f843568f | ||
|
|
f706f54eb2 | ||
|
|
c12ec892f3 | ||
|
|
8e88252337 | ||
|
|
23e34e6da4 | ||
|
|
5aa14ff5f6 | ||
|
|
c0cc9fc26a | ||
|
|
946a6ae9d2 | ||
|
|
73e6597301 | ||
|
|
e731556e6f | ||
|
|
f9a7ac49a1 | ||
|
|
83e4a5142a | ||
|
|
bb913644f4 | ||
|
|
4b9870244a | ||
|
|
ce2dc9b1cb | ||
|
|
1a2b3b7b59 | ||
|
|
c0ed7cbc90 | ||
|
|
a5eab305da | ||
|
|
c467e3ca69 | ||
|
|
e984d8cf71 | ||
|
|
0693a2a95b | ||
|
|
a2883fbee9 | ||
|
|
dbcf463fd1 | ||
|
|
6ab1411801 | ||
|
|
a9b5ca786e | ||
|
|
4379313932 | ||
|
|
8a997d533a | ||
|
|
b7e79b1ac5 | ||
|
|
9a5b737391 | ||
|
|
215515a4fc | ||
|
|
3ca1cc0747 | ||
|
|
0c2e4e6faa | ||
|
|
e50a1b6002 | ||
|
|
70c67b59f7 | ||
|
|
2022fa97cc | ||
|
|
809f84e1fa | ||
|
|
932971c872 | ||
|
|
7ed120e854 | ||
|
|
45c4cea6aa | ||
|
|
2545bbbd35 | ||
|
|
5b41d594da | ||
|
|
584abd6086 | ||
|
|
4b9c20eaa6 | ||
|
|
d35a7a6de2 | ||
|
|
0f9d2492e6 | ||
|
|
96ef070be7 | ||
|
|
506d2fb562 | ||
|
|
0b18caf4ff | ||
|
|
b3b0e2bae1 | ||
|
|
35369b890f | ||
|
|
53a2a116aa | ||
|
|
80ceb5988e | ||
|
|
9dc81781d1 | ||
|
|
3e8189ecd4 | ||
|
|
31acdecedf | ||
|
|
feeb757a10 | ||
|
|
1bbf9d26ac | ||
|
|
40e206fc94 | ||
|
|
48b631b0a8 | ||
|
|
5ac4da5075 | ||
|
|
bc392bd933 | ||
|
|
ceba3915a7 | ||
|
|
c46ac2383c | ||
|
|
a9083d9ba0 | ||
|
|
d9436b8141 | ||
|
|
72cc9b932a | ||
|
|
2c7425fbdb | ||
|
|
b5e699c781 | ||
|
|
a7b0ed6f21 | ||
|
|
b5d9a06422 | ||
|
|
6b9b13682b | ||
|
|
fab3240ecf | ||
|
|
2613ba2423 | ||
|
|
85e3c98b28 | ||
|
|
9d4802c270 | ||
|
|
e7227d5f91 | ||
|
|
ccc0a53abc | ||
|
|
b983c6bb90 | ||
|
|
854f1545d3 | ||
|
|
ca110b2b01 | ||
|
|
ced0667a21 | ||
|
|
240a2d5345 | ||
|
|
cb4479d8ce | ||
|
|
f4b473b9ae | ||
|
|
fac0c727e4 | ||
|
|
f0cf20d904 | ||
|
|
ebb73a29e1 | ||
|
|
e92e59fab4 | ||
|
|
be47ec0f8b | ||
|
|
3657428025 | ||
|
|
33f8faac04 | ||
|
|
e9f0d5b745 | ||
|
|
682881e133 | ||
|
|
3e2405aa76 | ||
|
|
52038084f3 | ||
|
|
af9c2a93dc | ||
|
|
e99ccb3100 | ||
|
|
ed21eec66e | ||
|
|
05c62d5fa0 | ||
|
|
bec34e5b0b | ||
|
|
634c6d6877 | ||
|
|
b47e7f206c | ||
|
|
1fe1889fa2 | ||
|
|
4a43c6169a | ||
|
|
6075608bda | ||
|
|
8a1f320bb7 | ||
|
|
ed5fb03aa4 | ||
|
|
21ccb52ab9 | ||
|
|
ff48ce94a3 | ||
|
|
d20ff0a6f1 | ||
|
|
c7d4d9cb33 | ||
|
|
9aec83fd70 | ||
|
|
4dbd761d16 | ||
|
|
fc365744ef | ||
|
|
900e43b5ec | ||
|
|
c78cd45b8a | ||
|
|
1268970ba7 | ||
|
|
f93e44abd5 | ||
|
|
d41f29c06d | ||
|
|
e27be37378 | ||
|
|
a8a7f11d44 | ||
|
|
c81f2f225c | ||
|
|
a89c6d0eb3 | ||
|
|
34c9d5a279 | ||
|
|
57999bec3c | ||
|
|
69400f8b68 | ||
|
|
18b6b2e5f0 | ||
|
|
7e802f7b23 | ||
|
|
ce23b9f3cd | ||
|
|
45d8c7b929 | ||
|
|
7a484ee794 | ||
|
|
832319ee1b | ||
|
|
668c001b49 | ||
|
|
e503dd2e8b | ||
|
|
e1c34cc4d9 | ||
|
|
b035d45ada | ||
|
|
75d34f0afb | ||
|
|
d053bfd6da | ||
|
|
920f81c514 | ||
|
|
b64a505ffc | ||
|
|
00debf509d | ||
|
|
73b32f9c1e | ||
|
|
abd1a1fd55 | ||
|
|
e8721b6133 | ||
|
|
a01bf36576 | ||
|
|
5f92c7feab | ||
|
|
e1cb517417 | ||
|
|
37c35906d6 | ||
|
|
355999562f | ||
|
|
c208972ed7 | ||
|
|
6a7ca3cd42 | ||
|
|
b6d869275a | ||
|
|
64eeacc029 | ||
|
|
3783081456 | ||
|
|
0a9b1a82e2 | ||
|
|
9322122dac | ||
|
|
a823718bcd | ||
|
|
7446b0545a | ||
|
|
3c4bedd43b | ||
|
|
21e8cf0d11 | ||
|
|
86e0f7740f | ||
|
|
a5a3c73708 | ||
|
|
a44cb9a9cc | ||
|
|
6f97bf3993 | ||
|
|
42f9538040 | ||
|
|
df393b075b | ||
|
|
13f9a8370b | ||
|
|
558978a0f2 | ||
|
|
a29b194612 | ||
|
|
3e04bf427f | ||
|
|
65ca59ff22 | ||
|
|
a91aaae131 | ||
|
|
71bae61015 | ||
|
|
288acb019e | ||
|
|
b9354531b2 | ||
|
|
388b8556e5 | ||
|
|
59d9d87d2f | ||
|
|
d5824466f7 | ||
|
|
3e11f09d63 | ||
|
|
6bbab332be | ||
|
|
85b86c5f13 | ||
|
|
d8652e8709 | ||
|
|
fe5279f942 | ||
|
|
828354abe5 | ||
|
|
48d9d6751b | ||
|
|
4bde3d34cf | ||
|
|
7f2ad7c4e4 | ||
|
|
47807d560f | ||
|
|
ad26fc4563 | ||
|
|
f252c86152 | ||
|
|
e5bcde3ade | ||
|
|
9436666d34 | ||
|
|
32fe54ac09 | ||
|
|
0461bb364a | ||
|
|
f78c6f258f | ||
|
|
b0796f493f | ||
|
|
8998f1f740 | ||
|
|
6362efcd61 | ||
|
|
dc5c1161cc | ||
|
|
3e6f95e0bf | ||
|
|
f053c2f852 | ||
|
|
8f1da5e7bd | ||
|
|
641d92515e | ||
|
|
d447fb8e2a | ||
|
|
ac87420a87 | ||
|
|
5669ac5c55 | ||
|
|
ae9ce197c1 | ||
|
|
37c67c2da8 | ||
|
|
66d3bf65fd | ||
|
|
dba40fb82b | ||
|
|
95db6d7ebb | ||
|
|
fef48940fc | ||
|
|
4086c9bd01 | ||
|
|
928517f80f | ||
|
|
4a9b91c411 | ||
|
|
9054879722 | ||
|
|
4c95bc5b70 | ||
|
|
8866ffe210 | ||
|
|
c33eaef713 | ||
|
|
78e14574ef | ||
|
|
073976b40e | ||
|
|
6d0844f873 | ||
|
|
983d1373ba | ||
|
|
0dd64a07bb | ||
|
|
6055bd3fa7 | ||
|
|
f3a576005a | ||
|
|
ff559997dd | ||
|
|
79fea25d1b | ||
|
|
9ae7c784df | ||
|
|
acf03ea0c7 | ||
|
|
a9553da1d4 | ||
|
|
fc4686b20f | ||
|
|
ab4743da90 | ||
|
|
7406662853 | ||
|
|
aa2f19442b | ||
|
|
cc99905eb8 | ||
|
|
1d697037c0 | ||
|
|
c024efb8cd | ||
|
|
150c0227c8 | ||
|
|
e594a42496 | ||
|
|
c484eeb37f | ||
|
|
655ba89b0f | ||
|
|
840f3599e7 | ||
|
|
d0da7f49f3 | ||
|
|
886a4a37aa | ||
|
|
6db82d00c2 | ||
|
|
0fb4fd0004 | ||
|
|
e147e59d8d | ||
|
|
28aef7e5cc | ||
|
|
b1617004d7 | ||
|
|
32b6b1c227 | ||
|
|
1c500fe925 | ||
|
|
3fd15ab91b | ||
|
|
2191f9d44b | ||
|
|
87b63301f2 | ||
|
|
0d5b42c908 | ||
|
|
12e2dca873 | ||
|
|
8eae6bf90c | ||
|
|
873244879f | ||
|
|
2ab1732b6b | ||
|
|
5eb83a80b3 | ||
|
|
7141a01101 | ||
|
|
24aaffda9c | ||
|
|
82aba605f9 | ||
|
|
7b80723560 | ||
|
|
39a302c23e | ||
|
|
059926fe98 | ||
|
|
3c9b607109 | ||
|
|
70e2933c4b | ||
|
|
4fb85eb45a | ||
|
|
afecacfcf0 | ||
|
|
2f7f363db9 | ||
|
|
7f0ec01937 | ||
|
|
ccdf2ecda6 | ||
|
|
5f9a7bb959 | ||
|
|
764fea836b | ||
|
|
27d6bd0f4a | ||
|
|
7e810b8265 | ||
|
|
6ad3bfab13 | ||
|
|
5ed00164a9 | ||
|
|
86894cf583 | ||
|
|
c789e75af5 | ||
|
|
6ad76b6217 | ||
|
|
2e097f7ec6 | ||
|
|
beeb45609c | ||
|
|
94eacec27e | ||
|
|
11f7ac459b | ||
|
|
afd47f9b24 | ||
|
|
aab2b0b769 | ||
|
|
ffc7717149 | ||
|
|
ff5921a505 | ||
|
|
45c7831a17 | ||
|
|
fcf57e8850 | ||
|
|
a875f370e5 | ||
|
|
b00f37ddaa | ||
|
|
a98d7550ae | ||
|
|
b12dc80cfd | ||
|
|
f7d6d4786a | ||
|
|
5db9fe2c9e | ||
|
|
d298b0d091 | ||
|
|
4a7a366fc6 | ||
|
|
c6ceea4f8b | ||
|
|
679af95901 | ||
|
|
1e812ebe93 | ||
|
|
75a9950909 | ||
|
|
81fb0fb583 | ||
|
|
4d6c008299 | ||
|
|
4505cbe325 | ||
|
|
60cfe32672 | ||
|
|
0decb6b9f1 | ||
|
|
d799ef0ca0 | ||
|
|
5a72bc835d | ||
|
|
3e44716e8c | ||
|
|
bf295394a8 | ||
|
|
64bc7f8bf3 | ||
|
|
63ba231996 | ||
|
|
562c221461 | ||
|
|
f123bbf930 | ||
|
|
1765350cbe | ||
|
|
94683ab5a9 | ||
|
|
5a74345fe1 | ||
|
|
195a625738 | ||
|
|
725f36a860 | ||
|
|
51212bdeed | ||
|
|
96694be017 | ||
|
|
a31f11151e | ||
|
|
10136c8cde | ||
|
|
c712804bb1 | ||
|
|
1373ce5a32 | ||
|
|
1b3d46533c | ||
|
|
5733bc125b | ||
|
|
943de8f77b | ||
|
|
9a4fd1141e | ||
|
|
2bf77f123f | ||
|
|
9785576866 | ||
|
|
3007dec127 | ||
|
|
1008452198 | ||
|
|
1f58c13179 | ||
|
|
5ce2efa638 | ||
|
|
e75805ba06 | ||
|
|
73f04eb0d4 | ||
|
|
097d991ea8 | ||
|
|
5cf494def9 | ||
|
|
23d6e3a819 | ||
|
|
9a6bacc73f | ||
|
|
e432b0be1d | ||
|
|
2dad04a354 | ||
|
|
1fb68b20e9 | ||
|
|
d61723dee2 | ||
|
|
1afd5ff257 | ||
|
|
04b8b333d4 | ||
|
|
fdd3853121 | ||
|
|
bdba92c842 | ||
|
|
66f3e20489 | ||
|
|
871a327d99 | ||
|
|
c53266f12f | ||
|
|
11ff6c9053 | ||
|
|
d392daa2e6 | ||
|
|
fc75aab94b | ||
|
|
760ae8ed8b | ||
|
|
25ada92574 | ||
|
|
12c8031f2b | ||
|
|
8c78e8e629 | ||
|
|
dc5cdcb7e1 | ||
|
|
383fc221d9 | ||
|
|
e4942bad2f | ||
|
|
460000308a | ||
|
|
826a22f348 | ||
|
|
43442e10de | ||
|
|
b710e0ace3 | ||
|
|
b0087d8d54 | ||
|
|
dcef1a5749 | ||
|
|
1c0a7f6b1e | ||
|
|
2444e9a980 | ||
|
|
417ee4648f | ||
|
|
4b2342979f | ||
|
|
ee306549d8 | ||
|
|
d3ec825688 | ||
|
|
fe11479b08 | ||
|
|
c98e604e76 | ||
|
|
f9d09e17ef | ||
|
|
27eb117466 | ||
|
|
46e87fb6a4 | ||
|
|
6d95180870 | ||
|
|
c7ad70b81c | ||
|
|
62bf3876f8 | ||
|
|
cff0893d53 | ||
|
|
6135c5eecc | ||
|
|
170fb4bf6d | ||
|
|
2f5ce65684 | ||
|
|
bb272f2579 | ||
|
|
56e132ec15 | ||
|
|
5379ae7d74 | ||
|
|
9d7e96e996 | ||
|
|
8bbe2f0dfb | ||
|
|
0794e0ad84 | ||
|
|
3f0f15b441 | ||
|
|
90f17e3bc9 | ||
|
|
8311c95100 | ||
|
|
97da08ae8b | ||
|
|
12a96cceee | ||
|
|
fac7e9a894 | ||
|
|
c47cfe9b57 | ||
|
|
671949a5cd | ||
|
|
602e220024 | ||
|
|
ebab1f528d | ||
|
|
4a5a1d0157 | ||
|
|
906f5bf931 | ||
|
|
9ef6d46958 | ||
|
|
50c1c871a8 | ||
|
|
8a6ba637a7 | ||
|
|
abde201bda | ||
|
|
428a613b65 | ||
|
|
1ddeac2d31 | ||
|
|
7961ed06f7 | ||
|
|
cedcb45484 | ||
|
|
cf6abdcf01 | ||
|
|
04d3aee2e9 | ||
|
|
5af18cb99b | ||
|
|
87396ec859 | ||
|
|
2f16908992 | ||
|
|
63e2dcd0ca | ||
|
|
afd8bff58e | ||
|
|
2ab56b86bc | ||
|
|
e9628511fc | ||
|
|
3402c02d6a | ||
|
|
fdf278cd79 | ||
|
|
e5b75debed | ||
|
|
eedfbbb934 | ||
|
|
2a5a149e83 | ||
|
|
4cb725d47e | ||
|
|
8228486ef3 | ||
|
|
c66dc40bab | ||
|
|
4f1a9fb0d5 | ||
|
|
454ec14b40 | ||
|
|
b4f3d865b4 | ||
|
|
86779cb629 | ||
|
|
e1d5d14ff7 | ||
|
|
1be1979c05 | ||
|
|
2b6693a826 | ||
|
|
7d369ad813 | ||
|
|
81a28a2f2e | ||
|
|
6b008bd433 | ||
|
|
241c0fe07c | ||
|
|
12b6a8c8a5 | ||
|
|
426d6c4ed6 | ||
|
|
4002425c0d | ||
|
|
230bb5f9f6 | ||
|
|
8a35060653 | ||
|
|
2b00d374e0 | ||
|
|
da87442b3a | ||
|
|
3c68731fe0 | ||
|
|
ef775b2e2c | ||
|
|
0759590f4c | ||
|
|
e7092c416b | ||
|
|
f272410803 | ||
|
|
b128f385e5 | ||
|
|
a4227df098 | ||
|
|
230af0ae17 | ||
|
|
aeb644cf16 | ||
|
|
c3e583ac5a | ||
|
|
48654f0e8f | ||
|
|
a772b86f09 | ||
|
|
026893d2fe | ||
|
|
2d4342607d | ||
|
|
02b6bc4ee6 | ||
|
|
0194eb5cd5 | ||
|
|
9ceedb4e1c | ||
|
|
85a0451f1b | ||
|
|
dfda0eb476 | ||
|
|
b160932ff3 | ||
|
|
f9c5ee5896 | ||
|
|
5097373a22 | ||
|
|
10dd66c7f8 | ||
|
|
b6e5d2c834 | ||
|
|
726f9b8c80 | ||
|
|
7349fa2298 | ||
|
|
143c4a436d | ||
|
|
60e025e78d | ||
|
|
de5bc5c02e | ||
|
|
a1ab4186b4 | ||
|
|
d3b9ae45cf | ||
|
|
7dfb8a4b19 | ||
|
|
2bd64805a9 | ||
|
|
b17b732c55 | ||
|
|
8157f3318b | ||
|
|
fbbb4ed792 | ||
|
|
ec3e7ed3ba | ||
|
|
585e0ad087 | ||
|
|
4815b0309e | ||
|
|
9d593b8c3b | ||
|
|
aaa74f0267 | ||
|
|
cf8e0f6ed2 | ||
|
|
303174117f | ||
|
|
ccda475073 | ||
|
|
ddba3c35ab | ||
|
|
87d04fc2c1 | ||
|
|
a3f05e353d | ||
|
|
69ce7b1a15 | ||
|
|
3922ee536b | ||
|
|
cf372f4dc2 | ||
|
|
854b142973 | ||
|
|
7d8f7a726b | ||
|
|
e0b2e1c3b9 | ||
|
|
f7a7b6bedf | ||
|
|
7b0550c713 | ||
|
|
ec8248e10c | ||
|
|
374d2e5697 | ||
|
|
e407e75389 | ||
|
|
342ff58cbe | ||
|
|
68842fb4ce | ||
|
|
88f229481d | ||
|
|
09b2a924af | ||
|
|
06715f0618 | ||
|
|
38d190f582 | ||
|
|
73cb20a2dd | ||
|
|
a08cc7aa62 | ||
|
|
9d4fd4d6d4 | ||
|
|
e8aa68beb4 | ||
|
|
178da458ad | ||
|
|
508c184f1a | ||
|
|
a8cf071f2f | ||
|
|
89768c9303 | ||
|
|
a31142de6d | ||
|
|
66ddcac412 | ||
|
|
645ba97c1d | ||
|
|
a26eacb247 | ||
|
|
667397ad83 | ||
|
|
99217499f9 | ||
|
|
b083c62bd4 | ||
|
|
d0b297b015 | ||
|
|
ddc5fedc4d | ||
|
|
968e2e508d | ||
|
|
7c936547aa | ||
|
|
4ac1b5329c | ||
|
|
bf44ca7ddd | ||
|
|
e25d7805d9 | ||
|
|
5aaf212d9a | ||
|
|
99bd993f25 | ||
|
|
a153bc9916 | ||
|
|
77ff8ff4c3 | ||
|
|
709fe35852 | ||
|
|
ecb8d8672a | ||
|
|
a79e1a86f8 | ||
|
|
4bdbb170a7 | ||
|
|
7960b55dbd | ||
|
|
77465baaf4 | ||
|
|
dcf13fd53f | ||
|
|
973804ed14 | ||
|
|
9978d9d75c | ||
|
|
e4f436fab1 | ||
|
|
ac5b5b89f6 | ||
|
|
b69977929c | ||
|
|
548dccf776 | ||
|
|
b4e4eb349c | ||
|
|
7a5fdb0b77 | ||
|
|
51e51db229 | ||
|
|
faba1b3087 | ||
|
|
123543a985 | ||
|
|
cd9332063c | ||
|
|
54a0a0befb | ||
|
|
289749115b | ||
|
|
cd1c2f3827 | ||
|
|
e8169e86d1 | ||
|
|
cac063324d | ||
|
|
aebaa8425c | ||
|
|
1b902e36f4 | ||
|
|
396b7ed6dc | ||
|
|
d6666d6388 | ||
|
|
7d5b183dea | ||
|
|
6c25a3c405 | ||
|
|
999b1e613f | ||
|
|
0cbbc058e0 | ||
|
|
a67f42f0de | ||
|
|
4ef74c4c70 | ||
|
|
255c0edd49 | ||
|
|
1177a71f73 | ||
|
|
e6bdcb687a | ||
|
|
cbff4cd805 | ||
|
|
a8a2f14b34 | ||
|
|
931aab5159 | ||
|
|
a310498c5a | ||
|
|
6744c02019 | ||
|
|
124f923720 | ||
|
|
e2048c3efd | ||
|
|
ac86dc7ad7 | ||
|
|
e73cb9c7d6 | ||
|
|
102429e090 | ||
|
|
627447f8b0 | ||
|
|
d5bffd5ad2 | ||
|
|
951f5daa2d | ||
|
|
77fe40848b | ||
|
|
07b80071a4 | ||
|
|
03f7764da2 | ||
|
|
231b03cd17 | ||
|
|
b271b7e433 | ||
|
|
746ef07e42 | ||
|
|
bff76f184e | ||
|
|
2244359edd | ||
|
|
b94edf0951 | ||
|
|
988e22fe95 | ||
|
|
401048e6a8 | ||
|
|
232289003e | ||
|
|
e18d1246f6 | ||
|
|
95c9e5be66 | ||
|
|
e1203df087 | ||
|
|
09678c6cdc | ||
|
|
f2ce4a4044 | ||
|
|
c23b991c9c | ||
|
|
4205b86f13 | ||
|
|
d710ab810c | ||
|
|
4e5a9be97a | ||
|
|
342a53ae7b | ||
|
|
a0e1971958 | ||
|
|
74502ba5b3 | ||
|
|
09b6aca08f | ||
|
|
03c4afb031 | ||
|
|
f923b94b84 | ||
|
|
20ea43a809 | ||
|
|
8b34934755 | ||
|
|
5ad187d54f | ||
|
|
711abe11bd | ||
|
|
a16c3c7580 | ||
|
|
e1446c7eb8 | ||
|
|
1f4af8c946 | ||
|
|
75bee32e54 | ||
|
|
1d49db4767 | ||
|
|
d39b57f054 | ||
|
|
e91d1ca0aa | ||
|
|
7d87d229d0 | ||
|
|
7ed7ff48b9 | ||
|
|
9d311220b1 | ||
|
|
e4de0f3ae0 | ||
|
|
72711ee64f | ||
|
|
c52052c1b9 | ||
|
|
aa26f4d0df | ||
|
|
be61d5e5ca | ||
|
|
1a3d902d23 | ||
|
|
35bddd2bb2 | ||
|
|
74ee35bfcd | ||
|
|
2915655695 | ||
|
|
5b80dd3a3d | ||
|
|
593bca3365 | ||
|
|
cca9fba79f | ||
|
|
a245cf2915 | ||
|
|
15134c335b | ||
|
|
85215f6760 | ||
|
|
9abb192148 | ||
|
|
f7fa02180a | ||
|
|
fc7f5d7b93 | ||
|
|
5dca42c3c1 | ||
|
|
665c2fb39d | ||
|
|
3ac79b2080 | ||
|
|
8d52690c97 | ||
|
|
2a8931e1ac | ||
|
|
94d77ff21b | ||
|
|
7a707cda67 | ||
|
|
8993d888b1 | ||
|
|
ffc256228a | ||
|
|
1b1de0b7c0 | ||
|
|
4c1480864e | ||
|
|
06cc114daa | ||
|
|
1258751dda | ||
|
|
67ca24a7d9 | ||
|
|
282ee6d103 | ||
|
|
bb5a8f495d | ||
|
|
c400ca3993 | ||
|
|
d7d7fa7c0b | ||
|
|
c1be084e3e | ||
|
|
178e59a618 | ||
|
|
7fe9c06a7f | ||
|
|
0f4531d3d7 | ||
|
|
c5228dff7e | ||
|
|
7131e7ab86 | ||
|
|
16ff7ef008 | ||
|
|
a178035796 | ||
|
|
61abed2fb7 | ||
|
|
30060578ef | ||
|
|
2c2beb0e76 | ||
|
|
2b864f6f44 | ||
|
|
0b1da2a15d | ||
|
|
ad70ef1461 | ||
|
|
6f110a56dd | ||
|
|
0e5253f7b3 | ||
|
|
124eed0264 | ||
|
|
9a055174e6 | ||
|
|
3a231697e5 | ||
|
|
3e74c8faea | ||
|
|
18b0bf19b9 | ||
|
|
020bfbcbf6 | ||
|
|
b1c0d4b2e8 | ||
|
|
6cefe44c64 | ||
|
|
4aa8889834 | ||
|
|
aee9c767dd | ||
|
|
916ecd5629 | ||
|
|
d3a6f6e22a | ||
|
|
86ecd53de7 | ||
|
|
8653ca8398 | ||
|
|
1d615e7568 | ||
|
|
4cb118ad65 | ||
|
|
4e64c4976f | ||
|
|
04adbc39e0 | ||
|
|
744e39e8dd | ||
|
|
baf86d1ade | ||
|
|
3e181b6aff | ||
|
|
8e0ba8ac4e | ||
|
|
3e39aa3630 | ||
|
|
cc1b55b6c9 | ||
|
|
242041c9f6 | ||
|
|
7717387909 | ||
|
|
31e453a4e6 | ||
|
|
9b988537e0 | ||
|
|
d9338f4341 | ||
|
|
b6397b5f73 | ||
|
|
584ace23b4 | ||
|
|
5e89dc43e8 | ||
|
|
024643e3a1 | ||
|
|
96fe2c89ea | ||
|
|
8d8acef481 | ||
|
|
51bdca7158 | ||
|
|
658984e784 | ||
|
|
f8ae90b631 | ||
|
|
b158062ce8 | ||
|
|
dc690bb93a | ||
|
|
342264eba9 | ||
|
|
453ce9d1ea | ||
|
|
e9c14139c2 | ||
|
|
d92d47faa5 | ||
|
|
b6bde0c9d9 | ||
|
|
f873f7da66 | ||
|
|
aabea985bd | ||
|
|
9f9b34f662 | ||
|
|
fc4842ed6d | ||
|
|
eb4e70a7cf | ||
|
|
19ffa91164 | ||
|
|
a519701510 | ||
|
|
5fb5e42b98 | ||
|
|
ba3636a203 | ||
|
|
e0b4ebc349 | ||
|
|
1d1a3cbfe6 | ||
|
|
7aa0367a3b | ||
|
|
0817efc33e | ||
|
|
0facb3f906 | ||
|
|
355c28477b | ||
|
|
c1a294315a | ||
|
|
f27628e719 | ||
|
|
fd1faa6023 | ||
|
|
8fa43e5306 | ||
|
|
94863134fc | ||
|
|
1b979252db | ||
|
|
852cb58ba9 | ||
|
|
7cf1468f38 | ||
|
|
62cd85c148 | ||
|
|
771f851784 | ||
|
|
def85614d8 | ||
|
|
0ad73d06e0 | ||
|
|
6c236da675 | ||
|
|
6b883f3352 | ||
|
|
ffa0f77a42 | ||
|
|
26ae21910f | ||
|
|
5c3af255e4 | ||
|
|
29c35f9037 | ||
|
|
1e1d6fdcc9 | ||
|
|
113f769160 | ||
|
|
278575c3e2 | ||
|
|
27e150ebf6 | ||
|
|
fe0f63c4cb | ||
|
|
b94aeb8b4a | ||
|
|
8b9b299786 | ||
|
|
6ce743057b | ||
|
|
fbb53f9033 | ||
|
|
8a894f7e88 | ||
|
|
5e88a710ae | ||
|
|
6b36d67ed7 | ||
|
|
5564be9ce9 | ||
|
|
9f676f42b9 | ||
|
|
981fd63963 | ||
|
|
cb9320f3a1 | ||
|
|
6d5bc305dc | ||
|
|
1c5b5c890b | ||
|
|
e43e1c9cbd | ||
|
|
5fe985d2c7 | ||
|
|
80ca4b97a9 | ||
|
|
d3b4bed654 | ||
|
|
c117555b2c | ||
|
|
abbf2d5391 | ||
|
|
a31c800204 | ||
|
|
877e8def80 | ||
|
|
fb650dbd52 | ||
|
|
c34c7d5545 | ||
|
|
fe37d45e1a | ||
|
|
bf3d4f7c0e | ||
|
|
69643e26f6 | ||
|
|
44b502a1e4 | ||
|
|
6d36dc7923 | ||
|
|
151935913f | ||
|
|
d72ed58436 | ||
|
|
d53a8b20b8 | ||
|
|
600ab1ad28 | ||
|
|
aa7350c419 | ||
|
|
c2077cb6b1 | ||
|
|
bedd50ec38 | ||
|
|
8a3525d879 | ||
|
|
853f1d1a68 | ||
|
|
7e50f0317e | ||
|
|
724d7532e2 | ||
|
|
45a548afd2 | ||
|
|
e8b4b7806b | ||
|
|
133367669d | ||
|
|
60a09a1cd9 | ||
|
|
9128fbbd61 | ||
|
|
c3c6465908 | ||
|
|
ed444b0615 | ||
|
|
9ada6aae64 | ||
|
|
0ef0f37bbf | ||
|
|
df37f5e95f | ||
|
|
468cada6c6 | ||
|
|
e60ab51b79 | ||
|
|
d58a48061c | ||
|
|
cabb62043d | ||
|
|
b7bc46b94a | ||
|
|
bc8edb7eb4 | ||
|
|
5deb2ad7cf | ||
|
|
dcc523edff | ||
|
|
26eba7ae09 | ||
|
|
9692c07da9 | ||
|
|
2860db3fbd | ||
|
|
1ce5bd3bcf | ||
|
|
9d3f87218e | ||
|
|
505b4bb156 | ||
|
|
25320f45af | ||
|
|
0b9f3160d9 | ||
|
|
bb27f40cb2 | ||
|
|
9853d6b721 | ||
|
|
52f041afba | ||
|
|
646814126f | ||
|
|
05cb4325da | ||
|
|
e8b3468a3a | ||
|
|
741607b78b |
29
.github/actions/setup-build-env/action.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: 'Setup Build Environment'
|
||||
description: 'Set up LLVM, MESA, and Flutter for builds'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup LLVM
|
||||
uses: KyleMayes/install-llvm-action@v1
|
||||
with:
|
||||
version: "16"
|
||||
- name: Add LLVM to PATH
|
||||
shell: bash
|
||||
run: |
|
||||
echo "${{ github.workspace }}/llvm/bin" >> $GITHUB_PATH
|
||||
echo "LD_LIBRARY_PATH=${{ github.workspace }}/llvm/lib/x86_64-unknown-linux-gnu/" >> $GITHUB_ENV
|
||||
- name: Verify installation
|
||||
shell: bash
|
||||
run: clang --version
|
||||
- name: Install MESA
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y xvfb
|
||||
sudo apt-get install -y libosmesa6-dev
|
||||
- name: Set up Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: master
|
||||
architecture: X64
|
||||
184
.github/workflows/dart.yml
vendored
@@ -1,7 +1,3 @@
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
name: Dart
|
||||
|
||||
on:
|
||||
@@ -11,72 +7,164 @@ on:
|
||||
branches: [ "develop" ]
|
||||
|
||||
jobs:
|
||||
thermion_dart:
|
||||
name: thermion_dart
|
||||
runs-on: self-hosted
|
||||
defaults:
|
||||
run:
|
||||
working-directory: thermion_dart # Adjust this path
|
||||
|
||||
dart-tests:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install dependencies
|
||||
run: flutter pub get # even though this is a Dart package, it has as dev_dependency on objective_c for testing which for some reason has a Flutter dependency
|
||||
|
||||
# Uncomment this step to verify the use of 'dart format' on each commit.
|
||||
# - name: Verify formatting
|
||||
# run: dart format --output=none --set-exit-if-changed .
|
||||
|
||||
#- name: Analyze project source
|
||||
# run: dart analyze
|
||||
|
||||
- name: Build and Test
|
||||
shell: cmd
|
||||
run: dart --enable-experiment=native-assets test
|
||||
|
||||
# Upload logs on failure
|
||||
- uses: ./.github/actions/setup-build-env
|
||||
- run: |
|
||||
cd thermion_dart
|
||||
dart pub get
|
||||
xvfb-run dart --enable-experiment=native-assets test \
|
||||
test/asset_tests.dart \
|
||||
test/instancing_tests.dart \
|
||||
test/light_tests.dart \
|
||||
test/entity_tests.dart \
|
||||
test/geometry_tests.dart \
|
||||
test/view_tests.dart \
|
||||
test/postprocessing_tests.dart \
|
||||
test/scene_tests.dart \
|
||||
test/picking_tests.dart \
|
||||
--concurrency=1
|
||||
#test/overlay_tests.dart \
|
||||
- name: Zip output
|
||||
run: zip -r output.zip ./thermion_dart/test/output
|
||||
- name: Upload test output
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: golden-images-${{ github.sha }}
|
||||
path: output.zip
|
||||
- name: Download golden images from previous run
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh run download 15990504915 \
|
||||
--name golden-images-4b9870244a529bf086280331d38c55e294425b4f \
|
||||
--dir ./thermion_dart/test/golden-downloads
|
||||
- name: Unzip golden images
|
||||
run: |
|
||||
cd thermion_dart/test/golden-downloads && unzip output.zip
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install Pillow numpy
|
||||
- name: Compare golden images
|
||||
run: cd thermion_dart/test && python compare_goldens.py
|
||||
- name: Upload logs
|
||||
if: failure() || steps.build.outcome == 'failure'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-logs
|
||||
path: |
|
||||
D:\a\thermion\thermion\thermion_dart\.dart_tool\thermion_dart\log\build.log
|
||||
# /Users/runner/work/thermion/thermion/thermion_dart//.dart_tool/thermion_dart/log/build.log
|
||||
${{ github.workspace }}/thermion_dart/.dart_tool/thermion_dart/log/build.log
|
||||
retention-days: 5
|
||||
|
||||
# Capture crash dumps if they exist
|
||||
- name: Collect crash dumps
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v3
|
||||
flutter_examples:
|
||||
name: flutter_examples
|
||||
runs-on: macos-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: examples/flutter
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
name: crash-dumps
|
||||
channel: master
|
||||
architecture: ARM64
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
- name: Install Android SDK
|
||||
uses: android-actions/setup-android@v3
|
||||
with:
|
||||
api-level: 34
|
||||
build-tools: 34.0.0
|
||||
cmake-version: 3.22.1
|
||||
ndk-version: 25.1.893739
|
||||
- name: Accept Android SDK licenses
|
||||
run: |
|
||||
yes | $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager --licenses
|
||||
- run: cd quickstart && flutter pub get && flutter build macos
|
||||
# - run: cd quickstart && flutter pub get && flutter build ios
|
||||
- run: cd quickstart && flutter pub get && flutter build appbundle
|
||||
- run: cd quickstart && flutter build web
|
||||
- run: cd picking && flutter pub get && flutter build macos
|
||||
# - run: cd picking && flutter pub get && flutter build ios
|
||||
- run: cd picking && flutter pub get && flutter build appbundle
|
||||
- run: cd picking && flutter build web
|
||||
- name: Upload logs
|
||||
if: failure() || steps.build.outcome == 'failure'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-logs
|
||||
path: |
|
||||
${{ runner.temp }}/*.dmp
|
||||
${{ env.LOCALAPPDATA }}/Temp/*.dmp
|
||||
${{ env.LOCALAPPDATA }}/CrashDumps/*.dmp
|
||||
# D:\a\thermion\thermion\thermion_dart\.dart_tool\thermion_dart\log\build.log
|
||||
/Users/runner/work/thermion/thermion/thermion_dart/.dart_tool/thermion_dart/log/build.log
|
||||
retention-days: 5
|
||||
|
||||
|
||||
# thermion_dart:
|
||||
# name: thermion_dart
|
||||
# runs-on: macos-latest
|
||||
# defaults:
|
||||
# run:
|
||||
# working-directory: thermion_dart
|
||||
# steps:
|
||||
# - uses: actions/checkout@v4
|
||||
# - name: Set up Flutter
|
||||
# uses: subosito/flutter-action@v2
|
||||
# with:
|
||||
# channel: master
|
||||
# - run: flutter pub get
|
||||
# - run: dart --enable-experiment=native-assets test -j1 test/light_tests.dart
|
||||
# thermion_flutter:
|
||||
# name: thermion_flutter
|
||||
# runs-on: macos-13
|
||||
# defaults:
|
||||
# run:
|
||||
# working-directory: thermion_flutter/thermion_flutter # Adjust this path
|
||||
|
||||
# steps:
|
||||
# - uses: actions/checkout@v4
|
||||
|
||||
# - uses: dart-lang/setup-dart@9a04e6d73cca37bd455e0608d7e5092f881fd603
|
||||
|
||||
# - name: Install dependencies
|
||||
# run: dart pub get
|
||||
#
|
||||
#
|
||||
##- uses: actions/checkout@v4
|
||||
#- name: Setup LLVM
|
||||
# uses: KyleMayes/install-llvm-action@v1
|
||||
# with:
|
||||
# version: "16"
|
||||
#- name: Add LLVM to PATH
|
||||
# run: |
|
||||
# echo "${{ github.workspace }}/llvm/bin" >> $GITHUB_PATH
|
||||
# echo "LD_LIBRARY_PATH=${{ github.workspace }}/llvm/lib/x86_64-unknown-linux-gnu/" >> $GITHUB_ENV
|
||||
#- name: Verify installation
|
||||
# run: clang --version
|
||||
#- name: Install MESA
|
||||
# run: |
|
||||
# sudo apt-get update
|
||||
# sudo apt-get install -y xvfb
|
||||
# sudo apt-get install -y libosmesa6-dev
|
||||
# #sudo apt-get -y build-dep mesa
|
||||
# #git clone https://gitlab.freedesktop.org/mesa/mesa.git
|
||||
# #cd mesa
|
||||
# #git checkout mesa-23.2.1
|
||||
# #mkdir -p out
|
||||
# #meson setup builddir/ -Dprefix="$(pwd)/out" -Dosmesa=true -Dglx=xlib -Dgallium-drivers=swrast -Dvulkan-drivers=swrast
|
||||
# #meson install -C builddir/
|
||||
#- name: Install Vulkan SDK and dependencies
|
||||
# run: |
|
||||
# wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add -
|
||||
# sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-jammy.list https://packages.lunarg.com/vulkan/lunarg-vulkan-jammy.list
|
||||
# sudo apt-get update
|
||||
# sudo apt-get install -y vulkan-sdk
|
||||
#- name: Set up Flutter
|
||||
# uses: subosito/flutter-action@v2
|
||||
# with:
|
||||
# channel: master
|
||||
# architecture: X64
|
||||
|
||||
# # Uncomment this step to verify the use of 'dart format' on each commit.
|
||||
# # - name: Verify formatting
|
||||
# # run: dart format --output=none --set-exit-if-changed .
|
||||
|
||||
# - name: Analyze project source
|
||||
# run: dart analyze
|
||||
|
||||
# - name: Run tests
|
||||
# run: dart --enable-experiment=native-assets test
|
||||
|
||||
2
.gitignore
vendored
@@ -1,5 +1,4 @@
|
||||
**/*/.cxx
|
||||
**/.github
|
||||
**/.idea
|
||||
.dart_tool
|
||||
**/.dart_tool
|
||||
@@ -10,3 +9,4 @@
|
||||
pubspec_overrides.yaml
|
||||
/pubspec.lock
|
||||
*.iml
|
||||
**/*/*.filamat
|
||||
|
||||
212
CHANGELOG.md
@@ -3,6 +3,218 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## 2025-07-03
|
||||
|
||||
### Changes
|
||||
|
||||
---
|
||||
|
||||
Packages with breaking changes:
|
||||
|
||||
- [`thermion_dart` - `v0.3.0`](#thermion_dart---v030)
|
||||
- [`thermion_flutter` - `v0.3.0`](#thermion_flutter---v030)
|
||||
- [`thermion_flutter_method_channel` - `v0.3.0`](#thermion_flutter_method_channel---v030)
|
||||
|
||||
Packages with other changes:
|
||||
|
||||
- [`thermion_flutter_web` - `v0.3.0`](#thermion_flutter_web---v030)
|
||||
- [`thermion_flutter_platform_interface` - `v0.3.0`](#thermion_flutter_platform_interface---v030)
|
||||
|
||||
---
|
||||
|
||||
#### `thermion_dart` - `v0.3.0`
|
||||
|
||||
- **REFACTOR**: gizmo/input handler improvements.
|
||||
- **REFACTOR**: add createGizmoRenderThread.
|
||||
- **REFACTOR**: Gizmo internals.
|
||||
- **REFACTOR**: dont require GizmoInputHandler to wrap an existing InputHandler (you can do this by creating your own InputHandler that wraps two children.
|
||||
- **FIX**: glTF instancing when loaded via buffer.
|
||||
- **FIX**: don't return entity from SceneManager_addLightRenderThread.
|
||||
- **FIX**: return light entity from SceneManager.
|
||||
- **FIX**: store reference to material instances in ThermionViewer so they can be cleaned up on dispose.
|
||||
- **FIX**: remove MaterialInstance from SceneManager storage when destroyed.
|
||||
- **FIX**: add destroyCamera to ThermionViewer interface.
|
||||
- **FIX**: UV calculation for geometry.
|
||||
- **FIX**: use createGizmoRenderThread.
|
||||
- **FIX**: remove MaterialInstance from SceneManager storage when destroyed.
|
||||
- **FIX**: move removeIbl to render thread.
|
||||
- **FIX**: move material/instance creation to render thread.
|
||||
- **FIX**: allow destroying instances independently of owner.
|
||||
- **FIX**: remove MaterialInstance from SceneManager storage when destroyed.
|
||||
- **FIX**: use render thread methods for grid overlay creation and create ubershader instance.
|
||||
- **FIX**: only use Windows-style ndkRoot when building on Windows.
|
||||
- **FIX**: set overlay layer visibility when adding grid.
|
||||
- **FIX**: only use Windows-style ndkRoot when building on Windows.
|
||||
- **FIX**: when creating geometry, normals/uvs are set to false by default. remove wirefame camera container (can now be replaced by bounding box methods.
|
||||
- **FIX**: fix highlights after first.
|
||||
- **FEAT**: remove bounding box from SceneAsset and create renderable wireframe bounding box in ThermionAsset.
|
||||
- **FEAT**: add setTransparencyMode to Dart Material class.
|
||||
- **FEAT**: expose attached entity as Stream on GizmoInputHandler.
|
||||
- **FEAT**: allow custom material for grid overlay, and material creation from Uint8List.
|
||||
- **FEAT**: allow setting material instance directly on ThermionAsset.
|
||||
- **FEAT**: allow passing custom material for grid overlay.
|
||||
- **FEAT**: allow passing custom material for grid overlay.
|
||||
- **FEAT**: allow passing custom material for grid overlay.
|
||||
- **FEAT**: more rotation gizmo improvements.
|
||||
- **FEAT**: rotation gizmo improvements.
|
||||
- **FEAT**: add rotation gizmo.
|
||||
- **FEAT**: add rotation gizmo asset + resource file.
|
||||
- **FEAT**: add rotation gizmo asset + resource file.
|
||||
- **FEAT**: use existing material instances when creating an instance of GeometrySceneAsset and no material instance is passed.
|
||||
- **FEAT**: re-implement grid overlay.
|
||||
- **FEAT**: add gizmo.glb to assets/resources.
|
||||
- **FEAT**: add TRACE macro.
|
||||
- **FEAT**: update Filament to v1.56.4.
|
||||
- **FEAT**: expose setCastShadows/setReceiveShadows.
|
||||
- **FEAT**: re-add uvScale, vertexScale to unlit material.
|
||||
- **FEAT**: re-add uvScale, vertexScale to unlit material.
|
||||
- **BREAKING** **REFACTOR**: move light methods from FilamentViewer to SceneManager/TLightManager and rename clearLights/clearAssets to destroyLights/destroyAssets.
|
||||
- **BREAKING** **REFACTOR**: rename removeAsset to destroyAsset.
|
||||
- **BREAKING** **FIX**: rename removeEntity to removeAsset.
|
||||
- **BREAKING** **FEAT**: change default near/far to 0.1/100.0.
|
||||
- **BREAKING** **FEAT**: use raw pointer scale (>1 meaning zoom in, <1 meaning zoom out) rather than binary -1/1 for DelegateInputHandler.
|
||||
- **BREAKING** **FEAT**: remove Viewer setRenderTarget method (use the View method instead).
|
||||
|
||||
#### `thermion_flutter` - `v0.3.0`
|
||||
|
||||
- **REFACTOR**: rename ThermionFlutterTexture->PlatformTextureDescriptor.
|
||||
- **FIX**: rename msPerFrame property.
|
||||
- **FEAT**: add FocusNode to ThermionListenerWidget.
|
||||
- **FEAT**: use new createTextureAndBindToView in ThermionTextureWidget.
|
||||
- **BREAKING** **REFACTOR**: move light methods from FilamentViewer to SceneManager/TLightManager and rename clearLights/clearAssets to destroyLights/destroyAssets.
|
||||
- **BREAKING** **FEAT**: remove superseded ThermionWindows widget.
|
||||
- **BREAKING** **FEAT**: rename thermion_flutter_ffi package to thermion_flutter_method_channel.
|
||||
|
||||
#### `thermion_flutter_method_channel` - `v0.3.0`
|
||||
|
||||
- **REFACTOR**: rename ThermionFlutterTexture->PlatformTextureDescriptor.
|
||||
- **BREAKING** **FEAT**: rename thermion_flutter_ffi package to thermion_flutter_method_channel.
|
||||
|
||||
#### `thermion_flutter_web` - `v0.3.0`
|
||||
|
||||
- **REFACTOR**: rename ThermionFlutterTexture->PlatformTextureDescriptor.
|
||||
|
||||
#### `thermion_flutter_platform_interface` - `v0.3.0`
|
||||
|
||||
- **REFACTOR**: rename ThermionFlutterTexture->PlatformTextureDescriptor.
|
||||
- **FEAT**: create separate createTexture and createTextureAndBindToView interface methods.
|
||||
|
||||
|
||||
## 2025-01-08
|
||||
|
||||
### Changes
|
||||
|
||||
---
|
||||
|
||||
Packages with breaking changes:
|
||||
|
||||
- There are no breaking changes in this release.
|
||||
|
||||
Packages with other changes:
|
||||
|
||||
- [`thermion_dart` - `v0.2.1-dev.20.0`](#thermion_dart---v021-dev200)
|
||||
- [`thermion_flutter` - `v0.2.1-dev.20.0`](#thermion_flutter---v021-dev200)
|
||||
- [`thermion_flutter_platform_interface` - `v0.2.1-dev.20.0`](#thermion_flutter_platform_interface---v021-dev200)
|
||||
- [`thermion_flutter_ffi` - `v0.2.1-dev.20.0`](#thermion_flutter_ffi---v021-dev200)
|
||||
- [`thermion_flutter_web` - `v0.2.0+11`](#thermion_flutter_web---v02011)
|
||||
|
||||
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.2.0+11`
|
||||
|
||||
---
|
||||
|
||||
#### `thermion_dart` - `v0.2.1-dev.20.0`
|
||||
|
||||
- **FIX**: only use Windows-style ndkRoot when building on Windows.
|
||||
|
||||
#### `thermion_flutter` - `v0.2.1-dev.20.0`
|
||||
|
||||
- Bump "thermion_flutter" to `0.2.1-dev.20.0`.
|
||||
|
||||
#### `thermion_flutter_platform_interface` - `v0.2.1-dev.20.0`
|
||||
|
||||
- Bump "thermion_flutter_platform_interface" to `0.2.1-dev.20.0`.
|
||||
|
||||
#### `thermion_flutter_ffi` - `v0.2.1-dev.20.0`
|
||||
|
||||
- Bump "thermion_flutter_ffi" to `0.2.1-dev.20.0`.
|
||||
|
||||
|
||||
## 2024-11-21
|
||||
|
||||
### Changes
|
||||
|
||||
---
|
||||
|
||||
Packages with breaking changes:
|
||||
|
||||
- [`thermion_dart` - `v0.2.1-dev.19.0`](#thermion_dart---v021-dev190)
|
||||
|
||||
Packages with other changes:
|
||||
|
||||
- [`thermion_flutter` - `v0.2.1-dev.19.0`](#thermion_flutter---v021-dev190)
|
||||
- [`thermion_flutter_web` - `v0.2.0+10`](#thermion_flutter_web---v02010)
|
||||
- [`thermion_flutter_platform_interface` - `v0.2.1-dev.19.0`](#thermion_flutter_platform_interface---v021-dev190)
|
||||
- [`thermion_flutter_ffi` - `v0.2.1-dev.19.0`](#thermion_flutter_ffi---v021-dev190)
|
||||
|
||||
Packages with dependency updates only:
|
||||
|
||||
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
|
||||
|
||||
- `thermion_flutter` - `v0.2.1-dev.19.0`
|
||||
- `thermion_flutter_web` - `v0.2.0+10`
|
||||
- `thermion_flutter_platform_interface` - `v0.2.1-dev.19.0`
|
||||
- `thermion_flutter_ffi` - `v0.2.1-dev.19.0`
|
||||
|
||||
---
|
||||
|
||||
#### `thermion_dart` - `v0.2.1-dev.19.0`
|
||||
|
||||
- **FEAT**: use InputAction.ZOOM for scroll wheel in free flight handler.
|
||||
- **FEAT**: free flight camera improvements.
|
||||
- **BREAKING** **FIX**: update Makefile & rebuild materials for Vulkan.
|
||||
|
||||
|
||||
## 2024-11-18
|
||||
|
||||
### Changes
|
||||
|
||||
---
|
||||
|
||||
Packages with breaking changes:
|
||||
|
||||
- There are no breaking changes in this release.
|
||||
|
||||
Packages with other changes:
|
||||
|
||||
- [`thermion_dart` - `v0.2.1-dev.18.0`](#thermion_dart---v021-dev180)
|
||||
- [`thermion_flutter` - `v0.2.1-dev.18.0`](#thermion_flutter---v021-dev180)
|
||||
- [`thermion_flutter_web` - `v0.2.0+9`](#thermion_flutter_web---v0209)
|
||||
- [`thermion_flutter_platform_interface` - `v0.2.1-dev.18.0`](#thermion_flutter_platform_interface---v021-dev180)
|
||||
- [`thermion_flutter_ffi` - `v0.2.1-dev.18.0`](#thermion_flutter_ffi---v021-dev180)
|
||||
|
||||
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.2.0+9`
|
||||
- `thermion_flutter_platform_interface` - `v0.2.1-dev.18.0`
|
||||
- `thermion_flutter_ffi` - `v0.2.1-dev.18.0`
|
||||
|
||||
---
|
||||
|
||||
#### `thermion_dart` - `v0.2.1-dev.18.0`
|
||||
|
||||
- **FEAT**: add MaterialInstance.setDepthFunc.
|
||||
|
||||
#### `thermion_flutter` - `v0.2.1-dev.18.0`
|
||||
|
||||
- **FIX**: fix windows import header.
|
||||
|
||||
|
||||
## 2024-11-15
|
||||
|
||||
### Changes
|
||||
|
||||
76
Dockerfile
Normal file
@@ -0,0 +1,76 @@
|
||||
FROM ubuntu:22.04
|
||||
|
||||
# Set non-interactive mode for apt
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install dependencies and add LLVM repository
|
||||
RUN apt-get update && apt-get install -y \
|
||||
git \
|
||||
build-essential \
|
||||
cmake \
|
||||
ninja-build \
|
||||
libgl1-mesa-dev \
|
||||
libc++-dev \
|
||||
libc++abi-dev \
|
||||
libsdl2-dev \
|
||||
libxi-dev \
|
||||
libtbb-dev \
|
||||
libassimp-dev \
|
||||
python3 \
|
||||
python3-pip \
|
||||
curl \
|
||||
wget \
|
||||
software-properties-common \
|
||||
lsb-release \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Add LLVM repository and install Clang 16
|
||||
RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - \
|
||||
&& add-apt-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-16 main" \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y \
|
||||
clang-16 \
|
||||
clang++-16 \
|
||||
libc++-16-dev \
|
||||
libc++abi-16-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Set Clang 16 as default
|
||||
RUN update-alternatives --install /usr/bin/clang clang /usr/bin/clang-16 100 \
|
||||
&& update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-16 100
|
||||
|
||||
# Set environment variables for Clang
|
||||
ENV CC=clang-16
|
||||
ENV CXX=clang++-16
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /opt
|
||||
|
||||
# Clone the filament repository
|
||||
RUN git clone https://github.com/google/filament.git
|
||||
|
||||
# Change to filament directory
|
||||
WORKDIR /opt/filament
|
||||
|
||||
# Checkout the specific version
|
||||
RUN git checkout v1.58.0
|
||||
|
||||
# Add CMAKE_POSITION_INDEPENDENT_CODE setting after project() line
|
||||
RUN sed -i '/^project(/a set(CMAKE_POSITION_INDEPENDENT_CODE ON)\nadd_compile_definitions(GLTFIO_USE_FILESYSTEM=0)' CMakeLists.txt
|
||||
RUN sed -i -e '/^#define GLTFIO_USE_FILESYSTEM 1$/i\
|
||||
#ifndef GLTFIO_USE_FILESYSTEM' -e '/^#define GLTFIO_USE_FILESYSTEM 1$/a\
|
||||
#endif' libs/gltfio/src/FFilamentAsset.h
|
||||
|
||||
# Make build script executable
|
||||
RUN chmod +x build.sh
|
||||
|
||||
# Run the build commands
|
||||
RUN ./build.sh -l -i -f -p desktop release
|
||||
RUN ./build.sh -l -i -f -p desktop release zstd
|
||||
RUN ./build.sh -l -i -f -p desktop release tinyexr
|
||||
RUN ./build.sh -l -i -f -p desktop release imageio
|
||||
RUN zip -r filament-v1.58.0-linux-release.zip /opt/filament/out/release/filament/lib/x86_64/*.a /opt/filament/out/cmake-release/third_party/tinyexr/tnt/libtinyexr.a /opt/filament/out/cmake-release/libs/imageio/libimageio.a
|
||||
# Set the working directory to the build output
|
||||
WORKDIR /opt/filament/out/release
|
||||
|
||||
CMD ["/bin/bash"]
|
||||
48
Makefile
@@ -1,36 +1,50 @@
|
||||
dart-web:
|
||||
cd thermion_dart/native/web; mkdir -p build && cd build && emcmake cmake .. && emmake make
|
||||
dart-web-clean:
|
||||
wasm:
|
||||
cd thermion_dart/native/web &&\
|
||||
mkdir -p build &&\
|
||||
cd build &&\
|
||||
emcmake cmake .. &&\
|
||||
emmake make
|
||||
wasm-clean:
|
||||
cd thermion_dart/native/web && rm -rf build
|
||||
dart-wasm-cli-example: dart-web-clean dart-web
|
||||
cd thermion_dart/examples/cli_wasm/bin && dart compile wasm example_cli.dart && node main.js
|
||||
dart-web-example: dart-web
|
||||
cp thermion_dart/native/web/build/build/out/thermion_dart* examples/web_wasm/bin
|
||||
cd thermion_dart/examples/web_wasm/bin && dart compile wasm example_web.dart
|
||||
wasm-example-web:
|
||||
cd examples/dart/js_wasm
|
||||
mkdir -p build
|
||||
dart compile js web/example.dart -o build/example.dart.js
|
||||
|
||||
flutter-example-web: dart-web-clean dart-web
|
||||
cd thermion_flutter_federated/thermion_flutter/example/web && dart compile wasm main.dart && cd .. && flutter build web --wasm --profile
|
||||
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
|
||||
swiftc -c thermion_dart/native/macos/ThermionTexture.swift -module-name swift_module -emit-objc-header-path thermion_dart/native/include/generated/ThermionTextureSwiftObjCAPI.h -emit-library -o thermion_dart/test/generated/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
|
||||
shared:
|
||||
cd thermion_dart/native && make
|
||||
|
||||
# We compile a small set of custom materials for various helpers (background image, gizmo, etc)
|
||||
# You must specify the `FILAMENT_PATH` environment variable, either the path /out/release
|
||||
# eg: FILAMENT_PATH=/path/to/filament/out/release/bin make materials
|
||||
#
|
||||
materials: FORCE
|
||||
ifndef FILAMENT_PATH
|
||||
@echo "FILAMENT_PATH is not set"
|
||||
else
|
||||
@echo "Using Filament build from ${FILAMENT_PATH}"
|
||||
@for material in unlit image unlit_fixed_size 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
|
||||
./materials/build.sh
|
||||
endif
|
||||
|
||||
#rm materials/*.filamat
|
||||
resources: FORCE
|
||||
ifndef FILAMENT_PATH
|
||||
@echo "FILAMENT_PATH is not set"
|
||||
else
|
||||
@echo "Using Filament build from ${FILAMENT_PATH}"
|
||||
@for gizmo in translation rotation; do \
|
||||
$(FILAMENT_PATH)/resgen -c -p $${gizmo}_gizmo_glb -x thermion_dart/native/include/resources assets/$${gizmo}_gizmo.glb || exit 1; \
|
||||
echo '#include "'$${gizmo}_gizmo_glb.h'"' | cat - thermion_dart/native/include/resources/$${gizmo}_gizmo_glb.c > thermion_dart/native/include/resources/$${gizmo}_gizmo_glb.c.new; \
|
||||
mv thermion_dart/native/include/resources/$${gizmo}_gizmo_glb.c.new thermion_dart/native/include/resources/$${gizmo}_gizmo_glb.c; \
|
||||
done
|
||||
endif
|
||||
|
||||
FORCE: ;
|
||||
|
||||
|
||||
65
README.md
@@ -15,6 +15,8 @@
|
||||
<a href="https://discord.gg/h2VdDK3EAQ"><img src="https://img.shields.io/discord/993167615587520602?logo=discord&logoColor=fff&labelColor=333940" alt="discord"></a>
|
||||
<a href="https://github.com/nmfisher/thermion"><img src="https://img.shields.io/github/contributors/nmfisher/flutter_filament?logo=github&labelColor=333940" alt="contributors"></a>
|
||||
|
||||
https://github.com/user-attachments/assets/b0c07b5a-6156-4e42-a09b-5f9bd85fbf32
|
||||
|
||||
### Features
|
||||
|
||||
- Supports iOS (arm64), MacOS (arm64/x64), Android (arm64), Windows (x64) (>= 10), Web/WASM
|
||||
@@ -22,6 +24,8 @@
|
||||
- camera/entity manipulation with mouse (desktop) and gestures (mobile)
|
||||
- skinning + morph animations
|
||||
|
||||
Uses the Filament PBR engine (currently v1.56.4).
|
||||
|
||||
### Quickstart (Flutter)
|
||||
|
||||
From the command line:
|
||||
@@ -35,50 +39,32 @@ flutter config --enable-native-assets
|
||||
In your Flutter app:
|
||||
|
||||
```dart
|
||||
_thermionViewer = await ThermionFlutterPlugin.createViewer();
|
||||
|
||||
// Geometry and models are represented as "entities". Here, we load a glTF
|
||||
// file containing a plain cube.
|
||||
// By default, all paths are treated as asset paths. To load from a file
|
||||
// instead, use file:// URIs.
|
||||
var entity =
|
||||
await _thermionViewer!.loadGlb("assets/cube.glb", keepData: true);
|
||||
|
||||
// Thermion uses a right-handed coordinate system where +Y is up and -Z is
|
||||
// "into" the screen.
|
||||
// By default, the camera is located at (0,0,0) looking at (0,0,-1); this
|
||||
// would place it directly inside the cube we just loaded.
|
||||
//
|
||||
// Let's move the camera to (0,0,10) to ensure the cube is visible in the
|
||||
// viewport.
|
||||
await _thermionViewer!.setCameraPosition(0, 0, 10);
|
||||
|
||||
// Without a light source, your scene will be totally black. Let's load a skybox
|
||||
// (a cubemap image that is rendered behind everything else in the scene)
|
||||
// and an image-based indirect light that has been precomputed from the same
|
||||
// skybox.
|
||||
await _thermionViewer!.loadSkybox("assets/default_env_skybox.ktx");
|
||||
await _thermionViewer!.loadIbl("assets/default_env_ibl.ktx");
|
||||
|
||||
// Finally, you need to explicitly enable rendering. Setting rendering to
|
||||
// false is designed to allow you to pause rendering to conserve battery life
|
||||
await _thermionViewer!.setRendering(true);
|
||||
```
|
||||
|
||||
and then in your widget tree:
|
||||
```dart
|
||||
@override
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(children: [
|
||||
if (_thermionViewer != null)
|
||||
return Scaffold(
|
||||
body: Stack(children: [
|
||||
Positioned.fill(
|
||||
child: ThermionWidget(
|
||||
viewer: _thermionViewer!,
|
||||
)),
|
||||
]);
|
||||
child: ViewerWidget(
|
||||
assetPath: "assets/cube.glb",
|
||||
skyboxPath: "assets/default_env_skybox.ktx",
|
||||
iblPath: "assets/default_env_ibl.ktx",
|
||||
transformToUnitCube: true,
|
||||
initialCameraPosition: Vector3(0, 0, 6),
|
||||
background: Colors.blue,
|
||||
manipulatorType: ManipulatorType.ORBIT,
|
||||
onViewerAvailable: (viewer) async {
|
||||
await Future.delayed(const Duration(seconds: 5));
|
||||
await viewer.removeSkybox();
|
||||
},
|
||||
initial: Container(
|
||||
color: Colors.red,
|
||||
),
|
||||
))]));
|
||||
}
|
||||
```
|
||||
|
||||
> the first time you build an app that consumes this package, the Dart native-assets build system will download static binaries from Cloudflare. This may take a few minutes (depending on which platform you are compiling for). These will be cached, so subsequent builds will be much faster.
|
||||
|
||||
### Sponsors, Contributors & Acknowledgments
|
||||
|
||||
Thermion uses the [Filament](https://github.com/google/filament) Physically Based Rendering engine under the hood.
|
||||
@@ -92,4 +78,5 @@ Thank you to the following people:
|
||||
- @daverin for MacOS library contributions
|
||||
- @LukasPoque for CI/refactoring work
|
||||
- @alexmercerind for his work on integrating ANGLE textures on Flutter Windows
|
||||
- @BrutalCoding for documentation fixes
|
||||
|
||||
|
||||
BIN
assets/rotation_gizmo.glb
Normal file
BIN
assets/translation_gizmo.glb
Normal file
@@ -8,14 +8,19 @@
|
||||
[
|
||||
["Overview", "/"],
|
||||
["Quick Start", "/quickstart"],
|
||||
["Viewer", "/viewer"],
|
||||
["Camera Manipulation", "/camera_manipulation"]
|
||||
]
|
||||
],
|
||||
["Misc.", [
|
||||
["Debugging", "/debugging"],
|
||||
["Playground", "https://dartpad.thermion.dev"],
|
||||
["Showcase", "/showcase"],
|
||||
["Windows", "/windows"],
|
||||
["Android", "/android"],
|
||||
["iOS", "/ios"],
|
||||
["Web", "/web"],
|
||||
["Linux", "/linux"],
|
||||
["Contributing", "/contributing"],
|
||||
["Discord", "https://discord.gg/h2VdDK3EAQ"]
|
||||
]]
|
||||
|
||||
@@ -12,15 +12,3 @@ Thermion requires Android SDK version 22, so change your `app/android/build.grad
|
||||
}
|
||||
```
|
||||
|
||||
### Shrink/Minify Resources
|
||||
|
||||
In release mode, you must add the following to your `app/build.gradle`:
|
||||
|
||||
```
|
||||
buildTypes {
|
||||
release {
|
||||
shrinkResources false
|
||||
minifyEnabled false
|
||||
}
|
||||
}
|
||||
```
|
||||
27
docs/debugging.mdx
Normal file
@@ -0,0 +1,27 @@
|
||||
# Debugging
|
||||
|
||||
If something is crashing or not working as expected, you can enable verbose logging and/or debug builds (particularly useful for debugging with lldb or getting legible stack traces):
|
||||
|
||||
Add a `hooks` section to your app's `pubspec.yaml`, e.g.
|
||||
```
|
||||
name: example_cli
|
||||
description: A sample command-line application.
|
||||
version: 1.0.0
|
||||
|
||||
environment:
|
||||
sdk: ^3.3.0
|
||||
|
||||
# Add the below
|
||||
hooks:
|
||||
user_defines:
|
||||
thermion_dart:
|
||||
mode: debug
|
||||
tracing: enabled
|
||||
```
|
||||
|
||||
After changing the `hooks` section, make sure you run `flutter clean` to propagate the changes correctly.
|
||||
|
||||
> [!CAUTION]
|
||||
> Debug builds won't work for Android. This is a known upstream issue with Filament.
|
||||
|
||||
Unless `mode: debug` is specified in your `pubspec.yaml`, Thermion will use release builds under the hood, even if your app is compiled/runing in debug mode.
|
||||
@@ -16,7 +16,7 @@ Thermion is divided into two packages:
|
||||
|
||||
With this structure, the Flutter-specific components are not coupled to the Dart components, meaning Thermion can be used for rendering in both Flutter and non-Flutter applications.
|
||||
|
||||
For example, Thermion ships with examples for rendering with Dart only (no Flutter) with a CLI/headless application on MacOS, and with a Javascript/WASM/HTML applicaiton in browsers.
|
||||
For example, Thermion ships with examples for rendering with Dart only (no Flutter) with a CLI/headless application on MacOS, and with a Javascript/WASM/HTML application in browsers.
|
||||
|
||||
`thermion_flutter` exports `thermion_dart`, so if you are working with a Flutter application, you will only need to import `thermion_fluttter`.
|
||||
|
||||
|
||||
55
docs/ios.mdx
Normal file
@@ -0,0 +1,55 @@
|
||||
## iOS
|
||||
|
||||
### Min iOS version
|
||||
|
||||
Thermion requires a minimum iOS version of 13.0. When building a Flutter application, ensure your application's `ios/Podfile` contains the following:
|
||||
|
||||
```ruby
|
||||
platform :ios, '13.0'
|
||||
```
|
||||
|
||||
and in ios/Info.plist:
|
||||
|
||||
```xml
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>13.0</string>
|
||||
```
|
||||
|
||||
When submitting to the App Store, you may encounter an error saying `thermion_dart.framework does not supported the minimum deployment target in Info.plist`.
|
||||
|
||||
This is because Flutter hardcodes a deployment target of iOS 12.0 when invoking the native assets build, which conflicts with actual requirement.
|
||||
|
||||
After running `flutter build ios` (but before archiving the build and submitting to the App Store), run the following script to replace the `MinimumOSVersion`:
|
||||
|
||||
```
|
||||
#!/bin/zsh
|
||||
|
||||
# Array of directories containing Info.plist files
|
||||
directories=(
|
||||
"./build/ios/iphoneos/Runner.app/Frameworks/thermion_dart.framework"
|
||||
"./build/ios/Release-iphoneos/Runner.app/Frameworks/thermion_dart.framework"
|
||||
"./build/native_assets/ios/thermion_dart.framework"
|
||||
)
|
||||
|
||||
# Loop through each directory
|
||||
for dir in "${directories[@]}"; do
|
||||
plist_path="$dir/Info.plist"
|
||||
|
||||
# Check if Info.plist exists in the directory
|
||||
if [[ -f "$plist_path" ]]; then
|
||||
echo "Processing: $plist_path"
|
||||
|
||||
# Use PlistBuddy to change the MinimumOSVersion
|
||||
/usr/libexec/PlistBuddy -c "Set :MinimumOSVersion 13.0" "$plist_path" 2>/dev/null
|
||||
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo "✓ Successfully updated version to 13.0"
|
||||
else
|
||||
echo "✗ Failed to update version in $plist_path"
|
||||
fi
|
||||
else
|
||||
echo "✗ Info.plist not found in $dir"
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
10
docs/linux.mdx
Normal file
@@ -0,0 +1,10 @@
|
||||
## Linux support
|
||||
|
||||
Currently, only the Dart package (`thermion_dart`) will work on Linux (primarily for the automated build/testing pipeline).
|
||||
|
||||
Filament has been built for Ubuntu 22.04; other distributions may work, but YMMV.
|
||||
|
||||
The Flutter package (`thermion_flutter`) won't work; it's missing the platform glue code necessary to set up the render target. Support will eventually be added, but there is currently no estimated timeline.
|
||||
|
||||
Contributions are welcome and it's actually not a lot of work. Join the Discord if you want to help out.
|
||||
|
||||
@@ -1,216 +1,202 @@
|
||||
## Quickstart (Flutter)
|
||||
# ViewerWidget Documentation
|
||||
|
||||
> You can find the entire project below in the [flutter/quickstart](https://github.com/nmfisher/thermion_examples/tree/master/flutter/quickstart) folder of the `thermion_examples` repository.
|
||||
`ViewerWidget` is a simplified wrapper around the Thermion 3D viewer that makes it easy to display 3D models in your Flutter application.
|
||||
|
||||
1. Switch to Flutter master channel, upgrade Flutter, create a new project, then add `thermion_flutter` as a dependency
|
||||
## Overview
|
||||
|
||||
```bash
|
||||
$ flutter channel master
|
||||
$ flutter upgrade
|
||||
$ flutter config --enable-native-assets
|
||||
$ flutter create thermion_sample_project && cd thermion_sample_project
|
||||
$ flutter pub add thermion_flutter
|
||||
```
|
||||
`ViewerWidget` handles the setup and configuration of a Thermion viewer, including:
|
||||
- Loading 3D models (glTF assets)
|
||||
- Configuring skyboxes and image-based lighting
|
||||
- Setting up camera positions and manipulators
|
||||
- Managing the rendering lifecycle
|
||||
|
||||
2. If running on iOS or MacOS, change the minimum deployment target to OSX 13
|
||||
## Installation
|
||||
|
||||
<Accordion title="Click to open iOS/MacOS instructions">
|
||||
|
||||
Make sure the `platform` entry refers to `13.0` in your Podfile.
|
||||
|
||||
In `macos/Podfile` (for macOS):
|
||||
```
|
||||
platform :osx, '13.0'
|
||||
```
|
||||
|
||||
In `ios/Podfile`, (for iOS):
|
||||
```
|
||||
platform :ios, '13.0'
|
||||
```
|
||||
|
||||
Then open XCode:
|
||||
```
|
||||
open macos/Runner.xcworkspace
|
||||
```
|
||||
|
||||
and change the minimum deployment target to 13.0:
|
||||
|
||||

|
||||
|
||||
</Accordion>
|
||||
|
||||
2. Add a folder containing your assets (glTF model + skybox ktx) to your `pubspec.yaml` asset list
|
||||
First, make sure you have the Thermion Flutter plugin added to your dependencies:
|
||||
|
||||
```yaml
|
||||
...
|
||||
flutter
|
||||
uses-material-design: true
|
||||
assets:
|
||||
- assets/
|
||||
dependencies:
|
||||
thermion_flutter: ^latest_version
|
||||
```
|
||||
|
||||
3. Create an instance of `ThermionFlutterPlugin` in your app.
|
||||
## Basic Usage
|
||||
|
||||
```dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:thermion_flutter/thermion_flutter.dart';
|
||||
import 'path_to_your_viewer_widget.dart';
|
||||
|
||||
class _MyAppState extends State<MyApp> {
|
||||
late ThermionFlutterPlugin _thermionFlutterPlugin;
|
||||
late Future<ThermionViewer> _thermionViewer;
|
||||
void initState() {
|
||||
_thermionFlutterPlugin = ThermionFlutterPlugin();
|
||||
_thermionViewer = _thermionFlutterPlugin.createViewer();
|
||||
class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
home: Scaffold(
|
||||
body: ViewerWidget(
|
||||
assetPath: 'assets/my_model.glb',
|
||||
initialCameraPosition: Vector3(0, 0, 5),
|
||||
manipulatorType: ManipulatorType.ORBIT,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
4. Add a `ThermionWidget` to your widget hierarchy
|
||||
| Property | Type | Default | Description |
|
||||
|----------|------|---------|-------------|
|
||||
| `initial` | `Widget` | Red decorated box | Widget to display while the viewer is loading |
|
||||
| `initialCameraPosition` | `Vector3` | `Vector3(0, 0, 5)` | The starting position for the camera (looking towards origin) |
|
||||
| `showFpsCounter` | `bool` | `false` | Whether to show an FPS counter overlay |
|
||||
| `assetPath` | `String?` | `null` | Path to the glTF asset to load |
|
||||
| `skyboxPath` | `String?` | `null` | Path to a KTX skybox image |
|
||||
| `iblPath` | `String?` | `null` | Path to a KTX image for image-based lighting |
|
||||
| `directLightType` | `LightType?` | `null` | Type of direct light to add to the scene |
|
||||
| `transformToUnitCube` | `bool` | `true` | If true, rescales the model to fit within a 1x1x1 cube |
|
||||
| `postProcessing` | `bool` | `true` | Enables ACES tone mapping and basic anti-aliasing |
|
||||
| `background` | `Color?` | `null` | Background color (not visible when skybox is provided) |
|
||||
| `destroyEngineOnUnload` | `bool` | `false` | If true, disposes the engine when widget is disposed |
|
||||
| `manipulatorType` | `ManipulatorType` | `ORBIT` | Type of camera control to use |
|
||||
| `onViewerAvailable` | `Future Function(ThermionViewer)?` | `null` | Callback when viewer is ready |
|
||||
|
||||
## Camera Manipulators
|
||||
|
||||
`ViewerWidget` supports three different camera manipulation modes:
|
||||
|
||||
- `ManipulatorType.NONE`: No camera controls, static view
|
||||
- `ManipulatorType.ORBIT`: Orbit controls (pinch to zoom, swipe to rotate)
|
||||
- `ManipulatorType.FREE_FLIGHT`: Free flight controls for unrestricted movement
|
||||
|
||||
Example:
|
||||
|
||||
```dart
|
||||
ViewerWidget(
|
||||
assetPath: 'assets/model.glb',
|
||||
manipulatorType: ManipulatorType.FREE_FLIGHT,
|
||||
)
|
||||
```
|
||||
|
||||
class _MyAppState extends State<MyApp> {
|
||||
## Lighting
|
||||
|
||||
You can set up lighting in multiple ways:
|
||||
|
||||
### Image-Based Lighting
|
||||
|
||||
```dart
|
||||
ViewerWidget(
|
||||
assetPath: 'assets/model.glb',
|
||||
iblPath: 'assets/environment.ktx',
|
||||
)
|
||||
```
|
||||
|
||||
### Direct Light
|
||||
|
||||
```dart
|
||||
ViewerWidget(
|
||||
assetPath: 'assets/model.glb',
|
||||
directLightType: LightType.SUN,
|
||||
)
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Accessing the Viewer
|
||||
|
||||
You can get access to the underlying `ThermionViewer` object for more advanced control:
|
||||
|
||||
```dart
|
||||
ViewerWidget(
|
||||
assetPath: 'assets/model.glb',
|
||||
onViewerAvailable: (viewer) async {
|
||||
// Now you can use the viewer directly
|
||||
final camera = await viewer.getActiveCamera();
|
||||
await camera.lookAt(Vector3(0, 1, 5));
|
||||
|
||||
ThermionViewer? _thermionViewer;
|
||||
void initState() {
|
||||
_thermionFlutterPlugin.createViewer().then((viewer) {
|
||||
setState(() {
|
||||
_thermionViewer = viewer;
|
||||
});
|
||||
});
|
||||
}
|
||||
// Add custom lights, manipulate materials, etc.
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(children:[
|
||||
if(_thermionViewer != null)
|
||||
Positioned.fill(
|
||||
child:ThermionWidget(
|
||||
plugin:_thermionViewer!
|
||||
)
|
||||
)
|
||||
]);
|
||||
}
|
||||
### Changing Manipulator at Runtime
|
||||
|
||||
The `manipulatorType` is the only property that can be changed after the widget is created:
|
||||
|
||||
```dart
|
||||
class _MyWidgetState extends State<MyWidget> {
|
||||
ManipulatorType _manipulatorType = ManipulatorType.ORBIT;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ViewerWidget(
|
||||
assetPath: 'assets/model.glb',
|
||||
manipulatorType: _manipulatorType,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_manipulatorType = ManipulatorType.ORBIT;
|
||||
});
|
||||
},
|
||||
child: Text('Orbit'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_manipulatorType = ManipulatorType.FREE_FLIGHT;
|
||||
});
|
||||
},
|
||||
child: Text('Free Flight'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
4. Add a button to load the model when pressed
|
||||
## Limitations
|
||||
|
||||
- Only the `manipulatorType` property can be changed at runtime. For any other property changes, create a new widget.
|
||||
- The widget requires that you have the correct environment setup for Thermion (Flutter master channel with native assets enabled).
|
||||
|
||||
## Example
|
||||
|
||||
Here's a complete example showing how to use `ViewerWidget` with multiple configuration options:
|
||||
|
||||
```dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:thermion_flutter/thermion_flutter.dart';
|
||||
import 'package:vector_math/vector_math_64.dart';
|
||||
|
||||
...
|
||||
|
||||
class _MyAppState extends State<MyApp> {
|
||||
|
||||
...
|
||||
|
||||
bool _loaded = false;
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(children:[
|
||||
if(_thermionViewer != null)
|
||||
Positioned.fill(
|
||||
child:ThermionWidget(
|
||||
plugin:_thermionViewer!
|
||||
)
|
||||
),
|
||||
if (!_loaded)
|
||||
Center(
|
||||
child: ElevatedButton(
|
||||
child: const Text("Load"),
|
||||
onPressed: () async {
|
||||
// TODO
|
||||
_loaded = true;
|
||||
setState(() {});
|
||||
}))
|
||||
]);
|
||||
}}
|
||||
```
|
||||
|
||||
5. When the button is pressed, load a skybox, lighting and the glb asset
|
||||
|
||||
You will need to import the `dart:math` and `package:vector_math` libraries.
|
||||
|
||||
```dart
|
||||
import 'package:vector_math/vector_math_64.dart' as v;
|
||||
import 'dart:math';
|
||||
|
||||
...
|
||||
|
||||
class _MyAppState extends State<MyApp> {
|
||||
|
||||
...
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(children:[
|
||||
...
|
||||
if(!_loaded)
|
||||
Center(
|
||||
child: ElevatedButton(
|
||||
child: const Text("Load"),
|
||||
onPressed: () async {
|
||||
var viewer = await _thermionViewer;
|
||||
await viewer.loadIbl("assets/default_env_ibl.ktx");
|
||||
await viewer.loadSkybox("assets/default_env_skybox.ktx");
|
||||
await viewer.loadGlb("assets/cube.glb");
|
||||
|
||||
await viewer.setCameraPosition(0, 1, 10);
|
||||
await viewer.setCameraRotation(v.Quaternion.axisAngle(
|
||||
v.Vector3(1, 0, 0), -30 / 180 * pi) *
|
||||
v.Quaternion.axisAngle(v.Vector3(0, 1, 0), 15 / 180 * pi));
|
||||
await viewer.addLight(
|
||||
LightType.SUN, 7500, 50000, 0, 0, 0, 1, -1, -1);
|
||||
await viewer.setRendering(true);
|
||||
_loaded = true;
|
||||
setState(() {});
|
||||
}
|
||||
)
|
||||
)
|
||||
]);
|
||||
}
|
||||
class ModelViewer extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text('3D Model Viewer')),
|
||||
body: ViewerWidget(
|
||||
assetPath: 'assets/robot.glb',
|
||||
skyboxPath: 'assets/studio_skybox.ktx',
|
||||
iblPath: 'assets/studio_ibl.ktx',
|
||||
initialCameraPosition: Vector3(0, 1.5, 3),
|
||||
manipulatorType: ManipulatorType.ORBIT,
|
||||
showFpsCounter: true,
|
||||
background: Colors.grey,
|
||||
postProcessing: true,
|
||||
transformToUnitCube: true,
|
||||
onViewerAvailable: (viewer) async {
|
||||
// You can perform additional setup here
|
||||
print('Viewer is ready!');
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here, we've added a skybox (the background (cube) image rendered behind all other elements in the scene), image-based lighting (where an image is used to determine the direction and intensity of a light source) and a directional light (Sun).
|
||||
|
||||
Anything added to the scene is referred to as an "entity" (including lights and cameras).
|
||||
|
||||
Entities are always added to the scene at position (0,0,0).
|
||||
|
||||
The default scene camera is located at (0,0,0) (and is looking at -Z, or "into" the screen), so by adding a cube at (0,0,0), the camera will now be inside the cube.
|
||||
|
||||
We need to move the camera outside the cube so it's visible.
|
||||
|
||||
6. Change the camera orientation
|
||||
```dart
|
||||
var viewer = await _thermionViewer;
|
||||
await viewer.loadSkybox("assets/default_env_skybox.ktx");
|
||||
await viewer.loadGlb("assets/cube.glb");
|
||||
|
||||
await viewer.setCameraPosition(0, 1, 10);
|
||||
await viewer.setCameraRotation(v.Quaternion.axisAngle(
|
||||
v.Vector3(1, 0, 0), -30 / 180 * pi) *
|
||||
v.Quaternion.axisAngle(v.Vector3(0, 1, 0), 15 / 180 * pi));
|
||||
```
|
||||
|
||||
The cube still won't be visible until we add a light to the scene and tell Thermion to start rendering.
|
||||
|
||||
7. Add a light and turn rendering on
|
||||
```dart
|
||||
...
|
||||
await viewer.addLight(
|
||||
LightType.SUN, 7500, 50000, 0, 0, 0, 1, -1, -1);
|
||||
await viewer.setRendering(true);
|
||||
...
|
||||
````
|
||||
|
||||
8. Run the project
|
||||
```
|
||||
$ flutter run -d macos
|
||||
```
|
||||
|
||||
> You may experience a noticeable delay the very first time you run the project. Don't panic, it's not frozen! This is due to the build system downloading the prebuilt Filament binaries from Cloudflare, which can take some time (particularly on Windows). These binaries will be cached after first download, so subsequent runs will be much faster (though every time you run flutter clean, the binaries will be re-downloaded).
|
||||
|
||||

|
||||
|
||||
Your first Thermion project is complete!
|
||||
```
|
||||
215
docs/viewer.mdx
Normal file
@@ -0,0 +1,215 @@
|
||||
## Quickstart (Flutter)
|
||||
|
||||
> You can find the entire project below in the [examples/flutter/quickstart](https://github.com/nmfisher/thermion/tree/master/examples/flutter/quickstart) folder of the repository.
|
||||
|
||||
1. Switch to Flutter master channel, upgrade Flutter, create a new project, then add `thermion_flutter` as a dependency
|
||||
|
||||
```bash
|
||||
$ flutter channel master
|
||||
$ flutter upgrade
|
||||
$ flutter config --enable-native-assets
|
||||
$ flutter create thermion_sample_project && cd thermion_sample_project
|
||||
$ flutter pub add thermion_flutter
|
||||
```
|
||||
|
||||
2. If running on iOS or MacOS, change the minimum deployment target to OSX 13
|
||||
|
||||
<Accordion title="Click to open iOS/MacOS instructions">
|
||||
|
||||
Make sure the `platform` entry refers to `13.0` in your Podfile.
|
||||
|
||||
In `macos/Podfile` (for macOS):
|
||||
```
|
||||
platform :osx, '13.0'
|
||||
```
|
||||
|
||||
In `ios/Podfile`, (for iOS):
|
||||
```
|
||||
platform :ios, '13.0'
|
||||
```
|
||||
|
||||
Then open XCode:
|
||||
```
|
||||
open macos/Runner.xcworkspace
|
||||
```
|
||||
|
||||
and change the minimum deployment target to 13.0:
|
||||
|
||||

|
||||
|
||||
</Accordion>
|
||||
|
||||
2. Add a folder containing your assets (glTF model + skybox ktx) to your `pubspec.yaml` asset list
|
||||
|
||||
```yaml
|
||||
...
|
||||
flutter
|
||||
assets:
|
||||
- assets/
|
||||
```
|
||||
|
||||
3. Create an instance of `ThermionFlutterPlugin` in your app.
|
||||
|
||||
```dart
|
||||
|
||||
class _MyAppState extends State<MyApp> {
|
||||
late ThermionFlutterPlugin _thermionFlutterPlugin;
|
||||
late Future<ThermionViewer> _thermionViewer;
|
||||
void initState() {
|
||||
_thermionFlutterPlugin = ThermionFlutterPlugin();
|
||||
_thermionViewer = _thermionFlutterPlugin.createViewer();
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
4. Add a `ThermionWidget` to your widget hierarchy
|
||||
|
||||
```dart
|
||||
|
||||
class _MyAppState extends State<MyApp> {
|
||||
|
||||
ThermionViewer? _thermionViewer;
|
||||
void initState() {
|
||||
_thermionFlutterPlugin.createViewer().then((viewer) {
|
||||
setState(() {
|
||||
_thermionViewer = viewer;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(children:[
|
||||
if(_thermionViewer != null)
|
||||
Positioned.fill(
|
||||
child:ThermionWidget(
|
||||
plugin:_thermionViewer!
|
||||
)
|
||||
)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
4. Add a button to load the model when pressed
|
||||
|
||||
```dart
|
||||
|
||||
...
|
||||
|
||||
class _MyAppState extends State<MyApp> {
|
||||
|
||||
...
|
||||
|
||||
bool _loaded = false;
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(children:[
|
||||
if(_thermionViewer != null)
|
||||
Positioned.fill(
|
||||
child:ThermionWidget(
|
||||
plugin:_thermionViewer!
|
||||
)
|
||||
),
|
||||
if (!_loaded)
|
||||
Center(
|
||||
child: ElevatedButton(
|
||||
child: const Text("Load"),
|
||||
onPressed: () async {
|
||||
// TODO
|
||||
_loaded = true;
|
||||
setState(() {});
|
||||
}))
|
||||
]);
|
||||
}}
|
||||
```
|
||||
|
||||
5. When the button is pressed, load a skybox, lighting and the glb asset
|
||||
|
||||
You will need to import the `dart:math` and `package:vector_math` libraries.
|
||||
|
||||
```dart
|
||||
import 'package:vector_math/vector_math_64.dart' as v;
|
||||
import 'dart:math';
|
||||
|
||||
...
|
||||
|
||||
class _MyAppState extends State<MyApp> {
|
||||
|
||||
...
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(children:[
|
||||
...
|
||||
if(!_loaded)
|
||||
Center(
|
||||
child: ElevatedButton(
|
||||
child: const Text("Load"),
|
||||
onPressed: () async {
|
||||
var viewer = await _thermionViewer;
|
||||
await viewer.loadIbl("assets/default_env_ibl.ktx");
|
||||
await viewer.loadSkybox("assets/default_env_skybox.ktx");
|
||||
await viewer.loadGlb("assets/cube.glb");
|
||||
|
||||
await viewer.setCameraPosition(0, 1, 10);
|
||||
await viewer.setCameraRotation(v.Quaternion.axisAngle(
|
||||
v.Vector3(1, 0, 0), -30 / 180 * pi) *
|
||||
v.Quaternion.axisAngle(v.Vector3(0, 1, 0), 15 / 180 * pi));
|
||||
await viewer.addLight(
|
||||
LightType.SUN, 7500, 50000, 0, 0, 0, 1, -1, -1);
|
||||
await viewer.setRendering(true);
|
||||
_loaded = true;
|
||||
setState(() {});
|
||||
}
|
||||
)
|
||||
)
|
||||
]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here, we've added a skybox (the background (cube) image rendered behind all other elements in the scene), image-based lighting (where an image is used to determine the direction and intensity of a light source) and a directional light (Sun).
|
||||
|
||||
Anything added to the scene is referred to as an "entity" (including lights and cameras).
|
||||
|
||||
Entities are always added to the scene at position (0,0,0).
|
||||
|
||||
The default scene camera is located at (0,0,0) (and is looking at -Z, or "into" the screen), so by adding a cube at (0,0,0), the camera will now be inside the cube.
|
||||
|
||||
We need to move the camera outside the cube so it's visible.
|
||||
|
||||
6. Change the camera orientation
|
||||
```dart
|
||||
var viewer = await _thermionViewer;
|
||||
await viewer.loadSkybox("assets/default_env_skybox.ktx");
|
||||
await viewer.loadGlb("assets/cube.glb");
|
||||
|
||||
await viewer.setCameraPosition(0, 1, 10);
|
||||
await viewer.setCameraRotation(v.Quaternion.axisAngle(
|
||||
v.Vector3(1, 0, 0), -30 / 180 * pi) *
|
||||
v.Quaternion.axisAngle(v.Vector3(0, 1, 0), 15 / 180 * pi));
|
||||
```
|
||||
|
||||
The cube still won't be visible until we add a light to the scene and tell Thermion to start rendering.
|
||||
|
||||
7. Add a light and turn rendering on
|
||||
```dart
|
||||
...
|
||||
await viewer.addLight(
|
||||
LightType.SUN, 7500, 50000, 0, 0, 0, 1, -1, -1);
|
||||
await viewer.setRendering(true);
|
||||
...
|
||||
````
|
||||
|
||||
8. Run the project
|
||||
```
|
||||
$ flutter run -d macos
|
||||
```
|
||||
|
||||
> You may experience a noticeable delay the very first time you run the project. Don't panic, it's not frozen! This is due to the build system downloading the prebuilt Filament binaries from Cloudflare, which can take some time (particularly on Windows). These binaries will be cached after first download, so subsequent runs will be much faster (though every time you run flutter clean, the binaries will be re-downloaded).
|
||||
|
||||

|
||||
|
||||
Your first Thermion project is complete!
|
||||
27
docs/web.mdx
Normal file
@@ -0,0 +1,27 @@
|
||||
## Web
|
||||
|
||||
First, you must manually compile the wasm+javascript module. This requires:
|
||||
|
||||
1) GNU Make
|
||||
2) CMake
|
||||
3) Emscripten
|
||||
|
||||
```
|
||||
make wasm
|
||||
```
|
||||
|
||||
### Flutter
|
||||
|
||||
Thermion requires Android SDK version 22, so change your `app/android/build.gradle` to match this version or higher:
|
||||
|
||||
```groovy
|
||||
defaultConfig {
|
||||
...
|
||||
minSdk = 22
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
flutter run -d chrome --web-header Cross-Origin-Embedder-Policy=require-corp --web-header Cross-Origin-Opener-Policy=same-origin
|
||||
```
|
||||
BIN
examples/assets/cube.bin
Normal file
121
examples/assets/cube.gltf
Normal file
@@ -0,0 +1,121 @@
|
||||
{
|
||||
"asset":{
|
||||
"generator":"Khronos glTF Blender I/O v4.2.60",
|
||||
"version":"2.0"
|
||||
},
|
||||
"scene":0,
|
||||
"scenes":[
|
||||
{
|
||||
"name":"Scene",
|
||||
"nodes":[
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes":[
|
||||
{
|
||||
"mesh":0,
|
||||
"name":"Cube"
|
||||
}
|
||||
],
|
||||
"materials":[
|
||||
{
|
||||
"doubleSided":true,
|
||||
"name":"Material",
|
||||
"pbrMetallicRoughness":{
|
||||
"baseColorFactor":[
|
||||
0.800000011920929,
|
||||
0.800000011920929,
|
||||
0.800000011920929,
|
||||
1
|
||||
],
|
||||
"metallicFactor":0,
|
||||
"roughnessFactor":0.5
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes":[
|
||||
{
|
||||
"name":"Cube",
|
||||
"primitives":[
|
||||
{
|
||||
"attributes":{
|
||||
"POSITION":0,
|
||||
"NORMAL":1,
|
||||
"TEXCOORD_0":2
|
||||
},
|
||||
"indices":3,
|
||||
"material":0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"accessors":[
|
||||
{
|
||||
"bufferView":0,
|
||||
"componentType":5126,
|
||||
"count":24,
|
||||
"max":[
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"min":[
|
||||
-1,
|
||||
-1,
|
||||
-1
|
||||
],
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":1,
|
||||
"componentType":5126,
|
||||
"count":24,
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":2,
|
||||
"componentType":5126,
|
||||
"count":24,
|
||||
"type":"VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView":3,
|
||||
"componentType":5123,
|
||||
"count":36,
|
||||
"type":"SCALAR"
|
||||
}
|
||||
],
|
||||
"bufferViews":[
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":288,
|
||||
"byteOffset":0,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":288,
|
||||
"byteOffset":288,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":192,
|
||||
"byteOffset":576,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":72,
|
||||
"byteOffset":768,
|
||||
"target":34963
|
||||
}
|
||||
],
|
||||
"buffers":[
|
||||
{
|
||||
"byteLength":840,
|
||||
"uri":"cube.bin"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
183
examples/assets/cube_texture2.svg
Normal file
@@ -0,0 +1,183 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="200"
|
||||
height="200"
|
||||
viewBox="0 0 1 1"
|
||||
version="1.1"
|
||||
id="svg10"
|
||||
sodipodi:docname="cube_texture2.svg"
|
||||
inkscape:export-filename="cube_texture_512x512.png"
|
||||
inkscape:export-xdpi="245.75999"
|
||||
inkscape:export-ydpi="245.75999"
|
||||
inkscape:version="1.4 (e7c3feb1, 2024-10-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs10" />
|
||||
<sodipodi:namedview
|
||||
id="namedview10"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="1.5185118"
|
||||
inkscape:cx="255.18405"
|
||||
inkscape:cy="172.86662"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="964"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="25"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg10"
|
||||
showgrid="false" />
|
||||
<!-- Background Grid (optional, for visual aid) -->
|
||||
<rect
|
||||
x="0"
|
||||
y="0"
|
||||
width="1"
|
||||
height="1"
|
||||
fill="#f0f0f0"
|
||||
id="rect1" />
|
||||
<line
|
||||
x1="0.333"
|
||||
y1="0"
|
||||
x2="0.333"
|
||||
y2="1"
|
||||
stroke="#ccc"
|
||||
stroke-width="0.005"
|
||||
id="line1" />
|
||||
<line
|
||||
x1="0.666"
|
||||
y1="0"
|
||||
x2="0.666"
|
||||
y2="1"
|
||||
stroke="#ccc"
|
||||
stroke-width="0.005"
|
||||
id="line2" />
|
||||
<line
|
||||
y1="0.25"
|
||||
x1="0"
|
||||
y2="0.25"
|
||||
x2="1"
|
||||
stroke="#ccc"
|
||||
stroke-width="0.005"
|
||||
id="line3" />
|
||||
<line
|
||||
y1="0.5"
|
||||
x1="0"
|
||||
y2="0.5"
|
||||
x2="1"
|
||||
stroke="#ccc"
|
||||
stroke-width="0.005"
|
||||
id="line4" />
|
||||
<line
|
||||
y1="0.75"
|
||||
x1="0"
|
||||
y2="0.75"
|
||||
x2="1"
|
||||
stroke="#ccc"
|
||||
stroke-width="0.005"
|
||||
id="line5" />
|
||||
<!-- Front Face -->
|
||||
<rect
|
||||
x="0.333"
|
||||
y="0"
|
||||
width="0.333"
|
||||
height="0.25"
|
||||
fill="#ff0000"
|
||||
id="rect5" />
|
||||
<text
|
||||
x="0.5"
|
||||
y="0.125"
|
||||
text-anchor="middle"
|
||||
dominant-baseline="middle"
|
||||
font-size="0.1"
|
||||
fill="white"
|
||||
id="text5">Front</text>
|
||||
<!-- Back Face -->
|
||||
<rect
|
||||
x="0.333"
|
||||
y="0.5"
|
||||
width="0.333"
|
||||
height="0.25"
|
||||
fill="#00ff00"
|
||||
id="rect6" />
|
||||
<text
|
||||
x="0.5"
|
||||
y="0.625"
|
||||
text-anchor="middle"
|
||||
dominant-baseline="middle"
|
||||
font-size="0.1"
|
||||
fill="white"
|
||||
id="text6">Back</text>
|
||||
<!-- Top Face -->
|
||||
<rect
|
||||
x="0.666"
|
||||
y="0.25"
|
||||
width="0.333"
|
||||
height="0.25"
|
||||
fill="#0000ff"
|
||||
id="rect7" />
|
||||
<text
|
||||
x="0.833"
|
||||
y="0.375"
|
||||
text-anchor="middle"
|
||||
dominant-baseline="middle"
|
||||
font-size="0.1"
|
||||
fill="white"
|
||||
id="text7">Top</text>
|
||||
<!-- Bottom Face -->
|
||||
<rect
|
||||
x="0"
|
||||
y="0.25"
|
||||
width="0.333"
|
||||
height="0.25"
|
||||
fill="#ffff00"
|
||||
id="rect8" />
|
||||
<text
|
||||
x="0.15610458"
|
||||
y="0.37936932"
|
||||
text-anchor="middle"
|
||||
dominant-baseline="middle"
|
||||
font-size="0.0733811px"
|
||||
fill="#000000"
|
||||
id="text8"
|
||||
style="stroke-width:0.733811">Bottom</text>
|
||||
<!-- Right Face -->
|
||||
<rect
|
||||
x="0.333"
|
||||
y="0.25"
|
||||
width="0.333"
|
||||
height="0.25"
|
||||
fill="#ff00ff"
|
||||
id="rect9" />
|
||||
<text
|
||||
x="0.5"
|
||||
y="0.375"
|
||||
text-anchor="middle"
|
||||
dominant-baseline="middle"
|
||||
font-size="0.1"
|
||||
fill="white"
|
||||
id="text9">Right</text>
|
||||
<!-- Left Face -->
|
||||
<rect
|
||||
x="0.333"
|
||||
y="0.75"
|
||||
width="0.333"
|
||||
height="0.25"
|
||||
fill="#00ffff"
|
||||
id="rect10" />
|
||||
<text
|
||||
x="0.5"
|
||||
y="0.875"
|
||||
text-anchor="middle"
|
||||
dominant-baseline="middle"
|
||||
font-size="0.1"
|
||||
fill="black"
|
||||
id="text10">Left</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
BIN
examples/assets/cube_with_morph_targets.glb
Normal file
BIN
examples/assets/green_12x12.png
Normal file
|
After Width: | Height: | Size: 312 B |
BIN
examples/assets/red_24x24.png
Normal file
|
After Width: | Height: | Size: 313 B |
4
examples/dart/cli_headless/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# https://dart.dev/guides/libraries/private-files
|
||||
# Created by `dart pub`
|
||||
.dart_tool/
|
||||
output/**
|
||||
3
examples/dart/cli_headless/CHANGELOG.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 1.0.0
|
||||
|
||||
- Initial version.
|
||||
9
examples/dart/cli_headless/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# cli_headless
|
||||
|
||||
A simple headless Dart CLI app using Thermion to render to a bitmap.
|
||||
|
||||
To run:
|
||||
`dart --enable-experiment=native-assets run bin/example.dart`
|
||||
|
||||
Currently, this will probably only work on a non-virtualized macOS host.
|
||||
|
||||
30
examples/dart/cli_headless/analysis_options.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
# This file configures the static analysis results for your project (errors,
|
||||
# warnings, and lints).
|
||||
#
|
||||
# This enables the 'recommended' set of lints from `package:lints`.
|
||||
# This set helps identify many issues that may lead to problems when running
|
||||
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||
# style and format.
|
||||
#
|
||||
# If you want a smaller set of lints you can change this to specify
|
||||
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||
# (the recommended set includes the core lints).
|
||||
# The core lints are also what is used by pub.dev for scoring packages.
|
||||
|
||||
include: package:lints/recommended.yaml
|
||||
|
||||
# Uncomment the following section to specify additional rules.
|
||||
|
||||
# linter:
|
||||
# rules:
|
||||
# - camel_case_types
|
||||
|
||||
# analyzer:
|
||||
# exclude:
|
||||
# - path/to/excluded/files/**
|
||||
|
||||
# For more information about the core and recommended set of lints, see
|
||||
# https://dart.dev/go/core-lints
|
||||
|
||||
# For additional information about configuring this file, see
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
32
examples/dart/cli_headless/bin/example.dart
Normal file
@@ -0,0 +1,32 @@
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:thermion_dart/thermion_dart.dart';
|
||||
import 'package:thermion_dart/src/filament/src/implementation/ffi_filament_app.dart';
|
||||
|
||||
void main() async {
|
||||
await FFIFilamentApp.create();
|
||||
final (width, height) = (500, 500);
|
||||
final sc = await FilamentApp.instance!.createHeadlessSwapChain(width, height);
|
||||
var viewer = ThermionViewerFFI();
|
||||
await viewer.initialized;
|
||||
|
||||
await FilamentApp.instance!.register(sc, viewer.view);
|
||||
|
||||
await viewer.view.setFrustumCullingEnabled(false);
|
||||
await viewer.setBackgroundColor(1, 0, 1, 1);
|
||||
await viewer.setViewport(width, height);
|
||||
final result = await FilamentApp.instance!.capture(
|
||||
sc,
|
||||
view: viewer.view,
|
||||
);
|
||||
|
||||
final bitmap = await pixelBufferToBmp(result.first.$2, width, height,
|
||||
hasAlpha: true, isFloat: true);
|
||||
|
||||
var outfile = File("output/render.bmp");
|
||||
outfile.parent.create();
|
||||
outfile.writeAsBytesSync(bitmap);
|
||||
await FilamentApp.instance!.destroy();
|
||||
Isolate.current.kill();
|
||||
}
|
||||
21
examples/dart/cli_headless/pubspec.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
name: example_cli
|
||||
description: A sample command-line application.
|
||||
version: 1.0.0
|
||||
|
||||
environment:
|
||||
sdk: ^3.3.0
|
||||
|
||||
hooks:
|
||||
user_defines:
|
||||
thermion_dart:
|
||||
mode: debug
|
||||
tracing: enabled
|
||||
|
||||
dependencies:
|
||||
thermion_dart:
|
||||
path: ../../../thermion_dart
|
||||
|
||||
dev_dependencies:
|
||||
ffigen: ^11.0.0
|
||||
lints: ^3.0.0
|
||||
test: ^1.24.0
|
||||
@@ -1,9 +1,8 @@
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:thermion_dart/src/utils/src/dart_resources.dart';
|
||||
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
|
||||
import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_viewer_ffi.dart';
|
||||
import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_dart.g.dart';
|
||||
import 'package:thermion_dart/thermion_dart.dart';
|
||||
@@ -13,29 +12,17 @@ import 'package:cli_windows/thermion_window.g.dart';
|
||||
void main(List<String> arguments) async {
|
||||
var hwnd = create_thermion_window(500, 500, 0, 0);
|
||||
update();
|
||||
|
||||
final resourceLoader = calloc<ResourceLoaderWrapper>(1);
|
||||
|
||||
var loadToOut = NativeCallable<
|
||||
Void Function(Pointer<Char>,
|
||||
Pointer<ResourceBuffer>)>.listener(DartResourceLoader.loadResource);
|
||||
|
||||
resourceLoader.ref.loadToOut = loadToOut.nativeFunction;
|
||||
var freeResource = NativeCallable<Void Function(ResourceBuffer)>.listener(
|
||||
DartResourceLoader.freeResource);
|
||||
resourceLoader.ref.freeResource = freeResource.nativeFunction;
|
||||
|
||||
var viewer = ThermionViewerFFI(
|
||||
resourceLoader: resourceLoader.cast<Void>());
|
||||
await FFIFilamentApp.create();
|
||||
var viewer = ThermionViewerFFI(
|
||||
loadAssetFromUri: (path) async => File(path.replaceAll("file://", "")).readAsBytesSync());
|
||||
|
||||
await viewer.initialized;
|
||||
var swapChain = await viewer.createHeadlessSwapChain(500,500);
|
||||
var view = await viewer.getViewAt(0);
|
||||
await view.updateViewport(500, 500);
|
||||
var camera = await viewer.getMainCamera();
|
||||
var swapChain = await FilamentApp.instance!.createSwapChain(Pointer<Void>.fromAddress(hwnd));
|
||||
var view = viewer.view;
|
||||
await view.setViewport(500, 500);
|
||||
var camera = await viewer.getActiveCamera();
|
||||
await camera.setLensProjection();
|
||||
|
||||
await view.setRenderable(true, swapChain);
|
||||
await FilamentApp.instance!.register(swapChain, view);
|
||||
|
||||
await viewer.setBackgroundColor(1.0, 0.0, 0.0, 1.0);
|
||||
|
||||
@@ -49,15 +36,15 @@ void main(List<String> arguments) async {
|
||||
|
||||
var last = 0;
|
||||
|
||||
await viewer.setCameraPosition(0, 0, 10);
|
||||
await camera.lookAt(Vector3(0, 0, 10));
|
||||
|
||||
while(true) {
|
||||
var angle = (stopwatch.elapsedMilliseconds / 1000) * 2 * pi;
|
||||
var rotation = Quaternion.axisAngle(Vector3(0,1,0), angle);
|
||||
var position = Vector3(10 * sin(angle), 0, 10 * cos(angle));
|
||||
var modelMatrix = Matrix4.compose(position, rotation, Vector3.all(1));
|
||||
await viewer.setCameraModelMatrix4(modelMatrix);
|
||||
await viewer.render();
|
||||
await camera.setModelMatrix(modelMatrix);
|
||||
await FilamentApp.instance!.requestFrame();
|
||||
update();
|
||||
await Future.delayed(Duration(milliseconds: 17));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import 'dart:io';
|
||||
import 'package:archive/archive.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:native_assets_cli/native_assets_cli.dart';
|
||||
import 'package:native_toolchain_c/native_toolchain_c.dart';
|
||||
@@ -10,23 +8,22 @@ void main(List<String> args) async {
|
||||
..level = Level.ALL
|
||||
..onRecord.listen((record) => print(
|
||||
record.message + "\n"));
|
||||
|
||||
await build(args, (config, output) async {
|
||||
|
||||
await build(args, (input, output) async {
|
||||
final cbuilder = CBuilder.library(
|
||||
name: "thermion_window",
|
||||
name: input.packageName,
|
||||
language: Language.cpp,
|
||||
assetName: 'thermion_window.dart',
|
||||
assetName: 'cli_windows.dart',
|
||||
sources: ['native/thermion_window.cpp'],
|
||||
includes: ['native', '../../../thermion_dart/native/include'],
|
||||
defines: {"UNICODE":"1"},
|
||||
flags:[],
|
||||
dartBuildFiles: ['hook/build.dart'],
|
||||
|
||||
);
|
||||
|
||||
await cbuilder.run(
|
||||
buildConfig: config,
|
||||
buildOutput: output,
|
||||
input: input,
|
||||
output: output,
|
||||
logger: logger,
|
||||
);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//
|
||||
// Generated by `package:ffigen`.
|
||||
// ignore_for_file: type=lint
|
||||
@ffi.DefaultAsset('package:cli_windows/thermion_window.dart')
|
||||
@ffi.DefaultAsset('package:cli_windows/cli_windows.dart')
|
||||
library;
|
||||
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
@@ -1,42 +1,21 @@
|
||||
name: cli_windows
|
||||
description: A sample command-line application with basic argument parsing.
|
||||
version: 0.0.1
|
||||
# repository: https://github.com/my_org/my_repo
|
||||
|
||||
environment:
|
||||
sdk: ^3.6.0-326.0.dev
|
||||
|
||||
# Add regular dependencies here.
|
||||
dependencies:
|
||||
args: ^2.5.0
|
||||
thermion_dart:
|
||||
path: ../../../thermion_dart
|
||||
ffi: ^2.1.3
|
||||
vector_math: ^2.1.4
|
||||
native_toolchain_c: ^0.4.2
|
||||
native_assets_cli: ^0.6.1
|
||||
native_toolchain_c: ^0.9.0
|
||||
native_assets_cli: ^0.12.0
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^5.0.0
|
||||
test: ^1.24.0
|
||||
ffigen: ^13.0.0
|
||||
ffigen:
|
||||
output: 'lib/thermion_window.g.dart'
|
||||
headers:
|
||||
entry-points:
|
||||
- 'native/thermion_window.h'
|
||||
include-directives:
|
||||
- 'native/thermion_window.h'
|
||||
ffi-native:
|
||||
assetId: package:cli_windows/thermion_window.dart
|
||||
ignore-source-errors: true
|
||||
llvm-path:
|
||||
- E:\clang+llvm-19.1.3-x86_64-pc-windows-msvc\bin
|
||||
- E:\clang+llvm-19.1.3-x86_64-pc-windows-msvc\
|
||||
- E:\clang+llvm-19.1.3-x86_64-pc-windows-msvc\lib
|
||||
functions:
|
||||
leaf:
|
||||
include:
|
||||
- '.*'
|
||||
|
||||
|
||||
|
||||
19
examples/dart/js_wasm/pubspec.yaml
Normal file
@@ -0,0 +1,19 @@
|
||||
name: example_web
|
||||
description: A sample command-line application.
|
||||
version: 1.0.0
|
||||
# repository: https://github.com/my_org/my_repo
|
||||
|
||||
environment:
|
||||
sdk: ^3.3.0
|
||||
|
||||
dependencies:
|
||||
thermion_dart:
|
||||
path: ../../../thermion_dart
|
||||
logging: ^1.3.0
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^3.0.0
|
||||
test: ^1.24.0
|
||||
build_runner: ^2.4.13
|
||||
build_test: ^2.2.2
|
||||
build_web_compilers: ^4.1.5
|
||||
1
examples/dart/js_wasm/web/assets
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../assets/
|
||||
101
examples/dart/js_wasm/web/example.dart
Normal file
@@ -0,0 +1,101 @@
|
||||
import 'dart:async';
|
||||
import 'dart:js_interop';
|
||||
import 'dart:math';
|
||||
import 'package:web/web.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:thermion_dart/thermion_dart.dart'
|
||||
hide NativeLibrary, Image_decode;
|
||||
import 'package:thermion_dart/src/filament/src/implementation/ffi_filament_app.dart';
|
||||
import 'web_input_handler.dart';
|
||||
import 'package:thermion_dart/src/bindings/src/thermion_dart_js_interop.g.dart';
|
||||
|
||||
void main(List<String> arguments) async {
|
||||
Logger.root.onRecord.listen((record) {
|
||||
print(record);
|
||||
});
|
||||
|
||||
NativeLibrary.initBindings("thermion_dart");
|
||||
|
||||
final canvas =
|
||||
document.getElementById("thermion_canvas") as HTMLCanvasElement;
|
||||
try {
|
||||
canvas.width = canvas.clientWidth;
|
||||
canvas.height = canvas.clientHeight;
|
||||
} catch (err) {
|
||||
print(err.toString());
|
||||
}
|
||||
|
||||
final config = FFIFilamentConfig(backend: Backend.OPENGL);
|
||||
|
||||
await FFIFilamentApp.create(config: config);
|
||||
|
||||
var swapChain = await FilamentApp.instance!
|
||||
.createHeadlessSwapChain(canvas.width, canvas.height);
|
||||
final viewer = ThermionViewerFFI();
|
||||
await viewer.initialized;
|
||||
await FilamentApp.instance!.setClearOptions(1.0, 0.0, 0.0, 1.0);
|
||||
await FilamentApp.instance!.register(swapChain, viewer.view);
|
||||
await viewer.setViewport(canvas.width, canvas.height);
|
||||
await viewer.setRendering(true);
|
||||
final rnd = Random();
|
||||
// ignore: prefer_function_declarations_over_variables
|
||||
bool resizing = false;
|
||||
// ignore: prefer_function_declarations_over_variables
|
||||
final resizer = () async {
|
||||
if (resizing) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
resizing = true;
|
||||
await viewer.setViewport(canvas.clientWidth, canvas.clientHeight);
|
||||
Thermion_resizeCanvas(canvas.clientWidth, canvas.clientHeight);
|
||||
} catch (err) {
|
||||
print(err);
|
||||
} finally {
|
||||
resizing = false;
|
||||
}
|
||||
};
|
||||
// ignore: unused_local_variable, prefer_function_declarations_over_variables
|
||||
final jsWrapper = () {
|
||||
var promise = resizer().toJS;
|
||||
return promise;
|
||||
};
|
||||
window.addEventListener('resize', jsWrapper.toJS);
|
||||
// // await FilamentApp.instance!.render();
|
||||
// // await Future.delayed(Duration(seconds: 1));
|
||||
|
||||
// // await FilamentApp.instance!.setClearOptions(1.0, 1.0, 0.0, 1.0);
|
||||
// // await FilamentApp.instance!.render();
|
||||
// // await Future.delayed(Duration(seconds: 1));
|
||||
|
||||
await viewer.loadSkybox("assets/default_env_skybox.ktx");
|
||||
await viewer.loadGltf("assets/cube.glb");
|
||||
final camera = await viewer.getActiveCamera();
|
||||
|
||||
var zOffset = 10.0;
|
||||
|
||||
final inputHandler = DelegateInputHandler.flight(viewer);
|
||||
|
||||
final webInputHandler =
|
||||
WebInputHandler(inputHandler: inputHandler, canvas: canvas);
|
||||
await camera.lookAt(Vector3(0, 0, zOffset));
|
||||
DateTime lastRender = DateTime.now();
|
||||
|
||||
while (true) {
|
||||
var stackPtr = stackSave();
|
||||
var now = DateTime.now();
|
||||
await FilamentApp.instance!.requestFrame();
|
||||
now = DateTime.now();
|
||||
var timeSinceLast =
|
||||
now.microsecondsSinceEpoch - lastRender.microsecondsSinceEpoch;
|
||||
lastRender = now;
|
||||
if (timeSinceLast < 1667) {
|
||||
var waitFor = 1667 - timeSinceLast;
|
||||
await Future.delayed(Duration(microseconds: waitFor));
|
||||
}
|
||||
stackRestore(stackPtr);
|
||||
// inputHandler.keyDown(PhysicalKey.S);
|
||||
// await camera.lookAt(Vector3(0,0,zOffset));
|
||||
// zOffset +=0.1;
|
||||
}
|
||||
}
|
||||
35
examples/dart/js_wasm/web/index_js.html
Normal file
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript" src="./thermion_dart.js"></script>
|
||||
<script type="module">
|
||||
try {
|
||||
window.thermion_dart = await thermion_dart();
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
}
|
||||
const script = document.createElement('script')
|
||||
script.src = 'example.dart.js'
|
||||
document.head.append(script);
|
||||
</script>
|
||||
<style>
|
||||
html, body {
|
||||
margin:0;
|
||||
padding:0;
|
||||
}
|
||||
canvas {
|
||||
position:absolute;
|
||||
left:0;
|
||||
right:0;
|
||||
top:0;
|
||||
bottom:0;
|
||||
width:100%;
|
||||
height:100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<canvas id="thermion_canvas"></canvas>
|
||||
</body>
|
||||
</html>
|
||||
35
examples/dart/js_wasm/web/index_wasm.html
Normal file
@@ -0,0 +1,35 @@
|
||||
<html>
|
||||
<head>
|
||||
<script src="thermion_dart.js"></script>
|
||||
<style>
|
||||
html, body {
|
||||
margin:0;
|
||||
padding:0;
|
||||
}
|
||||
canvas {
|
||||
position:absolute;
|
||||
left:0;
|
||||
right:0;
|
||||
top:0;
|
||||
bottom:0;
|
||||
width:100%;
|
||||
height:100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<script type="module">
|
||||
try {
|
||||
window.thermion_dart = await thermion_dart();
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
}
|
||||
const dartModulePromise = WebAssembly.compileStreaming(fetch('./example.wasm'));
|
||||
const imports = {};
|
||||
const dart2wasm_runtime = await import('./example.mjs');
|
||||
const moduleInstance = await dart2wasm_runtime.instantiate(dartModulePromise, imports);
|
||||
await dart2wasm_runtime.invoke(moduleInstance);
|
||||
</script>
|
||||
<body>
|
||||
<canvas id="canvas"></canvas>
|
||||
</body>
|
||||
</html>
|
||||
22
examples/dart/js_wasm/web/main.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import { readFile } from 'fs/promises';
|
||||
|
||||
import { compile } from './example.mjs';
|
||||
import thermion_dart from './thermion_dart.js';
|
||||
|
||||
|
||||
async function runDartWasm() {
|
||||
globalThis['thermion_dart'] = await thermion_dart();
|
||||
|
||||
const wasmBytes = await readFile('example.wasm');
|
||||
const compiledApp = await compile(wasmBytes);
|
||||
const instantiatedApp = await compiledApp.instantiate({});
|
||||
try {
|
||||
instantiatedApp.invokeMain();
|
||||
} catch(err) {
|
||||
console.error("Failed");
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
runDartWasm().catch(console.error);
|
||||
|
||||
1
examples/dart/js_wasm/web/thermion_dart.js
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../thermion_dart/native/web/build/build/out/thermion_dart.js
|
||||
1
examples/dart/js_wasm/web/thermion_dart.wasm
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../thermion_dart/native/web/build/build/out/thermion_dart.wasm
|
||||
299
examples/dart/js_wasm/web/web_input_handler.dart
Normal file
@@ -0,0 +1,299 @@
|
||||
import 'dart:js_interop';
|
||||
|
||||
import 'package:web/web.dart' as web;
|
||||
import 'package:thermion_dart/thermion_dart.dart';
|
||||
|
||||
class WebInputHandler {
|
||||
final InputHandler inputHandler;
|
||||
final web.HTMLCanvasElement canvas;
|
||||
late double pixelRatio;
|
||||
|
||||
final Map<int, Vector2> _touchPositions = {};
|
||||
|
||||
WebInputHandler({
|
||||
required this.inputHandler,
|
||||
required this.canvas,
|
||||
}) {
|
||||
pixelRatio = web.window.devicePixelRatio;
|
||||
_initializeEventListeners();
|
||||
}
|
||||
|
||||
void _initializeEventListeners() {
|
||||
canvas.addEventListener('mousedown', _onMouseDown.toJS);
|
||||
canvas.addEventListener('mousemove', _onMouseMove.toJS);
|
||||
canvas.addEventListener('mouseup', _onMouseUp.toJS);
|
||||
canvas.addEventListener('wheel', _onMouseWheel.toJS);
|
||||
web.window.addEventListener('keydown', _onKeyDown.toJS);
|
||||
web.window.addEventListener('keyup', _onKeyUp.toJS);
|
||||
|
||||
canvas.addEventListener('touchstart', _onTouchStart.toJS);
|
||||
canvas.addEventListener('touchmove', _onTouchMove.toJS);
|
||||
canvas.addEventListener('touchend', _onTouchEnd.toJS);
|
||||
canvas.addEventListener('touchcancel', _onTouchCancel.toJS);
|
||||
}
|
||||
|
||||
void _onMouseDown(web.MouseEvent event) {
|
||||
final localPos = _getLocalPositionFromEvent(event);
|
||||
final button = _getMouseButtonFromEvent(event);
|
||||
|
||||
inputHandler.handle(MouseEvent(
|
||||
MouseEventType.buttonDown,
|
||||
button,
|
||||
localPos,
|
||||
Vector2.zero(),
|
||||
));
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
void _onMouseMove(web.MouseEvent event) {
|
||||
final localPos = _getLocalPositionFromEvent(event);
|
||||
final delta = Vector2(event.movementX ?? 0, event.movementY ?? 0);
|
||||
final button = _getMouseButtonFromEvent(event);
|
||||
|
||||
inputHandler.handle(MouseEvent(
|
||||
MouseEventType.move,
|
||||
button,
|
||||
localPos,
|
||||
delta,
|
||||
));
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
void _onMouseUp(web.MouseEvent event) {
|
||||
final localPos = _getLocalPositionFromEvent(event);
|
||||
final button = _getMouseButtonFromEvent(event);
|
||||
|
||||
inputHandler.handle(MouseEvent(
|
||||
MouseEventType.buttonUp,
|
||||
button,
|
||||
localPos,
|
||||
Vector2.zero(),
|
||||
));
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
void _onMouseWheel(web.WheelEvent event) {
|
||||
final localPos = _getLocalPositionFromEvent(event);
|
||||
final delta = event.deltaY;
|
||||
|
||||
inputHandler.handle(ScrollEvent(
|
||||
localPosition: localPos,
|
||||
delta: delta,
|
||||
));
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
void _onKeyDown(web.KeyboardEvent event) {
|
||||
PhysicalKey? physicalKey = _getPhysicalKeyFromEvent(event);
|
||||
LogicalKey? logicalKey = _getLogicalKeyFromEvent(event);
|
||||
if (physicalKey != null && logicalKey != null) {
|
||||
inputHandler.handle(KeyEvent(KeyEventType.down, logicalKey, physicalKey));
|
||||
}
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
void _onKeyUp(web.KeyboardEvent event) {
|
||||
LogicalKey? logicalKey = _getLogicalKeyFromEvent(event);
|
||||
PhysicalKey? physicalKey = _getPhysicalKeyFromEvent(event);
|
||||
if (physicalKey != null && logicalKey != null) {
|
||||
inputHandler.handle(KeyEvent(KeyEventType.up, logicalKey, physicalKey));
|
||||
}
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
void _onTouchStart(web.TouchEvent event) {
|
||||
for (var touch in event.changedTouches.toList()) {
|
||||
final pos = _getLocalPositionFromTouch(touch);
|
||||
_touchPositions[touch.identifier] = pos;
|
||||
}
|
||||
|
||||
final touchCount = event.touches.toList().length;
|
||||
if (touchCount == 1) {
|
||||
final touch = event.touches.toList().first;
|
||||
final pos = _getLocalPositionFromTouch(touch);
|
||||
inputHandler.handle(TouchEvent(
|
||||
TouchEventType.tap,
|
||||
pos,
|
||||
null,
|
||||
));
|
||||
}
|
||||
|
||||
if (touchCount >= 2) {
|
||||
final focalPoint = _calculateFocalPoint(event.touches.toList());
|
||||
inputHandler.handle(ScaleStartEvent(
|
||||
numPointers: touchCount,
|
||||
localFocalPoint: (focalPoint.x, focalPoint.y),
|
||||
));
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
void _onTouchMove(web.TouchEvent event) {
|
||||
for (var touch in event.changedTouches.toList()) {
|
||||
final id = touch.identifier;
|
||||
final currPos = _getLocalPositionFromTouch(touch);
|
||||
final prevPos = _touchPositions[id];
|
||||
|
||||
if (prevPos != null) {
|
||||
_touchPositions[id] = currPos;
|
||||
}
|
||||
}
|
||||
|
||||
final touchCount = event.touches.toList().length;
|
||||
if (touchCount >= 2) {
|
||||
final touches = event.touches.toList();
|
||||
final focalPoint = _calculateFocalPoint(touches);
|
||||
|
||||
// Calculate scale
|
||||
final currPositions = touches.map((t) => _getLocalPositionFromTouch(t)).toList();
|
||||
final prevPositions = touches.map((t) => _touchPositions[t.identifier] ?? _getLocalPositionFromTouch(t)).toList();
|
||||
|
||||
final currDist = (currPositions[0] - currPositions[1]).length;
|
||||
final prevDist = (prevPositions[0] - prevPositions[1]).length;
|
||||
final scale = prevDist > 0 ? currDist / prevDist : 1.0;
|
||||
|
||||
// Calculate focal point delta
|
||||
final prevFocalPoint = _calculateFocalPoint(touches, prevPositions);
|
||||
final focalPointDelta = focalPoint - prevFocalPoint;
|
||||
|
||||
inputHandler.handle(ScaleUpdateEvent(
|
||||
numPointers: touchCount,
|
||||
localFocalPoint: (focalPoint.x, focalPoint.y),
|
||||
localFocalPointDelta: (focalPointDelta.x, focalPointDelta.y),
|
||||
rotation: 0.0, // We don't track rotation in the web implementation
|
||||
scale: scale,
|
||||
horizontalScale: scale,
|
||||
verticalScale: scale,
|
||||
));
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
void _onTouchEnd(web.TouchEvent event) {
|
||||
for (var touch in event.changedTouches.toList()) {
|
||||
_touchPositions.remove(touch.identifier);
|
||||
}
|
||||
|
||||
final touchCount = event.touches.toList().length;
|
||||
inputHandler.handle(ScaleEndEvent(
|
||||
numPointers: touchCount,
|
||||
));
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
void _onTouchCancel(web.TouchEvent event) {
|
||||
for (var touch in event.changedTouches.toList()) {
|
||||
_touchPositions.remove(touch.identifier);
|
||||
}
|
||||
|
||||
final touchCount = event.touches.toList().length;
|
||||
inputHandler.handle(ScaleEndEvent(
|
||||
numPointers: touchCount,
|
||||
));
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
MouseButton? _getMouseButtonFromEvent(web.MouseEvent event) {
|
||||
if (event.button == 1 || (event.buttons & 4 != 0)) {
|
||||
return MouseButton.middle;
|
||||
} else if (event.button == 0 || (event.buttons & 1 != 0)) {
|
||||
return MouseButton.left;
|
||||
} else if (event.button == 2 || (event.buttons & 2 != 0)) {
|
||||
return MouseButton.right;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
PhysicalKey? _getPhysicalKeyFromEvent(web.KeyboardEvent event) {
|
||||
switch (event.code) {
|
||||
case 'KeyW':
|
||||
return PhysicalKey.w;
|
||||
case 'KeyA':
|
||||
return PhysicalKey.a;
|
||||
case 'KeyS':
|
||||
return PhysicalKey.s;
|
||||
case 'KeyD':
|
||||
return PhysicalKey.d;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
LogicalKey? _getLogicalKeyFromEvent(web.KeyboardEvent event) {
|
||||
switch (event.key) {
|
||||
case 'KeyW':
|
||||
return LogicalKey.w;
|
||||
case 'KeyA':
|
||||
return LogicalKey.a;
|
||||
case 'KeyS':
|
||||
return LogicalKey.s;
|
||||
case 'KeyD':
|
||||
return LogicalKey.d;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 _getLocalPositionFromEvent(web.Event event) {
|
||||
final rect = canvas.getBoundingClientRect();
|
||||
double clientX = 0, clientY = 0;
|
||||
|
||||
if (event is web.MouseEvent) {
|
||||
clientX = event.clientX.toDouble();
|
||||
clientY = event.clientY.toDouble();
|
||||
} else if (event is web.TouchEvent) {
|
||||
final touch = event.touches.toList().firstOrNull;
|
||||
if (touch != null) {
|
||||
clientX = touch.clientX;
|
||||
clientY = touch.clientY;
|
||||
}
|
||||
}
|
||||
|
||||
return Vector2(
|
||||
(clientX - rect.left) * pixelRatio,
|
||||
(clientY - rect.top) * pixelRatio,
|
||||
);
|
||||
}
|
||||
|
||||
Vector2 _getLocalPositionFromTouch(web.Touch touch) {
|
||||
final rect = canvas.getBoundingClientRect();
|
||||
return Vector2(
|
||||
(touch.clientX - rect.left) * pixelRatio,
|
||||
(touch.clientY - rect.top) * pixelRatio,
|
||||
);
|
||||
}
|
||||
|
||||
Vector2 _calculateFocalPoint(List<web.Touch> touches, [List<Vector2>? positions]) {
|
||||
if (touches.isEmpty) return Vector2.zero();
|
||||
|
||||
final points = positions ?? touches.map((t) => _getLocalPositionFromTouch(t)).toList();
|
||||
|
||||
Vector2 sum = Vector2.zero();
|
||||
for (var point in points) {
|
||||
sum += point;
|
||||
}
|
||||
|
||||
return sum.scaled(1.0 / points.length);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
canvas.removeEventListener('mousedown', _onMouseDown.toJS);
|
||||
canvas.removeEventListener('mousemove', _onMouseMove.toJS);
|
||||
canvas.removeEventListener('mouseup', _onMouseUp.toJS);
|
||||
canvas.removeEventListener('wheel', _onMouseWheel.toJS);
|
||||
web.window.removeEventListener('keydown', _onKeyDown.toJS);
|
||||
web.window.removeEventListener('keyup', _onKeyUp.toJS);
|
||||
canvas.removeEventListener('touchstart', _onTouchStart.toJS);
|
||||
canvas.removeEventListener('touchmove', _onTouchMove.toJS);
|
||||
canvas.removeEventListener('touchend', _onTouchEnd.toJS);
|
||||
canvas.removeEventListener('touchcancel', _onTouchCancel.toJS);
|
||||
}
|
||||
}
|
||||
46
examples/flutter/animations/android/app/build.gradle
Normal file
@@ -0,0 +1,46 @@
|
||||
plugins {
|
||||
id "com.android.application"
|
||||
id "kotlin-android"
|
||||
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
||||
id "dev.flutter.flutter-gradle-plugin"
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.example.quickstart"
|
||||
compileSdk = flutter.compileSdkVersion
|
||||
ndkVersion = flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId = "com.example.quickstart"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||
minSdk = 22
|
||||
targetSdk = flutter.targetSdkVersion
|
||||
versionCode = flutter.versionCode
|
||||
versionName = flutter.versionName
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
signingConfig = signingConfigs.debug
|
||||
shrinkResources false
|
||||
minifyEnabled false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flutter {
|
||||
source = "../.."
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<application
|
||||
android:label="camera_manipulation"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
android:taskAffinity=""
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.EnableImpeller"
|
||||
android:value="false" />
|
||||
</application>
|
||||
<!-- Required to query activities that can process text, see:
|
||||
https://developer.android.com/training/package-visibility and
|
||||
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
||||
|
||||
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.PROCESS_TEXT"/>
|
||||
<data android:mimeType="text/plain"/>
|
||||
</intent>
|
||||
</queries>
|
||||
</manifest>
|
||||
|
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 544 B |
|
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 442 B |
|
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 721 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 295 B |
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 406 B |
|
Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 450 B |
|
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 282 B |
|
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 462 B |
|
Before Width: | Height: | Size: 704 B After Width: | Height: | Size: 704 B |
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 406 B |
|
Before Width: | Height: | Size: 586 B After Width: | Height: | Size: 586 B |
|
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 862 B |
|
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 862 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 762 B After Width: | Height: | Size: 762 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |