Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2730fbc31b | ||
|
|
7d3b8eb7b9 | ||
|
|
557387375f | ||
|
|
902f869721 | ||
|
|
ad1bc6f360 | ||
|
|
73c343635e | ||
|
|
432e672022 | ||
|
|
b56b04c5f8 | ||
|
|
99816d67c2 | ||
|
|
d6d4f92922 | ||
|
|
6a59a68622 | ||
|
|
4580f57987 | ||
|
|
38f7e579f1 | ||
|
|
9b1c8a2bf5 | ||
|
|
4504471021 | ||
|
|
37c316fa03 | ||
|
|
14960f7118 | ||
|
|
1deb657442 | ||
|
|
45c0d1b34f | ||
|
|
1ddd10f326 | ||
|
|
308668a705 | ||
|
|
1cd48619e3 | ||
|
|
89c3b3f40b | ||
|
|
e830ec28e4 | ||
|
|
b58ffb87e0 | ||
|
|
385d8969cf | ||
|
|
53bc372876 | ||
|
|
58f6d77e78 | ||
|
|
3769d0a9d3 | ||
|
|
2bc71240cf | ||
|
|
e1fb3f7442 | ||
|
|
e832805faf | ||
|
|
2ce71d6d98 | ||
|
|
26c51e0d9a | ||
|
|
510ae15867 | ||
|
|
d6caa9dc0b | ||
|
|
19209a00e6 | ||
|
|
188113bad6 | ||
|
|
5916837318 | ||
|
|
27aa517c48 | ||
|
|
4622e88a6b | ||
|
|
9bdb6acd63 | ||
|
|
751d213145 | ||
|
|
0c3ae457a6 | ||
|
|
92d4be6923 | ||
|
|
ad8c188f58 | ||
|
|
9716b3924b | ||
|
|
ae9b951b08 | ||
|
|
78a0d8f4f6 | ||
|
|
675d8bc5be | ||
|
|
a90019baa2 | ||
|
|
72997ee71e | ||
|
|
5b631056b1 | ||
|
|
caa334730a | ||
|
|
261f74a1e9 | ||
|
|
f10a7d9bbc | ||
|
|
358d594f34 | ||
|
|
b06b6b5c42 | ||
|
|
ac41a15191 | ||
|
|
ef42c55f56 | ||
|
|
bd67c9c67e | ||
|
|
8f19826fe4 | ||
|
|
afd0e67fb0 | ||
|
|
f1b14d6f65 | ||
|
|
09b5172962 | ||
|
|
39f0ea1706 | ||
|
|
ec4b9113df | ||
|
|
2a51b70a74 | ||
|
|
4ba2c7d65c | ||
|
|
3af28968ed | ||
|
|
2f36ab71c9 | ||
|
|
b40530ad3c | ||
|
|
0131949aff | ||
|
|
b85d52f727 | ||
|
|
53e6cd3126 | ||
|
|
69ae8c491b | ||
|
|
c35ae6571f | ||
|
|
4c621b83e9 | ||
|
|
4abf7cdaba | ||
|
|
9808aa5460 | ||
|
|
5c15d56cf5 | ||
|
|
ef24164464 | ||
|
|
a1abfa30b8 | ||
|
|
b5abcd9bc1 | ||
|
|
8d34af2004 | ||
|
|
db0524d59b | ||
|
|
6193f489a3 | ||
|
|
fb31759c27 | ||
|
|
375e3a03ec | ||
|
|
11bf3a4493 | ||
|
|
7c64fb9cf3 | ||
|
|
d3de9efc33 | ||
|
|
e9dcf2a63a | ||
|
|
8008d21782 | ||
|
|
852ecf048a | ||
|
|
3c91c74232 | ||
|
|
8d20d7abec | ||
|
|
2f1266f7dd | ||
|
|
05be4b0acc | ||
|
|
9d9c3d34f8 | ||
|
|
491531c76b | ||
|
|
bf4ea771be | ||
|
|
dd5882760b | ||
|
|
1707dda62a | ||
|
|
c4d3eded72 | ||
|
|
ca4b0650fa | ||
|
|
33d22b3146 | ||
|
|
e2dd47bf42 | ||
|
|
60b1951f90 | ||
|
|
6ce06b1b60 | ||
|
|
e21d4a5326 | ||
|
|
7fd9e728ae | ||
|
|
b66736b8cc | ||
|
|
ea8f6a3c92 | ||
|
|
adcdbb45f9 | ||
|
|
cff958587d | ||
|
|
9ab2326f15 | ||
|
|
8785acd352 | ||
|
|
57767b7f27 | ||
|
|
febcfc8f2d | ||
|
|
c04c387a89 | ||
|
|
78ac9467f8 | ||
|
|
73b60d4db8 | ||
|
|
7266fd67db | ||
|
|
469f2c94da | ||
|
|
d4e0d051b1 | ||
|
|
df74a5a352 | ||
|
|
21fd32266a | ||
|
|
cd7c7b369b | ||
|
|
65ed7099c0 | ||
|
|
af34572883 | ||
|
|
1f6a67b2a2 | ||
|
|
7701e7a65a | ||
|
|
65a91832a8 | ||
|
|
acb47ffed5 | ||
|
|
13c0304d84 | ||
|
|
febdf3974c | ||
|
|
390acec928 | ||
|
|
ff806f8c3e | ||
|
|
06c4faabc3 | ||
|
|
92aec16fa3 | ||
|
|
f1ffb783d8 | ||
|
|
b708300586 | ||
|
|
f88d757d06 | ||
|
|
4d96f188d1 | ||
|
|
2d943f40bb | ||
|
|
92c058a736 | ||
|
|
ba819bcfca | ||
|
|
e22f1adfa7 | ||
|
|
f2bca03360 | ||
|
|
5f07d701f0 | ||
|
|
496a50bc89 | ||
|
|
6ccb6c4cce | ||
|
|
42c78fd0a9 | ||
|
|
3d0e2923ae | ||
|
|
408b2371f0 | ||
|
|
6ea08b40a8 | ||
|
|
5eb0f7d3ec | ||
|
|
d465d59c3a | ||
|
|
af9a60d175 | ||
|
|
8e0f0c92ce | ||
|
|
5a14fb4b5d | ||
|
|
22a1aacb21 | ||
|
|
e5b6c11399 | ||
|
|
85ebd67a28 | ||
|
|
f33a779668 | ||
|
|
1b5916b655 | ||
|
|
d8720cd858 | ||
|
|
44636bddaa | ||
|
|
1741723687 | ||
|
|
ad2c291d4a | ||
|
|
52ffd80041 | ||
|
|
44dde744ea | ||
|
|
c18ddf929e | ||
|
|
5661365248 | ||
|
|
bcb9d258c2 | ||
|
|
53a94c4547 | ||
|
|
928ffe94a7 | ||
|
|
dcb78c7144 | ||
|
|
43dd8fa1cc | ||
|
|
91a5f00d30 | ||
|
|
0eff54b807 | ||
|
|
68eb0ce3f1 | ||
|
|
023318c08d | ||
|
|
c0e3aa2b0a | ||
|
|
a6d1b9188f | ||
|
|
43193c5ac5 | ||
|
|
35c58047ee | ||
|
|
2190f3544f | ||
|
|
fd8ea78e04 | ||
|
|
7fc336a6d9 | ||
|
|
3cc6fae5c0 | ||
|
|
f4f75da016 | ||
|
|
bdeba1aa86 | ||
|
|
8991be4eea | ||
|
|
940b93ceb8 | ||
|
|
00eb3d0d4f | ||
|
|
7f61eb7a0a | ||
|
|
53f506670c | ||
|
|
9fe7ab31ed | ||
|
|
008534b8a2 | ||
|
|
2c5842f908 | ||
|
|
a8ee9d473e | ||
|
|
1e3ab83e6a | ||
|
|
9720da2c68 | ||
|
|
ee74e2d50c | ||
|
|
6171009add | ||
|
|
d299a444fa | ||
|
|
107276e9cc | ||
|
|
fe00cbf106 | ||
|
|
02d0ddbcfe | ||
|
|
c4802f7683 | ||
|
|
b582b2d64e | ||
|
|
9c4edf56ca | ||
|
|
e24fdb3a9d | ||
|
|
05f24516dd | ||
|
|
a3708b2616 | ||
|
|
ee514df722 | ||
|
|
924c416644 | ||
|
|
597cf9a3ae | ||
|
|
316c8376d3 | ||
|
|
672ddddafc | ||
|
|
a8ee48c4b7 | ||
|
|
c300f77151 | ||
|
|
16734fc767 | ||
|
|
f701c1e1b5 | ||
|
|
98cec207ce | ||
|
|
3ecdf71917 | ||
|
|
ebba65e558 | ||
|
|
177d23b3ac | ||
|
|
cfd402f424 | ||
|
|
b7e492d674 | ||
|
|
d7e0202f37 | ||
|
|
4227d04eb7 | ||
|
|
272ed14737 | ||
|
|
223a754200 | ||
|
|
af480a58c4 | ||
|
|
192738ecb9 | ||
|
|
27c268f865 | ||
|
|
32fcc1a81b | ||
|
|
621eca340e | ||
|
|
ee6c8c3e76 | ||
|
|
dcf7621c35 | ||
|
|
6a8193ea80 | ||
|
|
d4efef9a9b | ||
|
|
cdffc9eaa0 | ||
|
|
a162a65dce | ||
|
|
369bab4744 | ||
|
|
c98038b2d7 | ||
|
|
849c4bb8c5 | ||
|
|
ee64322d76 | ||
|
|
79d65a768a | ||
|
|
37610dc8f3 | ||
|
|
2e31dc20e4 | ||
|
|
ae3d98fb47 | ||
|
|
a938a790fa | ||
|
|
71d556dddf | ||
|
|
d61227b2fd | ||
|
|
612a6b00e4 | ||
|
|
b3353b518c | ||
|
|
0b44761d43 | ||
|
|
c7979e4023 | ||
|
|
e00904e100 | ||
|
|
96e2366569 | ||
|
|
e76624862d | ||
|
|
c5c83a61d3 | ||
|
|
ccf0d6205f | ||
|
|
f482c1d702 | ||
|
|
a4c056e822 | ||
|
|
f5071b2aae | ||
|
|
0fa13ee43d | ||
|
|
1d2b8a08fa | ||
|
|
0c06744450 | ||
|
|
4ada29fbc3 | ||
|
|
76182231dd | ||
|
|
8cd6915b9d | ||
|
|
f90de26bc2 | ||
|
|
cc2e661aab | ||
|
|
3a92cdab3a | ||
|
|
b367982c23 | ||
|
|
a4f4dc617e | ||
|
|
2eb0f11d05 | ||
|
|
6694df991c | ||
|
|
8b2bfa7e9d | ||
|
|
7386220325 | ||
|
|
000faeaca7 | ||
|
|
3f9783ac02 | ||
|
|
4e6dc67ae8 | ||
|
|
666140bb73 | ||
|
|
56050de5cd | ||
|
|
772136decd | ||
|
|
f664601c51 | ||
|
|
f06b27b7fb | ||
|
|
ef53ce88d4 | ||
|
|
ef18030e1a | ||
|
|
f07176c0a2 | ||
|
|
15db141c7a | ||
|
|
d4bbb7c591 | ||
|
|
92e620d2ad | ||
|
|
311104da97 | ||
|
|
3127632f96 | ||
|
|
59f611bfde | ||
|
|
2d556bdca2 | ||
|
|
65e7dba8b8 | ||
|
|
6550a63056 | ||
|
|
aa4e2c56b5 | ||
|
|
aa4f1910b8 | ||
|
|
223a4b18a8 | ||
|
|
57ef534acd | ||
|
|
719914fb84 | ||
|
|
080f958da3 | ||
|
|
7547aa3807 | ||
|
|
67a0c6e0e1 | ||
|
|
37cb842993 | ||
|
|
ad27d48fd3 | ||
|
|
3a503976c8 | ||
|
|
ce6fa82026 | ||
|
|
e8349ab5cc | ||
|
|
4773fc4647 | ||
|
|
d47a69a529 | ||
|
|
d3b74e96b4 | ||
|
|
209d3f7550 | ||
|
|
92e65cb3fd | ||
|
|
a4945939de | ||
|
|
086760b307 | ||
|
|
ce1b63ce38 | ||
|
|
b7b8983653 | ||
|
|
d52fb1f4fd | ||
|
|
901c87761b | ||
|
|
081fe6a434 | ||
|
|
b84c6ace8d | ||
|
|
2113e04aba | ||
|
|
84fdf36ed9 | ||
|
|
dc1de994e0 | ||
|
|
7c6479020e | ||
|
|
a5f949d30c | ||
|
|
6c867c692b | ||
|
|
bd735fbab8 | ||
|
|
b4c4ce9e17 | ||
|
|
138cc55d3b | ||
|
|
3d6db313bd | ||
|
|
faa565c3ff | ||
|
|
2a2caad1bf | ||
|
|
804ee87356 | ||
|
|
f5c9d973dc | ||
|
|
778cbe09d1 | ||
|
|
bf6c51bae6 | ||
|
|
cb3933b349 | ||
|
|
4e9d691d9d | ||
|
|
fe5f8547f7 | ||
|
|
3207c31721 | ||
|
|
236d650ed7 | ||
|
|
294b79b321 | ||
|
|
0dc392a760 | ||
|
|
06fa370491 | ||
|
|
6fdd7398b6 | ||
|
|
32a89a9017 | ||
|
|
ddb2d89e6c | ||
|
|
ea608d409e | ||
|
|
494e454f38 | ||
|
|
a9bbb0bf3b | ||
|
|
21c7fa6253 | ||
|
|
df453dab89 | ||
|
|
629f35f5f7 | ||
|
|
6e93fc1b44 | ||
|
|
379b63d11b | ||
|
|
bfd23fcb4f | ||
|
|
9c8aea115c | ||
|
|
ca038169c6 | ||
|
|
0c5a0b3399 | ||
|
|
896246ba7c | ||
|
|
3021f8d0a2 | ||
|
|
9e437ea588 | ||
|
|
556da0ed0b | ||
|
|
ee1921ab3a | ||
|
|
2b8fe35a70 | ||
|
|
624b05f838 | ||
|
|
8a23ed91c0 | ||
|
|
db477e0549 | ||
|
|
ef3c391978 | ||
|
|
4547b69ccc | ||
|
|
326bc4c311 | ||
|
|
44ceee1f90 | ||
|
|
b646700ccc | ||
|
|
6caf88d3aa | ||
|
|
8d6e8b8b3c | ||
|
|
9da29e3ab7 | ||
|
|
e60280e79d | ||
|
|
70db8e241b | ||
|
|
7f43cf2f23 | ||
|
|
d3f4e91be2 | ||
|
|
3602864c43 | ||
|
|
ff3bd4dc4d | ||
|
|
b950598d11 | ||
|
|
0e1e5fb381 | ||
|
|
40d533ada1 | ||
|
|
b6df2b9b35 | ||
|
|
c379b31267 | ||
|
|
b03909c07a | ||
|
|
712c03b17b | ||
|
|
9dae0748d5 | ||
|
|
fa02a7fa3b | ||
|
|
a068d3df79 | ||
|
|
f64c087ffe | ||
|
|
9561137d53 | ||
|
|
d0efda21ad | ||
|
|
a5b047f93d | ||
|
|
06c4ed4e6b | ||
|
|
49c4a5d62c | ||
|
|
c757cc3629 | ||
|
|
dbdf8f672b | ||
|
|
54bd888374 | ||
|
|
b7dea28cc5 | ||
|
|
9aa8e7780b | ||
|
|
271e639abe | ||
|
|
082a79eebc | ||
|
|
7a10c09954 | ||
|
|
3139a37cc7 | ||
|
|
f8e8c27c04 | ||
|
|
c79d695ffb | ||
|
|
ad6f6cf149 | ||
|
|
8506e94f10 | ||
|
|
5db8e0b9ab | ||
|
|
da55574b32 | ||
|
|
736fed00b3 | ||
|
|
9b200fc33a | ||
|
|
fa436f1d12 | ||
|
|
837b2715a0 | ||
|
|
1a04312986 | ||
|
|
a89711b006 | ||
|
|
6d061b5d01 | ||
|
|
53ddb3dd1c | ||
|
|
c7b0a7f441 | ||
|
|
d7b44a2585 | ||
|
|
caf886df6b | ||
|
|
287984dd7f | ||
|
|
3294bb64a5 | ||
|
|
f52476a323 | ||
|
|
c839915a00 | ||
|
|
2deafc6b81 | ||
|
|
fdec0f79a2 | ||
|
|
31d66002a9 | ||
|
|
f2ed382cf1 | ||
|
|
a4746eab0c | ||
|
|
0b011fae7d | ||
|
|
a6c9922d33 | ||
|
|
7fe1ee3fd5 | ||
|
|
075726db8b | ||
|
|
b22ff3bebb | ||
|
|
bc794bbf7b | ||
|
|
9c29c3d192 | ||
|
|
8e5dabfa8e | ||
|
|
e974989a95 | ||
|
|
057ce2ea4b | ||
|
|
2e920f2780 | ||
|
|
b41e6dfd5c | ||
|
|
8413c84284 | ||
|
|
10e63bf2cf | ||
|
|
73ba8fc753 | ||
|
|
f07a44eb15 | ||
|
|
3dc3c78901 | ||
|
|
db099033e8 | ||
|
|
14a460961c | ||
|
|
b467690ac7 | ||
|
|
b02bb0eaf6 | ||
|
|
436dffcbb3 | ||
|
|
95935b38bc | ||
|
|
0fa3de5ac7 | ||
|
|
d8e8aafad7 | ||
|
|
33a4ab3d88 | ||
|
|
bcaf5e9da5 | ||
|
|
98f8c93950 | ||
|
|
d11a6b4467 | ||
|
|
91d4e0e688 | ||
|
|
8af1cb3489 | ||
|
|
456ae4cabe | ||
|
|
f233b20427 | ||
|
|
a1b825b5b4 | ||
|
|
f9beffe3dc | ||
|
|
db96b262a2 | ||
|
|
c0ab447ef7 | ||
|
|
855a8f0b49 | ||
|
|
9c455e856d | ||
|
|
009de921e2 | ||
|
|
7663ca8dfe | ||
|
|
ee51c054b9 | ||
|
|
884b7590cb | ||
|
|
decf316802 | ||
|
|
5516cd92e7 | ||
|
|
77d6092890 | ||
|
|
4ad619439a | ||
|
|
143d59a991 | ||
|
|
2a62518e5c | ||
|
|
edb17200d0 | ||
|
|
2afd0bd7f7 | ||
|
|
bf2459540e | ||
|
|
09bb9828a2 | ||
|
|
8fda29657a | ||
|
|
c53195fa9a | ||
|
|
1e59032396 | ||
|
|
9e79be419d | ||
|
|
a81d3217e7 | ||
|
|
aa67faefab | ||
|
|
e982d8a3c6 | ||
|
|
9267563af2 | ||
|
|
333d01a0a5 | ||
|
|
24a6bd30f9 | ||
|
|
fdc0bc472d | ||
|
|
a791665a2c | ||
|
|
20fcce13a4 | ||
|
|
8233ab1cda | ||
|
|
656a4b6f32 | ||
|
|
60df8ac6b8 | ||
|
|
1fb3d48e90 | ||
|
|
b47110c339 | ||
|
|
10f4632c88 | ||
|
|
6dbd702bb5 |
@@ -65,4 +65,6 @@ SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
PackConstructorInitializers: Never
|
||||
PackConstructorInitializers: Never
|
||||
ConstructorInitializerIndentWidth: 8
|
||||
IndentWrappedFunctionNames: true
|
||||
|
||||
@@ -1,43 +1,53 @@
|
||||
# This action retrieves the latest commit message from a push or pull_request event
|
||||
# and makes it available as an output variable named 'msg'.
|
||||
name: 'Get commit message'
|
||||
outputs:
|
||||
msg:
|
||||
value: ${{ steps.action_output.outputs.msg }}
|
||||
hash:
|
||||
value: ${{ steps.action_output.outputs.hash }}
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Find commit message (on push)
|
||||
if: github.event_name == 'push'
|
||||
shell: bash
|
||||
env:
|
||||
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
||||
run: |
|
||||
AUTHOR_NAME="${{ github.event.head_commit.author.name }}"
|
||||
AUTHOR_EMAIL="${{ github.event.head_commit.author.email }}"
|
||||
TSTAMP="${{ github.event.head_commit.timestamp }}"
|
||||
echo "commit ${{ github.event.head_commit.id }}" >> /tmp/commit_msg.txt
|
||||
HASH="${{ github.event.head_commit.id }}"
|
||||
echo "commit $HASH" >> /tmp/commit_msg.txt
|
||||
echo "Author: ${AUTHOR_NAME}<${AUTHOR_EMAIL}>" >> /tmp/commit_msg.txt
|
||||
echo "Date: ${TSTAMP}" >> /tmp/commit_msg.txt
|
||||
echo "" >> /tmp/commit_msg.txt
|
||||
echo "${{ github.event.head_commit.message }}" >> /tmp/commit_msg.txt
|
||||
echo "$COMMIT_MESSAGE" >> /tmp/commit_msg.txt
|
||||
echo "$HASH" > /tmp/commit_hash.txt
|
||||
- name: Find commit message (PR)
|
||||
shell: bash
|
||||
id: checkout_code
|
||||
if: github.event_name == 'pull_request'
|
||||
run: |
|
||||
echo "+++++ head commit message +++++"
|
||||
echo "$(git log -1 --no-merges)"
|
||||
echo "+++++++++++++++++++++++++++++++"
|
||||
echo "hash=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
||||
git checkout ${{ github.event.pull_request.head.sha }}
|
||||
echo "$(git log -1 --no-merges)" >> /tmp/commit_msg.txt
|
||||
BEFORE_HASH=$(git rev-parse HEAD)
|
||||
echo "hash=$BEFORE_HASH" >> "$GITHUB_OUTPUT"
|
||||
# Next we will checkout the actual head (not the merge commits) of the PR
|
||||
AFTER_HASH="${{ github.event.pull_request.head.sha }}"
|
||||
git checkout $AFTER_HASH
|
||||
COMMIT_MESSAGE=$(git log -1 --no-merges)
|
||||
echo "$COMMIT_MESSAGE" > /tmp/commit_msg.txt
|
||||
echo "$AFTER_HASH" > /tmp/commit_hash.txt
|
||||
- shell: bash
|
||||
id: action_output
|
||||
run: |
|
||||
# Get the commit message
|
||||
DELIMITER="EOF_FILE_CONTENT_$(date +%s)" # Using timestamp to make it more unique
|
||||
echo "msg<<$DELIMITER" >> "$GITHUB_OUTPUT"
|
||||
cat /tmp/commit_msg.txt >> "$GITHUB_OUTPUT"
|
||||
echo "$DELIMITER" >> "$GITHUB_OUTPUT"
|
||||
echo "----- got commit message ---"
|
||||
cat /tmp/commit_msg.txt
|
||||
echo "----------------------------"
|
||||
# Get the commit hash
|
||||
echo "hash=$(cat /tmp/commit_hash.txt)" >> "$GITHUB_OUTPUT"
|
||||
- name: Cleanup Find commit message (PR)
|
||||
shell: bash
|
||||
if: github.event_name == 'pull_request'
|
||||
|
||||
@@ -15,7 +15,7 @@ runs:
|
||||
uses: actions/cache@v4 # Use a specific version
|
||||
with:
|
||||
path: ~/Library/Caches/Homebrew
|
||||
key: ${{ runner.os }}-brew-20250424
|
||||
key: ${{ runner.os }}-brew-20251211
|
||||
- name: Install Mac Prerequisites
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
name: Android
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release
|
||||
- rc/**
|
||||
|
||||
jobs:
|
||||
build-android:
|
||||
name: build-android
|
||||
# We intentially use a larger runner here to enable larger disk space
|
||||
# (standard linux runner will fail on disk space and faster build time).
|
||||
runs-on: 'ubuntu-24.04-32core'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- name: Run Android Continuous
|
||||
uses: ./.github/actions/android-continuous
|
||||
with:
|
||||
build-abi: armeabi-v7a,arm64-v8a,x86_64
|
||||
@@ -1,29 +0,0 @@
|
||||
name: iOS
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release
|
||||
- rc/**
|
||||
|
||||
jobs:
|
||||
build-ios:
|
||||
name: build-ios
|
||||
runs-on: macos-14-xlarge
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/mac-prereq
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/ios && printf "y" | ./build.sh continuous
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: filament-ios
|
||||
path: out/filament-release-ios.tgz
|
||||
- name: Build iOS samples
|
||||
run: |
|
||||
cd build/ios && ./build-samples.sh continuous
|
||||
@@ -1,26 +0,0 @@
|
||||
name: Linux
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release
|
||||
- rc/**
|
||||
|
||||
jobs:
|
||||
build-linux:
|
||||
name: build-linux
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/linux && printf "y" | ./build.sh continuous
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: filament-linux
|
||||
path: out/filament-release-linux.tgz
|
||||
@@ -1,29 +0,0 @@
|
||||
name: macOS
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release
|
||||
- rc/**
|
||||
|
||||
jobs:
|
||||
build-mac:
|
||||
name: build-mac
|
||||
runs-on: macos-14-xlarge
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/mac-prereq
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/mac && printf "y" | ./build.sh continuous
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: filament-mac
|
||||
path: out/filament-release-darwin.tgz
|
||||
- name: Check public headers
|
||||
run: |
|
||||
test/check-headers/test.sh out/release/filament/include
|
||||
@@ -0,0 +1,99 @@
|
||||
name: 'Postsubmit Tasks'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
# Update the renderdiff goldens in filament-assets. This will add or merge the new goldens from
|
||||
# a branch on filament-assets.
|
||||
update-renderdiff-goldens:
|
||||
name: update-renderdiff-goldens
|
||||
runs-on: 'ubuntu-24.04-4core'
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- id: get_commit_msg
|
||||
uses: ./.github/actions/get-commit-msg
|
||||
- name: Build diffimg
|
||||
run: ./build.sh release diffimg
|
||||
- name: Run update script
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }}
|
||||
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
|
||||
run: |
|
||||
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 test/renderdiff/src/commit_msg.py)
|
||||
COMMIT_HASH="${{ steps.get_commit_msg.outputs.hash }}"
|
||||
if [[ "${GOLDEN_BRANCH}" != "main" ]]; then
|
||||
git config --global user.email "filament.bot@gmail.com"
|
||||
git config --global user.name "Filament Bot"
|
||||
git config --global credential.helper cache
|
||||
echo "branch==${GOLDEN_BRANCH}"
|
||||
echo "hash==${COMMIT_HASH}"
|
||||
python3 test/renderdiff/src/update_golden.py --branch=${GOLDEN_BRANCH} \
|
||||
--merge-to-main --filament-tag=${COMMIT_HASH} --golden-repo-token=${GH_TOKEN}
|
||||
fi
|
||||
|
||||
# Update the /docs (offiicla github-hosted Filament site) if necessary
|
||||
update-docs:
|
||||
name: update-docs
|
||||
runs-on: 'ubuntu-24.04-4core'
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- id: get_commit_msg
|
||||
uses: ./.github/actions/get-commit-msg
|
||||
- name: Prerequisites
|
||||
run: pip install selenium
|
||||
- name: Run update script
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }}
|
||||
run: |
|
||||
bash docs_src/build/install_mdbook.sh && source ~/.bashrc
|
||||
COMMIT_HASH="${{ steps.get_commit_msg.outputs.hash }}"
|
||||
git config --global user.email "filament.bot@gmail.com"
|
||||
git config --global user.name "Filament Bot"
|
||||
git config --global credential.helper cache
|
||||
bash docs_src/build/postsubmit.sh ${COMMIT_HASH} ${GH_TOKEN}
|
||||
|
||||
# Produce a json that describes the android artifact sizes, and will push that json to a folder in
|
||||
# filament-assets
|
||||
update-sizeguard:
|
||||
name: update-sizeguard
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
- id: get_commit_msg
|
||||
uses: ./.github/actions/get-commit-msg
|
||||
- name: Build and generate size report
|
||||
run: |
|
||||
cd build/android && printf "y" | ./build.sh release all
|
||||
cd ../..
|
||||
COMMIT_HASH="${{ steps.get_commit_msg.outputs.hash }}"
|
||||
python3 test/sizeguard/dump_artifact_size.py out/*.tgz out/*.aar > "${COMMIT_HASH}.json"
|
||||
- name: Push to filament-assets
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }}
|
||||
run: |
|
||||
COMMIT_HASH="${{ steps.get_commit_msg.outputs.hash }}"
|
||||
git config --global user.email "filament.bot@gmail.com"
|
||||
git config --global user.name "Filament Bot"
|
||||
git clone https://x-access-token:${GH_TOKEN}@github.com/google/filament-assets.git filament-assets
|
||||
mkdir -p filament-assets/sizeguard
|
||||
mv "${COMMIT_HASH}.json" filament-assets/sizeguard/
|
||||
cd filament-assets
|
||||
git add sizeguard/"${COMMIT_HASH}.json"
|
||||
git commit -m "Update sizeguard for filament@${COMMIT_HASH}" || echo "No changes to commit"
|
||||
git push https://x-access-token:${GH_TOKEN}@github.com/google/filament-assets.git main
|
||||
@@ -1,58 +1,115 @@
|
||||
name: 'Post-submit tasks'
|
||||
name: 'Postsubmit CI'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release
|
||||
- rc/**
|
||||
|
||||
jobs:
|
||||
update-renderdiff-goldens:
|
||||
name: update-renderdiff-goldens
|
||||
runs-on: 'ubuntu-24.04-4core'
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- id: get_commit_msg
|
||||
uses: ./.github/actions/get-commit-msg
|
||||
- name: Prerequisites
|
||||
run: pip install tifffile numpy
|
||||
- name: Run update script
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }}
|
||||
run: |
|
||||
GOLDEN_BRANCH=$(echo "${{ steps.get_commit_msg.outputs.msg }}" | python3 test/renderdiff/src/commit_msg.py)
|
||||
COMMIT_HASH=$(echo "${{ steps.get_commit_msg.outputs.msg }}" | head -n 1 | sed "s/commit //g")
|
||||
if [[ "${GOLDEN_BRANCH}" != "main" ]]; then
|
||||
git config --global user.email "filament.bot@gmail.com"
|
||||
git config --global user.name "Filament Bot"
|
||||
git config --global credential.helper cache
|
||||
echo "branch==${GOLDEN_BRANCH}"
|
||||
echo "hash==${COMMIT_HASH}"
|
||||
python3 test/renderdiff/src/update_golden.py --branch=${GOLDEN_BRANCH} \
|
||||
--merge-to-main --filament-tag=${COMMIT_HASH} --golden-repo-token=${GH_TOKEN}
|
||||
fi
|
||||
build-android:
|
||||
name: build-android
|
||||
# We intentially use a larger runner here to enable larger disk space
|
||||
# (standard linux runner will fail on disk space and faster build time).
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
|
||||
update-docs:
|
||||
name: update-docs
|
||||
runs-on: 'ubuntu-24.04-4core'
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- id: get_commit_msg
|
||||
uses: ./.github/actions/get-commit-msg
|
||||
- name: Prerequisites
|
||||
run: pip install selenium
|
||||
- name: Run update script
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }}
|
||||
- name: Run Android Continuous
|
||||
uses: ./.github/actions/android-continuous
|
||||
with:
|
||||
build-abi: armeabi-v7a,arm64-v8a,x86_64
|
||||
|
||||
build-ios:
|
||||
name: build-ios
|
||||
runs-on: macos-14-xlarge
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/mac-prereq
|
||||
- name: Run build script
|
||||
run: |
|
||||
bash docs_src/build/install_mdbook.sh && source ~/.bashrc
|
||||
COMMIT_HASH=$(echo "${{ steps.get_commit_msg.outputs.msg }}" | head -n 1 | sed "s/commit //g")
|
||||
git config --global user.email "filament.bot@gmail.com"
|
||||
git config --global user.name "Filament Bot"
|
||||
git config --global credential.helper cache
|
||||
bash docs_src/build/postsubmit.sh ${COMMIT_HASH} ${GH_TOKEN}
|
||||
cd build/ios && printf "y" | ./build.sh continuous
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: filament-ios
|
||||
path: out/filament-release-ios.tgz
|
||||
- name: Build iOS samples
|
||||
run: |
|
||||
cd build/ios && ./build-samples.sh continuous
|
||||
|
||||
build-linux:
|
||||
name: build-linux
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/linux && printf "y" | ./build.sh continuous
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: filament-linux
|
||||
path: out/filament-release-linux.tgz
|
||||
|
||||
build-mac:
|
||||
name: build-mac
|
||||
runs-on: macos-14-xlarge
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/mac-prereq
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/mac && printf "y" | ./build.sh continuous
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: filament-mac
|
||||
path: out/filament-release-darwin.tgz
|
||||
- name: Check public headers
|
||||
run: |
|
||||
test/check-headers/test.sh out/release/filament/include
|
||||
|
||||
build-web:
|
||||
name: build-web
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- uses: ./.github/actions/web-prereq
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/web && printf "y" | ./build.sh continuous
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: filament-web
|
||||
path: out/filament-release-web.tgz
|
||||
|
||||
build-windows:
|
||||
name: build-windows
|
||||
runs-on: windows-2022-32core
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- name: Run build script
|
||||
run: |
|
||||
build\windows\build-github.bat continuous
|
||||
shell: cmd
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: filament-windows
|
||||
path: out/filament-windows.tgz
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Presubmit
|
||||
name: 'Presubmit CI'
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -19,14 +19,14 @@ jobs:
|
||||
- uses: ./.github/actions/mac-prereq
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/mac && printf "y" | ./build.sh presubmit
|
||||
cd build/mac && printf "y" | ./build.sh presubmit-with-test
|
||||
- name: Test material parser
|
||||
run: |
|
||||
out/cmake-release/filament/test/test_material_parser
|
||||
|
||||
build-desktop-linux:
|
||||
name: build-linux
|
||||
runs-on: ubuntu-22.04-16core
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
@@ -67,7 +67,16 @@ jobs:
|
||||
# Only build 1 64 bit target during presubmit to cut down build times during presubmit
|
||||
# Continuous builds will build everything
|
||||
run: |
|
||||
cd build/android && printf "y" | ./build.sh presubmit arm64-v8a
|
||||
pushd .
|
||||
cd build/android && printf "y" | ./build.sh presubmit-with-archive arm64-v8a
|
||||
popd
|
||||
- name: Check artifact sizes
|
||||
run: |
|
||||
python3 test/sizeguard/dump_artifact_size.py out/*.aar > current_size.json
|
||||
python3 test/sizeguard/check_size.py current_size.json \
|
||||
--target-branch origin/main \
|
||||
--threshold 20480 \
|
||||
--artifacts filament-android-release.aar/jni/arm64-v8a/libfilament-jni.so
|
||||
|
||||
build-ios:
|
||||
name: build-iOS
|
||||
@@ -108,8 +117,7 @@ jobs:
|
||||
uses: ./.github/actions/get-commit-msg
|
||||
- name: Check for manual edits to /docs
|
||||
run: |
|
||||
COMMIT_ID=$(echo "${{ steps.get_commit_msg.outputs.msg }}" | head -n 1 | sed "s/commit //g")
|
||||
bash docs_src/build/presubmit_check.sh ${COMMIT_ID}
|
||||
bash docs_src/build/presubmit_check.sh ${{ steps.get_commit_msg.outputs.hash }}
|
||||
|
||||
test-renderdiff:
|
||||
name: test-renderdiff
|
||||
@@ -121,35 +129,40 @@ jobs:
|
||||
- uses: ./.github/actions/mac-prereq
|
||||
- uses: ./.github/actions/get-gltf-assets
|
||||
- uses: ./.github/actions/get-mesa
|
||||
- uses: ./.github/actions/get-vulkan-sdk
|
||||
- uses: ./.github/actions/get-vulkan-sdk
|
||||
- id: get_commit_msg
|
||||
uses: ./.github/actions/get-commit-msg
|
||||
- name: Prerequisites
|
||||
run: |
|
||||
pip install tifffile numpy
|
||||
# Must have at least clang-16 for a webgpu/dawn build.
|
||||
sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
|
||||
shell: bash
|
||||
- name: Render and compare
|
||||
id: render_compare
|
||||
env:
|
||||
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
|
||||
run: |
|
||||
ls ./gltf/Models
|
||||
TEST_DIR=test/renderdiff
|
||||
source ${TEST_DIR}/src/preamble.sh
|
||||
start_
|
||||
GOLDEN_BRANCH=$(echo "${{ steps.get_commit_msg.outputs.msg }}" | python3 ${TEST_DIR}/src/commit_msg.py)
|
||||
bash ${TEST_DIR}/generate.sh && \
|
||||
python3 ${TEST_DIR}/src/golden_manager.py \
|
||||
set -eux
|
||||
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 ${TEST_DIR}/src/commit_msg.py)
|
||||
bash ${TEST_DIR}/generate.sh
|
||||
# Build diffimg tool
|
||||
./build.sh release diffimg
|
||||
|
||||
python3 ${TEST_DIR}/src/golden_manager.py \
|
||||
--branch=${GOLDEN_BRANCH} \
|
||||
--output=${GOLDEN_OUTPUT_DIR}
|
||||
|
||||
# Note that we need to upload the output even if comparison fails, so we undo `set -ex`
|
||||
end_
|
||||
# Note that we need to upload the output even if comparison fails, so we undo `set -eux`
|
||||
set +eux
|
||||
|
||||
python3 ${TEST_DIR}/src/compare.py \
|
||||
--src=${GOLDEN_OUTPUT_DIR} \
|
||||
--dest=${RENDER_OUTPUT_DIR} \
|
||||
--out=${DIFF_OUTPUT_DIR} 2>&1 | tee compare_output.txt
|
||||
--out=${DIFF_OUTPUT_DIR} \
|
||||
--diffimg="$(pwd)/out/cmake-release/tools/diffimg/diffimg" \
|
||||
--test="${TEST_DIR}/tests/presubmit.json" 2>&1 | tee compare_output.txt
|
||||
|
||||
if grep "Failed" compare_output.txt > /dev/null; then
|
||||
DELIMITER="EOF_FILE_CONTENT_$(date +%s)" # Using timestamp to make it more unique
|
||||
@@ -157,6 +170,7 @@ jobs:
|
||||
cat compare_output.txt >> "$GITHUB_OUTPUT"
|
||||
echo "$DELIMITER" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
shell: bash
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: presubmit-renderdiff-result
|
||||
@@ -171,14 +185,14 @@ jobs:
|
||||
|
||||
validate-wgsl-webgpu:
|
||||
name: validate-wgsl-webgpu
|
||||
runs-on: 'ubuntu-24.04-8core'
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- name: Run build script
|
||||
run: ./build.sh -W debug test_filamat filament
|
||||
run: ./build.sh -W debug test_filamat filament gltf_viewer
|
||||
- name: Run test
|
||||
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
|
||||
|
||||
@@ -202,3 +216,23 @@ jobs:
|
||||
./build.sh -p desktop debug gltf_viewer
|
||||
- name: Run test
|
||||
run: bash test/code-correctness/test.sh
|
||||
|
||||
test-backend:
|
||||
name: test-backend
|
||||
runs-on: macos-14-xlarge
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/mac-prereq
|
||||
- uses: ./.github/actions/get-mesa
|
||||
- uses: ./.github/actions/get-vulkan-sdk
|
||||
- name: Prerequisites
|
||||
run: |
|
||||
# Must have at least clang-16 for a webgpu/dawn build.
|
||||
sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
|
||||
shell: bash
|
||||
- name: Run backend tests
|
||||
shell: bash
|
||||
run: |
|
||||
bash test/backend/test.sh
|
||||
|
||||
@@ -31,7 +31,7 @@ on:
|
||||
jobs:
|
||||
build-linux:
|
||||
name: build-linux
|
||||
runs-on: ubuntu-22.04-32core
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
if: github.event_name == 'release' || github.event.inputs.platform == 'desktop'
|
||||
|
||||
steps:
|
||||
@@ -163,9 +163,7 @@ jobs:
|
||||
mv out/filamat-android-release.aar out/filamat-${TAG}-android.aar
|
||||
mv out/gltfio-android-release.aar out/gltfio-${TAG}-android.aar
|
||||
mv out/filament-utils-android-release.aar out/filament-utils-${TAG}-android.aar
|
||||
cd out/android-release/filament
|
||||
tar -czf ../../filament-${TAG}-android-native.tgz .
|
||||
cd ../../..
|
||||
mv out/filament-android-release-linux.tgz out/filament-${TAG}-android-native.tgz
|
||||
- name: Sign sample-gltf-viewer
|
||||
run: |
|
||||
echo "${APK_KEYSTORE_BASE64}" > filament.jks.base64
|
||||
@@ -245,7 +243,8 @@ jobs:
|
||||
env:
|
||||
TAG: ${{ steps.git_ref.outputs.tag }}
|
||||
run: |
|
||||
build\windows\build-github.bat release
|
||||
@REMARK 'call' is required to ensure control returns to this script after the batch file finishes.
|
||||
call build\windows\build-github.bat release
|
||||
echo on
|
||||
move out\filament-windows.tgz out\filament-%TAG%-windows.tgz
|
||||
shell: cmd
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
name: Web
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release
|
||||
- rc/**
|
||||
|
||||
jobs:
|
||||
build-web:
|
||||
name: build-web
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/linux-prereq
|
||||
- uses: ./.github/actions/web-prereq
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/web && printf "y" | ./build.sh continuous
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: filament-web
|
||||
path: out/filament-release-web.tgz
|
||||
@@ -1,24 +0,0 @@
|
||||
name: Windows
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release
|
||||
- rc/**
|
||||
|
||||
jobs:
|
||||
build-windows:
|
||||
name: build-windows
|
||||
runs-on: windows-2022-32core
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- name: Run build script
|
||||
run: |
|
||||
build\windows\build-github.bat continuous
|
||||
shell: cmd
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: filament-windows
|
||||
path: out/filament-windows.tgz
|
||||
@@ -18,3 +18,9 @@ test*.json
|
||||
results
|
||||
/compile_commands.json
|
||||
/.cache
|
||||
|
||||
# For /test/renderdiff dependencies
|
||||
/mesa
|
||||
/gltf
|
||||
/filament-assets
|
||||
/venv
|
||||
@@ -1,6 +1,6 @@
|
||||
## Building Filament
|
||||
# Building Filament
|
||||
|
||||
### Prerequisites
|
||||
## Prerequisites
|
||||
|
||||
To build Filament, you must first install the following tools:
|
||||
|
||||
@@ -18,7 +18,7 @@ To build Filament for Android you must also install the following:
|
||||
- Android NDK 25.1 or higher
|
||||
- Java 17
|
||||
|
||||
### Environment variables
|
||||
## Environment variables
|
||||
|
||||
To build Filament for Android, make sure the environment variable `ANDROID_HOME` points to the
|
||||
location of your Android SDK.
|
||||
@@ -30,7 +30,7 @@ When building for WebGL, you'll also need to set `EMSDK`. See [WebAssembly](#web
|
||||
We recommend using CLion to develop for Filament. Simply open the root directory's CMakeLists.txt
|
||||
in CLion to obtain a usable project.
|
||||
|
||||
### Easy build
|
||||
## Easy build
|
||||
|
||||
Once the required OS specific dependencies listed below are installed, you can use the script
|
||||
located in `build.sh` to build Filament easily on macOS and Linux.
|
||||
@@ -62,7 +62,12 @@ force a clean build by adding the `-c` flag in that case.
|
||||
To install the libraries and executables in `out/debug/` and `out/release/`, add the `-i` flag.
|
||||
The script offers more features described by executing `build.sh -h`.
|
||||
|
||||
### Filament-specific CMake Options
|
||||
For more specialized options, please also consider the following pages:
|
||||
- `-d`: [`matdbg`](https://google.github.io/filament/dup/matdbg.html)
|
||||
- `-t`: [`fgviewer`](https://google.github.io/filament/dup/fgviewer.html)
|
||||
- `-b` and `-y`: [ASAN/UBSAN builds](https://google.github.io/filament/notes/asan_ubsan.html)
|
||||
|
||||
## Filament-specific CMake Options
|
||||
|
||||
The following CMake options are boolean options specific to Filament:
|
||||
|
||||
@@ -84,7 +89,7 @@ cmake . -DOPTION=ON # Replace OPTION with the option name, set to ON / OFF
|
||||
|
||||
Options can also be set with the CMake GUI.
|
||||
|
||||
### Linux
|
||||
## Linux
|
||||
|
||||
Make sure you've installed the following dependencies:
|
||||
|
||||
@@ -143,7 +148,7 @@ ninja
|
||||
|
||||
This will build Filament, its tests and samples, and various host tools.
|
||||
|
||||
### macOS
|
||||
## macOS
|
||||
|
||||
To compile Filament you must have the most recent version of Xcode installed and you need to
|
||||
make sure the command line tools are setup by running:
|
||||
@@ -164,7 +169,7 @@ cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release/fila
|
||||
ninja
|
||||
```
|
||||
|
||||
### iOS
|
||||
## iOS
|
||||
|
||||
The easiest way to build Filament for iOS is to use `build.sh` and the
|
||||
`-p ios` flag. For instance to build the debug target:
|
||||
@@ -175,9 +180,9 @@ The easiest way to build Filament for iOS is to use `build.sh` and the
|
||||
|
||||
See [ios/samples/README.md](./ios/samples/README.md) for more information.
|
||||
|
||||
### Windows
|
||||
## Windows
|
||||
|
||||
#### Building on Windows with Visual Studio 2019 or later
|
||||
### Building on Windows with Visual Studio 2019 or later
|
||||
|
||||
Install the following components:
|
||||
|
||||
@@ -220,7 +225,7 @@ You can also use CMake to invoke the build without opening Visual Studio. For ex
|
||||
cmake --build . --target gltf_viewer --config Release
|
||||
```
|
||||
|
||||
### Android
|
||||
## Android
|
||||
|
||||
Before building Filament for Android, make sure to build Filament for your host. Some of the
|
||||
host tools are required to successfully build for Android.
|
||||
@@ -237,7 +242,13 @@ foremost for `arm64-v8a`.
|
||||
|
||||
To build Android on Windows machines, see [android/Windows.md](android/Windows.md).
|
||||
|
||||
#### Easy Android build
|
||||
### Important: SDK location
|
||||
|
||||
Either ensure your `ANDROID_HOME` environment variable is set or make sure the root project
|
||||
contains a `local.properties` file with the `sdk.dir` property pointing to your installation of
|
||||
the Android SDK.
|
||||
|
||||
### Easy Android build
|
||||
|
||||
The easiest way to build Filament for Android is to use `build.sh` and the
|
||||
`-p android` flag. For instance to build the release target:
|
||||
@@ -246,9 +257,46 @@ The easiest way to build Filament for Android is to use `build.sh` and the
|
||||
./build.sh -p android release
|
||||
```
|
||||
|
||||
To build a sample (such as `android/samples/sample-hello-triangle`) for an ARM 64-bit phone, you would run
|
||||
```shell
|
||||
./build.sh -p android -q arm64-v8a -k sample-hello-triangle release
|
||||
```
|
||||
|
||||
The output APK can be found in `android/samples/sample-hello-triangle/build/outputs/apk/release/sample-hello-triangle-release-unsigned.apk`
|
||||
|
||||
Run `build.sh -h` for more information.
|
||||
|
||||
#### Manual builds
|
||||
### Android Studio
|
||||
|
||||
You must use the latest stable release of Android Studio.
|
||||
|
||||
The Android build of filament is separated into java/kotlin client APIs, a layer of jni bindings
|
||||
that bridges java/kotlin with native code, and Filament and other component code that have been compiled
|
||||
into architecture-specific libraries. Our default Android Studio gradle setup can compile java/kotlin and
|
||||
the jni bindings for you, but it will treat the filament libraries as already compiled and present on
|
||||
the system.
|
||||
|
||||
Therefore, before compiling the sample app or any other targets, you must
|
||||
make sure that the native filament libraries have been compiled and are located at a prescribed location
|
||||
so that the jni bindings can link against them. You can do so by using the easy build script
|
||||
|
||||
```shell
|
||||
./build.sh -p android release -q arm64-v8a
|
||||
```
|
||||
|
||||
Note that the above step will also install host machine tools into prescribed locations. These tools are
|
||||
required for compiling Filament assets such as materials and environment maps.
|
||||
|
||||
Now we are ready to compile the apps. To open the project, point Studio to the `android` folder.
|
||||
After opening the project and syncing with Gradle, select the sample of your choice
|
||||
using the drop-down widget in the toolbar. Additionally, you will need to select a deployment target.
|
||||
By doing so, Android Studio will automatically try to compile the app only for that specific
|
||||
device's architecture. So if you are targeting a new Pixel phone, make sure that the step above
|
||||
(compiling the library) is targeting ARM 64-bit (`-q arm64-v8a` ), and if you are running the app on
|
||||
an emulator on a Linux machine with an x86 64-bit chipset, you would indicate (`-q x86_64`) in the above step.
|
||||
|
||||
|
||||
### Manual builds
|
||||
|
||||
Invoke CMake in a build directory of your choice, inside of filament's directory. The commands
|
||||
below show how to build Filament for ARM 64-bit (`aarch64`).
|
||||
@@ -276,7 +324,7 @@ This will generate Filament's Android binaries in `out/android-release`. This lo
|
||||
to build the Android Studio projects located in `filament/android`. After install, the library
|
||||
binaries should be found in `out/android-release/filament/lib/arm64-v8a`.
|
||||
|
||||
#### AAR
|
||||
### AAR
|
||||
|
||||
Before you attempt to build the AAR, make sure you've compiled and installed the native libraries
|
||||
as explained in the sections above. You must have the following ABIs built in
|
||||
@@ -308,7 +356,7 @@ Alternatively you can build the AAR from the command line by executing the follo
|
||||
The `-Pcom.google.android.filament.dist-dir` can be used to specify a different installation
|
||||
directory (it must match the CMake install prefix used in the previous steps).
|
||||
|
||||
#### Using Filament's AAR
|
||||
### Using Filament's AAR
|
||||
|
||||
Create a new module in your project and select _Import .JAR or .AAR Package_ when prompted. Make
|
||||
sure to add the newly created module as a dependency to your application.
|
||||
@@ -349,7 +397,7 @@ productFlavors {
|
||||
}
|
||||
```
|
||||
|
||||
### WebAssembly
|
||||
## WebAssembly
|
||||
|
||||
The core Filament library can be cross-compiled to WebAssembly from either macOS or Linux. To get
|
||||
started, follow the instructions for building Filament on your platform ([macOS](#macos) or
|
||||
@@ -391,7 +439,7 @@ Alternatively, if you have node installed you can use the
|
||||
[live-server](https://www.npmjs.com/package/live-server) package, which automatically refreshes the
|
||||
web page when it detects a change.
|
||||
|
||||
Each sample app has its own handwritten html file. Additionally the server folder contains assets
|
||||
Each sample app has its own handwritten html file. Additionally, the server folder contains assets
|
||||
such as meshes, textures, and materials.
|
||||
|
||||
## Running the native samples
|
||||
|
||||
@@ -10,6 +10,7 @@ cmake_minimum_required(VERSION 3.22.1)
|
||||
# ==================================================================================================
|
||||
# Toolchain configuration
|
||||
# ==================================================================================================
|
||||
# On iOS, the deployment target is set inside third_party/clang/ios.cmake
|
||||
if (APPLE AND NOT IOS)
|
||||
# This must be set before project() is called
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15 CACHE STRING "")
|
||||
@@ -56,10 +57,16 @@ option(FILAMENT_ENABLE_FGVIEWER "Enable the frame graph viewer" OFF)
|
||||
|
||||
option(FILAMENT_USE_ABSEIL_LOGGING "Use Abseil to log, may increase binary size" OFF)
|
||||
|
||||
option(FILAMENT_ENABLE_EXPERIMENTAL_GCC_SUPPORT "Enable GCC support (unsupported)" OFF)
|
||||
|
||||
option(FILAMENT_SUPPORTS_WEBP_TEXTURES "Enable webp texture support for Filament (builds libwebp when ON)" OFF)
|
||||
|
||||
# This is to disable GTAO for the short-term while we investigate a way to better manage size increases.
|
||||
# On the regular filament build (where size is of less concern), we enable GTAO by default.
|
||||
option(FILAMENT_DISABLE_GTAO "Disable GTAO" OFF)
|
||||
|
||||
option(FILAMENT_BUILD_TESTING "Build tests" ON)
|
||||
|
||||
set(FILAMENT_NDK_VERSION "" CACHE STRING
|
||||
"Android NDK version or version prefix to be used when building for Android."
|
||||
)
|
||||
@@ -295,15 +302,10 @@ set(TOOLS ${CMAKE_CURRENT_SOURCE_DIR}/tools)
|
||||
# ==================================================================================================
|
||||
set(MIN_CLANG_VERSION "6.0")
|
||||
|
||||
if (CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
if (CMAKE_C_COMPILER_VERSION VERSION_LESS MIN_CLANG_VERSION)
|
||||
message(FATAL_ERROR "Detected C compiler Clang ${CMAKE_C_COMPILER_VERSION} < ${MIN_CLANG_VERSION}")
|
||||
endif()
|
||||
elseif (NOT MSVC)
|
||||
message(FATAL_ERROR "Detected C compiler ${CMAKE_C_COMPILER_ID} is unsupported")
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
set(FILAMENT_USING_GCC OFF)
|
||||
if (CMAKE_C_COMPILER_ID MATCHES "GNU" AND FILAMENT_ENABLE_EXPERIMENTAL_GCC_SUPPORT)
|
||||
set(FILAMENT_USING_GCC ON)
|
||||
elseif (CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS MIN_CLANG_VERSION)
|
||||
message(FATAL_ERROR "Detected CXX compiler Clang ${CMAKE_CXX_COMPILER_VERSION} < ${MIN_CLANG_VERSION}")
|
||||
endif()
|
||||
@@ -344,14 +346,18 @@ if (MSVC)
|
||||
set(CXX_STANDARD "/std:c++latest")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} /W0 /Zc:__cplusplus")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} -fstrict-aliasing -Wno-unknown-pragmas -Wno-unused-function -Wno-deprecated-declarations")
|
||||
if(FILAMENT_USING_GCC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} -Wno-changes-meaning -Wno-return-type -Wno-attributes -Wno-unknown-pragmas -Wno-class-memaccess -Wno-multichar -Wno-deprecated-declarations -Wno-subobject-linkage -Wno-invalid-constexpr")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} -fstrict-aliasing -Wno-unknown-pragmas -Wno-unused-function -Wno-deprecated-declarations")
|
||||
endif()
|
||||
if (APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-nullability-extension")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (FILAMENT_USE_EXTERNAL_GLES3)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_EXTERNAL_GLES3")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DFILAMENT_USE_EXTERNAL_GLES3")
|
||||
endif()
|
||||
|
||||
if (FILAMENT_SUPPORTS_EGL_ON_LINUX)
|
||||
@@ -364,11 +370,23 @@ endif()
|
||||
|
||||
if (LINUX)
|
||||
option(USE_STATIC_LIBCXX "Link against the static runtime libraries." ON)
|
||||
|
||||
# Add this step to support both glibc-based Linux distributions (e.g., Ubuntu, Debian)
|
||||
# and musl-based distributions (e.g., Alpine).
|
||||
include(CheckSymbolExists)
|
||||
check_symbol_exists(__GLIBC__ "features.h" CLANG_WITH_GLIBC)
|
||||
|
||||
if (${USE_STATIC_LIBCXX})
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
link_libraries("-static-libgcc -static-libstdc++")
|
||||
link_libraries(libc++.a)
|
||||
link_libraries(libc++abi.a)
|
||||
if (FILAMENT_USING_GCC)
|
||||
link_libraries("-static-libgcc -static-libstdc++")
|
||||
elseif (CLANG_WITH_GLIBC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
link_libraries("-static-libgcc -static-libstdc++")
|
||||
link_libraries(libc++.a)
|
||||
link_libraries(libc++abi.a)
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Only linux, clang doesn't want to use a shared library that is not PIC.
|
||||
@@ -393,8 +411,8 @@ if (MSVC)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE")
|
||||
endif()
|
||||
|
||||
# Add colors to ninja builds
|
||||
if (UNIX AND CMAKE_GENERATOR STREQUAL "Ninja")
|
||||
# Add colors to ninja builds (when not using gcc)
|
||||
if (UNIX AND CMAKE_GENERATOR STREQUAL "Ninja" AND NOT FILAMENT_USING_GCC)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fcolor-diagnostics")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics")
|
||||
endif()
|
||||
@@ -466,7 +484,7 @@ endif()
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${EXTRA_SANITIZE_OPTIONS}")
|
||||
|
||||
if (FILAMENT_ENABLE_COVERAGE)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fprofile-instr-generate -fcoverage-mapping")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-instr-generate -fcoverage-mapping -Wno-pass-failed")
|
||||
endif()
|
||||
|
||||
# Disable the stack check for macOS to workaround a known issue in clang 11.0.0.
|
||||
@@ -556,6 +574,10 @@ if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
add_definitions(-DFILAMENT_SUPPORTS_WEBGPU)
|
||||
endif()
|
||||
|
||||
if (FILAMENT_SUPPORTS_WEBP_TEXTURES)
|
||||
add_definitions(-DFILAMENT_SUPPORTS_WEBP_TEXTURES)
|
||||
endif()
|
||||
|
||||
# Build with Metal support on non-WebGL Apple platforms.
|
||||
if (APPLE AND NOT WEBGL)
|
||||
option(FILAMENT_SUPPORTS_METAL "Include the Metal backend" ON)
|
||||
@@ -567,7 +589,7 @@ if (FILAMENT_SUPPORTS_METAL)
|
||||
endif()
|
||||
|
||||
# Building filamat increases build times and isn't required for web, so turn it off by default.
|
||||
if (NOT WEBGL AND NOT IOS)
|
||||
if (NOT WEBGL)
|
||||
option(FILAMENT_BUILD_FILAMAT "Build filamat and JNI buildings" ON)
|
||||
else()
|
||||
option(FILAMENT_BUILD_FILAMAT "Build filamat and JNI buildings" OFF)
|
||||
@@ -748,12 +770,61 @@ function(combine_static_libs TARGET OUTPUT DEPS)
|
||||
endfunction()
|
||||
|
||||
# ==================================================================================================
|
||||
# Configuration for CMAKE_CROSSCOMPILING.
|
||||
# Configuration importing/exporting prebuilt "/tools" executables
|
||||
# ==================================================================================================
|
||||
|
||||
# In certain cases, we want the ability to separate the build type/flags (release, debug) of tools
|
||||
# (matc, resgen, etc...) from filament. Cross compilation is one such case (e.g. building material
|
||||
# with matc running on host while building filament for Android) [1]. Another example is when client
|
||||
# wants to enable ASAN for filament but not the tool [2].
|
||||
#
|
||||
# Here are the varibles that are used to determine behavior is such flows:
|
||||
# - CMAKE_CROSSCOMPILING : Set by cmake to indicate the build's target platform is different
|
||||
# from the host platform.
|
||||
# - IMPORT_EXECUTABLES_DIR : This is the directory containing the /ImportExecutables-type.cmake
|
||||
# which are then included in other CMakeLists.txt to enable finding targets that have been
|
||||
# prebuilt. This is set by the client in their `cmake` invocation.
|
||||
# - IMPORT_EXECUTABLES : Path to a file to for 1) exporting the prebuilt targets or 2)
|
||||
# importing a previously exported targets. (Used internally and should not be set by client).
|
||||
# - FILAMENT_EXPORT_PREBUILT_EXECUTABLES_DIR : A path set by the client to indicate that
|
||||
# they wish to export the tools as prebuilts, and the corresponding targets will be recorded
|
||||
# in a cmake file (i.e IMPORT_EXECUTABLES). IMPORT_EXECUTABLES will be set relative to the the
|
||||
# given path. This will set FILAMENT_EXPORT_PREBUILT_EXECUTABLES=ON.
|
||||
# - FILAMENT_IMPORT_PREBUILT_EXECUTABLES_DIR : A path set by the client to indicate that they wish
|
||||
# to import the tools as prebuilts from a .cmake file. The location (IMPORT_EXECUTABLES) of the
|
||||
# file is relative to the path given. This will set FILAMENT_IMPORT_PREBUILT_EXECUTABLES=ON.
|
||||
# - IMPORT_PREBUILT_EXECUTABLES and EXPORT_PREBUILT_EXECUTABLES are internal booleans set to
|
||||
# ON based on the prescence of FILAMENT_IMPORT_PREBUILT_EXECUTABLES_DIR and
|
||||
# FILAMENT_EXPORT_PREBUILT_EXECUTABLES_DIR.
|
||||
#
|
||||
# In conclusion, for cases
|
||||
# - [1] (crosscompiling), the client must set IMPORT_EXECUTABLES_DIR for both when they are building
|
||||
# the tools and when they are building the target-platform filament.
|
||||
# - [2] (all other instances), the client must set FILAMENT_EXPORT_PREBUILT_EXECUTABLES_DIR in
|
||||
# the prebuilt exporting step, and set FILAMENT_IMPORT_PREBUILT_EXECUTABLES_DIR in the importing
|
||||
# step.
|
||||
|
||||
# TODO: Fold the cross compilation case into the more general FILAMENT_IMPORT/EXPORT variables.
|
||||
|
||||
if (FILAMENT_EXPORT_PREBUILT_EXECUTABLES_DIR)
|
||||
set(FILAMENT_EXPORT_PREBUILT_EXECUTABLES ON)
|
||||
set(IMPORT_EXECUTABLES_DIR ${FILAMENT_EXPORT_PREBUILT_EXECUTABLES_DIR})
|
||||
endif()
|
||||
|
||||
if (FILAMENT_IMPORT_PREBUILT_EXECUTABLES_DIR)
|
||||
set(FILAMENT_IMPORT_PREBUILT_EXECUTABLES ON)
|
||||
set(IMPORT_EXECUTABLES_DIR ${FILAMENT_IMPORT_PREBUILT_EXECUTABLES_DIR})
|
||||
endif()
|
||||
|
||||
if (WEBGL)
|
||||
set(IMPORT_EXECUTABLES ${FILAMENT}/${IMPORT_EXECUTABLES_DIR}/ImportExecutables-Release.cmake)
|
||||
else()
|
||||
set(IMPORT_EXECUTABLES ${FILAMENT}/${IMPORT_EXECUTABLES_DIR}/ImportExecutables-${CMAKE_BUILD_TYPE}.cmake)
|
||||
if (FILAMENT_EXPORT_PREBUILT_EXECUTABLES OR FILAMENT_IMPORT_PREBUILT_EXECUTABLES)
|
||||
set(IMPORT_EXECUTABLES_BUILD_TYPE Prebuilt)
|
||||
else()
|
||||
set(IMPORT_EXECUTABLES_BUILD_TYPE ${CMAKE_BUILD_TYPE})
|
||||
endif()
|
||||
set(IMPORT_EXECUTABLES ${FILAMENT}/${IMPORT_EXECUTABLES_DIR}/ImportExecutables-${IMPORT_EXECUTABLES_BUILD_TYPE}.cmake)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
@@ -808,6 +879,7 @@ add_subdirectory(${LIBRARIES}/gltfio)
|
||||
add_subdirectory(${LIBRARIES}/ibl)
|
||||
add_subdirectory(${LIBRARIES}/iblprefilter)
|
||||
add_subdirectory(${LIBRARIES}/image)
|
||||
add_subdirectory(${LIBRARIES}/imagediff)
|
||||
add_subdirectory(${LIBRARIES}/ktxreader)
|
||||
add_subdirectory(${LIBRARIES}/math)
|
||||
add_subdirectory(${LIBRARIES}/mathio)
|
||||
@@ -816,7 +888,9 @@ add_subdirectory(${LIBRARIES}/utils)
|
||||
add_subdirectory(${LIBRARIES}/viewer)
|
||||
add_subdirectory(${FILAMENT}/shaders)
|
||||
add_subdirectory(${EXTERNAL}/abseil/tnt)
|
||||
add_subdirectory(${EXTERNAL}/basisu/tnt)
|
||||
# Add zstd before basisu to force it to use the external zstd target,
|
||||
# preventing a duplicate symbol conflict with its bundled version.
|
||||
add_subdirectory(${EXTERNAL}/zstd/tnt)
|
||||
add_subdirectory(${EXTERNAL}/civetweb/tnt)
|
||||
add_subdirectory(${EXTERNAL}/imgui/tnt)
|
||||
add_subdirectory(${EXTERNAL}/robin-map/tnt)
|
||||
@@ -830,7 +904,10 @@ add_subdirectory(${EXTERNAL}/jsmn/tnt)
|
||||
add_subdirectory(${EXTERNAL}/stb/tnt)
|
||||
add_subdirectory(${EXTERNAL}/getopt)
|
||||
add_subdirectory(${EXTERNAL}/perfetto/tnt)
|
||||
add_subdirectory(${EXTERNAL}/zstd/tnt)
|
||||
add_subdirectory(${EXTERNAL}/basisu/tnt)
|
||||
|
||||
# imageio-lite is needed for viewer
|
||||
add_subdirectory(${LIBRARIES}/imageio-lite)
|
||||
|
||||
# Note that this has to be placed after mikktspace in order for combine_static_libs to work.
|
||||
add_subdirectory(${LIBRARIES}/geometry)
|
||||
@@ -854,6 +931,10 @@ if (FILAMENT_ENABLE_FGVIEWER)
|
||||
add_subdirectory(${LIBRARIES}/fgviewer)
|
||||
endif()
|
||||
|
||||
if (FILAMENT_SUPPORTS_WEBP_TEXTURES)
|
||||
add_subdirectory(${EXTERNAL}/libwebp/tnt)
|
||||
endif()
|
||||
|
||||
if (FILAMENT_SUPPORTS_VULKAN)
|
||||
add_subdirectory(${LIBRARIES}/bluevk)
|
||||
add_subdirectory(${EXTERNAL}/vkmemalloc/tnt)
|
||||
@@ -895,6 +976,7 @@ if (IS_HOST_PLATFORM)
|
||||
|
||||
add_subdirectory(${TOOLS}/cmgen)
|
||||
add_subdirectory(${TOOLS}/cso-lut)
|
||||
add_subdirectory(${TOOLS}/diffimg)
|
||||
add_subdirectory(${TOOLS}/filamesh)
|
||||
add_subdirectory(${TOOLS}/glslminifier)
|
||||
add_subdirectory(${TOOLS}/matc)
|
||||
@@ -909,9 +991,10 @@ if (IS_HOST_PLATFORM)
|
||||
add_subdirectory(${TOOLS}/roughness-prefilter)
|
||||
add_subdirectory(${TOOLS}/specular-color)
|
||||
add_subdirectory(${TOOLS}/uberz)
|
||||
add_subdirectory(${TOOLS}/specgen)
|
||||
endif()
|
||||
|
||||
# Generate exported executables for cross-compiled builds (Android, WebGL, and iOS)
|
||||
if (NOT CMAKE_CROSSCOMPILING)
|
||||
if ((NOT CMAKE_CROSSCOMPILING AND NOT FILAMENT_IMPORT_PREBUILT_EXECUTABLES) OR FILAMENT_EXPORT_PREBUILT_EXECUTABLES)
|
||||
export(TARGETS matc cmgen filamesh mipgen resgen uberz glslminifier FILE ${IMPORT_EXECUTABLES})
|
||||
endif()
|
||||
|
||||
@@ -20,7 +20,7 @@ The guiding principles of the filament code style and code formatting can be res
|
||||
- class access modifiers are not indented
|
||||
- last line of `.cpp` or `.h` file must be an empty line
|
||||
|
||||
```
|
||||
```c++
|
||||
for (int i = 0; i < max; i++) {
|
||||
}
|
||||
|
||||
@@ -73,12 +73,24 @@ src/data.inc
|
||||
- `public` class attributes *are not* prefixed
|
||||
- class attributes and methods are lower camelcase
|
||||
|
||||
```
|
||||
```c++
|
||||
extern int gGlobalWarming;
|
||||
|
||||
class FooBar {
|
||||
public:
|
||||
void methodName();
|
||||
FooBar(int attributeName, int sizeInBytes)
|
||||
: mAttributeName(attributeName),
|
||||
sizeInBytes(sizeInBytes) {}
|
||||
|
||||
void reallyLongMethodNameWithLotsOfArguments(bool argument1,
|
||||
int someSecondArgument, int bestArgument) {
|
||||
std::pair<bool, int> pair = {
|
||||
argument1,
|
||||
argument2,
|
||||
};
|
||||
// etc
|
||||
}
|
||||
|
||||
int sizeInBytes;
|
||||
private:
|
||||
int mAttributeName;
|
||||
@@ -97,7 +109,7 @@ private:
|
||||
- always include the copyright notice at the top of every file
|
||||
- make sure the date is correct
|
||||
|
||||
```
|
||||
```c++
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
@@ -139,7 +151,7 @@ conversely containers and algorithms are not allowed. There are exceptions such
|
||||
|
||||
*Sorting the headers is important to help catching missing `#include` directives.*
|
||||
|
||||
```
|
||||
```c++
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
@@ -183,7 +195,7 @@ conversely containers and algorithms are not allowed. There are exceptions such
|
||||
### Misc
|
||||
|
||||
- Use `auto` only when the type appears on the same line or with iterators and lambdas.
|
||||
```
|
||||
```c++
|
||||
auto foo = new Foo();
|
||||
for (auto& i : collection) { }
|
||||
```
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# Filament Release Notes log
|
||||
|
||||
**If you are merging a PR into main**: please add the release note below, under the *Release notes
|
||||
We are chaning the way Vulkan buffers are handled. We need to switch over to a managed (or view-based) model where the data stored inside the object is a proxy to a Vulkan object that can dynamically be swapped around.
|
||||
|
||||
**If you are cherry-picking a commit into an rc/ branch**: add the release note under the
|
||||
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
||||
|
||||
## Release notes for next branch cut
|
||||
|
||||
- engine: fix crash when using variance shadow maps
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.65.0'
|
||||
implementation 'com.google.android.filament:filament-android:1.69.3'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -39,19 +39,18 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
||||
|
||||
| Artifact | Description |
|
||||
| ------------- | ------------- |
|
||||
| [](https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filament-android) | The Filament rendering engine itself. |
|
||||
| [](https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filament-android-debug) | Debug version of `filament-android`. |
|
||||
| [](https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/gltfio-android) | A glTF 2.0 loader for Filament, depends on `filament-android`. |
|
||||
| [](https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filament-utils-android) | KTX loading, Kotlin math, and camera utilities, depends on `gltfio-android`. |
|
||||
| [](https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filamat-android) | A runtime material builder/compiler. This library is large but contains a full shader compiler/validator/optimizer and supports both OpenGL and Vulkan. |
|
||||
| [](https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filamat-android-lite) | A much smaller alternative to `filamat-android` that can only generate OpenGL shaders. It does not provide validation or optimizations. |
|
||||
| [](https://mvnrepository.com/artifact/com.google.android.filament/filament-android) | The Filament rendering engine itself. |
|
||||
| [](https://mvnrepository.com/artifact/com.google.android.filament/filament-android-debug) | Debug version of `filament-android`. |
|
||||
| [](https://mvnrepository.com/artifact/com.google.android.filament/gltfio-android) | A glTF 2.0 loader for Filament, depends on `filament-android`. |
|
||||
| [](https://mvnrepository.com/artifact/com.google.android.filament/filament-utils-android) | KTX loading, Kotlin math, and camera utilities, depends on `gltfio-android`. |
|
||||
| [](https://mvnrepository.com/artifact/com.google.android.filament/filamat-android) | A runtime material builder/compiler. This library is large but contains a full shader compiler/validator/optimizer and supports both OpenGL and Vulkan. |
|
||||
|
||||
### iOS
|
||||
|
||||
iOS projects can use CocoaPods to install the latest release:
|
||||
|
||||
```shell
|
||||
pod 'Filament', '~> 1.65.0'
|
||||
pod 'Filament', '~> 1.69.3'
|
||||
```
|
||||
|
||||
## Documentation
|
||||
@@ -89,7 +88,8 @@ pod 'Filament', '~> 1.65.0'
|
||||
- OpenGL ES 3.0+ for Android and iOS
|
||||
- Metal for macOS and iOS
|
||||
- Vulkan 1.0 for Android, Linux, macOS, and Windows
|
||||
- WebGL 2.0 for all platforms
|
||||
- WebGPU for Android, Linux, macOS, and Windows
|
||||
- WebGL 2.0 for all browsers supporting it
|
||||
|
||||
### Rendering
|
||||
|
||||
@@ -124,7 +124,7 @@ pod 'Filament', '~> 1.65.0'
|
||||
|
||||
- HDR bloom
|
||||
- Depth of field bokeh
|
||||
- Multiple tone mappers: generic (customizable), ACES, filmic, etc.
|
||||
- Multiple tone mappers: PBR Neutral, AgX, generic (customizable), ACES, filmic, etc.
|
||||
- Color and tone management: luminance scaling, gamut mapping
|
||||
- Color grading: exposure, night adaptation, white balance, channel mixer,
|
||||
shadows/mid-tones/highlights, ASC CDL, contrast, saturation, etc.
|
||||
@@ -158,15 +158,16 @@ pod 'Filament', '~> 1.65.0'
|
||||
- [x] KHR_draco_mesh_compression
|
||||
- [x] KHR_lights_punctual
|
||||
- [x] KHR_materials_clearcoat
|
||||
- [x] KHR_materials_dispersion
|
||||
- [x] KHR_materials_emissive_strength
|
||||
- [x] KHR_materials_ior
|
||||
- [x] KHR_materials_pbrSpecularGlossiness
|
||||
- [x] KHR_materials_sheen
|
||||
- [x] KHR_materials_specular
|
||||
- [x] KHR_materials_transmission
|
||||
- [x] KHR_materials_unlit
|
||||
- [x] KHR_materials_variants
|
||||
- [x] KHR_materials_volume
|
||||
- [x] KHR_materials_specular
|
||||
- [x] KHR_mesh_quantization
|
||||
- [x] KHR_texture_basisu
|
||||
- [x] KHR_texture_transform
|
||||
@@ -331,7 +332,7 @@ and tools.
|
||||
- `filamesh`: Mesh converter
|
||||
- `glslminifier`: Minifies GLSL source code
|
||||
- `matc`: Material compiler
|
||||
- `filament-matp`: Material parser
|
||||
- `matedit`: Material editor for compiled materials
|
||||
- `matinfo` Displays information about materials compiled with `matc`
|
||||
- `mipgen` Generates a series of miplevels from a source image
|
||||
- `normal-blending`: Tool to blend normal maps
|
||||
|
||||
@@ -7,6 +7,84 @@ A new header is inserted each time a *tag* is created.
|
||||
Instead, if you are authoring a PR for the main branch, add your release note to
|
||||
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
||||
|
||||
## v1.69.4
|
||||
|
||||
|
||||
## v1.69.3
|
||||
|
||||
|
||||
## v1.69.2
|
||||
|
||||
- engine: fix shader compilation failure in TAA material
|
||||
- engine: fix stereo & parallel shader compilation
|
||||
|
||||
## v1.69.1
|
||||
|
||||
|
||||
## v1.69.0
|
||||
|
||||
- engine: Support custom attributes morphing, and allow for omitting position and/or normal data. [⚠️ **Recompile Materials**]
|
||||
|
||||
## v1.68.5
|
||||
|
||||
- engine: "native" Streams are officially deprecated. Use "acquired" streams instead.
|
||||
- engine: add "engine.skip_frame_when_cpu_ahead_of_display" feature [b/474599530]
|
||||
|
||||
## v1.68.4
|
||||
|
||||
- gltfio: Add optional support for webp textures (EXT_texture_webp), controlled via FILAMENT_SUPPORTS_WEBP_TEXTURES cmake option
|
||||
|
||||
## v1.68.3
|
||||
|
||||
- materials: added support for the glTF `KHR_materials_dispersion` extension, which adds dispersion for refractive objects
|
||||
|
||||
## v1.68.2
|
||||
|
||||
- Support `setPresentationTime` with the Metal backend.
|
||||
|
||||
## v1.68.1
|
||||
|
||||
|
||||
## v1.68.0
|
||||
|
||||
- engine: add `View::getLastDynamicResolutionScale()` (b/457753622)
|
||||
- Metal: report GPU errors to the platform via `debugUpdateStat` (b/431665753).
|
||||
- materials: Make Material Instances' UBO descriptor use dynamic offsets. [⚠️ **Recompile Materials**]
|
||||
|
||||
## v1.67.1
|
||||
|
||||
- Metal: Add support for the `SwapChain::CONFIG_MSAA_4_SAMPLES` flag.
|
||||
- third_party: Optionally add libwebp to build
|
||||
- controlled by cmake flag FILAMENT_SUPPORTS_WEBP_TEXTURES, defaults to OFF
|
||||
- actual webp texture support for libs/gltfio coming in subsequent change
|
||||
|
||||
## v1.67.0
|
||||
|
||||
- materials: Add a new API getParameterTransformName that will return the value of the transformName field of a sampler
|
||||
parameter. [⚠️ **Recompile Materials**]
|
||||
|
||||
## v1.66.2
|
||||
|
||||
|
||||
## v1.66.1
|
||||
|
||||
- filamat: Removed a dependency on Glslang's deprecated SPIR-V remapper.
|
||||
The functionality is already implemented by calling the CanonicalizeIds pass
|
||||
in the SPIRV-Tools, and should be a non-functional change.
|
||||
|
||||
## v1.66.0
|
||||
|
||||
- materials: include default values of spec constants in material metadata [⚠️ **Recompile Materials**]
|
||||
|
||||
## v1.65.4
|
||||
|
||||
|
||||
## v1.65.3
|
||||
|
||||
|
||||
## v1.65.2
|
||||
|
||||
|
||||
## v1.65.1
|
||||
|
||||
- `setFrameScheduledCallback` now works on all backends (frame presentation scheduling is still only
|
||||
|
||||
@@ -100,7 +100,7 @@ buildscript {
|
||||
'kotlin': '2.0.21',
|
||||
'kotlin_coroutines': '1.9.0',
|
||||
'buildTools': '35.0.0',
|
||||
'ndk': '27.0.11718014',
|
||||
'ndk': '29.0.14206865',
|
||||
'androidx_core': '1.13.1',
|
||||
'androidx_annotations': '1.9.0'
|
||||
]
|
||||
@@ -124,6 +124,7 @@ buildscript {
|
||||
|
||||
ext.cmakeArgs = [
|
||||
"--no-warn-unused-cli",
|
||||
"-DANDROID_WEAK_API_DEFS=ON",
|
||||
"-DANDROID_PIE=ON",
|
||||
"-DANDROID_PLATFORM=21",
|
||||
"-DANDROID_STL=c++_static",
|
||||
@@ -193,7 +194,7 @@ subprojects {
|
||||
google()
|
||||
}
|
||||
|
||||
if (!name.startsWith("sample")) {
|
||||
if (!name.startsWith("sample") && name != "filament-tools") {
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
# Filament Tools Gradle Plugin
|
||||
|
||||
## About
|
||||
|
||||
The **Filament Tools Gradle Plugin** helps integrate Filament into your Android project. It
|
||||
automates the use of Filament's command-line tools (`matc`, `cmgen`, and `filamesh`).
|
||||
|
||||
This plugin handles:
|
||||
- **Material Compilation**: Compiles `.mat` material definitions into `.filamat` binaries.
|
||||
- **IBL Generation**: Generates Image-Based Lighting assets from HDR environment maps.
|
||||
- **Mesh Compilation**: Converts models into Filament's efficient `.filamesh` binary format. *Note:
|
||||
This tool is no longer recommended; instead, use glTF and Filament's `gltfio` library for model
|
||||
loading.*
|
||||
|
||||
The plugin hooks directly into the Android build lifecycle (via `preBuild`) and supports incremental
|
||||
builds, so assets are only recompiled when source files change.
|
||||
|
||||
## Usage
|
||||
|
||||
Apply the plugin in your module's `build.gradle` file and configure the `filament` block. You can
|
||||
specify inputs and outputs for any combination of materials, IBLs, or meshes. If a path is not
|
||||
configured, the corresponding task will be disabled.
|
||||
|
||||
### Example Configuration
|
||||
|
||||
```groovy
|
||||
plugins {
|
||||
id 'filament-plugin'
|
||||
}
|
||||
|
||||
filament {
|
||||
materialInputDir = project.layout.projectDirectory.dir("src/main/materials")
|
||||
materialOutputDir = project.layout.projectDirectory.dir("src/main/assets/materials")
|
||||
|
||||
iblInputFile = project.layout.projectDirectory.file("path/to/environment.hdr")
|
||||
iblOutputDir = project.layout.projectDirectory.dir("src/main/assets/envs")
|
||||
|
||||
meshInputFile = project.layout.projectDirectory.file("path/to/model.obj")
|
||||
meshOutputDir = project.layout.projectDirectory.dir("src/main/assets/models")
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration Details
|
||||
|
||||
- **materialInputDir**: The directory containing your source material definitions (`.mat` files).
|
||||
- **materialOutputDir**: The directory where the compiled material files (`.filamat`) will be
|
||||
generated.
|
||||
- **iblInputFile**: The source high-dynamic-range image file (e.g., `.hdr` or `.exr`) used to
|
||||
generate Image Based Lighting assets.
|
||||
- **iblOutputDir**: The directory where the generated IBL assets (typically `.ktx` files) will be
|
||||
placed.
|
||||
- **meshInputFile**: The source mesh file (e.g., `.obj`) to be compiled.
|
||||
- **meshOutputDir**: The directory where the compiled mesh file (`.filamesh`) will be generated.
|
||||
|
||||
Automatically adds tasks to your Android build to compile materials, generate an IBL, and compile a
|
||||
mesh. The plugin hooks into `preBuild` to ensure assets are generated before the application is
|
||||
built.
|
||||
|
||||
### Configuration Flags
|
||||
|
||||
You can control specific compilation options using Gradle properties (e.g., in `gradle.properties`
|
||||
or via command line `-P`).
|
||||
|
||||
- **`com.google.android.filament.exclude-vulkan`** When set to `true`, the Vulkan backend is
|
||||
excluded from the compiled materials. This can be useful to reduce the size of the generated
|
||||
assets if your application does not target Vulkan. *Default: `false` (Vulkan is included)*
|
||||
|
||||
- **`com.google.android.filament.include-webgpu`** When set to `true`, the WebGPU backend is
|
||||
included in the compiled materials. Use this if you intend to use the materials in a context
|
||||
supporting WebGPU. *Default: `false`*
|
||||
|
||||
## Tools Configuration
|
||||
|
||||
The Filament Tools plugin requires some binary tools to be available: `matc`, `cmgen`, and
|
||||
`filamesh`.
|
||||
|
||||
There are three ways to configure Filament tools:
|
||||
|
||||
1. **Point to a local path directly**
|
||||
|
||||
```groovy
|
||||
filament {
|
||||
matc {
|
||||
path = "/path/to/matc"
|
||||
}
|
||||
|
||||
cmgen {
|
||||
path = "/path/to/cmgen"
|
||||
}
|
||||
|
||||
filamesh {
|
||||
path = "/path/to/filamesh"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **Point to a Maven artifact**
|
||||
|
||||
```groovy
|
||||
filament {
|
||||
matc {
|
||||
// The minor version (the middle number) must match the Filament dependency's.
|
||||
artifact = 'com.google.android.filament:matc:1.68.5'
|
||||
}
|
||||
|
||||
cmgen {
|
||||
artifact = 'com.google.android.filament:cmgen:1.68.5'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
*Note that the `filamesh` artifact is not hosted on Maven Central, so it must be provided locally or
|
||||
via another mechanism if needed.*
|
||||
|
||||
Gradle will automatically handle downloading the tool appropriate for your machine (MacOS/Linux/Windows) from Maven.
|
||||
|
||||
3. **Set the `com.google.android.filament.tools-dir` Gradle property**
|
||||
|
||||
This will override any other configuration. Gradle will attempt to locate the tools under
|
||||
`<tools-dir>/bin` (e.g., `.../bin/matc`, `.../bin/cmgen`, `.../bin/filamesh`).
|
||||
@@ -4,13 +4,18 @@ plugins {
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
create("filament-tools-plugin") {
|
||||
id = "filament-tools-plugin"
|
||||
implementationClass = "FilamentToolsPlugin"
|
||||
create("filament-plugin") {
|
||||
id = "filament-plugin"
|
||||
implementationClass = "com.google.android.filament.gradle.FilamentPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "com.google.gradle:osdetector-gradle-plugin:1.7.3"
|
||||
}
|
||||
|
||||
@@ -1,359 +0,0 @@
|
||||
// This plugin accepts the following parameters:
|
||||
//
|
||||
// com.google.android.filament.tools-dir
|
||||
// Path to the Filament distribution/install directory for desktop.
|
||||
// This directory must contain bin/matc.
|
||||
//
|
||||
// com.google.android.filament.exclude-vulkan
|
||||
// When set, support for Vulkan will be excluded.
|
||||
//
|
||||
// Example:
|
||||
// ./gradlew -Pcom.google.android.filament.tools-dir=../../dist-release assembleDebug
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.provider.ProviderFactory
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.FileSystemOperations
|
||||
import org.gradle.api.file.FileType
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.logging.LogLevel
|
||||
import org.gradle.api.logging.Logger
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.Optional
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.api.tasks.incremental.InputFileDetails
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.internal.os.OperatingSystem
|
||||
import org.gradle.process.ExecOperations
|
||||
import org.gradle.work.ChangeType
|
||||
import org.gradle.work.Incremental
|
||||
import org.gradle.work.InputChanges
|
||||
|
||||
import java.nio.file.Paths
|
||||
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class TaskWithBinary extends DefaultTask {
|
||||
private final String binaryName
|
||||
private Property<String> binaryPath = null
|
||||
|
||||
TaskWithBinary(String name) {
|
||||
binaryName = name
|
||||
}
|
||||
|
||||
@Inject abstract ObjectFactory getObjects()
|
||||
@Inject abstract ProviderFactory getProviders()
|
||||
|
||||
@Input
|
||||
Property<String> getBinary() {
|
||||
if (binaryPath == null) {
|
||||
def tool = ["/bin/${binaryName}.exe", "/bin/${binaryName}"]
|
||||
def fullPath = tool.collect { path ->
|
||||
def filamentToolsPath = providers
|
||||
.gradleProperty("com.google.android.filament.tools-dir")
|
||||
.forUseAtConfigurationTime().get()
|
||||
def directory = objects.fileProperty()
|
||||
.fileValue(new File(filamentToolsPath)).getAsFile().get()
|
||||
Paths.get(directory.absolutePath, path).toFile()
|
||||
}
|
||||
|
||||
binaryPath = objects.property(String.class)
|
||||
binaryPath.set(
|
||||
(OperatingSystem.current().isWindows() ? fullPath[0] : fullPath[1]).toString())
|
||||
}
|
||||
return binaryPath
|
||||
}
|
||||
}
|
||||
|
||||
class LogOutputStream extends ByteArrayOutputStream {
|
||||
private final Logger logger
|
||||
private final LogLevel level
|
||||
|
||||
LogOutputStream(Logger logger, LogLevel level) {
|
||||
this.logger = logger
|
||||
this.level = level
|
||||
}
|
||||
|
||||
@Override
|
||||
void flush() {
|
||||
logger.log(level, toString())
|
||||
reset()
|
||||
}
|
||||
}
|
||||
|
||||
// Custom task to compile material files using matc
|
||||
// This task handles incremental builds
|
||||
abstract class MaterialCompiler extends TaskWithBinary {
|
||||
@Incremental
|
||||
@InputDirectory
|
||||
abstract DirectoryProperty getInputDir()
|
||||
|
||||
@OutputDirectory
|
||||
abstract DirectoryProperty getOutputDir()
|
||||
|
||||
@Inject abstract FileSystemOperations getFs()
|
||||
@Inject abstract ExecOperations getExec()
|
||||
@Inject abstract ObjectFactory getObjects()
|
||||
@Inject abstract ProviderFactory getProviders()
|
||||
|
||||
MaterialCompiler() {
|
||||
super("matc")
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void execute(InputChanges inputs) {
|
||||
if (!inputs.incremental) {
|
||||
fs.delete({
|
||||
delete(objects.fileTree().from(outputDir).matching { include '*.filamat' })
|
||||
})
|
||||
}
|
||||
|
||||
inputs.getFileChanges(inputDir).each { InputFileDetails change ->
|
||||
if (change.fileType == FileType.DIRECTORY) return
|
||||
|
||||
def file = change.file
|
||||
|
||||
if (change.changeType == ChangeType.REMOVED) {
|
||||
getOutputFile(file).delete()
|
||||
} else {
|
||||
def out = new LogOutputStream(logger, LogLevel.LIFECYCLE)
|
||||
def err = new LogOutputStream(logger, LogLevel.ERROR)
|
||||
|
||||
def header = ("Compiling material " + file + "\n").getBytes()
|
||||
out.write(header)
|
||||
out.flush()
|
||||
|
||||
if (!new File(binary.get()).exists()) {
|
||||
throw new GradleException("Could not find ${binary.get()}." +
|
||||
" Ensure Filament has been built/installed before building this app.")
|
||||
}
|
||||
|
||||
def matcArgs = []
|
||||
def exclude_vulkan = providers
|
||||
.gradleProperty("com.google.android.filament.exclude-vulkan")
|
||||
.forUseAtConfigurationTime().present
|
||||
if (!exclude_vulkan) {
|
||||
matcArgs += ['-a', 'vulkan']
|
||||
}
|
||||
def include_webgpu = providers
|
||||
.gradleProperty("com.google.android.filament.include-webgpu")
|
||||
.forUseAtConfigurationTime().present
|
||||
if (include_webgpu) {
|
||||
matcArgs += ['-a', 'webgpu', '--variant-filter=stereo']
|
||||
}
|
||||
|
||||
def mat_no_opt = providers
|
||||
.gradleProperty("com.google.android.filament.matnopt")
|
||||
.forUseAtConfigurationTime().present
|
||||
if (mat_no_opt) {
|
||||
matcArgs += ['-g']
|
||||
}
|
||||
|
||||
matcArgs += ['-a', 'opengl', '-p', 'mobile', '-o', getOutputFile(file), file]
|
||||
|
||||
exec.exec {
|
||||
standardOutput out
|
||||
errorOutput err
|
||||
executable "${binary.get()}"
|
||||
args matcArgs
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File getOutputFile(final File file) {
|
||||
return outputDir.file(file.name[0..file.name.lastIndexOf('.')] + 'filamat').get().asFile
|
||||
}
|
||||
}
|
||||
|
||||
// Custom task to process IBLs using cmgen
|
||||
// This task handles incremental builds
|
||||
abstract class IblGenerator extends TaskWithBinary {
|
||||
@Input
|
||||
@Optional
|
||||
abstract Property<String> getCmgenArgs()
|
||||
|
||||
@Incremental
|
||||
@InputFile
|
||||
abstract RegularFileProperty getInputFile()
|
||||
|
||||
@OutputDirectory
|
||||
abstract DirectoryProperty getOutputDir()
|
||||
|
||||
@Inject abstract FileSystemOperations getFs()
|
||||
@Inject abstract ExecOperations getExec()
|
||||
@Inject abstract ObjectFactory getObjects()
|
||||
|
||||
IblGenerator() {
|
||||
super("cmgen")
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void execute(InputChanges inputs) {
|
||||
if (!inputs.incremental) {
|
||||
fs.delete({
|
||||
delete(objects.fileTree().from(outputDir).matching { include '*' })
|
||||
})
|
||||
}
|
||||
|
||||
inputs.getFileChanges(inputFile).each { InputFileDetails change ->
|
||||
if (change.fileType == FileType.DIRECTORY) return
|
||||
|
||||
def file = change.file
|
||||
|
||||
if (change.changeType == ChangeType.REMOVED) {
|
||||
getOutputFile(file).delete()
|
||||
} else {
|
||||
def out = new LogOutputStream(logger, LogLevel.LIFECYCLE)
|
||||
def err = new LogOutputStream(logger, LogLevel.ERROR)
|
||||
|
||||
def header = ("Generating IBL " + file + "\n").getBytes()
|
||||
out.write(header)
|
||||
out.flush()
|
||||
|
||||
if (!new File(binary.get()).exists()) {
|
||||
throw new GradleException("Could not find ${binary.get()}." +
|
||||
" Ensure Filament has been built/installed before building this app.")
|
||||
}
|
||||
|
||||
def outputPath = outputDir.get().asFile
|
||||
def commandArgs = cmgenArgs.getOrNull()
|
||||
if (commandArgs == null) {
|
||||
commandArgs =
|
||||
'-q -x ' + outputPath + ' --format=rgb32f ' +
|
||||
'--extract-blur=0.08 --extract=' + outputPath.absolutePath
|
||||
}
|
||||
commandArgs = commandArgs + " " + file
|
||||
|
||||
exec.exec {
|
||||
standardOutput out
|
||||
errorOutput err
|
||||
executable "${binary.get()}"
|
||||
args(commandArgs.split())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File getOutputFile(final File file) {
|
||||
return outputDir.file(file.name[0..file.name.lastIndexOf('.') - 1]).get().asFile
|
||||
}
|
||||
}
|
||||
|
||||
// Custom task to compile mesh files using filamesh
|
||||
// This task handles incremental builds
|
||||
abstract class MeshCompiler extends TaskWithBinary {
|
||||
@Incremental
|
||||
@InputFile
|
||||
abstract RegularFileProperty getInputFile()
|
||||
|
||||
@OutputDirectory
|
||||
abstract DirectoryProperty getOutputDir()
|
||||
|
||||
@Inject abstract FileSystemOperations getFs()
|
||||
@Inject abstract ExecOperations getExec()
|
||||
|
||||
MeshCompiler() {
|
||||
super("filamesh")
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void execute(InputChanges inputs) {
|
||||
if (!inputs.incremental) {
|
||||
fs.delete({
|
||||
delete(objects.fileTree().from(outputDir).matching { include '*.filamesh' })
|
||||
})
|
||||
}
|
||||
|
||||
inputs.getFileChanges(inputFile).each { InputFileDetails change ->
|
||||
if (change.fileType == FileType.DIRECTORY) return
|
||||
|
||||
def file = change.file
|
||||
|
||||
if (change.changeType == ChangeType.REMOVED) {
|
||||
getOutputFile(file).delete()
|
||||
} else {
|
||||
def out = new LogOutputStream(logger, LogLevel.LIFECYCLE)
|
||||
def err = new LogOutputStream(logger, LogLevel.ERROR)
|
||||
|
||||
def header = ("Compiling mesh " + file + "\n").getBytes()
|
||||
out.write(header)
|
||||
out.flush()
|
||||
|
||||
if (!new File(binary.get()).exists()) {
|
||||
throw new GradleException("Could not find ${binary.get()}." +
|
||||
" Ensure Filament has been built/installed before building this app.")
|
||||
}
|
||||
|
||||
exec.exec {
|
||||
standardOutput out
|
||||
errorOutput err
|
||||
executable "${binary.get()}"
|
||||
args(file, getOutputFile(file))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File getOutputFile(final File file) {
|
||||
return outputDir.file(file.name[0..file.name.lastIndexOf('.')] + 'filamesh').get().asFile
|
||||
}
|
||||
}
|
||||
|
||||
class FilamentToolsPluginExtension {
|
||||
DirectoryProperty materialInputDir
|
||||
DirectoryProperty materialOutputDir
|
||||
|
||||
String cmgenArgs
|
||||
RegularFileProperty iblInputFile
|
||||
DirectoryProperty iblOutputDir
|
||||
|
||||
RegularFileProperty meshInputFile
|
||||
DirectoryProperty meshOutputDir
|
||||
}
|
||||
|
||||
class FilamentToolsPlugin implements Plugin<Project> {
|
||||
void apply(Project project) {
|
||||
def extension = project.extensions.create('filamentTools', FilamentToolsPluginExtension)
|
||||
extension.materialInputDir = project.objects.directoryProperty()
|
||||
extension.materialOutputDir = project.objects.directoryProperty()
|
||||
extension.iblInputFile = project.objects.fileProperty()
|
||||
extension.iblOutputDir = project.objects.directoryProperty()
|
||||
extension.meshInputFile = project.objects.fileProperty()
|
||||
extension.meshOutputDir = project.objects.directoryProperty()
|
||||
|
||||
project.tasks.register("filamentCompileMaterials", MaterialCompiler) {
|
||||
enabled =
|
||||
extension.materialInputDir.isPresent() &&
|
||||
extension.materialOutputDir.isPresent()
|
||||
inputDir.set(extension.materialInputDir.getOrNull())
|
||||
outputDir.set(extension.materialOutputDir.getOrNull())
|
||||
}
|
||||
|
||||
project.preBuild.dependsOn "filamentCompileMaterials"
|
||||
|
||||
project.tasks.register("filamentGenerateIbl", IblGenerator) {
|
||||
enabled = extension.iblInputFile.isPresent() && extension.iblOutputDir.isPresent()
|
||||
cmgenArgs = extension.cmgenArgs
|
||||
inputFile = extension.iblInputFile.getOrNull()
|
||||
outputDir = extension.iblOutputDir.getOrNull()
|
||||
}
|
||||
|
||||
project.preBuild.dependsOn "filamentGenerateIbl"
|
||||
|
||||
project.tasks.register("filamentCompileMesh", MeshCompiler) {
|
||||
enabled = extension.meshInputFile.isPresent() && extension.meshOutputDir.isPresent()
|
||||
inputFile = extension.meshInputFile.getOrNull()
|
||||
outputDir = extension.meshOutputDir.getOrNull()
|
||||
}
|
||||
|
||||
project.preBuild.dependsOn "filamentCompileMesh"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2026 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.filament.gradle
|
||||
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
|
||||
class FilamentExtension {
|
||||
final ToolsLocator tools
|
||||
final DirectoryProperty materialInputDir
|
||||
final DirectoryProperty materialOutputDir
|
||||
final Property<String> cmgenArgs
|
||||
final RegularFileProperty iblInputFile
|
||||
final DirectoryProperty iblOutputDir
|
||||
final RegularFileProperty meshInputFile
|
||||
final DirectoryProperty meshOutputDir
|
||||
|
||||
FilamentExtension(Project project) {
|
||||
this.tools = new ToolsLocator(project)
|
||||
this.materialInputDir = project.objects.directoryProperty()
|
||||
this.materialOutputDir = project.objects.directoryProperty()
|
||||
this.cmgenArgs = project.objects.property(String)
|
||||
this.iblInputFile = project.objects.fileProperty()
|
||||
this.iblOutputDir = project.objects.directoryProperty()
|
||||
this.meshInputFile = project.objects.fileProperty()
|
||||
this.meshOutputDir = project.objects.directoryProperty()
|
||||
}
|
||||
|
||||
void matc(Action<ToolsLocator.ToolConfig> action) {
|
||||
action.execute(tools.matc)
|
||||
}
|
||||
|
||||
void cmgen(Action<ToolsLocator.ToolConfig> action) {
|
||||
action.execute(tools.cmgen)
|
||||
}
|
||||
|
||||
void filamesh(Action<ToolsLocator.ToolConfig> action) {
|
||||
action.execute(tools.filamesh)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2026 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.filament.gradle
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
|
||||
class FilamentPlugin implements Plugin<Project> {
|
||||
@Override
|
||||
void apply(Project project) {
|
||||
project.pluginManager.apply("com.google.osdetector")
|
||||
|
||||
FilamentExtension extension = project.extensions.create("filament", FilamentExtension, project)
|
||||
|
||||
project.afterEvaluate {
|
||||
extension.tools.resolve(project)
|
||||
|
||||
project.tasks.register("filamentCompileMaterials", MaterialCompileTask) {
|
||||
enabled = extension.materialInputDir.isPresent() && extension.materialOutputDir.isPresent()
|
||||
inputDir.set(extension.materialInputDir.getOrNull())
|
||||
outputDir.set(extension.materialOutputDir.getOrNull())
|
||||
matcTool.from(extension.tools.matcToolFiles)
|
||||
}
|
||||
|
||||
project.tasks.register("filamentGenerateIbl", IblGenerateTask) {
|
||||
enabled = extension.iblInputFile.isPresent() && extension.iblOutputDir.isPresent()
|
||||
cmgenArgs = extension.cmgenArgs
|
||||
inputFile.set(extension.iblInputFile.getOrNull())
|
||||
outputDir.set(extension.iblOutputDir.getOrNull())
|
||||
cmgenTool.from(extension.tools.cmgenToolFiles)
|
||||
}
|
||||
|
||||
project.tasks.register("filamentCompileMesh", MeshCompileTask) {
|
||||
enabled = extension.meshInputFile.isPresent() && extension.meshOutputDir.isPresent()
|
||||
inputFile = extension.meshInputFile.getOrNull()
|
||||
outputDir = extension.meshOutputDir.getOrNull()
|
||||
filameshTool.from(extension.tools.filameshToolFiles)
|
||||
}
|
||||
|
||||
project.preBuild.dependsOn "filamentCompileMaterials"
|
||||
project.preBuild.dependsOn "filamentGenerateIbl"
|
||||
project.preBuild.dependsOn "filamentCompileMesh"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (C) 2026 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.filament.gradle
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.FileSystemOperations
|
||||
import org.gradle.api.file.FileType
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
import org.gradle.api.tasks.Optional
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.process.ExecOperations
|
||||
import org.gradle.work.ChangeType
|
||||
import org.gradle.work.Incremental
|
||||
import org.gradle.work.InputChanges
|
||||
import org.gradle.api.tasks.incremental.InputFileDetails
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class IblGenerateTask extends DefaultTask {
|
||||
|
||||
@Input
|
||||
@Optional
|
||||
abstract Property<String> getCmgenArgs()
|
||||
|
||||
@Incremental
|
||||
@InputFile
|
||||
abstract RegularFileProperty getInputFile()
|
||||
|
||||
@OutputDirectory
|
||||
abstract DirectoryProperty getOutputDir()
|
||||
|
||||
@InputFiles
|
||||
abstract ConfigurableFileCollection getCmgenTool()
|
||||
|
||||
@Inject
|
||||
abstract FileSystemOperations getFileSystemOperations()
|
||||
|
||||
@Inject
|
||||
abstract ExecOperations getExecOperations()
|
||||
|
||||
@Inject
|
||||
abstract ObjectFactory getObjectFactory()
|
||||
|
||||
@TaskAction
|
||||
void execute(InputChanges inputs) {
|
||||
if (cmgenTool.empty) {
|
||||
throw new IllegalStateException(
|
||||
"cmgen executable not configured. Please configure the 'cmgen' block in the " +
|
||||
"'filament' extension or set the 'com.google.android.filament.tools-dir' " +
|
||||
"property."
|
||||
)
|
||||
}
|
||||
|
||||
File cmgen = getCmgenTool().singleFile
|
||||
if (!cmgen.exists()) {
|
||||
throw new IllegalStateException("cmgen executable does not exist: ${cmgen.absolutePath}")
|
||||
}
|
||||
|
||||
if (!cmgen.canExecute()) {
|
||||
cmgen.setExecutable(true)
|
||||
}
|
||||
|
||||
if (!inputs.incremental) {
|
||||
getFileSystemOperations().delete {
|
||||
delete(getObjectFactory().fileTree().from(getOutputDir()).matching { include '*' })
|
||||
}
|
||||
}
|
||||
|
||||
inputs.getFileChanges(getInputFile()).each { InputFileDetails change ->
|
||||
if (change.fileType == FileType.DIRECTORY) return
|
||||
|
||||
def file = change.file
|
||||
|
||||
if (change.changeType == ChangeType.REMOVED) {
|
||||
computeOutputFile(file).delete()
|
||||
} else {
|
||||
println "Generating IBL: ${file.name}"
|
||||
|
||||
def outputPath = getOutputDir().get().asFile
|
||||
def commandArgs = getCmgenArgs().getOrNull()
|
||||
if (commandArgs == null) {
|
||||
// Default args if not provided
|
||||
commandArgs = '-q -x ' + outputPath + ' --format=rgb32f ' +
|
||||
'--extract-blur=0.08 --extract=' + outputPath.absolutePath
|
||||
}
|
||||
|
||||
def argsList = commandArgs.split(' ').toList()
|
||||
argsList.add(file.absolutePath)
|
||||
|
||||
getExecOperations().exec { spec ->
|
||||
spec.executable(cmgen)
|
||||
spec.args(argsList)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File computeOutputFile(final File file) {
|
||||
String name = file.name
|
||||
int dotIndex = name.lastIndexOf('.')
|
||||
String baseName = dotIndex > 0 ? name.substring(0, dotIndex) : name
|
||||
return getOutputDir().file(baseName).get().asFile
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (C) 2026 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.filament.gradle
|
||||
|
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.FileSystemOperations
|
||||
import org.gradle.api.file.FileType
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.provider.ProviderFactory
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.PathSensitive
|
||||
import org.gradle.api.tasks.PathSensitivity
|
||||
import org.gradle.api.tasks.SkipWhenEmpty
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.process.ExecOperations
|
||||
import org.gradle.work.ChangeType
|
||||
import org.gradle.work.Incremental
|
||||
import org.gradle.work.InputChanges
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class MaterialCompileTask extends DefaultTask {
|
||||
|
||||
@Incremental
|
||||
@InputDirectory
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
abstract DirectoryProperty getInputDir()
|
||||
|
||||
@OutputDirectory
|
||||
abstract DirectoryProperty getOutputDir()
|
||||
|
||||
@InputFiles
|
||||
@PathSensitive(PathSensitivity.NONE)
|
||||
abstract ConfigurableFileCollection getMatcTool()
|
||||
|
||||
@Inject
|
||||
abstract ExecOperations getExecOperations()
|
||||
|
||||
@Inject
|
||||
abstract FileSystemOperations getFileSystemOperations()
|
||||
|
||||
@Inject
|
||||
abstract ObjectFactory getObjectFactory()
|
||||
|
||||
@Inject
|
||||
abstract ProviderFactory getProviderFactory()
|
||||
|
||||
@TaskAction
|
||||
void compile(InputChanges inputs) {
|
||||
if (matcTool.empty) {
|
||||
throw new IllegalStateException(
|
||||
"matc executable not configured. Please configure the 'matc' block in the " +
|
||||
"'filament' extension or set the 'com.google.android.filament.tools-dir' " +
|
||||
"property."
|
||||
)
|
||||
}
|
||||
|
||||
File matc = matcTool.singleFile
|
||||
if (!matc.exists()) {
|
||||
throw new IllegalStateException("matc executable does not exist: ${matc.absolutePath}")
|
||||
}
|
||||
|
||||
if (!matc.canExecute()) {
|
||||
matc.setExecutable(true)
|
||||
}
|
||||
|
||||
if (!inputs.incremental) {
|
||||
getFileSystemOperations().delete {
|
||||
delete(getObjectFactory().fileTree().from(getOutputDir()).matching {
|
||||
include '*.filamat'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
def pf = getProviderFactory()
|
||||
def excludeVulkanProperty = pf.gradleProperty("com.google.android.filament.exclude-vulkan")
|
||||
def includeWebGpuProperty = pf.gradleProperty("com.google.android.filament.include-webgpu")
|
||||
def matNoOptProperty = pf.gradleProperty("com.google.android.filament.matnopt")
|
||||
def excludeVulkan = excludeVulkanProperty.orNull == "true"
|
||||
def includeWebGpu = includeWebGpuProperty.orNull == "true"
|
||||
def matNoOpt = matNoOptProperty.orNull == "true"
|
||||
|
||||
inputs.getFileChanges(getInputDir()).each { change ->
|
||||
if (change.fileType == FileType.DIRECTORY) return
|
||||
|
||||
File file = change.file
|
||||
File outputFile = computeOutputFile(file)
|
||||
|
||||
if (change.changeType == ChangeType.REMOVED) {
|
||||
outputFile.delete()
|
||||
} else {
|
||||
println "Compiling material: ${file.name}"
|
||||
|
||||
def args = []
|
||||
if (!excludeVulkan) {
|
||||
args += ['-a', 'vulkan']
|
||||
}
|
||||
|
||||
if (includeWebGpu) {
|
||||
args += ['-a', 'webgpu', '--variant-filter=stereo']
|
||||
}
|
||||
|
||||
if (matNoOpt) {
|
||||
args += ['-g']
|
||||
}
|
||||
|
||||
args += [
|
||||
'-a', 'opengl', '-p', 'mobile',
|
||||
'-o', outputFile.absolutePath,
|
||||
file.absolutePath
|
||||
]
|
||||
|
||||
getExecOperations().exec { spec ->
|
||||
spec.executable(matc)
|
||||
spec.args(args)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File computeOutputFile(File inputFile) {
|
||||
String baseName = inputFile.name
|
||||
int dotIndex = baseName.lastIndexOf('.')
|
||||
if (dotIndex > 0) {
|
||||
baseName = baseName.substring(0, dotIndex)
|
||||
}
|
||||
return getOutputDir().file("${baseName}.filamat").get().asFile
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2026 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.filament.gradle
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.FileSystemOperations
|
||||
import org.gradle.api.file.FileType
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.PathSensitive
|
||||
import org.gradle.api.tasks.PathSensitivity
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.process.ExecOperations
|
||||
import org.gradle.work.ChangeType
|
||||
import org.gradle.work.Incremental
|
||||
import org.gradle.work.InputChanges
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class MeshCompileTask extends DefaultTask {
|
||||
|
||||
@Incremental
|
||||
@InputFile
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
abstract RegularFileProperty getInputFile()
|
||||
|
||||
@OutputDirectory
|
||||
abstract DirectoryProperty getOutputDir()
|
||||
|
||||
@InputFiles
|
||||
@PathSensitive(PathSensitivity.NONE)
|
||||
abstract ConfigurableFileCollection getFilameshTool()
|
||||
|
||||
@Inject
|
||||
abstract ExecOperations getExecOperations()
|
||||
|
||||
@Inject
|
||||
abstract FileSystemOperations getFileSystemOperations()
|
||||
|
||||
@Inject
|
||||
abstract ObjectFactory getObjectFactory()
|
||||
|
||||
@TaskAction
|
||||
void compile(InputChanges inputs) {
|
||||
if (filameshTool.empty) {
|
||||
throw new IllegalStateException(
|
||||
"filamesh executable not configured. Please configure the 'filamesh' block in the " +
|
||||
"'filament' extension or set the 'com.google.android.filament.tools-dir' " +
|
||||
"property."
|
||||
)
|
||||
}
|
||||
|
||||
File filamesh = filameshTool.singleFile
|
||||
if (!filamesh.exists()) {
|
||||
throw new IllegalStateException("filamesh executable does not exist: ${filamesh.absolutePath}")
|
||||
}
|
||||
|
||||
if (!filamesh.canExecute()) {
|
||||
filamesh.setExecutable(true)
|
||||
}
|
||||
|
||||
if (!inputs.incremental) {
|
||||
getFileSystemOperations().delete {
|
||||
delete(getObjectFactory().fileTree().from(getOutputDir()).matching {
|
||||
include '*.filamesh'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
inputs.getFileChanges(inputFile).each { change ->
|
||||
if (change.fileType == FileType.DIRECTORY) return
|
||||
|
||||
File file = change.file
|
||||
File outputFile = computeOutputFile(file)
|
||||
|
||||
if (change.changeType == ChangeType.REMOVED) {
|
||||
outputFile.delete()
|
||||
} else {
|
||||
println "Compiling mesh: ${file.name}"
|
||||
|
||||
getExecOperations().exec { spec ->
|
||||
spec.executable(filamesh)
|
||||
spec.args(file.absolutePath, outputFile.absolutePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File computeOutputFile(File inputFile) {
|
||||
String baseName = inputFile.name
|
||||
int dotIndex = baseName.lastIndexOf('.')
|
||||
if (dotIndex > 0) {
|
||||
baseName = baseName.substring(0, dotIndex)
|
||||
}
|
||||
return getOutputDir().file("${baseName}.filamesh").get().asFile
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (C) 2026 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.filament.gradle
|
||||
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.internal.os.OperatingSystem
|
||||
|
||||
import java.nio.file.Paths
|
||||
|
||||
class ToolsLocator {
|
||||
static class ToolConfig {
|
||||
String artifact
|
||||
String path
|
||||
FileCollection files
|
||||
}
|
||||
|
||||
final ToolConfig matc = new ToolConfig()
|
||||
final ToolConfig cmgen = new ToolConfig()
|
||||
final ToolConfig filamesh = new ToolConfig()
|
||||
private final Project project
|
||||
|
||||
ToolsLocator(Project project) {
|
||||
this.project = project
|
||||
}
|
||||
|
||||
void resolve(Project project) {
|
||||
resolveTool(matc, "matc")
|
||||
resolveTool(cmgen, "cmgen")
|
||||
resolveTool(filamesh, "filamesh")
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a specific tool by its name and sets the {@link ToolConfig#files} property of the
|
||||
* provided {@link ToolConfig} object. It first attempts to locate the tool based on a Gradle
|
||||
* property `com.google.android.filament.tools-dir` if present, otherwise it resolves the tool
|
||||
* through a Gradle configuration.
|
||||
*
|
||||
* @param tool The {@link ToolConfig} object whose {@code files} property will be set.
|
||||
* @param name The name of the tool (e.g., "matc", "cmgen").
|
||||
*/
|
||||
private void resolveTool(ToolConfig tool, String name) {
|
||||
// Find the OS classifier, e.g. 'osx-aarch_64'.
|
||||
def classifier =
|
||||
project.extensions.getByType(com.google.gradle.osdetector.OsDetector).classifier
|
||||
|
||||
// If com.google.android.filament.tools-dir is set, we'll use it as the tool's base path.
|
||||
def toolsDirProp = project.providers.gradleProperty("com.google.android.filament.tools-dir")
|
||||
if (toolsDirProp.isPresent()) {
|
||||
def toolsDir = toolsDirProp.get()
|
||||
def path = OperatingSystem.current().isWindows() ?
|
||||
"${toolsDir}/bin/${name}.exe" :
|
||||
"${toolsDir}/bin/${name}"
|
||||
tool.files = project.files(path)
|
||||
return
|
||||
}
|
||||
|
||||
// If an explicit path for the tool is provided in ToolConfig
|
||||
// (e.g. matc { path = 'path/to/tool' }), use it directly.
|
||||
if (tool.path) {
|
||||
tool.files = project.files(tool.path)
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise, if an artifact is provided
|
||||
// (e.g. matc { artifact = 'com.google.android.filament:matc:1.68.5' }), resolve it.
|
||||
if (tool.artifact) {
|
||||
String depString = tool.artifact
|
||||
|
||||
// In Gradle, a configuration is a named, manageable group of dependencies.
|
||||
// Resolve the tool artifact using a detached configuration. A detached configuration
|
||||
// is a temporary, isolated configuration that is not part of the project's regular
|
||||
// configuration hierarchy.
|
||||
Configuration config = project.configurations.detachedConfiguration()
|
||||
config.setTransitive(false) // We only want the tool itself, not its dependencies
|
||||
|
||||
def dep = project.dependencies.create("${depString}:${classifier}@exe")
|
||||
config.dependencies.add(dep)
|
||||
|
||||
// A Gradle Configuration implements FileCollection. When treated as a FileCollection,
|
||||
// it represents the resolved files of its dependencies.
|
||||
tool.files = config
|
||||
}
|
||||
}
|
||||
|
||||
FileCollection getMatcToolFiles() {
|
||||
return matc.files ?: project.files()
|
||||
}
|
||||
|
||||
FileCollection getCmgenToolFiles() {
|
||||
return cmgen.files ?: project.files()
|
||||
}
|
||||
|
||||
FileCollection getFilameshToolFiles() {
|
||||
return filamesh.files ?: project.files()
|
||||
}
|
||||
}
|
||||
@@ -335,6 +335,13 @@ Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderRefract
|
||||
builder->refractionMode((MaterialBuilder::RefractionMode) mode);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderReflectionMode(JNIEnv* env,
|
||||
jclass, jlong nativeBuilder, jint mode) {
|
||||
auto builder = (MaterialBuilder*) nativeBuilder;
|
||||
builder->reflectionMode((MaterialBuilder::ReflectionMode) mode);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderRefractionType(JNIEnv* env,
|
||||
jclass, jlong nativeBuilder, jint type) {
|
||||
|
||||
@@ -177,6 +177,11 @@ public class MaterialBuilder {
|
||||
SCREEN_SPACE
|
||||
}
|
||||
|
||||
public enum ReflectionMode {
|
||||
DEFAULT,
|
||||
SCREEN_SPACE
|
||||
}
|
||||
|
||||
public enum RefractionType {
|
||||
SOLID,
|
||||
THIN
|
||||
@@ -403,6 +408,12 @@ public class MaterialBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public MaterialBuilder reflectionMode(ReflectionMode mode) {
|
||||
nMaterialBuilderReflectionMode(mNativeObject, mode.ordinal());
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public MaterialBuilder refractionType(RefractionType type) {
|
||||
nMaterialBuilderRefractionType(mNativeObject, type.ordinal());
|
||||
@@ -604,6 +615,7 @@ public class MaterialBuilder {
|
||||
private static native void nMaterialBuilderSpecularAntiAliasingThreshold(long mNativeObject,
|
||||
float threshold);
|
||||
private static native void nMaterialBuilderRefractionMode(long nativeBuilder, int mode);
|
||||
private static native void nMaterialBuilderReflectionMode(long nativeBuilder, int mode);
|
||||
private static native void nMaterialBuilderRefractionType(long nativeBuilder, int type);
|
||||
private static native void nMaterialBuilderClearCoatIorChange(long mNativeObject,
|
||||
boolean clearCoatIorChange);
|
||||
|
||||
@@ -51,10 +51,6 @@ add_library(bluevk STATIC IMPORTED)
|
||||
set_target_properties(bluevk PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libbluevk.a)
|
||||
|
||||
add_library(vkshaders STATIC IMPORTED)
|
||||
set_target_properties(vkshaders PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libvkshaders.a)
|
||||
|
||||
if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
add_library(webgpu_dawn STATIC IMPORTED)
|
||||
set_target_properties(webgpu_dawn PROPERTIES IMPORTED_LOCATION
|
||||
@@ -138,6 +134,7 @@ target_include_directories(filament-jni PRIVATE
|
||||
../../filament/backend/include
|
||||
../../third_party/robin-map
|
||||
../../third_party/perfetto
|
||||
../../libs/bluevk/include
|
||||
../../libs/utils/include)
|
||||
|
||||
# Ordering is significant in the following list. The PRIVATE qualifier prevents transitive deps.
|
||||
@@ -165,10 +162,15 @@ target_link_libraries(filament-jni
|
||||
PRIVATE $<$<STREQUAL:${FILAMENT_ENABLE_MATDBG},ON>:matdbg>
|
||||
PRIVATE $<$<STREQUAL:${FILAMENT_ENABLE_MATDBG},ON>:filamat>
|
||||
PRIVATE $<$<STREQUAL:${FILAMENT_SUPPORTS_VULKAN},ON>:bluevk>
|
||||
PRIVATE $<$<STREQUAL:${FILAMENT_SUPPORTS_VULKAN},ON>:vkshaders>
|
||||
PRIVATE $<$<STREQUAL:${FILAMENT_SUPPORTS_VULKAN},ON>:smol-v>
|
||||
PRIVATE $<$<STREQUAL:${FILAMENT_SUPPORTS_WEBGPU},ON>:webgpu_dawn>
|
||||
)
|
||||
|
||||
# Force a relink when the version script is changed:
|
||||
set_target_properties(filament-jni PROPERTIES LINK_DEPENDS ${VERSION_SCRIPT})
|
||||
|
||||
if (FILAMENT_SUPPORTS_VULKAN)
|
||||
add_definitions(-DFILAMENT_SUPPORTS_VULKAN=1)
|
||||
else()
|
||||
add_definitions(-DFILAMENT_SUPPORTS_VULKAN=0)
|
||||
endif()
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <filament/Camera.h>
|
||||
|
||||
|
||||
#include <utils/Entity.h>
|
||||
|
||||
#include <math/mat4.h>
|
||||
@@ -40,6 +41,13 @@ Java_com_google_android_filament_Camera_nSetProjectionFov(JNIEnv*, jclass ,
|
||||
camera->setProjection(fovInDegrees, aspect, near, far, (Camera::Fov) fov);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jdouble JNICALL
|
||||
Java_com_google_android_filament_Camera_nGetFieldOfViewInDegrees(JNIEnv*, jclass,
|
||||
jlong nativeCamera, jint direction) {
|
||||
Camera *camera = (Camera *) nativeCamera;
|
||||
return camera->getFieldOfViewInDegrees((Camera::Fov) direction);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Camera_nSetLensProjection(JNIEnv*, jclass,
|
||||
jlong nativeCamera, jdouble focalLength, jdouble aspect, jdouble near, jdouble far) {
|
||||
@@ -62,6 +70,21 @@ Java_com_google_android_filament_Camera_nSetCustomProjection(JNIEnv *env, jclass
|
||||
env->ReleaseDoubleArrayElements(inProjectionForCulling_, inProjectionForCulling, JNI_ABORT);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Camera_nSetCustomEyeProjection(JNIEnv *env, jclass,
|
||||
jlong nativeCamera, jdoubleArray inProjection_, jint count, jdoubleArray inProjectionForCulling_,
|
||||
jdouble near, jdouble far) {
|
||||
Camera *camera = (Camera *) nativeCamera;
|
||||
jdouble *inProjection = env->GetDoubleArrayElements(inProjection_, NULL);
|
||||
jdouble *inProjectionForCulling = env->GetDoubleArrayElements(inProjectionForCulling_, NULL);
|
||||
camera->setCustomEyeProjection(
|
||||
reinterpret_cast<const filament::math::mat4 *>(inProjection), (size_t) count,
|
||||
*reinterpret_cast<const filament::math::mat4 *>(inProjectionForCulling),
|
||||
near, far);
|
||||
env->ReleaseDoubleArrayElements(inProjection_, inProjection, JNI_ABORT);
|
||||
env->ReleaseDoubleArrayElements(inProjectionForCulling_, inProjectionForCulling, JNI_ABORT);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Camera_nSetScaling(JNIEnv* env, jclass,
|
||||
jlong nativeCamera, jdouble x, jdouble y) {
|
||||
@@ -76,6 +99,17 @@ Java_com_google_android_filament_Camera_nSetShift(JNIEnv* env, jclass,
|
||||
camera->setShift({(double)x, (double)y});
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Camera_nGetShift(JNIEnv* env, jclass,
|
||||
jlong nativeCamera, jdoubleArray out_) {
|
||||
Camera *camera = (Camera *) nativeCamera;
|
||||
jdouble *out = env->GetDoubleArrayElements(out_, NULL);
|
||||
filament::math::double2 s = camera->getShift();
|
||||
out[0] = s.x;
|
||||
out[1] = s.y;
|
||||
env->ReleaseDoubleArrayElements(out_, out, 0);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Camera_nLookAt(JNIEnv*, jclass, jlong nativeCamera,
|
||||
jdouble eye_x, jdouble eye_y, jdouble eye_z, jdouble center_x, jdouble center_y,
|
||||
@@ -115,6 +149,15 @@ Java_com_google_android_filament_Camera_nSetModelMatrixFp64(JNIEnv *env, jclass,
|
||||
env->ReleaseDoubleArrayElements(in_, in, JNI_ABORT);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Camera_nSetEyeModelMatrix(JNIEnv *env, jclass,
|
||||
jlong nativeCamera, jint eyeId, jdoubleArray model_) {
|
||||
Camera* camera = (Camera *) nativeCamera;
|
||||
jdouble *model = env->GetDoubleArrayElements(model_, NULL);
|
||||
camera->setEyeModelMatrix((uint8_t)eyeId, *reinterpret_cast<const filament::math::mat4*>(model));
|
||||
env->ReleaseDoubleArrayElements(model_, model, JNI_ABORT);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Camera_nGetProjectionMatrix(JNIEnv *env, jclass,
|
||||
jlong nativeCamera, jdoubleArray out_) {
|
||||
@@ -280,3 +323,5 @@ Java_com_google_android_filament_Camera_nComputeEffectiveFov(JNIEnv*, jclass,
|
||||
jdouble fovInDegrees, jdouble focusDistance) {
|
||||
return Camera::computeEffectiveFov(fovInDegrees, focusDistance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <filament/Camera.h>
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/MorphTargetBuffer.h>
|
||||
|
||||
#include <utils/Entity.h>
|
||||
#include <utils/EntityManager.h>
|
||||
@@ -207,6 +208,14 @@ Java_com_google_android_filament_Engine_nDestroySkinningBuffer(JNIEnv*, jclass,
|
||||
return engine->destroy(skinningBuffer);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_Engine_nDestroyMorphTargetBuffer(JNIEnv*, jclass,
|
||||
jlong nativeEngine, jlong nativeMorphTargetBuffer) {
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
MorphTargetBuffer* mtb = (MorphTargetBuffer*) nativeMorphTargetBuffer;
|
||||
return engine->destroy(mtb);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_Engine_nDestroyIndirectLight(JNIEnv*, jclass,
|
||||
jlong nativeEngine, jlong nativeIndirectLight) {
|
||||
@@ -328,6 +337,13 @@ Java_com_google_android_filament_Engine_nIsValidSkinningBuffer(JNIEnv*, jclass,
|
||||
return (jboolean)engine->isValid((SkinningBuffer*)nativeSkinningBuffer);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_Engine_nIsValidMorphTargetBuffer(JNIEnv*, jclass,
|
||||
jlong nativeEngine, jlong nativeMorphTargetBuffer) {
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
return (jboolean) engine->isValid((MorphTargetBuffer*) nativeMorphTargetBuffer);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_Engine_nIsValidIndirectLight(JNIEnv*, jclass,
|
||||
jlong nativeEngine, jlong nativeIndirectLight) {
|
||||
@@ -557,7 +573,8 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
|
||||
jboolean disableHandleUseAfterFreeCheck,
|
||||
jint preferredShaderLanguage,
|
||||
jboolean forceGLES2Context, jboolean assertNativeWindowIsValid,
|
||||
jint gpuContextPriority) {
|
||||
jint gpuContextPriority,
|
||||
jlong sharedUboInitialSizeInBytes) {
|
||||
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
|
||||
Engine::Config config = {
|
||||
.commandBufferSizeMB = (uint32_t) commandBufferSizeMB,
|
||||
@@ -576,6 +593,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
|
||||
.forceGLES2Context = (bool) forceGLES2Context,
|
||||
.assertNativeWindowIsValid = (bool) assertNativeWindowIsValid,
|
||||
.gpuContextPriority = (Engine::GpuContextPriority) gpuContextPriority,
|
||||
.sharedUboInitialSizeInBytes = (uint32_t) sharedUboInitialSizeInBytes,
|
||||
};
|
||||
builder->config(&config);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ using namespace filament;
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_Material_nBuilderBuild(JNIEnv *env, jclass,
|
||||
jlong nativeEngine, jobject buffer_, jint size, jint shBandCount, jint shadowQuality) {
|
||||
jlong nativeEngine, jobject buffer_, jint size, jint shBandCount, jint shadowQuality, jint uboBatchingMode) {
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
AutoBuffer buffer(env, buffer_, size);
|
||||
auto builder = Material::Builder();
|
||||
@@ -33,6 +33,7 @@ Java_com_google_android_filament_Material_nBuilderBuild(JNIEnv *env, jclass,
|
||||
builder.sphericalHarmonicsBandCount(shBandCount);
|
||||
}
|
||||
builder.shadowSamplingQuality((Material::Builder::ShadowSamplingQuality)shadowQuality);
|
||||
builder.uboBatching((Material::UboBatchingMode)uboBatchingMode);
|
||||
Material* material = builder
|
||||
.package(buffer.getData(), buffer.getSize())
|
||||
.build(*engine);
|
||||
@@ -95,6 +96,14 @@ Java_com_google_android_filament_Material_nGetBlendingMode(JNIEnv*, jclass,
|
||||
return (jint) material->getBlendingMode();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_Material_nGetTransparencyMode(JNIEnv*, jclass,
|
||||
jlong nativeMaterial) {
|
||||
Material* material = (Material*) nativeMaterial;
|
||||
return (jint) material->getTransparencyMode();
|
||||
}
|
||||
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL
|
||||
@@ -279,6 +288,17 @@ Java_com_google_android_filament_Material_nHasParameter(JNIEnv* env, jclass,
|
||||
return (jboolean) hasParameter;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jstring JNICALL
|
||||
Java_com_google_android_filament_Material_nGetParameterTransformName(JNIEnv* env, jclass,
|
||||
jlong nativeMaterial, jstring samplerName_) {
|
||||
Material* material = (Material*) nativeMaterial;
|
||||
const char* samplerName = env->GetStringUTFChars(samplerName_, 0);
|
||||
const char* transformName = material->getParameterTransformName(samplerName);
|
||||
jstring transformName_ = env->NewStringUTF(transformName ? transformName : "");
|
||||
env->ReleaseStringUTFChars(samplerName_, samplerName);
|
||||
return transformName_;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Material_nCompile(JNIEnv *env, jclass clazz,
|
||||
|
||||
@@ -564,3 +564,19 @@ Java_com_google_android_filament_MaterialInstance_nGetDepthFunc(JNIEnv* env, jcl
|
||||
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
|
||||
return (jint)instance->getDepthFunc();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nSetTransparencyMode(JNIEnv*, jclass,
|
||||
jlong nativeMaterialInstance, jint mode) {
|
||||
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
|
||||
instance->setTransparencyMode((MaterialInstance::TransparencyMode) mode);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nGetTransparencyMode(JNIEnv*, jclass,
|
||||
jlong nativeMaterialInstance) {
|
||||
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
|
||||
return (jint) instance->getTransparencyMode();
|
||||
}
|
||||
|
||||
@@ -58,6 +58,27 @@ Java_com_google_android_filament_MorphTargetBuffer_nBuilderCount(JNIEnv*, jclass
|
||||
builder->count((size_t) count);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MorphTargetBuffer_nBuilderWithPositions(JNIEnv*, jclass,
|
||||
jlong nativeBuilder, jboolean enabled) {
|
||||
MorphTargetBuffer::Builder* builder = (MorphTargetBuffer::Builder*) nativeBuilder;
|
||||
builder->withPositions(enabled);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MorphTargetBuffer_nBuilderWithTangents(JNIEnv*, jclass,
|
||||
jlong nativeBuilder, jboolean enabled) {
|
||||
MorphTargetBuffer::Builder* builder = (MorphTargetBuffer::Builder*) nativeBuilder;
|
||||
builder->withTangents(enabled);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MorphTargetBuffer_nBuilderEnableCustomMorphing(JNIEnv*,
|
||||
jclass, jlong nativeBuilder, jboolean enabled) {
|
||||
MorphTargetBuffer::Builder* builder = (MorphTargetBuffer::Builder*) nativeBuilder;
|
||||
builder->enableCustomMorphing(enabled);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_MorphTargetBuffer_nBuilderBuild(JNIEnv*, jclass,
|
||||
@@ -112,3 +133,24 @@ Java_com_google_android_filament_MorphTargetBuffer_nGetCount(JNIEnv*, jclass,
|
||||
MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeObject;
|
||||
return (jint)morphTargetBuffer->getCount();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_MorphTargetBuffer_nHasPositions(JNIEnv*, jclass,
|
||||
jlong nativeObject) {
|
||||
MorphTargetBuffer* morphTargetBuffer = (MorphTargetBuffer*) nativeObject;
|
||||
return (jboolean) morphTargetBuffer->hasPositions();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_MorphTargetBuffer_nHasTangents(JNIEnv*, jclass,
|
||||
jlong nativeObject) {
|
||||
MorphTargetBuffer* morphTargetBuffer = (MorphTargetBuffer*) nativeObject;
|
||||
return (jboolean) morphTargetBuffer->hasTangents();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_MorphTargetBuffer_nIsCustomMorphingEnabled(JNIEnv*, jclass,
|
||||
jlong nativeObject) {
|
||||
MorphTargetBuffer* morphTargetBuffer = (MorphTargetBuffer*) nativeObject;
|
||||
return (jboolean) morphTargetBuffer->isCustomMorphingEnabled();
|
||||
}
|
||||
|
||||
@@ -366,6 +366,13 @@ Java_com_google_android_filament_RenderableManager_nSetPriority(JNIEnv*, jclass,
|
||||
rm->setPriority((RenderableManager::Instance) i, (uint8_t) priority);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_RenderableManager_nGetPriority(JNIEnv*, jclass,
|
||||
jlong nativeRenderableManager, jint i) {
|
||||
RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
|
||||
return (jint) rm->getPriority((RenderableManager::Instance) i);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_RenderableManager_nSetChannel(JNIEnv*, jclass,
|
||||
jlong nativeRenderableManager, jint i, jint channel) {
|
||||
@@ -373,6 +380,13 @@ Java_com_google_android_filament_RenderableManager_nSetChannel(JNIEnv*, jclass,
|
||||
rm->setChannel((RenderableManager::Instance) i, (uint8_t) channel);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_RenderableManager_nGetChannel(JNIEnv*, jclass,
|
||||
jlong nativeRenderableManager, jint i) {
|
||||
RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
|
||||
return (jint) rm->getChannel((RenderableManager::Instance) i);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_RenderableManager_nSetCulling(JNIEnv*, jclass,
|
||||
jlong nativeRenderableManager, jint i, jboolean enabled) {
|
||||
@@ -380,6 +394,13 @@ Java_com_google_android_filament_RenderableManager_nSetCulling(JNIEnv*, jclass,
|
||||
rm->setCulling((RenderableManager::Instance) i, enabled);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_RenderableManager_nIsCullingEnabled(JNIEnv*, jclass,
|
||||
jlong nativeRenderableManager, jint i) {
|
||||
RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
|
||||
return (jboolean) rm->isCullingEnabled((RenderableManager::Instance) i);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_RenderableManager_nSetFogEnabled(JNIEnv*, jclass,
|
||||
jlong nativeRenderableManager, jint i, jboolean enabled) {
|
||||
@@ -429,6 +450,13 @@ Java_com_google_android_filament_RenderableManager_nIsShadowReceiver(JNIEnv*, jc
|
||||
return (jboolean) rm->isShadowReceiver((RenderableManager::Instance) i);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_RenderableManager_nIsScreenSpaceContactShadowsEnabled(JNIEnv*, jclass,
|
||||
jlong nativeRenderableManager, jint i) {
|
||||
RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
|
||||
return (jboolean) rm->isScreenSpaceContactShadowsEnabled((RenderableManager::Instance) i);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_RenderableManager_nGetAxisAlignedBoundingBox(JNIEnv* env,
|
||||
jclass, jlong nativeRenderableManager, jint i, jfloatArray center_,
|
||||
@@ -500,6 +528,13 @@ Java_com_google_android_filament_RenderableManager_nSetBlendOrderAt(JNIEnv*, jcl
|
||||
(uint16_t) blendOrder);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_RenderableManager_nGetBlendOrderAt(JNIEnv*, jclass,
|
||||
jlong nativeRenderableManager, jint i, jint primitiveIndex) {
|
||||
RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
|
||||
return (jint) rm->getBlendOrderAt((RenderableManager::Instance) i, (size_t) primitiveIndex);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_RenderableManager_nSetGlobalBlendOrderEnabledAt(JNIEnv*, jclass,
|
||||
jlong nativeRenderableManager, jint i, jint primitiveIndex, jboolean enabled) {
|
||||
@@ -508,6 +543,13 @@ Java_com_google_android_filament_RenderableManager_nSetGlobalBlendOrderEnabledAt
|
||||
(bool) enabled);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_RenderableManager_nIsGlobalBlendOrderEnabledAt(JNIEnv*, jclass,
|
||||
jlong nativeRenderableManager, jint i, jint primitiveIndex) {
|
||||
RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
|
||||
return (jboolean) rm->isGlobalBlendOrderEnabledAt((RenderableManager::Instance) i, (size_t) primitiveIndex);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_RenderableManager_nGetEnabledAttributesAt(JNIEnv*, jclass,
|
||||
jlong nativeRenderableManager, jint i, jint primitiveIndex) {
|
||||
|
||||
@@ -208,3 +208,19 @@ Java_com_google_android_filament_Renderer_nSetVsyncTime(JNIEnv *, jclass,
|
||||
Renderer *renderer = (Renderer *) nativeRenderer;
|
||||
renderer->setVsyncTime(steadyClockTimeNano);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Renderer_nSkipNextFrames(JNIEnv *, jclass ,
|
||||
jlong nativeRenderer, jint frameCount) {
|
||||
Renderer *renderer = (Renderer *) nativeRenderer;
|
||||
renderer->skipNextFrames(frameCount);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_Renderer_nGetFrameToSkipCount(JNIEnv *, jclass ,
|
||||
jlong nativeRenderer) {
|
||||
Renderer *renderer = (Renderer *) nativeRenderer;
|
||||
return renderer->getFrameToSkipCount();
|
||||
}
|
||||
|
||||
@@ -63,6 +63,14 @@ Java_com_google_android_filament_Skybox_nBuilderColor(JNIEnv *, jclass,
|
||||
builder->color({r, g, b, a});
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Skybox_nBuilderPriority(JNIEnv *, jclass,
|
||||
jlong nativeSkyBoxBuilder, jint priority) {
|
||||
Skybox::Builder *builder = (Skybox::Builder *) nativeSkyBoxBuilder;
|
||||
builder->priority(uint8_t(priority));
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_Skybox_nBuilderBuild(JNIEnv *env, jclass type,
|
||||
jlong nativeSkyBoxBuilder, jlong nativeEngine) {
|
||||
|
||||
@@ -28,9 +28,8 @@ Java_com_google_android_filament_SwapChain_nSetFrameCompletedCallback(JNIEnv* en
|
||||
jlong nativeSwapChain, jobject handler, jobject runnable) {
|
||||
SwapChain* swapChain = (SwapChain*) nativeSwapChain;
|
||||
auto* callback = JniCallback::make(env, handler, runnable);
|
||||
swapChain->setFrameCompletedCallback(nullptr, [callback](SwapChain* swapChain) {
|
||||
JniCallback::postToJavaAndDestroy(callback);
|
||||
});
|
||||
swapChain->setFrameCompletedCallback(callback->getHandler(),
|
||||
[callback](SwapChain* swapChain) { JniCallback::postToJavaAndDestroy(callback); });
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
@@ -53,3 +52,22 @@ Java_com_google_android_filament_SwapChain_nIsProtectedContentSupported(
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
return (jboolean)SwapChain::isProtectedContentSupported(*engine);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_SwapChain_nSetFrameScheduledCallback(JNIEnv* env, jclass,
|
||||
jlong nativeSwapChain, jobject handler, jobject runnable) {
|
||||
SwapChain* swapChain = (SwapChain*) nativeSwapChain;
|
||||
auto* callback = JniCallback::make(env, handler, runnable);
|
||||
swapChain->setFrameScheduledCallback(callback->getHandler(),
|
||||
[callback](backend::PresentCallable) {
|
||||
// Ignore PresentCallable, which is only meaningful with the Metal backend.
|
||||
JniCallback::postToJavaAndDestroy(callback);
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_SwapChain_nIsFrameScheduledCallbackSet(
|
||||
JNIEnv *, jclass, jlong nativeSwapChain) {
|
||||
SwapChain* swapChain = (SwapChain*) nativeSwapChain;
|
||||
return (jboolean)swapChain->isFrameScheduledCallbackSet();
|
||||
}
|
||||
|
||||
@@ -21,6 +21,11 @@
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <android/bitmap.h>
|
||||
#include <android/hardware_buffer_jni.h>
|
||||
#include <backend/platforms/PlatformEGLAndroid.h>
|
||||
# if FILAMENT_SUPPORTS_VULKAN
|
||||
# include <backend/platforms/VulkanPlatformAndroid.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <filament/Engine.h>
|
||||
@@ -168,6 +173,13 @@ Java_com_google_android_filament_Texture_nBuilderSwizzle(JNIEnv *, jclass ,
|
||||
(Texture::Swizzle)r, (Texture::Swizzle)g, (Texture::Swizzle)b, (Texture::Swizzle)a);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Texture_nBuilderSamples(JNIEnv*, jclass,
|
||||
jlong nativeBuilder, jint samples) {
|
||||
Texture::Builder *builder = (Texture::Builder *) nativeBuilder;
|
||||
builder->samples((uint8_t) samples);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Texture_nBuilderImportTexture(JNIEnv*, jclass, jlong nativeBuilder, jlong id) {
|
||||
@@ -388,6 +400,57 @@ Java_com_google_android_filament_Texture_nSetExternalImage(JNIEnv*, jclass, jlon
|
||||
texture->setExternalImage(*engine, (void*)eglImage);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_Texture_nSetExternalImageByAHB(JNIEnv *env, jclass clazz,
|
||||
jlong nativeTexture, jlong nativeEngine, jobject ahb) {
|
||||
Texture *texture = (Texture *) nativeTexture;
|
||||
Engine *engine = (Engine *) nativeEngine;
|
||||
|
||||
#ifdef __ANDROID__
|
||||
Platform* platform = engine->getPlatform();
|
||||
AHardwareBuffer* nativeBuffer = nullptr;
|
||||
if (__builtin_available(android 26, *)) {
|
||||
nativeBuffer = AHardwareBuffer_fromHardwareBuffer(env, ahb);
|
||||
}
|
||||
if (!nativeBuffer) {
|
||||
// either we're not on Android 26, or ahb wasn't a AHardwareBuffer
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
if (engine->getBackend() == Backend::OPENGL) {
|
||||
// CAVEAT: we assume that Backend::OPENGL on Android implies PlatformEGLAndroid.
|
||||
#if UTILS_HAS_RTTI
|
||||
if (!dynamic_cast<PlatformEGLAndroid*>(platform)) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
#endif
|
||||
auto* eglPlatform = (PlatformEGLAndroid*) platform;
|
||||
auto ref = eglPlatform->createExternalImage(nativeBuffer, false);
|
||||
texture->setExternalImage(*engine, ref);
|
||||
}
|
||||
|
||||
#if FILAMENT_SUPPORTS_VULKAN
|
||||
else if (engine->getBackend() == Backend::VULKAN) {
|
||||
// CAVEAT: we assume that Backend::VULKAN on Android implies VulkanPlatformAndroid.
|
||||
#if UTILS_HAS_RTTI
|
||||
if (!dynamic_cast<VulkanPlatformAndroid*>(platform)) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
#endif
|
||||
auto* vulkanPlatform = (VulkanPlatformAndroid*) platform;
|
||||
auto ref = vulkanPlatform->createExternalImage(nativeBuffer, false);
|
||||
texture->setExternalImage(*engine, ref);
|
||||
}
|
||||
#endif // FILAMENT_SUPPORTS_VULKAN
|
||||
// success!
|
||||
return JNI_TRUE;
|
||||
#else
|
||||
// other platforms could come here
|
||||
return JNI_FALSE;
|
||||
#endif // __ANDROID__
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Texture_nSetExternalStream(JNIEnv*, jclass,
|
||||
jlong nativeTexture, jlong nativeEngine, jlong nativeStream) {
|
||||
@@ -607,3 +670,4 @@ Java_com_google_android_filament_android_TextureHelper_nSetBitmapWithCallback(JN
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -52,6 +52,11 @@ Java_com_google_android_filament_ToneMapper_nCreatePBRNeutralToneMapper(JNIEnv*,
|
||||
return (jlong) new PBRNeutralToneMapper();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_ToneMapper_nCreateGT7ToneMapper(JNIEnv*, jclass) {
|
||||
return (jlong) new GT7ToneMapper();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_ToneMapper_nCreateAgxToneMapper(JNIEnv*, jclass, jint look) {
|
||||
return (jlong) new AgxToneMapper(AgxToneMapper::AgxLook(look));
|
||||
|
||||
@@ -76,6 +76,12 @@ Java_com_google_android_filament_View_nSetVisibleLayers(JNIEnv*, jclass, jlong n
|
||||
view->setVisibleLayers((uint8_t) select, (uint8_t) value);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_View_nGetVisibleLayers(JNIEnv*, jclass, jlong nativeView) {
|
||||
View* view = (View*) nativeView;
|
||||
return view->getVisibleLayers();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_View_nSetShadowingEnabled(JNIEnv*, jclass, jlong nativeView, jboolean enabled) {
|
||||
View* view = (View*) nativeView;
|
||||
@@ -147,6 +153,15 @@ Java_com_google_android_filament_View_nSetDynamicResolutionOptions(JNIEnv*, jcla
|
||||
view->setDynamicResolutionOptions(options);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_View_nGetLastDynamicResolutionScale(JNIEnv *env, jclass, jlong nativeView, jfloatArray out_) {
|
||||
jfloat* out = env->GetFloatArrayElements(out_, nullptr);
|
||||
View *view = (View *) nativeView;
|
||||
math::float2 result = view->getLastDynamicResolutionScale();
|
||||
std::copy_n(result.v, 2, out);
|
||||
env->ReleaseFloatArrayElements(out_, out, 0);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_View_nSetShadowType(JNIEnv*, jclass, jlong nativeView, jint type) {
|
||||
View* view = (View*) nativeView;
|
||||
@@ -356,6 +371,7 @@ Java_com_google_android_filament_View_nSetFogOptions(JNIEnv *, jclass , jlong na
|
||||
view->setFogOptions(options);
|
||||
}
|
||||
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_View_nSetBlendMode(JNIEnv *, jclass , jlong nativeView, jint blendMode) {
|
||||
View* view = (View*) nativeView;
|
||||
@@ -430,6 +446,18 @@ Java_com_google_android_filament_View_nIsShadowingEnabled(JNIEnv *, jclass, jlon
|
||||
return (jboolean)view->isShadowingEnabled();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_View_nSetFrustumCullingEnabled(JNIEnv*, jclass, jlong nativeView, jboolean enabled) {
|
||||
View* view = (View*) nativeView;
|
||||
view->setFrustumCullingEnabled(enabled);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_View_nIsFrustumCullingEnabled(JNIEnv*, jclass, jlong nativeView) {
|
||||
View* view = (View*) nativeView;
|
||||
return (jboolean)view->isFrustumCullingEnabled();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_View_nSetScreenSpaceRefractionEnabled(JNIEnv *, jclass,
|
||||
@@ -554,3 +582,20 @@ Java_com_google_android_filament_View_nClearFrameHistory(JNIEnv *env, jclass cla
|
||||
Engine *engine = (Engine *) nativeEngine;
|
||||
view->clearFrameHistory(*engine);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_View_nSetChannelDepthClearEnabled(JNIEnv *, jclass ,
|
||||
jlong nativeView, jint channel, jboolean enabled) {
|
||||
View* view = (View*) nativeView;
|
||||
view->setChannelDepthClearEnabled((uint8_t) channel, (bool) enabled);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_View_nIsChannelDepthClearEnabled(JNIEnv *env, jclass clazz,
|
||||
jlong nativeView, jint channel) {
|
||||
// TODO: implement nIsChannelDepthClearEnabled()
|
||||
View* view = (View*) nativeView;
|
||||
return (jboolean)view->isChannelDepthClearEnabled((uint8_t) channel);
|
||||
}
|
||||
|
||||
@@ -99,6 +99,15 @@ final class Asserts {
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull @Size(min = 2)
|
||||
static float[] assertFloat2(@Nullable float[] out) {
|
||||
if (out == null) out = new float[2];
|
||||
else if (out.length < 2) {
|
||||
throw new ArrayIndexOutOfBoundsException("Array length must be at least 2");
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@NonNull @Size(min = 4)
|
||||
static float[] assertFloat4(@Nullable float[] out) {
|
||||
if (out == null) out = new float[4];
|
||||
@@ -127,4 +136,13 @@ final class Asserts {
|
||||
throw new ArrayIndexOutOfBoundsException("Array length must be at least 4");
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull @Size(min = 2)
|
||||
static double[] assertDouble2(@Nullable double[] out) {
|
||||
if (out == null) out = new double[2];
|
||||
else if (out.length < 2) {
|
||||
throw new ArrayIndexOutOfBoundsException("Array length must be at least 2");
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,6 +343,27 @@ public class Camera {
|
||||
nSetScaling(getNativeObject(), xscaling, yscaling);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a custom projection matrix for each eye.
|
||||
*
|
||||
* @param inProjection An array of projection matrices, one for each eye.
|
||||
* Must have at least 16 * count elements.
|
||||
* @param count Number of eyes to set.
|
||||
* @param inProjectionForCulling Custom projection matrix for culling, must encompass all eyes.
|
||||
* @param near Distance to the near plane.
|
||||
* @param far Distance to the far plane.
|
||||
*/
|
||||
public void setCustomEyeProjection(
|
||||
@NonNull double[] inProjection, int count,
|
||||
@NonNull @Size(min = 16) double[] inProjectionForCulling,
|
||||
double near, double far) {
|
||||
Asserts.assertMat4dIn(inProjectionForCulling);
|
||||
if (inProjection.length < 16 * count) {
|
||||
throw new IllegalArgumentException("inProjection array too small for the given count");
|
||||
}
|
||||
nSetCustomEyeProjection(getNativeObject(), inProjection, count, inProjectionForCulling, near, far);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an additional matrix that scales the projection matrix.
|
||||
*
|
||||
@@ -399,6 +420,31 @@ public class Camera {
|
||||
nSetShift(getNativeObject(), xshift, yshift);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shift amount used to translate the projection matrix.
|
||||
*
|
||||
* @param out A 2-double array where the shift will be stored, or null.
|
||||
* @return A 2-double array containing the x and y shift.
|
||||
*/
|
||||
@NonNull @Size(min = 2)
|
||||
public double[] getShift(@Nullable @Size(min = 2) double[] out) {
|
||||
out = Asserts.assertDouble2(out);
|
||||
nGetShift(getNativeObject(), out);
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the camera's field of view in degrees.
|
||||
*
|
||||
* @param direction The direction of the FOV (VERTICAL or HORIZONTAL).
|
||||
* @return The field of view in degrees.
|
||||
*/
|
||||
public double getFieldOfViewInDegrees(@NonNull Fov direction) {
|
||||
return nGetFieldOfViewInDegrees(getNativeObject(), direction.ordinal());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sets the camera's model matrix.
|
||||
* <p>
|
||||
@@ -745,6 +791,17 @@ public class Camera {
|
||||
return mEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the model matrix for a specific eye.
|
||||
*
|
||||
* @param eyeId The index of the eye.
|
||||
* @param model The model matrix for the eye.
|
||||
*/
|
||||
public void setEyeModelMatrix(int eyeId, @NonNull @Size(min = 16) double[] model) {
|
||||
Asserts.assertMat4dIn(model);
|
||||
nSetEyeModelMatrix(getNativeObject(), eyeId, model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to compute the effective focal length taking into account the focus distance
|
||||
*
|
||||
@@ -784,8 +841,13 @@ public class Camera {
|
||||
private static native void nSetCustomProjection(long nativeCamera, double[] inProjection, double[] inProjectionForCulling, double near, double far);
|
||||
private static native void nSetScaling(long nativeCamera, double x, double y);
|
||||
private static native void nSetShift(long nativeCamera, double x, double y);
|
||||
private static native void nGetShift(long nativeCamera, double[] out);
|
||||
private static native void nSetModelMatrix(long nativeCamera, float[] in);
|
||||
private static native void nSetModelMatrixFp64(long nativeCamera, double[] in);
|
||||
private static native void nSetEyeModelMatrix(long nativeCamera, int eyeId, double[] model);
|
||||
private static native void nSetCustomEyeProjection(long nativeCamera, double[] inProjection, int count, double[] inProjectionForCulling, double near, double far);
|
||||
private static native double nGetFieldOfViewInDegrees(long nativeCamera, int direction);
|
||||
|
||||
private static native void nLookAt(long nativeCamera, double eyeX, double eyeY, double eyeZ, double centerX, double centerY, double centerZ, double upX, double upY, double upZ);
|
||||
private static native double nGetNear(long nativeCamera);
|
||||
private static native double nGetCullingFar(long nativeCamera);
|
||||
|
||||
@@ -264,7 +264,8 @@ public class Engine {
|
||||
config.disableHandleUseAfterFreeCheck,
|
||||
config.preferredShaderLanguage.ordinal(),
|
||||
config.forceGLES2Context, config.assertNativeWindowIsValid,
|
||||
config.gpuContextPriority.ordinal());
|
||||
config.gpuContextPriority.ordinal(),
|
||||
config.sharedUboInitialSizeInBytes);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -525,6 +526,16 @@ public class Engine {
|
||||
* GPU context priority level. Controls GPU work scheduling and preemption.
|
||||
*/
|
||||
public GpuContextPriority gpuContextPriority = GpuContextPriority.DEFAULT;
|
||||
|
||||
/**
|
||||
* The initial size in bytes of the shared uniform buffer used for material instance batching.
|
||||
*
|
||||
* If the buffer runs out of space during a frame, it will be automatically reallocated
|
||||
* with a larger capacity. Setting an appropriate initial size can help avoid runtime
|
||||
* reallocations, which can cause a minor performance stutter, at the cost of higher
|
||||
* initial memory usage.
|
||||
*/
|
||||
public long sharedUboInitialSizeInBytes = 256 * 64;
|
||||
}
|
||||
|
||||
private Engine(long nativeEngine, Config config) {
|
||||
@@ -928,6 +939,15 @@ public class Engine {
|
||||
return nIsValidSkinningBuffer(getNativeObject(), object.getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the object is valid.
|
||||
* @param object Object to check for validity
|
||||
* @return returns true if the specified object is valid.
|
||||
*/
|
||||
public boolean isValidMorphTargetBuffer(@NonNull MorphTargetBuffer object) {
|
||||
return nIsValidMorphTargetBuffer(getNativeObject(), object.getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the object is valid.
|
||||
* @param object Object to check for validity
|
||||
@@ -1181,6 +1201,15 @@ public class Engine {
|
||||
skinningBuffer.clearNativeObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys a {@link MorphTargetBuffer} and frees all its associated resources.
|
||||
* @param morphTargetBuffer the {@link MorphTargetBuffer} to destroy
|
||||
*/
|
||||
public void destroyMorphTargetBuffer(@NonNull MorphTargetBuffer morphTargetBuffer) {
|
||||
assertDestroy(nDestroyMorphTargetBuffer(getNativeObject(), morphTargetBuffer.getNativeObject()));
|
||||
morphTargetBuffer.clearNativeObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys a {@link IndirectLight} and frees all its associated resources.
|
||||
* @param ibl the {@link IndirectLight} to destroy
|
||||
@@ -1472,6 +1501,7 @@ public class Engine {
|
||||
private static native boolean nDestroyIndexBuffer(long nativeEngine, long nativeIndexBuffer);
|
||||
private static native boolean nDestroyVertexBuffer(long nativeEngine, long nativeVertexBuffer);
|
||||
private static native boolean nDestroySkinningBuffer(long nativeEngine, long nativeSkinningBuffer);
|
||||
private static native boolean nDestroyMorphTargetBuffer(long nativeEngine, long nativeMorphTargetBuffer);
|
||||
private static native boolean nDestroyIndirectLight(long nativeEngine, long nativeIndirectLight);
|
||||
private static native boolean nDestroyMaterial(long nativeEngine, long nativeMaterial);
|
||||
private static native boolean nDestroyMaterialInstance(long nativeEngine, long nativeMaterialInstance);
|
||||
@@ -1488,6 +1518,7 @@ public class Engine {
|
||||
private static native boolean nIsValidIndexBuffer(long nativeEngine, long nativeIndexBuffer);
|
||||
private static native boolean nIsValidVertexBuffer(long nativeEngine, long nativeVertexBuffer);
|
||||
private static native boolean nIsValidSkinningBuffer(long nativeEngine, long nativeSkinningBuffer);
|
||||
private static native boolean nIsValidMorphTargetBuffer(long nativeEngine, long nativeMorphTargetBuffer);
|
||||
private static native boolean nIsValidIndirectLight(long nativeEngine, long nativeIndirectLight);
|
||||
private static native boolean nIsValidMaterial(long nativeEngine, long nativeMaterial);
|
||||
private static native boolean nIsValidMaterialInstance(long nativeEngine, long nativeMaterial, long nativeMaterialInstance);
|
||||
@@ -1529,7 +1560,8 @@ public class Engine {
|
||||
boolean disableHandleUseAfterFreeCheck,
|
||||
int preferredShaderLanguage,
|
||||
boolean forceGLES2Context, boolean assertNativeWindowIsValid,
|
||||
int gpuContextPriority);
|
||||
int gpuContextPriority,
|
||||
long sharedUboInitialSizeInBytes);
|
||||
private static native void nSetBuilderFeatureLevel(long nativeBuilder, int ordinal);
|
||||
private static native void nSetBuilderSharedContext(long nativeBuilder, long sharedContext);
|
||||
private static native void nSetBuilderPaused(long nativeBuilder, boolean paused);
|
||||
|
||||
@@ -54,6 +54,7 @@ public class Material {
|
||||
static final CullingMode[] sCullingModeValues = CullingMode.values();
|
||||
static final VertexBuffer.VertexAttribute[] sVertexAttributeValues =
|
||||
VertexBuffer.VertexAttribute.values();
|
||||
static final TransparencyMode[] sTransparencyModeValues = TransparencyMode.values();
|
||||
}
|
||||
|
||||
private long mNativeObject;
|
||||
@@ -160,6 +161,31 @@ public class Material {
|
||||
SCREEN,
|
||||
}
|
||||
|
||||
/**
|
||||
* How transparent objects are handled
|
||||
*
|
||||
* @see
|
||||
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/blendingandtransparency:transparencymode">
|
||||
* Blending and transparency: transparencyMode</a>
|
||||
*/
|
||||
public enum TransparencyMode {
|
||||
/** The transparent object is drawn honoring the raster state. */
|
||||
DEFAULT,
|
||||
|
||||
/**
|
||||
* The transparent object is first drawn in the depth buffer,
|
||||
* then in the color buffer, honoring the culling mode, but ignoring the depth test function.
|
||||
*/
|
||||
TWO_PASSES_ONE_SIDE,
|
||||
|
||||
/**
|
||||
* The transparent object is drawn twice in the color buffer,
|
||||
* first with back faces only, then with front faces; the culling
|
||||
* mode is ignored. Can be combined with two-sided lighting.
|
||||
*/
|
||||
TWO_PASSES_TWO_SIDES
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported refraction modes
|
||||
*
|
||||
@@ -261,6 +287,20 @@ public class Material {
|
||||
LOW
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines whether a material instance should use UBO batching or not.
|
||||
*/
|
||||
public enum UboBatchingMode {
|
||||
/**
|
||||
* For default, it follows the engine settings.
|
||||
* If UBO batching is enabled on the engine and the material domain is SURFACE, it
|
||||
* turns on the UBO batching. Otherwise, it turns off the UBO batching.
|
||||
*/
|
||||
DEFAULT,
|
||||
/** Disable the Ubo Batching for this material */
|
||||
DISABLED
|
||||
}
|
||||
|
||||
public static class UserVariantFilterBit {
|
||||
/** Directional lighting */
|
||||
public static int DIRECTIONAL_LIGHTING = 0x01;
|
||||
@@ -372,6 +412,7 @@ public class Material {
|
||||
private int mSize;
|
||||
private int mShBandCount = 0;
|
||||
private ShadowSamplingQuality mShadowSamplingQuality = ShadowSamplingQuality.LOW;
|
||||
private UboBatchingMode mUboBatchingMode = UboBatchingMode.DEFAULT;
|
||||
|
||||
|
||||
/**
|
||||
@@ -416,6 +457,17 @@ public class Material {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the batching mode of the instances created from this material.
|
||||
* @param uboBatchingMode
|
||||
* @return Reference to this Builder for chaining calls.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder uboBatching(UboBatchingMode mode) {
|
||||
mUboBatchingMode = mode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns the Material object.
|
||||
*
|
||||
@@ -428,7 +480,7 @@ public class Material {
|
||||
@NonNull
|
||||
public Material build(@NonNull Engine engine) {
|
||||
long nativeMaterial = nBuilderBuild(engine.getNativeObject(),
|
||||
mBuffer, mSize, mShBandCount, mShadowSamplingQuality.ordinal());
|
||||
mBuffer, mSize, mShBandCount, mShadowSamplingQuality.ordinal(), mUboBatchingMode.ordinal());
|
||||
if (nativeMaterial == 0) throw new IllegalStateException("Couldn't create Material");
|
||||
return new Material(nativeMaterial);
|
||||
}
|
||||
@@ -561,6 +613,18 @@ public class Material {
|
||||
return EnumCache.sBlendingModeValues[nGetBlendingMode(getNativeObject())];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the transparency mode of this material.
|
||||
* This value only makes sense when the blending mode is transparent or fade.
|
||||
*
|
||||
* @see
|
||||
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/blendingandtransparency:transparencymode">
|
||||
* Blending and transparency: transparencyMode</a>
|
||||
*/
|
||||
public TransparencyMode getTransparencyMode() {
|
||||
return EnumCache.sTransparencyModeValues[nGetTransparencyMode(getNativeObject())];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the refraction mode of this material.
|
||||
*
|
||||
@@ -787,6 +851,21 @@ public class Material {
|
||||
return nHasParameter(getNativeObject(), name);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns the name of the transform parameter associated with the given sampler parameter.
|
||||
* In the case the parameter doesn't have a transform name field, it will return an empty string.
|
||||
*
|
||||
* @param samplerName the name of the sampler parameter to query.
|
||||
*
|
||||
* @see
|
||||
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/general:parameters">
|
||||
* General: parameters</a>
|
||||
*/
|
||||
public String getParameterTransformName(@NonNull String samplerName) {
|
||||
return nGetParameterTransformName(getNativeObject(), samplerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of a bool parameter on this material's default instance.
|
||||
*
|
||||
@@ -1079,7 +1158,7 @@ public class Material {
|
||||
mNativeObject = 0;
|
||||
}
|
||||
|
||||
private static native long nBuilderBuild(long nativeEngine, @NonNull Buffer buffer, int size, int shBandCount, int shadowQuality);
|
||||
private static native long nBuilderBuild(long nativeEngine, @NonNull Buffer buffer, int size, int shBandCount, int shadowQuality, int uboBatchingMode);
|
||||
private static native long nCreateInstance(long nativeMaterial);
|
||||
private static native long nCreateInstanceWithName(long nativeMaterial, @NonNull String name);
|
||||
private static native long nGetDefaultInstance(long nativeMaterial);
|
||||
@@ -1089,6 +1168,7 @@ public class Material {
|
||||
private static native int nGetShading(long nativeMaterial);
|
||||
private static native int nGetInterpolation(long nativeMaterial);
|
||||
private static native int nGetBlendingMode(long nativeMaterial);
|
||||
private static native int nGetTransparencyMode(long nativeMaterial);
|
||||
private static native int nGetVertexDomain(long nativeMaterial);
|
||||
private static native int nGetCullingMode(long nativeMaterial);
|
||||
private static native boolean nIsColorWriteEnabled(long nativeMaterial);
|
||||
@@ -1111,4 +1191,5 @@ public class Material {
|
||||
private static native int nGetRequiredAttributes(long nativeMaterial);
|
||||
|
||||
private static native boolean nHasParameter(long nativeMaterial, @NonNull String name);
|
||||
private static native String nGetParameterTransformName(long nativeMaterial, @NonNull String samplerName);
|
||||
}
|
||||
|
||||
@@ -537,6 +537,14 @@ public class MaterialInstance {
|
||||
nSetDoubleSided(getNativeObject(), doubleSided);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the transparency mode for this material instance.
|
||||
* @see Material.TransparencyMode
|
||||
*/
|
||||
public void setTransparencyMode(@NonNull Material.TransparencyMode mode) {
|
||||
nSetTransparencyMode(getNativeObject(), mode.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether double-sided lighting is enabled when the parent Material has double-sided
|
||||
* capability.
|
||||
@@ -545,6 +553,14 @@ public class MaterialInstance {
|
||||
return nIsDoubleSided(getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the transparency mode.
|
||||
*/
|
||||
@NonNull
|
||||
public Material.TransparencyMode getTransparencyMode() {
|
||||
return Material.EnumCache.sTransparencyModeValues[nGetTransparencyMode(getNativeObject())];
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the default triangle culling state that was set on the material.
|
||||
*
|
||||
@@ -982,4 +998,6 @@ public class MaterialInstance {
|
||||
private static native boolean nIsStencilWriteEnabled(long nativeMaterialInstance);
|
||||
private static native boolean nIsDepthCullingEnabled(long nativeMaterialInstance);
|
||||
private static native int nGetDepthFunc(long nativeMaterialInstance);
|
||||
private static native void nSetTransparencyMode(long nativeMaterialInstance, int mode);
|
||||
private static native int nGetTransparencyMode(long nativeMaterialInstance);
|
||||
}
|
||||
|
||||
@@ -64,6 +64,45 @@ public class MorphTargetBuffer {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to enable or disable the built-in position morphing buffer.
|
||||
* Default is true.
|
||||
*
|
||||
* @param enabled true to enable, false to disable
|
||||
* @return this <code>Builder</code> object for chaining calls
|
||||
*/
|
||||
@NonNull
|
||||
public Builder withPositions(boolean enabled) {
|
||||
nBuilderWithPositions(mNativeBuilder, enabled);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to enable or disable the built-in tangent morphing buffer.
|
||||
* Default is true.
|
||||
*
|
||||
* @param enabled true to enable, false to disable
|
||||
* @return this <code>Builder</code> object for chaining calls
|
||||
*/
|
||||
@NonNull
|
||||
public Builder withTangents(boolean enabled) {
|
||||
nBuilderWithTangents(mNativeBuilder, enabled);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to enable or disable custom morphing.
|
||||
* Default is false.
|
||||
*
|
||||
* @param enabled true to enable, false to disable
|
||||
* @return this <code>Builder</code> object for chaining calls
|
||||
*/
|
||||
@NonNull
|
||||
public Builder enableCustomMorphing(boolean enabled) {
|
||||
nBuilderEnableCustomMorphing(mNativeBuilder, enabled);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns the <code>MorphTargetBuffer</code> object.
|
||||
*
|
||||
@@ -156,6 +195,27 @@ public class MorphTargetBuffer {
|
||||
return nGetCount(mNativeObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this MorphTargetBuffer has a position buffer.
|
||||
*/
|
||||
public boolean hasPositions() {
|
||||
return nHasPositions(mNativeObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this MorphTargetBuffer has a tangent buffer.
|
||||
*/
|
||||
public boolean hasTangents() {
|
||||
return nHasTangents(mNativeObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if custom morphing is enabled.
|
||||
*/
|
||||
public boolean isCustomMorphingEnabled() {
|
||||
return nIsCustomMorphingEnabled(mNativeObject);
|
||||
}
|
||||
|
||||
public long getNativeObject() {
|
||||
if (mNativeObject == 0) {
|
||||
throw new IllegalStateException("Calling method on destroyed MorphTargetBuffer");
|
||||
@@ -171,10 +231,16 @@ public class MorphTargetBuffer {
|
||||
private static native void nDestroyBuilder(long nativeBuilder);
|
||||
private static native void nBuilderVertexCount(long nativeBuilder, int vertexCount);
|
||||
private static native void nBuilderCount(long nativeBuilder, int count);
|
||||
private static native void nBuilderWithPositions(long nativeBuilder, boolean enabled);
|
||||
private static native void nBuilderWithTangents(long nativeBuilder, boolean enabled);
|
||||
private static native void nBuilderEnableCustomMorphing(long nativeBuilder, boolean enabled);
|
||||
private static native long nBuilderBuild(long nativeBuilder, long nativeEngine);
|
||||
|
||||
private static native int nSetPositionsAt(long nativeObject, long nativeEngine, int targetIndex, float[] positions, int count);
|
||||
private static native int nSetTangentsAt(long nativeObject, long nativeEngine, int targetIndex, short[] tangents, int count);
|
||||
private static native int nGetVertexCount(long nativeObject);
|
||||
private static native int nGetCount(long nativeObject);
|
||||
private static native boolean nHasPositions(long nativeObject);
|
||||
private static native boolean nHasTangents(long nativeObject);
|
||||
private static native boolean nIsCustomMorphingEnabled(long nativeObject);
|
||||
}
|
||||
|
||||
@@ -332,7 +332,7 @@ public class RenderableManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the channel this renderable is associated to. There can be 4 channels.
|
||||
* Set the channel this renderable is associated to. There can be 8 channels.
|
||||
*
|
||||
* <p>All renderables in a given channel are rendered together, regardless of anything else.
|
||||
* They are sorted as usual within a channel.</p>
|
||||
@@ -342,16 +342,16 @@ public class RenderableManager {
|
||||
* <p>Channels 0 and 1 may not have render primitives using a material with `refractionType`
|
||||
* set to `screenspace`.</p>
|
||||
*
|
||||
* @param channel clamped to the range [0..3], defaults to 2.
|
||||
* @param channel clamped to the range [0..7], defaults to 2.
|
||||
*
|
||||
* @return Builder reference for chaining calls.
|
||||
*
|
||||
* @see Builder::blendOrder()
|
||||
* @see Builder::priority()
|
||||
* @see Builder#blendOrder()
|
||||
* @see Builder#priority()
|
||||
* @see RenderableManager::setBlendOrderAt()
|
||||
*/
|
||||
@NonNull
|
||||
public Builder channel(@IntRange(from = 0, to = 3) int channel) {
|
||||
public Builder channel(@IntRange(from = 0, to = 7) int channel) {
|
||||
nBuilderChannel(mNativeBuilder, channel);
|
||||
return this;
|
||||
}
|
||||
@@ -725,15 +725,23 @@ public class RenderableManager {
|
||||
nSetPriority(mNativeObject, i, priority);
|
||||
}
|
||||
|
||||
public int getPriority(@EntityInstance int i) {
|
||||
return nGetPriority(mNativeObject, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the channel of a renderable
|
||||
*
|
||||
* @see Builder#channel
|
||||
*/
|
||||
public void setChannel(@EntityInstance int i, @IntRange(from = 0, to = 3) int channel) {
|
||||
public void setChannel(@EntityInstance int i, @IntRange(from = 0, to = 7) int channel) {
|
||||
nSetChannel(mNativeObject, i, channel);
|
||||
}
|
||||
|
||||
public int getChannel(@EntityInstance int i) {
|
||||
return nGetChannel(mNativeObject, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes whether or not frustum culling is on.
|
||||
*
|
||||
@@ -743,6 +751,10 @@ public class RenderableManager {
|
||||
nSetCulling(mNativeObject, i, enabled);
|
||||
}
|
||||
|
||||
public boolean isCullingEnabled(@EntityInstance int i) {
|
||||
return nIsCullingEnabled(mNativeObject, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes whether or not the large-scale fog is applied to this renderable
|
||||
* @see Builder#fog
|
||||
@@ -812,6 +824,10 @@ public class RenderableManager {
|
||||
nSetScreenSpaceContactShadows(mNativeObject, i, enabled);
|
||||
}
|
||||
|
||||
public boolean isScreenSpaceContactShadowsEnabled(@EntityInstance int i) {
|
||||
return nIsScreenSpaceContactShadowsEnabled(mNativeObject, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the renderable can cast shadows.
|
||||
*
|
||||
@@ -932,6 +948,10 @@ public class RenderableManager {
|
||||
nSetBlendOrderAt(mNativeObject, instance, primitiveIndex, blendOrder);
|
||||
}
|
||||
|
||||
public int getBlendOrderAt(@EntityInstance int instance, @IntRange(from = 0) int primitiveIndex) {
|
||||
return nGetBlendOrderAt(mNativeObject, instance, primitiveIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes whether the blend order is global or local to this Renderable (by default).
|
||||
*
|
||||
@@ -946,6 +966,10 @@ public class RenderableManager {
|
||||
nSetGlobalBlendOrderEnabledAt(mNativeObject, instance, primitiveIndex, enabled);
|
||||
}
|
||||
|
||||
public boolean isGlobalBlendOrderEnabledAt(@EntityInstance int instance, @IntRange(from = 0) int primitiveIndex) {
|
||||
return nIsGlobalBlendOrderEnabledAt(mNativeObject, instance, primitiveIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the set of enabled attribute slots in the given primitive's VertexBuffer.
|
||||
*/
|
||||
@@ -1013,8 +1037,11 @@ public class RenderableManager {
|
||||
private static native void nSetAxisAlignedBoundingBox(long nativeRenderableManager, int i, float cx, float cy, float cz, float ex, float ey, float ez);
|
||||
private static native void nSetLayerMask(long nativeRenderableManager, int i, int select, int value);
|
||||
private static native void nSetPriority(long nativeRenderableManager, int i, int priority);
|
||||
private static native int nGetPriority(long nativeRenderableManager, int i);
|
||||
private static native void nSetChannel(long nativeRenderableManager, int i, int channel);
|
||||
private static native int nGetChannel(long nativeRenderableManager, int i);
|
||||
private static native void nSetCulling(long nativeRenderableManager, int i, boolean enabled);
|
||||
private static native boolean nIsCullingEnabled(long nativeRenderableManager, int i);
|
||||
private static native void nSetFogEnabled(long nativeRenderableManager, int i, boolean enabled);
|
||||
private static native boolean nGetFogEnabled(long nativeRenderableManager, int i);
|
||||
private static native void nSetLightChannel(long nativeRenderableManager, int i, int channel, boolean enable);
|
||||
@@ -1022,6 +1049,7 @@ public class RenderableManager {
|
||||
private static native void nSetCastShadows(long nativeRenderableManager, int i, boolean enabled);
|
||||
private static native void nSetReceiveShadows(long nativeRenderableManager, int i, boolean enabled);
|
||||
private static native void nSetScreenSpaceContactShadows(long nativeRenderableManager, int i, boolean enabled);
|
||||
private static native boolean nIsScreenSpaceContactShadowsEnabled(long nativeRenderableManager, int i);
|
||||
private static native boolean nIsShadowCaster(long nativeRenderableManager, int i);
|
||||
private static native boolean nIsShadowReceiver(long nativeRenderableManager, int i);
|
||||
private static native void nGetAxisAlignedBoundingBox(long nativeRenderableManager, int i, float[] center, float[] halfExtent);
|
||||
@@ -1032,6 +1060,8 @@ public class RenderableManager {
|
||||
private static native long nGetMaterialInstanceAt(long nativeRenderableManager, int i, int primitiveIndex);
|
||||
private static native void nSetGeometryAt(long nativeRenderableManager, int i, int primitiveIndex, int primitiveType, long nativeVertexBuffer, long nativeIndexBuffer, int offset, int count);
|
||||
private static native void nSetBlendOrderAt(long nativeRenderableManager, int i, int primitiveIndex, int blendOrder);
|
||||
private static native int nGetBlendOrderAt(long nativeRenderableManager, int i, int primitiveIndex);
|
||||
private static native void nSetGlobalBlendOrderEnabledAt(long nativeRenderableManager, int i, int primitiveIndex, boolean enabled);
|
||||
private static native boolean nIsGlobalBlendOrderEnabledAt(long nativeRenderableManager, int i, int primitiveIndex);
|
||||
private static native int nGetEnabledAttributesAt(long nativeRenderableManager, int i, int primitiveIndex);
|
||||
}
|
||||
|
||||
@@ -732,6 +732,22 @@ public class Renderer {
|
||||
nResetUserTime(getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests the next frameCount frames to be skipped. For Debugging.
|
||||
* @param frameCount number of frames to skip.
|
||||
*/
|
||||
public void skipNextFrames(int frameCount) {
|
||||
nSkipNextFrames(getNativeObject(), frameCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remainder count of frame to be skipped
|
||||
* @return remaining frames to be skipped
|
||||
*/
|
||||
public int getFrameToSkipCount() {
|
||||
return nGetFrameToSkipCount(getNativeObject());
|
||||
}
|
||||
|
||||
public long getNativeObject() {
|
||||
if (mNativeObject == 0) {
|
||||
throw new IllegalStateException("Calling method on destroyed Renderer");
|
||||
@@ -773,4 +789,7 @@ public class Renderer {
|
||||
float interval, float headRoomRatio, float scaleRate, int history);
|
||||
private static native void nSetClearOptions(long nativeRenderer,
|
||||
float r, float g, float b, float a, boolean clear, boolean discard);
|
||||
|
||||
private static native void nSkipNextFrames(long nativeObject, int frameCount);
|
||||
private static native int nGetFrameToSkipCount(long nativeObject);
|
||||
}
|
||||
|
||||
@@ -155,6 +155,24 @@ public class Skybox {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the rendering priority of the Skybox. By default, it is set to the lowest
|
||||
* priority (7) such that the Skybox is always rendered after the opaque objects,
|
||||
* to reduce overdraw when depth culling is enabled.
|
||||
*
|
||||
* @param priority clamped to the range [0..7], defaults to 4; 7 is lowest priority
|
||||
* (rendered last).
|
||||
*
|
||||
* @return Builder reference for chaining calls.
|
||||
*
|
||||
* @see RenderableManager.Builder#priority
|
||||
*/
|
||||
@NonNull
|
||||
public Builder priority(@IntRange(from = 0, to = 7) int priority) {
|
||||
nBuilderPriority(mNativeBuilder, priority);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <code>Skybox</code> object
|
||||
*
|
||||
@@ -262,6 +280,7 @@ public class Skybox {
|
||||
private static native void nBuilderShowSun(long nativeSkyboxBuilder, boolean show);
|
||||
private static native void nBuilderIntensity(long nativeSkyboxBuilder, float intensity);
|
||||
private static native void nBuilderColor(long nativeSkyboxBuilder, float r, float g, float b, float a);
|
||||
private static native void nBuilderPriority(long nativeSkyboxBuilder, int priority);
|
||||
private static native long nBuilderBuild(long nativeSkyboxBuilder, long nativeEngine);
|
||||
private static native void nSetLayerMask(long nativeSkybox, int select, int value);
|
||||
private static native int nGetLayerMask(long nativeSkybox);
|
||||
|
||||
@@ -139,6 +139,78 @@ public class SwapChain {
|
||||
nSetFrameCompletedCallback(getNativeObject(), handler, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* FrameScheduledCallback is a callback function that notifies an application about the status
|
||||
* of a frame after Filament has finished its processing.
|
||||
*
|
||||
* <p>
|
||||
* The exact timing and semantics of this callback differ depending on the graphics backend in
|
||||
* use.
|
||||
* </p>
|
||||
*
|
||||
* <h3>Metal Backend</h3>
|
||||
* <p>
|
||||
* With the Metal backend, this callback signifies that Filament has completed all CPU-side
|
||||
* processing for a frame and the frame is ready to be scheduled for presentation.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Typically, Filament is responsible for scheduling the frame's presentation to the SwapChain.
|
||||
* If a FrameScheduledCallback is set, however, the application bears the responsibility of
|
||||
* scheduling the frame for presentation by calling the PresentCallable passed to the callback
|
||||
* function. In this mode, Filament will not automatically schedule the frame for presentation.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* When using the Metal backend, if your application delays the call to the PresentCallable
|
||||
* (e.g., by invoking it on a separate thread), you must ensure all PresentCallables have been
|
||||
* called before shutting down the Filament Engine. You can guarantee this by calling
|
||||
* Engine.flushAndWait() before Engine.shutdown(). This is necessary to ensure the Engine has
|
||||
* a chance to clean up all memory related to frame presentation.
|
||||
* </p>
|
||||
*
|
||||
* <h3>Other Backends (OpenGL, Vulkan, WebGPU)</h3>
|
||||
* <p>
|
||||
* On other backends, this callback serves as a notification that Filament has completed all
|
||||
* CPU-side processing for a frame. Filament proceeds with its normal presentation logic
|
||||
* automatically, and the PresentCallable passed to the callback is a no-op that can be safely
|
||||
* ignored.
|
||||
* </p>
|
||||
*
|
||||
* <h3>General Behavior</h3>
|
||||
* <p>
|
||||
* A FrameScheduledCallback can be set on an individual SwapChain through
|
||||
* setFrameScheduledCallback. Each SwapChain can have only one callback set per frame. If
|
||||
* setFrameScheduledCallback is called multiple times on the same SwapChain before
|
||||
* Renderer.endFrame(), the most recent call effectively overwrites any previously set callback.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The callback set by setFrameScheduledCallback is "latched" when Renderer.endFrame() is
|
||||
* executed. At this point, the callback is fixed for the frame that was just encoded.
|
||||
* Subsequent calls to setFrameScheduledCallback after endFrame() will apply to the next frame.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Use setFrameScheduledCallback() (with default arguments) to unset the callback.
|
||||
* </p>
|
||||
*
|
||||
* @param handler A {@link java.util.concurrent.Executor Executor}.
|
||||
* @param callback The Runnable callback to invoke when frame processing is complete.
|
||||
*/
|
||||
public void setFrameScheduledCallback(@NonNull Object handler, @NonNull Runnable callback) {
|
||||
nSetFrameScheduledCallback(getNativeObject(), handler, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this SwapChain currently has a FrameScheduledCallback set.
|
||||
*
|
||||
* @return true, if the last call to setFrameScheduledCallback set a callback
|
||||
*/
|
||||
public boolean isFrameScheduledCallbackSet() {
|
||||
return nIsFrameScheduledCallbackSet(getNativeObject());
|
||||
}
|
||||
|
||||
public long getNativeObject() {
|
||||
if (mNativeObject == 0) {
|
||||
throw new IllegalStateException("Calling method on destroyed SwapChain");
|
||||
@@ -151,6 +223,8 @@ public class SwapChain {
|
||||
}
|
||||
|
||||
private static native void nSetFrameCompletedCallback(long nativeSwapChain, Object handler, Runnable callback);
|
||||
private static native void nSetFrameScheduledCallback(long nativeSwapChain, Object handler, Runnable callback);
|
||||
private static native boolean nIsFrameScheduledCallbackSet(long nativeSwapChain);
|
||||
private static native boolean nIsSRGBSwapChainSupported(long nativeEngine);
|
||||
private static native boolean nIsMSAASwapChainSupported(long nativeEngine, int samples);
|
||||
private static native boolean nIsProtectedContentSupported(long nativeEngine);
|
||||
|
||||
@@ -71,6 +71,16 @@ import static com.google.android.filament.Texture.Type.COMPRESSED;
|
||||
* @see MaterialInstance#setParameter(String, Texture, TextureSampler)
|
||||
*/
|
||||
public class Texture {
|
||||
|
||||
private static Class<?> HardwareBufferClass = null;
|
||||
|
||||
static {
|
||||
try {
|
||||
HardwareBufferClass = Class.forName("android.hardware.HardwareBuffer");
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private static final Sampler[] sSamplerValues = Sampler.values();
|
||||
private static final InternalFormat[] sInternalFormatValues = InternalFormat.values();
|
||||
|
||||
@@ -785,6 +795,17 @@ public class Texture {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the number of samples for multisample anti-aliasing.
|
||||
* @param samples number of samples, must be at least 1. Default is 1.
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder samples(@IntRange(from = 1) int samples) {
|
||||
nBuilderSamples(mNativeBuilder, samples);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the texture's internal format.
|
||||
* <p>The internal format specifies how texels are stored (which may be different from how
|
||||
@@ -1172,6 +1193,38 @@ public class Texture {
|
||||
nSetExternalImage(getNativeObject(), engine.getNativeObject(), eglImage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the external image to associate with this <code>Texture</code>.
|
||||
*
|
||||
* <p>Typically, the external image is OS specific, and can be a video or camera frame.
|
||||
* There are many restrictions when using an external image as a texture, such as:</p>
|
||||
* <ul>
|
||||
* <li> only the level of detail (lod) 0 can be specified</li>
|
||||
* <li> only nearest or linear filtering is supported</li>
|
||||
* <li> the size and format of the texture is defined by the external image</li>
|
||||
* <li> only the CLAMP_TO_EDGE wrap mode is supported</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param engine {@link Engine} this texture is associated to. Must be the
|
||||
* instance passed to {@link Builder#build Builder.build()}.
|
||||
* @param externalImageRef An OS specific Object. On Android it must be a
|
||||
* <code>android.hardware.HardwareBuffer</code>
|
||||
*/
|
||||
public void setExternalImage(@NonNull Engine engine, Object externalImageRef) {
|
||||
if (HardwareBufferClass != null) {
|
||||
if (!HardwareBufferClass.isInstance(externalImageRef)) {
|
||||
throw new IllegalArgumentException("externalImageRef must be a AHardwareBuffer");
|
||||
}
|
||||
if (!nSetExternalImageByAHB(getNativeObject(), engine.getNativeObject(), externalImageRef)) {
|
||||
throw new IllegalStateException("Error setting AHardwareBuffer as external image");
|
||||
}
|
||||
} else {
|
||||
throw new UnsupportedOperationException(
|
||||
"setExternalImage(Engine, Object) not supported on this platform");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specifies the external stream to associate with this <code>Texture</code>.
|
||||
*
|
||||
@@ -1328,6 +1381,7 @@ public class Texture {
|
||||
private static native void nBuilderFormat(long nativeBuilder, int format);
|
||||
private static native void nBuilderUsage(long nativeBuilder, int flags);
|
||||
private static native void nBuilderSwizzle(long nativeBuilder, int r, int g, int b, int a);
|
||||
private static native void nBuilderSamples(long nativeBuilder, int samples);
|
||||
private static native void nBuilderImportTexture(long nativeBuilder, long id);
|
||||
private static native void nBuilderExternal(long nativeBuilder);
|
||||
private static native long nBuilderBuild(long nativeBuilder, long nativeEngine);
|
||||
@@ -1364,6 +1418,8 @@ public class Texture {
|
||||
private static native void nSetExternalImage(
|
||||
long nativeObject, long nativeEngine, long eglImage);
|
||||
|
||||
private static native boolean nSetExternalImageByAHB(long nativeTexture, long nativeObject, Object ahb);
|
||||
|
||||
private static native void nSetExternalStream(long nativeTexture,
|
||||
long nativeEngine, long nativeStream);
|
||||
|
||||
|
||||
@@ -294,8 +294,8 @@ public class TextureSampler {
|
||||
/**
|
||||
* This controls anisotropic filtering.
|
||||
*
|
||||
* @param anisotropy Amount of anisotropy, should be a power-of-two. The default is 0.
|
||||
* The maximum permissible value is 7.
|
||||
* @param anisotropy Amount of anisotropy, should be a power-of-two. The default is 1.
|
||||
* The maximum permissible value is 128.
|
||||
*/
|
||||
public void setAnisotropy(float anisotropy) {
|
||||
mSampler = nSetAnisotropy(mSampler, anisotropy);
|
||||
|
||||
@@ -24,6 +24,7 @@ package com.google.android.filament;
|
||||
* <li>ACESLegacyToneMapper</li>
|
||||
* <li>FilmicToneMapper</li>
|
||||
* <li>PBRNeutralToneMapper</li>
|
||||
* <li>GT7ToneMapper</li>
|
||||
* </ul>
|
||||
* <li>Debug/validation tone mapping operators</li>
|
||||
* <ul>
|
||||
@@ -111,6 +112,19 @@ public class ToneMapper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gran Turismo 7 tone mapping operator. This tone mapper was designed
|
||||
* to preserve the appearance of materials across lighting conditions while
|
||||
* avoiding artifacts in the highlights in high dynamic range conditions.
|
||||
* This tone mapper targets an SDR paper white value of 250 nits, with a
|
||||
* reference luminance of 100 cd/m^2 (a value of 1.0 in the HDR framebuffer).
|
||||
*/
|
||||
public static class GT7ToneMapper extends ToneMapper {
|
||||
public GT7ToneMapper() {
|
||||
super(nCreateGT7ToneMapper());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AgX tone mapping operator.
|
||||
*/
|
||||
@@ -244,6 +258,7 @@ public class ToneMapper {
|
||||
private static native long nCreateACESLegacyToneMapper();
|
||||
private static native long nCreateFilmicToneMapper();
|
||||
private static native long nCreatePBRNeutralToneMapper();
|
||||
private static native long nCreateGT7ToneMapper();
|
||||
private static native long nCreateAgxToneMapper(int look);
|
||||
private static native long nCreateGenericToneMapper(
|
||||
float contrast, float midGrayIn, float midGrayOut, float hdrMax);
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
/.idea/caches
|
||||
/.idea/gradle.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
/.cxx
|
||||
@@ -0,0 +1,64 @@
|
||||
plugins {
|
||||
id "de.undercouch.download" version "5.6.0"
|
||||
}
|
||||
|
||||
apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
|
||||
|
||||
def tools = ['matc', 'cmgen']
|
||||
|
||||
def platforms = [
|
||||
'mac': [classifier: 'osx-aarch_64', archive: "filament-v${VERSION_NAME}-mac.tgz", path: { t -> "filament/bin/${t}" }],
|
||||
'linux': [classifier: 'linux-x86_64', archive: "filament-v${VERSION_NAME}-linux.tgz", path: { t -> "filament/bin/${t}" }],
|
||||
'windows': [classifier: 'windows-x86_64', archive: "filament-v${VERSION_NAME}-windows.tgz", path: { t -> "bin/${t}.exe" }]
|
||||
]
|
||||
|
||||
platforms.each { platform, config ->
|
||||
def platformName = platform.capitalize()
|
||||
def remoteUrl = "https://github.com/google/filament/releases/download/v${VERSION_NAME}/${config.archive}"
|
||||
def downloadFile = file("${buildDir}/downloads/${config.archive}")
|
||||
def extractDir = file("${buildDir}/extracted/filament-v${VERSION_NAME}-${platform}")
|
||||
|
||||
task "downloadRelease${platformName}"(type: Download) {
|
||||
src remoteUrl
|
||||
dest downloadFile
|
||||
overwrite false
|
||||
}
|
||||
|
||||
def extractionTask = task "extractTools${platformName}"(dependsOn: "downloadRelease${platformName}", type: Copy) {
|
||||
group = "setup"
|
||||
from tarTree(resources.gzip(downloadFile))
|
||||
|
||||
// Include specific tools based on platform pattern
|
||||
include tools.collect { tool -> config.path(tool) }
|
||||
|
||||
// Flatten the path so it lands directly in 'into'
|
||||
eachFile { fcd ->
|
||||
fcd.relativePath = new RelativePath(true, fcd.name)
|
||||
}
|
||||
|
||||
into extractDir
|
||||
includeEmptyDirs = false
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
tools.each { toolName ->
|
||||
create(toolName, MavenPublication) {
|
||||
artifactId = toolName
|
||||
|
||||
platforms.each { platform, config ->
|
||||
def extractDir = file("${buildDir}/extracted/filament-v${VERSION_NAME}-${platform}")
|
||||
def archivePath = config.path(toolName)
|
||||
def exeName = new File(archivePath).name
|
||||
|
||||
artifact(new File(extractDir, exeName)) {
|
||||
classifier = config.classifier
|
||||
extension = "exe"
|
||||
builtBy tasks.named("extractTools${platform.capitalize()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
POM_NAME=Filament
|
||||
POM_PACKAGING=exe
|
||||
@@ -22,6 +22,10 @@ add_library(image STATIC IMPORTED)
|
||||
set_target_properties(image PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libimage.a)
|
||||
|
||||
add_library(imageio-lite STATIC IMPORTED)
|
||||
set_target_properties(imageio-lite PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libimageio-lite.a)
|
||||
|
||||
add_library(ktxreader STATIC IMPORTED)
|
||||
set_target_properties(ktxreader PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libktxreader.a)
|
||||
@@ -30,6 +34,10 @@ add_library(viewer STATIC IMPORTED)
|
||||
set_target_properties(viewer PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libviewer.a)
|
||||
|
||||
add_library(imagediff STATIC IMPORTED)
|
||||
set_target_properties(imagediff PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libimagediff.a)
|
||||
|
||||
add_library(civetweb STATIC IMPORTED)
|
||||
set_target_properties(civetweb PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libcivetweb.a)
|
||||
@@ -57,6 +65,7 @@ add_library(filament-utils-jni SHARED
|
||||
src/main/cpp/IBLPrefilterContext.cpp
|
||||
src/main/cpp/Utils.cpp
|
||||
src/main/cpp/Manipulator.cpp
|
||||
src/main/cpp/ImageDiff.cpp
|
||||
src/main/cpp/RemoteServer.cpp
|
||||
|
||||
${IMAGEIO_DIR}/include/imageio/ImageDecoder.h
|
||||
@@ -74,6 +83,7 @@ target_include_directories(filament-utils-jni PRIVATE
|
||||
${FILAMENT_DIR}/include
|
||||
../../filament/backend/include
|
||||
${IMAGEIO_DIR}/include
|
||||
../../libs/imagediff/include
|
||||
../../libs/utils/include)
|
||||
|
||||
set_target_properties(filament-utils-jni PROPERTIES LINK_DEPENDS ${VERSION_SCRIPT})
|
||||
@@ -85,10 +95,13 @@ target_link_libraries(filament-utils-jni
|
||||
PRIVATE camutils
|
||||
PRIVATE iblprefilter
|
||||
PRIVATE image
|
||||
PRIVATE imageio-lite
|
||||
PRIVATE filament-jni
|
||||
PRIVATE ktxreader
|
||||
PRIVATE viewer
|
||||
PRIVATE imagediff
|
||||
PRIVATE log
|
||||
PRIVATE utils
|
||||
PRIVATE perfetto # needed only when FILAMENT_ENABLE_PERFETTO is defined
|
||||
PRIVATE jnigraphics # needed for AndroidBitmap_* functions in ImageDiff
|
||||
)
|
||||
|
||||
@@ -71,8 +71,10 @@ Java_com_google_android_filament_utils_AutomationEngine_nStartBatchMode(JNIEnv*
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_utils_AutomationEngine_nTick(JNIEnv* env, jclass klass,
|
||||
jlong nativeAutomation, jlong nativeEngine,
|
||||
jlong view, jlongArray materials, jlong renderer, jfloat deltaTime) {
|
||||
jlong view, jlongArray materials, jlong renderer, jlong nativeIbl, jint sunlightEntity,
|
||||
jintArray assetLights, jlong nativeLm, jlong scene, jfloat deltaTime) {
|
||||
using MaterialPointer = MaterialInstance*;
|
||||
|
||||
jsize materialCount = 0;
|
||||
jlong* longMaterials = nullptr;
|
||||
MaterialPointer* ptrMaterials = nullptr;
|
||||
@@ -84,12 +86,28 @@ Java_com_google_android_filament_utils_AutomationEngine_nTick(JNIEnv* env, jclas
|
||||
ptrMaterials[i] = (MaterialPointer) longMaterials[i];
|
||||
}
|
||||
}
|
||||
|
||||
jsize lightCount = 0;
|
||||
jint* intLights = nullptr;
|
||||
if (assetLights) {
|
||||
lightCount = env->GetArrayLength(assetLights);
|
||||
intLights = env->GetIntArrayElements(assetLights, nullptr);
|
||||
}
|
||||
|
||||
static_assert(sizeof(jint) == sizeof(Entity));
|
||||
|
||||
AutomationEngine* automation = (AutomationEngine*) nativeAutomation;
|
||||
AutomationEngine::ViewerContent content = {
|
||||
.view = (View*) view,
|
||||
.renderer = (Renderer*) renderer,
|
||||
.materials = ptrMaterials,
|
||||
.materialCount = (size_t) materialCount,
|
||||
.lightManager = (LightManager*) nativeLm,
|
||||
.scene = (Scene*) scene,
|
||||
.indirectLight = (IndirectLight*) nativeIbl,
|
||||
.sunlight = (Entity&) sunlightEntity,
|
||||
.assetLights = (Entity*) intLights,
|
||||
.assetLightCount = (size_t) lightCount,
|
||||
};
|
||||
Engine* engine = (Engine*)nativeEngine;
|
||||
automation->tick(engine, content, deltaTime);
|
||||
@@ -97,6 +115,9 @@ Java_com_google_android_filament_utils_AutomationEngine_nTick(JNIEnv* env, jclas
|
||||
env->ReleaseLongArrayElements(materials, longMaterials, 0);
|
||||
delete[] ptrMaterials;
|
||||
}
|
||||
if (intLights) {
|
||||
env->ReleaseIntArrayElements(assetLights, intLights, 0);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
@@ -159,37 +180,50 @@ extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_utils_AutomationEngine_nGetViewerOptions(JNIEnv* env, jclass,
|
||||
jlong nativeObject, jobject result) {
|
||||
AutomationEngine* automation = (AutomationEngine*) nativeObject;
|
||||
auto options = automation->getViewerOptions();
|
||||
const auto& settings = automation->getSettings();
|
||||
const auto& options = settings.viewer;
|
||||
|
||||
const jclass klass = env->GetObjectClass(result);
|
||||
|
||||
const jfieldID cameraAperture = env->GetFieldID(klass, "cameraAperture", "F");
|
||||
const jfieldID cameraSpeed = env->GetFieldID(klass, "cameraSpeed", "F");
|
||||
const jfieldID cameraISO = env->GetFieldID(klass, "cameraISO", "F");
|
||||
const jfieldID cameraNear = env->GetFieldID(klass, "cameraNear", "F");
|
||||
const jfieldID cameraFar = env->GetFieldID(klass, "cameraFar", "F");
|
||||
const jfieldID groundShadowStrength = env->GetFieldID(klass, "groundShadowStrength", "F");
|
||||
const jfieldID groundPlaneEnabled = env->GetFieldID(klass, "groundPlaneEnabled", "Z");
|
||||
const jfieldID skyboxEnabled = env->GetFieldID(klass, "skyboxEnabled", "Z");
|
||||
const jfieldID cameraFocalLength = env->GetFieldID(klass, "cameraFocalLength", "F");
|
||||
const jfieldID cameraFocusDistance = env->GetFieldID(klass, "cameraFocusDistance", "F");
|
||||
const jfieldID autoScaleEnabled = env->GetFieldID(klass, "autoScaleEnabled", "Z");
|
||||
const jfieldID autoInstancingEnabled = env->GetFieldID(klass, "autoInstancingEnabled", "Z");
|
||||
|
||||
env->SetFloatField(result, cameraAperture, options.cameraAperture);
|
||||
env->SetFloatField(result, cameraSpeed, options.cameraSpeed);
|
||||
env->SetFloatField(result, cameraISO, options.cameraISO);
|
||||
env->SetFloatField(result, cameraNear, options.cameraNear);
|
||||
env->SetFloatField(result, cameraFar, options.cameraFar);
|
||||
env->SetFloatField(result, groundShadowStrength, options.groundShadowStrength);
|
||||
env->SetBooleanField(result, groundPlaneEnabled, options.groundPlaneEnabled);
|
||||
env->SetBooleanField(result, skyboxEnabled, options.skyboxEnabled);
|
||||
env->SetFloatField(result, cameraFocalLength, options.cameraFocalLength);
|
||||
env->SetFloatField(result, cameraFocusDistance, options.cameraFocusDistance);
|
||||
env->SetBooleanField(result, autoScaleEnabled, options.autoScaleEnabled);
|
||||
env->SetBooleanField(result, autoInstancingEnabled, options.autoInstancingEnabled);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_utils_AutomationEngine_nGetCameraSettings(JNIEnv* env, jclass,
|
||||
jlong nativeObject, jobject result) {
|
||||
AutomationEngine* automation = (AutomationEngine*) nativeObject;
|
||||
const auto& settings = automation->getSettings();
|
||||
const auto& camera = settings.camera;
|
||||
|
||||
const jclass klass = env->GetObjectClass(result);
|
||||
|
||||
const jfieldID aperture = env->GetFieldID(klass, "aperture", "F");
|
||||
const jfieldID shutterSpeed = env->GetFieldID(klass, "shutterSpeed", "F");
|
||||
const jfieldID sensitivity = env->GetFieldID(klass, "sensitivity", "F");
|
||||
const jfieldID near = env->GetFieldID(klass, "near", "F");
|
||||
const jfieldID far = env->GetFieldID(klass, "far", "F");
|
||||
const jfieldID focalLength = env->GetFieldID(klass, "focalLength", "F");
|
||||
const jfieldID focusDistance = env->GetFieldID(klass, "focusDistance", "F");
|
||||
|
||||
env->SetFloatField(result, aperture, camera.aperture);
|
||||
env->SetFloatField(result, shutterSpeed, camera.shutterSpeed);
|
||||
env->SetFloatField(result, sensitivity, camera.sensitivity);
|
||||
env->SetFloatField(result, near, camera.near);
|
||||
env->SetFloatField(result, far, camera.far);
|
||||
env->SetFloatField(result, focalLength, camera.focalLength);
|
||||
env->SetFloatField(result, focusDistance, camera.focusDistance);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_utils_AutomationEngine_nGetColorGrading(JNIEnv*, jclass,
|
||||
jlong nativeObject, jlong nativeEngine) {
|
||||
@@ -215,6 +249,18 @@ Java_com_google_android_filament_utils_AutomationEngine_nShouldClose(JNIEnv*, jc
|
||||
return automation->shouldClose();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_utils_AutomationEngine_nGetTestCount(JNIEnv*, jclass, jlong native) {
|
||||
AutomationEngine* automation = (AutomationEngine*) native;
|
||||
return (jint) automation->testCount();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_utils_AutomationEngine_nGetCurrentTest(JNIEnv*, jclass, jlong native) {
|
||||
AutomationEngine* automation = (AutomationEngine*) native;
|
||||
return (jint) automation->currentTest();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_utils_AutomationEngine_nDestroy(JNIEnv*, jclass, jlong native) {
|
||||
AutomationEngine* automation = (AutomationEngine*) native;
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright (C) 2026 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <jni.h>
|
||||
#include <android/bitmap.h>
|
||||
|
||||
#include <imagediff/ImageDiff.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
using namespace imagediff;
|
||||
using namespace utils;
|
||||
|
||||
namespace {
|
||||
|
||||
struct BitmapLock {
|
||||
JNIEnv* env;
|
||||
jobject bitmap;
|
||||
void* pixels;
|
||||
AndroidBitmapInfo info;
|
||||
|
||||
BitmapLock(JNIEnv* env, jobject bitmap) : env(env), bitmap(bitmap), pixels(nullptr) {
|
||||
if (!bitmap) return;
|
||||
if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) {
|
||||
return;
|
||||
}
|
||||
if (AndroidBitmap_lockPixels(env, bitmap, &pixels) < 0) {
|
||||
pixels = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
~BitmapLock() {
|
||||
if (pixels) {
|
||||
AndroidBitmap_unlockPixels(env, bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
bool isValid() const { return pixels != nullptr; }
|
||||
|
||||
imagediff::Bitmap toBitmap() const {
|
||||
return {
|
||||
.width = (uint32_t) info.width,
|
||||
.height = (uint32_t) info.height,
|
||||
.stride = (size_t) info.stride,
|
||||
.data = pixels
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
|
||||
// Helper to convert C++ ImageDiffResult to Java Result
|
||||
jobject createResult(JNIEnv* env, ImageDiffResult const& result, bool generateDiff) {
|
||||
// Create Result class/objects
|
||||
jclass resultClass = env->FindClass("com/google/android/filament/utils/ImageDiff$Result");
|
||||
jmethodID resultCtor = env->GetMethodID(resultClass, "<init>", "()V");
|
||||
jobject resultObj = env->NewObject(resultClass, resultCtor);
|
||||
jfieldID statusField = env->GetFieldID(resultClass, "status", "Lcom/google/android/filament/utils/ImageDiff$Result$Status;");
|
||||
jfieldID failingCountField = env->GetFieldID(resultClass, "failingPixelCount", "J");
|
||||
jfieldID maxDiffField = env->GetFieldID(resultClass, "maxDiffFound", "[F");
|
||||
jfieldID diffImageField = env->GetFieldID(resultClass, "diffImage", "Landroid/graphics/Bitmap;");
|
||||
|
||||
// Map Status enum
|
||||
jclass statusEnum = env->FindClass("com/google/android/filament/utils/ImageDiff$Result$Status");
|
||||
jobject statusObj = nullptr;
|
||||
jfieldID enumField = nullptr;
|
||||
switch (result.status) {
|
||||
case ImageDiffResult::Status::PASSED:
|
||||
enumField = env->GetStaticFieldID(statusEnum, "PASSED", "Lcom/google/android/filament/utils/ImageDiff$Result$Status;");
|
||||
break;
|
||||
case ImageDiffResult::Status::SIZE_MISMATCH:
|
||||
enumField = env->GetStaticFieldID(statusEnum, "SIZE_MISMATCH", "Lcom/google/android/filament/utils/ImageDiff$Result$Status;");
|
||||
break;
|
||||
case ImageDiffResult::Status::PIXEL_DIFFERENCE:
|
||||
enumField = env->GetStaticFieldID(statusEnum, "PIXEL_DIFFERENCE", "Lcom/google/android/filament/utils/ImageDiff$Result$Status;");
|
||||
break;
|
||||
}
|
||||
statusObj = env->GetStaticObjectField(statusEnum, enumField);
|
||||
env->SetObjectField(resultObj, statusField, statusObj);
|
||||
|
||||
env->SetLongField(resultObj, failingCountField, (jlong) result.failingPixelCount);
|
||||
|
||||
jfloatArray maxDiffArray = env->NewFloatArray(4);
|
||||
env->SetFloatArrayRegion(maxDiffArray, 0, 4, result.maxDiffFound);
|
||||
env->SetObjectField(resultObj, maxDiffField, maxDiffArray);
|
||||
|
||||
if (generateDiff && result.diffImage.getWidth() > 0) {
|
||||
jclass bitmapClass = env->FindClass("android/graphics/Bitmap");
|
||||
jmethodID createBitmap = env->GetStaticMethodID(bitmapClass, "createBitmap",
|
||||
"(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
|
||||
|
||||
jclass configClass = env->FindClass("android/graphics/Bitmap$Config");
|
||||
jfieldID argb8888 = env->GetStaticFieldID(configClass, "ARGB_8888", "Landroid/graphics/Bitmap$Config;");
|
||||
jobject configObj = env->GetStaticObjectField(configClass, argb8888);
|
||||
|
||||
uint32_t width = result.diffImage.getWidth();
|
||||
uint32_t height = result.diffImage.getHeight();
|
||||
jobject diffBitmap = env->CallStaticObjectMethod(bitmapClass, createBitmap, (jint)width, (jint)height, configObj);
|
||||
|
||||
if (diffBitmap) {
|
||||
void* diffPixels;
|
||||
if (AndroidBitmap_lockPixels(env, diffBitmap, &diffPixels) == 0) {
|
||||
float const* src = result.diffImage.getPixelRef();
|
||||
uint8_t* dst = (uint8_t*) diffPixels;
|
||||
uint32_t channels = result.diffImage.getChannels(); // usually 4
|
||||
|
||||
for (size_t i = 0; i < width * height; ++i) {
|
||||
for (int c = 0; c < 4; ++c) {
|
||||
float v = 0.0f;
|
||||
if (c < channels) v = src[i * channels + c];
|
||||
if (c == 3 && channels < 4) v = 1.0f; // Alpha 1.0 if missing
|
||||
dst[i * 4 + c] = (uint8_t) std::min(255.0f, std::max(0.0f, v * 255.0f));
|
||||
}
|
||||
}
|
||||
AndroidBitmap_unlockPixels(env, diffBitmap);
|
||||
env->SetObjectField(resultObj, diffImageField, diffBitmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resultObj;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jobject JNICALL
|
||||
Java_com_google_android_filament_utils_ImageDiff_nCompareBasic(JNIEnv* env, jclass,
|
||||
jobject refBitmap, jobject candBitmap, jint mode, jint swizzle, jint channelMask,
|
||||
jfloat maxAbsDiff, jfloat maxFailingPixelsFraction, jobject maskBitmap) {
|
||||
|
||||
BitmapLock refArg(env, refBitmap);
|
||||
BitmapLock candArg(env, candBitmap);
|
||||
BitmapLock maskArg(env, maskBitmap);
|
||||
|
||||
if (!refArg.isValid() || !candArg.isValid()) {
|
||||
ImageDiffResult emptyResult;
|
||||
emptyResult.status = ImageDiffResult::Status::SIZE_MISMATCH; // or ERROR
|
||||
return createResult(env, emptyResult, false);
|
||||
}
|
||||
|
||||
ImageDiffConfig config;
|
||||
config.mode = (ImageDiffConfig::Mode) mode;
|
||||
config.swizzle = (ImageDiffConfig::Swizzle) swizzle;
|
||||
config.channelMask = (uint8_t) channelMask;
|
||||
config.maxAbsDiff = maxAbsDiff;
|
||||
config.maxFailingPixelsFraction = maxFailingPixelsFraction;
|
||||
|
||||
imagediff::Bitmap const* maskPtr = nullptr;
|
||||
imagediff::Bitmap maskVal;
|
||||
if (maskBitmap && maskArg.isValid()) {
|
||||
maskVal = maskArg.toBitmap();
|
||||
maskPtr = &maskVal;
|
||||
}
|
||||
|
||||
bool generateDiff = true;
|
||||
ImageDiffResult result = compare(refArg.toBitmap(), candArg.toBitmap(), config, maskPtr, generateDiff);
|
||||
|
||||
return createResult(env, result, generateDiff);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jobject JNICALL
|
||||
Java_com_google_android_filament_utils_ImageDiff_nCompareJson(JNIEnv* env, jclass,
|
||||
jobject refBitmap, jobject candBitmap, jstring jsonConfig, jobject maskBitmap) {
|
||||
|
||||
BitmapLock refArg(env, refBitmap);
|
||||
BitmapLock candArg(env, candBitmap);
|
||||
BitmapLock maskArg(env, maskBitmap);
|
||||
|
||||
if (!refArg.isValid() || !candArg.isValid()) {
|
||||
ImageDiffResult emptyResult;
|
||||
emptyResult.status = ImageDiffResult::Status::SIZE_MISMATCH; // or ERROR
|
||||
return createResult(env, emptyResult, false);
|
||||
}
|
||||
|
||||
ImageDiffConfig config;
|
||||
const char* nativeJson = env->GetStringUTFChars(jsonConfig, 0);
|
||||
size_t length = env->GetStringUTFLength(jsonConfig);
|
||||
|
||||
bool parsed = parseConfig(nativeJson, length, &config);
|
||||
env->ReleaseStringUTFChars(jsonConfig, nativeJson);
|
||||
|
||||
if (!parsed) {
|
||||
// Fallback to default or error?
|
||||
// We could log error.
|
||||
utils::slog.e << "ImageDiff JNI: Failed to parse JSON config" << utils::io::endl;
|
||||
ImageDiffResult errResult;
|
||||
errResult.status = ImageDiffResult::Status::PIXEL_DIFFERENCE; // assume fail
|
||||
return createResult(env, errResult, false);
|
||||
}
|
||||
|
||||
imagediff::Bitmap const* maskPtr = nullptr;
|
||||
imagediff::Bitmap maskVal;
|
||||
if (maskBitmap && maskArg.isValid()) {
|
||||
maskVal = maskArg.toBitmap();
|
||||
maskPtr = &maskVal;
|
||||
}
|
||||
|
||||
bool generateDiff = true;
|
||||
ImageDiffResult result = compare(refArg.toBitmap(), candArg.toBitmap(), config, maskPtr, generateDiff);
|
||||
|
||||
return createResult(env, result, generateDiff);
|
||||
}
|
||||
|
||||
@@ -94,20 +94,23 @@ public class AutomationEngine {
|
||||
* Allows remote control for the viewer.
|
||||
*/
|
||||
public static class ViewerOptions {
|
||||
public float cameraAperture = 16.0f;
|
||||
public float cameraSpeed = 125.0f;
|
||||
public float cameraISO = 100.0f;
|
||||
public float cameraNear = 0.1f;
|
||||
public float cameraFar = 100.0f;
|
||||
public float groundShadowStrength = 0.75f;
|
||||
public boolean groundPlaneEnabled = false;
|
||||
public boolean skyboxEnabled = true;
|
||||
public float cameraFocalLength = 28.0f;
|
||||
public float cameraFocusDistance = 0.0f;
|
||||
public boolean autoScaleEnabled = true;
|
||||
public boolean autoInstancingEnabled = false;
|
||||
}
|
||||
|
||||
public static class CameraSettings {
|
||||
public float aperture = 16.0f;
|
||||
public float shutterSpeed = 125.0f;
|
||||
public float sensitivity = 100.0f;
|
||||
public float near = 0.1f;
|
||||
public float far = 100.0f;
|
||||
public float focalLength = 28.0f;
|
||||
public float focusDistance = 10.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an automation engine from a JSON specification.
|
||||
*
|
||||
@@ -175,7 +178,12 @@ public class AutomationEngine {
|
||||
}
|
||||
long nativeView = content.view.getNativeObject();
|
||||
long nativeRenderer = content.renderer.getNativeObject();
|
||||
nTick(mNativeObject, engine.getNativeObject(), nativeView, nativeMaterialInstances, nativeRenderer, deltaTime);
|
||||
long nativeIbl = content.indirectLight == null ? 0 : content.indirectLight.getNativeObject();
|
||||
long nativeLm = content.lightManager == null ? 0 : content.lightManager.getNativeObject();
|
||||
long nativeScene = content.scene == null ? 0 : content.scene.getNativeObject();
|
||||
nTick(mNativeObject, engine.getNativeObject(), nativeView, nativeMaterialInstances,
|
||||
nativeRenderer, nativeIbl, content.sunlight, content.assetLights, nativeLm,
|
||||
nativeScene, deltaTime);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,6 +237,13 @@ public class AutomationEngine {
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public CameraSettings getCameraSettings() {
|
||||
CameraSettings result = new CameraSettings();
|
||||
nGetCameraSettings(mNativeObject, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a color grading object that corresponds to the latest settings.
|
||||
*
|
||||
@@ -261,6 +276,9 @@ public class AutomationEngine {
|
||||
*/
|
||||
public boolean shouldClose() { return nShouldClose(mNativeObject); }
|
||||
|
||||
public int getTestCount() { return nGetTestCount(mNativeObject); }
|
||||
public int getCurrentTest() { return nGetCurrentTest(mNativeObject); }
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
nDestroy(mNativeObject);
|
||||
@@ -274,15 +292,19 @@ public class AutomationEngine {
|
||||
private static native void nStartRunning(long nativeObject);
|
||||
private static native void nStartBatchMode(long nativeObject);
|
||||
private static native void nTick(long nativeObject, long nativeEngine,
|
||||
long view, long[] materials, long renderer, float deltaTime);
|
||||
long view, long[] materials, long renderer, long ibl, int sunlight, int[] assetLights,
|
||||
long lightManager, long scene, float deltaTime);
|
||||
private static native void nApplySettings(long nativeObject, long nativeEngine,
|
||||
String jsonSettings, long view,
|
||||
long[] materials, long ibl, int sunlight, int[] assetLights, long lightManager,
|
||||
long scene, long renderer);
|
||||
private static native void nGetViewerOptions(long nativeObject, Object result);
|
||||
private static native void nGetCameraSettings(long nativeObject, Object result);
|
||||
private static native long nGetColorGrading(long nativeObject, long nativeEngine);
|
||||
private static native void nSignalBatchMode(long nativeObject);
|
||||
private static native void nStopRunning(long nativeObject);
|
||||
private static native boolean nShouldClose(long nativeObject);
|
||||
private static native int nGetTestCount(long nativeObject);
|
||||
private static native int nGetCurrentTest(long nativeObject);
|
||||
private static native void nDestroy(long nativeObject);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2026 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.filament.utils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
public class ImageDiff {
|
||||
public enum Mode {
|
||||
LEAF, AND, OR
|
||||
}
|
||||
|
||||
public enum Swizzle {
|
||||
RGBA, BGRA
|
||||
}
|
||||
|
||||
public static class Config {
|
||||
@NonNull
|
||||
public Mode mode = Mode.LEAF;
|
||||
@NonNull
|
||||
public Swizzle swizzle = Swizzle.RGBA;
|
||||
public int channelMask = 0xF;
|
||||
public float maxAbsDiff = 0.0f;
|
||||
public float maxFailingPixelsFraction = 0.0f;
|
||||
// Children not supported in this simple wrapper for now, can be added if needed
|
||||
}
|
||||
|
||||
public static class Result {
|
||||
public enum Status {
|
||||
PASSED,
|
||||
SIZE_MISMATCH,
|
||||
PIXEL_DIFFERENCE
|
||||
}
|
||||
|
||||
public Status status;
|
||||
public long failingPixelCount;
|
||||
public float[] maxDiffFound; // [R, G, B, A]
|
||||
public Bitmap diffImage; // Null if not generated
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two bitmaps using a configuration object.
|
||||
*
|
||||
* @param reference Golden image
|
||||
* @param candidate Actual image
|
||||
* @param config Comparison configuration
|
||||
* @param mask Optional mask (grayscale)
|
||||
* @return Result of comparison
|
||||
*/
|
||||
@NonNull
|
||||
public static Result compareBasic(@NonNull Bitmap reference, @NonNull Bitmap candidate,
|
||||
@NonNull Config config, @Nullable Bitmap mask) {
|
||||
return nCompareBasic(reference, candidate, config.mode.ordinal(), config.swizzle.ordinal(),
|
||||
config.channelMask, config.maxAbsDiff, config.maxFailingPixelsFraction, mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two bitmaps using a JSON configuration string.
|
||||
*
|
||||
* @param reference Golden image
|
||||
* @param candidate Actual image
|
||||
* @param jsonConfig Comparison configuration in JSON format
|
||||
* @param mask Optional mask (grayscale)
|
||||
* @return Result of comparison
|
||||
*/
|
||||
@NonNull
|
||||
public static Result compare(@NonNull Bitmap reference, @NonNull Bitmap candidate,
|
||||
@NonNull String jsonConfig, @Nullable Bitmap mask) {
|
||||
return nCompareJson(reference, candidate, jsonConfig, mask);
|
||||
}
|
||||
|
||||
private static native Result nCompareBasic(Bitmap reference, Bitmap candidate, int mode, int swizzle,
|
||||
int channelMask, float maxAbsDiff, float maxFailingPixelsFraction, Bitmap mask);
|
||||
|
||||
private static native Result nCompareJson(Bitmap reference, Bitmap candidate, String jsonConfig, Bitmap mask);
|
||||
}
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package com.google.android.filament.utils
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.MotionEvent
|
||||
import android.view.Surface
|
||||
import android.view.SurfaceView
|
||||
@@ -26,6 +29,7 @@ import com.google.android.filament.android.UiHelper
|
||||
import com.google.android.filament.gltfio.*
|
||||
import kotlinx.coroutines.*
|
||||
import java.nio.Buffer
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
private const val kNearPlane = 0.05f // 5 cm
|
||||
private const val kFarPlane = 1000.0f // 1 km
|
||||
@@ -119,6 +123,8 @@ class ModelViewer(
|
||||
private val target = DoubleArray(3)
|
||||
private val upward = DoubleArray(3)
|
||||
|
||||
private var debugFrameCallback: ((Bitmap) -> Unit)? = null
|
||||
|
||||
init {
|
||||
renderer = engine.createRenderer()
|
||||
scene = engine.createScene()
|
||||
@@ -305,10 +311,39 @@ class ModelViewer(
|
||||
// Render the scene, unless the renderer wants to skip the frame.
|
||||
if (renderer.beginFrame(swapChain!!, frameTimeNanos)) {
|
||||
renderer.render(view)
|
||||
|
||||
debugFrameCallback?.let {
|
||||
val viewport = view.viewport
|
||||
val bitmap = Bitmap.createBitmap(viewport.width, viewport.height,
|
||||
Bitmap.Config.ARGB_8888)
|
||||
val buffer = ByteBuffer.allocateDirect(viewport.width * viewport.height * 4)
|
||||
|
||||
val handler = Handler(Looper.getMainLooper())
|
||||
val pixelBufferDescriptor = Texture.PixelBufferDescriptor(buffer,
|
||||
Texture.Format.RGBA, Texture.Type.UBYTE, 1, 0, 0, 0, handler) {
|
||||
buffer.rewind()
|
||||
bitmap.copyPixelsFromBuffer(buffer)
|
||||
it(bitmap)
|
||||
}
|
||||
renderer.readPixels(viewport.left, viewport.bottom, viewport.width,
|
||||
viewport.height, pixelBufferDescriptor)
|
||||
debugFrameCallback = null
|
||||
}
|
||||
|
||||
renderer.endFrame()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets a callback that will be invoked with the next rendered frame as a Bitmap. Note that this
|
||||
* is a one-time callback.
|
||||
*
|
||||
* @param callback callback to be invoked with a rendered frame as [Bitmap]
|
||||
*/
|
||||
fun debugGetNextFrameCallback(callback: (Bitmap) -> Unit) {
|
||||
debugFrameCallback = callback
|
||||
}
|
||||
|
||||
private fun populateScene(asset: FilamentAsset) {
|
||||
val rcm = engine.renderableManager
|
||||
var count = 0
|
||||
|
||||
@@ -40,7 +40,21 @@ fun loadTexture(engine: Engine, resources: Resources, resourceId: Int, type: Tex
|
||||
options.inPremultiplied = type == TextureType.COLOR
|
||||
|
||||
val bitmap = BitmapFactory.decodeResource(resources, resourceId, options)
|
||||
return buildTexture(engine, bitmap, type)
|
||||
}
|
||||
|
||||
fun loadTexture(engine: Engine, bytes: ByteArray, type: TextureType, offset: Int = 0, length: Int = bytes.size): Texture {
|
||||
val options = BitmapFactory.Options()
|
||||
// Color is the only type of texture we want to pre-multiply with the alpha channel
|
||||
// Pre-multiplication is the default behavior, so we need to turn it off here
|
||||
options.inPremultiplied = type == TextureType.COLOR
|
||||
|
||||
val bitmap = BitmapFactory.decodeByteArray(bytes, offset, length, options)
|
||||
return buildTexture(engine, bitmap, type)
|
||||
}
|
||||
|
||||
|
||||
private fun buildTexture(engine: Engine, bitmap: Bitmap, type: TextureType): Texture {
|
||||
val texture = Texture.Builder()
|
||||
.width(bitmap.width)
|
||||
.height(bitmap.height)
|
||||
|
||||
@@ -22,6 +22,7 @@ object Utils {
|
||||
/**
|
||||
* Initializes the utils JNI layer. Must be called before using any utils functionality.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun init() {
|
||||
// Load Filament first to ensure that the NioUtils Java class is available in the JNIEnv.
|
||||
Filament.init()
|
||||
|
||||
@@ -6,6 +6,7 @@ option(FILAMENT_ENABLE_FGVIEWER "Enables Frame Graph Viewer" OFF)
|
||||
option(FILAMENT_ENABLE_MATDBG "Enables Material debugger" OFF)
|
||||
option(FILAMENT_DISABLE_MATOPT "Disables material optimizations" OFF)
|
||||
option(FILAMENT_SUPPORTS_WEBGPU "Enables WebGPU on Android" OFF)
|
||||
option(FILAMENT_SUPPORTS_WEBP_TEXTURES "Enable webp texture support on Android" OFF)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
@@ -30,6 +31,12 @@ add_library(stb STATIC IMPORTED)
|
||||
set_target_properties(stb PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libstb.a)
|
||||
|
||||
if (FILAMENT_SUPPORTS_WEBP_TEXTURES)
|
||||
add_library(webpdecoder STATIC IMPORTED)
|
||||
set_target_properties(webpdecoder PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libwebpdecoder.a)
|
||||
endif()
|
||||
|
||||
add_library(basis_transcoder STATIC IMPORTED)
|
||||
set_target_properties(basis_transcoder PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libbasis_transcoder.a)
|
||||
@@ -81,6 +88,10 @@ set(GLTFIO_INCLUDE_DIRS
|
||||
../../libs/ktxreader/include
|
||||
)
|
||||
|
||||
if (FILAMENT_SUPPORTS_WEBP_TEXTURES)
|
||||
list(APPEND GLTFIO_INCLUDE_DIRS ../../third_party/libwebp/src)
|
||||
endif()
|
||||
|
||||
add_library(gltfio-jni SHARED ${GLTFIO_SRCS})
|
||||
|
||||
target_compile_definitions(gltfio-jni PUBLIC GLTFIO_DRACO_SUPPORTED=1)
|
||||
@@ -107,3 +118,6 @@ target_link_libraries(gltfio-jni
|
||||
PRIVATE perfetto # needed only when FILAMENT_ENABLE_PERFETTO is defined
|
||||
PRIVATE log # needed only when perfetto above is used
|
||||
)
|
||||
if (FILAMENT_SUPPORTS_WEBP_TEXTURES)
|
||||
target_link_libraries(gltfio-jni PRIVATE webpdecoder)
|
||||
endif()
|
||||
|
||||
@@ -128,6 +128,18 @@ Java_com_google_android_filament_gltfio_ResourceLoader_nCreateKtx2Provider(JNIEn
|
||||
return (jlong) createKtx2Provider(engine);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_gltfio_ResourceLoader_nIsWebpSupported(JNIEnv*, jclass) {
|
||||
return (jboolean) isWebpSupported();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_gltfio_ResourceLoader_nCreateWebpProvider(JNIEnv*, jclass,
|
||||
jlong nativeEngine) {
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
return (jlong) createWebpProvider(engine);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_gltfio_ResourceLoader_nDestroyTextureProvider(JNIEnv*, jclass,
|
||||
jlong nativeProvider) {
|
||||
|
||||
@@ -37,6 +37,7 @@ public class ResourceLoader {
|
||||
private final long mNativeObject;
|
||||
private final long mNativeStbProvider;
|
||||
private final long mNativeKtx2Provider;
|
||||
private final long mNativeWebpProvider;
|
||||
|
||||
/**
|
||||
* Constructs a resource loader tied to the given Filament engine.
|
||||
@@ -50,9 +51,17 @@ public class ResourceLoader {
|
||||
mNativeObject = nCreateResourceLoader(nativeEngine, false);
|
||||
mNativeStbProvider = nCreateStbProvider(nativeEngine);
|
||||
mNativeKtx2Provider = nCreateKtx2Provider(nativeEngine);
|
||||
|
||||
nAddTextureProvider(mNativeObject, "image/jpeg", mNativeStbProvider);
|
||||
nAddTextureProvider(mNativeObject, "image/png", mNativeStbProvider);
|
||||
nAddTextureProvider(mNativeObject, "image/ktx2", mNativeKtx2Provider);
|
||||
if (nIsWebpSupported()) {
|
||||
mNativeWebpProvider = nCreateWebpProvider(nativeEngine);
|
||||
nAddTextureProvider(mNativeObject, "image/webp", mNativeWebpProvider);
|
||||
}
|
||||
else {
|
||||
mNativeWebpProvider = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,9 +77,17 @@ public class ResourceLoader {
|
||||
mNativeObject = nCreateResourceLoader(nativeEngine, normalizeSkinningWeights);
|
||||
mNativeStbProvider = nCreateStbProvider(nativeEngine);
|
||||
mNativeKtx2Provider = nCreateKtx2Provider(nativeEngine);
|
||||
|
||||
nAddTextureProvider(mNativeObject, "image/jpeg", mNativeStbProvider);
|
||||
nAddTextureProvider(mNativeObject, "image/png", mNativeStbProvider);
|
||||
nAddTextureProvider(mNativeObject, "image/ktx2", mNativeKtx2Provider);
|
||||
if (nIsWebpSupported()) {
|
||||
mNativeWebpProvider = nCreateWebpProvider(nativeEngine);
|
||||
nAddTextureProvider(mNativeObject, "image/webp", mNativeWebpProvider);
|
||||
}
|
||||
else {
|
||||
mNativeWebpProvider = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,6 +97,9 @@ public class ResourceLoader {
|
||||
nDestroyResourceLoader(mNativeObject);
|
||||
nDestroyTextureProvider(mNativeStbProvider);
|
||||
nDestroyTextureProvider(mNativeKtx2Provider);
|
||||
if (nIsWebpSupported()) {
|
||||
nDestroyTextureProvider(mNativeWebpProvider);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -191,6 +211,9 @@ public class ResourceLoader {
|
||||
|
||||
private static native long nCreateStbProvider(long nativeEngine);
|
||||
private static native long nCreateKtx2Provider(long nativeEngine);
|
||||
private static native boolean nIsWebpSupported();
|
||||
private static native long nCreateWebpProvider(long nativeEngine);
|
||||
|
||||
private static native void nAddTextureProvider(long nativeLoader, String url, long nativeProvider);
|
||||
private static native void nDestroyTextureProvider(long nativeProvider);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.65.0
|
||||
VERSION_NAME=1.69.3
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
@@ -80,54 +80,12 @@ frame and the external texture are perfectly synchronized.
|
||||
|
||||

|
||||
|
||||
## Prerequisites
|
||||
## Building Samples
|
||||
|
||||
Before you start, make sure to read [Filament's README](../../README.md). You need to be able to
|
||||
compile Filament's native library and Filament's AAR for this project. The easiest way to proceed
|
||||
is to install all the required dependencies and to run the following commands at the root of the
|
||||
source tree:
|
||||
source tree.
|
||||
|
||||
```shell
|
||||
./build.sh -p desktop -i release
|
||||
./build.sh -p android release
|
||||
```
|
||||
To build the samples, please follow the steps described in [BUILDING.md](../../BUILDING.md#android)
|
||||
|
||||
This will build all the native components and the AAR required by this sample application.
|
||||
|
||||
If you do not use the build script, you must set the `filament_tools_dir` property when invoking
|
||||
Gradle, either from the command line or from `local.properties`. This property must point to the
|
||||
distribution/install directory for desktop (produced by make/ninja install). This directory must
|
||||
contain `bin/matc` and `bin/cmgen`.
|
||||
|
||||
Example:
|
||||
```shell
|
||||
./gradlew -Pfilament_tools_dir=../../dist-release assembleDebug
|
||||
```
|
||||
|
||||
## Important: SDK location
|
||||
|
||||
Either ensure your `ANDROID_HOME` environment variable is set or make sure the root project
|
||||
contains a `local.properties` file with the `sdk.dir` property pointing to your installation of
|
||||
the Android SDK.
|
||||
|
||||
## Compiling
|
||||
|
||||
### Android Studio
|
||||
|
||||
You must use the latest stable release of Android Studio. To open the project, point Studio to the
|
||||
`android` folder. After opening the project and syncing to gradle, select the sample of your choice
|
||||
using the drop-down widget in the toolbar.
|
||||
|
||||
To compile and run each sample make sure you have selected the appropriate build variant
|
||||
(arm7, arm8, x86 or x86_64). If you are not sure you can simply select the "universal"
|
||||
variant which includes all the other ones.
|
||||
|
||||
### Command Line
|
||||
|
||||
From the `android` directory in the project root:
|
||||
|
||||
```shell
|
||||
./gradlew :samples:sample-hello-triangle:installDebug
|
||||
```
|
||||
|
||||
Replace `sample-hello-triangle` with your preferred project.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
id 'filament-tools-plugin'
|
||||
id 'filament-plugin'
|
||||
}
|
||||
|
||||
project.ext.isSample = true
|
||||
@@ -10,19 +10,27 @@ kotlin {
|
||||
jvmToolchain(versions.jdk)
|
||||
}
|
||||
|
||||
filamentTools {
|
||||
filament {
|
||||
cmgenArgs = "-q --format=ktx --size=256 --extract-blur=0.1 --deploy=src/main/assets/envs/default_env"
|
||||
iblInputFile = project.layout.projectDirectory.file("../../../third_party/environments/lightroom_14b.hdr")
|
||||
iblOutputDir = project.layout.projectDirectory.dir("src/main/assets/envs")
|
||||
}
|
||||
|
||||
// don't forget to update MainACtivity.kt when/if changing this.
|
||||
tasks.register('copyMesh', Copy) {
|
||||
tasks.register('copyDamagedHelmetGltf', Copy) {
|
||||
from file("../../../third_party/models/DamagedHelmet/DamagedHelmet.glb")
|
||||
into file("src/main/assets/models")
|
||||
rename {String fileName -> "helmet.glb"}
|
||||
}
|
||||
|
||||
// don't forget to update MainACtivity.kt when/if changing this.
|
||||
tasks.register('copyBusterGltf', Copy) {
|
||||
from "../../../third_party/models/BusterDrone"
|
||||
into "src/main/assets/models"
|
||||
}
|
||||
|
||||
preBuild.dependsOn copyMesh
|
||||
preBuild.dependsOn copyDamagedHelmetGltf
|
||||
preBuild.dependsOn copyBusterGltf
|
||||
|
||||
clean.doFirst {
|
||||
delete "src/main/assets"
|
||||
|
||||
@@ -389,9 +389,9 @@ class MainActivity : Activity() {
|
||||
viewerContent.assetLights = modelViewer.asset?.lightEntities
|
||||
automation.applySettings(modelViewer.engine, json, viewerContent)
|
||||
modelViewer.view.colorGrading = automation.getColorGrading(modelViewer.engine)
|
||||
modelViewer.cameraFocalLength = automation.viewerOptions.cameraFocalLength
|
||||
modelViewer.cameraNear = automation.viewerOptions.cameraNear
|
||||
modelViewer.cameraFar = automation.viewerOptions.cameraFar
|
||||
modelViewer.cameraFocalLength = automation.cameraSettings.focalLength
|
||||
modelViewer.cameraNear = automation.cameraSettings.near
|
||||
modelViewer.cameraFar = automation.cameraSettings.far
|
||||
updateRootTransform()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
id 'filament-tools-plugin'
|
||||
id 'filament-plugin'
|
||||
}
|
||||
|
||||
project.ext.isSample = true
|
||||
@@ -10,7 +10,7 @@ kotlin {
|
||||
jvmToolchain(versions.jdk)
|
||||
}
|
||||
|
||||
filamentTools {
|
||||
filament {
|
||||
materialInputDir = project.layout.projectDirectory.dir("src/main/materials")
|
||||
materialOutputDir = project.layout.projectDirectory.dir("src/main/assets/materials")
|
||||
}
|
||||
@@ -24,7 +24,7 @@ android {
|
||||
|
||||
compileSdkVersion versions.compileSdk
|
||||
defaultConfig {
|
||||
applicationId "com.google.android.filament.hellocamera"
|
||||
applicationId "com.google.android.filament.hellocam"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
id 'filament-tools-plugin'
|
||||
id 'filament-plugin'
|
||||
}
|
||||
|
||||
project.ext.isSample = true
|
||||
@@ -10,7 +10,7 @@ kotlin {
|
||||
jvmToolchain(versions.jdk)
|
||||
}
|
||||
|
||||
filamentTools {
|
||||
filament {
|
||||
materialInputDir = project.layout.projectDirectory.dir("src/main/materials")
|
||||
materialOutputDir = project.layout.projectDirectory.dir("src/main/assets/materials")
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
id 'filament-tools-plugin'
|
||||
id 'filament-plugin'
|
||||
}
|
||||
|
||||
project.ext.isSample = true
|
||||
@@ -10,7 +10,7 @@ kotlin {
|
||||
jvmToolchain(versions.jdk)
|
||||
}
|
||||
|
||||
filamentTools {
|
||||
filament {
|
||||
materialInputDir = project.layout.projectDirectory.dir("src/main/materials")
|
||||
materialOutputDir = project.layout.projectDirectory.dir("src/main/assets/materials")
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
id 'filament-tools-plugin'
|
||||
id 'filament-plugin'
|
||||
}
|
||||
|
||||
project.ext.isSample = true
|
||||
@@ -10,7 +10,7 @@ kotlin {
|
||||
jvmToolchain(versions.jdk)
|
||||
}
|
||||
|
||||
filamentTools {
|
||||
filament {
|
||||
materialInputDir = project.layout.projectDirectory.dir("src/main/materials")
|
||||
materialOutputDir = project.layout.projectDirectory.dir("src/main/assets/materials")
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
id 'filament-tools-plugin'
|
||||
id 'filament-plugin'
|
||||
}
|
||||
|
||||
project.ext.isSample = true
|
||||
@@ -10,7 +10,7 @@ kotlin {
|
||||
jvmToolchain(versions.jdk)
|
||||
}
|
||||
|
||||
filamentTools {
|
||||
filament {
|
||||
meshInputFile = project.layout.projectDirectory.file("../../../third_party/models/shader_ball/shader_ball.obj")
|
||||
meshOutputDir = project.layout.projectDirectory.dir("src/main/assets/models")
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
/.idea/caches
|
||||
/.idea/gradle.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
/src/main/assets
|
||||
.externalNativeBuild
|
||||
@@ -0,0 +1,52 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
id 'filament-plugin'
|
||||
}
|
||||
|
||||
project.ext.isSample = true
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(versions.jdk)
|
||||
}
|
||||
|
||||
filament {
|
||||
materialInputDir = project.layout.projectDirectory.dir("src/main/materials")
|
||||
materialOutputDir = project.layout.projectDirectory.dir("src/main/assets/materials")
|
||||
}
|
||||
|
||||
clean.doFirst {
|
||||
delete "src/main/assets"
|
||||
}
|
||||
android {
|
||||
namespace 'com.google.android.filament.materialinstancestress'
|
||||
|
||||
compileSdkVersion versions.compileSdk
|
||||
defaultConfig {
|
||||
applicationId "com.google.android.filament.materialinstancestress"
|
||||
minSdkVersion versions.minSdk
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
|
||||
// is not configuration-cache friendly yet; this is only useful for Play publication
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
|
||||
// We use the .filamat extension for materials compiled with matc
|
||||
// Telling aapt to not compress them allows to load them efficiently
|
||||
aaptOptions {
|
||||
noCompress 'filamat', 'ktx'
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility versions.jdk
|
||||
targetCompatibility versions.jdk
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation deps.kotlin
|
||||
implementation project(':filament-android')
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:exported="true"
|
||||
android:name=".MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,532 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.android.filament.materialinstancestress
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.app.Activity
|
||||
import android.opengl.Matrix
|
||||
import android.os.Bundle
|
||||
import android.view.Choreographer
|
||||
import android.view.Surface
|
||||
import android.view.SurfaceView
|
||||
import android.view.animation.LinearInterpolator
|
||||
|
||||
import com.google.android.filament.*
|
||||
import com.google.android.filament.RenderableManager.*
|
||||
import com.google.android.filament.VertexBuffer.*
|
||||
import com.google.android.filament.android.DisplayHelper
|
||||
import com.google.android.filament.android.FilamentHelper
|
||||
import com.google.android.filament.android.UiHelper
|
||||
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import java.nio.channels.Channels
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
|
||||
class MainActivity : Activity() {
|
||||
// Make sure to initialize Filament first
|
||||
// This loads the JNI library needed by most API calls
|
||||
companion object {
|
||||
init {
|
||||
Filament.init()
|
||||
}
|
||||
|
||||
private const val NUM_CUBES = 1000
|
||||
private const val GRID_SIZE = 10
|
||||
private const val SPACING = 2.5f
|
||||
}
|
||||
|
||||
// The View we want to render into
|
||||
private lateinit var surfaceView: SurfaceView
|
||||
// UiHelper is provided by Filament to manage SurfaceView and SurfaceTexture
|
||||
private lateinit var uiHelper: UiHelper
|
||||
// DisplayHelper is provided by Filament to manage the display
|
||||
private lateinit var displayHelper: DisplayHelper
|
||||
// Choreographer is used to schedule new frames
|
||||
private lateinit var choreographer: Choreographer
|
||||
|
||||
// Engine creates and destroys Filament resources
|
||||
// Each engine must be accessed from a single thread of your choosing
|
||||
// Resources cannot be shared across engines
|
||||
private lateinit var engine: Engine
|
||||
// A renderer instance is tied to a single surface (SurfaceView, TextureView, etc.)
|
||||
private lateinit var renderer: Renderer
|
||||
// A scene holds all the renderable, lights, etc. to be drawn
|
||||
private lateinit var scene: Scene
|
||||
// A view defines a viewport, a scene and a camera for rendering
|
||||
private lateinit var view: View
|
||||
// Should be pretty obvious :)
|
||||
private lateinit var camera: Camera
|
||||
|
||||
private lateinit var material: Material
|
||||
private lateinit var vertexBuffer: VertexBuffer
|
||||
private lateinit var indexBuffer: IndexBuffer
|
||||
|
||||
// Filament entity representing a renderable object
|
||||
@Entity private val renderables = IntArray(NUM_CUBES)
|
||||
private val materialInstances = arrayOfNulls<MaterialInstance>(NUM_CUBES)
|
||||
private val translationMatrices = Array(NUM_CUBES) { FloatArray(16) }
|
||||
|
||||
@Entity private var light = 0
|
||||
|
||||
// A swap chain is Filament's representation of a surface
|
||||
private var swapChain: SwapChain? = null
|
||||
|
||||
private var cameraRadius = 42.7f // Initial distance (calculated from original 15Y, 40Z)
|
||||
private var cameraTheta = 0.0f // Azimuth (horizontal) in radians
|
||||
private var cameraPhi = 0.358f // Elevation (vertical) in radians (from original 15Y, 40Z)
|
||||
|
||||
private var mLastX = 0.0f
|
||||
private var mLastY = 0.0f
|
||||
private val DRAG_SPEED = 0.005f
|
||||
|
||||
// Performs the rendering and schedules new frames
|
||||
private val frameScheduler = FrameCallback()
|
||||
|
||||
private val animator = ValueAnimator.ofFloat(0.0f, 360.0f)
|
||||
|
||||
private val animatorListener = object : ValueAnimator.AnimatorUpdateListener {
|
||||
// Pre-allocate matrix to avoid GC churn
|
||||
private val transformMatrix = FloatArray(16)
|
||||
|
||||
override fun onAnimationUpdate(a: ValueAnimator) {
|
||||
val tcm = engine.transformManager
|
||||
val time = a.animatedFraction // 0.0 to 1.0
|
||||
|
||||
// Calculate the sine wave scale factor.
|
||||
// sin(time * 2 * PI) oscillates between -1.0 and 1.0.
|
||||
// We multiply by 0.5f (so it's -0.5 to 0.5) and add 1.0f.
|
||||
// This makes the final 'scale' oscillate between 0.5 and 1.5.
|
||||
val scale = 1.0f + sin(time * 2.0 * Math.PI).toFloat() * 0.5f
|
||||
|
||||
val localScale = (scale - 0.5f) * 0.5f + 0.5f
|
||||
|
||||
for (i in 0 until NUM_CUBES) {
|
||||
val inst = tcm.getInstance(renderables[i])
|
||||
val materialInst = materialInstances[i] ?: continue
|
||||
|
||||
// Get the cube's base position from its stored matrix
|
||||
// The translation is in elements 12 (x), 13 (y), and 14 (z)
|
||||
val baseX = translationMatrices[i][12]
|
||||
val baseY = translationMatrices[i][13]
|
||||
val baseZ = translationMatrices[i][14]
|
||||
|
||||
// Set the transformMatrix to a new translation matrix,
|
||||
// scaled from the center (0,0,0)
|
||||
Matrix.setIdentityM(transformMatrix, 0)
|
||||
Matrix.translateM(transformMatrix, 0,
|
||||
baseX * scale,
|
||||
baseY * scale,
|
||||
baseZ * scale)
|
||||
Matrix.scaleM(transformMatrix, 0, localScale, localScale, localScale)
|
||||
|
||||
tcm.setTransform(inst, transformMatrix)
|
||||
|
||||
// Vary roughness
|
||||
val roughness = 0.5f + 0.5f * sin(time * 2.0 * Math.PI + i * 0.1).toFloat()
|
||||
materialInst.setParameter("roughness", roughness)
|
||||
|
||||
// Vary color (using linear RGB)
|
||||
val r = 0.5f + 0.5f * cos(time * 2.0 * Math.PI + i * 0.5).toFloat()
|
||||
val g = 0.5f + 0.5f * sin(time * 2.0 * Math.PI + i * 0.3).toFloat()
|
||||
val b = 0.5f + 0.5f * cos(time * 2.0 * Math.PI + i * 0.1).toFloat()
|
||||
materialInst.setParameter("baseColor", Colors.RgbType.LINEAR, r, g, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
surfaceView = SurfaceView(this)
|
||||
setContentView(surfaceView)
|
||||
|
||||
surfaceView.setOnTouchListener(touchListener)
|
||||
choreographer = Choreographer.getInstance()
|
||||
|
||||
displayHelper = DisplayHelper(this)
|
||||
|
||||
setupSurfaceView()
|
||||
setupFilament()
|
||||
setupView()
|
||||
setupScene()
|
||||
}
|
||||
|
||||
private fun setupSurfaceView() {
|
||||
uiHelper = UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK)
|
||||
uiHelper.renderCallback = SurfaceCallback()
|
||||
uiHelper.attachTo(surfaceView)
|
||||
}
|
||||
|
||||
private fun setupFilament() {
|
||||
engine = Engine.create()
|
||||
renderer = engine.createRenderer()
|
||||
scene = engine.createScene()
|
||||
view = engine.createView()
|
||||
camera = engine.createCamera(engine.entityManager.create())
|
||||
}
|
||||
|
||||
private fun setupView() {
|
||||
scene.skybox = Skybox.Builder().color(0.035f, 0.035f, 0.035f, 1.0f).build(engine)
|
||||
view.camera = camera
|
||||
view.scene = scene
|
||||
}
|
||||
|
||||
private fun setupScene() {
|
||||
loadMaterial()
|
||||
createMesh()
|
||||
|
||||
val tcm = engine.transformManager
|
||||
val center = (GRID_SIZE - 1) * 0.5f
|
||||
|
||||
for (i in 0 until NUM_CUBES) {
|
||||
// Calculate grid position
|
||||
val x = (i % GRID_SIZE) - center
|
||||
val y = ((i / GRID_SIZE) % GRID_SIZE) - center
|
||||
val z = (i / (GRID_SIZE * GRID_SIZE)) - center
|
||||
|
||||
val posX = x * SPACING
|
||||
val posY = y * SPACING
|
||||
val posZ = z * SPACING
|
||||
|
||||
// Create material instance for this cube
|
||||
materialInstances[i] = material.createInstance()
|
||||
|
||||
// Set initial parameters (will be overwritten by animator, but good practice)
|
||||
materialInstances[i]?.setParameter("baseColor", Colors.RgbType.SRGB, 1.0f, 0.85f, 0.57f)
|
||||
materialInstances[i]?.setParameter("metallic", 0.0f)
|
||||
materialInstances[i]?.setParameter("roughness", 0.3f)
|
||||
|
||||
// Create the renderable entity
|
||||
renderables[i] = EntityManager.get().create()
|
||||
|
||||
// Create the renderable component
|
||||
RenderableManager.Builder(1)
|
||||
.boundingBox(Box(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f))
|
||||
.geometry(0, PrimitiveType.TRIANGLES, vertexBuffer, indexBuffer, 0, 6 * 6)
|
||||
.material(0, materialInstances[i]!!)
|
||||
.build(engine, renderables[i])
|
||||
|
||||
// Add to scene
|
||||
scene.addEntity(renderables[i])
|
||||
|
||||
// Set its initial transform
|
||||
// Store the base translation matrix
|
||||
Matrix.setIdentityM(translationMatrices[i], 0)
|
||||
Matrix.translateM(translationMatrices[i], 0, posX, posY, posZ)
|
||||
|
||||
// Get the transform component instance
|
||||
val inst = tcm.getInstance(renderables[i])
|
||||
tcm.setTransform(inst, translationMatrices[i])
|
||||
}
|
||||
|
||||
// We now need a light, let's create a directional light
|
||||
light = EntityManager.get().create()
|
||||
|
||||
val (r, g, b) = Colors.cct(5_500.0f)
|
||||
LightManager.Builder(LightManager.Type.DIRECTIONAL)
|
||||
.color(r, g, b)
|
||||
.intensity(110_000.0f)
|
||||
.direction(0.0f, -0.5f, -1.0f)
|
||||
.castShadows(true)
|
||||
.build(engine, light)
|
||||
|
||||
scene.addEntity(light)
|
||||
|
||||
camera.setExposure(16.0f, 1.0f / 125.0f, 100.0f)
|
||||
|
||||
updateCamera()
|
||||
|
||||
startAnimation()
|
||||
}
|
||||
|
||||
private fun loadMaterial() {
|
||||
readUncompressedAsset("materials/lit.filamat").let {
|
||||
material = Material.Builder().payload(it, it.remaining()).build(engine)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createMesh() {
|
||||
val floatSize = 4
|
||||
val shortSize = 2
|
||||
val vertexSize = 3 * floatSize + 4 * floatSize
|
||||
|
||||
// A vertex is a position + a tangent frame:
|
||||
// 3 floats for XYZ position, 4 floats for normal+tangents (quaternion)
|
||||
@Suppress("ArrayInDataClass")
|
||||
data class Vertex(val x: Float, val y: Float, val z: Float, val tangents: FloatArray)
|
||||
fun ByteBuffer.put(v: Vertex): ByteBuffer {
|
||||
putFloat(v.x)
|
||||
putFloat(v.y)
|
||||
putFloat(v.z)
|
||||
v.tangents.forEach { putFloat(it) }
|
||||
return this
|
||||
}
|
||||
|
||||
// 6 faces, 4 vertices per face
|
||||
val vertexCount = 6 * 4
|
||||
|
||||
// Create tangent frames, one per face
|
||||
val tfPX = FloatArray(4)
|
||||
val tfNX = FloatArray(4)
|
||||
val tfPY = FloatArray(4)
|
||||
val tfNY = FloatArray(4)
|
||||
val tfPZ = FloatArray(4)
|
||||
val tfNZ = FloatArray(4)
|
||||
|
||||
MathUtils.packTangentFrame( 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, tfPX)
|
||||
MathUtils.packTangentFrame( 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, tfNX)
|
||||
MathUtils.packTangentFrame(-1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, tfPY)
|
||||
MathUtils.packTangentFrame(-1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 0.0f, tfNY)
|
||||
MathUtils.packTangentFrame( 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, tfPZ)
|
||||
MathUtils.packTangentFrame( 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, tfNZ)
|
||||
|
||||
val vertexData = ByteBuffer.allocate(vertexCount * vertexSize)
|
||||
// It is important to respect the native byte order
|
||||
.order(ByteOrder.nativeOrder())
|
||||
// Face -Z
|
||||
.put(Vertex(-1.0f, -1.0f, -1.0f, tfNZ))
|
||||
.put(Vertex(-1.0f, 1.0f, -1.0f, tfNZ))
|
||||
.put(Vertex( 1.0f, 1.0f, -1.0f, tfNZ))
|
||||
.put(Vertex( 1.0f, -1.0f, -1.0f, tfNZ))
|
||||
// Face +X
|
||||
.put(Vertex( 1.0f, -1.0f, -1.0f, tfPX))
|
||||
.put(Vertex( 1.0f, 1.0f, -1.0f, tfPX))
|
||||
.put(Vertex( 1.0f, 1.0f, 1.0f, tfPX))
|
||||
.put(Vertex( 1.0f, -1.0f, 1.0f, tfPX))
|
||||
// Face +Z
|
||||
.put(Vertex(-1.0f, -1.0f, 1.0f, tfPZ))
|
||||
.put(Vertex( 1.0f, -1.0f, 1.0f, tfPZ))
|
||||
.put(Vertex( 1.0f, 1.0f, 1.0f, tfPZ))
|
||||
.put(Vertex(-1.0f, 1.0f, 1.0f, tfPZ))
|
||||
// Face -X
|
||||
.put(Vertex(-1.0f, -1.0f, 1.0f, tfNX))
|
||||
.put(Vertex(-1.0f, 1.0f, 1.0f, tfNX))
|
||||
.put(Vertex(-1.0f, 1.0f, -1.0f, tfNX))
|
||||
.put(Vertex(-1.0f, -1.0f, -1.0f, tfNX))
|
||||
// Face -Y
|
||||
.put(Vertex(-1.0f, -1.0f, 1.0f, tfNY))
|
||||
.put(Vertex(-1.0f, -1.0f, -1.0f, tfNY))
|
||||
.put(Vertex( 1.0f, -1.0f, -1.0f, tfNY))
|
||||
.put(Vertex( 1.0f, -1.0f, 1.0f, tfNY))
|
||||
// Face +Y
|
||||
.put(Vertex(-1.0f, 1.0f, -1.0f, tfPY))
|
||||
.put(Vertex(-1.0f, 1.0f, 1.0f, tfPY))
|
||||
.put(Vertex( 1.0f, 1.0f, 1.0f, tfPY))
|
||||
.put(Vertex( 1.0f, 1.0f, -1.0f, tfPY))
|
||||
// Make sure the cursor is pointing in the right place in the byte buffer
|
||||
.flip()
|
||||
|
||||
// Declare the layout of our mesh
|
||||
vertexBuffer = VertexBuffer.Builder()
|
||||
.bufferCount(1)
|
||||
.vertexCount(vertexCount)
|
||||
// Because we interleave position and color data we must specify offset and stride
|
||||
// We could use de-interleaved data by declaring two buffers and giving each
|
||||
// attribute a different buffer index
|
||||
.attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, vertexSize)
|
||||
.attribute(VertexAttribute.TANGENTS, 0, AttributeType.FLOAT4, 3 * floatSize, vertexSize)
|
||||
.build(engine)
|
||||
|
||||
// Feed the vertex data to the mesh
|
||||
// We only set 1 buffer because the data is interleaved
|
||||
vertexBuffer.setBufferAt(engine, 0, vertexData)
|
||||
|
||||
// Create the indices
|
||||
val indexData = ByteBuffer.allocate(6 * 2 * 3 * shortSize)
|
||||
.order(ByteOrder.nativeOrder())
|
||||
repeat(6) {
|
||||
val i = (it * 4).toShort()
|
||||
indexData
|
||||
.putShort(i).putShort((i + 1).toShort()).putShort((i + 2).toShort())
|
||||
.putShort(i).putShort((i + 2).toShort()).putShort((i + 3).toShort())
|
||||
}
|
||||
indexData.flip()
|
||||
|
||||
// 6 faces, 2 triangles per face,
|
||||
indexBuffer = IndexBuffer.Builder()
|
||||
.indexCount(vertexCount * 2)
|
||||
.bufferType(IndexBuffer.Builder.IndexType.USHORT)
|
||||
.build(engine)
|
||||
indexBuffer.setBuffer(engine, indexData)
|
||||
}
|
||||
|
||||
private fun updateCamera() {
|
||||
// Calculate eye position from spherical coordinates
|
||||
val eyeY = cameraRadius * sin(cameraPhi)
|
||||
val horizontalRadius = cameraRadius * cos(cameraPhi)
|
||||
val eyeX = horizontalRadius * sin(cameraTheta)
|
||||
val eyeZ = horizontalRadius * cos(cameraTheta)
|
||||
|
||||
// Point the camera at the center (0,0,0)
|
||||
camera.lookAt(eyeX.toDouble(), eyeY.toDouble(), eyeZ.toDouble(), // eye
|
||||
0.0, 0.0, 0.0, // center
|
||||
0.0, 1.0, 0.0) // up
|
||||
}
|
||||
|
||||
private val touchListener = object : android.view.View.OnTouchListener {
|
||||
override fun onTouch(v: android.view.View?, event: android.view.MotionEvent?): Boolean {
|
||||
if (event == null) return false
|
||||
when (event.action) {
|
||||
android.view.MotionEvent.ACTION_DOWN -> {
|
||||
mLastX = event.x
|
||||
mLastY = event.y
|
||||
return true
|
||||
}
|
||||
android.view.MotionEvent.ACTION_MOVE -> {
|
||||
val dx = event.x - mLastX
|
||||
val dy = event.y - mLastY
|
||||
|
||||
cameraTheta -= dx * DRAG_SPEED
|
||||
cameraPhi += dy * DRAG_SPEED
|
||||
|
||||
// Clamp vertical angle to avoid flipping over
|
||||
cameraPhi = cameraPhi.coerceIn(-1.5f, 1.5f)
|
||||
|
||||
updateCamera()
|
||||
|
||||
mLastX = event.x
|
||||
mLastY = event.y
|
||||
return true
|
||||
}
|
||||
}
|
||||
return v?.onTouchEvent(event) ?: false
|
||||
}
|
||||
}
|
||||
|
||||
private fun startAnimation() {
|
||||
animator.interpolator = LinearInterpolator()
|
||||
animator.duration = 6000
|
||||
animator.repeatMode = ValueAnimator.RESTART
|
||||
animator.repeatCount = ValueAnimator.INFINITE
|
||||
animator.addUpdateListener(animatorListener)
|
||||
animator.start()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
choreographer.postFrameCallback(frameScheduler)
|
||||
animator.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
choreographer.removeFrameCallback(frameScheduler)
|
||||
animator.cancel()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
|
||||
// Stop the animation and any pending frame
|
||||
choreographer.removeFrameCallback(frameScheduler)
|
||||
animator.cancel();
|
||||
|
||||
// Always detach the surface before destroying the engine
|
||||
uiHelper.detach()
|
||||
|
||||
// Cleanup all resources
|
||||
engine.destroyEntity(light)
|
||||
|
||||
// Destroy all 1000 entities and material instances
|
||||
for (i in 0 until NUM_CUBES) {
|
||||
engine.destroyEntity(renderables[i])
|
||||
materialInstances[i]?.let { engine.destroyMaterialInstance(it) }
|
||||
}
|
||||
|
||||
engine.destroyRenderer(renderer)
|
||||
engine.destroyVertexBuffer(vertexBuffer)
|
||||
engine.destroyIndexBuffer(indexBuffer)
|
||||
engine.destroyMaterial(material)
|
||||
engine.destroyView(view)
|
||||
engine.destroyScene(scene)
|
||||
engine.destroyCameraComponent(camera.entity)
|
||||
|
||||
// Engine.destroyEntity() destroys Filament related resources only
|
||||
// (components), not the entity itself
|
||||
val entityManager = EntityManager.get()
|
||||
entityManager.destroy(light)
|
||||
for (entity in renderables) {
|
||||
entityManager.destroy(entity)
|
||||
}
|
||||
entityManager.destroy(camera.entity)
|
||||
|
||||
// Destroying the engine will free up any resource you may have forgotten
|
||||
// to destroy, but it's recommended to do the cleanup properly
|
||||
engine.destroy()
|
||||
}
|
||||
|
||||
inner class FrameCallback : Choreographer.FrameCallback {
|
||||
override fun doFrame(frameTimeNanos: Long) {
|
||||
// Schedule the next frame
|
||||
choreographer.postFrameCallback(this)
|
||||
|
||||
// This check guarantees that we have a swap chain
|
||||
if (uiHelper.isReadyToRender) {
|
||||
// If beginFrame() returns false you should skip the frame
|
||||
// This means you are sending frames too quickly to the GPU
|
||||
if (renderer.beginFrame(swapChain!!, frameTimeNanos)) {
|
||||
renderer.render(view)
|
||||
renderer.endFrame()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner class SurfaceCallback : UiHelper.RendererCallback {
|
||||
override fun onNativeWindowChanged(surface: Surface) {
|
||||
swapChain?.let { engine.destroySwapChain(it) }
|
||||
swapChain = engine.createSwapChain(surface)
|
||||
displayHelper.attach(renderer, surfaceView.display)
|
||||
}
|
||||
|
||||
override fun onDetachedFromSurface() {
|
||||
displayHelper.detach()
|
||||
swapChain?.let {
|
||||
engine.destroySwapChain(it)
|
||||
// Required to ensure we don't return before Filament is done executing the
|
||||
// destroySwapChain command, otherwise Android might destroy the Surface
|
||||
// too early
|
||||
engine.flushAndWait()
|
||||
swapChain = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResized(width: Int, height: Int) {
|
||||
val aspect = width.toDouble() / height.toDouble()
|
||||
camera.setProjection(45.0, aspect, 0.1, 100.0, Camera.Fov.VERTICAL)
|
||||
|
||||
view.viewport = Viewport(0, 0, width, height)
|
||||
|
||||
FilamentHelper.synchronizePendingFrames(engine)
|
||||
}
|
||||
}
|
||||
|
||||
private fun readUncompressedAsset(assetName: String): ByteBuffer {
|
||||
assets.openFd(assetName).use { fd ->
|
||||
val input = fd.createInputStream()
|
||||
val dst = ByteBuffer.allocate(fd.length.toInt())
|
||||
|
||||
val src = Channels.newChannel(input)
|
||||
src.read(dst)
|
||||
src.close()
|
||||
|
||||
return dst.apply { rewind() }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Simple lit material that defines 3 parameters:
|
||||
// - baseColor
|
||||
// - roughness
|
||||
// - metallic
|
||||
//
|
||||
// These parameters can be used by the application to change the appearance of the material.
|
||||
//
|
||||
// This source material must be compiled to a binary material using the matc tool.
|
||||
// The command used to compile this material is:
|
||||
// matc -p mobile -a opengl -o app/src/main/assets/lit.filamat app/src/materials/lit.mat
|
||||
//
|
||||
// See build.gradle for an example of how to compile materials automatically
|
||||
// Please refer to the documentation for more information about matc and the materials system.
|
||||
|
||||
material {
|
||||
name : lit,
|
||||
|
||||
// Dynamic lighting is enabled on this material
|
||||
shadingModel : lit,
|
||||
|
||||
// We don't need to declare a "requires" array, lit materials
|
||||
// always requires the "tangents" vertex attribute (the normal
|
||||
// is required for lighting, tangent/bitangent for normal mapping
|
||||
// and anisotropy)
|
||||
|
||||
// List of parameters exposed by this material
|
||||
parameters : [
|
||||
// The color must be passed in linear space, not sRGB
|
||||
{
|
||||
type : float3,
|
||||
name : baseColor
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : roughness
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : metallic
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
fragment {
|
||||
void material(inout MaterialInputs material) {
|
||||
prepareMaterial(material);
|
||||
|
||||
// Nothing fancy here, we simply copy the parameters
|
||||
material.baseColor.rgb = materialParams.baseColor;
|
||||
material.roughness = materialParams.roughness;
|
||||
material.metallic = materialParams.metallic;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="78.5885"
|
||||
android:endY="90.9159"
|
||||
android:startX="48.7653"
|
||||
android:startY="61.0927"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0"/>
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,171 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path
|
||||
android:fillColor="#26A69A"
|
||||
android:pathData="M0,0h108v108h-108z"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 10 KiB |