mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-09-22 23:18:01 +03:00
Compare commits
1043
Commits
alpha-1
...
04333ad689
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04333ad689 | ||
|
|
960c97ecc9 | ||
|
|
e61f4b3c42 | ||
|
|
03f9e7e87e | ||
|
|
87e4bebb12 | ||
|
|
3a0dc49340 | ||
|
|
e93cddad76 | ||
|
|
461eca1a85 | ||
|
|
6ea8af95ba | ||
|
|
167d75d092 | ||
|
|
275d71712e | ||
|
|
d68baa74a0 | ||
|
|
267f7bb762 | ||
|
|
addc26ca45 | ||
|
|
bc745035e1 | ||
|
|
11ad7a583a | ||
|
|
9654950ab4 | ||
|
|
021187752b | ||
|
|
5cc163ab16 | ||
|
|
e14b4bca79 | ||
|
|
02f29b6c6d | ||
|
|
fa09b0059f | ||
|
|
4233c7d294 | ||
|
|
b30a107fda | ||
|
|
bf3430dbad | ||
|
|
135cae216f | ||
|
|
05820dc536 | ||
|
|
e76762d11d | ||
|
|
5fb3c49f75 | ||
|
|
5d032d739e | ||
|
|
e3bf653165 | ||
|
|
a19c200870 | ||
|
|
5cbc697f23 | ||
|
|
4e20a499d4 | ||
|
|
0d4eb2a2a7 | ||
|
|
d800e12f59 | ||
|
|
b1f8948b74 | ||
|
|
ebc319994e | ||
|
|
9bb871d9fd | ||
|
|
62d38aaf59 | ||
|
|
5bb36c7524 | ||
|
|
4dc9792632 | ||
|
|
a7e973c0a9 | ||
|
|
33f1295237 | ||
|
|
e699fb0aa2 | ||
|
|
4e0dd82776 | ||
|
|
77d9e77053 | ||
|
|
d65a1e6d9b | ||
|
|
68e8257c27 | ||
|
|
8e339d1537 | ||
|
|
a2524bafd5 | ||
|
|
f146320e36 | ||
|
|
09a0e3329a | ||
|
|
b68e1622fd | ||
|
|
433e3be3e6 | ||
|
|
d5b27a0a47 | ||
|
|
a38c55602d | ||
|
|
b3bcd79bae | ||
|
|
f0c4db718d | ||
|
|
d17fc30731 | ||
|
|
ca2254360c | ||
|
|
81f9113906 | ||
|
|
d63e71e85d | ||
|
|
6d05546c2c | ||
|
|
e260b82828 | ||
|
|
c0cdf4f070 | ||
|
|
74461f5b9c | ||
|
|
d8479ad80a | ||
|
|
e8bd5ab036 | ||
|
|
05fee4ba83 | ||
|
|
bf3b3416fe | ||
|
|
fb3d0bbd3e | ||
|
|
507b65fc9c | ||
|
|
24df15b9e9 | ||
|
|
fab789df65 | ||
|
|
3012faa6ff | ||
|
|
6dbb5baf19 | ||
|
|
db8279ebc3 | ||
|
|
250bc82818 | ||
|
|
4843e4e87a | ||
|
|
1051294247 | ||
|
|
fa98a76c42 | ||
|
|
d073875d94 | ||
|
|
2b67a9e000 | ||
|
|
b62edb7188 | ||
|
|
85547ef4f4 | ||
|
|
f108b7f436 | ||
|
|
90c3ab5008 | ||
|
|
7b9565fe25 | ||
|
|
4dcc50d6ca | ||
|
|
5c47ad5aae | ||
|
|
062713f70e | ||
|
|
0e8bd36407 | ||
|
|
28554137cc | ||
|
|
32024bc805 | ||
|
|
ec9bd95dc9 | ||
|
|
8dee7d8658 | ||
|
|
0f73ded483 | ||
|
|
847987ec1b | ||
|
|
ec4b608877 | ||
|
|
12735d5c8c | ||
|
|
22d58d977a | ||
|
|
20e0d00661 | ||
|
|
a706a41dcb | ||
|
|
bf0b2bb44f | ||
|
|
916daec6b2 | ||
|
|
1fcccb766c | ||
|
|
aa112849de | ||
|
|
8ca1a31ccc | ||
|
|
2c622bb331 | ||
|
|
c01a27583c | ||
|
|
ced4daa331 | ||
|
|
9eee82c13b | ||
|
|
f53ec98d53 | ||
|
|
32f4253948 | ||
|
|
cb0b969492 | ||
|
|
0030e28471 | ||
|
|
224db2f9cc | ||
|
|
33d07c190e | ||
|
|
a4dd6a42df | ||
|
|
da268288c3 | ||
|
|
f535223988 | ||
|
|
f9087eb7ac | ||
|
|
636743478c | ||
|
|
a2f087b7f9 | ||
|
|
07bd9c7438 | ||
|
|
028c907a12 | ||
|
|
c45bb3a736 | ||
|
|
484f383826 | ||
|
|
af12e19859 | ||
|
|
e83cb1c91e | ||
|
|
4c4a62e1ed | ||
|
|
f01e41be12 | ||
|
|
6bb00a268c | ||
|
|
9ed1f52627 | ||
|
|
59789c0a8a | ||
|
|
13d0c51351 | ||
|
|
46bdbb05ef | ||
|
|
234b04b460 | ||
|
|
ae9544a1ac | ||
|
|
6509dca693 | ||
|
|
bf455de35d | ||
|
|
511f6a4bfa | ||
|
|
4239345289 | ||
|
|
955950198e | ||
|
|
63ab438391 | ||
|
|
1dd37559bb | ||
|
|
f52f4bd1f0 | ||
|
|
1b87d3f9a5 | ||
|
|
15f70f29a0 | ||
|
|
df6998c0f9 | ||
|
|
2f436ce5bf | ||
|
|
c4f89d040a | ||
|
|
93c790dcc6 | ||
|
|
88f199a8f2 | ||
|
|
28c8c277c9 | ||
|
|
e5087f0757 | ||
|
|
6dc62bffc8 | ||
|
|
0ea5d90797 | ||
|
|
d20f52cf79 | ||
|
|
dcbd149409 | ||
|
|
1655c8a8e0 | ||
|
|
2a99c75e4d | ||
|
|
70336546ed | ||
|
|
cb24db29c2 | ||
|
|
03ef51e76a | ||
|
|
a0d78a024c | ||
|
|
dc105aeb16 | ||
|
|
c877e261c5 | ||
|
|
39c0094ce8 | ||
|
|
93ca13a50b | ||
|
|
c540eb9796 | ||
|
|
753e202cea | ||
|
|
f884a58a77 | ||
|
|
2bc9a3d2cb | ||
|
|
1e29930f4c | ||
|
|
887eebf31d | ||
|
|
96d46069ce | ||
|
|
9192bc80c5 | ||
|
|
003ab16c72 | ||
|
|
03062d5d61 | ||
|
|
0bf5aaa8c8 | ||
|
|
fe952354c5 | ||
|
|
f43ca3a31a | ||
|
|
52f2948e19 | ||
|
|
92b2dd1990 | ||
|
|
f8ce39a324 | ||
|
|
9562e0d1c3 | ||
|
|
623b3d3407 | ||
|
|
39886c30bd | ||
|
|
1ad496c28d | ||
|
|
039f1c9a2e | ||
|
|
caa593d00b | ||
|
|
38610d93bb | ||
|
|
65fa898d9f | ||
|
|
547e8158a2 | ||
|
|
197b89620c | ||
|
|
ecc04bc9bb | ||
|
|
65ff73ca06 | ||
|
|
2ad0de6663 | ||
|
|
5f318e859e | ||
|
|
efddc4f128 | ||
|
|
d93bb91d72 | ||
|
|
49ee259d2c | ||
|
|
3c362e9db6 | ||
|
|
1ad7cbb2c5 | ||
|
|
a476b1d892 | ||
|
|
d4121032db | ||
|
|
9474e321e1 | ||
|
|
d130f38e74 | ||
|
|
e66fe928e5 | ||
|
|
134168d864 | ||
|
|
6e755b21ef | ||
|
|
a5d488564a | ||
|
|
205e0a3ed0 | ||
|
|
b758a04760 | ||
|
|
9c962e3467 | ||
|
|
02129edc10 | ||
|
|
5ac79311b9 | ||
|
|
75cfc9f18d | ||
|
|
eaa7f3d8d0 | ||
|
|
0d7ef9b577 | ||
|
|
4a7fc4d12a | ||
|
|
e662955a1d | ||
|
|
a3c23387f4 | ||
|
|
f23125cb77 | ||
|
|
30ac11e74b | ||
|
|
f40132497c | ||
|
|
7540712de1 | ||
|
|
6a75bc5a6b | ||
|
|
a1a809faef | ||
|
|
3929de81f1 | ||
|
|
65ccb29423 | ||
|
|
9f3af7b256 | ||
|
|
249bc06f53 | ||
|
|
621eb49be8 | ||
|
|
5f13b3e9d0 | ||
|
|
15cfc5463b | ||
|
|
ed2239fb1b | ||
|
|
5980e49a96 | ||
|
|
a6871623f2 | ||
|
|
477bbdb949 | ||
|
|
0baa493f79 | ||
|
|
2c51bec001 | ||
|
|
d6b5bde80c | ||
|
|
2510fa6b9b | ||
|
|
8fecf72281 | ||
|
|
93ce40bcdd | ||
|
|
4f2d367b19 | ||
|
|
74bdbd4f37 | ||
|
|
2c8fc425db | ||
|
|
f59158648f | ||
|
|
484b643f02 | ||
|
|
d1fdab4cde | ||
|
|
43c6a2804c | ||
|
|
180c81e5f5 | ||
|
|
e29739f0be | ||
|
|
1e682bad09 | ||
|
|
0a4981533b | ||
|
|
998ab23dab | ||
|
|
dc8342cbab | ||
|
|
8d9eb931f6 | ||
|
|
1621378ba1 | ||
|
|
d3cb6b4001 | ||
|
|
2198bf5970 | ||
|
|
0a152f0e34 | ||
|
|
2a3b636bb0 | ||
|
|
1506d085db | ||
|
|
729957b842 | ||
|
|
6f661fb370 | ||
|
|
57a73cd924 | ||
|
|
6424efa6ab | ||
|
|
dc4a6f1dfa | ||
|
|
b53c511b4a | ||
|
|
0c75f21985 | ||
|
|
b4c3818d6b | ||
|
|
fe61704b90 | ||
|
|
199c91dd6c | ||
|
|
828971ce19 | ||
|
|
e1b1208438 | ||
|
|
b42e59c494 | ||
|
|
0d689ac1c2 | ||
|
|
aba2a53afe | ||
|
|
30d599f161 | ||
|
|
7817592728 | ||
|
|
85244a304a | ||
|
|
f2d26c2a46 | ||
|
|
d48f637744 | ||
|
|
008d43be1b | ||
|
|
05b78e6dc6 | ||
|
|
b28624203a | ||
|
|
e943ef8ea9 | ||
|
|
d547919713 | ||
|
|
ddf6ac5088 | ||
|
|
037616f38d | ||
|
|
af70ee09de | ||
|
|
8d67c87f6f | ||
|
|
94c8329d7a | ||
|
|
2c771acfe6 | ||
|
|
eef99c0910 | ||
|
|
44c73f644f | ||
|
|
6cada80d34 | ||
|
|
96a560f6a2 | ||
|
|
e13583a75e | ||
|
|
f0e25e53d7 | ||
|
|
eb0c642ab1 | ||
|
|
acb4d92705 | ||
|
|
b6c523f4f3 | ||
|
|
6b9edcdd69 | ||
|
|
538480b348 | ||
|
|
d9be9a7ad3 | ||
|
|
554f742a00 | ||
|
|
eda8ad35f3 | ||
|
|
59a66a8a5a | ||
|
|
02eab7149c | ||
|
|
1042b328a1 | ||
|
|
f3484643a6 | ||
|
|
46469e8732 | ||
|
|
62ec0be41a | ||
|
|
0972c60558 | ||
|
|
ba2805b09f | ||
|
|
20711ffa57 | ||
|
|
7adbf9d6e6 | ||
|
|
2dcc9b2aa9 | ||
|
|
441419d0f9 | ||
|
|
a32e3b9401 | ||
|
|
d579374a9c | ||
|
|
f0a54b337d | ||
|
|
80f4246095 | ||
|
|
b71b61c1d6 | ||
|
|
8c791db5d4 | ||
|
|
d404324e7b | ||
|
|
aeb5dbb08c | ||
|
|
9db75ab870 | ||
|
|
67387e88d0 | ||
|
|
d57c8cc9f4 | ||
|
|
677f20628c | ||
|
|
e68c41a0b9 | ||
|
|
fbd105b697 | ||
|
|
3c7634522a | ||
|
|
5ed05ba685 | ||
|
|
11fe6f6dda | ||
|
|
3057c2a96b | ||
|
|
5041d817d1 | ||
|
|
cb4e44a551 | ||
|
|
74ae7223c3 | ||
|
|
7dc6760928 | ||
|
|
e1ca00a27b | ||
|
|
7989ffc542 | ||
|
|
6e14285038 | ||
|
|
55a214553a | ||
|
|
bf9900d8aa | ||
|
|
e00ba69546 | ||
|
|
b88abe375a | ||
|
|
c86977ad1b | ||
|
|
ffb9fbae31 | ||
|
|
3ef7ce926b | ||
|
|
bf6dcef2a8 | ||
|
|
8a62ec6dc5 | ||
|
|
152af8685f | ||
|
|
c5d6afdd08 | ||
|
|
9d6861b10b | ||
|
|
c7ba379173 | ||
|
|
f4d4e8f90a | ||
|
|
379bbfeb07 | ||
|
|
117c72cf09 | ||
|
|
3577079a04 | ||
|
|
8d120b8a4d | ||
|
|
e2440a5a44 | ||
|
|
bba3f96a1b | ||
|
|
f4f7adb618 | ||
|
|
8730475f1c | ||
|
|
a84d78ac5d | ||
|
|
92322c27f0 | ||
|
|
50cb0f8063 | ||
|
|
13e76f67b3 | ||
|
|
37f1bc2723 | ||
|
|
0d7830f24c | ||
|
|
c70e5e028e | ||
|
|
a4b4e7f5bb | ||
|
|
9ccc4ab0c7 | ||
|
|
13a00c131d | ||
|
|
929a2dd3e7 | ||
|
|
b5b92bc367 | ||
|
|
e5fadf2b73 | ||
|
|
2713290ac9 | ||
|
|
3bfb92e1fb | ||
|
|
378450c625 | ||
|
|
919b86916c | ||
|
|
6fbd147c54 | ||
|
|
bcae91003a | ||
|
|
7e584c5fd2 | ||
|
|
b0c7aad6c3 | ||
|
|
5d0819a115 | ||
|
|
724bbcdfdd | ||
|
|
dc0daf3329 | ||
|
|
5d271deec0 | ||
|
|
2ede7e6bb1 | ||
|
|
e6d069b757 | ||
|
|
d6abcc2f92 | ||
|
|
de0646e430 | ||
|
|
c04fd2280d | ||
|
|
71c58e85ae | ||
|
|
61650308b7 | ||
|
|
bb26326929 | ||
|
|
f50c9f6d77 | ||
|
|
ea081e0eb7 | ||
|
|
e8ecad598a | ||
|
|
cb551e8f06 | ||
|
|
002bca4e24 | ||
|
|
0bbd9171b5 | ||
|
|
7dfc9926be | ||
|
|
1dd10ecc45 | ||
|
|
ba11900350 | ||
|
|
508839ed4c | ||
|
|
55f826db36 | ||
|
|
882354c692 | ||
|
|
071d7d2137 | ||
|
|
eddac54fa0 | ||
|
|
2e1059cbac | ||
|
|
f9177b89b1 | ||
|
|
7bae2cdf8a | ||
|
|
769bdca725 | ||
|
|
6517303e2a | ||
|
|
0e388c74c0 | ||
|
|
7ff5ec89e7 | ||
|
|
33403cbd3d | ||
|
|
40fe2068e7 | ||
|
|
40914a7cef | ||
|
|
0946900e02 | ||
|
|
56d9b607b2 | ||
|
|
155c801804 | ||
|
|
4942e41fb8 | ||
|
|
ac8a37f67b | ||
|
|
331563a165 | ||
|
|
c30510e688 | ||
|
|
81c0c494b0 | ||
|
|
e9bec3c2fe | ||
|
|
298e9bddf5 | ||
|
|
bf3c4feb9d | ||
|
|
6a36fc8f53 | ||
|
|
45876a9921 | ||
|
|
2461c1e52d | ||
|
|
41c7830558 | ||
|
|
009bafae40 | ||
|
|
e891e74bcf | ||
|
|
5576f14a0f | ||
|
|
90075f2e25 | ||
|
|
c0cc3f729b | ||
|
|
4d303b291d | ||
|
|
e73abc3cd6 | ||
|
|
579c158477 | ||
|
|
842bac92f3 | ||
|
|
dc8a9379d0 | ||
|
|
f8f02a1c99 | ||
|
|
d7d9bb70f2 | ||
|
|
dcdb82e2a8 | ||
|
|
f1a2904262 | ||
|
|
ecd71896b3 | ||
|
|
d7b7879747 | ||
|
|
1cbf6125ce | ||
|
|
9b03741288 | ||
|
|
4e04db0045 | ||
|
|
abd3a276a3 | ||
|
|
9ca3d3e9c6 | ||
|
|
d8ad22b638 | ||
|
|
3f02a22a8d | ||
|
|
bff491bbe9 | ||
|
|
f566539e4f | ||
|
|
c04aa8a844 | ||
|
|
87b1e2abe2 | ||
|
|
a0a68a022c | ||
|
|
3fb2dc3650 | ||
|
|
c1ce2c88c6 | ||
|
|
222d2700a9 | ||
|
|
dd18d68be5 | ||
|
|
db7c3a608c | ||
|
|
899c6c02a1 | ||
|
|
8ff5ffe30b | ||
|
|
ff9175c1fd | ||
|
|
74666c9b90 | ||
|
|
68bd33e099 | ||
|
|
84ab9a41da | ||
|
|
22a06b1da9 | ||
|
|
128b2fd6d5 | ||
|
|
09dc9470ae | ||
|
|
0f5a39c292 | ||
|
|
73f7acde40 | ||
|
|
3afe4e4453 | ||
|
|
8f69f1911a | ||
|
|
e466b3f06b | ||
|
|
a2ae28c1c1 | ||
|
|
41626b5018 | ||
|
|
9a76d472db | ||
|
|
964bba1823 | ||
|
|
49b85f885d | ||
|
|
93e9357f84 | ||
|
|
2a7f58c187 | ||
|
|
08e47b8d04 | ||
|
|
217c4d5d65 | ||
|
|
ea762de937 | ||
|
|
e6105ca654 | ||
|
|
7a87ffc20a | ||
|
|
f53ad7d557 | ||
|
|
ffe4d0d4ae | ||
|
|
9e2f03d8be | ||
|
|
489cfe5a96 | ||
|
|
71debfbb65 | ||
|
|
34258511b9 | ||
|
|
0f121860fe | ||
|
|
70a6f547cf | ||
|
|
886f15aa9f | ||
|
|
d895301116 | ||
|
|
fdaa2d1dba | ||
|
|
da52b312ca | ||
|
|
8cc2334692 | ||
|
|
486d89669f | ||
|
|
c3614c9280 | ||
|
|
00aa0c2ae6 | ||
|
|
240c2c9395 | ||
|
|
88236b5a0b | ||
|
|
041cbafef1 | ||
|
|
c5b0916279 | ||
|
|
3c61f7cebf | ||
|
|
a13bd155dc | ||
|
|
09bae3d423 | ||
|
|
1f0fc09294 | ||
|
|
acc869086f | ||
|
|
0cdf793b42 | ||
|
|
d1ba60b673 | ||
|
|
ade7ea9235 | ||
|
|
7365e8a7ca | ||
|
|
c4207e4226 | ||
|
|
ddaef07fb5 | ||
|
|
f8174231cb | ||
|
|
0c8e52f18c | ||
|
|
559ccae1ad | ||
|
|
0597bfa513 | ||
|
|
921c0b699c | ||
|
|
3f36bf29b1 | ||
|
|
c6e1f8f155 | ||
|
|
65c178c04c | ||
|
|
69ab413676 | ||
|
|
71a4f5079e | ||
|
|
caeba1536a | ||
|
|
fd16bab763 | ||
|
|
2046ad4d06 | ||
|
|
12ddb585fb | ||
|
|
0a78489993 | ||
|
|
21abe6ff9a | ||
|
|
ab810e8f57 | ||
|
|
ac298baaec | ||
|
|
cf3fe0e852 | ||
|
|
79ebe443f5 | ||
|
|
f886f0a64c | ||
|
|
6b78f53bd7 | ||
|
|
b21e28ce82 | ||
|
|
35bd6292bb | ||
|
|
c4e17c672d | ||
|
|
d06d0ff8b5 | ||
|
|
07290cc8df | ||
|
|
bc65a40eb4 | ||
|
|
bc04af131a | ||
|
|
d48cd2545d | ||
|
|
133ff5421f | ||
|
|
69deb9ee91 | ||
|
|
f2700398dd | ||
|
|
8f7d9886c2 | ||
|
|
a2c225bf64 | ||
|
|
dd77de027b | ||
|
|
bcf81af71d | ||
|
|
c3c241adf5 | ||
|
|
cb4a04ce14 | ||
|
|
877ca1f74e | ||
|
|
d3bd28a0c1 | ||
|
|
4e4f0b8683 | ||
|
|
dfad543f6e | ||
|
|
9a1f9e763a | ||
|
|
1a9021e1ed | ||
|
|
5cca12b0ce | ||
|
|
0aed62f448 | ||
|
|
37a5f977c3 | ||
|
|
d7c550ea62 | ||
|
|
da86fd4875 | ||
|
|
aba32d7fab | ||
|
|
103976b2b0 | ||
|
|
0330006b89 | ||
|
|
12f3b5d241 | ||
|
|
673827cf70 | ||
|
|
5212d5b7f9 | ||
|
|
bfa2deeffb | ||
|
|
635cce877f | ||
|
|
0bab7544cd | ||
|
|
48b6e24135 | ||
|
|
423ceb5abc | ||
|
|
9b6a032d24 | ||
|
|
525006d716 | ||
|
|
083fd23503 | ||
|
|
3f18b181a3 | ||
|
|
50ec61ba37 | ||
|
|
f1ad3ddf25 | ||
|
|
936167ee9c | ||
|
|
52a624009d | ||
|
|
71ead2bf85 | ||
|
|
d56f075ee3 | ||
|
|
04d5237020 | ||
|
|
6379dbad4b | ||
|
|
b155b4b79f | ||
|
|
d7fd6d4e1e | ||
|
|
64fd2a6858 | ||
|
|
1ec06a4b80 | ||
|
|
2840b21946 | ||
|
|
69b1826d63 | ||
|
|
69cd1cb630 | ||
|
|
012f8f8af4 | ||
|
|
b85b426872 | ||
|
|
10169951c0 | ||
|
|
8eefbcec95 | ||
|
|
f73dd38dab | ||
|
|
9e821a754c | ||
|
|
f2caed46a8 | ||
|
|
7ef9719cc7 | ||
|
|
f47e5e5a82 | ||
|
|
f0fc0b6699 | ||
|
|
8cfc20999f | ||
|
|
bb72ad60b2 | ||
|
|
2b3e100cba | ||
|
|
5f432ce672 | ||
|
|
c3e4cd5e9f | ||
|
|
0f4529e1a6 | ||
|
|
58366e199e | ||
|
|
d512275f99 | ||
|
|
7e54e159df | ||
|
|
a92e26c49c | ||
|
|
e65a506dac | ||
|
|
6e78f05d75 | ||
|
|
f0f84a05ce | ||
|
|
18a71960e8 | ||
|
|
5e80c96587 | ||
|
|
9ec354c198 | ||
|
|
b7304f747b | ||
|
|
b7ab1ed476 | ||
|
|
bc522b27e0 | ||
|
|
3e9f975213 | ||
|
|
ba9e42fad6 | ||
|
|
05226453d9 | ||
|
|
4e2c050ffc | ||
|
|
39701c7657 | ||
|
|
6bdd22f875 | ||
|
|
20411f3518 | ||
|
|
27e733758d | ||
|
|
288381d8f6 | ||
|
|
c3f39aeb96 | ||
|
|
05d2ad8600 | ||
|
|
0911103949 | ||
|
|
9c8c290d83 | ||
|
|
bf3700ea8e | ||
|
|
c521f1faf7 | ||
|
|
ef7b789810 | ||
|
|
530f606657 | ||
|
|
120b9888f1 | ||
|
|
87884d4230 | ||
|
|
0b4eb92e95 | ||
|
|
51e55644f5 | ||
|
|
296496e895 | ||
|
|
afae3fc9a6 | ||
|
|
a301ca1311 | ||
|
|
eb97693e3f | ||
|
|
a6f01f7f82 | ||
|
|
a27996def8 | ||
|
|
fa96987eca | ||
|
|
192e65a827 | ||
|
|
a536912b38 | ||
|
|
fcf26a9ca7 | ||
|
|
4cb374dd5d | ||
|
|
9543bf1761 | ||
|
|
1527a1cb85 | ||
|
|
588d4f1024 | ||
|
|
c815d48274 | ||
|
|
1730ddebae | ||
|
|
1694f5cb89 | ||
|
|
1855cd4167 | ||
|
|
a4ff641366 | ||
|
|
2085c28bd1 | ||
|
|
6c2a55c67a | ||
|
|
2ffb4a4ab0 | ||
|
|
0e5d41a728 | ||
|
|
e1d5eba1f2 | ||
|
|
c99e9842ad | ||
|
|
20fabf40fa | ||
|
|
d8bba6e391 | ||
|
|
01429fe2a8 | ||
|
|
3496f45e39 | ||
|
|
88c37a9165 | ||
|
|
6d3abdaa02 | ||
|
|
eaa08b6991 | ||
|
|
1887ceff7f | ||
|
|
ff26a7f646 | ||
|
|
7dd85e445e | ||
|
|
4f22907004 | ||
|
|
ddc9f1ae12 | ||
|
|
7392d74efe | ||
|
|
535a1f1804 | ||
|
|
d387ee2816 | ||
|
|
dc174fa91c | ||
|
|
cf318bb47c | ||
|
|
d0a2c5ec40 | ||
|
|
cff6272c18 | ||
|
|
e808d770a9 | ||
|
|
63067d32d3 | ||
|
|
f9e9f20c05 | ||
|
|
3ee2f703a4 | ||
|
|
7b512485e6 | ||
|
|
d4e06db8e2 | ||
|
|
4ad893c147 | ||
|
|
381e644a42 | ||
|
|
ba81077c2e | ||
|
|
f7cf2264fa | ||
|
|
9bb4665fa9 | ||
|
|
4c9c23f620 | ||
|
|
75d6386e25 | ||
|
|
983944a776 | ||
|
|
373903cdb5 | ||
|
|
4b1257fc13 | ||
|
|
0416d4f1f7 | ||
|
|
911f82cda0 | ||
|
|
e4ed459376 | ||
|
|
46f25755c5 | ||
|
|
249e447920 | ||
|
|
cc199dcf4e | ||
|
|
baae8f5237 | ||
|
|
8437a87b54 | ||
|
|
b11a173b67 | ||
|
|
0e18f69cbf | ||
|
|
c3f9d93917 | ||
|
|
ac411754a0 | ||
|
|
45ebaa2c7f | ||
|
|
a923b0ce23 | ||
|
|
1be2cffd22 | ||
|
|
50af8bd3ff | ||
|
|
32ef29ee20 | ||
|
|
8620be452e | ||
|
|
cc79f9caee | ||
|
|
6a2eae7274 | ||
|
|
66002f7ceb | ||
|
|
d1b18f39dd | ||
|
|
dbd1e54c46 | ||
|
|
3c0ee51714 | ||
|
|
d4f7ab06cf | ||
|
|
66d8def3f8 | ||
|
|
8f29146c28 | ||
|
|
e53dc6ad0e | ||
|
|
90a96eb8bb | ||
|
|
3ff2c33d3c | ||
|
|
2237bb814c | ||
|
|
6b62e1db23 | ||
|
|
267bb8c99a | ||
|
|
d2ff9833de | ||
|
|
91e4478a29 | ||
|
|
842d712a31 | ||
|
|
2dbc322db8 | ||
|
|
d913ee38a3 | ||
|
|
5649236c7c | ||
|
|
a55893bf2f | ||
|
|
d8f936f1df | ||
|
|
d2539b978e | ||
|
|
92bbcb4866 | ||
|
|
96df171157 | ||
|
|
9fd0415ec6 | ||
|
|
31930c5e97 | ||
|
|
def987ad5f | ||
|
|
a4bb1a815a | ||
|
|
71f3450037 | ||
|
|
f7c9d9326d | ||
|
|
8b4df9b9a4 | ||
|
|
d4d292434c | ||
|
|
f598a7276b | ||
|
|
2f75253080 | ||
|
|
b0477deca1 | ||
|
|
de2db1af93 | ||
|
|
b6f9999a56 | ||
|
|
20b60467f2 | ||
|
|
64a92a2ffa | ||
|
|
620fecb244 | ||
|
|
ca982221f7 | ||
|
|
75d8a8b69d | ||
|
|
5a5954811f | ||
|
|
d5b1b155e9 | ||
|
|
3796521285 | ||
|
|
ec5a2c054c | ||
|
|
82407a1d59 | ||
|
|
e21f06331e | ||
|
|
d3902470c2 | ||
|
|
68eff9a122 | ||
|
|
1edf95bcde | ||
|
|
6e63b4a5e9 | ||
|
|
9465ee1c63 | ||
|
|
10710e9908 | ||
|
|
defeb0ba17 | ||
|
|
9a8e3c59ec | ||
|
|
f23d713f46 | ||
|
|
1f84f33346 | ||
|
|
8aafb271f9 | ||
|
|
c7cebb9ea9 | ||
|
|
2de5132803 | ||
|
|
70b4071350 | ||
|
|
eb13ae0cc1 | ||
|
|
06b9da800c | ||
|
|
fdd0af8dcf | ||
|
|
cfaed35f23 | ||
|
|
f2f5391ae8 | ||
|
|
1bcf026f25 | ||
|
|
acb814021c | ||
|
|
de7cbf1c1e | ||
|
|
8c72c5b5e0 | ||
|
|
9a6553f215 | ||
|
|
3ef252c68b | ||
|
|
6cc0bd7cb4 | ||
|
|
02b0c8c80c | ||
|
|
a0f4f9a4ab | ||
|
|
86bee21a9d | ||
|
|
4897dbf3c7 | ||
|
|
b79eb9ccea | ||
|
|
408f35f4ec | ||
|
|
5b9e7ac7e4 | ||
|
|
c5b0122d72 | ||
|
|
0d83bce07d | ||
|
|
f78013b70b | ||
|
|
592e7c3f9f | ||
|
|
f152f41d0a | ||
|
|
7aaa19404c | ||
|
|
6efa31454d | ||
|
|
31e91cc3ad | ||
|
|
b9637a59ff | ||
|
|
638de2057a | ||
|
|
28f95bfe38 | ||
|
|
c296ca1682 | ||
|
|
33760da0fa | ||
|
|
36d2216e66 | ||
|
|
00d1d01353 | ||
|
|
a3e3232825 | ||
|
|
eec9663145 | ||
|
|
5f17dafdf8 | ||
|
|
581593f9b5 | ||
|
|
241f631d9d | ||
|
|
814ebb7d3d | ||
|
|
1a9a2e9753 | ||
|
|
31823f5731 | ||
|
|
835679205c | ||
|
|
88268b084c | ||
|
|
76aba9cd6b | ||
|
|
2370052b54 | ||
|
|
252f1b5464 | ||
|
|
5c288f87e5 | ||
|
|
0dec3ff5eb | ||
|
|
3f1d76cd9f | ||
|
|
bc852567f2 | ||
|
|
11356db636 | ||
|
|
1ae812ff03 | ||
|
|
3615223b52 | ||
|
|
fbf74db7a4 | ||
|
|
773cce03eb | ||
|
|
8d0cf7cdcb | ||
|
|
97c65a33db | ||
|
|
909168acdb | ||
|
|
296fe887e5 | ||
|
|
803bb1a8a4 | ||
|
|
c30225d535 | ||
|
|
2ceb727a66 | ||
|
|
7bffaeffe6 | ||
|
|
57525689a2 | ||
|
|
0ff9c5a62c | ||
|
|
30b810b309 | ||
|
|
06a630ea62 | ||
|
|
3e536af09a | ||
|
|
62a2dc003c | ||
|
|
d185ae71bf | ||
|
|
be977bab46 | ||
|
|
233b2a4f20 | ||
|
|
b0e540e67b | ||
|
|
9675c8f256 | ||
|
|
2918a6310c | ||
|
|
fc81d0ec4e | ||
|
|
e7c9410df4 | ||
|
|
feb2fc17f4 | ||
|
|
3343efbcb9 | ||
|
|
b1f3e85c4d | ||
|
|
af7127580c | ||
|
|
84f55a55cd | ||
|
|
c86ca5de87 | ||
|
|
b98fff601f | ||
|
|
80c6b62074 | ||
|
|
fe02c8f401 | ||
|
|
2e23554162 | ||
|
|
6ed2417612 | ||
|
|
5e25dd4000 | ||
|
|
b39ad5fdc3 | ||
|
|
b42032c9f8 | ||
|
|
b23d05bcc2 | ||
|
|
8b6d4943b3 | ||
|
|
a78f5348f3 | ||
|
|
73b21210c2 | ||
|
|
be2b1421f1 | ||
|
|
60e9696a23 | ||
|
|
9f42ee379d | ||
|
|
3a40542aab | ||
|
|
841947d544 | ||
|
|
c14e7bd218 | ||
|
|
96f307838f | ||
|
|
fc42c42ef5 | ||
|
|
8ee136b287 | ||
|
|
37cd4ac179 | ||
|
|
57b93839be | ||
|
|
ed31e25a38 | ||
|
|
d11116f647 | ||
|
|
3851dc7c6a | ||
|
|
25695c3e86 | ||
|
|
e1872edb5a | ||
|
|
7ecbc808c6 | ||
|
|
1568d63117 | ||
|
|
790502c51f | ||
|
|
ad83a0cd39 | ||
|
|
a6913af2bc | ||
|
|
8ff4fa8666 | ||
|
|
ced0a832cc | ||
|
|
e68ff819de | ||
|
|
bbe8b4053f | ||
|
|
554d9708da | ||
|
|
7effe8920a | ||
|
|
20d5820efc | ||
|
|
e78119bf5a | ||
|
|
b60cce3e8f | ||
|
|
d79020656c | ||
|
|
49774fa4b6 | ||
|
|
db97942393 | ||
|
|
06949f59e8 | ||
|
|
315c7e6a50 | ||
|
|
442ebce079 | ||
|
|
a2bbe15b20 | ||
|
|
caec854ed8 | ||
|
|
7e04db958b | ||
|
|
1484a207c5 | ||
|
|
09f2c3e1ed | ||
|
|
7205570611 | ||
|
|
7223d4d6aa | ||
|
|
eaa0baad7f | ||
|
|
19ac7e6935 | ||
|
|
7eb620aca7 | ||
|
|
e21111bc5f | ||
|
|
9da9f53671 | ||
|
|
53b61fa69b | ||
|
|
dca30c3aab | ||
|
|
afc7665dc8 | ||
|
|
7d7bc00765 | ||
|
|
9f7cc44949 | ||
|
|
81ff379eb8 | ||
|
|
fd4cd139f8 | ||
|
|
7be684e9bf | ||
|
|
e1268262fc | ||
|
|
ce8d9aaf65 | ||
|
|
696601abcf | ||
|
|
941cb71a49 | ||
|
|
9c76a24d01 | ||
|
|
7dc4898a21 | ||
|
|
1fd475813d | ||
|
|
9b83a6d09d | ||
|
|
128edda6c0 | ||
|
|
6070f702fa | ||
|
|
8068992244 | ||
|
|
191bde690d | ||
|
|
2262f7067c | ||
|
|
c09836e551 | ||
|
|
a7ca9430e8 | ||
|
|
03f23c4339 | ||
|
|
39ce914bf9 | ||
|
|
0b98e57938 | ||
|
|
b499c432fd | ||
|
|
fa4cdc81d0 | ||
|
|
0b52a4baa0 | ||
|
|
6080af3621 | ||
|
|
5924225bc2 | ||
|
|
7fc60f6607 | ||
|
|
44891458f1 | ||
|
|
703a66106f | ||
|
|
af33413826 | ||
|
|
54f6ece82d | ||
|
|
3b75b6b545 | ||
|
|
e65560b67a | ||
|
|
2fd081234b | ||
|
|
65be47e05b | ||
|
|
9108ce19ea | ||
|
|
2f573803c4 | ||
|
|
6d745054a4 | ||
|
|
0fc5442e42 | ||
|
|
d482e165d6 | ||
|
|
d085951f68 | ||
|
|
f3969e7d7e | ||
|
|
ae56a0f4b4 | ||
|
|
6e05874ee1 | ||
|
|
ccf049b4d5 | ||
|
|
237fbebcf5 | ||
|
|
8d381dbf49 | ||
|
|
b0d1549964 | ||
|
|
a25f066a05 | ||
|
|
0d771384da | ||
|
|
f2dd4def12 | ||
|
|
f7b6da9679 | ||
|
|
9089fa1fd1 | ||
|
|
8fb0153963 | ||
|
|
3b72b88e9c | ||
|
|
547524d07b | ||
|
|
76710475fb | ||
|
|
06d51dc30e | ||
|
|
e8481d04c0 | ||
|
|
da12d6183e | ||
|
|
3d5f63a583 | ||
|
|
8639e53348 | ||
|
|
9bcd17a151 | ||
|
|
55aed4c755 | ||
|
|
c1e9099a6c | ||
|
|
5b436ed98e | ||
|
|
f58c1b43bc | ||
|
|
92ee64dcb8 | ||
|
|
0ab9a59e99 | ||
|
|
68ca748296 | ||
|
|
21cb503c06 | ||
|
|
9ede2d7af9 | ||
|
|
caf11f3991 | ||
|
|
78354cbdaf | ||
|
|
af3af870fa | ||
|
|
5e1ab7894f | ||
|
|
951834fb57 | ||
|
|
34fa732f91 | ||
|
|
3507ebc609 | ||
|
|
5bff9aa922 | ||
|
|
912a73d42a | ||
|
|
ef8ebadc7e | ||
|
|
5581417937 | ||
|
|
75bb6d1e43 | ||
|
|
eca92c4f7e | ||
|
|
322eccb371 | ||
|
|
a48704e0e2 | ||
|
|
95a7719b65 |
@@ -0,0 +1,194 @@
|
||||
name: Build
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
linux-build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
build-type: ["debug", "release"]
|
||||
|
||||
name: Linux ${{ matrix.build-type }}
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install flex bison meson libfreetype-dev libglew-dev libffi-dev libpng-dev libsndfile-dev libturbojpeg0-dev libwebp-dev libsdl2-dev zlib1g-dev
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir -p out/${{ matrix.build-type }}
|
||||
meson -Dbuildtype=${{ matrix.build-type }} out/${{ matrix.build-type }}
|
||||
ninja -C out/${{ matrix.build-type }}
|
||||
|
||||
- name: Test
|
||||
run: out/${{ matrix.build-type }}/src/xsystem4 test/Run/test.ain
|
||||
|
||||
flatpak-build:
|
||||
name: Flatpak
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-48
|
||||
options: --privileged
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
||||
with:
|
||||
bundle: xsystem4-linux-x86_64-nightly.flatpak
|
||||
manifest-path: technology.haniwa.xsystem4.yml
|
||||
cache-key: flatpak-builder-${{ github.sha }}
|
||||
|
||||
windows-build:
|
||||
runs-on: windows-latest
|
||||
name: Windows
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup MSYS2
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
update: true
|
||||
install: >-
|
||||
base-devel
|
||||
git
|
||||
nasm
|
||||
diffutils
|
||||
zip
|
||||
mingw-w64-x86_64-gcc
|
||||
mingw-w64-x86_64-meson
|
||||
mingw-w64-x86_64-pkg-config
|
||||
mingw-w64-x86_64-SDL2
|
||||
mingw-w64-x86_64-freetype
|
||||
mingw-w64-x86_64-libjpeg-turbo
|
||||
mingw-w64-x86_64-libwebp
|
||||
mingw-w64-x86_64-libpng
|
||||
mingw-w64-x86_64-libsndfile
|
||||
mingw-w64-x86_64-glew
|
||||
|
||||
- name: Cache FFmpeg
|
||||
id: cache-ffmpeg
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: FFmpeg/destdir
|
||||
key: ${{ runner.os }}-ffmpeg-n7.1.2
|
||||
|
||||
- name: Build FFmpeg
|
||||
if: ${{ steps.cache-ffmpeg.outputs.cache-hit != 'true' }}
|
||||
run: |
|
||||
git clone --depth 1 --branch n7.1.2 https://github.com/FFmpeg/FFmpeg.git
|
||||
cd FFmpeg
|
||||
./configure --disable-everything \
|
||||
--enable-decoder=mpegvideo \
|
||||
--enable-decoder=mpeg1video \
|
||||
--enable-decoder=mpeg2video \
|
||||
--enable-decoder=mp2 \
|
||||
--enable-parser=mpegaudio \
|
||||
--enable-parser=mpegvideo \
|
||||
--enable-demuxer=mpegps \
|
||||
--enable-demuxer=mpegts \
|
||||
--enable-demuxer=mpegtsraw \
|
||||
--enable-demuxer=mpegvideo \
|
||||
--enable-decoder=vc1 \
|
||||
--enable-decoder=wmapro \
|
||||
--enable-parser=vc1 \
|
||||
--enable-hwaccel=vc1_d3d11va \
|
||||
--enable-hwaccel=vc1_d3d11va2 \
|
||||
--enable-hwaccel=vc1_dxva2 \
|
||||
--enable-demuxer=asf \
|
||||
--enable-protocol=file \
|
||||
--enable-filter=scale \
|
||||
--enable-static \
|
||||
--disable-shared \
|
||||
--extra-libs=-static \
|
||||
--extra-cflags=--static \
|
||||
--prefix=/mingw64
|
||||
make -j$(nproc)
|
||||
mkdir destdir
|
||||
DESTDIR=destdir make install
|
||||
cd ..
|
||||
|
||||
- name: Install FFmpeg
|
||||
run: |
|
||||
cp -a FFmpeg/destdir/. /
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir debug
|
||||
meson debug -Dbuildtype=debug
|
||||
ninja -C debug
|
||||
mkdir release
|
||||
meson release -Dbuildtype=release
|
||||
ninja -C release
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
debug/src/xsystem4.exe test/Run/test.ain
|
||||
release/src/xsystem4.exe test/Run/test.ain
|
||||
|
||||
- name: Copy licenses
|
||||
run: |
|
||||
ls /mingw64/share/licenses
|
||||
mkdir dist
|
||||
cp -r /mingw64/share/licenses dist/
|
||||
|
||||
- name: Package
|
||||
run: |
|
||||
cp release/src/xsystem4.exe COPYING README.md game_compatibility.md dist/
|
||||
cp debug/src/xsystem4.exe dist/xsystem4-debug.exe
|
||||
cp /mingw64/bin/glew32.dll dist/
|
||||
cp extra/*.lnk dist/
|
||||
cp -r fonts dist/
|
||||
cp -r shaders dist/
|
||||
mv dist xsystem4
|
||||
zip -r -9 xsystem4-windows-x86_64-nightly.zip xsystem4
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: xsystem4
|
||||
path: xsystem4-windows-x86_64-nightly.zip
|
||||
|
||||
nightly:
|
||||
name: Update nightly release
|
||||
needs: [windows-build, flatpak-build]
|
||||
if: ${{ github.repository == 'nunuhara/xsystem4' && github.ref == 'refs/heads/master' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download release artifacts
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
merge-multiple: true
|
||||
|
||||
- name: Update nightly tag
|
||||
uses: richardsimko/update-tag@v1
|
||||
with:
|
||||
tag_name: nightly
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Release nightly
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
prerelease: true
|
||||
name: nightly
|
||||
tag_name: nightly
|
||||
files: |
|
||||
xsystem4-windows-x86_64-nightly.zip
|
||||
xsystem4-linux-x86_64-nightly.flatpak
|
||||
fail_on_unmatched_files: true
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
# CMakeLists.txt for Android build
|
||||
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(xsystem4 LANGUAGES C)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
cglm
|
||||
URL https://github.com/recp/cglm/archive/refs/tags/v0.9.2.tar.gz
|
||||
URL_HASH SHA1=cb8472aa8c2ab67b66378dbaf10c2c7368d4e4c3
|
||||
)
|
||||
FetchContent_MakeAvailable(cglm)
|
||||
|
||||
add_library(ffi STATIC IMPORTED)
|
||||
set_target_properties(ffi PROPERTIES
|
||||
IMPORTED_LOCATION ${CMAKE_STAGING_PREFIX}/lib/libffi.a
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_STAGING_PREFIX}/include)
|
||||
|
||||
find_package(Freetype REQUIRED)
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_package(SndFile REQUIRED)
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
add_subdirectory(subprojects/libsys4)
|
||||
|
||||
add_library(xsystem4 SHARED)
|
||||
|
||||
target_compile_definitions(xsystem4 PRIVATE
|
||||
_DEFAULT_SOURCE
|
||||
USE_GLES
|
||||
)
|
||||
|
||||
target_include_directories(xsystem4 PRIVATE include)
|
||||
|
||||
execute_process(
|
||||
COMMAND git rev-parse --short HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE VCS_TAG
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
configure_file(src/version.h.in version.h)
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
target_sources(xsystem4 PRIVATE
|
||||
src/audio.c
|
||||
src/audio_meta.c
|
||||
src/audio_mixer.c
|
||||
src/asset_manager.c
|
||||
src/base64.c
|
||||
src/cJSON.c
|
||||
src/draw.c
|
||||
src/effect.c
|
||||
src/ffi.c
|
||||
src/font_freetype.c
|
||||
src/font_fnl.c
|
||||
src/format.c
|
||||
src/hacks.c
|
||||
src/heap.c
|
||||
src/icon.c
|
||||
src/id_pool.c
|
||||
src/input.c
|
||||
src/json.c
|
||||
src/movie_plmpeg.c
|
||||
src/msgqueue.c
|
||||
src/page.c
|
||||
src/resume.c
|
||||
src/savedata.c
|
||||
src/scene.c
|
||||
src/screenshot.c
|
||||
src/sprite.c
|
||||
src/swf.c
|
||||
src/system4.c
|
||||
src/text.c
|
||||
src/util.c
|
||||
src/video.c
|
||||
src/vm.c
|
||||
|
||||
src/3d/collision.c
|
||||
src/3d/debug.c
|
||||
src/3d/model.c
|
||||
src/3d/mpr.c
|
||||
src/3d/parser.c
|
||||
src/3d/particle.c
|
||||
src/3d/reign.c
|
||||
src/3d/renderer.c
|
||||
src/3d/s3de.c
|
||||
src/3d/save.c
|
||||
|
||||
src/dungeon/dgn.c
|
||||
src/dungeon/dtx.c
|
||||
src/dungeon/dungeon.c
|
||||
src/dungeon/generator.c
|
||||
src/dungeon/map.c
|
||||
src/dungeon/mtrand43.c
|
||||
src/dungeon/polyobj.c
|
||||
src/dungeon/renderer.c
|
||||
src/dungeon/skybox.c
|
||||
src/dungeon/tes.c
|
||||
|
||||
src/parts/construction.c
|
||||
src/parts/debug.c
|
||||
src/parts/flash.c
|
||||
src/parts/flat.c
|
||||
src/parts/input.c
|
||||
src/parts/layoutbox.c
|
||||
src/parts/message.c
|
||||
src/parts/motion.c
|
||||
src/parts/parts.c
|
||||
src/parts/render.c
|
||||
src/parts/text.c
|
||||
src/parts/save.c
|
||||
|
||||
src/hll/ACXLoader.c
|
||||
src/hll/ADVSYS.c
|
||||
src/hll/AFAFactory.c
|
||||
src/hll/AliceLogo.c
|
||||
src/hll/AliceLogo2.c
|
||||
src/hll/AliceLogo3.c
|
||||
src/hll/AliceLogo4.c
|
||||
src/hll/AliceLogo5.c
|
||||
src/hll/AnteaterADVEngine.c
|
||||
src/hll/Array.c
|
||||
src/hll/BanMisc.c
|
||||
src/hll/Bitarray.c
|
||||
src/hll/CalcTable.c
|
||||
src/hll/CGManager.c
|
||||
src/hll/CharSpriteManager.c
|
||||
src/hll/ChipmunkSpriteEngine.c
|
||||
src/hll/ChrLoader.c
|
||||
src/hll/CommonSystemData.c
|
||||
src/hll/Confirm.c
|
||||
src/hll/Confirm2.c
|
||||
src/hll/Confirm3.c
|
||||
src/hll/CrayfishLogViewer.c
|
||||
src/hll/Cursor.c
|
||||
src/hll/DALKDemo.c
|
||||
src/hll/DALKEDemo.c
|
||||
src/hll/Data.c
|
||||
src/hll/DataFile.c
|
||||
src/hll/Discord.c
|
||||
src/hll/DrawDungeon.c
|
||||
src/hll/DrawEffect.c
|
||||
src/hll/DrawGraph.c
|
||||
src/hll/DrawMovie.c
|
||||
src/hll/DrawNumeral.c
|
||||
src/hll/DrawPluginManager.c
|
||||
src/hll/DrawRain.c
|
||||
src/hll/DrawRipple.c
|
||||
src/hll/DrawSimpleText.c
|
||||
src/hll/DrawSnow.c
|
||||
src/hll/File.c
|
||||
src/hll/File2.c
|
||||
src/hll/FileOperation.c
|
||||
src/hll/FillAngle.c
|
||||
src/hll/GoatGUIEngine.c
|
||||
src/hll/Gpx2Plus.c
|
||||
src/hll/GUIEngine.c
|
||||
src/hll/HTTPDownloader.c
|
||||
src/hll/iarray.c
|
||||
src/hll/IbisInputEngine.c
|
||||
src/hll/InputDevice.c
|
||||
src/hll/InputString.c
|
||||
src/hll/KiwiSoundEngine.c
|
||||
src/hll/LoadCG.c
|
||||
src/hll/MADLoader.c
|
||||
src/hll/MainEXFile.c
|
||||
src/hll/MainSurface.c
|
||||
src/hll/MamanyoDemo.c
|
||||
src/hll/MamanyoSDemo.c
|
||||
src/hll/MarmotModelEngine.c
|
||||
src/hll/Math.c
|
||||
src/hll/MapLoader.c
|
||||
src/hll/MenuMsg.c
|
||||
src/hll/MonsterInfo.c
|
||||
src/hll/MsgLogManager.c
|
||||
src/hll/MsgLogViewer.c
|
||||
src/hll/MsgSkip.c
|
||||
src/hll/MusicSystem.c
|
||||
src/hll/NewFont.c
|
||||
src/hll/OutputLog.c
|
||||
src/hll/P3MapSprite.c
|
||||
src/hll/P3SquareSprite.c
|
||||
src/hll/PassRegister.c
|
||||
src/hll/PastelChime2.c
|
||||
src/hll/PartsEngine.c
|
||||
src/hll/PixelRestore.c
|
||||
src/hll/PlayDemo.c
|
||||
src/hll/PlayMovie.c
|
||||
src/hll/ReignEngine.c
|
||||
src/hll/SACT2.c
|
||||
src/hll/SengokuRanceFont.c
|
||||
src/hll/SoundFilePlayer.c
|
||||
src/hll/Sound2ex.c
|
||||
src/hll/StoatSpriteEngine.c
|
||||
src/hll/StretchHelper.c
|
||||
src/hll/SystemService.c
|
||||
src/hll/SystemServiceEx.c
|
||||
src/hll/Timer.c
|
||||
src/hll/Toushin3Loader.c
|
||||
src/hll/vmAnime.c
|
||||
src/hll/vmArray.c
|
||||
src/hll/vmCG.c
|
||||
src/hll/vmChrLoader.c
|
||||
src/hll/vmCursor.c
|
||||
src/hll/vmDalkGaiden.c
|
||||
src/hll/vmData.c
|
||||
src/hll/vmDialog.c
|
||||
src/hll/vmDrawGauge.c
|
||||
src/hll/vmDrawMsg.c
|
||||
src/hll/vmDrawNumber.c
|
||||
src/hll/vmFile.c
|
||||
src/hll/vmGraph.c
|
||||
src/hll/vmGraphQuake.c
|
||||
src/hll/vmKey.c
|
||||
src/hll/vmMapLoader.c
|
||||
src/hll/vmMsgLog.c
|
||||
src/hll/vmMusic.c
|
||||
src/hll/vmSound.c
|
||||
src/hll/vmSprite.c
|
||||
src/hll/vmString.c
|
||||
src/hll/vmSurface.c
|
||||
src/hll/vmSystem.c
|
||||
src/hll/vmTimer.c
|
||||
src/hll/ValueEncryption.c
|
||||
src/hll/VSFile.c
|
||||
)
|
||||
|
||||
target_link_libraries(xsystem4 PRIVATE
|
||||
m ZLIB::ZLIB SDL2::SDL2 Freetype::Freetype ffi GLESv3 cglm SndFile::sndfile sys4)
|
||||
|
||||
target_compile_options(xsystem4 PRIVATE -Wno-unused-parameter)
|
||||
|
||||
install(TARGETS xsystem4)
|
||||
@@ -4,11 +4,11 @@ xsystem4
|
||||
xsystem4 is an implementation of AliceSoft's System 4 game engine for unix-like
|
||||
operating systems.
|
||||
|
||||
NOTE: This is a work in progress. I am focusing primarily on the Japanese
|
||||
version of Sengoku Rance (and by extension, the English fan translation)
|
||||
which is currently more or less playable. Some other games from the same
|
||||
era may also work, but I have not done any significant testing on them so
|
||||
breakage is likely.
|
||||
Compatiblity
|
||||
------------
|
||||
|
||||
See the [game compatibility table](game_compatibility.md) for a list of games
|
||||
that can be played on xsystem4.
|
||||
|
||||
Building
|
||||
--------
|
||||
@@ -19,6 +19,8 @@ First install the dependencies (corresponding Debian package in parentheses):
|
||||
* bison (bison)
|
||||
* cglm (libcglm-dev) [optional, fetched by meson if not available]
|
||||
* flex (flex)
|
||||
* freetype (libfreetype-dev)
|
||||
* glew (libglew-dev)
|
||||
* meson (meson)
|
||||
* libffi (libffi-dev)
|
||||
* libpng (libpng-dev)
|
||||
@@ -26,7 +28,6 @@ First install the dependencies (corresponding Debian package in parentheses):
|
||||
* libturbojpeg (libturbojpeg0-dev)
|
||||
* libwebp (libwebp-dev)
|
||||
* SDL2 (libsdl2-dev)
|
||||
* SDL2_ttf (libsdl2-ttf-dev)
|
||||
* zlib (zlib1g-dev)
|
||||
|
||||
Then fetch the git submodules,
|
||||
@@ -46,6 +47,98 @@ Finally install it to your system (optional),
|
||||
|
||||
ninja -C build install
|
||||
|
||||
### Windows
|
||||
|
||||
xsystem4 can be built on Windows using MSYS2.
|
||||
|
||||
First install MSYS2, and then open the MINGW64 shell and run the following command,
|
||||
|
||||
pacman -S make flex bison \
|
||||
mingw-w64-x86_64-gcc \
|
||||
mingw-w64-x86_64-meson \
|
||||
mingw-w64-x86_64-pkg-config \
|
||||
mingw-w64-x86_64-SDL2 \
|
||||
mingw-w64-x86_64-freetype \
|
||||
mingw-w64-x86_64-libjpeg-turbo \
|
||||
mingw-w64-x86_64-libwebp \
|
||||
mingw-w64-x86_64-libsndfile \
|
||||
mingw-w64-x86_64-glew \
|
||||
mingw-w64-x86_64-nasm \
|
||||
mingw-w64-x86_64-diffutils
|
||||
|
||||
To build with FFmpeg support, you must compile FFmpeg as a static library:
|
||||
|
||||
git clone https://github.com/FFmpeg/FFmpeg.git
|
||||
cd FFmpeg
|
||||
git checkout n7.1.2
|
||||
./configure --disable-everything \
|
||||
--enable-decoder=mpegvideo \
|
||||
--enable-decoder=mpeg1video \
|
||||
--enable-decoder=mpeg2video \
|
||||
--enable-decoder=mp2 \
|
||||
--enable-parser=mpegaudio \
|
||||
--enable-parser=mpegvideo \
|
||||
--enable-demuxer=mpegps \
|
||||
--enable-demuxer=mpegts \
|
||||
--enable-demuxer=mpegtsraw \
|
||||
--enable-demuxer=mpegvideo \
|
||||
--enable-decoder=vc1 \
|
||||
--enable-decoder=wmapro \
|
||||
--enable-parser=vc1 \
|
||||
--enable-hwaccel=vc1_d3d11va \
|
||||
--enable-hwaccel=vc1_d3d11va2 \
|
||||
--enable-hwaccel=vc1_dxva2 \
|
||||
--enable-demuxer=asf \
|
||||
--enable-protocol=file \
|
||||
--enable-filter=scale \
|
||||
--enable-static \
|
||||
--disable-shared \
|
||||
--extra-libs=-static \
|
||||
--extra-cflags=--static
|
||||
make
|
||||
make install
|
||||
cd ..
|
||||
export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
|
||||
Then build the xsystem4 executable with meson,
|
||||
|
||||
mkdir build
|
||||
meson build
|
||||
ninja -C build
|
||||
|
||||
To create a portable executable, it is neccessary to copy some DLLs into the same directory as xsystem4.exe.
|
||||
You can determine the required DLLs with the following command,
|
||||
|
||||
ldd build/src/xsystem4.exe | grep mingw64
|
||||
|
||||
The `fonts` and `shaders` directories must also be shipped together with xsystem4.exe.
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
If building from source, run:
|
||||
|
||||
ninja -C build install
|
||||
|
||||
to install xsystem4 on your system.
|
||||
|
||||
### Windows
|
||||
|
||||
The recommended way to install on Windows is to copy the xsystem4 directory
|
||||
into the game directory. E.g. if Sengoku Rance is installed at
|
||||
`C:\Games\AliceSoft\Sengoku Rance`, then your filesystem should look something
|
||||
like this:
|
||||
|
||||
C:\
|
||||
Games\
|
||||
AliceSoft\
|
||||
Sengoku Rance\
|
||||
System40.ini
|
||||
xsystem4\
|
||||
xsystem4.exe
|
||||
...
|
||||
...
|
||||
|
||||
Running
|
||||
-------
|
||||
|
||||
@@ -58,3 +151,8 @@ Alternatively, run xsystem4 from within the game directory,
|
||||
|
||||
cd /path/to/game_directory
|
||||
xsystem4
|
||||
|
||||
### Windows
|
||||
|
||||
If you've installed xsystem4 into the game directory as described above, simply
|
||||
double-click `xsystem4.exe` to run the game.
|
||||
|
||||
Binary file not shown.
Generated
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1757244434,
|
||||
"narHash": "sha256-AeqTqY0Y95K1Fgs6wuT1LafBNcmKxcOkWnm4alD9pqM=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "092c565d333be1e17b4779ac22104338941d913f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-25.05",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
{
|
||||
description = "Open source implementation of AliceSoft's System 4 game engine for unix-like operating systems";
|
||||
|
||||
# Nixpkgs / NixOS version to use.
|
||||
inputs.nixpkgs.url = "nixpkgs/nixos-25.05";
|
||||
|
||||
outputs = { self, nixpkgs }:
|
||||
let
|
||||
|
||||
# System types to support.
|
||||
supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ];
|
||||
|
||||
# Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'.
|
||||
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||
|
||||
# Nixpkgs instantiated for supported system types.
|
||||
nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; });
|
||||
|
||||
in {
|
||||
|
||||
# Set up xsystem4 development environment:
|
||||
# nix develop # create shell environment with dependencies available
|
||||
devShell = forAllSystems (system:
|
||||
let pkgs = nixpkgsFor.${system}; in with pkgs; pkgs.mkShell {
|
||||
nativeBuildInputs = [ meson ninja pkg-config ];
|
||||
buildInputs = [
|
||||
cglm
|
||||
SDL2
|
||||
freetype
|
||||
libffi
|
||||
libjpeg
|
||||
libwebp
|
||||
libsndfile
|
||||
glew
|
||||
readline
|
||||
chibi
|
||||
flex
|
||||
bison
|
||||
ffmpeg
|
||||
];
|
||||
}
|
||||
);
|
||||
|
||||
# Build xsystem4:
|
||||
# nix build .?submodules=1 # build xsystem4 (outputs to ./result/)
|
||||
# nix shell .?submodules=1 # create shell environment with xsystem4 available
|
||||
# Install xsystem4 to user profile:
|
||||
# nix profile install .?submodules=1
|
||||
packages = forAllSystems (system:
|
||||
let pkgs = nixpkgsFor.${system}; in {
|
||||
default = with pkgs; stdenv.mkDerivation rec {
|
||||
name = "xsystem4";
|
||||
src = ./.;
|
||||
mesonAutoFeatures = "auto";
|
||||
nativeBuildInputs = [ meson ninja pkg-config ];
|
||||
buildInputs = [
|
||||
cglm
|
||||
SDL2
|
||||
freetype
|
||||
libffi
|
||||
libjpeg
|
||||
libwebp
|
||||
libsndfile
|
||||
glew
|
||||
readline
|
||||
chibi
|
||||
flex
|
||||
bison
|
||||
ffmpeg
|
||||
];
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
Game Compatibility
|
||||
==================
|
||||
|
||||
The table below lists xsystem4's compatibility with various games.
|
||||
The values in the "status" column have these meanings:
|
||||
|
||||
* Supported - known to be working
|
||||
* Unsupported - known to be broken
|
||||
* Unknown - insufficient/no testing to determine compatibility
|
||||
|
||||
In practice, many of the games with "unknown" status will run fine (perhaps
|
||||
with some quirks). If you manage to play an "unknown" game to the end without
|
||||
issues, please let me know or open a pull request to update this table.
|
||||
|
||||
| Game | Status | Notes |
|
||||
| ------------------------------------------------------- | ----------- | ----- |
|
||||
| DALK外伝 | Supported | |
|
||||
| ままにょにょ | Supported | |
|
||||
| 大番長 -Big Bang Age- | Supported | |
|
||||
| Daibanchou -Big Bang Age- (EN) | Supported | |
|
||||
| 魔女の贖罪 | Unknown | |
|
||||
| Rance VI -ゼス崩壊- | Supported | |
|
||||
| Rance VI -Collapse of Zeth- (EN) | Supported | |
|
||||
| ウルトラ魔法少女まなな Vol.1~Vol.5 | Supported | |
|
||||
| 鈴宮刑務娼館 | Supported | From ALICEの館7 |
|
||||
| しまいま | Supported | From ALICEの館7 |
|
||||
| DUNGEONS & DOLLS | Supported | From ALICEの館7 |
|
||||
| こすぷれれもん | Supported | From ALICEの館7 |
|
||||
| エルメイフラワー | Supported | From ALICEの館7 |
|
||||
| CGは17年ゼミ | Supported | From ALICEの館7 |
|
||||
| でーぴーえすVer_N | Supported | From ALICEの館7 |
|
||||
| 大番長外伝 | Supported | From ALICEの館7 |
|
||||
| ZERO | Unsupported | From ALICEの館7 |
|
||||
| ぷろGRX | Supported | From ALICEの館7 |
|
||||
| Girls | Supported | From ALICEの館7 |
|
||||
| 神武 -カムナガラ- | Supported | From ALICEの館7 |
|
||||
| オニむス | Supported | From ALICEの館7 |
|
||||
| 透色 | Supported | From ALICEの館7 |
|
||||
| DEBUT | Supported | From ALICEの館7 |
|
||||
| ランス6その後 | Supported | From ALICEの館7 |
|
||||
| ほいっぷハニー | Supported | From ALICEの館7 |
|
||||
| Text Jam Session | Supported | From ALICEの館7 |
|
||||
| 没ロゴの哀しみ | Supported | From ALICEの館7 |
|
||||
| スタッフコーナー | Supported | From ALICEの館7 |
|
||||
| ぱすてるチャイムContinue | Supported | |
|
||||
| GALZOOアイランド | Supported | |
|
||||
| ぱすちゃC++ | Supported | |
|
||||
| よくばりサボテン | Supported | |
|
||||
| 妻しぼり | Unknown | |
|
||||
| 戦国ランス | Supported | |
|
||||
| Sengoku Rance (EN FanTL) | Supported | |
|
||||
| Sengoku Rance (MangaGamer) | Supported | |
|
||||
| だぶる先生らいふっ | Unknown | |
|
||||
| お嬢様をいいなりにするゲーム | Supported | |
|
||||
| 超昂閃忍ハルカ | Unknown | |
|
||||
| 戦国ランス~三匹が斬ったり突いたり燃やしたり | Supported | |
|
||||
| Sengoku Rance -Three for the Slash! Thrust! Burn!- (EN) | Supported | |
|
||||
| AliveZ | Unknown | |
|
||||
| エドランゼ ~Come on!水戸姫様~ | Supported | |
|
||||
| 闘神都市III | Supported | |
|
||||
| ばにしゅ!~おっぱいの消えた王国~ | Unknown | |
|
||||
| 僕だけの保健室 | Supported | |
|
||||
| ももいろガーディアン | Unknown | |
|
||||
| 超昂閃忍ハルカ ハルカVSエスカレイヤー | Unknown | From Alice 2010 |
|
||||
| 超昂閃忍ハルカ ―疾風!?忍者大作戦― | Unknown | From Alice 2010 |
|
||||
| 眼鏡のメイと夏休み | Unknown | From Alice 2010 |
|
||||
| レイチェルの桜貝 | Unknown | From Alice 2010 |
|
||||
| 砂漠のニッカ | Unknown | From Alice 2010 |
|
||||
| ばにしゅ!~この手のひらにおっぱいを~ | Unknown | From Alice 2010 |
|
||||
| はるうられ -校内赤線区域- | Supported | From Alice 2010 |
|
||||
| わいどにょ | Supported | From Alice 2010 |
|
||||
| ランス02 -反逆の少女たち- | Supported | From Alice 2010 |
|
||||
| Rance 02 -The Rebellious Maidens- (Fan TL) | Supported | |
|
||||
| Rance 02 -The Rebellious Maidens- (MangaGamer) | Supported | |
|
||||
| しゃーまんず・さんくちゅあり-巫女の聖域- | Unknown | |
|
||||
| 大帝国 | Supported | |
|
||||
| ランス・クエスト | Supported | |
|
||||
| ランス・クエスト マグナム | Supported | |
|
||||
| 母娘乱館 | Unknown | |
|
||||
| パステルチャイム3 バインドシーカー | Supported | |
|
||||
| パスチャ3 プラスコンテンツ | Supported | |
|
||||
| どらぺこ! ~おねだりドラゴンとおっぱい勇者~ | Unsupported | |
|
||||
| ランス01 光をもとめて | Supported | |
|
||||
| Rance 01 - Quest for Hikari (EN) | Supported | |
|
||||
| ランスIX-ヘルマン革命- | Supported | |
|
||||
| Rance IX - The Helmanian Revolution (EN) | Unknown | |
|
||||
| 武想少女隊ぶれいど☆ブライダーズ | Supported | |
|
||||
| イブニクル | Unsupported | |
|
||||
| Evenicle (EN) | Unsupported | |
|
||||
| ランス03 リーザス陥落 | Unsupported | |
|
||||
| Rance 03 -The Fall of Leazas- (EN) | Unsupported | |
|
||||
| 妻みぐい3 | Unsupported | |
|
||||
| は~とふるママン | Unsupported | |
|
||||
| 超昂神騎エクシール | Unsupported | |
|
||||
| Beat Valkyrie Ixseal (EN) | Unsupported | |
|
||||
| Rance X -決戦- | Unsupported | |
|
||||
| All games released after Rance X | Unsupported | |
|
||||
@@ -30,22 +30,22 @@ enum asset_type {
|
||||
ASSET_CG,
|
||||
ASSET_FLAT,
|
||||
ASSET_PACT,
|
||||
ASSET_DATA
|
||||
ASSET_DATA,
|
||||
ASSET_FLASH
|
||||
};
|
||||
#define ASSET_TYPE_MAX (ASSET_DATA+1)
|
||||
#define ASSET_TYPE_MAX (ASSET_FLASH+1)
|
||||
|
||||
const char *asset_strtype(enum asset_type type);
|
||||
void asset_manager_init(void);
|
||||
bool asset_manager_load_archive(enum asset_type type, const char *archive_name);
|
||||
bool asset_exists(enum asset_type type, int no);
|
||||
bool asset_exists_by_name(enum asset_type type, const char *name, int *id_out);
|
||||
struct archive_data *asset_get(enum asset_type type, int no);
|
||||
struct archive_data *asset_get_by_name(enum asset_type type, const char *name, int *id_out);
|
||||
|
||||
// FIXME: CG code should be refactored to read from buffer only, not archive,
|
||||
// then caller can just use asset_get and cg_load
|
||||
struct cg *asset_cg_load(int no);
|
||||
struct cg *asset_cg_load_by_name(const char *name, int *no);
|
||||
int asset_cg_name_to_index(const char *name);
|
||||
bool asset_cg_exists(int no);
|
||||
struct cg *asset_cg_load_by_name(const char *name, int *id_out);
|
||||
bool asset_cg_get_metrics(int no, struct cg_metrics *metrics);
|
||||
void asset_cg_index_init(void);
|
||||
bool asset_cg_get_metrics_by_name(const char *name, struct cg_metrics *metrics);
|
||||
|
||||
#endif /* SYSTEM4_ASSET_MANAGER_H */
|
||||
|
||||
+15
-6
@@ -19,10 +19,16 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#define AUDIO_NO_METADATA -1
|
||||
|
||||
struct archive_data;
|
||||
|
||||
void audio_init(void);
|
||||
void audio_fini(void);
|
||||
void audio_reset(void);
|
||||
void audio_update(void);
|
||||
bool audio_play_sound(int sound_no);
|
||||
// Takes ownership of dfile.
|
||||
bool audio_play_archive_data(struct archive_data *dfile);
|
||||
|
||||
bool wav_exists(int no);
|
||||
bool bgm_exists(int no);
|
||||
@@ -74,14 +80,17 @@ int wav_get_volume(int id);
|
||||
int bgm_get_volume(int id);
|
||||
int wav_get_time_length(int id);
|
||||
int bgm_get_time_length(int id);
|
||||
//int wav_get_group_num(int id);
|
||||
int wav_get_data_length(int no);
|
||||
int bgm_get_data_length(int no);
|
||||
int wav_get_group_num(int id);
|
||||
//int bgm_get_group_num(int id);
|
||||
//int wav_prepare_from_file(int id, char *filename);
|
||||
//int bgm_prepare_from_file(int id, char *filename);
|
||||
int wav_prepare_from_file(int id, char *filename);
|
||||
int bgm_prepare_from_file(int id, char *filename);
|
||||
|
||||
struct archive_data;
|
||||
int wav_get_group_num_from_data_num(int no);
|
||||
|
||||
// Takes ownership of dfile.
|
||||
int wav_prepare_from_archive_data(int id, struct archive_data *dfile);
|
||||
int wav_prepare_from_archive_data(int id, int metadata_no, struct archive_data *dfile);
|
||||
int bgm_prepare_from_archive_data(int id, int metadata_no, struct archive_data *dfile);
|
||||
|
||||
#endif /* SYSTEM4_AUDIO_H */
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/* Copyright (C) 2023 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM4_BASE64_H
|
||||
#define SYSTEM4_BASE64_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
unsigned char *base64_encode(const unsigned char *src, size_t len,
|
||||
size_t *out_len);
|
||||
|
||||
#endif /* SYSTEM4_BASE64_H */
|
||||
@@ -210,6 +210,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
|
||||
|
||||
/* These utilities create an Array of count items.
|
||||
* The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateArray_cb(int count, cJSON *(*get_item)(int,void*), void *data);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray_cb(int count, int (*get_number)(int,void*), void *data);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
|
||||
|
||||
+73
-4
@@ -18,12 +18,29 @@
|
||||
#define SYSTEM4_DEBUGGER_H
|
||||
#ifdef DEBUGGER_ENABLED
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "system4/instructions.h"
|
||||
#include "vm.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
#define DBG_ERROR(fmt, ...) sys_warning("ERROR: " fmt "\n", ##__VA_ARGS__)
|
||||
|
||||
struct ain_variable;
|
||||
|
||||
enum dbg_stop_type {
|
||||
DBG_STOP_PAUSE,
|
||||
DBG_STOP_ERROR,
|
||||
DBG_STOP_BREAKPOINT,
|
||||
DBG_STOP_STEP,
|
||||
};
|
||||
|
||||
struct dbg_stop {
|
||||
enum dbg_stop_type type;
|
||||
const char *message;
|
||||
};
|
||||
|
||||
struct breakpoint {
|
||||
enum opcode restore_op;
|
||||
void *data;
|
||||
@@ -32,25 +49,77 @@ struct breakpoint {
|
||||
int count;
|
||||
};
|
||||
|
||||
extern bool dbg_enabled;
|
||||
struct dbg_cmd {
|
||||
const char *fullname;
|
||||
const char *shortname;
|
||||
const char *arg_description;
|
||||
const char *description;
|
||||
unsigned min_args;
|
||||
unsigned max_args;
|
||||
void (*run)(unsigned nr_args, char **args);
|
||||
};
|
||||
|
||||
void dbg_init(void);
|
||||
struct dbg_info;
|
||||
|
||||
extern bool dbg_dap;
|
||||
extern bool dbg_enabled;
|
||||
extern bool dbg_start_in_debugger;
|
||||
extern unsigned dbg_current_frame;
|
||||
extern struct dbg_info *dbg_info;
|
||||
|
||||
void dbg_init(const char *debug_info_path);
|
||||
void dbg_fini(void);
|
||||
void dbg_repl(void);
|
||||
void dbg_repl(enum dbg_stop_type, const char *msg);
|
||||
|
||||
void dbg_log(const char *log, const char *fmt, va_list ap);
|
||||
|
||||
void dbg_continue(void);
|
||||
void dbg_quit(void);
|
||||
void dbg_start(void(*fun)(void*), void *data);
|
||||
void dbg_cmd_init(void);
|
||||
void dbg_cmd_repl(void);
|
||||
enum opcode dbg_handle_breakpoint(unsigned bp_no);
|
||||
void dbg_cmd_add_module(const char *name, unsigned nr_commands, struct dbg_cmd *commands);
|
||||
void dbg_handle_breakpoint(void);
|
||||
bool dbg_clear_breakpoint(uint32_t addr, void(*free_data)(void*));
|
||||
void dbg_foreach_breakpoint(void (*fun)(int addr, struct breakpoint*, void *data), void *data);
|
||||
bool dbg_set_function_breakpoint(const char *_name, void(*cb)(struct breakpoint*), void *data);
|
||||
bool dbg_set_address_breakpoint(uint32_t address, void(*cb)(struct breakpoint*), void *data);
|
||||
bool dbg_set_step_over_breakpoint(void);
|
||||
bool dbg_set_step_into_breakpoint(void);
|
||||
bool dbg_set_finish_breakpoint(void);
|
||||
void dbg_print_frame(unsigned no);
|
||||
void dbg_print_stack_trace(void);
|
||||
void dbg_print_stack(void);
|
||||
void dbg_print_vm_state(void);
|
||||
void dbg_print_current_line(void);
|
||||
union vm_value dbg_eval_string(const char *str, struct ain_type *type_out);
|
||||
struct string *dbg_value_to_string(struct ain_type *type, union vm_value value, int recursive);
|
||||
struct string *dbg_variable_to_string(struct ain_type *type, struct page *page, int slot, int recursive);
|
||||
|
||||
void dbg_dap_init(void);
|
||||
void dbg_dap_quit(void);
|
||||
void dbg_dap_repl(struct dbg_stop *stop);
|
||||
void dbg_dap_handle_messages(void);
|
||||
void dbg_dap_log(const char *log, const char *fmt, va_list ap);
|
||||
|
||||
struct dbg_info *dbg_info_load(const char *path);
|
||||
const char *dbg_info_source_name(const struct dbg_info *info, int file);
|
||||
char *dbg_info_source_path(const struct dbg_info *info, int file);
|
||||
const char *dbg_info_source_line(const struct dbg_info *info, int file, int line);
|
||||
int dbg_info_find_file(const struct dbg_info *info, const char *filename);
|
||||
bool dbg_info_addr2line(const struct dbg_info *info, int addr, int *file, int *line);
|
||||
int dbg_info_line2addr(const struct dbg_info *info, int file, int line);
|
||||
|
||||
#ifdef HAVE_SCHEME
|
||||
void dbg_scm_init(void);
|
||||
void dbg_scm_fini(void);
|
||||
void dbg_scm_repl(void);
|
||||
#endif /* HAVE_SCHEME */
|
||||
#else /* DEBUGGER ENABLED */
|
||||
#define dbg_init(debug_info_path)
|
||||
#define dbg_repl(type, msg)
|
||||
#define dbg_dap 0
|
||||
#define dbg_dap_handle_messages()
|
||||
#define dbg_dap_log(log, fmt, ap)
|
||||
#endif /* DEBUGGER_ENABLED */
|
||||
#endif /* SYSTEM4_DEBUGGER_H */
|
||||
|
||||
+28
-3
@@ -20,6 +20,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
/*
|
||||
* Dungeon data is a collection of cells indexed by three integers (x, y, z).
|
||||
@@ -45,7 +46,13 @@ struct dgn_cell {
|
||||
int32_t west_door;
|
||||
int32_t stairs_texture;
|
||||
int32_t stairs_orientation;
|
||||
// int32_t unknown[13];
|
||||
int32_t lightmap_floor;
|
||||
int32_t lightmap_ceiling;
|
||||
int32_t lightmap_north;
|
||||
int32_t lightmap_south;
|
||||
int32_t lightmap_east;
|
||||
int32_t lightmap_west;
|
||||
// int32_t unknown[7];
|
||||
int32_t enterable;
|
||||
int32_t enterable_north;
|
||||
int32_t enterable_south;
|
||||
@@ -86,11 +93,16 @@ struct dgn_cell {
|
||||
float south_door_angle;
|
||||
float east_door_angle;
|
||||
float west_door_angle;
|
||||
int north_door_lock;
|
||||
int south_door_lock;
|
||||
int east_door_lock;
|
||||
int west_door_lock;
|
||||
int32_t floor_event2;
|
||||
int32_t north_event2;
|
||||
int32_t south_event2;
|
||||
int32_t east_event2;
|
||||
int32_t west_event2;
|
||||
int pathfinding_cost;
|
||||
struct dgn_cell **visible_cells;
|
||||
};
|
||||
|
||||
@@ -106,16 +118,28 @@ struct packed_pvs {
|
||||
};
|
||||
|
||||
struct dgn {
|
||||
uint32_t version; // 10: Rance VI, 13: GALZOO Island
|
||||
uint32_t size_x;
|
||||
uint32_t size_y;
|
||||
uint32_t size_z;
|
||||
// uint32_t unknown[10];
|
||||
struct dgn_cell *cells;
|
||||
struct packed_pvs *pvs;
|
||||
vec3 sphere_theta;
|
||||
float sphere_color_top;
|
||||
float sphere_color_bottom;
|
||||
|
||||
// for generated dungeons
|
||||
int start_x, start_y;
|
||||
int exit_x, exit_y;
|
||||
|
||||
// DrawField
|
||||
int32_t back_color_r;
|
||||
int32_t back_color_g;
|
||||
int32_t back_color_b;
|
||||
};
|
||||
|
||||
struct dgn *dgn_parse(uint8_t *data, size_t size);
|
||||
struct dgn *dgn_new(uint32_t size_x, uint32_t size_y, uint32_t size_z);
|
||||
struct dgn *dgn_parse(uint8_t *data, size_t size, bool for_draw_field);
|
||||
void dgn_free(struct dgn *dgn);
|
||||
|
||||
int dgn_cell_index(struct dgn *dgn, uint32_t x, uint32_t y, uint32_t z);
|
||||
@@ -134,5 +158,6 @@ static inline bool dgn_is_in_map(struct dgn *dgn, uint32_t x, uint32_t y, uint32
|
||||
|
||||
// Returns a list of cells visible from (x, y, z), sorted by distance from (x, y, z).
|
||||
struct dgn_cell **dgn_get_visible_cells(struct dgn *dgn, int x, int y, int z, int *nr_cells_out);
|
||||
void dgn_calc_lightmap(struct dgn *dgn);
|
||||
|
||||
#endif /* SYSTEM4_DGN_H */
|
||||
|
||||
+13
-7
@@ -23,14 +23,19 @@
|
||||
struct cg;
|
||||
|
||||
enum dtx_texture_type {
|
||||
DTX_WALL = 0,
|
||||
DTX_FLOOR = 1,
|
||||
DTX_CEILING = 2,
|
||||
DTX_STAIRS = 3,
|
||||
DTX_DOOR = 4,
|
||||
DTX_SKYBOX = 5,
|
||||
// cell textures
|
||||
DTX_WALL,
|
||||
DTX_FLOOR,
|
||||
DTX_CEILING,
|
||||
DTX_STAIRS,
|
||||
DTX_DOOR,
|
||||
DTX_LIGHTMAP,
|
||||
// skybox textures
|
||||
DTX_SKYBOX,
|
||||
};
|
||||
|
||||
#define DTX_NR_CELL_TEXTURE_TYPES (DTX_LIGHTMAP + 1)
|
||||
|
||||
struct dtx_entry {
|
||||
uint32_t size;
|
||||
uint8_t *data;
|
||||
@@ -45,6 +50,7 @@ struct dtx {
|
||||
|
||||
struct dtx *dtx_parse(uint8_t *data, size_t size);
|
||||
void dtx_free(struct dtx *dtx);
|
||||
struct cg *dtx_create_cg(struct dtx *dtx, int type, int index);
|
||||
struct cg *dtx_create_cg(struct dtx *dtx, enum dtx_texture_type type, int index);
|
||||
struct dtx *dtx_load_from_dtl(const char *path, uint32_t seed);
|
||||
|
||||
#endif /* SYSTEM4_DTX_H */
|
||||
|
||||
@@ -20,7 +20,11 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "gfx/gl.h"
|
||||
#include <cglm/cglm.h>
|
||||
#include "gfx/gfx.h"
|
||||
#include "plugin.h"
|
||||
|
||||
#define DRAWFIELD_NR_CHARACTERS 256
|
||||
|
||||
struct page;
|
||||
struct dgn;
|
||||
@@ -28,11 +32,13 @@ struct dtx;
|
||||
struct tes;
|
||||
struct dungeon_renderer;
|
||||
struct dungeon_map;
|
||||
struct drawfield_character;
|
||||
|
||||
enum draw_dungeon_version {
|
||||
DRAW_DUNGEON_1, // Rance VI
|
||||
// DRAW_DUNGEON_2, // Dungeons & Dolls
|
||||
DRAW_DUNGEON_14 // GALZOO Island
|
||||
DRAW_DUNGEON_2, // Dungeons & Dolls
|
||||
DRAW_DUNGEON_14, // GALZOO Island
|
||||
DRAW_FIELD // Pastel Chime Continue
|
||||
};
|
||||
|
||||
struct camera {
|
||||
@@ -51,6 +57,7 @@ struct camera {
|
||||
*/
|
||||
|
||||
struct dungeon_context {
|
||||
struct draw_plugin plugin;
|
||||
enum draw_dungeon_version version;
|
||||
int surface;
|
||||
bool loaded;
|
||||
@@ -60,22 +67,35 @@ struct dungeon_context {
|
||||
struct dgn *dgn;
|
||||
struct dtx *dtx;
|
||||
struct tes *tes;
|
||||
ivec3 player_pos;
|
||||
struct drawfield_character *characters;
|
||||
mat4 proj_transform;
|
||||
struct dungeon_renderer *renderer;
|
||||
struct texture texture;
|
||||
GLuint depth_buffer;
|
||||
};
|
||||
|
||||
struct dungeon_context *dungeon_context_create(enum draw_dungeon_version version, int surface);
|
||||
void dungeon_context_free(struct dungeon_context *ctx);
|
||||
struct dungeon_context *dungeon_context_create(enum draw_dungeon_version version, int width, int height);
|
||||
bool dungeon_load(struct dungeon_context *ctx, int num);
|
||||
bool dungeon_load_dungeon(struct dungeon_context *ctx, const char *filename, int num);
|
||||
bool dungeon_load_texture(struct dungeon_context *ctx, const char *filename);
|
||||
void dungeon_set_camera(int surface, float x, float y, float z, float angle, float angle_p);
|
||||
void dungeon_render(struct dungeon_context *ctx);
|
||||
void dungeon_set_perspective(int surface, int width, int height, float near, float far, float deg);
|
||||
void dungeon_set_player_pos(int surface, int x, int y, int z);
|
||||
void dungeon_set_walked(int surface, int x, int y, int z, int flag);
|
||||
int dungeon_get_walked(int surface, int x, int y, int z);
|
||||
void dungeon_set_walked_all(int surface);
|
||||
int dungeon_calc_conquer(int surface);
|
||||
bool dungeon_load_walk_data(int surface, int map, struct page **page);
|
||||
bool dungeon_save_walk_data(int surface, int map, struct page **page);
|
||||
void dungeon_paint_step(int surface, int x, int y, int z);
|
||||
void dungeon_set_chara_sprite(int surface, int num, int sprite);
|
||||
void dungeon_set_chara_pos(int surface, int num, float x, float y, float z);
|
||||
void dungeon_set_chara_cg(int surface, int num, int cg);
|
||||
void dungeon_set_chara_cg_info(int surface, int num, int num_chara_x, int num_chara_y);
|
||||
void dungeon_set_chara_show(int surface, int num, bool show);
|
||||
bool dungeon_project_world_to_screen(struct dungeon_context *ctx, vec3 world_pos, Point *screen_pos);
|
||||
|
||||
struct dungeon_context *dungeon_get_context(int surface);
|
||||
void dungeon_update(void);
|
||||
|
||||
#endif /* SYSTEM4_DUNGEON_H */
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/* Copyright (C) 2025 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef SYSTEM4_DUNGEON_GENERATOR_H
|
||||
#define SYSTEM4_DUNGEON_GENERATOR_H
|
||||
|
||||
struct dgn;
|
||||
|
||||
enum drawfield_cell_flag {
|
||||
DF_FLAG_ITEM = 1 << 0,
|
||||
DF_FLAG_ENEMY = 1 << 1,
|
||||
DF_FLAG_TRAP = 1 << 2,
|
||||
DF_FLAG_NO_TREASURE = 1 << 3,
|
||||
DF_FLAG_NO_MONSTER = 1 << 4,
|
||||
DF_FLAG_NO_TRAP = 1 << 5,
|
||||
DF_FLAG_TREASURE_ROOM = 1 << 6
|
||||
};
|
||||
|
||||
struct dgn *dgn_generate_drawdungeon2(int level);
|
||||
void dgn_paint_step(struct dgn *dgn, int x, int y);
|
||||
|
||||
struct dgn *dgn_generate_drawfield(
|
||||
int floor, int complexity, int wall_arrange_method, int floor_arrange_method,
|
||||
int field_size_x, int field_size_y, int door_lock_percent, uint32_t seed,
|
||||
uint8_t *cell_flags);
|
||||
|
||||
#endif /* SYSTEM4_DUNGEON_GENERATOR_H */
|
||||
@@ -32,6 +32,7 @@ void dungeon_map_set_all_view(int surface, int flag);
|
||||
void dungeon_map_set_cg(int surface, int index, int sprite);
|
||||
void dungeon_map_set_small_map_floor(int surface, int floor);
|
||||
void dungeon_map_set_large_map_floor(int surface, int floor);
|
||||
void dungeon_map_reveal(struct dungeon_context *ctx, int x, int y, int z, bool transparent);
|
||||
void dungeon_map_reveal(struct dungeon_context *ctx, int x, int y, int z, bool from_walk_data);
|
||||
void dungeon_map_hide(struct dungeon_context *ctx, int x, int y, int z);
|
||||
|
||||
#endif /* SYSTEM4_DUNGEON_MAP_H */
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/* Copyright (C) 2021 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM4_MTRAND43_H
|
||||
#define SYSTEM4_MTRAND43_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define MTRAND43_STATE_SIZE 4
|
||||
|
||||
// Mersenne Twister engine with N=4, M=3
|
||||
struct mtrand43 {
|
||||
uint32_t st[MTRAND43_STATE_SIZE];
|
||||
int i;
|
||||
};
|
||||
|
||||
void mtrand43_init(struct mtrand43 *mt, uint32_t seed);
|
||||
uint32_t mtrand43_genrand(struct mtrand43 *mt);
|
||||
float mtrand43_genfloat(struct mtrand43 *mt);
|
||||
|
||||
#endif /* SYSTEM4_MTRAND43_H */
|
||||
@@ -20,16 +20,32 @@
|
||||
#include <cglm/cglm.h>
|
||||
#include "gfx/gl.h"
|
||||
|
||||
struct texture;
|
||||
struct dgn_cell;
|
||||
struct dtx;
|
||||
struct polyobj;
|
||||
struct dungeon_renderer;
|
||||
|
||||
struct dungeon_renderer *dungeon_renderer_create(enum draw_dungeon_version version, struct dtx *dtx, GLuint *event_textures, int nr_event_textures, struct polyobj *po);
|
||||
struct drawfield_character {
|
||||
vec3 pos;
|
||||
int sprite;
|
||||
int rows;
|
||||
int cols;
|
||||
int cg_index;
|
||||
bool show;
|
||||
};
|
||||
|
||||
struct dungeon_renderer *dungeon_renderer_create(enum draw_dungeon_version version, int num, struct dtx *dtx, GLuint *event_textures, int nr_event_textures, struct polyobj *po);
|
||||
void dungeon_renderer_free(struct dungeon_renderer *r);
|
||||
void dungeon_renderer_render(struct dungeon_renderer *r, struct dgn_cell **cells, int nr_cells, mat4 view_transform, mat4 proj_transform);
|
||||
void dungeon_renderer_render(struct dungeon_renderer *r, struct dgn_cell **cells, int nr_cells, struct drawfield_character *characters, mat4 view_transform, mat4 proj_transform);
|
||||
void dungeon_renderer_enable_event_markers(struct dungeon_renderer *r, bool enable);
|
||||
bool dungeon_renderer_event_markers_enabled(struct dungeon_renderer *r);
|
||||
bool dungeon_renderer_is_floor_opaque(struct dungeon_renderer *r, struct dgn_cell *cell);
|
||||
void dungeon_renderer_run_post_processing(struct dungeon_renderer *r, struct texture *src, struct texture *dst);
|
||||
void dungeon_renderer_set_raster_scroll(struct dungeon_renderer *r, int type);
|
||||
void dungeon_renderer_set_raster_amp(struct dungeon_renderer *r, float amp);
|
||||
void dungeon_renderer_enable_lightmap(struct dungeon_renderer *r, bool enable);
|
||||
bool dungeon_renderer_get_draw_obj_flag(struct dungeon_renderer *r, int type);
|
||||
void dungeon_renderer_set_draw_obj_flag(struct dungeon_renderer *r, int type, bool flag);
|
||||
|
||||
#endif /* SYSTEM4_DUNGEON_RENDERER_H */
|
||||
|
||||
@@ -19,10 +19,11 @@
|
||||
|
||||
#include <cglm/types.h>
|
||||
|
||||
enum draw_dungeon_version;
|
||||
struct dtx;
|
||||
struct skybox;
|
||||
|
||||
struct skybox *skybox_create(struct dtx *dtx);
|
||||
struct skybox *skybox_create(enum draw_dungeon_version version, struct dtx *dtx);
|
||||
void skybox_free(struct skybox *s);
|
||||
void skybox_render(struct skybox *s, const mat4 view_transform, const mat4 proj_transform);
|
||||
|
||||
|
||||
+10
-1
@@ -17,6 +17,8 @@
|
||||
#ifndef SYSTEM4_EFFECT_H
|
||||
#define SYSTEM4_EFFECT_H
|
||||
|
||||
struct texture;
|
||||
|
||||
enum effect {
|
||||
EFFECT_CROSSFADE = 1,
|
||||
EFFECT_FADEOUT = 2,
|
||||
@@ -61,7 +63,7 @@ enum effect {
|
||||
EFFECT_VERTICAL_BAR_BLUR_HD = 41,
|
||||
EFFECT_AMAP_CROSSFADE2 = 42,
|
||||
EFFECT_ZOOM_LR = 43,
|
||||
EFFECT_ZOOR_RL = 44,
|
||||
EFFECT_ZOOM_RL = 44,
|
||||
EFFECT_CROSSFADE_LR = 45,
|
||||
EFFECT_CROSSFADE_RL = 46,
|
||||
EFFECT_PIXEL_EXPLOSION = 47,
|
||||
@@ -70,9 +72,16 @@ enum effect {
|
||||
EFFECT_BLUR_FADEOUT = 50,
|
||||
EFFECT_BLUR_CROSSFADE = 51,
|
||||
EFFECT_2ROT_ZOOM_BLEND_BLUR = 52,
|
||||
EFFECT_VWAVE_SCROLL_CROSSFADE = 55,
|
||||
EFFECT_VWAVE_CROSSFADE = 60,
|
||||
NR_EFFECTS
|
||||
};
|
||||
|
||||
extern const char *effect_names[NR_EFFECTS];
|
||||
|
||||
int effect_init(enum effect type);
|
||||
void effect_update_texture(int type, struct texture *dst, struct texture *old, struct texture *new, float rate);
|
||||
int effect_update(float rate);
|
||||
int effect_fini(void);
|
||||
|
||||
#endif /* SYSTEM4_EFFECT_H */
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM4_FONT_H
|
||||
#define SYSTEM4_FONT_H
|
||||
|
||||
#include "system4.h"
|
||||
#include "gfx/gfx.h"
|
||||
|
||||
struct fnl;
|
||||
struct hash_table;
|
||||
|
||||
// standard font weight values
|
||||
enum {
|
||||
FW_NORMAL = 400,
|
||||
FW_BOLD = 700,
|
||||
FW_MEDIUM = 1400,
|
||||
FW_HEAVY = 1700
|
||||
};
|
||||
|
||||
enum font_face {
|
||||
FONT_GOTHIC = 0,
|
||||
FONT_MINCHO = 1
|
||||
};
|
||||
|
||||
enum font_weight {
|
||||
FONT_WEIGHT_NORMAL,
|
||||
FONT_WEIGHT_BOLD,
|
||||
FONT_WEIGHT_HEAVY
|
||||
};
|
||||
#define NR_FONT_WEIGHTS (FONT_WEIGHT_HEAVY+1)
|
||||
|
||||
struct glyph {
|
||||
Rectangle rect;
|
||||
float advance;
|
||||
Texture t[NR_FONT_WEIGHTS];
|
||||
};
|
||||
|
||||
struct font_size {
|
||||
float size;
|
||||
int y_offset;
|
||||
struct font *font;
|
||||
struct hash_table *glyph_table;
|
||||
};
|
||||
|
||||
enum charmap {
|
||||
CHARMAP_UNICODE,
|
||||
CHARMAP_SJIS
|
||||
};
|
||||
|
||||
struct font {
|
||||
enum charmap charmap;
|
||||
struct font_size *(*get_size)(struct font *font, float size);
|
||||
float (*get_actual_size)(struct font *font, float size);
|
||||
float (*get_actual_size_round_down)(struct font *font, float size);
|
||||
bool (*get_glyph)(struct font_size *size, struct glyph *dst, uint32_t code, enum font_weight weight);
|
||||
float (*size_char)(struct font_size *size, uint32_t code);
|
||||
float (*size_char_kerning)(struct font_size *size, uint32_t code, uint32_t code_next);
|
||||
};
|
||||
|
||||
struct text_style {
|
||||
unsigned face;
|
||||
float size;
|
||||
float bold_width; // SengokuRanceFont
|
||||
unsigned weight; // SACT2, etc.
|
||||
float edge_left;
|
||||
float edge_up;
|
||||
float edge_right;
|
||||
float edge_down;
|
||||
SDL_Color color;
|
||||
SDL_Color edge_color;
|
||||
float scale_x;
|
||||
float space_scale_x;
|
||||
float font_spacing;
|
||||
|
||||
// cached, for SengokuRanceFont
|
||||
struct font_size *font_size;
|
||||
};
|
||||
|
||||
enum text_render_mode {
|
||||
RENDER_COPY,
|
||||
RENDER_BLENDED,
|
||||
RENDER_PMAP,
|
||||
RENDER_AMAP,
|
||||
};
|
||||
|
||||
struct text_render_metrics {
|
||||
float x;
|
||||
int y;
|
||||
SDL_Color color;
|
||||
enum font_weight weight;
|
||||
float edge_width;
|
||||
float scale_x;
|
||||
float space_scale_x;
|
||||
float font_spacing;
|
||||
float edge_spacing;
|
||||
enum text_render_mode mode;
|
||||
struct font_size *font_size;
|
||||
};
|
||||
|
||||
static inline float text_style_edge_width(struct text_style *ts)
|
||||
{
|
||||
return max(max(ts->edge_up, ts->edge_down), max(ts->edge_left, ts->edge_right));
|
||||
}
|
||||
|
||||
static inline bool text_style_has_edge(struct text_style *ts)
|
||||
{
|
||||
return text_style_edge_width(ts) > 0.01f;
|
||||
}
|
||||
|
||||
static inline void text_style_set_edge_width(struct text_style *ts, float w)
|
||||
{
|
||||
ts->edge_left = w;
|
||||
ts->edge_up = w;
|
||||
ts->edge_right = w;
|
||||
ts->edge_down = w;
|
||||
}
|
||||
|
||||
extern bool gfx_text_advance_edges;
|
||||
|
||||
void gfx_font_init(void);
|
||||
void ft_font_init(void);
|
||||
struct font *ft_font_load(const char *path);
|
||||
struct font *fnl_font_load(struct fnl *lib, unsigned index);
|
||||
|
||||
bool gfx_set_font(enum font_face face, unsigned int size);
|
||||
bool gfx_set_font_size(unsigned int size);
|
||||
bool gfx_set_font_face(enum font_face face);
|
||||
bool gfx_set_font_weight(int weight);
|
||||
bool gfx_set_font_underline(bool on);
|
||||
bool gfx_set_font_strikeout(bool on);
|
||||
bool gfx_set_font_space(int space);
|
||||
bool gfx_set_font_color(SDL_Color color);
|
||||
|
||||
int gfx_get_font_size(void);
|
||||
enum font_face gfx_get_font_face(void);
|
||||
int gfx_get_font_weight(void);
|
||||
bool gfx_get_font_underline(void);
|
||||
bool gfx_get_font_strikeout(void);
|
||||
int gfx_get_font_space(void);
|
||||
SDL_Color gfx_get_font_color(void);
|
||||
void gfx_set_font_name(const char *name);
|
||||
|
||||
struct font_size *gfx_font_get_size(unsigned face, float size);
|
||||
enum font_weight gfx_int_to_font_weight(int weight);
|
||||
float _gfx_render_text(Texture *dst, char *msg, struct text_render_metrics *tm);
|
||||
|
||||
int gfx_render_text(Texture *dst, float x, int y, char *msg, struct text_style *ts, bool blend);
|
||||
float gfx_render_textf(Texture *dst, float x, int y, char *msg, struct text_style *ts, bool blend);
|
||||
void gfx_render_dash_text(Texture *dst, struct text_style *ts);
|
||||
void gfx_draw_text_to_amap(Texture *dst, int x, int y, char *text);
|
||||
void gfx_draw_text_to_pmap(Texture *dst, int x, int y, char *text);
|
||||
float gfx_size_char_unstyled(struct text_style *ts, const char *ch);
|
||||
float gfx_size_char(struct text_style *ts, const char *ch);
|
||||
float gfx_size_text(struct text_style *ts, const char *text);
|
||||
float gfx_get_actual_font_size(unsigned face, float size);
|
||||
float gfx_get_actual_font_size_round_down(unsigned face, float size);
|
||||
|
||||
static inline float text_style_height(struct text_style *ts)
|
||||
{
|
||||
return ts->size + ceilf(ts->bold_width)*2 + ts->edge_up + ts->edge_down;
|
||||
}
|
||||
|
||||
#endif /* SYSTEM4_FONT_H */
|
||||
+63
-94
@@ -25,6 +25,7 @@
|
||||
|
||||
struct cg;
|
||||
struct text_metrics;
|
||||
enum cg_type;
|
||||
|
||||
enum draw_method {
|
||||
DRAW_METHOD_NORMAL,
|
||||
@@ -35,11 +36,7 @@ enum draw_method {
|
||||
|
||||
typedef struct texture {
|
||||
GLuint handle;
|
||||
mat4 world_transform;
|
||||
int w, h;
|
||||
bool has_alpha;
|
||||
int alpha_mod;
|
||||
enum draw_method draw_method;
|
||||
} Texture;
|
||||
|
||||
struct gfx_render_job;
|
||||
@@ -50,37 +47,59 @@ typedef struct shader {
|
||||
GLuint world_transform;
|
||||
GLuint view_transform;
|
||||
GLuint texture;
|
||||
GLuint vertex;
|
||||
GLuint alpha_mod;
|
||||
GLuint vertex_pos;
|
||||
GLuint vertex_uv;
|
||||
void (*prepare)(struct gfx_render_job *, void *);
|
||||
} Shader;
|
||||
|
||||
enum gfx_shape {
|
||||
GFX_RECTANGLE,
|
||||
GFX_LINE,
|
||||
GFX_QUADRILATERAL,
|
||||
};
|
||||
|
||||
struct gfx_render_job {
|
||||
struct shader *shader;
|
||||
enum gfx_shape shape;
|
||||
GLuint texture;
|
||||
GLfloat *world_transform;
|
||||
GLfloat *view_transform;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct gfx_vertex {
|
||||
GLfloat x, y, z, w;
|
||||
GLfloat u, v;
|
||||
};
|
||||
|
||||
int gfx_init(void);
|
||||
void gfx_fini(void);
|
||||
|
||||
Texture *gfx_main_surface(void);
|
||||
void gfx_set_window_logical_size(int w, int h);
|
||||
bool gfx_set_fullscreen(bool enable);
|
||||
bool gfx_is_fullscreen(void);
|
||||
void gfx_toggle_fullscreen(void);
|
||||
void gfx_update_screen_scale(void);
|
||||
void gfx_set_wait_vsync(bool wait);
|
||||
float gfx_get_frame_rate(void);
|
||||
|
||||
void gfx_load_shader(struct shader *dst, const char *vertex_shader_path, const char *fragment_shader_path);
|
||||
GLuint gfx_load_shader_file(const char *path, GLenum type, const char *defines);
|
||||
|
||||
// rendering
|
||||
void gfx_set_clear_color(int r, int g, int b, int a);
|
||||
void gfx_set_view_offset(int x, int y);
|
||||
void gfx_clear(void);
|
||||
void gfx_swap(void);
|
||||
void gfx_set_view(struct texture *t);
|
||||
void gfx_reset_view(void);
|
||||
void gfx_prepare_job(struct gfx_render_job *job);
|
||||
void gfx_run_job(struct gfx_render_job *job);
|
||||
void gfx_render(struct gfx_render_job *job);
|
||||
void _gfx_render_texture(struct shader *s, struct texture *t, Rectangle *r, void *data);
|
||||
void gfx_render_texture(struct texture *t, Rectangle *r);
|
||||
void gfx_render_quadrilateral(struct texture *t, struct gfx_vertex vertices[4]);
|
||||
|
||||
// texture management
|
||||
void gfx_init_texture_blank(struct texture *t, int w, int h);
|
||||
@@ -88,12 +107,16 @@ void gfx_init_texture_with_cg(struct texture *t, struct cg *cg);
|
||||
void gfx_init_texture_rgba(struct texture *t, int w, int h, SDL_Color color);
|
||||
void gfx_init_texture_rgb(struct texture *t, int w, int h, SDL_Color color);
|
||||
void gfx_init_texture_with_pixels(struct texture *t, int w, int h, void *pixels);
|
||||
void gfx_init_texture_amap(struct texture *t, int w, int h, uint8_t *amap, SDL_Color color);
|
||||
void gfx_init_texture_rmap(struct texture *t, int w, int h, uint8_t *rmap);
|
||||
void gfx_update_texture_with_pixels(struct texture *t, void *pixels);
|
||||
void gfx_copy_main_surface(struct texture *dst);
|
||||
void gfx_delete_texture(struct texture *t);
|
||||
GLuint gfx_set_framebuffer(GLenum target, Texture *t, int x, int y, int w, int h);
|
||||
void gfx_reset_framebuffer(GLenum target, GLuint fbo);
|
||||
SDL_Color gfx_get_pixel(Texture *t, int x, int y);
|
||||
void *gfx_get_pixels(Texture *t);
|
||||
int gfx_save_texture(Texture *t, const char *path, enum cg_type);
|
||||
|
||||
// drawing
|
||||
void gfx_draw_init(void);
|
||||
@@ -108,28 +131,51 @@ void gfx_copy_use_amap_border(struct texture *dst, int dx, int dy, struct textur
|
||||
void gfx_copy_amap_max(struct texture *dst, int dx, int dy, struct texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_copy_amap_min(struct texture *dst, int dx, int dy, struct texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_blend(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int a);
|
||||
void gfx_blend_src_bright(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int a, int rate);
|
||||
void gfx_blend_add_satur(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_blend_amap(struct texture *dst, int dx, int dy, struct texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_blend_amap_src_only(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_blend_amap_color(struct texture *dst, int dx, int dy, struct texture *src, int sx, int sy, int w, int h, int r, int g, int b);
|
||||
void gfx_blend_amap_color_alpha(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int r, int g, int b, int a);
|
||||
void gfx_blend_amap_alpha(struct texture *dst, int dx, int dy, struct texture *src, int sx, int sy, int w, int h, int a);
|
||||
void gfx_blend_amap_bright(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int rate);
|
||||
void gfx_blend_amap_alpha_src_bright(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int alpha, int rate);
|
||||
void gfx_blend_use_amap_color(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int r, int g, int b, int rate);
|
||||
void gfx_blend_screen(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_blend_multiply(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_blend_screen_alpha(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int a);
|
||||
void gfx_fill(struct texture *dst, int x, int y, int w, int h, int r, int g, int b);
|
||||
void gfx_fill_alpha_color(struct texture *dst, int x, int y, int w, int h, int r, int g, int b, int a);
|
||||
void gfx_fill_amap(struct texture *dst, int x, int y, int w, int h, int a);
|
||||
void gfx_fill_amap_over_border(Texture *dst, int x, int y, int w, int h, int alpha, int border);
|
||||
void gfx_fill_amap_under_border(Texture *dst, int x, int y, int w, int h, int alpha, int border);
|
||||
void gfx_fill_amap_gradation_ud(Texture *dst, int x, int y, int w, int h, int up_a, int down_a);
|
||||
void gfx_fill_screen(Texture *dst, int x, int y, int w, int h, int r, int g, int b);
|
||||
void gfx_fill_multiply(Texture *dst, int x, int y, int w, int h, int r, int g, int b);
|
||||
void gfx_satur_dp_dpxsa(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_screen_da_daxsa(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_add_da_daxsa(struct texture *dst, int dx, int dy, struct texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_blend_da_daxsa(struct texture *dst, int dx, int dy, struct texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_sub_da_daxsa(struct texture *dst, int dx, int dy, struct texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_bright_dest_only(Texture *dst, int x, int y, int w, int h, int rate);
|
||||
void gfx_copy_stretch(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh);
|
||||
void gfx_copy_stretch_amap(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh);
|
||||
void gfx_copy_stretch_blend(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh, int a);
|
||||
void gfx_copy_stretch_blend_screen(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh);
|
||||
void gfx_copy_stretch_blend_amap(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh);
|
||||
void gfx_copy_stretch_blend_amap_alpha(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh, int a);
|
||||
void gfx_copy_rot_zoom(Texture *dst, Texture *src, int sx, int sy, int w, int h, float rotate, float mag);
|
||||
void gfx_copy_rot_zoom_amap(Texture *dst, Texture *src, int sx, int sy, int w, int h, float rotate, float mag);
|
||||
void gfx_copy_rot_zoom_use_amap(Texture *dst, Texture *src, int sx, int sy, int w, int h, float rotate, float mag);
|
||||
void gfx_copy_rot_zoom2(Texture *dst, float cx, float cy, Texture *src, float scx, float scy, float rot, float mag);
|
||||
void gfx_copy_reverse_LR(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_copy_reverse_UD(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_copy_reverse_amap_LR(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_fill_amap_over_border(Texture *dst, int x, int y, int w, int h, int alpha, int border);
|
||||
void gfx_fill_amap_under_border(Texture *dst, int x, int y, int w, int h, int alpha, int border);
|
||||
void gfx_copy_reverse_LR_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_copy_rotate_y(Texture *dst, Texture *front, Texture *back, int sx, int sy, int w, int h, float rot, float mag);
|
||||
void gfx_copy_rotate_y_use_amap(Texture *dst, Texture *front, Texture *back, int sx, int sy, int w, int h, float rot, float mag);
|
||||
void gfx_copy_rotate_x(Texture *dst, Texture *front, Texture *back, int sx, int sy, int w, int h, float rot, float mag);
|
||||
void gfx_copy_rotate_x_use_amap(Texture *dst, Texture *front, Texture *back, int sx, int sy, int w, int h, float rot, float mag);
|
||||
void gfx_copy_width_blur(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int blur);
|
||||
void gfx_copy_height_blur(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int blur);
|
||||
void gfx_copy_amap_width_blur(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int blur);
|
||||
@@ -137,91 +183,14 @@ void gfx_copy_amap_height_blur(Texture *dst, int dx, int dy, Texture *src, int s
|
||||
void gfx_copy_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_fill_with_alpha(Texture *dst, int x, int y, int w, int h, int r, int g, int b, int a);
|
||||
void gfx_copy_stretch_with_alpha_map(Texture *dst, int dx, int dy, int dw, int dh, Texture *src, int sx, int sy, int sw, int sh);
|
||||
|
||||
enum {
|
||||
FW_NORMAL = 400,
|
||||
FW_BOLD = 700,
|
||||
FW_NORMAL2 = 1400,
|
||||
FW_BOLD2 = 1700
|
||||
};
|
||||
|
||||
enum font_face {
|
||||
FONT_GOTHIC = 0,
|
||||
FONT_MINCHO = 1
|
||||
};
|
||||
|
||||
extern const char *font_paths[2];
|
||||
|
||||
struct text_metrics {
|
||||
SDL_Color color;
|
||||
SDL_Color outline_color;
|
||||
unsigned int size;
|
||||
int weight;
|
||||
enum font_face face;
|
||||
int outline_left;
|
||||
int outline_up;
|
||||
int outline_right;
|
||||
int outline_down;
|
||||
};
|
||||
|
||||
struct font_metrics {
|
||||
int size;
|
||||
enum font_face face;
|
||||
int weight;
|
||||
bool underline;
|
||||
bool strikeout;
|
||||
int space;
|
||||
SDL_Color color;
|
||||
};
|
||||
|
||||
void gfx_font_init(void);
|
||||
bool gfx_set_font(enum font_face face, unsigned int size);
|
||||
|
||||
bool gfx_set_font_size(unsigned int size);
|
||||
bool gfx_set_font_face(enum font_face face);
|
||||
bool gfx_set_font_weight(int weight);
|
||||
bool gfx_set_font_underline(bool on);
|
||||
bool gfx_set_font_strikeout(bool on);
|
||||
bool gfx_set_font_space(int space);
|
||||
bool gfx_set_font_color(SDL_Color color);
|
||||
|
||||
int gfx_get_font_size(void);
|
||||
enum font_face gfx_get_font_face(void);
|
||||
int gfx_get_font_weight(void);
|
||||
bool gfx_get_font_underline(void);
|
||||
bool gfx_get_font_strikeout(void);
|
||||
int gfx_get_font_space(void);
|
||||
SDL_Color gfx_get_font_color(void);
|
||||
void gfx_set_font_name(const char *name);
|
||||
|
||||
int gfx_render_text(Texture *dst, Point pos, char *msg, struct text_metrics *tm, int char_space);
|
||||
void gfx_draw_text_to_amap(Texture *dst, int x, int y, char *text);
|
||||
void gfx_draw_text_to_pmap(Texture *dst, int x, int y, char *text);
|
||||
|
||||
struct fnl_font_inst {
|
||||
struct fnl_font_face *font;
|
||||
float scale;
|
||||
SDL_Color color;
|
||||
SDL_Color outline_color;
|
||||
int outline_size;
|
||||
};
|
||||
|
||||
struct text_style {
|
||||
unsigned font_type;
|
||||
float size;
|
||||
float bold_width;
|
||||
SDL_Color color;
|
||||
float edge_width;
|
||||
SDL_Color edge_color;
|
||||
float scale_x;
|
||||
float space_scale_x;
|
||||
float font_spacing;
|
||||
|
||||
struct fnl_font_size *font_size;
|
||||
};
|
||||
|
||||
struct fnl;
|
||||
struct fnl_font_face;
|
||||
int fnl_draw_text(struct fnl *fnl, struct text_style *ts, Texture *dst, int x, int y, char *text);
|
||||
void gfx_copy_grayscale(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_copy_grayscale_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_copy_grayscale_reverse_LR_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_draw_line(Texture *dst, int x0, int y0, int x1, int y1, int r, int g, int b);
|
||||
void gfx_draw_line_to_amap(Texture *dst, int x0, int y0, int x1, int y1, int a);
|
||||
void gfx_draw_glyph(Texture *dst, float dx, int dy, Texture *glyph, SDL_Color color, float scale_x, float bold_width, bool blend);
|
||||
void gfx_draw_glyph_to_pmap(Texture *dst, float dx, int dy, Texture *glyph, Rectangle glyph_pos, SDL_Color color, float scale_x);
|
||||
void gfx_draw_glyph_to_amap(Texture *dst, float dx, int dy, Texture *glyph, Rectangle glyph_pos, float scale_x);
|
||||
void gfx_draw_quadrilateral(Texture *dst, Texture *src, struct gfx_vertex vertices[4]);
|
||||
|
||||
#endif /* SYSTEM4_SDL_CORE_H */
|
||||
|
||||
@@ -31,7 +31,9 @@ struct sdl_private {
|
||||
SDL_GLContext context;
|
||||
GLuint vao;
|
||||
GLuint vbo;
|
||||
GLuint ibo;
|
||||
GLuint quad_vbo;
|
||||
GLuint rect_ibo;
|
||||
GLuint line_ibo;
|
||||
} gl;
|
||||
int w, h;
|
||||
SDL_Rect viewport;
|
||||
|
||||
@@ -56,4 +56,10 @@ typedef SDL_Rect Rectangle;
|
||||
0, 0, 1, -1, \
|
||||
0, 0, 0, 1)
|
||||
|
||||
#define WORLD_TRANSFORM(w, h, x, y) \
|
||||
MAT4(w, 0, 0, x, \
|
||||
0, h, 0, y, \
|
||||
0, 0, 1, 0, \
|
||||
0, 0, 0, 1)
|
||||
|
||||
#endif /* SYSTEM4_GRAPHICS_H */
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/* Copyright (C) 2025 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM4_ICON_H
|
||||
#define SYSTEM4_ICON_H
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#define icon_init()
|
||||
#else
|
||||
void icon_init(void);
|
||||
#endif
|
||||
|
||||
#endif /* SYSTEM4_ICON_H */
|
||||
+10
-2
@@ -17,14 +17,22 @@
|
||||
#ifndef SYSTEM4_ID_POOL_H
|
||||
#define SYSTEM4_ID_POOL_H
|
||||
|
||||
// A reasonable base value for pointer-based handles.
|
||||
#define ID_POOL_HANDLE_BASE 0x100000
|
||||
|
||||
struct id_pool {
|
||||
void **slots;
|
||||
int nr_slots;
|
||||
int base;
|
||||
};
|
||||
|
||||
void id_pool_init(struct id_pool *pool);
|
||||
void id_pool_release(struct id_pool *pool, int id);
|
||||
void id_pool_init(struct id_pool *pool, int base);
|
||||
void id_pool_delete(struct id_pool *pool);
|
||||
void *id_pool_release(struct id_pool *pool, int id);
|
||||
int id_pool_count(struct id_pool *pool);
|
||||
int id_pool_get_unused(struct id_pool *pool);
|
||||
int id_pool_get_first(struct id_pool *pool);
|
||||
int id_pool_get_next(struct id_pool *pool, int id);
|
||||
void *id_pool_get(struct id_pool *pool, int id);
|
||||
void *id_pool_set(struct id_pool *pool, int id, void *data);
|
||||
|
||||
|
||||
+5
-1
@@ -137,11 +137,13 @@ enum sact_keycode {
|
||||
extern bool mouse_focus;
|
||||
extern bool keyboard_focus;
|
||||
|
||||
void handle_window_events(void);
|
||||
void handle_events(void);
|
||||
bool key_is_down(enum sact_keycode code);
|
||||
void key_clear_flag(void);
|
||||
void key_clear_flag(bool no_ctrl);
|
||||
bool joy_key_is_down(uint8_t code);
|
||||
void joy_clear_flag(void);
|
||||
bool joy_get_stick_status(int controller, int type, float *deg, float *pow);
|
||||
void mouse_get_pos(int *x, int *y);
|
||||
void mouse_set_pos(int x, int y);
|
||||
void mouse_get_wheel(int *forward, int *back);
|
||||
@@ -151,5 +153,7 @@ void register_input_handler(void(*handler)(const char*));
|
||||
void clear_input_handler(void);
|
||||
void register_editing_handler(void(*handler)(const char*, int, int));
|
||||
void clear_editing_handler(void);
|
||||
void register_key_handler(void(*handler)(int));
|
||||
void clear_key_handler(void);
|
||||
|
||||
#endif /* SYSTEM4_INPUT_H */
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/* Copyright (C) 2023 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef XSYSTEM4_JSON_H
|
||||
#define XSYSTEM4_JSON_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "gfx/types.h"
|
||||
|
||||
typedef struct cJSON cJSON;
|
||||
struct texture;
|
||||
struct text_style;
|
||||
struct sact_sprite;
|
||||
struct sprite;
|
||||
|
||||
cJSON *num2_to_json(double a, double b);
|
||||
cJSON *num2_to_json_object(const char *a_name, double a, const char *b_name, double b);
|
||||
cJSON *num3_to_json(double a, double b, double c);
|
||||
cJSON *num3_to_json_object(const char *a_name, double a, const char *b_name, double b,
|
||||
const char *c_name, double c);
|
||||
cJSON *num4_to_json(double a, double b, double c, double d);
|
||||
cJSON *num4_to_json_object(const char *a_name, double a, const char *b_name, double b,
|
||||
const char *c_name, double c, const char *d_name, double d);
|
||||
cJSON *texture_to_json(struct texture *t, bool verbose);
|
||||
cJSON *texture_to_json_with_pixels(struct texture *t);
|
||||
cJSON *text_style_to_json(struct text_style *ts, bool verbose);
|
||||
cJSON *scene_sprite_to_json(struct sprite *sp, bool verbose);
|
||||
cJSON *sprite_to_json(struct sact_sprite *sp, bool verbose);
|
||||
|
||||
static inline cJSON *xy_to_json(double x, double y, bool verbose)
|
||||
{
|
||||
if (!verbose)
|
||||
return num2_to_json(x, y);
|
||||
return num2_to_json_object("x", x, "y", y);
|
||||
}
|
||||
|
||||
static inline cJSON *wh_to_json(double w, double h, bool verbose)
|
||||
{
|
||||
if (!verbose)
|
||||
return num2_to_json(w, h);
|
||||
return num2_to_json_object("w", w, "h", h);
|
||||
}
|
||||
|
||||
static inline cJSON *point_to_json(Point *p, bool verbose)
|
||||
{
|
||||
return xy_to_json(p->x, p->y, verbose);
|
||||
}
|
||||
|
||||
static inline cJSON *rectangle_to_json(Rectangle *r, bool verbose)
|
||||
{
|
||||
if (!verbose)
|
||||
return num4_to_json(r->x, r->y, r->w, r->h);
|
||||
return num4_to_json_object("x", r->x, "y", r->y, "w", r->w, "h", r->h);
|
||||
}
|
||||
|
||||
static inline cJSON *color_to_json(Color *c, bool verbose)
|
||||
{
|
||||
if (!verbose)
|
||||
return num4_to_json(c->r, c->g, c->b, c->a);
|
||||
return num4_to_json_object("r", c->r, "g", c->g, "b", c->b, "a", c->a);
|
||||
}
|
||||
|
||||
static inline cJSON *vec3_point_to_json(vec3 v, bool verbose)
|
||||
{
|
||||
if (!verbose)
|
||||
return num3_to_json(v[0], v[1], v[2]);
|
||||
return num3_to_json_object("x", v[0], "y", v[1], "z", v[2]);
|
||||
}
|
||||
|
||||
static inline cJSON *vec3_color_to_json(vec3 v, bool verbose)
|
||||
{
|
||||
if (!verbose)
|
||||
return num3_to_json(v[0], v[1], v[2]);
|
||||
return num3_to_json_object("r", v[0], "g", v[1], "b", v[2]);
|
||||
}
|
||||
|
||||
#endif
|
||||
-627
@@ -1,627 +0,0 @@
|
||||
/* The MIT License
|
||||
|
||||
Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
An example:
|
||||
|
||||
#include "khash.h"
|
||||
KHASH_MAP_INIT_INT(32, char)
|
||||
int main() {
|
||||
int ret, is_missing;
|
||||
khiter_t k;
|
||||
khash_t(32) *h = kh_init(32);
|
||||
k = kh_put(32, h, 5, &ret);
|
||||
kh_value(h, k) = 10;
|
||||
k = kh_get(32, h, 10);
|
||||
is_missing = (k == kh_end(h));
|
||||
k = kh_get(32, h, 5);
|
||||
kh_del(32, h, k);
|
||||
for (k = kh_begin(h); k != kh_end(h); ++k)
|
||||
if (kh_exist(h, k)) kh_value(h, k) = 1;
|
||||
kh_destroy(32, h);
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
2013-05-02 (0.2.8):
|
||||
|
||||
* Use quadratic probing. When the capacity is power of 2, stepping function
|
||||
i*(i+1)/2 guarantees to traverse each bucket. It is better than double
|
||||
hashing on cache performance and is more robust than linear probing.
|
||||
|
||||
In theory, double hashing should be more robust than quadratic probing.
|
||||
However, my implementation is probably not for large hash tables, because
|
||||
the second hash function is closely tied to the first hash function,
|
||||
which reduce the effectiveness of double hashing.
|
||||
|
||||
Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php
|
||||
|
||||
2011-12-29 (0.2.7):
|
||||
|
||||
* Minor code clean up; no actual effect.
|
||||
|
||||
2011-09-16 (0.2.6):
|
||||
|
||||
* The capacity is a power of 2. This seems to dramatically improve the
|
||||
speed for simple keys. Thank Zilong Tan for the suggestion. Reference:
|
||||
|
||||
- http://code.google.com/p/ulib/
|
||||
- http://nothings.org/computer/judy/
|
||||
|
||||
* Allow to optionally use linear probing which usually has better
|
||||
performance for random input. Double hashing is still the default as it
|
||||
is more robust to certain non-random input.
|
||||
|
||||
* Added Wang's integer hash function (not used by default). This hash
|
||||
function is more robust to certain non-random input.
|
||||
|
||||
2011-02-14 (0.2.5):
|
||||
|
||||
* Allow to declare global functions.
|
||||
|
||||
2009-09-26 (0.2.4):
|
||||
|
||||
* Improve portability
|
||||
|
||||
2008-09-19 (0.2.3):
|
||||
|
||||
* Corrected the example
|
||||
* Improved interfaces
|
||||
|
||||
2008-09-11 (0.2.2):
|
||||
|
||||
* Improved speed a little in kh_put()
|
||||
|
||||
2008-09-10 (0.2.1):
|
||||
|
||||
* Added kh_clear()
|
||||
* Fixed a compiling error
|
||||
|
||||
2008-09-02 (0.2.0):
|
||||
|
||||
* Changed to token concatenation which increases flexibility.
|
||||
|
||||
2008-08-31 (0.1.2):
|
||||
|
||||
* Fixed a bug in kh_get(), which has not been tested previously.
|
||||
|
||||
2008-08-31 (0.1.1):
|
||||
|
||||
* Added destructor
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __AC_KHASH_H
|
||||
#define __AC_KHASH_H
|
||||
|
||||
/*!
|
||||
@header
|
||||
|
||||
Generic hash table library.
|
||||
*/
|
||||
|
||||
#define AC_VERSION_KHASH_H "0.2.8"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
/* compiler specific configuration */
|
||||
|
||||
#if UINT_MAX == 0xffffffffu
|
||||
typedef unsigned int khint32_t;
|
||||
#elif ULONG_MAX == 0xffffffffu
|
||||
typedef unsigned long khint32_t;
|
||||
#endif
|
||||
|
||||
#if ULONG_MAX == ULLONG_MAX
|
||||
typedef unsigned long khint64_t;
|
||||
#else
|
||||
typedef unsigned long long khint64_t;
|
||||
#endif
|
||||
|
||||
#ifndef kh_inline
|
||||
#ifdef _MSC_VER
|
||||
#define kh_inline __inline
|
||||
#else
|
||||
#define kh_inline inline
|
||||
#endif
|
||||
#endif /* kh_inline */
|
||||
|
||||
#ifndef klib_unused
|
||||
#if (defined __clang__ && __clang_major__ >= 3) || (defined __GNUC__ && __GNUC__ >= 3)
|
||||
#define klib_unused __attribute__ ((__unused__))
|
||||
#else
|
||||
#define klib_unused
|
||||
#endif
|
||||
#endif /* klib_unused */
|
||||
|
||||
typedef khint32_t khint_t;
|
||||
typedef khint_t khiter_t;
|
||||
|
||||
#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
|
||||
#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
|
||||
#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
|
||||
#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
|
||||
#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
|
||||
#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
|
||||
#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
|
||||
|
||||
#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
|
||||
|
||||
#ifndef kroundup32
|
||||
#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
|
||||
#endif
|
||||
|
||||
#ifndef kcalloc
|
||||
#define kcalloc(N,Z) calloc(N,Z)
|
||||
#endif
|
||||
#ifndef kmalloc
|
||||
#define kmalloc(Z) malloc(Z)
|
||||
#endif
|
||||
#ifndef krealloc
|
||||
#define krealloc(P,Z) realloc(P,Z)
|
||||
#endif
|
||||
#ifndef kfree
|
||||
#define kfree(P) free(P)
|
||||
#endif
|
||||
|
||||
static const double __ac_HASH_UPPER = 0.77;
|
||||
|
||||
#define __KHASH_TYPE(name, khkey_t, khval_t) \
|
||||
typedef struct kh_##name##_s { \
|
||||
khint_t n_buckets, size, n_occupied, upper_bound; \
|
||||
khint32_t *flags; \
|
||||
khkey_t *keys; \
|
||||
khval_t *vals; \
|
||||
} kh_##name##_t;
|
||||
|
||||
#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \
|
||||
extern kh_##name##_t *kh_init_##name(void); \
|
||||
extern void kh_destroy_##name(kh_##name##_t *h); \
|
||||
extern void kh_clear_##name(kh_##name##_t *h); \
|
||||
extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \
|
||||
extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
|
||||
extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
|
||||
extern void kh_del_##name(kh_##name##_t *h, khint_t x);
|
||||
|
||||
#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
|
||||
SCOPE kh_##name##_t *kh_init_##name(void) { \
|
||||
return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \
|
||||
} \
|
||||
SCOPE void kh_destroy_##name(kh_##name##_t *h) \
|
||||
{ \
|
||||
if (h) { \
|
||||
kfree((void *)h->keys); kfree(h->flags); \
|
||||
kfree((void *)h->vals); \
|
||||
kfree(h); \
|
||||
} \
|
||||
} \
|
||||
SCOPE void kh_clear_##name(kh_##name##_t *h) \
|
||||
{ \
|
||||
if (h && h->flags) { \
|
||||
memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
|
||||
h->size = h->n_occupied = 0; \
|
||||
} \
|
||||
} \
|
||||
SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
|
||||
{ \
|
||||
if (h->n_buckets) { \
|
||||
khint_t k, i, last, mask, step = 0; \
|
||||
mask = h->n_buckets - 1; \
|
||||
k = __hash_func(key); i = k & mask; \
|
||||
last = i; \
|
||||
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
|
||||
i = (i + (++step)) & mask; \
|
||||
if (i == last) return h->n_buckets; \
|
||||
} \
|
||||
return __ac_iseither(h->flags, i)? h->n_buckets : i; \
|
||||
} else return 0; \
|
||||
} \
|
||||
SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
|
||||
{ /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
|
||||
khint32_t *new_flags = 0; \
|
||||
khint_t j = 1; \
|
||||
{ \
|
||||
kroundup32(new_n_buckets); \
|
||||
if (new_n_buckets < 4) new_n_buckets = 4; \
|
||||
if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \
|
||||
else { /* hash table size to be changed (shrink or expand); rehash */ \
|
||||
new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
|
||||
if (!new_flags) return -1; \
|
||||
memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
|
||||
if (h->n_buckets < new_n_buckets) { /* expand */ \
|
||||
khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
|
||||
if (!new_keys) { kfree(new_flags); return -1; } \
|
||||
h->keys = new_keys; \
|
||||
if (kh_is_map) { \
|
||||
khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
|
||||
if (!new_vals) { kfree(new_flags); return -1; } \
|
||||
h->vals = new_vals; \
|
||||
} \
|
||||
} /* otherwise shrink */ \
|
||||
} \
|
||||
} \
|
||||
if (j) { /* rehashing is needed */ \
|
||||
for (j = 0; j != h->n_buckets; ++j) { \
|
||||
if (__ac_iseither(h->flags, j) == 0) { \
|
||||
khkey_t key = h->keys[j]; \
|
||||
khval_t val; \
|
||||
khint_t new_mask; \
|
||||
new_mask = new_n_buckets - 1; \
|
||||
if (kh_is_map) val = h->vals[j]; \
|
||||
__ac_set_isdel_true(h->flags, j); \
|
||||
while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
|
||||
khint_t k, i, step = 0; \
|
||||
k = __hash_func(key); \
|
||||
i = k & new_mask; \
|
||||
while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \
|
||||
__ac_set_isempty_false(new_flags, i); \
|
||||
if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
|
||||
{ khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
|
||||
if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
|
||||
__ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
|
||||
} else { /* write the element and jump out of the loop */ \
|
||||
h->keys[i] = key; \
|
||||
if (kh_is_map) h->vals[i] = val; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
|
||||
h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
|
||||
if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
|
||||
} \
|
||||
kfree(h->flags); /* free the working space */ \
|
||||
h->flags = new_flags; \
|
||||
h->n_buckets = new_n_buckets; \
|
||||
h->n_occupied = h->size; \
|
||||
h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
|
||||
} \
|
||||
return 0; \
|
||||
} \
|
||||
SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
|
||||
{ \
|
||||
khint_t x; \
|
||||
if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
|
||||
if (h->n_buckets > (h->size<<1)) { \
|
||||
if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \
|
||||
*ret = -1; return h->n_buckets; \
|
||||
} \
|
||||
} else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \
|
||||
*ret = -1; return h->n_buckets; \
|
||||
} \
|
||||
} /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
|
||||
{ \
|
||||
khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \
|
||||
x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
|
||||
if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \
|
||||
else { \
|
||||
last = i; \
|
||||
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
|
||||
if (__ac_isdel(h->flags, i)) site = i; \
|
||||
i = (i + (++step)) & mask; \
|
||||
if (i == last) { x = site; break; } \
|
||||
} \
|
||||
if (x == h->n_buckets) { \
|
||||
if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
|
||||
else x = i; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
if (__ac_isempty(h->flags, x)) { /* not present at all */ \
|
||||
h->keys[x] = key; \
|
||||
__ac_set_isboth_false(h->flags, x); \
|
||||
++h->size; ++h->n_occupied; \
|
||||
*ret = 1; \
|
||||
} else if (__ac_isdel(h->flags, x)) { /* deleted */ \
|
||||
h->keys[x] = key; \
|
||||
__ac_set_isboth_false(h->flags, x); \
|
||||
++h->size; \
|
||||
*ret = 2; \
|
||||
} else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
|
||||
return x; \
|
||||
} \
|
||||
SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \
|
||||
{ \
|
||||
if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \
|
||||
__ac_set_isdel_true(h->flags, x); \
|
||||
--h->size; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define KHASH_DECLARE(name, khkey_t, khval_t) \
|
||||
__KHASH_TYPE(name, khkey_t, khval_t) \
|
||||
__KHASH_PROTOTYPES(name, khkey_t, khval_t)
|
||||
|
||||
#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
|
||||
__KHASH_TYPE(name, khkey_t, khval_t) \
|
||||
__KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
|
||||
|
||||
#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
|
||||
KHASH_INIT2(name, static kh_inline klib_unused, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
|
||||
|
||||
/* --- BEGIN OF HASH FUNCTIONS --- */
|
||||
|
||||
/*! @function
|
||||
@abstract Integer hash function
|
||||
@param key The integer [khint32_t]
|
||||
@return The hash value [khint_t]
|
||||
*/
|
||||
#define kh_int_hash_func(key) (khint32_t)(key)
|
||||
/*! @function
|
||||
@abstract Integer comparison function
|
||||
*/
|
||||
#define kh_int_hash_equal(a, b) ((a) == (b))
|
||||
/*! @function
|
||||
@abstract 64-bit integer hash function
|
||||
@param key The integer [khint64_t]
|
||||
@return The hash value [khint_t]
|
||||
*/
|
||||
#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)
|
||||
/*! @function
|
||||
@abstract 64-bit integer comparison function
|
||||
*/
|
||||
#define kh_int64_hash_equal(a, b) ((a) == (b))
|
||||
/*! @function
|
||||
@abstract const char* hash function
|
||||
@param s Pointer to a null terminated string
|
||||
@return The hash value
|
||||
*/
|
||||
static kh_inline khint_t __ac_X31_hash_string(const char *s)
|
||||
{
|
||||
khint_t h = (khint_t)*s;
|
||||
if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;
|
||||
return h;
|
||||
}
|
||||
/*! @function
|
||||
@abstract Another interface to const char* hash function
|
||||
@param key Pointer to a null terminated string [const char*]
|
||||
@return The hash value [khint_t]
|
||||
*/
|
||||
#define kh_str_hash_func(key) __ac_X31_hash_string(key)
|
||||
/*! @function
|
||||
@abstract Const char* comparison function
|
||||
*/
|
||||
#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)
|
||||
|
||||
static kh_inline khint_t __ac_Wang_hash(khint_t key)
|
||||
{
|
||||
key += ~(key << 15);
|
||||
key ^= (key >> 10);
|
||||
key += (key << 3);
|
||||
key ^= (key >> 6);
|
||||
key += ~(key << 11);
|
||||
key ^= (key >> 16);
|
||||
return key;
|
||||
}
|
||||
#define kh_int_hash_func2(key) __ac_Wang_hash((khint_t)key)
|
||||
|
||||
/* --- END OF HASH FUNCTIONS --- */
|
||||
|
||||
/* Other convenient macros... */
|
||||
|
||||
/*!
|
||||
@abstract Type of the hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
*/
|
||||
#define khash_t(name) kh_##name##_t
|
||||
|
||||
/*! @function
|
||||
@abstract Initiate a hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@return Pointer to the hash table [khash_t(name)*]
|
||||
*/
|
||||
#define kh_init(name) kh_init_##name()
|
||||
|
||||
/*! @function
|
||||
@abstract Destroy a hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
*/
|
||||
#define kh_destroy(name, h) kh_destroy_##name(h)
|
||||
|
||||
/*! @function
|
||||
@abstract Reset a hash table without deallocating memory.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
*/
|
||||
#define kh_clear(name, h) kh_clear_##name(h)
|
||||
|
||||
/*! @function
|
||||
@abstract Resize a hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param s New size [khint_t]
|
||||
*/
|
||||
#define kh_resize(name, h, s) kh_resize_##name(h, s)
|
||||
|
||||
/*! @function
|
||||
@abstract Insert a key to the hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param k Key [type of keys]
|
||||
@param r Extra return code: -1 if the operation failed;
|
||||
0 if the key is present in the hash table;
|
||||
1 if the bucket is empty (never used); 2 if the element in
|
||||
the bucket has been deleted [int*]
|
||||
@return Iterator to the inserted element [khint_t]
|
||||
*/
|
||||
#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
|
||||
|
||||
/*! @function
|
||||
@abstract Retrieve a key from the hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param k Key [type of keys]
|
||||
@return Iterator to the found element, or kh_end(h) if the element is absent [khint_t]
|
||||
*/
|
||||
#define kh_get(name, h, k) kh_get_##name(h, k)
|
||||
|
||||
/*! @function
|
||||
@abstract Remove a key from the hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param k Iterator to the element to be deleted [khint_t]
|
||||
*/
|
||||
#define kh_del(name, h, k) kh_del_##name(h, k)
|
||||
|
||||
/*! @function
|
||||
@abstract Test whether a bucket contains data.
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param x Iterator to the bucket [khint_t]
|
||||
@return 1 if containing data; 0 otherwise [int]
|
||||
*/
|
||||
#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
|
||||
|
||||
/*! @function
|
||||
@abstract Get key given an iterator
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param x Iterator to the bucket [khint_t]
|
||||
@return Key [type of keys]
|
||||
*/
|
||||
#define kh_key(h, x) ((h)->keys[x])
|
||||
|
||||
/*! @function
|
||||
@abstract Get value given an iterator
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param x Iterator to the bucket [khint_t]
|
||||
@return Value [type of values]
|
||||
@discussion For hash sets, calling this results in segfault.
|
||||
*/
|
||||
#define kh_val(h, x) ((h)->vals[x])
|
||||
|
||||
/*! @function
|
||||
@abstract Alias of kh_val()
|
||||
*/
|
||||
#define kh_value(h, x) ((h)->vals[x])
|
||||
|
||||
/*! @function
|
||||
@abstract Get the start iterator
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@return The start iterator [khint_t]
|
||||
*/
|
||||
#define kh_begin(h) (khint_t)(0)
|
||||
|
||||
/*! @function
|
||||
@abstract Get the end iterator
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@return The end iterator [khint_t]
|
||||
*/
|
||||
#define kh_end(h) ((h)->n_buckets)
|
||||
|
||||
/*! @function
|
||||
@abstract Get the number of elements in the hash table
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@return Number of elements in the hash table [khint_t]
|
||||
*/
|
||||
#define kh_size(h) ((h)->size)
|
||||
|
||||
/*! @function
|
||||
@abstract Get the number of buckets in the hash table
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@return Number of buckets in the hash table [khint_t]
|
||||
*/
|
||||
#define kh_n_buckets(h) ((h)->n_buckets)
|
||||
|
||||
/*! @function
|
||||
@abstract Iterate over the entries in the hash table
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param kvar Variable to which key will be assigned
|
||||
@param vvar Variable to which value will be assigned
|
||||
@param code Block of code to execute
|
||||
*/
|
||||
#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \
|
||||
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
|
||||
if (!kh_exist(h,__i)) continue; \
|
||||
(kvar) = kh_key(h,__i); \
|
||||
(vvar) = kh_val(h,__i); \
|
||||
code; \
|
||||
} }
|
||||
|
||||
/*! @function
|
||||
@abstract Iterate over the values in the hash table
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param vvar Variable to which value will be assigned
|
||||
@param code Block of code to execute
|
||||
*/
|
||||
#define kh_foreach_value(h, vvar, code) { khint_t __i; \
|
||||
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
|
||||
if (!kh_exist(h,__i)) continue; \
|
||||
(vvar) = kh_val(h,__i); \
|
||||
code; \
|
||||
} }
|
||||
|
||||
/* More convenient interfaces */
|
||||
|
||||
/*! @function
|
||||
@abstract Instantiate a hash set containing integer keys
|
||||
@param name Name of the hash table [symbol]
|
||||
*/
|
||||
#define KHASH_SET_INIT_INT(name) \
|
||||
KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
|
||||
|
||||
/*! @function
|
||||
@abstract Instantiate a hash map containing integer keys
|
||||
@param name Name of the hash table [symbol]
|
||||
@param khval_t Type of values [type]
|
||||
*/
|
||||
#define KHASH_MAP_INIT_INT(name, khval_t) \
|
||||
KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
|
||||
|
||||
/*! @function
|
||||
@abstract Instantiate a hash set containing 64-bit integer keys
|
||||
@param name Name of the hash table [symbol]
|
||||
*/
|
||||
#define KHASH_SET_INIT_INT64(name) \
|
||||
KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
|
||||
|
||||
/*! @function
|
||||
@abstract Instantiate a hash map containing 64-bit integer keys
|
||||
@param name Name of the hash table [symbol]
|
||||
@param khval_t Type of values [type]
|
||||
*/
|
||||
#define KHASH_MAP_INIT_INT64(name, khval_t) \
|
||||
KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
|
||||
|
||||
typedef const char *kh_cstr_t;
|
||||
/*! @function
|
||||
@abstract Instantiate a hash map containing const char* keys
|
||||
@param name Name of the hash table [symbol]
|
||||
*/
|
||||
#define KHASH_SET_INIT_STR(name) \
|
||||
KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
|
||||
|
||||
/*! @function
|
||||
@abstract Instantiate a hash map containing const char* keys
|
||||
@param name Name of the hash table [symbol]
|
||||
@param khval_t Type of values [type]
|
||||
*/
|
||||
#define KHASH_MAP_INIT_STR(name, khval_t) \
|
||||
KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
|
||||
|
||||
#endif /* __AC_KHASH_H */
|
||||
@@ -1,94 +0,0 @@
|
||||
/* The MIT License
|
||||
|
||||
Copyright (c) 2008, by Attractive Chaos <attractor@live.co.uk>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
An example:
|
||||
|
||||
#include "kvec.h"
|
||||
int main() {
|
||||
kvec_t(int) array;
|
||||
kv_init(array);
|
||||
kv_push(int, array, 10); // append
|
||||
kv_a(int, array, 20) = 5; // dynamic
|
||||
kv_A(array, 20) = 4; // static
|
||||
kv_destroy(array);
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
2008-09-22 (0.1.0):
|
||||
|
||||
* The initial version.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef AC_KVEC_H
|
||||
#define AC_KVEC_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define kv_roundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
|
||||
|
||||
#define kvec_t(type) struct { size_t n, m; type *a; }
|
||||
#define kv_decl(name, type) typedef struct { size_t n, m; type *a; } name
|
||||
#define kv_init(v) ((v).n = (v).m = 0, (v).a = 0)
|
||||
#define kv_destroy(v) free((v).a)
|
||||
#define kv_A(v, i) ((v).a[(i)])
|
||||
#define kv_pop(v) ((v).a[--(v).n])
|
||||
#define kv_size(v) ((v).n)
|
||||
#define kv_max(v) ((v).m)
|
||||
#define kv_data(v) ((v).a)
|
||||
|
||||
#define kv_resize(type, v, s) ((v).m = (s), (v).a = (type*)realloc((v).a, sizeof(type) * (v).m))
|
||||
|
||||
#define kv_copy(type, v1, v0) do { \
|
||||
if ((v1).m < (v0).n) kv_resize(type, v1, (v0).n); \
|
||||
(v1).n = (v0).n; \
|
||||
memcpy((v1).a, (v0).a, sizeof(type) * (v0).n); \
|
||||
} while (0) \
|
||||
|
||||
#define kv_push(type, v, x) do { \
|
||||
if ((v).n == (v).m) { \
|
||||
(v).m = (v).m? (v).m<<1 : 2; \
|
||||
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m); \
|
||||
} \
|
||||
(v).a[(v).n++] = (x); \
|
||||
} while (0)
|
||||
|
||||
#define kv_pushp(type, v) ((((v).n == (v).m)? \
|
||||
((v).m = ((v).m? (v).m<<1 : 2), \
|
||||
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \
|
||||
: 0), \
|
||||
((v).a + ((v).n++)))
|
||||
|
||||
#define kv_a(type, v, i) (((v).m <= (size_t)(i)? \
|
||||
((v).m = (v).n = (i) + 1, kv_roundup32((v).m), \
|
||||
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \
|
||||
: (v).n <= (size_t)(i)? (v).n = (i) + 1 \
|
||||
: 0), \
|
||||
(v).a[(i)])
|
||||
|
||||
#endif
|
||||
@@ -1,70 +0,0 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* Based on code from xsystem35
|
||||
* Copyright (C) 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp>
|
||||
* 1998- <masaki-c@is.aist-nara.ac.jp>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LITTLE_ENDIAN_H
|
||||
#define LITTLE_ENDIAN_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static inline int32_t LittleEndian_getDW(const uint8_t *b, int i)
|
||||
{
|
||||
int c0, c1, c2, c3;
|
||||
int d0, d1;
|
||||
c0 = *(b + i + 0);
|
||||
c1 = *(b + i + 1);
|
||||
c2 = *(b + i + 2);
|
||||
c3 = *(b + i + 3);
|
||||
d0 = c0 + (c1 << 8);
|
||||
d1 = c2 + (c3 << 8);
|
||||
return d0 + (d1 << 16);
|
||||
}
|
||||
|
||||
static inline int32_t LittleEndian_get3B(const uint8_t *b, int i)
|
||||
{
|
||||
int c0, c1, c2;
|
||||
c0 = *(b + i + 0);
|
||||
c1 = *(b + i + 1);
|
||||
c2 = *(b + i + 2);
|
||||
return c0 + (c1 << 8) + (c2 << 16);
|
||||
}
|
||||
|
||||
static inline int16_t LittleEndian_getW(const uint8_t *b, int i)
|
||||
{
|
||||
int c0, c1;
|
||||
c0 = *(b + i + 0);
|
||||
c1 = *(b + i + 1);
|
||||
return c0 + (c1 << 8);
|
||||
}
|
||||
|
||||
static inline void LittleEndian_putDW(uint8_t *dst, int i, uint32_t dword)
|
||||
{
|
||||
dst[i] = (uint8_t)(dword);
|
||||
dst[i+1] = (uint8_t)(dword >> 8);
|
||||
dst[i+2] = (uint8_t)(dword >> 16);
|
||||
dst[i+3] = (uint8_t)(dword >> 24);
|
||||
}
|
||||
|
||||
static inline void LittleEndian_putW(uint8_t *dst, int i, uint16_t word)
|
||||
{
|
||||
dst[i] = word & 0xFF;
|
||||
dst[i+1] = word >> 8;
|
||||
}
|
||||
|
||||
#endif /* LITTLE_ENDIAN_H */
|
||||
+11
-2
@@ -43,18 +43,26 @@ struct wai *wai_get(int no);
|
||||
void mixer_init(void);
|
||||
int mixer_get_numof(void);
|
||||
const char *mixer_get_name(int n);
|
||||
int mixer_set_name(int n, const char *name);
|
||||
int mixer_get_volume(int n, int *volume);
|
||||
int mixer_set_volume(int n, int volume);
|
||||
int mixer_get_mute(int n, int *mute);
|
||||
int mixer_set_mute(int n, int mute);
|
||||
|
||||
struct sts_mixer_stream_t;
|
||||
int mixer_stream_play(struct sts_mixer_stream_t* stream, int volume);
|
||||
bool mixer_stream_set_volume(int voice, int volume);
|
||||
void mixer_stream_stop(int voice);
|
||||
|
||||
struct archive_data;
|
||||
struct channel;
|
||||
enum asset_type;
|
||||
|
||||
struct channel *channel_open(enum asset_type type, int no);
|
||||
// Takes ownership of dfile.
|
||||
struct channel *channel_open_archive_data(struct archive_data *dfile);
|
||||
// Takes ownership of dfile. Metadata is not applied if metadata_no is negative.
|
||||
struct channel *channel_open_archive_data(struct archive_data *dfile,
|
||||
enum asset_type type, int metadata_no);
|
||||
struct channel *channel_open_file(const char *path);
|
||||
void channel_close(struct channel *ch);
|
||||
int channel_play(struct channel *ch);
|
||||
int channel_stop(struct channel *ch);
|
||||
@@ -77,5 +85,6 @@ int channel_seek(struct channel *ch, int pos);
|
||||
int channel_reverse_LR(struct channel *ch);
|
||||
int channel_get_volume(struct channel *ch);
|
||||
int channel_get_time_length(struct channel *ch);
|
||||
int channel_get_data_no(struct channel *ch);
|
||||
|
||||
#endif /* SYSTEM4_MIXER_H */
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/* Copyright (C) 2023 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM4_MOVIE_H
|
||||
#define SYSTEM4_MOVIE_H
|
||||
|
||||
struct movie_context;
|
||||
struct sact_sprite;
|
||||
|
||||
struct movie_context *movie_load(const char *filename);
|
||||
void movie_free(struct movie_context *mc);
|
||||
bool movie_play(struct movie_context *mc);
|
||||
bool movie_draw(struct movie_context *mc, struct sact_sprite *sprite);
|
||||
bool movie_is_end(struct movie_context *mc);
|
||||
int movie_get_position(struct movie_context *mc);
|
||||
bool movie_set_volume(struct movie_context *mc, int volume /* 0-100 */);
|
||||
|
||||
#endif /* SYSTEM4_MOVIE_H */
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2021 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MSGQUEUE_H__
|
||||
#define __MSGQUEUE_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <SDL_mutex.h>
|
||||
|
||||
struct msgq_elem;
|
||||
|
||||
struct msgq {
|
||||
SDL_mutex *mutex;
|
||||
SDL_cond *cond_nonempty;
|
||||
struct msgq_elem *head;
|
||||
struct msgq_elem *last;
|
||||
};
|
||||
|
||||
static inline bool msgq_isempty(struct msgq *q) {
|
||||
return !q->head;
|
||||
}
|
||||
|
||||
struct msgq *msgq_new(void);
|
||||
void msgq_free(struct msgq *q);
|
||||
void msgq_enqueue(struct msgq *q, void *msg);
|
||||
void *msgq_dequeue(struct msgq *q);
|
||||
void *msgq_dequeue_timeout(struct msgq *q, uint32_t timeout_ms);
|
||||
|
||||
#endif // __MSGQUEUE_H__
|
||||
+210
-32
@@ -23,108 +23,240 @@ struct page;
|
||||
struct string;
|
||||
|
||||
// parts.c
|
||||
void PE_enable_multi_controller(void);
|
||||
bool PE_Init(void);
|
||||
void PE_Reset(void);
|
||||
void PE_Update(int passed_time, bool message_window_show);
|
||||
void PE_UpdateComponent(int passed_time);
|
||||
void PE_UpdateParts(int passed_time, bool is_skip, bool message_window_show);
|
||||
void PE_SetDelegateIndex(int parts_no, int delegate_index);
|
||||
int PE_GetDelegateIndex(int parts_no);
|
||||
bool PE_SetPartsCG(int parts_no, struct string *cg_name, int sprite_deform, int state);
|
||||
bool PE_SetPartsCG_by_index(int parts_no, int cg_no, int sprite_deform, int state);
|
||||
bool PE_SetPartsCG_by_string_index(int parts_no, struct string *cg_no,
|
||||
int sprite_deform, int state);
|
||||
void PE_GetPartsCGName(int parts_no, struct string **cg_name, int state);
|
||||
bool PE_SetPartsCGSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
void PE_GetPartsCGSurfaceArea(int parts_no, int *x, int *y, int *w, int *h, int state);
|
||||
int PE_GetPartsCGNumber(int parts_no, int state);
|
||||
bool PE_SetLoopCG(int parts_no, int cg_no, int nr_frames, int frame_time, int state);
|
||||
bool PE_SetHGaugeCG(int parts_no, int cg_no, int state);
|
||||
bool PE_SetHGaugeRate(int parts_no, int numerator, int denominator, int state);
|
||||
bool PE_SetVGaugeCG(int parts_no, int cg_no, int state);
|
||||
bool PE_SetVGaugeRate(int parts_no, int numerator, int denominator, int state);
|
||||
bool PE_SetNumeralCG(int parts_no, int cg_no, int state);
|
||||
bool PE_SetNumeralLinkedCGNumberWidthWidthList(int parts_no, int cg_no, int w0, int w1, int w2, int w3, int w4, int w5, int w6, int w7, int w8, int w9, int w_minus, int w_comma, int state);
|
||||
bool PE_SetLoopCG_by_index(int parts_no, int cg_no, int nr_frames, int frame_time, int state);
|
||||
bool PE_SetLoopCG(int parts_no, struct string *cg_name, int start_no, int nr_frames,
|
||||
int frame_time, int state);
|
||||
bool PE_SetLoopCGSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
bool PE_SetHGaugeCG(int parts_no, struct string *cg_name, int state);
|
||||
bool PE_SetHGaugeCG_by_index(int parts_no, int cg_no, int state);
|
||||
bool PE_SetHGaugeRate(int parts_no, float numerator, float denominator, int state);
|
||||
bool PE_SetHGaugeRate_int(int parts_no, int numerator, int denominator, int state);
|
||||
bool PE_SetHGaugeSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
bool PE_SetVGaugeCG(int parts_no, struct string *cg_name, int state);
|
||||
bool PE_SetVGaugeCG_by_index(int parts_no, int cg_no, int state);
|
||||
bool PE_SetVGaugeRate(int parts_no, float numerator, float denominator, int state);
|
||||
bool PE_SetVGaugeRate_int(int parts_no, int numerator, int denominator, int state);
|
||||
bool PE_SetVGaugeSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
bool PE_SetNumeralCG(int parts_no, struct string *cg_name, int state);
|
||||
bool PE_SetNumeralCG_by_index(int parts_no, int cg_no, int state);
|
||||
bool PE_SetNumeralLinkedCGNumberWidthWidthList_by_index(int parts_no, int cg_no,
|
||||
int w0, int w1, int w2, int w3, int w4, int w5, int w6, int w7, int w8,
|
||||
int w9, int w_minus, int w_comma, int state);
|
||||
bool PE_SetNumeralLinkedCGNumberWidthWidthList(int parts_no, struct string *cg_name,
|
||||
int w0, int w1, int w2, int w3, int w4, int w5, int w6, int w7, int w8,
|
||||
int w9, int w_minus, int w_comma, int state);
|
||||
bool PE_SetNumeralNumber(int parts_no, int n, int state);
|
||||
bool PE_SetNumeralShowComma(int parts_no, bool show_comma, int state);
|
||||
bool PE_SetNumeralSpace(int parts_no, int space, int state);
|
||||
bool PE_SetPartsRectangleDetectionSize(int PartsNumber, int Width, int Height, int State);
|
||||
bool PE_SetPartsFlash(int PartsNumber, struct string *pIFlashFileName, int State);
|
||||
bool PE_IsPartsFlashEnd(int PartsNumber, int State);
|
||||
int PE_GetPartsFlashCurrentFrameNumber(int PartsNumber, int State);
|
||||
bool PE_BackPartsFlashBeginFrame(int PartsNumber, int State);
|
||||
bool PE_StepPartsFlashFinalFrame(int PartsNumber, int State);
|
||||
bool PE_SetNumeralLength(int parts_no, int length, int state);
|
||||
bool PE_SetNumeralSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
void PE_ReleaseParts(int parts_no);
|
||||
void PE_ReleaseAllParts(void);
|
||||
void PE_ReleaseAllPartsWithoutSystem(void);
|
||||
void PE_ReleaseAllWithoutSystem(struct page **erase_number_list);
|
||||
void PE_SetPos(int parts_no, int x, int y);
|
||||
void PE_SetZ(int parts_no, int z);
|
||||
void PE_SetShow(int parts_no, bool show);
|
||||
void PE_SetAlpha(int parts_no, int alpha);
|
||||
void PE_SetPartsDrawFilter(int PartsNumber, int DrawFilter);;
|
||||
int PE_GetPartsX(int parts_no);
|
||||
int PE_GetPartsY(int parts_no);
|
||||
void PE_SetZ(int parts_no, int z);
|
||||
int PE_GetPartsZ(int parts_no);
|
||||
void PE_SetShow(int parts_no, bool show);
|
||||
bool PE_GetPartsShow(int parts_no);
|
||||
void PE_SetAlpha(int parts_no, int alpha);
|
||||
int PE_GetPartsAlpha(int parts_no);
|
||||
void PE_SetPartsDrawFilter(int parts_no, int draw_filter);
|
||||
int PE_GetPartsDrawFilter(int parts_no);
|
||||
void PE_SetAddColor(int parts_no, int r, int g, int b);
|
||||
void PE_GetAddColor(int parts_no, int *r, int *g, int *b);
|
||||
void PE_SetMultiplyColor(int parts_no, int r, int g, int b);
|
||||
void PE_GetMultiplyColor(int parts_no, int *r, int *g, int *b);
|
||||
int PE_GetPartsWidth(int parts_no, int state);
|
||||
int PE_GetPartsHeight(int parts_no, int state);
|
||||
bool PE_GetPartsShow(int parts_no);
|
||||
int PE_GetPartsAlpha(int parts_no);
|
||||
int PE_GetPartsUpperLeftPosX(int parts_no, int state);
|
||||
int PE_GetPartsUpperLeftPosY(int parts_no, int state);
|
||||
void PE_SetPartsOriginPosMode(int parts_no, int origin_pos_mode);
|
||||
int PE_GetPartsOriginPosMode(int parts_no);
|
||||
void PE_SetParentPartsNumber(int parts_no, int parent_parts_no);
|
||||
bool PE_SetPartsGroupNumber(int PartsNumber, int GroupNumber);
|
||||
int PE_GetParentPartsNumber(int parts_no);
|
||||
bool PE_SetPartsGroupNumber(int parts_no, int group_no);
|
||||
void PE_SetPartsMessageWindowShowLink(int parts_no, bool message_window_show_link);
|
||||
bool PE_GetPartsMessageWindowShowLink(int PartsNumber);
|
||||
bool PE_GetPartsMessageWindowShowLink(int parts_no);
|
||||
void PE_SetSpeedupRateByMessageSkip(int parts_no, int rate);
|
||||
int PE_GetSpeedupRateByMessageSkip(int parts_no);
|
||||
void PE_SetPartsMagX(int parts_no, float scale_x);
|
||||
float PE_GetPartsMagX(int parts_no);
|
||||
void PE_SetPartsMagY(int parts_no, float scale_y);
|
||||
float PE_GetPartsMagY(int parts_no);
|
||||
void PE_SetPartsRotateX(int parts_no, float rot_x);
|
||||
void PE_SetPartsRotateY(int parts_no, float rot_y);
|
||||
void PE_SetPartsRotateZ(int parts_no, float rot_z);
|
||||
void PE_SetPartsAlphaClipperPartsNumber(int PartsNumber, int AlphaClipperPartsNumber);
|
||||
void PE_SetPartsPixelDecide(int PartsNumber, bool PixelDecide);
|
||||
bool PE_SetThumbnailReductionSize(int ReductionSize);
|
||||
float PE_GetPartsRotateZ(int parts_no);
|
||||
void PE_SetPartsAlphaClipperPartsNumber(int parts_no, int alpha_clipper_parts_no);
|
||||
void PE_SetPartsPixelDecide(int PartsNumber, bool pixel_decide);
|
||||
bool PE_SetThumbnailReductionSize(int reduction_size);
|
||||
bool PE_SetThumbnailMode(bool Mode);
|
||||
void PE_SetComponentType(int parts_no, int type, int state);
|
||||
int PE_GetComponentType(int parts_no, int state);
|
||||
void PE_SetInputState(int parts_no, int state);
|
||||
int PE_GetInputState(int parts_no);
|
||||
bool PE_SetPartsRectangleDetectionSize(int parts_no, int w, int h, int state);
|
||||
bool PE_SetPartsCGDetectionSize(int parts_no, struct string *cg_name, int state);
|
||||
bool PE_Save(struct page **buffer);
|
||||
bool PE_SaveWithoutHideParts(struct page **buffer);
|
||||
bool PE_Load(struct page **buffer);
|
||||
int PE_AddController(int index);
|
||||
void PE_RemoveController(struct page **erase_number_list, int index);
|
||||
bool PE_CreateParts3DLayerPluginID(int parts_no, int state);
|
||||
int PE_GetParts3DLayerPluginID(int parts_no, int state);
|
||||
bool PE_ReleaseParts3DLayerPluginID(int parts_no, int state);
|
||||
// GUIEngine
|
||||
int PE_GetFreeNumber(void);
|
||||
bool PE_IsExist(int parts_no);
|
||||
// PartsFunc interface
|
||||
void PE_set_active_controller(int controller_no);
|
||||
int PE_get_active_controller(void);
|
||||
int PE_get_system_controller(void);
|
||||
int PE_get_nr_controllers(void);
|
||||
void PE_parts_set_want_save(int parts_no, bool want_save);
|
||||
bool PE_save_thumbnail(struct string *filename, int reduction_factor);
|
||||
bool PE_init_parts_movie(int parts_no, int width, int height, int bg_r, int bg_g, int bg_b, int state);
|
||||
int PE_get_movie_sprite(int parts_no, int state);
|
||||
float PE_parts_get_absolute_x(int parts_no);
|
||||
float PE_parts_get_absolute_y(int parts_no);
|
||||
int PE_parts_get_absolute_z(int parts_no);
|
||||
void PE_parts_set_lock_input_state(int parts_no, bool lock);
|
||||
|
||||
// construction.c
|
||||
bool PE_AddCreateToPartsConstructionProcess(int parts_no, int w, int h, int state);
|
||||
bool PE_AddCreatePixelOnlyToPartsConstructionProcess(int parts_no, int w, int h, int state);
|
||||
bool PE_AddCreateCGToProcess(int parts_no, struct string *cg_name, int state);
|
||||
bool PE_AddFillAlphaColorToPartsConstructionProcess(int parts_no, int x, int y, int w, int h, int r, int g, int b, int a, int state);
|
||||
bool PE_AddFillToPartsConstructionProcess(int parts_no, int x, int y, int w, int h,
|
||||
int r, int g, int b, int state);
|
||||
bool PE_AddFillAlphaColorToPartsConstructionProcess(int parts_no, int x, int y, int w, int h,
|
||||
int r, int g, int b, int a, int state);
|
||||
bool PE_AddFillAMapToPartsConstructionProcess(int parts_no, int x, int y, int w, int h, int a, int state);
|
||||
bool PE_AddFillWithAlphaToPartsConstructionProcess(int parts_no, int x, int y, int w, int h,
|
||||
int r, int g, int b, int a, int state);
|
||||
bool PE_AddDrawRectToPartsConstructionProcess(int parts_no, int x, int y, int w, int h,
|
||||
int r, int g, int b, int state);
|
||||
bool PE_AddDrawCutCGToPartsConstructionProcess(int parts_no, struct string *cg_name,
|
||||
int dx, int dy, int dw, int dh, int sx, int sy, int sw, int sh, int interp_type, int state);
|
||||
bool PE_AddCopyCutCGToPartsConstructionProcess(int parts_no, struct string *cg_name,
|
||||
int dx, int dy, int dw, int dh, int sx, int sy, int sw, int sh, int interp_type, int state);
|
||||
bool PE_AddGrayFilterToPartsConstructionProcess(int parts_no, int x, int y, int w, int h,
|
||||
bool full_size, int state);
|
||||
bool PE_AddCopyTextToPartsConstructionProcess(int parts_no, int x, int y, struct string *text,
|
||||
int type, int size, int r, int g, int b, float bold_weight,
|
||||
int edge_r, int edge_g, int edge_b, float edge_weight,
|
||||
int char_space, int line_space, int state);
|
||||
bool PE_AddDrawTextToPartsConstructionProcess(int parts_no, int x, int y, struct string *text,
|
||||
int type, int size, int r, int g, int b, float bold_weight,
|
||||
int edge_r, int edge_g, int edge_b, float edge_weight,
|
||||
int char_space, int line_space, int state);
|
||||
bool PE_BuildPartsConstructionProcess(int parts_no, int state);
|
||||
bool PE_ClearPartsConstructionProcess(int parts_no, int state);
|
||||
bool PE_SetPartsConstructionSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
|
||||
// input.c
|
||||
void PE_UpdateInputState(int passed_time);
|
||||
void PE_SetPassCursor(int parts_no, bool pass);
|
||||
bool PE_GetPartsPassCursor(int parts_no);
|
||||
void PE_SetClickable(int parts_no, bool clickable);
|
||||
bool PE_GetPartsClickable(int parts_no);
|
||||
void PE_SetPartsGroupDecideOnCursor(int GroupNumber, bool DecideOnCursor);
|
||||
void PE_SetPartsGroupDecideClick(int GroupNumber, bool DecideClick);
|
||||
void PE_SetDrag(int parts_no, bool enable);
|
||||
void PE_SetPartsGroupDecideOnCursor(int group_no, bool decide_on_cursor);
|
||||
void PE_SetPartsGroupDecideClick(int group_no, bool decide_click);
|
||||
void PE_SetOnCursorShowLinkPartsNumber(int parts_no, int link_parts_no);
|
||||
int PE_GetOnCursorShowLinkPartsNumber(int parts_no);
|
||||
bool PE_SetPartsOnCursorSoundNumber(int parts_no, int sound_no);
|
||||
bool PE_SetPartsClickSoundNumber(int parts_no, int sound_no);
|
||||
bool PE_SetClickMissSoundNumber(int sound_no);
|
||||
void PE_BeginInput(void);
|
||||
void PE_EndInput(void);
|
||||
int PE_GetClickPartsNumber(void);
|
||||
bool PE_IsCursorIn(int parts_no, int mouse_x, int mouse_y, int state);
|
||||
|
||||
// message.c
|
||||
void PE_ReleaseMessage(void);
|
||||
void PE_PopMessage(void);
|
||||
int PE_GetMessageType(void);
|
||||
int PE_GetMessagePartsNumber(void);
|
||||
int PE_GetMessageDelegateIndex(void);
|
||||
int PE_GetMessageVariableCount(void);
|
||||
int PE_GetMessageVariableType(int index);
|
||||
int PE_GetMessageVariableInt(int index);
|
||||
float PE_GetMessageVariableFloat(int index);
|
||||
bool PE_GetMessageVariableBool(int index);
|
||||
void PE_GetMessageVariableString(int index, struct string **out);
|
||||
|
||||
// motion.c
|
||||
void PE_AddMotionPos(int parts_no, int begin_x, int begin_y, int end_x, int end_y, int begin_t, int end_t);
|
||||
void PE_AddMotionPos_curve(int parts_no, int begin_x, int begin_y, int end_x, int end_y,
|
||||
int begin_t, int end_t, struct string *curve_name);
|
||||
void PE_AddMotionAlpha(int parts_no, int begin_a, int end_a, int begin_t, int end_t);
|
||||
void PE_AddMotionCG(int parts_no, int begin_cg_no, int nr_cg, int begin_t, int end_t);
|
||||
void PE_AddMotionHGaugeRate(int parts_no, int begin_numerator, int begin_denominator,
|
||||
int end_numerator, int end_denominator, int begin_t, int end_t);
|
||||
void PE_AddMotionVGaugeRate(int parts_no, int begin_numerator, int begin_denominator,
|
||||
int end_numerator, int end_denominator, int begin_t, int end_t);
|
||||
void PE_AddMotionAlpha_curve(int parts_no, int begin_a, int end_a, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionCG_by_index(int parts_no, int begin_cg_no, int nr_cg, int begin_t, int end_t);
|
||||
void PE_AddMotionHGaugeRate(int parts_no, float begin_numerator, float begin_denominator,
|
||||
float end_numerator, float end_denominator, int begin_t, int end_t);
|
||||
void PE_AddMotionHGaugeRate_curve(int parts_no, float begin_numerator, float begin_denominator,
|
||||
float end_numerator, float end_denominator, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionVGaugeRate(int parts_no, float begin_numerator, float begin_denominator,
|
||||
float end_numerator, float end_denominator, int begin_t, int end_t);
|
||||
void PE_AddMotionVGaugeRate_curve(int parts_no, float begin_numerator, float begin_denominator,
|
||||
float end_numerator, float end_denominator, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionNumeralNumber(int parts_no, int begin_n, int end_n, int begin_t, int end_t);
|
||||
void PE_AddMotionNumeralNumber_curve(int parts_no, int begin_n, int end_n, int begin_t,
|
||||
int end_t, struct string *curve_name);
|
||||
void PE_AddMotionMagX(int parts_no, float begin, float end, int begin_t, int end_t);
|
||||
void PE_AddMotionMagX_curve(int parts_no, float begin, float end, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionMagY(int parts_no, float begin, float end, int begin_t, int end_t);
|
||||
void PE_AddMotionMagY_curve(int parts_no, float begin, float end, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionRotateX(int parts_no, float begin, float end, int begin_t, int end_t);
|
||||
void PE_AddMotionRotateX_curve(int parts_no, float begin, float end, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionRotateY(int parts_no, float begin, float end, int begin_t, int end_t);
|
||||
void PE_AddMotionRotateY_curve(int parts_no, float begin, float end, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionRotateZ(int parts_no, float begin, float end, int begin_t, int end_t);
|
||||
void PE_AddMotionRotateZ_curve(int parts_no, float begin, float end, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionVibrationSize(int parts_no, int begin_w, int begin_h, int begin_t, int end_t);
|
||||
void PE_AddMotionSound(int sound_no, int begin_t);
|
||||
void PE_AddWholeMotionVibrationSize(int begin_w, int begin_h, int begin_t, int end_t);
|
||||
void PE_BeginMotion(void);
|
||||
void PE_EndMotion(void);
|
||||
void PE_SetMotionTime(int t);
|
||||
void PE_SeekEndMotion(void);
|
||||
void PE_UpdateMotionTime(int time, bool skip);
|
||||
bool PE_IsMotion(void);
|
||||
int PE_GetMotionEndTime(void);
|
||||
void PE_PauseMotion(bool pause);
|
||||
|
||||
// text.c
|
||||
bool PE_SetText(int parts_no, struct string *text, int state);
|
||||
struct string *PE_GetText(int parts_no, int state);
|
||||
bool PE_AddPartsText(int parts_no, struct string *text, int state);
|
||||
bool PE_SetPartsTextSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
bool PE_SetFont(int parts_no, int type, int size, int r, int g, int b, float bold_weight, int edge_r, int edge_g, int edge_b, float edge_weight, int state);
|
||||
bool PE_SetPartsFontType(int parts_no, int type, int state);
|
||||
bool PE_SetPartsFontSize(int parts_no, int size, int state);
|
||||
@@ -135,4 +267,50 @@ bool PE_SetPartsFontEdgeWeight(int parts_no, float edge_weight, int state);
|
||||
bool PE_SetTextCharSpace(int parts_no, int char_space, int state);
|
||||
bool PE_SetTextLineSpace(int parts_no, int line_space, int state);
|
||||
|
||||
// flash.c
|
||||
bool PE_ExistsFlashFile(struct string *flash_filename);
|
||||
bool PE_SetPartsFlash(int parts_no, struct string *flash_filename, int state);
|
||||
bool PE_IsPartsFlashEnd(int parts_no, int state);
|
||||
int PE_GetPartsFlashCurrentFrameNumber(int parts_no, int state);
|
||||
bool PE_BackPartsFlashBeginFrame(int parts_no, int state);
|
||||
bool PE_StepPartsFlashFinalFrame(int parts_no, int state);
|
||||
bool PE_SetPartsFlashAndStop(int parts_no, struct string *flash_filename, int state);
|
||||
bool PE_StopPartsFlash(int parts_no, int state);
|
||||
bool PE_StartPartsFlash(int parts_no, int state);
|
||||
bool PE_GoFramePartsFlash(int parts_no, int frame_no, int state);
|
||||
int PE_GetPartsFlashEndFrame(int parts_no, int state);
|
||||
|
||||
// flat.c
|
||||
bool PE_ExistsFlatFile(struct string *filename);
|
||||
bool PE_SetPartsFlat(int parts_no, struct string *filename, int state);
|
||||
bool PE_IsPartsFlatEnd(int parts_no, int state);
|
||||
int PE_GetPartsFlatCurrentFrameNumber(int parts_no, int state);
|
||||
bool PE_BackPartsFlatBeginFrame(int parts_no, int state);
|
||||
bool PE_StepPartsFlatFinalFrame(int parts_no, int state);
|
||||
bool PE_SetPartsFlatSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
bool PE_SetPartsFlatAndStop(int parts_no, struct string *filename, int state);
|
||||
bool PE_StopPartsFlat(int parts_no, int state);
|
||||
bool PE_StartPartsFlat(int parts_no, int state);
|
||||
bool PE_GoFramePartsFlat(int parts_no, int frame_no, int state);
|
||||
int PE_GetPartsFlatEndFrame(int parts_no, int state);
|
||||
|
||||
// layoutbox.c
|
||||
void PE_SetLayoutBoxLayoutType(int parts_no, int type);
|
||||
int PE_GetLayoutBoxLayoutType(int parts_no);
|
||||
void PE_SetLayoutBoxReturn(int parts_no, bool return_flag, int return_size);
|
||||
bool PE_IsLayoutBoxReturn(int parts_no);
|
||||
int PE_GetLayoutBoxReturnSize(int parts_no);
|
||||
void PE_SetLayoutBoxAlign(int parts_no, int align);
|
||||
int PE_GetLayoutBoxAlign(int parts_no);
|
||||
void PE_SetComponentMargin(int parts_no, int top, int bottom, int left, int right);
|
||||
int PE_GetComponentMarginTop(int parts_no);
|
||||
int PE_GetComponentMarginBottom(int parts_no);
|
||||
int PE_GetComponentMarginLeft(int parts_no);
|
||||
int PE_GetComponentMarginRight(int parts_no);
|
||||
void PE_set_layoutbox_padding(int parts_no, int top, int bottom, int left, int right);
|
||||
int PE_get_layoutbox_padding_top(int parts_no);
|
||||
int PE_get_layoutbox_padding_bottom(int parts_no);
|
||||
int PE_get_layoutbox_padding_left(int parts_no);
|
||||
int PE_get_layoutbox_padding_right(int parts_no);
|
||||
|
||||
#endif /* SYSTEM4_PARTS_H */
|
||||
|
||||
+4361
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,33 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM4_PLUGIN_H
|
||||
#define SYSTEM4_PLUGIN_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct cJSON cJSON;
|
||||
struct sact_sprite;
|
||||
|
||||
struct draw_plugin {
|
||||
const char *name;
|
||||
void (*free)(struct draw_plugin *);
|
||||
void (*update)(struct sact_sprite *);
|
||||
void (*render)(struct sact_sprite *);
|
||||
cJSON *(*to_json)(struct sact_sprite *, bool);
|
||||
};
|
||||
|
||||
#endif /* SYSTEM4_PLUGIN_H */
|
||||
+384
@@ -0,0 +1,384 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM4_REIGN_H
|
||||
#define SYSTEM4_REIGN_H
|
||||
|
||||
#define RE_NR_BACK_CGS 16
|
||||
#define RE_NR_INSTANCE_TARGETS 2
|
||||
#define RE_NR_LIGHT_PARAMS 41 // .lit parameter count
|
||||
|
||||
#include <cglm/types.h>
|
||||
|
||||
#include "gfx/gfx.h"
|
||||
#include "plugin.h"
|
||||
|
||||
typedef struct cJSON cJSON;
|
||||
struct hash_table;
|
||||
struct billboard_texture;
|
||||
struct iarray_reader;
|
||||
struct iarray_writer;
|
||||
|
||||
enum RE_plugin_version {
|
||||
RE_REIGN_PLUGIN, // Toushin Toshi 3
|
||||
RE_TAPIR_PLUGIN, // Rance Quest
|
||||
RE_SEAL_PLUGIN, // Rance 9
|
||||
};
|
||||
|
||||
enum RE_instance_type {
|
||||
RE_ITYPE_UNINITIALIZED = 0,
|
||||
RE_ITYPE_STATIC = 1,
|
||||
RE_ITYPE_SKINNED = 2,
|
||||
RE_ITYPE_BILLBOARD = 3,
|
||||
RE_ITYPE_DIRECTIONAL_LIGHT = 4,
|
||||
RE_ITYPE_SPECULAR_LIGHT = 6,
|
||||
RE_ITYPE_PARTICLE_EFFECT = 8,
|
||||
RE_ITYPE_PATH_LINE = 9,
|
||||
};
|
||||
|
||||
enum RE_motion_state {
|
||||
RE_MOTION_STATE_STOP = 0,
|
||||
RE_MOTION_STATE_NOLOOP = 1,
|
||||
RE_MOTION_STATE_LOOP = 2,
|
||||
};
|
||||
|
||||
enum RE_fog_type {
|
||||
RE_FOG_NONE = 0,
|
||||
RE_FOG_LINEAR = 1,
|
||||
RE_FOG_LIGHT_SCATTERING = 2,
|
||||
};
|
||||
|
||||
// Indices into light_params. Vector entries name their first component.
|
||||
enum seal_light_param {
|
||||
SEAL_LP_TONEMAP_EXPOSURE_BIAS = 0,
|
||||
SEAL_LP_TONEMAP_WHITE_POINT = 1,
|
||||
SEAL_LP_TONEMAP_A = 2,
|
||||
SEAL_LP_TONEMAP_B = 3,
|
||||
SEAL_LP_TONEMAP_C = 4,
|
||||
SEAL_LP_TONEMAP_D = 5,
|
||||
SEAL_LP_TONEMAP_E = 6,
|
||||
SEAL_LP_TONEMAP_F = 7,
|
||||
SEAL_LP_HEMI_VEC = 8, // x,y,z
|
||||
SEAL_LP_HEMI_SKY_COLOR = 11, // r,g,b
|
||||
SEAL_LP_HEMI_MID_COLOR = 14, // r,g,b
|
||||
SEAL_LP_HEMI_GROUND_COLOR = 17, // r,g,b
|
||||
SEAL_LP_LS_BETA_R = 20,
|
||||
SEAL_LP_LS_BETA_M = 21,
|
||||
SEAL_LP_LS_G = 22,
|
||||
SEAL_LP_LS_DISTANCE = 23,
|
||||
SEAL_LP_LS_LIGHT_VEC = 24, // x,y,z
|
||||
SEAL_LP_LS_LIGHT_COLOR = 27, // r,g,b
|
||||
SEAL_LP_LS_SUN_COLOR = 30, // r,g,b
|
||||
};
|
||||
|
||||
enum RE_draw_type {
|
||||
RE_DRAW_TYPE_NORMAL = 0,
|
||||
RE_DRAW_TYPE_ADDITIVE = 1,
|
||||
RE_DRAW_TYPE_MAX = RE_DRAW_TYPE_ADDITIVE
|
||||
};
|
||||
|
||||
enum RE_draw_options {
|
||||
RE_DRAW_OPTION_EDGE = 0,
|
||||
RE_DRAW_OPTION_LIGHTING = 1,
|
||||
RE_DRAW_OPTION_MAX
|
||||
};
|
||||
|
||||
struct RE_options {
|
||||
int anti_aliasing;
|
||||
int wait_vsync;
|
||||
};
|
||||
extern struct RE_options RE_options;
|
||||
|
||||
struct RE_camera {
|
||||
vec3 pos;
|
||||
float pitch, roll, yaw; // in degrees
|
||||
float quake_pitch, quake_yaw;
|
||||
// Per-frame full override from an 3de "camera" object. Cleared at the
|
||||
// start of every RE_build_model tick and re-asserted by s3de_effect_update
|
||||
// if any active camera object selects this frame.
|
||||
bool override_active;
|
||||
vec3 override_pos;
|
||||
float override_pitch, override_yaw, override_roll;
|
||||
};
|
||||
|
||||
struct RE_back_cg {
|
||||
struct texture texture;
|
||||
struct string *name;
|
||||
int no;
|
||||
float blend_rate;
|
||||
float x;
|
||||
float y;
|
||||
float mag;
|
||||
bool show;
|
||||
};
|
||||
|
||||
struct RE_plugin {
|
||||
struct draw_plugin plugin;
|
||||
enum RE_plugin_version version;
|
||||
int sprite;
|
||||
int nr_instances;
|
||||
struct RE_instance **instances;
|
||||
struct archive *aar;
|
||||
struct hash_table *model_cache;
|
||||
struct hash_table *effect_cache;
|
||||
struct RE_renderer *renderer;
|
||||
struct RE_camera camera;
|
||||
mat4 proj_transform;
|
||||
int viewport_x, viewport_y, viewport_width, viewport_height;
|
||||
|
||||
struct RE_back_cg back_cg[RE_NR_BACK_CGS];
|
||||
|
||||
// Settings.
|
||||
int render_mode;
|
||||
int shadow_mode;
|
||||
vec3 shadow_map_light_dir;
|
||||
float shadow_bias;
|
||||
int bump_mode;
|
||||
int bloom_mode;
|
||||
int glare_mode;
|
||||
int fog_mode;
|
||||
enum RE_fog_type fog_type;
|
||||
float fog_near;
|
||||
float fog_far;
|
||||
vec3 fog_color;
|
||||
float ls_beta_r;
|
||||
float ls_beta_m;
|
||||
float ls_g;
|
||||
float ls_distance;
|
||||
vec3 ls_light_dir;
|
||||
vec3 ls_light_color;
|
||||
vec3 ls_sun_color;
|
||||
int specular_mode;
|
||||
int light_map_mode;
|
||||
int soft_fog_edge_mode;
|
||||
int ssao_mode;
|
||||
int vertex_blend_mode;
|
||||
int shader_precision_mode;
|
||||
int texture_filter_mode;
|
||||
bool use_power2_texture;
|
||||
int shadow_map_resolution_level;
|
||||
int texture_resolution_level;
|
||||
vec3 global_ambient;
|
||||
float post_effect_filter_y;
|
||||
float post_effect_filter_cb;
|
||||
float post_effect_filter_cr;
|
||||
int draw_options[RE_DRAW_OPTION_MAX];
|
||||
|
||||
// TapirEngine
|
||||
bool suspended;
|
||||
|
||||
// SealEngine
|
||||
int mag_speed;
|
||||
float light_params[RE_NR_LIGHT_PARAMS];
|
||||
float edge_length;
|
||||
float edge_reduction_rate;
|
||||
vec3 edge_color;
|
||||
};
|
||||
|
||||
struct RE_instance {
|
||||
struct RE_plugin *plugin;
|
||||
struct model *model;
|
||||
struct particle_effect *effect;
|
||||
struct s3de_effect *s3de_effect;
|
||||
struct motion *motion;
|
||||
struct motion *next_motion;
|
||||
struct height_detector *height_detector;
|
||||
|
||||
enum RE_instance_type type;
|
||||
struct string *name;
|
||||
int target[RE_NR_INSTANCE_TARGETS];
|
||||
vec3 pos;
|
||||
float pitch, roll, yaw; // in degrees
|
||||
vec3 scale;
|
||||
float alpha;
|
||||
float grayscale_rate;
|
||||
bool draw;
|
||||
bool draw_edge;
|
||||
bool draw_shadow;
|
||||
bool make_shadow;
|
||||
float shadow_volume_bone_radius;
|
||||
bool draw_bump;
|
||||
float fps;
|
||||
bool use_mag_speed;
|
||||
bool motion_blend;
|
||||
float motion_blend_rate;
|
||||
vec3 diffuse; // color multiplier for meshes, light color for lights
|
||||
vec3 ambient;
|
||||
vec3 column_pos;
|
||||
float column_height;
|
||||
float column_radius;
|
||||
float column_angle; // around the Y axis, in degrees
|
||||
|
||||
// Billboards
|
||||
enum RE_draw_type draw_type;
|
||||
vec3 vertex_pos[4];
|
||||
vec2 vertex_uv[4];
|
||||
struct billboard_texture **billboard_frames;
|
||||
int nr_billboard_frames;
|
||||
|
||||
// Lights
|
||||
vec3 vec;
|
||||
vec3 globe_diffuse;
|
||||
float *light_params;
|
||||
|
||||
// Private
|
||||
bool local_transform_needs_update;
|
||||
mat4 local_transform;
|
||||
mat3 normal_transform;
|
||||
mat3x4 *bone_transforms; // row-major, model->nr_bones elements
|
||||
GLuint bone_transforms_ubo;
|
||||
vec4 bounding_sphere;
|
||||
struct RE_instance *shadow_volume_instance;
|
||||
float z_from_camera;
|
||||
int texture_animation_index;
|
||||
};
|
||||
|
||||
#define RE_MAX_PLUGINS 2
|
||||
struct RE_plugin *RE_get_plugin(int handle);
|
||||
|
||||
struct RE_plugin *RE_plugin_new(enum RE_plugin_version version);
|
||||
void RE_plugin_free(struct RE_plugin *plugin);
|
||||
bool RE_plugin_bind(struct RE_plugin *plugin, int sprite);
|
||||
bool RE_plugin_unbind(struct RE_plugin *plugin);
|
||||
bool RE_plugin_suspend(struct RE_plugin *plugin);
|
||||
bool RE_plugin_resume(struct RE_plugin *plugin);
|
||||
bool RE_build_model(struct RE_plugin *plugin, int elapsed_ms);
|
||||
void RE_update_model(struct RE_plugin *plugin);
|
||||
bool RE_set_viewport(struct RE_plugin *plugin, int x, int y, int width, int height);
|
||||
bool RE_set_projection(struct RE_plugin *plugin, float width, float height, float near, float far, float deg);
|
||||
int RE_create_instance(struct RE_plugin *plugin);
|
||||
bool RE_release_instance(struct RE_plugin *plugin, int instance);
|
||||
|
||||
bool RE_instance_set_type(struct RE_instance *instance, int type);
|
||||
bool RE_instance_data_exists(struct RE_instance *instance, const char *name);
|
||||
bool RE_instance_load(struct RE_instance *instance, const char *name);
|
||||
bool RE_instance_motion_exists(struct RE_instance *instance, const char *name);
|
||||
bool RE_instance_load_motion(struct RE_instance *instance, const char *name);
|
||||
bool RE_instance_load_next_motion(struct RE_instance *instance, const char *name);
|
||||
bool RE_instance_free_next_motion(struct RE_instance *instance);
|
||||
bool RE_instance_set_mesh_show(struct RE_instance *instance, const char *mesh_name, bool show);
|
||||
bool RE_instance_set_vertex_pos(struct RE_instance *instance, int index, float x, float y, float z);
|
||||
bool RE_instance_set_vertex_uv(struct RE_instance *instance, int index, float u, float v);
|
||||
int RE_instance_get_bone_index(struct RE_instance *instance, const char *name);
|
||||
bool RE_instance_trans_local_pos_to_world_pos_by_bone(struct RE_instance *instance, int bone, vec3 offset, vec3 out);
|
||||
float RE_instance_calc_height(struct RE_instance *instance, float x, float z);
|
||||
float RE_instance_calc_2d_detection_height(struct RE_instance *instance, float x, float z);
|
||||
bool RE_instance_calc_2d_detection(struct RE_instance *instance, float x0, float y0, float z0, float x1, float y1, float z1, float *x2, float *y2, float *z2, float radius);
|
||||
bool RE_instance_set_debug_draw_shadow_volume(struct RE_instance *instance, bool draw);
|
||||
bool RE_instance_find_path(struct RE_instance *instance, vec3 start, vec3 goal);
|
||||
const vec3 *RE_instance_get_path_line(struct RE_instance *instance, int *nr_path_points);
|
||||
bool RE_instance_optimize_path_line(struct RE_instance *instance);
|
||||
bool RE_instance_calc_path_finder_intersect_eye_vec(struct RE_instance *instance, int mouse_x, int mouse_y, vec3 out);
|
||||
bool RE_plugin_transform_pos_to_view_pos(struct RE_plugin *plugin, float x, float y, float z, int *view_x, int *view_y);
|
||||
bool RE_plugin_get_camera_z_vector(struct RE_plugin *plugin, vec3 out);
|
||||
void RE_plugin_reset_light_param(struct RE_plugin *plugin);
|
||||
bool RE_plugin_set_fog_type(struct RE_plugin *plugin, int type);
|
||||
int RE_plugin_get_fog_type(struct RE_plugin *plugin);
|
||||
|
||||
int RE_motion_get_state(struct motion *motion);
|
||||
bool RE_motion_set_state(struct motion *motion, int state);
|
||||
float RE_motion_get_frame(struct motion *motion);
|
||||
bool RE_motion_set_frame(struct motion *motion, float frame);
|
||||
bool RE_motion_set_frame_range(struct motion *motion, float begin, float end);
|
||||
bool RE_motion_set_loop_frame_range(struct motion *motion, float begin, float end);
|
||||
|
||||
int RE_effect_get_num_object(struct particle_effect *effect);
|
||||
bool RE_effect_get_camera_quake_flag(struct particle_effect *effect);
|
||||
bool RE_effect_set_camera_quake_flag(struct particle_effect *effect, bool enable);
|
||||
struct particle_object *RE_get_effect_object(struct particle_effect *effect, unsigned object);
|
||||
int RE_particle_get_type(struct particle_object *po);
|
||||
int RE_particle_get_move_type(struct particle_object *po);
|
||||
int RE_particle_get_up_vec_type(struct particle_object *po);
|
||||
float RE_particle_get_move_curve(struct particle_object *po);
|
||||
void RE_particle_get_frame(struct particle_object *po, int *begin_frame, int *end_frame);
|
||||
int RE_particle_get_stop_frame(struct particle_object *po);
|
||||
const char *RE_particle_get_name(struct particle_object *po);
|
||||
int RE_particle_get_num_pos(struct particle_object *po);
|
||||
int RE_particle_get_num_pos_unit(struct particle_object *po, int pos);
|
||||
int RE_particle_get_pos_unit_type(struct particle_object *po, int pos, int unit);
|
||||
int RE_particle_get_pos_unit_index(struct particle_object *po, int pos, int unit);
|
||||
int RE_particle_get_num_texture(struct particle_object *po);
|
||||
const char *RE_particle_get_texture(struct particle_object *po, int texture);
|
||||
bool RE_particle_get_size(struct particle_object *po, float *begin_size, float *end_size);
|
||||
bool RE_particle_get_size2(struct particle_object *po, int frame, float *size);
|
||||
bool RE_particle_get_size_x(struct particle_object *po, int frame, float *size);
|
||||
bool RE_particle_get_size_y(struct particle_object *po, int frame, float *size);
|
||||
bool RE_particle_get_size_type(struct particle_object *po, int frame, int *type);
|
||||
bool RE_particle_get_size_x_type(struct particle_object *po, int frame, int *type);
|
||||
bool RE_particle_get_size_y_type(struct particle_object *po, int frame, int *type);
|
||||
int RE_particle_get_num_size2(struct particle_object *po);
|
||||
int RE_particle_get_num_size_x(struct particle_object *po);
|
||||
int RE_particle_get_num_size_y(struct particle_object *po);
|
||||
int RE_particle_get_num_size_type(struct particle_object *po);
|
||||
int RE_particle_get_num_size_x_type(struct particle_object *po);
|
||||
int RE_particle_get_num_size_y_type(struct particle_object *po);
|
||||
int RE_particle_get_blend_type(struct particle_object *po);
|
||||
const char *RE_particle_get_polygon_name(struct particle_object *po);
|
||||
int RE_particle_get_num_particles(struct particle_object *po);
|
||||
int RE_particle_get_alpha_fadein_time(struct particle_object *po);
|
||||
int RE_particle_get_alpha_fadeout_time(struct particle_object *po);
|
||||
int RE_particle_get_texture_anime_time(struct particle_object *po);
|
||||
float RE_particle_get_alpha_fadein_frame(struct particle_object *po);
|
||||
float RE_particle_get_alpha_fadeout_frame(struct particle_object *po);
|
||||
float RE_particle_get_texture_anime_frame(struct particle_object *po);
|
||||
int RE_particle_get_frame_ref_type(struct particle_object *po);
|
||||
int RE_particle_get_frame_ref_param(struct particle_object *po);
|
||||
void RE_particle_get_x_rotation_angle(struct particle_object *po, float *begin_angle, float *end_angle);
|
||||
void RE_particle_get_y_rotation_angle(struct particle_object *po, float *begin_angle, float *end_angle);
|
||||
void RE_particle_get_z_rotation_angle(struct particle_object *po, float *begin_angle, float *end_angle);
|
||||
void RE_particle_get_x_revolution_angle(struct particle_object *po, float *begin_angle, float *end_angle);
|
||||
void RE_particle_get_x_revolution_distance(struct particle_object *po, float *begin_distance, float *end_distance);
|
||||
void RE_particle_get_y_revolution_angle(struct particle_object *po, float *begin_angle, float *end_angle);
|
||||
void RE_particle_get_y_revolution_distance(struct particle_object *po, float *begin_distance, float *end_distance);
|
||||
void RE_particle_get_z_revolution_angle(struct particle_object *po, float *begin_angle, float *end_angle);
|
||||
void RE_particle_get_z_revolution_distance(struct particle_object *po, float *begin_distance, float *end_distance);
|
||||
bool RE_particle_get_curve_length(struct particle_object *po, vec3 out);
|
||||
int RE_particle_get_child_frame(struct particle_object *po);
|
||||
float RE_particle_get_child_length(struct particle_object *po);
|
||||
float RE_particle_get_child_begin_slope(struct particle_object *po);
|
||||
float RE_particle_get_child_end_slope(struct particle_object *po);
|
||||
float RE_particle_get_child_create_begin_frame(struct particle_object *po);
|
||||
float RE_particle_get_child_create_end_frame(struct particle_object *po);
|
||||
int RE_particle_get_child_move_dir_type(struct particle_object *po);
|
||||
int RE_particle_get_dir_type(struct particle_object *po);
|
||||
int RE_particle_get_num_damage(struct particle_object *po);
|
||||
int RE_particle_get_damage(struct particle_object *po, int frame);
|
||||
float RE_particle_get_offset_x(struct particle_object *po);
|
||||
float RE_particle_get_offset_y(struct particle_object *po);
|
||||
bool RE_effect_get_frame_range(struct RE_instance *instance, int *begin_frame, int *end_frame);
|
||||
|
||||
bool RE_back_cg_set(struct RE_back_cg *bcg, int no);
|
||||
bool RE_back_cg_set_name(struct RE_back_cg *bcg, struct string *name, struct archive *aar);
|
||||
|
||||
void RE_render(struct sact_sprite *sp);
|
||||
cJSON *RE_to_json(struct sact_sprite *sp, bool verbose);
|
||||
|
||||
// save.c
|
||||
void RE_plugin_serialize(struct RE_plugin *plugin, struct iarray_writer *w);
|
||||
void RE_plugin_deserialize(struct RE_plugin *plugin, struct iarray_reader *r, int version);
|
||||
|
||||
// Exposed for PartsEngine 3DLayer
|
||||
int ReignEngine_create_plugin(enum RE_plugin_version version);
|
||||
bool ReignEngine_ReleasePlugin(int handle);
|
||||
bool ReignEngine_BindPlugin(int handle, int sprite);
|
||||
|
||||
// Exposed for MarmotModelEngine
|
||||
void ReignEngine_update_models(void);
|
||||
|
||||
// debug.c
|
||||
void re_debug_init(void);
|
||||
|
||||
#endif /* SYSTEM4_REIGN_H */
|
||||
+26
-4
@@ -20,11 +20,19 @@
|
||||
#include <stdbool.h>
|
||||
#include "sprite.h"
|
||||
|
||||
enum sprite_engine_type {
|
||||
UNINITIALIZED_SPRITE_ENGINE,
|
||||
SACT2_SPRITE_ENGINE,
|
||||
SACTDX_SPRITE_ENGINE,
|
||||
STOAT_SPRITE_ENGINE,
|
||||
CHIPMUNK_SPRITE_ENGINE,
|
||||
};
|
||||
|
||||
struct sact_sprite *sact_get_sprite(int sp);
|
||||
struct sact_sprite *sact_try_get_sprite(int sp);
|
||||
struct sact_sprite *sact_create_sprite(int sp_no, int width, int height, int r, int g, int b, int a);
|
||||
void sact_ModuleFini(void);
|
||||
int sact_Init(void *_, int cg_cache_size);
|
||||
int sact_init(int cg_cache_size, enum sprite_engine_type engine);
|
||||
#define sact_SetWP scene_set_wp
|
||||
#define sact_SetWP_Color scene_set_wp_color
|
||||
int sact_GetScreenWidth(void);
|
||||
@@ -37,10 +45,12 @@ int sact_SP_Count(void);
|
||||
int sact_SP_Enum(struct page **array);
|
||||
int sact_SP_GetMaxZ(void);
|
||||
int sact_SP_SetCG(int sp, int cg);
|
||||
int sact_SP_SetCG2X(int sp_no, int cg_no);
|
||||
int sact_SP_SetCGFromFile(int sp, struct string *filename);
|
||||
int sact_SP_SaveCG(int sp, struct string *filename);
|
||||
int sact_SP_Create(int sp, int width, int height, int r, int g, int b, int a);
|
||||
int sact_SP_CreatePixelOnly(int sp, int width, int height);
|
||||
int sact_SP_CreateCustom(int sp);
|
||||
int sact_SP_Delete(int sp);
|
||||
int sact_SP_DeleteAll(void);
|
||||
int sact_SP_SetPos(int sp_no, int x, int y);
|
||||
@@ -49,7 +59,7 @@ int sact_SP_SetY(int sp_no, int y);
|
||||
int sact_SP_SetZ(int sp, int z);
|
||||
int sact_SP_GetBlendRate(int sp_no);
|
||||
int sact_SP_SetBlendRate(int sp_no, int rate);
|
||||
int sact_SP_SetShow(int sp_no, bool show);
|
||||
int sact_SP_SetShow(int sp_no, int show);
|
||||
int sact_SP_SetDrawMethod(int sp_no, int method);
|
||||
int sact_SP_GetDrawMethod(int sp_no);
|
||||
int sact_SP_IsUsing(int sp_no);
|
||||
@@ -64,7 +74,7 @@ int sact_SP_SetTextHome(int sp_no, int x, int y);
|
||||
int sact_SP_SetTextLineSpace(int sp_no, int px);
|
||||
int sact_SP_SetTextCharSpace(int sp_no, int px);
|
||||
int sact_SP_SetTextPos(int sp_no, int x, int y);
|
||||
int _sact_SP_TextDraw(int sp_no, struct string *text, struct text_metrics *tm);
|
||||
int _sact_SP_TextDraw(int sp_no, struct string *text, struct text_style *tm);
|
||||
int sact_SP_TextDraw(int sp_no, struct string *text, struct page *tm);
|
||||
int sact_SP_TextClear(int sp_no);
|
||||
int sact_SP_TextHome(int sp_no, int size);
|
||||
@@ -81,6 +91,8 @@ int sact_SP_IsPtInRect(int sp_no, int x, int y);
|
||||
int sact_CG_GetMetrics(int cg_no, struct page **page);
|
||||
int sact_SP_GetAMapValue(int sp_no, int x, int y);
|
||||
int sact_SP_GetPixelValue(int sp_no, int x, int y, int *r, int *g, int *b);
|
||||
int sact_SP_SetBrightness(int sp_no, int brightness);
|
||||
int sact_SP_GetBrightness(int sp_no);
|
||||
int sact_GAME_MSG_GetNumOf(void);
|
||||
void sact_IntToZenkaku(struct string **s, int value, int figures, int zero_pad);
|
||||
void sact_IntToHankaku(struct string **s, int value, int figures, int zero_pad);
|
||||
@@ -91,6 +103,7 @@ int sact_Joypad_IsKeyDown(int num, int key);
|
||||
bool sact_Joypad_GetAnalogStickStatus(int num, int type, float *degree, float *power);
|
||||
bool sact_Joypad_GetDigitalStickStatus(int num, int type, bool *left, bool *right, bool *up, bool *down);
|
||||
int sact_Key_ClearFlag(void);
|
||||
void sact_Key_ClearFlagNoCtrl(void);
|
||||
int sact_Key_IsDown(int keycode);
|
||||
int sact_CG_IsExist(int cg_no);
|
||||
void sact_System_GetDate(int *year, int *month, int *mday, int *wday);
|
||||
@@ -99,8 +112,14 @@ void sact_CG_BlendAMapBin(int dst, int dx, int dy, int src, int sx, int sy, int
|
||||
int sact_TRANS_Begin(int type);
|
||||
int sact_TRANS_Update(float rate);
|
||||
int sact_TRANS_End(void);
|
||||
bool sact_VIEW_SetMode(int mode);
|
||||
int sact_VIEW_GetMode(void);
|
||||
bool sact_DX_GetUsePower2Texture(void);
|
||||
void sact_DX_SetUsePower2Texture(bool use);
|
||||
|
||||
extern struct text_metrics text_sprite_tm;
|
||||
extern struct text_style text_sprite_ts;
|
||||
int StoatSpriteEngine_SP_SetDrawMethod(int sp_no, int method);
|
||||
int StoatSpriteEngine_SP_GetDrawMethod(int sp_no);
|
||||
bool StoatSpriteEngine_SP_SetTextSprite(int sp_no, struct string *text);
|
||||
void StoatSpriteEngine_SP_SetTextSpriteType(int type);
|
||||
void StoatSpriteEngine_SP_SetTextSpriteSize(int size);
|
||||
@@ -109,6 +128,9 @@ void StoatSpriteEngine_SP_SetTextSpriteBoldWeight(float weight);
|
||||
void StoatSpriteEngine_SP_SetTextSpriteEdgeWeight(float weight);
|
||||
void StoatSpriteEngine_SP_SetTextSpriteEdgeColor(int r, int g, int b);
|
||||
bool StoatSpriteEngine_SP_SetDashTextSprite(int sp_no, int width, int height);
|
||||
void StoatSpriteEngine_FPS_SetShow(bool show);
|
||||
bool StoatSpriteEngine_FPS_GetShow(void);
|
||||
float StoatSpriteEngine_FPS_Get(void);
|
||||
//int StoatSpriteEngine_SP_SetBrightness(int sp_no, int brightness);
|
||||
//int StoatSpriteEngine_SP_GetBrightness(int sp_no);
|
||||
int StoatSpriteEngine_SYSTEM_IsResetOnce(void);
|
||||
|
||||
+2
-5
@@ -20,14 +20,11 @@
|
||||
#include "cJSON.h"
|
||||
#include "vm.h"
|
||||
|
||||
cJSON *page_to_json(struct page *page);
|
||||
cJSON *vm_value_to_json(enum ain_data_type type, union vm_value val);
|
||||
void json_load_page(struct page *page, cJSON *vars, bool call_dtors);
|
||||
union vm_value json_to_vm_value(enum ain_data_type type, enum ain_data_type struct_type, int array_rank, cJSON *json);
|
||||
|
||||
int save_json(const char *filename, cJSON *json);
|
||||
int save_globals(const char *keyname, const char *filename);
|
||||
int save_group(const char *keyname, const char *filename, const char *group_name, int *n);
|
||||
cJSON *load_json(const char *filename);
|
||||
int save_globals(const char *keyname, const char *filename, const char *group_name, int *n);
|
||||
int load_globals(const char *keyname, const char *filename, const char *group_name, int *n);
|
||||
int delete_save_file(const char *filename);
|
||||
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
#include <stdbool.h>
|
||||
#include "queue.h"
|
||||
|
||||
typedef struct cJSON cJSON;
|
||||
struct texture;
|
||||
|
||||
struct sprite {
|
||||
TAILQ_ENTRY(sprite) entry;
|
||||
// The Z-layer of the sprite within the scene
|
||||
@@ -35,8 +38,12 @@ struct sprite {
|
||||
// the current scene. A sprite should be in the scene if there is pixel
|
||||
// or text data attached to it and it is not hidden.
|
||||
bool in_scene;
|
||||
// Unique ID
|
||||
int id;
|
||||
// The rendering function.
|
||||
void (*render)(struct sprite*);
|
||||
// Debug printing function
|
||||
cJSON *(*to_json)(struct sprite*, bool);
|
||||
};
|
||||
|
||||
extern bool scene_is_dirty;
|
||||
@@ -46,9 +53,14 @@ void scene_unregister_sprite(struct sprite *sp);
|
||||
void scene_render(void);
|
||||
int scene_set_wp(int cg_no);
|
||||
int scene_set_wp_color(int r, int g, int b);
|
||||
int scene_set_wp_texture(struct texture *tex);
|
||||
void scene_set_sprite_z(struct sprite *sp, int z);
|
||||
void scene_set_sprite_z2(struct sprite *sp, int z, int z2);
|
||||
|
||||
void scene_print(void);
|
||||
cJSON *scene_to_json(bool verbose);
|
||||
struct sprite *scene_get(int id);
|
||||
|
||||
static inline void scene_sprite_dirty(struct sprite *sp)
|
||||
{
|
||||
if (!sp)
|
||||
|
||||
+39
-4
@@ -26,16 +26,25 @@
|
||||
struct string;
|
||||
struct page;
|
||||
union vm_value;
|
||||
struct text_style;
|
||||
|
||||
struct sact_sprite {
|
||||
struct sprite sp;
|
||||
TAILQ_ENTRY(sact_sprite) entry;
|
||||
LIST_ENTRY(sact_sprite) entry;
|
||||
// The sprite's texture (CG or solid color). Initialized lazily.
|
||||
struct texture texture;
|
||||
// If no CG is attached to the sprite, the solid color to fill with.
|
||||
SDL_Color color;
|
||||
// Shader params
|
||||
int blend_rate;
|
||||
SDL_Color multiply_color;
|
||||
SDL_Color add_color;
|
||||
// Draw method (blend mode)
|
||||
enum draw_method draw_method;
|
||||
// The position and dimensions of the sprite.
|
||||
Rectangle rect;
|
||||
// The visible area of the sprite
|
||||
Rectangle surface_area;
|
||||
// Arbitrary text can be attached to the sprite. This is rendered on a
|
||||
// separate texture and overlayed.
|
||||
struct {
|
||||
@@ -45,13 +54,29 @@ struct sact_sprite {
|
||||
Point pos;
|
||||
int char_space;
|
||||
int line_space;
|
||||
int current_line_height;
|
||||
} text;
|
||||
// The sprite handle
|
||||
int no;
|
||||
// The CG number attached to the sprite.
|
||||
int cg_no;
|
||||
// The sprite is suspended. The exact meaning of this is unknown.
|
||||
bool suspended;
|
||||
// (optional) Draw plugin bound to this sprite.
|
||||
struct draw_plugin *plugin;
|
||||
};
|
||||
|
||||
/*
|
||||
* XXX: There are two different sprite renderers:
|
||||
* * SACT2 - blend rate and brightness (stored in multiply_color field)
|
||||
* * ChipmunkSpriteEngine - blend rate, add color, multiply color, surface area
|
||||
* (uses parts shader)
|
||||
*
|
||||
* The initialization function determines which renderer is used.
|
||||
*/
|
||||
void sprite_init_sact(void);
|
||||
void sprite_init_chipmunk(void);
|
||||
|
||||
static inline void sprite_dirty(struct sact_sprite *sp)
|
||||
{
|
||||
scene_sprite_dirty(&sp->sp);
|
||||
@@ -60,17 +85,25 @@ static inline void sprite_dirty(struct sact_sprite *sp)
|
||||
void sprite_free(struct sact_sprite *sp);
|
||||
struct texture *sprite_get_texture(struct sact_sprite *sp);
|
||||
void sprite_set_cg(struct sact_sprite *sp, struct cg *cg);
|
||||
void sprite_set_cg_2x(struct sact_sprite *sp, struct cg *cg);
|
||||
int sprite_set_cg_from_asset(struct sact_sprite *sp, int cg_no);
|
||||
int sprite_set_cg_2x_from_asset(struct sact_sprite *sp, int cg_no);
|
||||
int sprite_set_cg_by_name(struct sact_sprite *sp, const char *name);
|
||||
int sprite_set_cg_from_file(struct sact_sprite *sp, const char *path);
|
||||
int sprite_save_cg(struct sact_sprite *sp, const char *path);
|
||||
void sprite_init(struct sact_sprite *sp, int w, int h, int r, int g, int b, int a);
|
||||
void sprite_init(struct sact_sprite *sp);
|
||||
void sprite_init_color(struct sact_sprite *sp, int w, int h, int r, int g, int b, int a);
|
||||
void sprite_init_custom(struct sact_sprite *sp);
|
||||
void sprite_set_pos(struct sact_sprite *sp, int x, int y);
|
||||
void sprite_set_x(struct sact_sprite *sp, int x);
|
||||
void sprite_set_y(struct sact_sprite *sp, int y);
|
||||
static inline void sprite_set_z(struct sact_sprite *sp, int z) { scene_set_sprite_z(&sp->sp, z); }
|
||||
static inline void sprite_set_z2(struct sact_sprite *sp, int z, int z2) { scene_set_sprite_z2(&sp->sp, z, z2); }
|
||||
int sprite_get_blend_rate(struct sact_sprite *sp);
|
||||
static inline int sprite_get_blend_rate(struct sact_sprite *sp) { return sp->blend_rate; }
|
||||
void sprite_set_blend_rate(struct sact_sprite *sp, int rate);
|
||||
void sprite_set_multiply_color(struct sact_sprite *sp, int r, int g, int b);
|
||||
void sprite_set_add_color(struct sact_sprite *sp, int r, int g, int b);
|
||||
void sprite_set_surface_area(struct sact_sprite *sp, int x, int y, int w, int h);
|
||||
static inline void sprite_set_show(struct sact_sprite *sp, bool show) { scene_set_sprite_show(&sp->sp, show); }
|
||||
int sprite_set_draw_method(struct sact_sprite *sp, enum draw_method method);
|
||||
enum draw_method sprite_get_draw_method(struct sact_sprite *sp);
|
||||
@@ -86,7 +119,7 @@ void sprite_set_text_home(struct sact_sprite *sp, int x, int y);
|
||||
void sprite_set_text_line_space(struct sact_sprite *sp, int px);
|
||||
void sprite_set_text_char_space(struct sact_sprite *sp, int px);
|
||||
void sprite_set_text_pos(struct sact_sprite *sp, int x, int y);
|
||||
void sprite_text_draw(struct sact_sprite *sp, struct string *text, struct text_metrics *tm);
|
||||
void sprite_text_draw(struct sact_sprite *sp, struct string *text, struct text_style *ts);
|
||||
void sprite_text_clear(struct sact_sprite *sp);
|
||||
void sprite_text_home(struct sact_sprite *sp, int size);
|
||||
void sprite_text_new_line(struct sact_sprite *sp, int size);
|
||||
@@ -101,5 +134,7 @@ bool sprite_is_point_in(struct sact_sprite *sp, int x, int y);
|
||||
bool sprite_is_point_in_rect(struct sact_sprite *sp, int x, int y);
|
||||
int sprite_get_amap_value(struct sact_sprite *sp, int x, int y);
|
||||
void sprite_get_pixel_value(struct sact_sprite *sp, int x, int y, int *r, int *g, int *b);
|
||||
void sprite_bind_plugin(struct sact_sprite *sp, struct draw_plugin *plugin);
|
||||
void sprite_call_plugins(void);
|
||||
|
||||
#endif /* SYSTEM4_SPRITE_H */
|
||||
|
||||
+9
-9
@@ -39,6 +39,11 @@ enum {
|
||||
STS_MIXER_SAMPLE_FORMAT_FLOAT // floats
|
||||
};
|
||||
|
||||
enum {
|
||||
STS_STREAM_COMPLETE = 0,
|
||||
STS_STREAM_CONTINUE = 1
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -66,7 +71,7 @@ typedef struct {
|
||||
// The callback which will be called when the stream needs more data.
|
||||
typedef int (*sts_mixer_stream_callback)(sts_mixer_sample_t* sample, void* userdata);
|
||||
|
||||
typedef struct {
|
||||
typedef struct sts_mixer_stream_t {
|
||||
void* userdata; // a userdata pointer which will passed to the callback
|
||||
sts_mixer_stream_callback callback; // this callback will be called when the stream needs more data
|
||||
sts_mixer_sample_t sample; // the current stream "sample" which holds the current piece of audio
|
||||
@@ -160,11 +165,6 @@ enum {
|
||||
STS_MIXER_VOICE_STREAMING
|
||||
};
|
||||
|
||||
enum {
|
||||
STS_STREAM_COMPLETE = 0,
|
||||
STS_STREAM_CONTINUE = 1
|
||||
};
|
||||
|
||||
static float sts_mixer__clamp(const float value, const float min, const float max) {
|
||||
if (value < min) return min;
|
||||
else if (value > max) return max;
|
||||
@@ -329,14 +329,14 @@ void sts_mixer_mix_audio(sts_mixer_t* mixer, void* output, unsigned int samples)
|
||||
voice->position = 0.0f;
|
||||
position = 0;
|
||||
}
|
||||
left += sts_mixer__clamp_sample(sts_mixer__get_sample(&voice->stream->sample, position) * voice->gain);
|
||||
right += sts_mixer__clamp_sample(sts_mixer__get_sample(&voice->stream->sample, position + 1) * voice->gain);
|
||||
// added in xsystem4: allow stopping stream via callback return value
|
||||
if (status == STS_STREAM_COMPLETE) {
|
||||
sts_mixer_stop_voice(mixer, i);
|
||||
} else {
|
||||
left += sts_mixer__clamp_sample(sts_mixer__get_sample(&voice->stream->sample, position) * voice->gain);
|
||||
right += sts_mixer__clamp_sample(sts_mixer__get_sample(&voice->stream->sample, position + 1) * voice->gain);
|
||||
voice->position += (float)voice->stream->sample.frequency * advance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
/* Copyright (C) 2023 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM4_SWF_H
|
||||
#define SYSTEM4_SWF_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef int32_t fixed32; // 32-bit 16.16 fixed-point number
|
||||
typedef int16_t fixed16; // 16-bit 8.8 fixed-point number
|
||||
typedef uint16_t fixedu16;
|
||||
typedef uint32_t rgb_t; // 0x00BBGGRR
|
||||
typedef uint32_t rgba_t; // 0xAABBGGRR
|
||||
|
||||
static inline float twips_to_float(int32_t twips) {
|
||||
return twips / 20.0f;
|
||||
}
|
||||
|
||||
static inline float fixed32_to_float(fixed32 f32) {
|
||||
return f32 / 65536.0f;
|
||||
}
|
||||
|
||||
static inline float fixed16_to_float(fixed16 f16) {
|
||||
return f16 / 256.0f;
|
||||
}
|
||||
|
||||
static inline fixed16 fixed16_mul(fixed16 a, fixed16 b) {
|
||||
return (int32_t)a * (int32_t)b / 256;
|
||||
}
|
||||
|
||||
struct swf_rect {
|
||||
// in twips (1/20 pixel)
|
||||
int32_t x_min;
|
||||
int32_t x_max;
|
||||
int32_t y_min;
|
||||
int32_t y_max;
|
||||
};
|
||||
|
||||
struct swf {
|
||||
uint8_t version;
|
||||
uint32_t file_length;
|
||||
struct swf_rect frame_size;
|
||||
fixedu16 frame_rate;
|
||||
uint16_t frame_count;
|
||||
struct swf_tag *tags;
|
||||
};
|
||||
|
||||
struct swf_matrix {
|
||||
fixed32 scale_x;
|
||||
fixed32 scale_y;
|
||||
fixed32 rotate_skew_0;
|
||||
fixed32 rotate_skew_1;
|
||||
int32_t translate_x;
|
||||
int32_t translate_y;
|
||||
};
|
||||
|
||||
struct swf_cxform_with_alpha {
|
||||
int16_t add_terms[4]; // red, green, blue, alpha
|
||||
fixed16 mult_terms[4]; // red, green, blue, alpha
|
||||
};
|
||||
|
||||
// We only support clipped bitmap fill.
|
||||
struct swf_fill_style {
|
||||
uint16_t bitmap_id;
|
||||
bool smoothed;
|
||||
struct swf_matrix matrix;
|
||||
};
|
||||
|
||||
struct swf_sound_spec {
|
||||
uint16_t rate;
|
||||
uint8_t sample_size; // 8 or 16 (bits)
|
||||
uint8_t channels;
|
||||
};
|
||||
|
||||
// Tags
|
||||
|
||||
enum swf_tag_type {
|
||||
TAG_END = 0,
|
||||
TAG_SHOW_FRAME = 1,
|
||||
TAG_DEFINE_SHAPE = 2,
|
||||
TAG_SET_BACKGROUND_COLOR = 9,
|
||||
TAG_DO_ACTION = 12,
|
||||
TAG_DEFINE_SOUND = 14,
|
||||
TAG_START_SOUND = 15,
|
||||
TAG_DEFINE_BITS_LOSSLESS = 20,
|
||||
TAG_PLACE_OBJECT2 = 26,
|
||||
TAG_REMOVE_OBJECT2 = 28,
|
||||
TAG_DEFINE_BITS_LOSSLESS2 = 36,
|
||||
TAG_DEFINE_SPRITE = 39,
|
||||
TAG_SOUND_STREAM_HEAD2 = 45,
|
||||
TAG_FILE_ATTRIBUTES = 69,
|
||||
TAG_PLACE_OBJECT3 = 70,
|
||||
};
|
||||
|
||||
struct swf_tag {
|
||||
int type;
|
||||
struct swf_tag *next;
|
||||
};
|
||||
|
||||
struct swf_tag_define_bits_lossless {
|
||||
struct swf_tag t; // TAG_DEFINE_BITS_LOSSLESS or TAG_DEFINE_BITS_LOSSLESS2
|
||||
uint16_t character_id;
|
||||
uint8_t format;
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
// We only support simple rectangle shapes with a fill style.
|
||||
struct swf_tag_define_shape {
|
||||
struct swf_tag t;
|
||||
uint16_t shape_id;
|
||||
struct swf_rect bounds;
|
||||
struct swf_fill_style fill_style;
|
||||
};
|
||||
|
||||
enum swf_sound_format {
|
||||
SWF_SOUND_UNCOMPRESSED_LE = 3,
|
||||
};
|
||||
|
||||
struct swf_tag_define_sound {
|
||||
struct swf_tag t;
|
||||
uint16_t sound_id;
|
||||
uint8_t format;
|
||||
struct swf_sound_spec spec;
|
||||
uint32_t sample_count;
|
||||
uint8_t *data;
|
||||
uint32_t data_len;
|
||||
};
|
||||
|
||||
struct swf_tag_define_sprite {
|
||||
struct swf_tag t;
|
||||
uint16_t sprite_id;
|
||||
uint16_t frame_count;
|
||||
struct swf_tag *tags;
|
||||
};
|
||||
|
||||
struct swf_tag_do_action {
|
||||
struct swf_tag t;
|
||||
struct swf_action *actions;
|
||||
};
|
||||
|
||||
enum place_object_flags {
|
||||
PLACE_FLAG_RESERVED_BITS = 0xffff - ((1 << 11) - 1),
|
||||
PLACE_FLAG_HAS_CACHE_AS_BITMAP = 1 << 10,
|
||||
PLACE_FLAG_HAS_BLEND_MODE = 1 << 9,
|
||||
PLACE_FLAG_HAS_FILTER_LIST = 1 << 8,
|
||||
PLACE_FLAG_HAS_CLIP_ACTIONS = 1 << 7,
|
||||
PLACE_FLAG_HAS_CLIP_DEPTH = 1 << 6,
|
||||
PLACE_FLAG_HAS_NAME = 1 << 5,
|
||||
PLACE_FLAG_HAS_RATIO = 1 << 4,
|
||||
PLACE_FLAG_HAS_COLOR_TRANSFORM = 1 << 3,
|
||||
PLACE_FLAG_HAS_MATRIX = 1 << 2,
|
||||
PLACE_FLAG_HAS_CHARACTER = 1 << 1,
|
||||
PLACE_FLAG_MOVE = 1 << 0,
|
||||
};
|
||||
|
||||
struct swf_tag_place_object {
|
||||
struct swf_tag t; // TAG_PLACE_OBJECT2 or TAG_PLACE_OBJECT3
|
||||
uint16_t flags;
|
||||
uint16_t depth;
|
||||
uint16_t character_id;
|
||||
struct swf_matrix matrix;
|
||||
struct swf_cxform_with_alpha color_transform;
|
||||
uint16_t ratio;
|
||||
char *name;
|
||||
uint8_t blend_mode;
|
||||
uint8_t bitmap_cache;
|
||||
};
|
||||
|
||||
struct swf_tag_remove_object2 {
|
||||
struct swf_tag t;
|
||||
uint16_t depth;
|
||||
};
|
||||
|
||||
struct swf_tag_set_background_color {
|
||||
struct swf_tag t;
|
||||
rgb_t background_color;
|
||||
};
|
||||
|
||||
struct swf_tag_sound_stream_head {
|
||||
struct swf_tag t;
|
||||
struct swf_sound_spec playback_spec;
|
||||
uint8_t compression;
|
||||
struct swf_sound_spec stream_spec;
|
||||
uint16_t sample_count;
|
||||
int16_t latency_seek;
|
||||
};
|
||||
|
||||
enum start_sound_flags {
|
||||
SOUND_INFO_RESERVED_BITS = 0xff - ((1 << 6) - 1),
|
||||
SOUND_INFO_SYNC_STOP = 1 << 5,
|
||||
SOUND_INFO_SYNC_NO_MULTIPLE = 1 << 4,
|
||||
SOUND_INFO_HAS_ENVELOPE = 1 << 3,
|
||||
SOUND_INFO_HAS_LOOPS = 1 << 2,
|
||||
SOUND_INFO_HAS_OUT_POINT = 1 << 1,
|
||||
SOUND_INFO_HAS_IN_POINT = 1 << 0,
|
||||
};
|
||||
|
||||
struct swf_tag_start_sound {
|
||||
struct swf_tag t;
|
||||
uint16_t sound_id;
|
||||
uint8_t flags;
|
||||
uint32_t in_point;
|
||||
uint32_t out_point;
|
||||
uint16_t loop_count;
|
||||
};
|
||||
|
||||
// Actions
|
||||
|
||||
enum swf_action_type {
|
||||
// Actions without payload.
|
||||
ACTION_END = 0x00,
|
||||
ACTION_PLAY = 0x06,
|
||||
ACTION_STOP = 0x07,
|
||||
ACTION_POP = 0x17,
|
||||
ACTION_CALL_FUNCTION = 0x3d,
|
||||
|
||||
// Actions with payloads.
|
||||
ACTION_GOTO_FRAME = 0x81,
|
||||
ACTION_CONSTANT_POOL = 0x88,
|
||||
ACTION_PUSH = 0x96,
|
||||
};
|
||||
|
||||
struct swf_action {
|
||||
uint8_t code;
|
||||
struct swf_action *next;
|
||||
};
|
||||
|
||||
struct swf_action_constant_pool {
|
||||
struct swf_action a;
|
||||
uint16_t count;
|
||||
char *constant_pool[];
|
||||
};
|
||||
|
||||
struct swf_action_goto_frame {
|
||||
struct swf_action a;
|
||||
uint16_t frame;
|
||||
};
|
||||
|
||||
enum swf_action_push_type {
|
||||
ACTION_PUSH_INTEGER = 7,
|
||||
ACTION_PUSH_CONSTANT8 = 8,
|
||||
};
|
||||
|
||||
struct swf_action_push {
|
||||
struct swf_action a;
|
||||
uint16_t count;
|
||||
struct {
|
||||
enum swf_action_push_type type;
|
||||
uint32_t value;
|
||||
} values[];
|
||||
};
|
||||
|
||||
struct swf *swf_load(uint8_t *data, size_t size);
|
||||
void swf_free(struct swf *swf);
|
||||
struct swf *aff_load(uint8_t *data, size_t size);
|
||||
|
||||
#endif /* SYSTEM4_SWF_H */
|
||||
+54
-4
@@ -26,6 +26,11 @@ enum ain_data_type;
|
||||
struct page;
|
||||
struct string;
|
||||
|
||||
// xsystem4-specific system calls (used for hacks)
|
||||
enum vm_extra_syscall {
|
||||
VM_XSYS_KEY_IS_DOWN = 0x80000000,
|
||||
};
|
||||
|
||||
// Non-heap values. Stored in pages and on the stack.
|
||||
union vm_value {
|
||||
int32_t i;
|
||||
@@ -88,10 +93,16 @@ static inline void stack_push_value(union vm_value v)
|
||||
#define stack_set(n, v) (stack_set_value((n), vm_value_cast(v)))
|
||||
#define stack_push(v) (stack_push_value(vm_value_cast(v)))
|
||||
union vm_value stack_pop(void);
|
||||
union vm_value stack_peek(int n);
|
||||
union vm_value *stack_peek_var(void);
|
||||
|
||||
union vm_value local_get(int varno);
|
||||
union vm_value global_get(int varno);
|
||||
union vm_value member_get(int varno);
|
||||
void global_set(int varno, union vm_value val, bool call_dtors);
|
||||
struct page *local_page(void);
|
||||
struct page *get_local_page(int frame_no);
|
||||
struct page *get_struct_page(int frame_no);
|
||||
|
||||
int vm_string_ref(struct string *s);
|
||||
int vm_copy_page(struct page *page);
|
||||
@@ -100,21 +111,43 @@ union vm_value vm_copy(union vm_value v, enum ain_data_type type);
|
||||
int vm_execute_ain(struct ain *program);
|
||||
void vm_call(int fno, int struct_page);
|
||||
int vm_time(void);
|
||||
void vm_sleep(int ms);
|
||||
|
||||
void hll_call(int libno, int fno);
|
||||
void link_libraries(void);
|
||||
bool library_exists(int libno);
|
||||
bool library_function_exists(int libno, int fno);
|
||||
void init_libraries(void);
|
||||
void exit_libraries(void);
|
||||
|
||||
void vm_stack_trace(void);
|
||||
noreturn void _vm_error(const char *fmt, ...);
|
||||
noreturn void vm_exit(int code);
|
||||
// NOTE: This can probably be merged with ain_data_type, since the values are
|
||||
// disjoint.
|
||||
enum string_format_type {
|
||||
STRFMT_INT = 2,
|
||||
STRFMT_FLOAT = 3,
|
||||
STRFMT_STRING = 4,
|
||||
STRFMT_BOOL = 48,
|
||||
STRFMT_LONG_INT = 56,
|
||||
};
|
||||
struct string *string_format(struct string *fmt, union vm_value arg, enum string_format_type type);
|
||||
|
||||
void vm_stack_trace(void);
|
||||
_Noreturn void _vm_error(const char *fmt, ...);
|
||||
_Noreturn void vm_exit(int code);
|
||||
_Noreturn void vm_reset(void);
|
||||
|
||||
extern bool vm_reset_once;
|
||||
|
||||
#if defined(__ANDROID__) || defined(RELEASE)
|
||||
// Report the error with a message box and exit.
|
||||
#define VM_ERROR ERROR
|
||||
#else
|
||||
#define VM_ERROR(fmt, ...) \
|
||||
_vm_error("*ERROR*(%s:%s:%d): " fmt "\n", __FILE__, __func__, __LINE__, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifdef VM_PRIVATE
|
||||
#include "system4/little_endian.h"
|
||||
#include "system4/ain.h"
|
||||
|
||||
struct function_call {
|
||||
int32_t fno;
|
||||
@@ -129,6 +162,23 @@ extern int32_t call_stack_ptr;
|
||||
|
||||
extern size_t instr_ptr;
|
||||
|
||||
// Read argument N for the current instruction.
|
||||
static inline int32_t get_argument(int n)
|
||||
{
|
||||
return LittleEndian_getDW(ain->code, instr_ptr + 2 + n*4);
|
||||
}
|
||||
|
||||
// XXX: not strictly portable
|
||||
static inline float get_argument_float(int n)
|
||||
{
|
||||
union vm_value v;
|
||||
v.i = LittleEndian_getDW(ain->code, instr_ptr + 2 + n*4);
|
||||
return v.f;
|
||||
}
|
||||
|
||||
uint32_t get_switch_address(int no, int val);
|
||||
uint32_t get_strswitch_address(int no, struct string *str);
|
||||
|
||||
int vm_save_image(const char *key, const char *path);
|
||||
void vm_load_image(const char *key, const char *path);
|
||||
struct page *vm_load_image_comments(const char *key, const char *path, int *success);
|
||||
|
||||
+9
-9
@@ -33,6 +33,7 @@ enum vm_pointer_type {
|
||||
// Heap-backed objects. Reference counted.
|
||||
struct vm_pointer {
|
||||
int ref;
|
||||
uint32_t seq;
|
||||
enum vm_pointer_type type;
|
||||
union {
|
||||
struct string *s;
|
||||
@@ -40,7 +41,10 @@ struct vm_pointer {
|
||||
};
|
||||
#ifdef DEBUG_HEAP
|
||||
size_t alloc_addr;
|
||||
size_t ref_addr;
|
||||
size_t ref_addr[16];
|
||||
size_t ref_nr;
|
||||
size_t deref_addr[16];
|
||||
size_t deref_nr;
|
||||
size_t free_addr;
|
||||
#endif
|
||||
};
|
||||
@@ -57,6 +61,8 @@ void heap_ref(int slot);
|
||||
void heap_unref(int slot);
|
||||
void exit_unref(int slot);
|
||||
|
||||
uint32_t heap_get_seq(int slot);
|
||||
|
||||
bool heap_index_valid(int index);
|
||||
bool page_index_valid(int index);
|
||||
bool string_index_valid(int index);
|
||||
@@ -70,17 +76,11 @@ void heap_struct_assign(int lval, int rval);
|
||||
int32_t heap_alloc_page(struct page *page);
|
||||
int32_t heap_alloc_string(struct string *s);
|
||||
|
||||
/*
|
||||
* Guarantee `headroom` free slots are available on the heap.
|
||||
*
|
||||
* XXX: this is a hack to fix an issue with HLL calls where a pointer into the
|
||||
* heap can be made invalid by a heap reallocation triggered within the
|
||||
* body of the HLL function.
|
||||
*/
|
||||
void heap_guarantee(unsigned headroom);
|
||||
void heap_describe_slot(int slot);
|
||||
|
||||
#ifdef VM_PRIVATE
|
||||
|
||||
extern uint32_t heap_next_seq;
|
||||
extern int32_t *heap_free_stack;
|
||||
extern size_t heap_free_ptr;
|
||||
|
||||
|
||||
+17
-9
@@ -50,8 +50,8 @@ enum page_type {
|
||||
* Multi-dimensional arrays are implemented as a tree of pages (meaning the
|
||||
* whole array is NOT contiguous in memory).
|
||||
*
|
||||
* Delegates: each delegate object is backed by a page storing object/function
|
||||
* pairs.
|
||||
* Delegates: each delegate object is backed by a page storing (object,
|
||||
* function, seq) triples.
|
||||
*/
|
||||
struct page {
|
||||
enum page_type type;
|
||||
@@ -66,11 +66,17 @@ struct page {
|
||||
int index;
|
||||
enum ain_data_type a_type;
|
||||
};
|
||||
// array-specific metadata
|
||||
struct {
|
||||
int struct_type;
|
||||
int rank;
|
||||
} array;
|
||||
union {
|
||||
// local-specific metadata
|
||||
struct {
|
||||
int struct_ptr;
|
||||
} local;
|
||||
// array-specific metadata
|
||||
struct {
|
||||
int struct_type;
|
||||
int rank;
|
||||
} array;
|
||||
};
|
||||
int nr_vars;
|
||||
union vm_value values[];
|
||||
};
|
||||
@@ -97,7 +103,7 @@ static inline void _page_set_var(struct page *page, int i, union vm_value v)
|
||||
|
||||
// variables
|
||||
union vm_value variable_initval(enum ain_data_type type);
|
||||
void variable_fini(union vm_value v, enum ain_data_type type);
|
||||
void variable_fini(union vm_value v, enum ain_data_type type, bool call_dtor);
|
||||
enum ain_data_type variable_type(struct page *page, int varno, int *struct_type, int *array_rank);
|
||||
void variable_set(struct page *page, int varno, enum ain_data_type type, union vm_value val);
|
||||
|
||||
@@ -133,10 +139,12 @@ void array_reverse(struct page *page);
|
||||
// delegates
|
||||
struct page *delegate_new_from_method(int obj, int fun);
|
||||
int delegate_numof(struct page *page);
|
||||
bool delegate_contains(struct page *dst, int obj, int fun);
|
||||
void delegate_erase(struct page *page, int obj, int fun);
|
||||
struct page *delegate_append(struct page *dst, int obj, int fun);
|
||||
struct page *delegate_plusa(struct page *dst, struct page *add);
|
||||
struct page *delegate_minusa(struct page *dst, struct page *minus);
|
||||
struct page *delegate_clear(struct page *page);
|
||||
void delegate_get(struct page *page, int i, int *obj_out, int *fun_out);
|
||||
bool delegate_get(struct page *page, int i, int *obj_out, int *fun_out);
|
||||
|
||||
#endif /* SYSTEM4_PAGE_H */
|
||||
|
||||
@@ -20,9 +20,16 @@
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
enum resume_save_format {
|
||||
SAVE_FORMAT_JSON,
|
||||
SAVE_FORMAT_RSM,
|
||||
};
|
||||
|
||||
struct config {
|
||||
char *game_name;
|
||||
char *boot_name;
|
||||
char *ain_filename;
|
||||
char *vm_name;
|
||||
char *game_dir;
|
||||
char *save_dir;
|
||||
char *home_dir;
|
||||
@@ -36,9 +43,15 @@ struct config {
|
||||
char *bgi_path;
|
||||
char *wai_path;
|
||||
char *ex_path;
|
||||
char *fnl_path;
|
||||
char *font_paths[2];
|
||||
|
||||
bool joypad;
|
||||
bool echo;
|
||||
float text_x_scale;
|
||||
bool manual_text_x_scale;
|
||||
enum resume_save_format save_format;
|
||||
int msgskip_delay;
|
||||
};
|
||||
|
||||
extern struct config config;
|
||||
@@ -52,17 +65,36 @@ const char *display_utf0(const char *utf);
|
||||
const char *display_utf1(const char *utf);
|
||||
const char *display_utf2(const char *utf);
|
||||
|
||||
void indent_message(int indent, const char *fmt, ...);
|
||||
|
||||
void log_message(const char *log, const char *fmt, ...);
|
||||
|
||||
#define UNIMPLEMENTED(fmt, ...) \
|
||||
sys_warning("unimplemented: %s" fmt "\n", __func__, ##__VA_ARGS__)
|
||||
|
||||
bool is_absolute_path(const char *path);
|
||||
char *unix_path(const char *path);
|
||||
char *gamedir_path(const char *path);
|
||||
char *gamedir_path_icase(const char *path);
|
||||
char *savedir_path(const char *path);
|
||||
|
||||
void get_date(int *year, int *month, int *mday, int *wday);
|
||||
void get_time(int *hour, int *min, int *sec, int *ms);
|
||||
|
||||
void screenshot_save(void);
|
||||
|
||||
#ifndef XSYS4_DATA_DIR
|
||||
#define XSYS4_DATA_DIR "/usr/local/share/xsystem4"
|
||||
#endif
|
||||
|
||||
#define MMAP_IF_64BIT (sizeof(void*) >= 8 ? ARCHIVE_MMAP : 0)
|
||||
|
||||
extern bool game_daibanchou_en;
|
||||
extern bool game_rance02_mg;
|
||||
extern bool game_rance6_mg;
|
||||
extern bool game_rance7_mg;
|
||||
extern bool game_rance8;
|
||||
extern bool game_rance8_mg;
|
||||
extern bool game_dungeons_and_dolls;
|
||||
|
||||
#endif /* XSYSTEM4_H */
|
||||
|
||||
+29
-14
@@ -1,21 +1,36 @@
|
||||
project('system4', 'c',
|
||||
meson_version: '>= 0.59',
|
||||
default_options : ['c_std=c11'])
|
||||
add_project_arguments('-D_DEFAULT_SOURCE', language : 'c')
|
||||
add_project_arguments('-DXSYS4_DATA_DIR="' + get_option('prefix') / get_option('datadir') / 'xsystem4"', language : 'c')
|
||||
|
||||
zlib = dependency('zlib')
|
||||
libm = meson.get_compiler('c').find_library('m', required: false)
|
||||
sdl2 = dependency('sdl2')
|
||||
sdl2ttf = dependency('SDL2_ttf')
|
||||
ffi = dependency('libffi')
|
||||
tj = dependency('libturbojpeg')
|
||||
webp = dependency('libwebp')
|
||||
png = dependency('libpng')
|
||||
cglm = dependency('cglm', fallback : ['cglm', 'cglm_dep'])
|
||||
sndfile = dependency('sndfile')
|
||||
if get_option('buildtype').startswith('release')
|
||||
add_project_arguments('-DRELEASE', language : 'c')
|
||||
endif
|
||||
|
||||
if get_option('use_gles')
|
||||
gles = dependency('glesv2')
|
||||
static_libs = false
|
||||
if host_machine.system() == 'windows'
|
||||
static_libs = true
|
||||
endif
|
||||
|
||||
zlib = dependency('zlib', static : static_libs)
|
||||
libm = meson.get_compiler('c').find_library('m', required: false)
|
||||
sdl2 = dependency('sdl2', static : static_libs)
|
||||
ft2 = dependency('freetype2', static : static_libs)
|
||||
ffi = dependency('libffi', static : static_libs)
|
||||
tj = dependency('libturbojpeg', static : static_libs)
|
||||
webp = dependency('libwebp', static : static_libs)
|
||||
png = dependency('libpng', static : static_libs)
|
||||
cglm = dependency('cglm', version : '>=0.9.2', fallback : ['cglm', 'cglm_dep'])
|
||||
sndfile = dependency('sndfile', static : static_libs)
|
||||
|
||||
avcodec = dependency('libavcodec', version : '>=59.37.100', required: false, static : static_libs)
|
||||
avformat = dependency('libavformat', required: false, static : static_libs)
|
||||
avutil = dependency('libavutil', version : '>=57.28.100', required: false, static : static_libs)
|
||||
swscale = dependency('libswscale', required: false, static : static_libs)
|
||||
|
||||
gles = dependency('glesv2', version : '>=3', required: get_option('opengles'))
|
||||
if gles.found()
|
||||
gl_deps = [gles]
|
||||
add_project_arguments('-DUSE_GLES', language : 'c')
|
||||
else
|
||||
@@ -24,8 +39,8 @@ else
|
||||
gl_deps = [gl, glew]
|
||||
endif
|
||||
|
||||
chibi = dependency('chibi-scheme', required : false)
|
||||
readline = dependency('readline', required : false)
|
||||
chibi = dependency('chibi-scheme', required : false, static : static_libs)
|
||||
readline = dependency('readline', required : false, static : static_libs)
|
||||
|
||||
flex = find_program('flex')
|
||||
bison = find_program('bison')
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
option('debugger', type : 'feature', value : 'auto')
|
||||
option('use_gles', type : 'boolean', value : false, description : 'Target OpenGL ES 3.0')
|
||||
option('opengles', type : 'feature', value : 'auto', description : 'Target OpenGL ES 3.0')
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
Updates to the System 4 language since Evenicle
|
||||
===============================================
|
||||
|
||||
SLC's System 4 decompiler/compiler was last updated to work with Evenicle. There have been
|
||||
significant changes to the System 4 language since. What follows is a list of features which
|
||||
have been added.
|
||||
|
||||
Namespaces
|
||||
----------
|
||||
|
||||
Added in Tsumamigui 3. This is purely a compile time feature. If you look at the global
|
||||
variable and function names, you will notice lots of `::`, e.g. `time::detail::g_PrevPlayTime`.
|
||||
|
||||
Lambdas
|
||||
-------
|
||||
|
||||
Added in Heartful Maman. I'm not sure how these work, but if you look at the disassembled
|
||||
code there are now functions embedded within functions with names like `<lambda ...>`.
|
||||
|
||||
Generic Arrays
|
||||
--------------
|
||||
|
||||
Added in Ixseal. The old array types have been replaced with a new generic array type, with
|
||||
the syntax `array<type>`. The newly added types 79 and 80 were added to support this feature
|
||||
(type 79 is the regular array type, and type 80 is the reference-to-array type).
|
||||
|
||||
There also appears to be a `foreach` loop construct added which operates on the new arrays.
|
||||
I believe the newly added type 82 is some kind of iterator type which is used in these loops.
|
||||
The syntax is likely something like:
|
||||
|
||||
foreach (var : array) {
|
||||
// do somthing with var
|
||||
}
|
||||
|
||||
where `var` is a type 82.
|
||||
|
||||
Array methods are now implemented via the `Array` library rather than as dedicated bytecode
|
||||
instructions.
|
||||
|
||||
Properties (Getters/Setters)
|
||||
----------------------------
|
||||
|
||||
Added in Ixseal. Some struct/class members now have their names surrounded in angle brackets,
|
||||
e.g. `<PropertyName>`. These members have get/set methods defined for them, with names like
|
||||
`ClassName@PropertyName::get`/`ClassName@PropertyName::set`. Most likely these methods are
|
||||
automatically generated.
|
||||
|
||||
Interfaces & vtables
|
||||
--------------------
|
||||
|
||||
Added in Ixseal. If you look at the struct declarations, you will find empty structs with
|
||||
names like `IParts`. Embedded in the struct data in the ain file there is a list of interfaces
|
||||
for each struct, listing the implemented interfaces, along with indices into the struct's
|
||||
vtable corresponding to the location of the methods for that interface.
|
||||
|
||||
All structures which implement an interface have an `array<int>` as their first member with
|
||||
the name `<vtable>`. This is a table listing the virtual (interface) methods of the struct.
|
||||
|
||||
`CALLMETHOD` now takes the function index as a stack-passed argument, rather than a static
|
||||
instruction argument. The instruction argument to `CALLMETHOD` is changed to be the number of
|
||||
arguments that are passed to the method. The calling convention is as follows:
|
||||
|
||||
PUSH <struct page>
|
||||
PUSH <function index>
|
||||
PUSH <argument 0>
|
||||
...
|
||||
PUSH <argument n>
|
||||
CALLMETHOD <n>
|
||||
|
||||
This new calling convention enables calling compile-time-indeterminate methods through the
|
||||
vtable.
|
||||
|
||||
To support variables which represent an abstract *instance of an interface*, the new type 89
|
||||
was added. This is a 2-valued type in which the first value is a struct page, and the second
|
||||
value is an index into the struct's vtable corresponding to the location of the interface's
|
||||
methods. Thus, methods can be called on interface objects as follows:
|
||||
|
||||
PUSH <page> ; [A] (A is page of type-89 variable)
|
||||
PUSH <var> ; [A,B] (B is index of type-89 variable)
|
||||
REFREF ; [C,D] (C is page of object; D is index into object's vtable)
|
||||
DUP_U2 ; [C,D,C]
|
||||
PUSH 0 ; [C,D,C,0]
|
||||
REF ; [C,D,E] (E is the vtable of object C)
|
||||
SWAP ; [C,E,D]
|
||||
PUSH <n> ; [C,E,D,n]
|
||||
ADD ; [C,E,D+n]
|
||||
REF ; [C,F] (F is global index of method)
|
||||
PUSH <arg 0> ; [C,F,a0] (a0 is the first argument)
|
||||
...
|
||||
PUSH <arg n> ; [C,F,a0,...,an] (an is the last argument)
|
||||
CALLMETHOD <n> ; [stack now contains return value of method]
|
||||
|
||||
Generic Structures
|
||||
------------------
|
||||
|
||||
Added in Rance X. This is purely a compile time feature. If you look at the struct names you
|
||||
will find names like `MapPair<int, QuestMapPosition>`, where the type names inside the angled
|
||||
brackets appear again in the struct members.
|
||||
|
||||
Function Overloading
|
||||
--------------------
|
||||
|
||||
Added in Rance X. This is purely a compile time feature. If you look at the function
|
||||
signatures you will notice duplicate names with differing argument lists. In fact, there are
|
||||
even duplicate function names with *exactly the same argument list*. I'm not sure why.
|
||||
|
||||
Enums
|
||||
-----
|
||||
|
||||
Added in Rance X. Enum types have the following automatically generated static methods:
|
||||
|
||||
* `string EnumType@String(EnumType value)`
|
||||
* `EnumType EnumType::Parse(string value)`
|
||||
* `EnumType EnumType::Parse(int value)`
|
||||
* `array<EnumType> EnumType::GetList(void)`
|
||||
* `bool EnumType::IsExist(int value)`
|
||||
* `int EnumType::NumOf(void)`
|
||||
|
||||
There are 3 new types associated with enums. Types 92 and 93 are the regular enum and
|
||||
reference-to-enum types, respectively. They store an index into the enum table in the struct
|
||||
type field of their type descriptor.
|
||||
|
||||
Type 91 is only used in the return value from the EnumType::Parse methods. It's wrapped in an option
|
||||
type (new type 86).
|
||||
|
||||
Option Types
|
||||
------------
|
||||
|
||||
Added in Rance X. The new type 86 serves this function. Type 86 is a 2-valued type where the first
|
||||
value is the wrapped value, and the second value indicates sucess/failure.
|
||||
|
||||
In Rance X, a value of -1 as the second value indicates failure, and a value of 0 indicates success.
|
||||
In Evenicle 2 and later games, a value of 1 as the second value indicates failure.
|
||||
|
||||
Unclear
|
||||
-------
|
||||
|
||||
Variables in the ain file now have two names. Usually these names are the same string, but
|
||||
occasionally there are minor differences between them, and the differences don't reveal much
|
||||
about what the purpose of the second name is.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,13 +15,12 @@
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float threshold;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
vec4 texel = texture(tex, tex_coord);
|
||||
texel.a *= threshold;
|
||||
frag_color = texel;
|
||||
float alpha = texture(tex, tex_coord).a;
|
||||
float on = step(0.001, alpha);
|
||||
frag_color = vec4(on, on, on, alpha);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform vec4 color;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
frag_color = texture(tex, tex_coord) * color;
|
||||
}
|
||||
@@ -22,5 +22,5 @@ out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
vec4 texel = texture(tex, tex_coord);
|
||||
frag_color = vec4(color.rgb, texel.a);
|
||||
frag_color = vec4(color.rgb, texel.a * color.a);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform vec4 color; // .a is the discard threshold
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
vec4 texel = texture(tex, tex_coord);
|
||||
if (texel.r < color.a)
|
||||
discard;
|
||||
frag_color = vec4(color.rgb, texel.r);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float threshold;
|
||||
uniform vec4 color;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
// alpha values at or above `rate + 16` get an alpha of 0
|
||||
// alpha values below `rate` get an alpha of 255
|
||||
// alpha values between `rate` and `rate + 16` are blended
|
||||
float delta = texture(tex, tex_coord).a - threshold;
|
||||
float alpha = 1.0 - clamp(delta * 16.0, 0.0, 1.0);
|
||||
frag_color = vec4(color.rgb, alpha);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/* Copyright (C) 2023 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
vec4 texel = texture(tex, tex_coord);
|
||||
float gray = dot(texel.rgb, vec3(0.299, 0.587, 0.114));
|
||||
frag_color = vec4(vec3(gray), texel.a);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float threshold;
|
||||
uniform vec4 color; // .a is the discard threshold
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
// the fractional part of threshold becomes a weight value for the
|
||||
// edge pixels
|
||||
int size = int(floor(threshold));
|
||||
int cutoff = size + 1;
|
||||
float edge_weight = fract(threshold) / 4.0;
|
||||
vec2 tex_size = vec2(textureSize(tex, 0).xy);
|
||||
float t = ceil(threshold);
|
||||
|
||||
float a_out = 0.0;
|
||||
|
||||
for (int x = -cutoff; x <= cutoff; x++) {
|
||||
for (int y = -cutoff; y <= cutoff; y++) {
|
||||
float d = distance(vec2(x, y), vec2(0, 0));
|
||||
if (d > t)
|
||||
continue;
|
||||
float a = texture(tex, tex_coord + vec2(x, y) / tex_size).r;
|
||||
if (d > float(size))
|
||||
a *= edge_weight;
|
||||
a_out += a;
|
||||
}
|
||||
}
|
||||
|
||||
if (a_out < color.a)
|
||||
discard;
|
||||
frag_color = vec4(color.rgb, min(a_out, 1.0));
|
||||
}
|
||||
+17
-3
@@ -15,7 +15,10 @@
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform sampler2D light_texture;
|
||||
uniform bool use_lightmap;
|
||||
uniform float alpha_mod;
|
||||
uniform bool use_fog;
|
||||
|
||||
in float dist;
|
||||
in vec2 tex_coord;
|
||||
@@ -26,7 +29,18 @@ const vec3 FOG_COLOR = vec3(0.0, 0.0, 0.0);
|
||||
|
||||
void main() {
|
||||
vec4 texel = texture(tex, tex_coord);
|
||||
float fog_factor = (FOG_MAX_DIST - dist) / FOG_MAX_DIST;
|
||||
fog_factor = clamp(fog_factor, 0.3, 1.0);
|
||||
frag_color = vec4(mix(FOG_COLOR, vec3(texel), fog_factor), texel.a * alpha_mod);
|
||||
float light_factor = 1.0;
|
||||
if (use_lightmap) {
|
||||
light_factor = texture(light_texture, tex_coord).a;
|
||||
}
|
||||
if (use_fog) {
|
||||
float fog_factor = (FOG_MAX_DIST - dist) / FOG_MAX_DIST;
|
||||
fog_factor = clamp(fog_factor, 0.3, 1.0);
|
||||
frag_color = vec4(mix(FOG_COLOR, texel.rgb * light_factor, fog_factor), texel.a * alpha_mod);
|
||||
} else {
|
||||
if (texel.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
frag_color = vec4(texel.rgb * light_factor, texel.a * alpha_mod);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
uniform mat4 local_transform;
|
||||
uniform mat4 view_transform;
|
||||
uniform mat4 proj_transform;
|
||||
uniform vec2 uv_offset;
|
||||
uniform vec2 uv_scale;
|
||||
|
||||
in vec3 vertex_pos;
|
||||
in vec2 vertex_uv;
|
||||
@@ -27,5 +29,5 @@ void main() {
|
||||
vec4 pos = view_transform * local_transform * vec4(vertex_pos, 1.0);
|
||||
dist = abs(pos.z);
|
||||
gl_Position = proj_transform * pos;
|
||||
tex_coord = vertex_uv;
|
||||
tex_coord = vertex_uv * uv_scale + uv_offset;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/* Copyright (C) 2023 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
|
||||
uniform float amp;
|
||||
uniform float t;
|
||||
|
||||
const float PI = 3.14159265;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
vec4 sample_texture(float offset_x) {
|
||||
return texture(tex, vec2(tex_coord.x + offset_x, tex_coord.y));
|
||||
}
|
||||
|
||||
void main() {
|
||||
float a = amp * cos((tex_coord.y + fract(t / 8.0)) * 16.0 * PI);
|
||||
float p1 = 6.0 * tex_coord.y + 1.5;
|
||||
float p2 = 4.0 * tex_coord.y + 2.0;
|
||||
float offset1 = cos(p1 * 2.0 * PI) * a;
|
||||
float offset2 = cos(p2 * 2.0 * PI) * a;
|
||||
|
||||
frag_color = mix(sample_texture(offset1), sample_texture(offset2), 0.5);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex; // the new scene
|
||||
uniform sampler2D old; // the old scene
|
||||
uniform vec2 resolution; // the screen resolution
|
||||
uniform float progress; // effect progress (0..1)
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
const float PI = 3.14159265;
|
||||
const vec2 tex_offset = vec2(1.0, 1.0);
|
||||
const float sigma = 50.0;
|
||||
|
||||
vec3 get_pixel(vec2 xy) {
|
||||
return texture(old, xy).rgb;
|
||||
}
|
||||
|
||||
// incremental gaussian blur algorithm
|
||||
void main() {
|
||||
vec2 p = tex_coord * resolution;
|
||||
float blur_size = progress * 100.0;
|
||||
float blur_half = blur_size / 2.0;
|
||||
|
||||
vec3 gauss;
|
||||
gauss.x = 1.0 / (sqrt(2.0 * PI) * sigma);
|
||||
gauss.y = exp(-0.5 / (sigma * sigma));
|
||||
gauss.z = gauss.y * gauss.y;
|
||||
|
||||
vec3 avg = vec3(0.0);
|
||||
float coefficient_sum = 0.0;
|
||||
|
||||
avg += get_pixel(p / resolution) * gauss.x;
|
||||
coefficient_sum += gauss.x;
|
||||
gauss.xy *= gauss.yz;
|
||||
|
||||
vec2 tex_offset = vec2(0.25, 0.75);
|
||||
|
||||
for (float i = 1.0; i <= blur_half; i += 1.0) {
|
||||
avg += get_pixel((p - i * tex_offset) / resolution) * gauss.x;
|
||||
avg += get_pixel((p + i * tex_offset) / resolution) * gauss.x;
|
||||
coefficient_sum += 2.0 * gauss.x;
|
||||
gauss.xy *= gauss.yz;
|
||||
}
|
||||
|
||||
tex_offset = vec2(0.75, 0.25);
|
||||
|
||||
for (float i = 1.0; i <= blur_half; i += 1.0) {
|
||||
avg += get_pixel((p - i * tex_offset) / resolution) * gauss.x;
|
||||
avg += get_pixel((p + i * tex_offset) / resolution) * gauss.x;
|
||||
coefficient_sum += 2.0 * gauss.x;
|
||||
gauss.xy *= gauss.yz;
|
||||
}
|
||||
|
||||
vec3 old_px = avg / coefficient_sum;
|
||||
vec3 new_px = texture(tex, tex_coord).rgb;
|
||||
frag_color = vec4(mix(old_px, new_px, pow(progress, 3.0)), 1.0);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/* Copyright (C) 2026 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex; // the new scene
|
||||
uniform sampler2D old; // the old scene
|
||||
uniform vec2 resolution; // the screen resolution
|
||||
uniform float progress; // effect progress (0..1)
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
#define EFFECT_HEIGHT 210.0
|
||||
|
||||
void main() {
|
||||
// start position of effect
|
||||
float top = ((resolution.y + EFFECT_HEIGHT) * (1.0 - progress)) - EFFECT_HEIGHT;
|
||||
// current pixel y-coordinate
|
||||
float p = tex_coord.y * resolution.y;
|
||||
// calculate alpha value
|
||||
float alpha = 1.0 - clamp((p - top) / EFFECT_HEIGHT, 0.0, 1.0);
|
||||
vec3 col = mix(texture(tex, tex_coord).rgb, texture(old, tex_coord).rgb, alpha);
|
||||
frag_color = vec4(col, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex; // the new scene
|
||||
uniform sampler2D old; // the old scene
|
||||
uniform vec2 resolution; // the screen resolution
|
||||
uniform float progress; // effect progress (0..1)
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
const float PI = 3.14159265;
|
||||
|
||||
vec3 get_pixel(vec2 xy, float fade) {
|
||||
return mix(texture(old, xy).rgb, texture(tex, xy).rgb, fade);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 p = tex_coord * resolution;
|
||||
// 1 - (x - 1)^2
|
||||
float fade_progress = 1.0 - pow(progress - 1.0, 2.0); // (0..0.5..1) -> (0..1) [parabolic]
|
||||
// sin(PI*x)^4
|
||||
float blur_size = pow(sin(PI * progress), 4.0); // (0..0.5..1) -> (0..1..0) [sine wave ^ 4]
|
||||
float blur_half = (blur_size * resolution.x) / 2.0;
|
||||
|
||||
vec3 avg = vec3(0.0);
|
||||
float count = 0.0;
|
||||
|
||||
avg += get_pixel(p / resolution, fade_progress);
|
||||
count++;
|
||||
|
||||
for (float i = 1.0; i <= blur_half; i += 2.0) {
|
||||
avg += get_pixel(vec2(p.x + i, p.y) / resolution, fade_progress);
|
||||
avg += get_pixel(vec2(p.x - i, p.y) / resolution, fade_progress);
|
||||
count += 2.0;
|
||||
}
|
||||
|
||||
frag_color = vec4(avg / count, 1.0);
|
||||
}
|
||||
@@ -22,47 +22,30 @@ uniform float progress; // effect progress (0..1)
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
#define MAX_STRIDE 128.0
|
||||
#define MAX_CHUNK 128.0
|
||||
|
||||
vec3 get_pixel(vec2 xy) {
|
||||
return mix(texture(old, xy).rgb, texture(tex, xy).rgb, progress);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// size of each chunk
|
||||
float stride = MAX_STRIDE * (1.0 - abs(progress * 2.0 - 1.0)); // progress=(0..0.5..1) -> stride=(0..max..0)
|
||||
// offset of frag coordinate relative to chunk
|
||||
vec2 offset = mod(gl_FragCoord.xy, stride);
|
||||
// the chunk coordinate
|
||||
vec2 chunk = tex_coord - offset / resolution;
|
||||
// coordinate of chunk center
|
||||
vec2 center = chunk + vec2(stride/2.0, stride/2.0) / resolution;
|
||||
|
||||
// FIXME: In this implementation, the anchor point is at the top left of the screen.
|
||||
// It should be anchored at the center to match SACT2.dll.
|
||||
frag_color = vec4(get_pixel(center), 1.0);
|
||||
}
|
||||
|
||||
/* NOTE: This is an alternate implementation which computes an average for each chunk.
|
||||
SACT2.dll does not do this.
|
||||
|
||||
#define SAMPLES 8.0
|
||||
#define MAX_STRIDE 16.0
|
||||
|
||||
vec3 get_pixel(vec2 xy) {
|
||||
return mix(texture(old, xy).xyz, texture(tex, xy).xyz, progress);
|
||||
float triangle(float f) {
|
||||
return 1.0 - abs(f * 2.0 - 1.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
float stride = MAX_STRIDE * (1.0 - abs(progress * 2.0 - 1.0)); // progress=(0..0.5..1) -> stride=(0..max..0)
|
||||
vec3 avg = vec3(0, 0, 0);
|
||||
vec2 xy = floor(gl_FragCoord.xy / (SAMPLES * stride)) * (SAMPLES * stride);
|
||||
for (float j = 0.0; j < SAMPLES; j += 1.0) {
|
||||
for (float i = 0.0; i < SAMPLES; i+= 1.0) {
|
||||
avg += get_pixel((xy + stride * vec2(i, j)) / resolution);
|
||||
}
|
||||
}
|
||||
avg /= SAMPLES * SAMPLES;
|
||||
frag_color = vec4(avg, 1.0);
|
||||
// center of the screen
|
||||
vec2 center = resolution * 0.5;
|
||||
// size of each chunk
|
||||
float chunk_size = floor(triangle(progress) * MAX_CHUNK);
|
||||
// number of chunks in each screen quadrant (including partial chunks at edges)
|
||||
vec2 chunks_per_quadrant = floor(center / chunk_size);
|
||||
// offset from screen to edge of grid (negative or zero)
|
||||
vec2 grid_off = center - (chunks_per_quadrant * chunk_size);
|
||||
// offset of pixel in chunk
|
||||
vec2 chunk_off = mod(gl_FragCoord.xy - grid_off, chunk_size);
|
||||
// the chunk coordinate
|
||||
vec2 chunk = tex_coord - vec2(chunk_off) / resolution;
|
||||
// coordinate of chunk center
|
||||
vec2 chunk_center = chunk + vec2(chunk_size/2.0, chunk_size/2.0) / resolution;
|
||||
frag_color = vec4(get_pixel(chunk_center), 1.0);
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex; // the new scene
|
||||
uniform sampler2D old; // the old scene
|
||||
uniform vec2 resolution; // the screen resolution
|
||||
uniform float progress; // effect progress (0..1)
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
float rand = fract(sin(dot(vec3(tex_coord, progress), vec3(12.9898, 78.233, 81.2463))) * 43758.5453);
|
||||
|
||||
vec3 col;
|
||||
if (progress < 0.5) {
|
||||
col = mix(texture(old, tex_coord).rgb, vec3(rand), progress * 2.0);
|
||||
} else {
|
||||
col = mix(texture(tex, tex_coord).rgb, vec3(rand), (1.0 - progress) * 2.0);
|
||||
}
|
||||
frag_color = vec4(col, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex; // the new scene
|
||||
uniform sampler2D old; // the old scene
|
||||
uniform vec2 resolution; // the screen resolution
|
||||
uniform float progress; // effect progress (0..1)
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
float rand = fract(sin(dot(vec3(tex_coord, progress), vec3(12.9898, 78.233, 81.2463))) * 43758.5453);
|
||||
vec3 sepia = rand * vec3(1.0, 0.888, 0.691);
|
||||
|
||||
vec3 col;
|
||||
if (progress < 0.5) {
|
||||
col = mix(texture(old, tex_coord).rgb, sepia, progress * 2.0);
|
||||
} else {
|
||||
col = mix(texture(tex, tex_coord).rgb, sepia, (1.0 - progress) * 2.0);
|
||||
}
|
||||
frag_color = vec4(col, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex; // the new scene
|
||||
uniform sampler2D old; // the old scene
|
||||
uniform vec2 resolution; // the screen resolution
|
||||
uniform float progress; // effect progress (0..1)
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
/*
|
||||
|
||||
+---------------------+
|
||||
| . |
|
||||
| . |
|
||||
| . |
|
||||
| . |
|
||||
| . |
|
||||
| . |
|
||||
| p2 +-------+ p4
|
||||
| | . ,'|
|
||||
| | ,' |
|
||||
| | ,' . |
|
||||
+-------------+'------+
|
||||
p3 p1
|
||||
|
||||
The triangle between p2-p3-p4 contains the old scene mirrored.
|
||||
The triangle between p1-p3-p4 contains the new scene.
|
||||
The rest contains the old scene.
|
||||
|
||||
p2 moves towards the top-left along the line between p1 and the origin.
|
||||
|
||||
*/
|
||||
|
||||
void main() {
|
||||
vec2 p = tex_coord * resolution;
|
||||
|
||||
vec2 p1 = resolution;
|
||||
vec2 p2 = vec2((1.0 - max(progress, 0.01)*2.0) * p1.x, (1.0 - max(progress, 0.01)*2.0) * p1.y);
|
||||
vec2 p3 = vec2(p2.x, resolution.y);
|
||||
vec2 p4 = vec2(resolution.x, p2.y);
|
||||
|
||||
// 1.0 if p is below the line between p3 and p4, else 0.0
|
||||
float b_below_line = step(0.0, (p4.x - p3.x)*(p.y - p3.y) - (p4.y - p3.y)*(p.x - p3.x));
|
||||
|
||||
// bool b_old = p.x < p2.x || p.y < p2.y;
|
||||
float b_old = step(0.5, step(p.x, p2.x) + step(p.y, p2.y));
|
||||
// bool b_mirror = !b_old && !b_below_line;
|
||||
float b_mirror = abs(1.0 - b_old) * abs(1.0 - b_below_line);
|
||||
// bool b_new = b_below_line;
|
||||
float b_new = b_below_line;
|
||||
|
||||
vec2 p_diff = p - p2;
|
||||
vec3 c_old = texture(old, tex_coord).rgb;
|
||||
vec3 c_mirror = texture(old, vec2(p1.x - p_diff.x, p1.y - p_diff.y)/resolution).rgb;
|
||||
vec3 c_new = texture(tex, tex_coord).rgb;
|
||||
|
||||
vec3 col = c_old * b_old + c_mirror * b_mirror + c_new * b_new;
|
||||
frag_color = vec4(col, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex; // the new scene
|
||||
uniform sampler2D old; // the old scene
|
||||
uniform vec2 resolution; // the screen resolution
|
||||
uniform float progress; // effect progress (0..1)
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
#define M_PI 3.1415926535897932384626433832795
|
||||
#define OLD_PERIOD ((2.0 * M_PI) / 360.0)
|
||||
#define NEW_PERIOD ((2.0 * M_PI) / 720.0)
|
||||
#define OLD_SHIFT (-M_PI)
|
||||
#define NEW_SHIFT (M_PI / 2.0)
|
||||
|
||||
vec3 get_pixel(vec2 old_xy, vec2 new_xy) {
|
||||
// influence of old color is described by: C_old = (1-t)^2
|
||||
// influence of new color increases linearly: C_new = t
|
||||
// the resulting blend is not normalized
|
||||
vec3 old = texture(old, old_xy).rgb;
|
||||
vec3 new = texture(tex, new_xy).rgb;
|
||||
float old_weight = (1.0 - progress) * (1.0 - progress);
|
||||
float new_weight = progress;
|
||||
return (old * old_weight + new * new_weight);
|
||||
}
|
||||
|
||||
float triangle(float f) {
|
||||
return 1.0 - abs(f * 2.0 - 1.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// old: x = amp * sin((2pi/360)*y + phase + -pi)
|
||||
// new: x = amp * sin((2pi/720)*y + phase + pi/2)
|
||||
vec2 p = tex_coord * resolution;
|
||||
float amp = triangle(progress) * 80.0; // (0..0.5..1) -> (0..80..0);
|
||||
float phase = progress * M_PI * 8.0;
|
||||
float old_off = amp * sin(OLD_PERIOD * p.y + OLD_SHIFT + phase);
|
||||
float new_off = amp * sin(NEW_PERIOD * p.y + NEW_SHIFT + phase);
|
||||
vec2 old_xy = fract(vec2(p.x + old_off, p.y) / resolution);
|
||||
vec2 new_xy = fract(vec2(p.x - new_off, p.y) / resolution);
|
||||
frag_color = vec4(get_pixel(old_xy, new_xy), 1.0);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex; // the new scene
|
||||
uniform sampler2D old; // the old scene
|
||||
uniform vec2 resolution; // the screen resolution
|
||||
uniform float progress; // effect progress (0..1)
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
#define M_PI 3.1415926535897932384626433832795
|
||||
#define OLD_PERIOD ((2.0 * M_PI) / 360.0)
|
||||
#define NEW_PERIOD ((2.0 * M_PI) / 720.0)
|
||||
#define OLD_SHIFT (-M_PI)
|
||||
#define NEW_SHIFT (M_PI / 2.0)
|
||||
|
||||
vec3 get_pixel(vec2 old_xy, vec2 new_xy) {
|
||||
// influence of old color is described by: C_old = (1-t)^2
|
||||
// influence of new color increases linearly: C_new = t
|
||||
// the resulting blend is not normalized
|
||||
vec3 old = texture(old, old_xy).rgb;
|
||||
vec3 new = texture(tex, new_xy).rgb;
|
||||
float old_weight = (1.0 - progress) * (1.0 - progress);
|
||||
float new_weight = progress;
|
||||
return (old * old_weight + new * new_weight);
|
||||
}
|
||||
|
||||
float triangle(float f) {
|
||||
return 1.0 - abs(f * 2.0 - 1.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// old: x = amp * sin((2pi/360)*y + phase + -pi)
|
||||
// new: x = amp * sin((2pi/720)*y + phase + pi/2)
|
||||
vec2 p = tex_coord * resolution;
|
||||
float amp = triangle(progress) * 160.0; // (0..0.5..1) -> (0..160..0);
|
||||
float phase = progress * M_PI * 8.0;
|
||||
float old_off_x = amp * sin(OLD_PERIOD * p.y + OLD_SHIFT + phase);
|
||||
float new_off_x = amp * sin(NEW_PERIOD * p.y + NEW_SHIFT + phase);
|
||||
float off_y = resolution.y * progress;
|
||||
vec2 old_xy = fract(vec2(p.x + old_off_x, p.y + off_y) / resolution);
|
||||
vec2 new_xy = fract(vec2(p.x - new_off_x, p.y + off_y) / resolution);
|
||||
frag_color = vec4(get_pixel(old_xy, new_xy), 1.0);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float threshold;
|
||||
uniform float threshold2;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
float rate = threshold + (threshold2 - threshold) * tex_coord.y;
|
||||
frag_color = vec4(texture(tex, tex_coord).rgb, rate);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform vec2 center;
|
||||
uniform float start_angle;
|
||||
uniform float angle;
|
||||
uniform vec4 color;
|
||||
|
||||
const float PI = 3.14159265358979323846;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
vec2 p = tex_coord - center;
|
||||
float a = atan(p.y, p.x);
|
||||
a = mod(a - start_angle, 2.0 * PI);
|
||||
if (a > angle)
|
||||
discard;
|
||||
frag_color = color;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/* Copyright (C) 2023 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D texture_y;
|
||||
uniform sampler2D texture_cb;
|
||||
uniform sampler2D texture_cr;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
mat4 rec601 = mat4(
|
||||
1.16438, 0.00000, 1.59603, -0.87079,
|
||||
1.16438, -0.39176, -0.81297, 0.52959,
|
||||
1.16438, 2.01723, 0.00000, -1.08139,
|
||||
0.0, 0.0, 0.0, 1.0
|
||||
);
|
||||
|
||||
void main() {
|
||||
float y = texture(texture_y, tex_coord).r;
|
||||
float cb = texture(texture_cb, tex_coord).r;
|
||||
float cr = texture(texture_cr, tex_coord).r;
|
||||
|
||||
frag_color = vec4(y, cb, cr, 1.0) * rec601;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float blend_rate;
|
||||
uniform vec2 bot_left;
|
||||
uniform vec2 top_right;
|
||||
uniform vec3 add_color;
|
||||
uniform vec3 multiply_color;
|
||||
uniform int draw_filter;
|
||||
|
||||
uniform int use_clipper;
|
||||
uniform sampler2D clipper_tex;
|
||||
|
||||
in vec2 tex_coord;
|
||||
in vec2 clip_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
const int DRAW_FILTER_MULTIPLY = 2;
|
||||
const int DRAW_FILTER_SCREEN = 3;
|
||||
|
||||
bool inside_rect(vec2 p, vec2 bl, vec2 tr) {
|
||||
return all(greaterThanEqual(p, bl)) && all(lessThan(p, tr));
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 size = vec2(textureSize(tex, 0));
|
||||
if (!inside_rect(tex_coord, bot_left / size, top_right / size))
|
||||
discard;
|
||||
|
||||
vec4 tex_color = texture(tex, tex_coord);
|
||||
vec3 mod_color = (tex_color.rgb + add_color) * multiply_color;
|
||||
float alpha = tex_color.a * blend_rate;
|
||||
|
||||
if (use_clipper != 0) {
|
||||
if (!inside_rect(clip_coord, vec2(0.0), vec2(1.0)))
|
||||
discard;
|
||||
alpha *= texture(clipper_tex, clip_coord).a;
|
||||
}
|
||||
|
||||
if (draw_filter == DRAW_FILTER_MULTIPLY) {
|
||||
// result = mix(dst, mod_color*dst, alpha), used with (GL_DST_COLOR, GL_ZERO)
|
||||
frag_color = vec4(mix(vec3(1.0), mod_color, alpha), 1.0);
|
||||
} else if (draw_filter == DRAW_FILTER_SCREEN) {
|
||||
// result = mod_color*alpha + (1 - mod_color*alpha)*dst,
|
||||
// used with (GL_ONE, GL_ONE_MINUS_SRC_COLOR)
|
||||
frag_color = vec4(mod_color * alpha, 1.0);
|
||||
} else {
|
||||
// normal or additive — blend func handles alpha
|
||||
frag_color = vec4(mod_color, alpha);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform mat4 world_transform;
|
||||
uniform mat4 view_transform;
|
||||
uniform mat4 inv_clipper_transform;
|
||||
|
||||
in vec4 vertex_pos;
|
||||
in vec2 vertex_uv;
|
||||
out vec2 tex_coord;
|
||||
out vec2 clip_coord;
|
||||
|
||||
void main() {
|
||||
vec4 world_pos = world_transform * vertex_pos;
|
||||
gl_Position = view_transform * world_pos;
|
||||
tex_coord = vertex_uv;
|
||||
clip_coord = (inv_clipper_transform * world_pos).xy;
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define DIFFUSE_EMISSIVE 0
|
||||
#define DIFFUSE_NORMAL 1
|
||||
#define DIFFUSE_LIGHT_MAP 2
|
||||
#define DIFFUSE_ENV_MAP 3
|
||||
|
||||
#define ALPHA_BLEND 0
|
||||
#define ALPHA_TEST 1
|
||||
#define ALPHA_MAP_BLEND 2
|
||||
#define ALPHA_MAP_TEST 3
|
||||
|
||||
#define FOG_LINEAR 1
|
||||
#define FOG_LIGHT_SCATTERING 2
|
||||
|
||||
#define ENABLE_SPECULAR (ENGINE == REIGN_ENGINE || ENGINE == SEAL_ENGINE)
|
||||
#define ENABLE_LIGHT_SCATTERING (ENGINE == REIGN_ENGINE || ENGINE == SEAL_ENGINE)
|
||||
|
||||
#if ENGINE == REIGN_ENGINE
|
||||
|
||||
#define NR_DIR_LIGHTS 3
|
||||
|
||||
struct dir_light {
|
||||
vec3 dir;
|
||||
vec3 diffuse;
|
||||
vec3 globe_diffuse;
|
||||
};
|
||||
|
||||
uniform dir_light dir_lights[NR_DIR_LIGHTS];
|
||||
uniform float rim_exponent;
|
||||
uniform vec3 rim_color;
|
||||
uniform float fog_near;
|
||||
uniform float fog_far;
|
||||
uniform vec3 fog_color;
|
||||
in vec3 light_dir[NR_DIR_LIGHTS];
|
||||
|
||||
uniform vec3 global_ambient;
|
||||
|
||||
vec3 compute_diffuse(vec3 norm)
|
||||
{
|
||||
vec3 diffuse = vec3(0.0);
|
||||
for (int i = 0; i < NR_DIR_LIGHTS; i++) {
|
||||
float half_lambert = dot(norm, -light_dir[i]) * 0.5 + 0.5;
|
||||
diffuse += mix(dir_lights[i].globe_diffuse, dir_lights[i].diffuse, half_lambert);
|
||||
}
|
||||
return diffuse;
|
||||
}
|
||||
|
||||
#elif ENGINE == SEAL_ENGINE
|
||||
|
||||
const vec3 global_ambient = vec3(0.0);
|
||||
|
||||
// Hemisphere lighting (sky/middle/ground).
|
||||
uniform vec3 hemi_light_dir;
|
||||
uniform vec3 hemi_sky_color;
|
||||
uniform vec3 hemi_mid_color;
|
||||
uniform vec3 hemi_ground_color;
|
||||
|
||||
vec3 compute_diffuse(vec3 norm)
|
||||
{
|
||||
float t = 0.5 * dot(norm, hemi_light_dir) + 0.5;
|
||||
vec3 low_mid = mix(hemi_ground_color, hemi_mid_color, t);
|
||||
vec3 mid_high = mix(hemi_mid_color, hemi_sky_color, t);
|
||||
return mix(low_mid, mid_high, t);
|
||||
}
|
||||
|
||||
#else // TAPIR_ENGINE
|
||||
|
||||
const vec3 global_ambient = vec3(0.0);
|
||||
|
||||
vec3 compute_diffuse(vec3 norm)
|
||||
{
|
||||
return vec3(1.0);
|
||||
}
|
||||
|
||||
#endif // ENGINE == REIGN_ENGINE
|
||||
|
||||
#if ENABLE_LIGHT_SCATTERING
|
||||
uniform int fog_type;
|
||||
in vec3 ls_ex;
|
||||
in vec3 ls_in;
|
||||
#endif
|
||||
|
||||
#if ENABLE_SPECULAR
|
||||
uniform sampler2D specular_texture;
|
||||
uniform vec3 specular_color;
|
||||
uniform float specular_shininess;
|
||||
uniform bool use_specular_map;
|
||||
in vec3 specular_dir;
|
||||
#endif
|
||||
|
||||
#if ENGINE == SEAL_ENGINE
|
||||
// Uncharted 2 (Hable) filmic tone mapping.
|
||||
uniform vec4 tonemap_param; // (ExposureBias, WhitePoint, A, B)
|
||||
uniform vec4 tonemap_param2; // (C, D, E, F)
|
||||
uniform bool nolighting;
|
||||
uniform float grayscale_rate;
|
||||
|
||||
vec3 hable(vec3 v)
|
||||
{
|
||||
float a = tonemap_param.z, b = tonemap_param.w;
|
||||
float c = tonemap_param2.x, d = tonemap_param2.y;
|
||||
float e = tonemap_param2.z, f = tonemap_param2.w;
|
||||
return ((v * (a * v + c * b) + d * e) / (v * (a * v + b) + d * f)) - e / f;
|
||||
}
|
||||
|
||||
vec3 tone_map(vec3 color)
|
||||
{
|
||||
vec3 curr = hable(color * tonemap_param.x);
|
||||
vec3 white = hable(vec3(tonemap_param.y));
|
||||
return curr / white;
|
||||
}
|
||||
#endif
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform sampler2D blend_tex;
|
||||
uniform bool use_blend_texture;
|
||||
uniform sampler2D alpha_texture;
|
||||
uniform sampler2D light_texture;
|
||||
uniform sampler2D shadow_texture;
|
||||
uniform float alpha_mod;
|
||||
|
||||
uniform bool use_normal_map;
|
||||
uniform sampler2D normal_texture;
|
||||
|
||||
uniform vec3 instance_ambient;
|
||||
uniform vec3 diffuse_mod;
|
||||
uniform int diffuse_type;
|
||||
uniform float shadow_darkness;
|
||||
uniform float shadow_bias;
|
||||
uniform int alpha_mode;
|
||||
|
||||
in vec2 tex_coord;
|
||||
in vec2 light_tex_coord;
|
||||
in vec4 color_mod;
|
||||
in vec3 frag_pos;
|
||||
in vec4 shadow_frag_pos;
|
||||
in float dist;
|
||||
in vec3 eye;
|
||||
in vec3 normal;
|
||||
in float blend_weight;
|
||||
in vec2 blend_tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
vec3 norm;
|
||||
if (use_normal_map) {
|
||||
// NOTE: TT3's shader doesn't normalize this vector.
|
||||
norm = texture(normal_texture, tex_coord).rgb * 2.0 - 1.0;
|
||||
} else {
|
||||
norm = normalize(normal);
|
||||
}
|
||||
|
||||
vec3 view_dir = normalize(eye - frag_pos);
|
||||
vec3 frag_rgb = global_ambient + instance_ambient;
|
||||
|
||||
// Diffuse lighting
|
||||
vec4 texel;
|
||||
vec3 diffuse = vec3(1.0);
|
||||
if (diffuse_type == DIFFUSE_ENV_MAP) {
|
||||
texel = texture(tex, (vec2(normal.x, -normal.y) + 1.0) / 2.0);
|
||||
} else {
|
||||
texel = texture(tex, tex_coord);
|
||||
if (use_blend_texture) {
|
||||
texel = mix(texel, texture(blend_tex, blend_tex_coord), blend_weight);
|
||||
}
|
||||
if (diffuse_type != DIFFUSE_EMISSIVE) {
|
||||
diffuse = compute_diffuse(norm);
|
||||
if (diffuse_type == DIFFUSE_LIGHT_MAP) {
|
||||
diffuse *= texture(light_texture, light_tex_coord).rgb;
|
||||
}
|
||||
}
|
||||
}
|
||||
frag_rgb += texel.rgb * diffuse * color_mod.rgb * diffuse_mod;
|
||||
|
||||
#if ENABLE_SPECULAR
|
||||
// Specular lighting
|
||||
if (any(greaterThan(specular_color, vec3(0.0)))) {
|
||||
vec3 reflect_dir = reflect(specular_dir, norm);
|
||||
float specular = pow(max(dot(view_dir, reflect_dir), 0.0), specular_shininess);
|
||||
if (use_specular_map) {
|
||||
specular *= texture(specular_texture, tex_coord).r;
|
||||
}
|
||||
frag_rgb += specular_color * specular;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENGINE == REIGN_ENGINE
|
||||
// Rim lighting
|
||||
if (rim_exponent > 0.0) {
|
||||
// Normal map is not used here.
|
||||
vec3 n = use_normal_map ? vec3(0.0, 0.0, 1.0) : normalize(normal);
|
||||
frag_rgb += pow(1.0 - max(dot(n, view_dir), 0.0), rim_exponent) * rim_color;
|
||||
}
|
||||
|
||||
// Fog
|
||||
if (fog_type == FOG_LINEAR) {
|
||||
float fog_factor = clamp((dist - fog_near) / (fog_far - fog_near), 0.0, 1.0);
|
||||
frag_rgb = mix(frag_rgb, fog_color, fog_factor);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_LIGHT_SCATTERING
|
||||
if (fog_type == FOG_LIGHT_SCATTERING) {
|
||||
frag_rgb = frag_rgb * ls_ex + ls_in;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Shadow mapping
|
||||
if (shadow_darkness > 0.0) {
|
||||
vec3 shadow_coords = shadow_frag_pos.xyz / shadow_frag_pos.w * 0.5 + 0.5;
|
||||
shadow_coords.z = min(1.0, shadow_coords.z - shadow_bias);
|
||||
// PCF with 9 samples.
|
||||
float brightness = 1.0;
|
||||
vec2 texel_size = 1.0 / vec2(textureSize(shadow_texture, 0));
|
||||
for (int x = -1; x <= 1; ++x) {
|
||||
for (int y = -1; y <= 1; ++y) {
|
||||
float depth = texture(shadow_texture, shadow_coords.xy + vec2(x, y) * texel_size).r;
|
||||
if (depth < shadow_coords.z)
|
||||
brightness -= 0.05 * shadow_darkness;
|
||||
}
|
||||
}
|
||||
frag_rgb *= brightness;
|
||||
}
|
||||
|
||||
#if ENGINE == SEAL_ENGINE
|
||||
if (!nolighting)
|
||||
frag_rgb = tone_map(frag_rgb);
|
||||
|
||||
if (grayscale_rate != 0.0)
|
||||
frag_rgb = mix(frag_rgb, vec3(dot(frag_rgb, vec3(0.299, 0.587, 0.114))), grayscale_rate);
|
||||
#endif
|
||||
|
||||
// Alpha mapping
|
||||
float alpha = texel.a * color_mod.a * alpha_mod;
|
||||
if (alpha_mode == ALPHA_TEST) {
|
||||
if (alpha < 0.75)
|
||||
discard;
|
||||
} else if (alpha_mode == ALPHA_MAP_BLEND) {
|
||||
alpha *= texture(alpha_texture, tex_coord).r;
|
||||
} else if (alpha_mode == ALPHA_MAP_TEST) {
|
||||
if (texture(alpha_texture, tex_coord).r < 0.1)
|
||||
discard;
|
||||
}
|
||||
|
||||
frag_color = vec4(frag_rgb, alpha);
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const float PI = 3.14159265358979323846;
|
||||
|
||||
#define FOG_LIGHT_SCATTERING 2
|
||||
|
||||
#define ENABLE_SPECULAR (ENGINE == REIGN_ENGINE || ENGINE == SEAL_ENGINE)
|
||||
#define ENABLE_LIGHT_SCATTERING (ENGINE == REIGN_ENGINE || ENGINE == SEAL_ENGINE)
|
||||
|
||||
#if ENGINE == REIGN_ENGINE
|
||||
|
||||
#define NR_DIR_LIGHTS 3
|
||||
|
||||
struct dir_light {
|
||||
vec3 dir;
|
||||
vec3 diffuse;
|
||||
vec3 globe_diffuse;
|
||||
};
|
||||
|
||||
uniform dir_light dir_lights[NR_DIR_LIGHTS];
|
||||
out vec3 light_dir[NR_DIR_LIGHTS];
|
||||
|
||||
const vec2 uv_scroll = vec2(0.0);
|
||||
|
||||
#else // ENGINE == REIGN_ENGINE
|
||||
|
||||
uniform vec2 uv_scroll;
|
||||
|
||||
#endif // ENGINE == REIGN_ENGINE
|
||||
|
||||
uniform vec4 uv_tiling;
|
||||
|
||||
#if ENABLE_LIGHT_SCATTERING
|
||||
uniform int fog_type;
|
||||
uniform vec4 ls_params; // (beta_r, beta_m, g, distance)
|
||||
uniform vec3 ls_light_dir;
|
||||
uniform vec3 ls_light_color;
|
||||
uniform vec3 ls_sun_color;
|
||||
out vec3 ls_ex;
|
||||
out vec3 ls_in;
|
||||
#endif
|
||||
|
||||
#if ENABLE_SPECULAR
|
||||
uniform vec3 specular_light_dir;
|
||||
out vec3 specular_dir;
|
||||
#endif
|
||||
|
||||
uniform mat4 local_transform;
|
||||
uniform mat4 view_transform;
|
||||
uniform mat4 proj_transform;
|
||||
uniform mat3 normal_transform;
|
||||
|
||||
const int MAX_BONES = 308; // see 3d_internal.h
|
||||
const int NR_WEIGHTS = 4;
|
||||
uniform bool has_bones;
|
||||
layout(std140) uniform BoneTransforms {
|
||||
mat3x4 bone_matrices[MAX_BONES]; // transposed
|
||||
};
|
||||
|
||||
uniform bool use_normal_map;
|
||||
|
||||
uniform vec3 camera_pos;
|
||||
uniform mat4 shadow_transform;
|
||||
|
||||
in vec3 vertex_pos;
|
||||
in vec3 vertex_normal;
|
||||
in vec2 vertex_uv;
|
||||
in vec2 vertex_light_uv;
|
||||
in vec4 vertex_color;
|
||||
in vec4 vertex_tangent;
|
||||
in ivec4 vertex_bone_index;
|
||||
in vec4 vertex_bone_weight;
|
||||
in float vertex_blend_weight;
|
||||
in vec2 vertex_blend_uv;
|
||||
|
||||
out vec2 tex_coord;
|
||||
out vec2 light_tex_coord;
|
||||
out vec4 color_mod;
|
||||
out vec3 frag_pos;
|
||||
out vec4 shadow_frag_pos;
|
||||
out float dist;
|
||||
out vec3 eye;
|
||||
out vec3 normal;
|
||||
out float blend_weight;
|
||||
out vec2 blend_tex_coord;
|
||||
|
||||
void main() {
|
||||
mat4 local_bone_transform = local_transform;
|
||||
mat3 normal_bone_transform = normal_transform;
|
||||
if (has_bones) {
|
||||
mat3x4 bone_transform = mat3x4(0.0);
|
||||
for (int i = 0; i < NR_WEIGHTS; i++) {
|
||||
if (vertex_bone_index[i] >= 0) {
|
||||
bone_transform += bone_matrices[vertex_bone_index[i]] * vertex_bone_weight[i];
|
||||
}
|
||||
}
|
||||
local_bone_transform *= mat4(
|
||||
vec4(bone_transform[0].x, bone_transform[1].x, bone_transform[2].x, 0.0),
|
||||
vec4(bone_transform[0].y, bone_transform[1].y, bone_transform[2].y, 0.0),
|
||||
vec4(bone_transform[0].z, bone_transform[1].z, bone_transform[2].z, 0.0),
|
||||
vec4(bone_transform[0].w, bone_transform[1].w, bone_transform[2].w, 1.0));
|
||||
normal_bone_transform *= mat3(bone_transform);
|
||||
}
|
||||
|
||||
// World-space normal vector.
|
||||
normal = normalize(normal_bone_transform * vertex_normal);
|
||||
|
||||
mat3 TBN = mat3(1.0f);
|
||||
if (use_normal_map) {
|
||||
vec3 tangent = normalize(normal_bone_transform * vertex_tangent.xyz);
|
||||
vec3 bitangent = cross(normal, tangent) * vertex_tangent.w;
|
||||
TBN = transpose(mat3(tangent, bitangent, normal));
|
||||
}
|
||||
|
||||
vec4 world_pos = local_bone_transform * vec4(vertex_pos, 1.0);
|
||||
vec4 view_pos = view_transform * world_pos;
|
||||
gl_Position = proj_transform * view_pos;
|
||||
|
||||
tex_coord = (vertex_uv + uv_scroll) * uv_tiling.xy;
|
||||
light_tex_coord = vertex_light_uv + uv_scroll;
|
||||
color_mod = vertex_color;
|
||||
blend_weight = vertex_blend_weight;
|
||||
blend_tex_coord = (vertex_blend_uv + uv_scroll) * uv_tiling.zw;
|
||||
dist = -view_pos.z;
|
||||
shadow_frag_pos = shadow_transform * world_pos;
|
||||
|
||||
// These are in tangent-space if use_normal_map is true, in world-space
|
||||
// otherwise.
|
||||
frag_pos = TBN * vec3(world_pos);
|
||||
eye = TBN * camera_pos;
|
||||
|
||||
#if ENABLE_SPECULAR
|
||||
specular_dir = TBN * specular_light_dir;
|
||||
#endif
|
||||
#if ENGINE == REIGN_ENGINE
|
||||
light_dir[0] = TBN * dir_lights[0].dir;
|
||||
light_dir[1] = TBN * dir_lights[1].dir;
|
||||
light_dir[2] = TBN * dir_lights[2].dir;
|
||||
#endif
|
||||
#if ENABLE_LIGHT_SCATTERING
|
||||
if (fog_type == FOG_LIGHT_SCATTERING) {
|
||||
float beta_r = ls_params.x;
|
||||
float beta_m = ls_params.y;
|
||||
float g = ls_params.z;
|
||||
float distance = dist / ls_params.w;
|
||||
|
||||
vec3 view_dir = normalize(camera_pos - vec3(world_pos));
|
||||
float cos_theta = dot(view_dir, normalize(ls_light_dir));
|
||||
// Note: `* PI` in the two assignments below should be `/ PI` (see the
|
||||
// definitions of Rayleigh / Mie phase functions in [1]), but this is
|
||||
// how TT3's shader works.
|
||||
// [1] http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2012/10/ATI-LightScattering.pdf
|
||||
float phase_r = 3.0 / 16.0 * PI * (1.0 + cos_theta * cos_theta);
|
||||
float phase_m = 1.0 / 4.0 * PI * (1.0 - g) * (1.0 - g) / pow(1.0 + g * g - 2.0 * g * cos_theta, 1.5);
|
||||
float f_ex = exp((beta_r + beta_m) * -distance);
|
||||
ls_in = (phase_r * beta_r + phase_m * beta_m) / (beta_r + beta_m) * (1.0 - f_ex) * ls_sun_color;
|
||||
ls_ex = ls_light_color * f_ex;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/* Copyright (C) 2024 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform vec3 outline_color;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
frag_color = vec4(outline_color, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/* Copyright (C) 2024 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform mat4 local_transform;
|
||||
uniform mat4 view_transform;
|
||||
uniform mat4 proj_transform;
|
||||
uniform mat3 normal_transform;
|
||||
|
||||
const int MAX_BONES = 308; // see 3d_internal.h
|
||||
const int NR_WEIGHTS = 4;
|
||||
uniform bool has_bones;
|
||||
layout(std140) uniform BoneTransforms {
|
||||
mat3x4 bone_matrices[MAX_BONES]; // transposed
|
||||
};
|
||||
uniform float outline_thickness;
|
||||
|
||||
in vec3 vertex_pos;
|
||||
in vec3 vertex_normal;
|
||||
in ivec4 vertex_bone_index;
|
||||
in vec4 vertex_bone_weight;
|
||||
|
||||
void main() {
|
||||
mat4 local_bone_transform = local_transform;
|
||||
mat3 normal_bone_transform = normal_transform;
|
||||
if (has_bones) {
|
||||
mat3x4 bone_transform = mat3x4(0.0);
|
||||
for (int i = 0; i < NR_WEIGHTS; i++) {
|
||||
if (vertex_bone_index[i] >= 0) {
|
||||
bone_transform += bone_matrices[vertex_bone_index[i]] * vertex_bone_weight[i];
|
||||
}
|
||||
}
|
||||
local_bone_transform *= mat4(
|
||||
vec4(bone_transform[0].x, bone_transform[1].x, bone_transform[2].x, 0.0),
|
||||
vec4(bone_transform[0].y, bone_transform[1].y, bone_transform[2].y, 0.0),
|
||||
vec4(bone_transform[0].z, bone_transform[1].z, bone_transform[2].z, 0.0),
|
||||
vec4(bone_transform[0].w, bone_transform[1].w, bone_transform[2].w, 1.0));
|
||||
normal_bone_transform *= mat3(bone_transform);
|
||||
}
|
||||
|
||||
vec4 pos = local_bone_transform * vec4(vertex_pos, 1.0);
|
||||
vec3 normal = normalize(normal_bone_transform * vertex_normal);
|
||||
|
||||
gl_Position = proj_transform * view_transform * (pos + vec4(normal, 0.0) * outline_thickness);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
out uvec4 frag_color;
|
||||
|
||||
void main() {
|
||||
frag_color.r = uint(gl_FragCoord.z * 65535.0);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform mat4 local_transform;
|
||||
uniform mat4 view_transform;
|
||||
|
||||
const int MAX_BONES = 308; // see 3d_internal.h
|
||||
const int NR_WEIGHTS = 4;
|
||||
uniform bool has_bones;
|
||||
layout(std140) uniform BoneTransforms {
|
||||
mat3x4 bone_matrices[MAX_BONES]; // transposed
|
||||
};
|
||||
|
||||
in vec3 vertex_pos;
|
||||
in ivec4 vertex_bone_index;
|
||||
in vec4 vertex_bone_weight;
|
||||
|
||||
void main() {
|
||||
mat4 local_bone_transform = local_transform;
|
||||
if (has_bones) {
|
||||
mat3x4 bone_transform = mat3x4(0.0);
|
||||
for (int i = 0; i < NR_WEIGHTS; i++) {
|
||||
if (vertex_bone_index[i] >= 0) {
|
||||
bone_transform += bone_matrices[vertex_bone_index[i]] * vertex_bone_weight[i];
|
||||
}
|
||||
}
|
||||
local_bone_transform *= mat4(
|
||||
vec4(bone_transform[0].x, bone_transform[1].x, bone_transform[2].x, 0.0),
|
||||
vec4(bone_transform[0].y, bone_transform[1].y, bone_transform[2].y, 0.0),
|
||||
vec4(bone_transform[0].z, bone_transform[1].z, bone_transform[2].z, 0.0),
|
||||
vec4(bone_transform[0].w, bone_transform[1].w, bone_transform[2].w, 1.0));
|
||||
}
|
||||
|
||||
gl_Position = view_transform * local_bone_transform * vec4(vertex_pos, 1.0);
|
||||
}
|
||||
@@ -15,12 +15,10 @@
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float alpha_mod;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
frag_color = texture(tex, tex_coord);
|
||||
frag_color.a *= alpha_mod;
|
||||
}
|
||||
|
||||
@@ -18,9 +18,10 @@ uniform mat4 world_transform;
|
||||
uniform mat4 view_transform;
|
||||
|
||||
in vec4 vertex_pos;
|
||||
in vec2 vertex_uv;
|
||||
out vec2 tex_coord;
|
||||
|
||||
void main() {
|
||||
gl_Position = view_transform * world_transform * vertex_pos;
|
||||
tex_coord = vertex_pos.xy;
|
||||
tex_coord = vertex_uv;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/* Copyright (C) 2023 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float blend_rate;
|
||||
uniform vec3 multiply_color;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
frag_color = texture(tex, tex_coord);
|
||||
frag_color.rgb *= multiply_color;
|
||||
frag_color.a *= blend_rate;
|
||||
}
|
||||
@@ -0,0 +1,877 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM4_3D_3D_INTERNAL_H
|
||||
#define SYSTEM4_3D_3D_INTERNAL_H
|
||||
|
||||
#include <cglm/types.h>
|
||||
|
||||
#include "system4/archive.h"
|
||||
|
||||
#include "gfx/gl.h"
|
||||
#include "gfx/gfx.h"
|
||||
#include "reign.h"
|
||||
|
||||
#define NR_DIR_LIGHTS 3
|
||||
#define MAX_BONES 308 // Maximum in Rance Quest
|
||||
|
||||
typedef struct vec3_range {
|
||||
vec3 begin;
|
||||
vec3 end;
|
||||
} vec3_range;
|
||||
|
||||
struct model {
|
||||
char *path;
|
||||
int nr_meshes;
|
||||
struct mesh *meshes;
|
||||
int nr_materials;
|
||||
struct material *materials;
|
||||
int nr_bones;
|
||||
struct bone *bones; // ordered so that parents precede their children
|
||||
struct bone **bones_by_pol_index; // POL bone array index -> struct bone *
|
||||
struct hash_table *bone_map; // bone id in POL -> struct bone *
|
||||
struct hash_table *mot_cache; // name -> struct mot *
|
||||
struct collider *collider;
|
||||
vec3 aabb[2]; // axis-aligned bounding box
|
||||
bool has_transparent_mesh;
|
||||
};
|
||||
|
||||
struct mesh {
|
||||
char *name;
|
||||
uint32_t flags;
|
||||
bool is_transparent;
|
||||
bool hidden;
|
||||
GLuint vao;
|
||||
GLuint attr_buffer;
|
||||
GLuint index_buffer;
|
||||
int nr_vertices;
|
||||
int nr_indices;
|
||||
int material;
|
||||
vec3 outline_color;
|
||||
float outline_thickness;
|
||||
vec2 uv_scroll;
|
||||
vec3 specular_color;
|
||||
float specular_power;
|
||||
};
|
||||
|
||||
struct material {
|
||||
uint32_t flags;
|
||||
bool is_transparent;
|
||||
vec2 uv_tiling;
|
||||
vec2 blend_uv_tiling;
|
||||
int nr_color_maps;
|
||||
GLuint *color_maps;
|
||||
GLuint specular_map;
|
||||
GLuint alpha_map;
|
||||
GLuint light_map;
|
||||
GLuint normal_map;
|
||||
GLuint blend_texture;
|
||||
float specular_strength;
|
||||
float specular_shininess;
|
||||
float shadow_darkness;
|
||||
float rim_exponent;
|
||||
vec3 rim_color;
|
||||
};
|
||||
|
||||
struct bone {
|
||||
char *name;
|
||||
int index; // index on model->bones
|
||||
int parent; // index on model->bones, or -1
|
||||
mat4 inverse_bind_matrix;
|
||||
};
|
||||
|
||||
struct motion {
|
||||
struct RE_instance *instance;
|
||||
struct mot *mot;
|
||||
int state;
|
||||
float current_frame;
|
||||
float frame_begin, frame_end;
|
||||
float loop_frame_begin, loop_frame_end;
|
||||
int texture_index;
|
||||
};
|
||||
|
||||
// Attribute variable locations
|
||||
enum RE_attribute_location {
|
||||
VATTR_POS = 0,
|
||||
VATTR_NORMAL,
|
||||
VATTR_UV,
|
||||
VATTR_BONE_INDEX,
|
||||
VATTR_BONE_WEIGHT,
|
||||
VATTR_LIGHT_UV,
|
||||
VATTR_COLOR,
|
||||
VATTR_TANGENT,
|
||||
VATTR_BLEND_WEIGHT,
|
||||
VATTR_BLEND_UV,
|
||||
};
|
||||
|
||||
struct shadow_renderer {
|
||||
GLuint program;
|
||||
GLuint fbo;
|
||||
GLuint texture;
|
||||
|
||||
// Uniform variable locations
|
||||
GLint local_transform;
|
||||
GLint view_transform;
|
||||
GLint has_bones;
|
||||
};
|
||||
|
||||
struct outline_renderer {
|
||||
GLuint program;
|
||||
|
||||
// Uniform variable locations
|
||||
GLint local_transform;
|
||||
GLint view_transform;
|
||||
GLint proj_transform;
|
||||
GLint normal_transform;
|
||||
GLint has_bones;
|
||||
GLint outline_color;
|
||||
GLint outline_thickness;
|
||||
};
|
||||
|
||||
struct RE_renderer {
|
||||
int viewport_width;
|
||||
int viewport_height;
|
||||
GLuint program;
|
||||
GLuint depth_buffer;
|
||||
struct shadow_renderer shadow;
|
||||
struct outline_renderer outline;
|
||||
|
||||
// Uniform variable locations
|
||||
GLint view_transform;
|
||||
GLint texture;
|
||||
GLint local_transform;
|
||||
GLint proj_transform;
|
||||
GLint normal_transform;
|
||||
GLint alpha_mod;
|
||||
GLint has_bones;
|
||||
GLint global_ambient;
|
||||
GLint instance_ambient;
|
||||
GLint diffuse_mod;
|
||||
struct {
|
||||
GLint dir;
|
||||
GLint diffuse;
|
||||
GLint globe_diffuse;
|
||||
GLint ambient;
|
||||
} dir_lights[NR_DIR_LIGHTS];
|
||||
GLint specular_light_dir;
|
||||
GLint specular_color;
|
||||
GLint specular_shininess;
|
||||
GLint use_specular_map;
|
||||
GLint specular_texture;
|
||||
GLint rim_exponent;
|
||||
GLint rim_color;
|
||||
GLint camera_pos;
|
||||
GLint diffuse_type;
|
||||
GLint light_texture;
|
||||
GLint use_normal_map;
|
||||
GLint normal_texture;
|
||||
GLint shadow_darkness;
|
||||
GLint shadow_transform;
|
||||
GLint shadow_texture;
|
||||
GLint shadow_bias;
|
||||
GLint fog_type;
|
||||
GLint fog_near;
|
||||
GLint fog_far;
|
||||
GLint fog_color;
|
||||
GLint ls_params;
|
||||
GLint ls_light_dir;
|
||||
GLint ls_light_color;
|
||||
GLint ls_sun_color;
|
||||
GLint hemi_light_dir;
|
||||
GLint hemi_sky_color;
|
||||
GLint hemi_mid_color;
|
||||
GLint hemi_ground_color;
|
||||
GLint tonemap_param;
|
||||
GLint tonemap_param2;
|
||||
GLint nolighting;
|
||||
GLint grayscale_rate;
|
||||
GLint alpha_mode;
|
||||
GLint alpha_texture;
|
||||
GLint uv_scroll;
|
||||
GLint uv_tiling;
|
||||
GLint blend_tex;
|
||||
GLint use_blend_texture;
|
||||
|
||||
GLuint billboard_particle_vao;
|
||||
GLuint billboard_particle_attr_buffer;
|
||||
GLuint billboard_instance_vao;
|
||||
GLuint billboard_instance_attr_buffer;
|
||||
struct hash_table *billboard_textures_by_no; // cg_no -> struct billboard_texture*
|
||||
struct hash_table *billboard_textures_by_path; // path -> struct billboard_texture*
|
||||
|
||||
uint32_t last_frame_timestamp;
|
||||
};
|
||||
|
||||
struct billboard_texture {
|
||||
GLuint texture;
|
||||
bool has_alpha;
|
||||
};
|
||||
|
||||
// reign.c
|
||||
|
||||
extern enum RE_plugin_version re_plugin_version;
|
||||
|
||||
struct RE_instance *RE_create_instance_at(struct RE_plugin *plugin, int index);
|
||||
void RE_instance_update_local_transform(struct RE_instance *inst);
|
||||
|
||||
// model.c
|
||||
|
||||
struct model *model_load(struct archive *aar, const char *path);
|
||||
void model_free(struct model *model);
|
||||
struct model *model_create_sphere(int r, int g, int b, int a);
|
||||
|
||||
struct motion *motion_load(const char *name, struct RE_instance *instance, struct archive *aar);
|
||||
void motion_free(struct motion *motion);
|
||||
|
||||
struct archive_data *RE_get_aar_entry(struct archive *aar, const char *dir, const char *name, const char *ext);
|
||||
|
||||
// renderer.c
|
||||
|
||||
struct RE_renderer *RE_renderer_new(void);
|
||||
void RE_renderer_free(struct RE_renderer *r);
|
||||
void RE_renderer_set_viewport_size(struct RE_renderer *r, int width, int height);
|
||||
bool RE_renderer_load_billboard_texture_by_no(struct RE_renderer *r, int cg_no);
|
||||
struct billboard_texture *RE_renderer_load_billboard_texture_by_path(struct RE_renderer *r, struct archive *aar, const char *path);
|
||||
struct height_detector *RE_renderer_create_height_detector(struct RE_renderer *r, struct model *model);
|
||||
void RE_renderer_free_height_detector(struct height_detector *hd);
|
||||
bool RE_renderer_detect_height(struct height_detector *hd, float x, float z, float *y_out);
|
||||
void RE_calc_view_matrix(struct RE_camera *camera, vec3 up, mat4 out);
|
||||
|
||||
// particle.c
|
||||
|
||||
enum particle_type {
|
||||
PARTICLE_TYPE_BILLBOARD = 0,
|
||||
PARTICLE_TYPE_POLYGON_OBJECT = 1,
|
||||
PARTICLE_TYPE_SWORD_BLUR = 2,
|
||||
PARTICLE_TYPE_CAMERA_QUAKE = 3,
|
||||
};
|
||||
|
||||
enum particle_move_type {
|
||||
PARTICLE_MOVE_FIXED = 0,
|
||||
PARTICLE_MOVE_LINEAR = 1,
|
||||
PARTICLE_MOVE_EMITTER = 2,
|
||||
};
|
||||
|
||||
enum particle_up_vector_type {
|
||||
PARTICLE_UP_VECTOR_Y_AXIS = 0,
|
||||
PARTICLE_UP_VECTOR_OBJECT_MOVE = 1,
|
||||
PARTICLE_UP_VECTOR_INSTANCE_MOVE = 2,
|
||||
PARTICLE_UP_VECTOR_TYPE_MAX = PARTICLE_UP_VECTOR_INSTANCE_MOVE
|
||||
};
|
||||
|
||||
enum particle_blend_type {
|
||||
PARTICLE_BLEND_NORMAL = 0,
|
||||
PARTICLE_BLEND_ADDITIVE = 1,
|
||||
};
|
||||
|
||||
enum particle_frame_ref_type {
|
||||
PARTICLE_FRAME_REF_EFFECT = 0,
|
||||
PARTICLE_FRAME_REF_TARGET = 1,
|
||||
};
|
||||
|
||||
enum particle_child_move_dir_type {
|
||||
PARTICLE_CHILD_MOVE_DIR_NORMAL = 0,
|
||||
PARTICLE_CHILD_MOVE_DIR_REVERSE = 1,
|
||||
PARTICLE_CHILD_MOVE_DIR_TYPE_MAX = PARTICLE_CHILD_MOVE_DIR_REVERSE
|
||||
};
|
||||
|
||||
enum particle_dir_type {
|
||||
PARTICLE_DIR_TYPE_NORMAL = 0,
|
||||
PARTICLE_DIR_TYPE_XY_PLANE = 1,
|
||||
PARTICLE_DIR_TYPE_MAX = PARTICLE_DIR_TYPE_XY_PLANE
|
||||
};
|
||||
|
||||
struct particle_position_unit {
|
||||
enum {
|
||||
PARTICLE_POS_UNIT_NONE = 0,
|
||||
PARTICLE_POS_UNIT_TARGET = 1,
|
||||
PARTICLE_POS_UNIT_BONE = 2,
|
||||
PARTICLE_POS_UNIT_RAND = 4,
|
||||
PARTICLE_POS_UNIT_RAND_POSITIVE_Y = 5,
|
||||
PARTICLE_POS_UNIT_ABSOLUTE = 6,
|
||||
} type;
|
||||
union {
|
||||
struct {
|
||||
int index;
|
||||
vec3 offset;
|
||||
} target;
|
||||
struct {
|
||||
int target_index;
|
||||
char *name;
|
||||
vec3 offset;
|
||||
} bone;
|
||||
struct {
|
||||
float f;
|
||||
} rand;
|
||||
vec3 absolute;
|
||||
};
|
||||
};
|
||||
|
||||
#define NR_PARTICLE_POSITIONS 2
|
||||
#define NR_PARTICLE_POSITION_UNITS 3
|
||||
struct particle_position {
|
||||
struct particle_position_unit units[NR_PARTICLE_POSITION_UNITS];
|
||||
};
|
||||
|
||||
struct pae {
|
||||
char *path;
|
||||
struct hash_table *textures; // name -> struct billboard_texture*
|
||||
int nr_objects;
|
||||
struct pae_object *objects;
|
||||
};
|
||||
|
||||
struct pae_object {
|
||||
char *name;
|
||||
enum particle_type type;
|
||||
enum particle_move_type move_type;
|
||||
enum particle_up_vector_type up_vector_type;
|
||||
float move_curve;
|
||||
struct particle_position *position[NR_PARTICLE_POSITIONS];
|
||||
float size[2];
|
||||
int nr_sizes2;
|
||||
float *sizes2;
|
||||
int nr_sizes_x;
|
||||
float *sizes_x;
|
||||
int nr_sizes_y;
|
||||
float *sizes_y;
|
||||
int nr_size_types;
|
||||
int *size_types;
|
||||
int nr_size_x_types;
|
||||
int *size_x_types;
|
||||
int nr_size_y_types;
|
||||
int *size_y_types;
|
||||
int nr_textures;
|
||||
char **textures;
|
||||
enum particle_blend_type blend_type;
|
||||
float texture_anime_frame;
|
||||
int texture_anime_time;
|
||||
char *polygon_name;
|
||||
int begin_frame;
|
||||
int end_frame;
|
||||
int stop_frame;
|
||||
enum particle_frame_ref_type frame_ref_type;
|
||||
int frame_ref_param;
|
||||
int nr_particles;
|
||||
float alpha_fadein_frame;
|
||||
int alpha_fadein_time;
|
||||
float alpha_fadeout_frame;
|
||||
int alpha_fadeout_time;
|
||||
vec3_range rotation;
|
||||
vec3_range revolution_angle;
|
||||
vec3_range revolution_distance;
|
||||
vec3 curve_length;
|
||||
int child_frame;
|
||||
float child_length;
|
||||
float child_begin_slope;
|
||||
float child_end_slope;
|
||||
float child_create_begin_frame;
|
||||
float child_create_end_frame;
|
||||
enum particle_child_move_dir_type child_move_dir_type;
|
||||
enum particle_dir_type dir_type;
|
||||
int nr_damages;
|
||||
int *damages;
|
||||
float offset_x;
|
||||
float offset_y;
|
||||
|
||||
struct model *model;
|
||||
};
|
||||
|
||||
struct particle_effect {
|
||||
struct pae *pae;
|
||||
struct particle_object *objects; // pae->nr_objects elements
|
||||
bool camera_quake_enabled;
|
||||
};
|
||||
|
||||
struct particle_object {
|
||||
struct pae_object *pae_obj;
|
||||
struct particle_instance *instances; // pae_obj->nr_particles elements
|
||||
vec3_range pos;
|
||||
vec3 move_vec, move_ortho;
|
||||
};
|
||||
|
||||
struct particle_instance {
|
||||
float begin_frame;
|
||||
float end_frame;
|
||||
vec3_range random_offset;
|
||||
float roll_angle;
|
||||
float pitch_angle;
|
||||
};
|
||||
|
||||
struct pae *pae_load(struct archive *aar, const char *path);
|
||||
void pae_free(struct pae *pae);
|
||||
void pae_calc_frame_range(struct pae *pae, struct motion *motion);
|
||||
float pae_object_calc_alpha(struct pae_object *po, struct particle_instance *pi, float frame);
|
||||
|
||||
struct particle_effect *particle_effect_create(struct pae *pae);
|
||||
void particle_effect_free(struct particle_effect *effect);
|
||||
void particle_effect_update(struct RE_instance *inst);
|
||||
void particle_object_calc_local_transform(struct RE_instance *inst, struct particle_object *po, struct particle_instance *pi, float frame, mat4 dest);
|
||||
|
||||
// s3de.c
|
||||
|
||||
// .3de effect system. The 's' prefix is just to avoid an identifier starting
|
||||
// with a digit; it stands for SealEngine.
|
||||
|
||||
enum s3de_object_type {
|
||||
S3DE_OBJ_BILLBOARD,
|
||||
S3DE_OBJ_POLYGON,
|
||||
S3DE_OBJ_CAMERA,
|
||||
};
|
||||
|
||||
enum s3de_move_type {
|
||||
S3DE_MOVE_LINEAR,
|
||||
S3DE_MOVE_BEZIER,
|
||||
};
|
||||
|
||||
enum s3de_spawn_position_type {
|
||||
S3DE_SPAWN_NORMAL,
|
||||
S3DE_SPAWN_SPHERE,
|
||||
S3DE_SPAWN_HORIZONTAL,
|
||||
};
|
||||
|
||||
enum s3de_blend_type {
|
||||
S3DE_BLEND_NORMAL,
|
||||
S3DE_BLEND_ADDITIVE,
|
||||
};
|
||||
|
||||
enum s3de_emitter_link_type {
|
||||
S3DE_LINK_NONE = 0, // frozen at spawn-time world pos
|
||||
S3DE_LINK_FOLLOW = 1, // particle follows current emitter pos
|
||||
S3DE_LINK_ROTATE_AT_SPAWN = 2, // emitter rotation around spawn pos
|
||||
S3DE_LINK_ROTATE_AT_CURRENT = 3, // emitter rotation around current pos
|
||||
S3DE_LINK_MAX = S3DE_LINK_ROTATE_AT_CURRENT
|
||||
};
|
||||
|
||||
enum s3de_direction_type {
|
||||
S3DE_DIR_RANDOM,
|
||||
S3DE_DIR_SPECIFIED,
|
||||
S3DE_DIR_EMITTER,
|
||||
S3DE_DIR_EMITTER_REVERSE,
|
||||
S3DE_DIR_EMITTER_COORD,
|
||||
S3DE_DIR_ARBITRARY_PLANE,
|
||||
S3DE_DIR_SPAWN,
|
||||
};
|
||||
|
||||
enum s3de_posture_type {
|
||||
S3DE_POSE_NONE,
|
||||
S3DE_POSE_CAMERA,
|
||||
S3DE_POSE_MOVE_DIR,
|
||||
S3DE_POSE_MOVE_AND_FLY,
|
||||
S3DE_POSE_FLY_AND_EMITTER_MOVE,
|
||||
};
|
||||
|
||||
enum s3de_interp_type {
|
||||
S3DE_INTERP_NONE,
|
||||
S3DE_INTERP_LINEAR,
|
||||
S3DE_INTERP_SLERP,
|
||||
};
|
||||
|
||||
struct s3de_range {
|
||||
float base;
|
||||
float random;
|
||||
};
|
||||
|
||||
struct s3de_scalar_entry {
|
||||
int frame;
|
||||
float value;
|
||||
enum s3de_interp_type interp;
|
||||
};
|
||||
|
||||
struct s3de_vec3_entry {
|
||||
int frame;
|
||||
vec3 v;
|
||||
enum s3de_interp_type interp;
|
||||
};
|
||||
|
||||
struct s3de_position_entry {
|
||||
int frame;
|
||||
vec3 pos;
|
||||
enum s3de_interp_type interp;
|
||||
float spawn_radius;
|
||||
float spawn_angle_deg;
|
||||
};
|
||||
|
||||
struct s3de_object {
|
||||
char *name;
|
||||
enum s3de_object_type type;
|
||||
enum s3de_move_type movement_type;
|
||||
enum s3de_spawn_position_type spawn_position_type;
|
||||
float spawn_distance;
|
||||
int particle_count;
|
||||
|
||||
char *texture;
|
||||
|
||||
enum s3de_blend_type blend_type;
|
||||
char *polygon_name;
|
||||
enum s3de_emitter_link_type emitter_link_type;
|
||||
bool soft_fog_edge;
|
||||
|
||||
enum s3de_posture_type posture_type;
|
||||
enum s3de_direction_type direction_type;
|
||||
vec3 direction;
|
||||
float direction_angle;
|
||||
|
||||
struct s3de_range start_size, end_size;
|
||||
struct s3de_range start_x_size, end_x_size;
|
||||
struct s3de_range start_y_size, end_y_size;
|
||||
struct s3de_range offset_x, offset_y, offset_z;
|
||||
struct s3de_range speed, acceleration;
|
||||
struct s3de_range movement_distance, movement_curve;
|
||||
|
||||
bool free_fall;
|
||||
float mass, air_resistance;
|
||||
|
||||
struct s3de_range start_x_rotation, end_x_rotation;
|
||||
struct s3de_range start_y_rotation, end_y_rotation;
|
||||
struct s3de_range start_z_rotation, end_z_rotation;
|
||||
|
||||
float x_revolution_angle[2], y_revolution_angle[2], z_revolution_angle[2];
|
||||
float x_revolution_distance[2], y_revolution_distance[2], z_revolution_distance[2];
|
||||
|
||||
int child_frame;
|
||||
float texture_anime_frame;
|
||||
|
||||
struct s3de_range alpha_fade_in_frame, alpha_fade_out_frame;
|
||||
|
||||
int *damages;
|
||||
int nr_damages;
|
||||
|
||||
struct s3de_position_entry *position_list;
|
||||
int nr_position_entries;
|
||||
struct s3de_scalar_entry *size_list;
|
||||
int nr_size_entries;
|
||||
struct s3de_scalar_entry *x_size_list;
|
||||
int nr_x_size_entries;
|
||||
struct s3de_scalar_entry *y_size_list;
|
||||
int nr_y_size_entries;
|
||||
struct s3de_scalar_entry *alpha_list;
|
||||
int nr_alpha_entries;
|
||||
struct s3de_vec3_entry *rotation_list;
|
||||
int nr_rotation_entries;
|
||||
struct s3de_vec3_entry *multiply_color_list;
|
||||
int nr_multiply_color_entries;
|
||||
struct s3de_vec3_entry *additive_color_list;
|
||||
int nr_additive_color_entries;
|
||||
|
||||
struct model *model;
|
||||
};
|
||||
|
||||
struct s3de {
|
||||
char *path;
|
||||
int loop_start_frame;
|
||||
int loop_end_frame;
|
||||
struct s3de_object *objects;
|
||||
int nr_objects;
|
||||
struct hash_table *textures; // name -> struct billboard_texture*
|
||||
};
|
||||
|
||||
struct s3de *s3de_load(struct archive *aar, const char *path);
|
||||
void s3de_free(struct s3de *s);
|
||||
|
||||
struct s3de_particle {
|
||||
float begin_frame, end_frame;
|
||||
vec3 world_pos; // spawn-time world position
|
||||
vec3 spawn_offset; // offset from emitter pos
|
||||
vec3 direction;
|
||||
vec3 spawn_dir; // emitter motion direction at spawn frame
|
||||
float speed, accel;
|
||||
float movement_distance, movement_curve;
|
||||
float start_size_scale, end_size_scale;
|
||||
float start_x_scale, end_x_scale;
|
||||
float start_y_scale, end_y_scale;
|
||||
float offset_x, offset_y; // per-particle 2D quad pivot shift
|
||||
vec3 start_rotation_deg, end_rotation_deg;
|
||||
float fade_in_frames, fade_out_frames;
|
||||
};
|
||||
|
||||
// Emitter-level values interpolated once per frame by s3de_effect_update,
|
||||
// then read (and combined with per-particle values) during rendering.
|
||||
struct s3de_object_state {
|
||||
vec3 emitter_pos;
|
||||
float emitter_size, emitter_x_size, emitter_y_size;
|
||||
float emitter_alpha;
|
||||
vec3 emitter_rotation_deg;
|
||||
vec3 multiply_color;
|
||||
vec3 additive_color;
|
||||
struct s3de_particle *particles; // length = obj->particle_count
|
||||
};
|
||||
|
||||
struct s3de_effect {
|
||||
struct s3de *s3de;
|
||||
struct s3de_object_state *objects; // length = s3de->nr_objects
|
||||
int wav_channel; // -1 if no sound
|
||||
bool sound_started;
|
||||
float last_frame;
|
||||
};
|
||||
|
||||
struct s3de_effect *s3de_effect_create(struct s3de *s, struct archive *aar);
|
||||
void s3de_effect_free(struct s3de_effect *eff);
|
||||
void s3de_effect_update(struct RE_instance *inst);
|
||||
void s3de_calc_frame_range(struct s3de *s, struct motion *motion);
|
||||
bool s3de_particle_alpha(struct s3de_object_state *st, struct s3de_particle *p,
|
||||
float frame, float *alpha_out);
|
||||
bool s3de_billboard_world_transform(struct RE_instance *inst,
|
||||
struct s3de_object *obj, struct s3de_object_state *st,
|
||||
struct s3de_particle *p, float frame, mat3 camera_rot,
|
||||
mat4 out);
|
||||
bool s3de_mesh_world_transform(struct RE_instance *inst,
|
||||
struct s3de_object *obj, struct s3de_object_state *st,
|
||||
struct s3de_particle *p, float frame, mat3 camera_rot, vec3 camera_pos,
|
||||
mat4 out);
|
||||
|
||||
// parser.c
|
||||
|
||||
struct pol {
|
||||
int32_t version;
|
||||
uint32_t nr_materials;
|
||||
struct pol_material_group *materials;
|
||||
uint32_t nr_meshes;
|
||||
struct pol_mesh **meshes;
|
||||
uint32_t nr_bones;
|
||||
struct pol_bone *bones;
|
||||
};
|
||||
|
||||
enum pol_texture_type {
|
||||
COLOR_MAP = 1,
|
||||
SPECULAR_MAP = 4,
|
||||
ALPHA_MAP = 6,
|
||||
LIGHT_MAP = 7,
|
||||
NORMAL_MAP = 8,
|
||||
MAX_TEXTURE_TYPE
|
||||
};
|
||||
|
||||
enum material_flags {
|
||||
MATERIAL_SPRITE = 1 << 0,
|
||||
MATERIAL_ALPHA = 1 << 1,
|
||||
};
|
||||
|
||||
struct pol_material {
|
||||
char *name;
|
||||
uint32_t flags;
|
||||
char *textures[MAX_TEXTURE_TYPE];
|
||||
vec2 uv_tiling;
|
||||
};
|
||||
|
||||
struct pol_material_group {
|
||||
struct pol_material m;
|
||||
uint32_t nr_children;
|
||||
struct pol_material_group *children;
|
||||
};
|
||||
|
||||
enum mesh_flags {
|
||||
MESH_NOLIGHTING = 1 << 0,
|
||||
MESH_NOMAKESHADOW = 1 << 1,
|
||||
MESH_ENVMAP = 1 << 2,
|
||||
MESH_BOTH = 1 << 3,
|
||||
MESH_SPRITE = 1 << 4,
|
||||
MESH_BLEND_ADDITIVE = 1 << 5,
|
||||
MESH_NO_EDGE = 1 << 6,
|
||||
MESH_NO_HEIGHT_DETECTION = 1 << 7,
|
||||
MESH_ALPHA = 1 << 8,
|
||||
MESH_HAS_LIGHT_UV = 1 << 9,
|
||||
MESH_NO_ZWRITE = 1 << 10,
|
||||
MESH_HEIGHT_DETECTION = 1 << 11,
|
||||
MESH_HAS_SPECULAR_COLOR = 1 << 12,
|
||||
MESH_HAS_SPECULAR_POWER = 1 << 13,
|
||||
MESH_NO_DRAWSHADOW = 1 << 14,
|
||||
};
|
||||
|
||||
struct pol_mesh {
|
||||
char *name;
|
||||
uint32_t flags;
|
||||
uint32_t material;
|
||||
uint32_t nr_vertices;
|
||||
struct pol_vertex *vertices;
|
||||
uint32_t nr_uvs;
|
||||
vec2 *uvs;
|
||||
uint32_t nr_light_uvs;
|
||||
vec2 *light_uvs;
|
||||
uint32_t nr_colors;
|
||||
vec3 *colors;
|
||||
uint32_t nr_alphas;
|
||||
float *alphas;
|
||||
uint32_t nr_blend_uvs;
|
||||
vec2 *blend_uvs;
|
||||
uint32_t nr_blend_weights;
|
||||
float *blend_weights;
|
||||
uint32_t nr_triangles;
|
||||
struct pol_triangle *triangles;
|
||||
// Parameters in .opr file.
|
||||
Color edge_color;
|
||||
float edge_size;
|
||||
vec2 uv_scroll;
|
||||
vec3 specular_color;
|
||||
float specular_power;
|
||||
};
|
||||
|
||||
struct pol_vertex {
|
||||
vec3 pos;
|
||||
uint32_t nr_weights;
|
||||
struct pol_bone_weight *weights;
|
||||
};
|
||||
|
||||
struct pol_bone_weight {
|
||||
uint32_t bone;
|
||||
float weight;
|
||||
};
|
||||
|
||||
struct pol_triangle {
|
||||
uint32_t vert_index[3];
|
||||
uint32_t uv_index[3];
|
||||
uint32_t light_uv_index[3];
|
||||
uint32_t color_index[3];
|
||||
uint32_t alpha_index[3];
|
||||
uint32_t blend_uv_index[3];
|
||||
uint32_t blend_weight_index[3];
|
||||
vec3 normals[3];
|
||||
uint32_t material_group_index;
|
||||
};
|
||||
|
||||
struct pol_bone {
|
||||
char *name;
|
||||
uint32_t id;
|
||||
int32_t parent;
|
||||
vec3 pos;
|
||||
versor rotq;
|
||||
};
|
||||
|
||||
struct mot {
|
||||
char *name;
|
||||
uint32_t nr_frames;
|
||||
uint32_t nr_bones;
|
||||
uint32_t nr_texture_indices;
|
||||
uint32_t *texture_indices; // texture index for each frame
|
||||
struct mpr *mpr; // optional
|
||||
struct mot_bone *motions[];
|
||||
};
|
||||
|
||||
struct mot_frame {
|
||||
vec3 pos;
|
||||
versor rotq;
|
||||
};
|
||||
|
||||
struct mot_bone {
|
||||
char *name;
|
||||
uint32_t id;
|
||||
uint32_t parent;
|
||||
struct mot_frame frames[];
|
||||
};
|
||||
|
||||
struct amt {
|
||||
int version;
|
||||
int nr_materials;
|
||||
struct amt_material *materials[];
|
||||
};
|
||||
|
||||
struct amt_material {
|
||||
char *name;
|
||||
float fields[];
|
||||
};
|
||||
|
||||
enum amt_field_index {
|
||||
AMT_SPECULAR_STRENGTH = 0,
|
||||
AMT_SPECULAR_SHININESS = 2,
|
||||
// amt v4+
|
||||
AMT_SHADOW_DARKNESS = 6,
|
||||
// amt v5+
|
||||
AMT_RIM_EXPONENT = 7,
|
||||
AMT_RIM_R = 8,
|
||||
AMT_RIM_G = 9,
|
||||
AMT_RIM_B = 10,
|
||||
};
|
||||
|
||||
struct pol *pol_parse(uint8_t *data, size_t size);
|
||||
void pol_free(struct pol *pol);
|
||||
void pol_compute_aabb(struct pol *model, vec3 dest[2]);
|
||||
struct pol_bone *pol_find_bone(struct pol *pol, uint32_t id);
|
||||
|
||||
struct mot *mot_parse(uint8_t *data, size_t size, const char *name);
|
||||
void mot_free(struct mot *mot);
|
||||
|
||||
struct amt *amt_parse(uint8_t *data, size_t size);
|
||||
void amt_free(struct amt *amt);
|
||||
struct amt_material *amt_find_material(struct amt *amt, const char *name);
|
||||
|
||||
void opr_load(uint8_t *data, size_t size, struct pol *pol);
|
||||
void txa_load(uint8_t *data, size_t size, struct mot *mot);
|
||||
|
||||
void lit_reset(float *out);
|
||||
bool lit_parse(uint8_t *data, size_t size, float *out);
|
||||
|
||||
// mpr.c
|
||||
|
||||
struct mpr_float_key { int frame; float v; };
|
||||
struct mpr_vec3_key { int frame; vec3 v; };
|
||||
struct mpr_int_key { int frame; int v; };
|
||||
|
||||
struct mpr_track_set {
|
||||
int nr_mul_alpha;
|
||||
struct mpr_float_key *mul_alpha;
|
||||
int nr_mul_diffuse;
|
||||
struct mpr_vec3_key *mul_diffuse;
|
||||
int nr_add_ambient;
|
||||
struct mpr_vec3_key *add_ambient;
|
||||
int nr_texture_anime;
|
||||
struct mpr_int_key *texture_anime; // mesh-scope only
|
||||
};
|
||||
|
||||
struct mpr {
|
||||
int nr_meshes; // == model->nr_meshes
|
||||
struct mpr_track_set **mesh_tracks; // sparse: indexed by mesh index, NULL if absent
|
||||
struct mpr_track_set object;
|
||||
bool has_mesh_alpha; // any mesh_tracks[i]->nr_mul_alpha > 0
|
||||
};
|
||||
|
||||
// Per-draw material modulation.
|
||||
struct mpr_modulation {
|
||||
float alpha;
|
||||
vec3 ambient;
|
||||
vec3 diffuse;
|
||||
};
|
||||
|
||||
struct mpr *mpr_load(uint8_t *data, size_t size, struct model *model);
|
||||
void mpr_free(struct mpr *mpr);
|
||||
void mpr_evaluate_object(const struct mpr *mpr, float frame,
|
||||
struct RE_instance *inst, struct mpr_modulation *out);
|
||||
void mpr_evaluate_mesh(const struct mpr_track_set *mt, float frame,
|
||||
const struct mpr_modulation *obj, struct mpr_modulation *out);
|
||||
void mpr_build_mat_tex_index(const struct model *model, const struct RE_instance *inst,
|
||||
const struct mpr *mpr, float frame, int *mat_tex_index);
|
||||
|
||||
// collision.c
|
||||
|
||||
struct collider_triangle;
|
||||
struct collider_edge;
|
||||
|
||||
struct collider {
|
||||
struct collider_triangle *triangles;
|
||||
uint32_t nr_triangles;
|
||||
struct collider_edge *edges; // boundary edges
|
||||
uint32_t nr_edges;
|
||||
vec3 *path_points;
|
||||
uint32_t nr_path_points;
|
||||
};
|
||||
|
||||
struct collider *collider_create(struct pol_mesh *mesh);
|
||||
struct collider *collider_create_raycast(struct pol_mesh **meshes, int nr_meshes);
|
||||
void collider_free(struct collider *collider);
|
||||
bool collider_height(struct collider *collider, vec2 xz, float *h_out);
|
||||
bool check_collision(struct collider *collider, vec2 p0, vec2 p1, float radius, vec2 out);
|
||||
bool collider_raycast(struct collider *collider, vec3 origin, vec3 direction, vec3 out);
|
||||
bool collider_find_path(struct collider *collider, vec3 start, vec3 goal, mat4 vp_transform);
|
||||
bool collider_optimize_path(struct collider *collider);
|
||||
|
||||
#endif /* SYSTEM4_3D_3D_INTERNAL_H */
|
||||
@@ -0,0 +1,534 @@
|
||||
/* Copyright (C) 2024 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <float.h>
|
||||
#include <stdlib.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#include "system4.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
|
||||
struct collider_triangle {
|
||||
vec3 vertices[3];
|
||||
vec3 center;
|
||||
vec2 aabb[2]; // xz coordinates
|
||||
vec2 slope;
|
||||
float intercept;
|
||||
int neighbors[3];
|
||||
};
|
||||
|
||||
struct collider_edge {
|
||||
vec2 vertices[2]; // xz coordinates
|
||||
vec2 aabb[2];
|
||||
};
|
||||
|
||||
static struct collider_triangle *fill_triangles(struct collider_triangle *t, struct pol_mesh *mesh)
|
||||
{
|
||||
for (int tri_i = 0; tri_i < mesh->nr_triangles; tri_i++, t++) {
|
||||
glm_aabb2d_invalidate(t->aabb);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
glm_vec3_copy(mesh->vertices[mesh->triangles[tri_i].vert_index[i]].pos, t->vertices[i]);
|
||||
glm_vec3_add(t->center, t->vertices[i], t->center);
|
||||
vec2 xz = { t->vertices[i][0], t->vertices[i][2] };
|
||||
glm_vec2_minv(xz, t->aabb[0], t->aabb[0]);
|
||||
glm_vec2_maxv(xz, t->aabb[1], t->aabb[1]);
|
||||
}
|
||||
glm_vec3_divs(t->center, 3.f, t->center);
|
||||
|
||||
// Do some precomputation so that we can calculate height(x, z) quickly.
|
||||
vec3 v1, v2, normal;
|
||||
glm_vec3_sub(t->vertices[1], t->vertices[0], v1);
|
||||
glm_vec3_sub(t->vertices[2], t->vertices[0], v2);
|
||||
glm_vec3_cross(v1, v2, normal);
|
||||
t->slope[0] = -normal[0] / normal[1];
|
||||
t->slope[1] = -normal[2] / normal[1];
|
||||
t->intercept = glm_vec3_dot(normal, t->vertices[0]) / normal[1];
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
t->neighbors[i] = -1;
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
static void init_triangles(struct collider *collider, struct pol_mesh *mesh)
|
||||
{
|
||||
collider->nr_triangles = mesh->nr_triangles;
|
||||
collider->triangles = xcalloc(mesh->nr_triangles, sizeof(struct collider_triangle));
|
||||
fill_triangles(collider->triangles, mesh);
|
||||
}
|
||||
|
||||
static void link_neighbors(struct collider *collider, int t1_index, int t2_index)
|
||||
{
|
||||
struct collider_triangle *t1 = &collider->triangles[t1_index];
|
||||
struct collider_triangle *t2 = &collider->triangles[t2_index];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (t1->neighbors[i] == -1) {
|
||||
t1->neighbors[i] = t2_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (t2->neighbors[i] == -1) {
|
||||
t2->neighbors[i] = t1_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void init_edges(struct collider *collider, struct pol_mesh *mesh)
|
||||
{
|
||||
// Link neighboring triangles.
|
||||
if (mesh->nr_triangles >= 65535)
|
||||
ERROR("Too many triangles in collision mesh: %d", mesh->nr_triangles);
|
||||
const int nv = mesh->nr_vertices;
|
||||
const int table_size = nv * nv;
|
||||
uint16_t *table = xcalloc(table_size, sizeof(uint16_t));
|
||||
int nr_edges = 0;
|
||||
for (int i = 0; i < mesh->nr_triangles; i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
int v1 = mesh->triangles[i].vert_index[j];
|
||||
int v2 = mesh->triangles[i].vert_index[(j + 1) % 3];
|
||||
int k = v2 * nv + v1;
|
||||
if (table[k]) {
|
||||
link_neighbors(collider, table[k] - 1, i);
|
||||
table[k] = 0;
|
||||
nr_edges--;
|
||||
} else {
|
||||
table[v1 * nv + v2] = i + 1;
|
||||
nr_edges++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Collect boundary edges, i.e., edges that belong to only one triangle.
|
||||
collider->nr_edges = nr_edges;
|
||||
collider->edges = xcalloc(nr_edges, sizeof(struct collider_edge));
|
||||
struct collider_edge* edge = collider->edges;
|
||||
for (int i = 0; i < table_size; i++) {
|
||||
if (!table[i]) continue;
|
||||
int v1 = i / nv;
|
||||
int v2 = i % nv;
|
||||
edge->vertices[0][0] = mesh->vertices[v1].pos[0];
|
||||
edge->vertices[0][1] = mesh->vertices[v1].pos[2];
|
||||
edge->vertices[1][0] = mesh->vertices[v2].pos[0];
|
||||
edge->vertices[1][1] = mesh->vertices[v2].pos[2];
|
||||
glm_vec2_minv(edge->vertices[0], edge->vertices[1], edge->aabb[0]);
|
||||
glm_vec2_maxv(edge->vertices[0], edge->vertices[1], edge->aabb[1]);
|
||||
edge++;
|
||||
}
|
||||
assert(edge == collider->edges + nr_edges);
|
||||
free(table);
|
||||
}
|
||||
|
||||
struct collider *collider_create(struct pol_mesh *mesh)
|
||||
{
|
||||
struct collider *collider = xcalloc(1, sizeof(struct collider));
|
||||
init_triangles(collider, mesh);
|
||||
init_edges(collider, mesh);
|
||||
return collider;
|
||||
}
|
||||
|
||||
// Creates a collider for raycasting only (no edges / neighbor links), from the
|
||||
// union of the given meshes' triangles. Used for SealEngine HeightDetection meshes.
|
||||
struct collider *collider_create_raycast(struct pol_mesh **meshes, int nr_meshes)
|
||||
{
|
||||
struct collider *collider = xcalloc(1, sizeof(struct collider));
|
||||
for (int i = 0; i < nr_meshes; i++)
|
||||
collider->nr_triangles += meshes[i]->nr_triangles;
|
||||
collider->triangles = xcalloc(collider->nr_triangles, sizeof(struct collider_triangle));
|
||||
struct collider_triangle *t = collider->triangles;
|
||||
for (int i = 0; i < nr_meshes; i++)
|
||||
t = fill_triangles(t, meshes[i]);
|
||||
return collider;
|
||||
}
|
||||
|
||||
void collider_free(struct collider *collider)
|
||||
{
|
||||
free(collider->triangles);
|
||||
free(collider->edges);
|
||||
free(collider->path_points);
|
||||
free(collider);
|
||||
}
|
||||
|
||||
static bool in_triangle(struct collider_triangle* t, vec2 xz)
|
||||
{
|
||||
vec2 v[3];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
v[i][0] = t->vertices[i][0];
|
||||
v[i][1] = t->vertices[i][2];
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
vec2 a, b;
|
||||
glm_vec2_sub(v[(i + 1) % 3], v[i], a);
|
||||
glm_vec2_sub(xz, v[i], b);
|
||||
if (glm_vec2_cross(a, b) > 0.f)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct collider_triangle* find_triangle(struct collider *collider, vec2 xz)
|
||||
{
|
||||
struct collider_triangle* t = collider->triangles;
|
||||
for (; t < collider->triangles + collider->nr_triangles; t++) {
|
||||
if (!glm_aabb2d_point(t->aabb, xz))
|
||||
continue;
|
||||
if (in_triangle(t, xz))
|
||||
return t;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool collider_height(struct collider *collider, vec2 xz, float *h_out)
|
||||
{
|
||||
struct collider_triangle* t = find_triangle(collider, xz);
|
||||
if (!t)
|
||||
return false;
|
||||
*h_out = glm_vec2_dot(t->slope, xz) + t->intercept;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Calculates the squared distance from a point to a line segment in 2D space.
|
||||
static float distance2_point_to_segment(vec2 p, vec2 a, vec2 b, vec2 closest_point_out)
|
||||
{
|
||||
vec2 v, w;
|
||||
glm_vec2_sub(b, a, v);
|
||||
glm_vec2_sub(p, a, w);
|
||||
|
||||
float c1 = glm_vec2_dot(w, v);
|
||||
if (c1 < 0.f) {
|
||||
glm_vec2_copy(a, closest_point_out);
|
||||
return glm_vec2_distance2(p, a);
|
||||
}
|
||||
float c2 = glm_vec2_norm2(v);
|
||||
if (c2 <= c1) {
|
||||
glm_vec2_copy(b, closest_point_out);
|
||||
return glm_vec2_distance2(p, b);
|
||||
}
|
||||
float t = c1 / c2;
|
||||
glm_vec2_copy(a, closest_point_out);
|
||||
glm_vec2_muladds(v, t, closest_point_out);
|
||||
return glm_vec2_distance2(p, closest_point_out);
|
||||
}
|
||||
|
||||
static bool segments_intersect(vec2 p0, vec2 p1, vec2 q0, vec2 q1)
|
||||
{
|
||||
vec2 r, s;
|
||||
glm_vec2_sub(p1, p0, r);
|
||||
glm_vec2_sub(q1, q0, s);
|
||||
float r_cross_s = glm_vec2_cross(r, s);
|
||||
if (r_cross_s == 0.f) {
|
||||
// The segments are collinear.
|
||||
return false;
|
||||
}
|
||||
vec2 q0_p0;
|
||||
glm_vec2_sub(q0, p0, q0_p0);
|
||||
float t = glm_vec2_cross(q0_p0, s) / r_cross_s;
|
||||
float u = glm_vec2_cross(q0_p0, r) / r_cross_s;
|
||||
return (t >= 0.f && t <= 1.f && u >= 0.f && u <= 1.f);
|
||||
}
|
||||
|
||||
bool check_collision(struct collider *collider, vec2 p0, vec2 p1, float radius, vec2 out)
|
||||
{
|
||||
if (find_triangle(collider, p1)) {
|
||||
glm_vec2_copy(p1, out);
|
||||
} else {
|
||||
if (!find_triangle(collider, p0))
|
||||
return false;
|
||||
// Find a point on the "collision" mesh, using binary search.
|
||||
vec2 good, bad;
|
||||
glm_vec2_copy(p0, good);
|
||||
glm_vec2_copy(p1, bad);
|
||||
float w = glm_vec2_distance(good, bad);
|
||||
while (w > radius) {
|
||||
vec2 mid;
|
||||
glm_vec2_center(good, bad, mid);
|
||||
if (find_triangle(collider, mid)) {
|
||||
glm_vec2_copy(mid, good);
|
||||
} else {
|
||||
glm_vec2_copy(mid, bad);
|
||||
}
|
||||
w /= 2.f;
|
||||
}
|
||||
glm_vec2_copy(good, out);
|
||||
}
|
||||
|
||||
// Ensure that `out` is at least `radius` away from any boundary edge.
|
||||
vec2 aabb[2];
|
||||
glm_vec2_subs(out, radius, aabb[0]);
|
||||
glm_vec2_adds(out, radius, aabb[1]);
|
||||
for (int i = 0; i < collider->nr_edges; i++) {
|
||||
struct collider_edge *e = &collider->edges[i];
|
||||
if (!glm_aabb2d_aabb(e->aabb, aabb))
|
||||
continue;
|
||||
vec2 q;
|
||||
float sq_distance = distance2_point_to_segment(out, e->vertices[0], e->vertices[1], q);
|
||||
if (sq_distance < radius * radius) {
|
||||
// out = q + normalize(out - q) * radius
|
||||
vec2 v;
|
||||
glm_vec2_sub(out, q, v);
|
||||
glm_vec2_normalize(v);
|
||||
glm_vec2_copy(q, out);
|
||||
glm_vec2_muladds(v, radius, out);
|
||||
// update AABB
|
||||
glm_vec2_subs(out, radius, aabb[0]);
|
||||
glm_vec2_adds(out, radius, aabb[1]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool collider_raycast(struct collider *collider, vec3 origin, vec3 direction, vec3 out)
|
||||
{
|
||||
// Return the intersection nearest to the ray origin (closest to the camera).
|
||||
float nearest = FLT_MAX;
|
||||
struct collider_triangle* t = collider->triangles;
|
||||
for (; t < collider->triangles + collider->nr_triangles; t++) {
|
||||
float d;
|
||||
if (glm_ray_triangle(origin, direction, t->vertices[0], t->vertices[1], t->vertices[2], &d)) {
|
||||
if (d < nearest)
|
||||
nearest = d;
|
||||
}
|
||||
}
|
||||
if (nearest == FLT_MAX)
|
||||
return false;
|
||||
glm_vec3_scale(direction, nearest, out);
|
||||
glm_vec3_add(origin, out, out);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct pathfinder_node {
|
||||
int pred;
|
||||
float g_score;
|
||||
enum { UNDISCOVERED, IN_FRONTIER, VISITED, NOT_VISIBLE } state;
|
||||
};
|
||||
|
||||
struct frontier {
|
||||
int triangle_index;
|
||||
float f_score;
|
||||
};
|
||||
|
||||
struct pathfinder {
|
||||
struct pathfinder_node *nodes; // indexed by triangle index
|
||||
struct frontier *frontiers; // min-heap
|
||||
int nr_frontiers;
|
||||
};
|
||||
|
||||
static int frontier_pop(struct pathfinder *pf)
|
||||
{
|
||||
if (pf->nr_frontiers == 0) {
|
||||
return -1;
|
||||
}
|
||||
int index = pf->frontiers[0].triangle_index;
|
||||
if (--pf->nr_frontiers > 0) {
|
||||
pf->frontiers[0] = pf->frontiers[pf->nr_frontiers];
|
||||
int i = 0;
|
||||
while (i < pf->nr_frontiers) {
|
||||
int left = 2 * i + 1;
|
||||
int right = 2 * i + 2;
|
||||
int smallest = i;
|
||||
if (left < pf->nr_frontiers && pf->frontiers[left].f_score < pf->frontiers[smallest].f_score) {
|
||||
smallest = left;
|
||||
}
|
||||
if (right < pf->nr_frontiers && pf->frontiers[right].f_score < pf->frontiers[smallest].f_score) {
|
||||
smallest = right;
|
||||
}
|
||||
if (smallest == i) {
|
||||
break;
|
||||
}
|
||||
struct frontier tmp = pf->frontiers[i];
|
||||
pf->frontiers[i] = pf->frontiers[smallest];
|
||||
pf->frontiers[smallest] = tmp;
|
||||
i = smallest;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
static void frontier_push(struct pathfinder *pf, int index, float f_score)
|
||||
{
|
||||
pf->frontiers[pf->nr_frontiers++] = (struct frontier){ index, f_score };
|
||||
int i = pf->nr_frontiers - 1;
|
||||
while (i > 0) {
|
||||
int parent = (i - 1) / 2;
|
||||
if (pf->frontiers[i].f_score >= pf->frontiers[parent].f_score) {
|
||||
break;
|
||||
}
|
||||
struct frontier tmp = pf->frontiers[i];
|
||||
pf->frontiers[i] = pf->frontiers[parent];
|
||||
pf->frontiers[parent] = tmp;
|
||||
i = parent;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_point_visible(vec3 point, mat4 vp_transform)
|
||||
{
|
||||
vec4 p = { point[0], point[1], point[2], 1.f };
|
||||
glm_mat4_mulv(vp_transform, p, p);
|
||||
// Check if the point is in the view frustum.
|
||||
return p[0] >= -p[3] && p[0] <= p[3] &&
|
||||
p[1] >= -p[3] && p[1] <= p[3] &&
|
||||
p[2] >= -p[3] && p[2] <= p[3];
|
||||
}
|
||||
|
||||
bool collider_find_path(struct collider *collider, vec3 start, vec3 goal, mat4 vp_transform)
|
||||
{
|
||||
if (collider->path_points) {
|
||||
collider->nr_path_points = 0;
|
||||
free(collider->path_points);
|
||||
collider->path_points = NULL;
|
||||
}
|
||||
|
||||
// A* search for a graph with triangle centers as nodes and neighbors as edges.
|
||||
struct collider_triangle* start_t = find_triangle(collider, (vec2){ start[0], start[2] });
|
||||
if (!start_t || !is_point_visible(start_t->center, vp_transform))
|
||||
return false;
|
||||
struct collider_triangle* goal_t = find_triangle(collider, (vec2){ goal[0], goal[2] });
|
||||
if (!goal_t || !is_point_visible(goal_t->center, vp_transform))
|
||||
return false;
|
||||
|
||||
int start_i = start_t - collider->triangles;
|
||||
int goal_i = goal_t - collider->triangles;
|
||||
struct pathfinder pf;
|
||||
pf.nodes = xcalloc(collider->nr_triangles, sizeof(struct pathfinder_node));
|
||||
pf.nodes[goal_i].pred = -2;
|
||||
pf.nodes[start_i].pred = -1;
|
||||
pf.nodes[start_i].g_score = 0.f;
|
||||
pf.nodes[start_i].state = IN_FRONTIER;
|
||||
pf.frontiers = xcalloc(collider->nr_triangles, sizeof(struct frontier));
|
||||
pf.nr_frontiers = 0;
|
||||
frontier_push(&pf, start_i, glm_vec3_distance(start_t->center, goal_t->center));
|
||||
|
||||
while (pf.nr_frontiers > 0) {
|
||||
int current_i = frontier_pop(&pf);
|
||||
if (pf.nodes[current_i].state == VISITED)
|
||||
continue;
|
||||
struct collider_triangle *current_t = &collider->triangles[current_i];
|
||||
if (current_i == goal_i)
|
||||
break;
|
||||
pf.nodes[current_i].state = VISITED;
|
||||
for (int j = 0; j < 3; j++) {
|
||||
int neighbor_i = current_t->neighbors[j];
|
||||
if (neighbor_i == -1)
|
||||
continue;
|
||||
struct collider_triangle *neighbor_t = &collider->triangles[neighbor_i];
|
||||
struct pathfinder_node *neighbor_n = &pf.nodes[neighbor_i];
|
||||
if (neighbor_n->state == NOT_VISIBLE)
|
||||
continue;
|
||||
if (neighbor_n->state == UNDISCOVERED && !is_point_visible(neighbor_t->center, vp_transform)) {
|
||||
neighbor_n->state = NOT_VISIBLE;
|
||||
continue;
|
||||
}
|
||||
float g = pf.nodes[current_i].g_score + glm_vec3_distance(current_t->center, neighbor_t->center);
|
||||
if (neighbor_n->state == UNDISCOVERED || g < neighbor_n->g_score) {
|
||||
neighbor_n->pred = current_i;
|
||||
neighbor_n->g_score = g;
|
||||
neighbor_n->state = IN_FRONTIER;
|
||||
float f_score = g + glm_vec3_distance(neighbor_t->center, goal_t->center);
|
||||
frontier_push(&pf, neighbor_i, f_score);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pf.nodes[goal_i].pred != -2) {
|
||||
// Reconstruct the path.
|
||||
int path_length = 0;
|
||||
int current_i = goal_i;
|
||||
while (current_i != -1) {
|
||||
path_length++;
|
||||
current_i = pf.nodes[current_i].pred;
|
||||
}
|
||||
collider->nr_path_points = path_length + 2;
|
||||
collider->path_points = xcalloc(path_length + 2, sizeof(vec3));
|
||||
glm_vec3_copy(start, collider->path_points[0]);
|
||||
glm_vec3_copy(goal, collider->path_points[path_length + 1]);
|
||||
current_i = goal_i;
|
||||
for (int i = path_length - 1; i >= 0; i--) {
|
||||
glm_vec3_copy(collider->triangles[current_i].center, collider->path_points[i + 1]);
|
||||
current_i = pf.nodes[current_i].pred;
|
||||
}
|
||||
}
|
||||
free(pf.nodes);
|
||||
free(pf.frontiers);
|
||||
return !!collider->path_points;
|
||||
}
|
||||
|
||||
// determines if a segment intersects with any collider edges, or is close enough to them.
|
||||
static bool test_segment(struct collider *collider, vec2 p0, vec2 p1, float threshold)
|
||||
{
|
||||
vec2 aabb[2];
|
||||
glm_vec2_minv(p0, p1, aabb[0]);
|
||||
glm_vec2_maxv(p0, p1, aabb[1]);
|
||||
aabb[0][0] -= threshold;
|
||||
aabb[0][1] -= threshold;
|
||||
aabb[1][0] += threshold;
|
||||
aabb[1][1] += threshold;
|
||||
float threshold2 = threshold * threshold;
|
||||
for (int i = 0; i < collider->nr_edges; i++) {
|
||||
struct collider_edge *e = &collider->edges[i];
|
||||
if (!glm_aabb2d_aabb(e->aabb, aabb))
|
||||
continue;
|
||||
if (segments_intersect(p0, p1, e->vertices[0], e->vertices[1]))
|
||||
return true;
|
||||
vec2 closest_point;
|
||||
float distance2 = distance2_point_to_segment(p0, e->vertices[0], e->vertices[1], closest_point);
|
||||
if (distance2 < threshold2)
|
||||
return true;
|
||||
distance2 = distance2_point_to_segment(p1, e->vertices[0], e->vertices[1], closest_point);
|
||||
if (distance2 < threshold2)
|
||||
return true;
|
||||
distance2 = distance2_point_to_segment(p0, p1, e->vertices[0], closest_point);
|
||||
if (distance2 < threshold2)
|
||||
return true;
|
||||
distance2 = distance2_point_to_segment(p0, p1, e->vertices[1], closest_point);
|
||||
if (distance2 < threshold2)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void optimize_path_rec(struct collider *collider, vec3 *points, int start, int end)
|
||||
{
|
||||
if (end - start >= 2) {
|
||||
// If the segment (points[start], points[end]) does not intersect any
|
||||
// collider edges, we can skip all points in between.
|
||||
vec2 p0 = { points[start][0], points[start][2] };
|
||||
vec2 p1 = { points[end][0], points[end][2] };
|
||||
if (test_segment(collider, p0, p1, 0.5f)) {
|
||||
int mid = (start + end) / 2;
|
||||
optimize_path_rec(collider, points, start, mid);
|
||||
optimize_path_rec(collider, points, mid, end);
|
||||
return;
|
||||
}
|
||||
}
|
||||
glm_vec3_copy(points[start], collider->path_points[collider->nr_path_points++]);
|
||||
}
|
||||
|
||||
bool collider_optimize_path(struct collider *collider)
|
||||
{
|
||||
if (collider->nr_path_points < 3)
|
||||
return collider->nr_path_points > 0; // nothing to optimize
|
||||
|
||||
int nr_points = collider->nr_path_points;
|
||||
vec3 *points = xmalloc(nr_points * sizeof(vec3));
|
||||
memcpy(points, collider->path_points, nr_points * sizeof(vec3));
|
||||
collider->nr_path_points = 0;
|
||||
optimize_path_rec(collider, points, 0, nr_points - 1);
|
||||
glm_vec3_copy(points[nr_points - 1], collider->path_points[collider->nr_path_points++]);
|
||||
free(points);
|
||||
return true;
|
||||
}
|
||||
+447
@@ -0,0 +1,447 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "system4/string.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
#include "cJSON.h"
|
||||
#include "debugger.h"
|
||||
#include "gfx/gfx.h"
|
||||
#include "json.h"
|
||||
#include "reign.h"
|
||||
#include "sact.h"
|
||||
#include "scene.h"
|
||||
#include "sprite.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
#define SPREAD_VEC3(v) (v)[0], (v)[1], (v)[2]
|
||||
|
||||
static const char *instance_type_name(enum RE_instance_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case RE_ITYPE_UNINITIALIZED: return "UNINITIALIZED";
|
||||
case RE_ITYPE_STATIC: return "STATIC";
|
||||
case RE_ITYPE_SKINNED: return "SKINNED";
|
||||
case RE_ITYPE_BILLBOARD: return "BILLBOARD";
|
||||
case RE_ITYPE_DIRECTIONAL_LIGHT: return "DIRECTIONAL_LIGHT";
|
||||
case RE_ITYPE_SPECULAR_LIGHT: return "SPECULAR_LIGHT";
|
||||
case RE_ITYPE_PARTICLE_EFFECT: return "PARTICLE_EFFECT";
|
||||
case RE_ITYPE_PATH_LINE: return "PATH_LINE";
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
static const char *motion_state_name(enum RE_motion_state state)
|
||||
{
|
||||
switch (state) {
|
||||
case RE_MOTION_STATE_STOP: return "STOP";
|
||||
case RE_MOTION_STATE_NOLOOP: return "NOLOOP";
|
||||
case RE_MOTION_STATE_LOOP: return "LOOP";
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
static const char *fog_type_name(enum RE_fog_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case RE_FOG_NONE: return "NONE";
|
||||
case RE_FOG_LINEAR: return "LINEAR";
|
||||
case RE_FOG_LIGHT_SCATTERING: return "LIGHT_SCATTERING";
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
static cJSON *motion_to_json(struct motion *m)
|
||||
{
|
||||
if (m->instance->type == RE_ITYPE_BILLBOARD) {
|
||||
cJSON *obj = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(obj, "state", motion_state_name(m->state));
|
||||
cJSON_AddNumberToObject(obj, "frame", m->current_frame);
|
||||
cJSON_AddItemToObjectCS(obj, "range", num2_to_json(m->frame_begin, m->frame_end));
|
||||
cJSON_AddItemToObjectCS(obj, "loop_range", num2_to_json(m->loop_frame_begin, m->loop_frame_end));
|
||||
return obj;
|
||||
}
|
||||
if (m->mot) {
|
||||
cJSON *obj = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(obj, "name", m->mot->name);
|
||||
cJSON_AddStringToObject(obj, "state", motion_state_name(m->state));
|
||||
cJSON_AddNumberToObject(obj, "frame", m->current_frame);
|
||||
return obj;
|
||||
}
|
||||
return cJSON_CreateNull();
|
||||
}
|
||||
|
||||
static cJSON *instance_to_json(struct RE_instance *inst, int index, bool verbose)
|
||||
{
|
||||
cJSON *obj = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(obj, "index", index);
|
||||
cJSON_AddStringToObject(obj, "type", instance_type_name(inst->type));
|
||||
if (inst->type == RE_ITYPE_DIRECTIONAL_LIGHT || inst->type == RE_ITYPE_SPECULAR_LIGHT) {
|
||||
cJSON_AddItemToObjectCS(obj, "vec", vec3_point_to_json(inst->vec, verbose));
|
||||
cJSON_AddItemToObjectCS(obj, "diffuse", vec3_color_to_json(inst->diffuse, verbose));
|
||||
cJSON_AddItemToObjectCS(obj, "globe_diffuse", vec3_color_to_json(inst->globe_diffuse, verbose));
|
||||
} else {
|
||||
cJSON_AddNumberToObject(obj, "draw", inst->draw);
|
||||
cJSON_AddItemToObjectCS(obj, "pos", vec3_point_to_json(inst->pos, verbose));
|
||||
cJSON_AddItemToObjectCS(obj, "scale", vec3_point_to_json(inst->scale, verbose));
|
||||
cJSON_AddItemToObjectCS(obj, "ambient", vec3_color_to_json(inst->ambient, verbose));
|
||||
}
|
||||
if (inst->model) {
|
||||
cJSON *tmp;
|
||||
cJSON_AddStringToObject(obj, "path", inst->model->path);
|
||||
cJSON_AddItemToObjectCS(obj, "aabb", tmp = cJSON_CreateObject());
|
||||
cJSON_AddItemToObjectCS(tmp, "min", vec3_point_to_json(inst->model->aabb[0], verbose));
|
||||
cJSON_AddItemToObjectCS(tmp, "max", vec3_point_to_json(inst->model->aabb[1], verbose));
|
||||
}
|
||||
if (inst->motion) {
|
||||
cJSON_AddNumberToObject(obj, "fps", inst->fps);
|
||||
cJSON_AddItemToObjectCS(obj, "motion", motion_to_json(inst->motion));
|
||||
}
|
||||
if (inst->next_motion) {
|
||||
cJSON_AddItemToObjectCS(obj, "next_motion", motion_to_json(inst->next_motion));
|
||||
}
|
||||
if (inst->motion_blend) {
|
||||
cJSON_AddNumberToObject(obj, "motion_blend_rate", inst->motion_blend_rate);
|
||||
}
|
||||
if (inst->effect) {
|
||||
cJSON_AddStringToObject(obj, "path", inst->effect->pae->path);
|
||||
}
|
||||
if (inst->s3de_effect && inst->s3de_effect->s3de) {
|
||||
cJSON_AddStringToObject(obj, "path", inst->s3de_effect->s3de->path);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static cJSON *back_cg_to_json(struct RE_back_cg *bcg, int index, bool verbose)
|
||||
{
|
||||
cJSON *obj = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(obj, "index", index);
|
||||
if (bcg->no) {
|
||||
cJSON_AddNumberToObject(obj, "no", bcg->no);
|
||||
}
|
||||
if (bcg->name) {
|
||||
cJSON_AddStringToObject(obj, "name", bcg->name->text);
|
||||
}
|
||||
if (!verbose) {
|
||||
cJSON_AddItemToObjectCS(obj, "pos", num2_to_json(bcg->x, bcg->y));
|
||||
} else {
|
||||
cJSON *pos;
|
||||
cJSON_AddItemToObjectCS(obj, "pos", pos = cJSON_CreateObject());
|
||||
cJSON_AddNumberToObject(pos, "x", bcg->x);
|
||||
cJSON_AddNumberToObject(pos, "y", bcg->y);
|
||||
}
|
||||
cJSON_AddNumberToObject(obj, "blend_rate", bcg->blend_rate);
|
||||
cJSON_AddNumberToObject(obj, "mag", bcg->mag);
|
||||
cJSON_AddNumberToObject(obj, "show", bcg->show);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static cJSON *re_plugin_to_json(struct RE_plugin *p, bool verbose)
|
||||
{
|
||||
cJSON *obj, *cam, *a;
|
||||
|
||||
obj = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(obj, "name", p->plugin.name);
|
||||
cJSON_AddItemToObjectCS(obj, "camera", cam = cJSON_CreateObject());
|
||||
cJSON_AddItemToObjectCS(cam, "pos", vec3_point_to_json(p->camera.pos, verbose));
|
||||
cJSON_AddNumberToObject(cam, "pitch", p->camera.pitch);
|
||||
cJSON_AddNumberToObject(cam, "roll", p->camera.roll);
|
||||
cJSON_AddNumberToObject(cam, "yaw", p->camera.yaw);
|
||||
cJSON_AddItemToObjectCS(obj, "shadow_map_light_dir",
|
||||
vec3_point_to_json(p->shadow_map_light_dir, verbose));
|
||||
cJSON_AddNumberToObject(obj, "shadow_bias", p->shadow_bias);
|
||||
cJSON_AddStringToObject(obj, "fog_type", fog_type_name(p->fog_type));
|
||||
switch (p->fog_type) {
|
||||
case RE_FOG_NONE:
|
||||
break;
|
||||
case RE_FOG_LINEAR:
|
||||
cJSON_AddNumberToObject(obj, "fog_near", p->fog_near);
|
||||
cJSON_AddNumberToObject(obj, "fog_far", p->fog_far);
|
||||
break;
|
||||
case RE_FOG_LIGHT_SCATTERING:
|
||||
cJSON_AddNumberToObject(obj, "ls_beta_r", p->ls_beta_r);
|
||||
cJSON_AddNumberToObject(obj, "ls_beta_m", p->ls_beta_m);
|
||||
cJSON_AddNumberToObject(obj, "ls_g", p->ls_g);
|
||||
cJSON_AddNumberToObject(obj, "ls_distance", p->ls_distance);
|
||||
cJSON_AddItemToObjectCS(obj, "ls_light_dir",
|
||||
vec3_point_to_json(p->ls_light_dir, verbose));
|
||||
cJSON_AddItemToObjectCS(obj, "ls_light_color",
|
||||
vec3_color_to_json(p->ls_light_color, verbose));
|
||||
cJSON_AddItemToObjectCS(obj, "ls_sun_color",
|
||||
vec3_color_to_json(p->ls_sun_color, verbose));
|
||||
break;
|
||||
}
|
||||
|
||||
cJSON_AddItemToObjectCS(obj, "instances", a = cJSON_CreateArray());
|
||||
for (int i = 0; i < p->nr_instances; i++) {
|
||||
if (!p->instances[i])
|
||||
continue;
|
||||
cJSON_AddItemToArray(a, instance_to_json(p->instances[i], i, verbose));
|
||||
|
||||
}
|
||||
|
||||
cJSON_AddItemToObjectCS(obj, "back_cgs", a = cJSON_CreateArray());
|
||||
for (int i = 0; i < RE_NR_BACK_CGS; i++) {
|
||||
struct RE_back_cg *bcg = &p->back_cg[i];
|
||||
if (!bcg->name && !bcg->no)
|
||||
continue;
|
||||
cJSON_AddItemToArray(a, back_cg_to_json(bcg, i, verbose));
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
cJSON *RE_to_json(struct sact_sprite *sp, bool verbose)
|
||||
{
|
||||
return re_plugin_to_json((struct RE_plugin*)sp->plugin, verbose);
|
||||
}
|
||||
|
||||
#ifdef DEBUGGER_ENABLED
|
||||
|
||||
static int dbg_current_plugin = -1;
|
||||
|
||||
// Returns the plugin number to use. Falls back to the only existing plugin
|
||||
// if no current is set. Prints an error and returns -1 if neither holds.
|
||||
static int dbg_resolve_plugin(void)
|
||||
{
|
||||
if (dbg_current_plugin >= 0) {
|
||||
if (RE_get_plugin(dbg_current_plugin))
|
||||
return dbg_current_plugin;
|
||||
DBG_ERROR("Current plugin %d no longer exists", dbg_current_plugin);
|
||||
dbg_current_plugin = -1;
|
||||
return -1;
|
||||
}
|
||||
int found = -1;
|
||||
for (int h = 0; h < RE_MAX_PLUGINS; h++) {
|
||||
if (!RE_get_plugin(h))
|
||||
continue;
|
||||
if (found >= 0) {
|
||||
DBG_ERROR("Multiple plugins exist; use '3d plugin <no>' to choose one");
|
||||
return -1;
|
||||
}
|
||||
found = h;
|
||||
}
|
||||
if (found < 0) {
|
||||
DBG_ERROR("No ReignEngine plugins");
|
||||
return -1;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
static struct RE_plugin *dbg_current_re_plugin(void)
|
||||
{
|
||||
int no = dbg_resolve_plugin();
|
||||
return no < 0 ? NULL : RE_get_plugin(no);
|
||||
}
|
||||
|
||||
static bool instance_has_content(struct RE_instance *inst)
|
||||
{
|
||||
if (inst->type == RE_ITYPE_UNINITIALIZED)
|
||||
return false;
|
||||
if (inst->model && inst->model->path)
|
||||
return true;
|
||||
if (inst->motion && inst->motion->mot)
|
||||
return true;
|
||||
if (inst->effect && inst->effect->pae)
|
||||
return true;
|
||||
if (inst->s3de_effect && inst->s3de_effect->s3de)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void instance_list_print(struct RE_instance *inst, int index, int indent)
|
||||
{
|
||||
indent_message(indent, inst->draw ? "+ " : "- ");
|
||||
sys_message("instance %d (%s)", index, instance_type_name(inst->type));
|
||||
if (inst->model && inst->model->path)
|
||||
sys_message(" model=%s", display_sjis0(inst->model->path));
|
||||
if (inst->motion && inst->motion->mot)
|
||||
sys_message(" motion=%s/%s frame=%.2f",
|
||||
display_sjis0(inst->motion->mot->name),
|
||||
motion_state_name(inst->motion->state),
|
||||
inst->motion->current_frame);
|
||||
if (inst->effect && inst->effect->pae)
|
||||
sys_message(" effect=%s", display_sjis0(inst->effect->pae->path));
|
||||
if (inst->s3de_effect && inst->s3de_effect->s3de)
|
||||
sys_message(" s3de=%s", display_sjis0(inst->s3de_effect->s3de->path));
|
||||
sys_message("\n");
|
||||
}
|
||||
|
||||
static void re_cmd_list(unsigned nr_args, char **args)
|
||||
{
|
||||
bool show_all = nr_args >= 1 && !strcmp(args[0], "all");
|
||||
for (int h = 0; h < RE_MAX_PLUGINS; h++) {
|
||||
struct RE_plugin *p = RE_get_plugin(h);
|
||||
if (!p)
|
||||
continue;
|
||||
int nr_hidden = 0;
|
||||
sys_message("plugin %d (%s sprite=%d nr_instances=%d)\n",
|
||||
h, p->plugin.name, p->sprite, p->nr_instances);
|
||||
for (int i = 0; i < p->nr_instances; i++) {
|
||||
struct RE_instance *inst = p->instances[i];
|
||||
if (!inst)
|
||||
continue;
|
||||
if (!show_all && !instance_has_content(inst)) {
|
||||
nr_hidden++;
|
||||
continue;
|
||||
}
|
||||
instance_list_print(inst, i, 1);
|
||||
}
|
||||
if (nr_hidden)
|
||||
indent_message(1, "(%d empty instance(s) hidden; use 'list all' to show)\n",
|
||||
nr_hidden);
|
||||
}
|
||||
}
|
||||
|
||||
static void re_cmd_plugin(unsigned nr_args, char **args)
|
||||
{
|
||||
if (nr_args == 0) {
|
||||
int no = dbg_resolve_plugin();
|
||||
if (no < 0)
|
||||
return;
|
||||
struct RE_plugin *p = RE_get_plugin(no);
|
||||
sys_message("plugin %d (%s)%s\n", no, p->plugin.name,
|
||||
dbg_current_plugin < 0 ? " (auto-selected)" : "");
|
||||
return;
|
||||
}
|
||||
int no = atoi(args[0]);
|
||||
if (!RE_get_plugin(no)) {
|
||||
DBG_ERROR("No ReignEngine plugin with handle '%s'", args[0]);
|
||||
return;
|
||||
}
|
||||
dbg_current_plugin = no;
|
||||
}
|
||||
|
||||
static void re_cmd_show(unsigned nr_args, char **args)
|
||||
{
|
||||
struct RE_plugin *p = dbg_current_re_plugin();
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
cJSON *json;
|
||||
if (nr_args >= 1) {
|
||||
int idx = atoi(args[0]);
|
||||
if (idx < 0 || idx >= p->nr_instances || !p->instances[idx]) {
|
||||
DBG_ERROR("No instance with index '%s'", args[0]);
|
||||
return;
|
||||
}
|
||||
json = instance_to_json(p->instances[idx], idx, true);
|
||||
} else {
|
||||
json = re_plugin_to_json(p, true);
|
||||
}
|
||||
char *text = cJSON_Print(json);
|
||||
|
||||
sys_message("%s\n", text);
|
||||
|
||||
free(text);
|
||||
cJSON_Delete(json);
|
||||
}
|
||||
|
||||
static void re_cmd_set(unsigned nr_args, char **args)
|
||||
{
|
||||
struct RE_plugin *p = dbg_current_re_plugin();
|
||||
if (!p)
|
||||
return;
|
||||
int idx = atoi(args[0]);
|
||||
if (idx < 0 || idx >= p->nr_instances || !p->instances[idx]) {
|
||||
DBG_ERROR("No instance with index '%s'", args[0]);
|
||||
return;
|
||||
}
|
||||
struct RE_instance *inst = p->instances[idx];
|
||||
const char *prop = args[1];
|
||||
unsigned nr_values = nr_args - 2;
|
||||
char **values = &args[2];
|
||||
|
||||
if (!strcmp(prop, "draw")) {
|
||||
if (nr_values != 1) {
|
||||
DBG_ERROR("'draw' requires 1 value (0 or 1)");
|
||||
return;
|
||||
}
|
||||
inst->draw = atoi(values[0]) != 0;
|
||||
} else if (!strcmp(prop, "pos")) {
|
||||
if (nr_values != 3) {
|
||||
DBG_ERROR("'pos' requires 3 values (x y z)");
|
||||
return;
|
||||
}
|
||||
inst->pos[0] = strtof(values[0], NULL);
|
||||
inst->pos[1] = strtof(values[1], NULL);
|
||||
inst->pos[2] = strtof(values[2], NULL);
|
||||
inst->local_transform_needs_update = true;
|
||||
} else {
|
||||
DBG_ERROR("Unknown property '%s'", prop);
|
||||
return;
|
||||
}
|
||||
|
||||
sprite_call_plugins();
|
||||
scene_render();
|
||||
gfx_swap();
|
||||
}
|
||||
|
||||
static void re_cmd_help(unsigned nr_args, char **args);
|
||||
|
||||
static struct dbg_cmd re_cmds[] = {
|
||||
{ "list", NULL, "[all]",
|
||||
"Display ReignEngine plugins and their instances (use 'all' to include uninitialized)",
|
||||
0, 1, re_cmd_list },
|
||||
{ "plugin", NULL, "[<plugin-no>]",
|
||||
"Show or set the current plugin used by 'show' and 'set'",
|
||||
0, 1, re_cmd_plugin },
|
||||
{ "show", NULL, "[<instance-no>]",
|
||||
"Display current plugin or instance state as JSON",
|
||||
0, 1, re_cmd_show },
|
||||
{ "set", NULL, "<instance-no> <property> <value...>",
|
||||
"Set an instance property (draw, pos)",
|
||||
3, 5, re_cmd_set },
|
||||
{ "help", "h", NULL, "List 3d commands",
|
||||
0, 0, re_cmd_help },
|
||||
};
|
||||
|
||||
static void re_cmd_help(unsigned nr_args, char **args)
|
||||
{
|
||||
int name_len = 0;
|
||||
for (unsigned i = 0; i < sizeof(re_cmds)/sizeof(*re_cmds); i++) {
|
||||
int n = strlen(re_cmds[i].fullname);
|
||||
if (n > name_len)
|
||||
name_len = n;
|
||||
}
|
||||
sys_message("\n3d commands\n===========\n");
|
||||
for (unsigned i = 0; i < sizeof(re_cmds)/sizeof(*re_cmds); i++) {
|
||||
struct dbg_cmd *c = &re_cmds[i];
|
||||
sys_message("%*s", name_len, c->fullname);
|
||||
if (c->shortname)
|
||||
sys_message(" (%s)", c->shortname);
|
||||
if (c->arg_description)
|
||||
sys_message(" %s", c->arg_description);
|
||||
sys_message(" -- %s\n", c->description);
|
||||
}
|
||||
}
|
||||
|
||||
void re_debug_init(void)
|
||||
{
|
||||
static bool initialized = false;
|
||||
if (initialized)
|
||||
return;
|
||||
initialized = true;
|
||||
|
||||
dbg_cmd_add_module("3d", sizeof(re_cmds)/sizeof(*re_cmds), re_cmds);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void re_debug_init(void) {}
|
||||
|
||||
#endif // DEBUGGER_ENABLED
|
||||
+823
@@ -0,0 +1,823 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/aar.h"
|
||||
#include "system4/cg.h"
|
||||
#include "system4/hashtable.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
#include "reign.h"
|
||||
|
||||
#define FP16_MIN 6.103516e-5f
|
||||
#define NR_WEIGHTS 4
|
||||
#define DEFAULT_OUTLINE_THICKNESS 0.003
|
||||
|
||||
struct vertex_common {
|
||||
GLfloat pos[3];
|
||||
GLfloat normal[3];
|
||||
GLfloat uv[2];
|
||||
};
|
||||
|
||||
struct vertex_light_uv {
|
||||
GLfloat uv[2];
|
||||
};
|
||||
|
||||
struct vertex_color {
|
||||
GLfloat color[4];
|
||||
};
|
||||
|
||||
struct vertex_tangent {
|
||||
GLfloat tangent[4];
|
||||
};
|
||||
|
||||
struct vertex_bones {
|
||||
GLint bone_id[NR_WEIGHTS];
|
||||
GLfloat bone_weight[NR_WEIGHTS];
|
||||
};
|
||||
|
||||
struct vertex_blend {
|
||||
GLfloat blend_weight;
|
||||
GLfloat blend_uv[2];
|
||||
};
|
||||
|
||||
static bool is_transparent_mesh(const struct pol_mesh *mesh)
|
||||
{
|
||||
if (re_plugin_version == RE_REIGN_PLUGIN)
|
||||
return !(mesh->flags & MESH_SPRITE);
|
||||
else
|
||||
return mesh->flags & MESH_ALPHA;
|
||||
}
|
||||
|
||||
static bool is_transparent_material(const struct pol_material *material)
|
||||
{
|
||||
if (re_plugin_version == RE_REIGN_PLUGIN)
|
||||
return !(material->flags & MATERIAL_SPRITE);
|
||||
else
|
||||
return material->flags & MATERIAL_ALPHA;
|
||||
}
|
||||
|
||||
struct archive_data *RE_get_aar_entry(struct archive *aar, const char *dir, const char *name, const char *ext)
|
||||
{
|
||||
char *path = xmalloc(strlen(dir) + strlen(name) + strlen(ext) + 2);
|
||||
sprintf(path, "%s\\%s%s", dir, name, ext);
|
||||
struct archive_data *dfile = archive_get_by_name(aar, path);
|
||||
free(path);
|
||||
return dfile;
|
||||
}
|
||||
|
||||
static GLuint load_texture(struct archive *aar, const char *path, const char *name, bool *has_alpha_out)
|
||||
{
|
||||
struct archive_data *dfile = RE_get_aar_entry(aar, path, name, "");
|
||||
if (!dfile) {
|
||||
return 0;
|
||||
}
|
||||
struct cg *cg = cg_load_data(dfile);
|
||||
if (!cg) {
|
||||
WARNING("cg_load_data failed: %s", dfile->name);
|
||||
archive_free_data(dfile);
|
||||
return 0;
|
||||
}
|
||||
archive_free_data(dfile);
|
||||
|
||||
GLuint texture;
|
||||
glGenTextures(1, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cg->metrics.w, cg->metrics.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, cg->pixels);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
if (has_alpha_out)
|
||||
*has_alpha_out = cg->metrics.has_alpha;
|
||||
cg_free(cg);
|
||||
return texture;
|
||||
}
|
||||
|
||||
static GLuint *load_texture_list(struct archive *aar, const char *path, const char *name, int *nr_textures_out, bool *has_alpha_out)
|
||||
{
|
||||
// Load the base texture (e.g. face.png)
|
||||
GLuint tex = load_texture(aar, path, name, has_alpha_out);
|
||||
if (!tex)
|
||||
return NULL;
|
||||
GLuint *textures = xmalloc(10 * sizeof(GLuint));
|
||||
int nr_textures = 0;
|
||||
textures[nr_textures++] = tex;
|
||||
|
||||
// Try to load additional textures (e.g. face[1].png, face[2].png, etc.)
|
||||
const char *ext = strrchr(name, '.');
|
||||
if (ext) {
|
||||
for (;;) {
|
||||
char next_name[100];
|
||||
snprintf(next_name, sizeof(next_name), "%.*s[%d]%s", (int)(ext - name), name, nr_textures, ext);
|
||||
tex = load_texture(aar, path, next_name, NULL);
|
||||
if (!tex)
|
||||
break;
|
||||
textures[nr_textures++] = tex;
|
||||
if (nr_textures % 10 == 0) {
|
||||
textures = xrealloc(textures, (nr_textures + 10) * sizeof(GLuint));
|
||||
}
|
||||
}
|
||||
}
|
||||
*nr_textures_out = nr_textures;
|
||||
return textures;
|
||||
}
|
||||
|
||||
static bool init_material(struct material *material, struct pol_material *m, struct amt *amt, struct archive *aar, const char *path)
|
||||
{
|
||||
material->flags = m->flags;
|
||||
glm_vec2_copy(m->uv_tiling, material->uv_tiling);
|
||||
glm_vec2_one(material->blend_uv_tiling);
|
||||
if (!m->textures[COLOR_MAP]) {
|
||||
WARNING("No color texture");
|
||||
return false;
|
||||
}
|
||||
bool has_alpha;
|
||||
material->color_maps = load_texture_list(aar, path, m->textures[COLOR_MAP], &material->nr_color_maps, &has_alpha);
|
||||
if (!material->color_maps)
|
||||
return false;
|
||||
|
||||
if (m->textures[SPECULAR_MAP])
|
||||
material->specular_map = load_texture(aar, path, m->textures[SPECULAR_MAP], NULL);
|
||||
if (m->textures[ALPHA_MAP]) {
|
||||
if (has_alpha && !strcmp(m->textures[COLOR_MAP], m->textures[ALPHA_MAP])) {
|
||||
// Do nothing; the alpha channel of the color map is used.
|
||||
} else {
|
||||
material->alpha_map = load_texture(aar, path, m->textures[ALPHA_MAP], NULL);
|
||||
}
|
||||
}
|
||||
if (m->textures[LIGHT_MAP])
|
||||
material->light_map = load_texture(aar, path, m->textures[LIGHT_MAP], NULL);
|
||||
if (m->textures[NORMAL_MAP])
|
||||
material->normal_map = load_texture(aar, path, m->textures[NORMAL_MAP], NULL);
|
||||
|
||||
material->is_transparent = (has_alpha || material->alpha_map) && is_transparent_material(m);
|
||||
material->shadow_darkness = 1.0f;
|
||||
|
||||
struct amt_material *amt_m = amt ? amt_find_material(amt, m->name) : NULL;
|
||||
if (amt_m) {
|
||||
material->specular_strength = amt_m->fields[AMT_SPECULAR_STRENGTH];
|
||||
material->specular_shininess = amt_m->fields[AMT_SPECULAR_SHININESS];
|
||||
if (amt->version >= 4)
|
||||
material->shadow_darkness = amt_m->fields[AMT_SHADOW_DARKNESS];
|
||||
if (amt->version >= 5) {
|
||||
material->rim_exponent = amt_m->fields[AMT_RIM_EXPONENT];
|
||||
material->rim_color[0] = amt_m->fields[AMT_RIM_R];
|
||||
material->rim_color[1] = amt_m->fields[AMT_RIM_G];
|
||||
material->rim_color[2] = amt_m->fields[AMT_RIM_B];
|
||||
// Very small rim_exponent value should not be used for rim
|
||||
// lighting. (e.g. meizi.amt in TT3)
|
||||
if (material->rim_exponent < FP16_MIN)
|
||||
material->rim_exponent = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void destroy_material(struct material *material)
|
||||
{
|
||||
if (material->color_maps) {
|
||||
glDeleteTextures(material->nr_color_maps, material->color_maps);
|
||||
free(material->color_maps);
|
||||
}
|
||||
if (material->specular_map)
|
||||
glDeleteTextures(1, &material->specular_map);
|
||||
if (material->alpha_map)
|
||||
glDeleteTextures(1, &material->alpha_map);
|
||||
if (material->light_map)
|
||||
glDeleteTextures(1, &material->light_map);
|
||||
if (material->normal_map)
|
||||
glDeleteTextures(1, &material->normal_map);
|
||||
if (material->blend_texture)
|
||||
glDeleteTextures(1, &material->blend_texture);
|
||||
}
|
||||
|
||||
static int cmp_by_bone_weight(const void *lhs, const void *rhs)
|
||||
{
|
||||
float l = ((struct pol_bone_weight *)lhs)->weight;
|
||||
float r = ((struct pol_bone_weight *)rhs)->weight;
|
||||
return (l < r) - (l > r); // descending order.
|
||||
}
|
||||
|
||||
static void sort_and_normalize_bone_weights(struct pol_vertex *v)
|
||||
{
|
||||
qsort(v->weights, v->nr_weights, sizeof(struct pol_bone_weight), cmp_by_bone_weight);
|
||||
float total = 0.0;
|
||||
for (uint32_t i = 0; i < v->nr_weights && i < NR_WEIGHTS; i++) {
|
||||
total += v->weights[i].weight;
|
||||
}
|
||||
for (uint32_t i = 0; i < v->nr_weights && i < NR_WEIGHTS; i++) {
|
||||
v->weights[i].weight /= total;
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_tangent(struct pol_mesh *m, struct pol_triangle *t, vec4 tangent[3])
|
||||
{
|
||||
vec3 v1, v2;
|
||||
glm_vec3_sub(m->vertices[t->vert_index[1]].pos, m->vertices[t->vert_index[0]].pos, v1);
|
||||
glm_vec3_sub(m->vertices[t->vert_index[2]].pos, m->vertices[t->vert_index[0]].pos, v2);
|
||||
|
||||
vec2 w1, w2;
|
||||
glm_vec2_sub(m->uvs[t->uv_index[1]], m->uvs[t->uv_index[0]], w1);
|
||||
glm_vec2_sub(m->uvs[t->uv_index[2]], m->uvs[t->uv_index[0]], w2);
|
||||
|
||||
float r = 1.0 / (w1[0] * w2[1] - w2[0] * w1[1]);
|
||||
if (!isfinite(r)) // degenerate uv triangle
|
||||
r = 1.0; // ??
|
||||
|
||||
vec3 sdir, tdir;
|
||||
glm_vec3_scale(v1, w2[1], sdir);
|
||||
glm_vec3_muladds(v2, -w1[1], sdir);
|
||||
glm_vec3_scale(sdir, r, sdir);
|
||||
|
||||
glm_vec3_scale(v2, w1[0], tdir);
|
||||
glm_vec3_muladds(v1, -w2[0], tdir);
|
||||
glm_vec3_scale(tdir, r, tdir);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
vec3 s;
|
||||
glm_vec3_copy(sdir, s);
|
||||
|
||||
// Gram-Schmidt orthogonalize.
|
||||
glm_vec3_muladds(t->normals[i], -glm_vec3_dot(t->normals[i], s), s);
|
||||
glm_vec3_normalize(s);
|
||||
|
||||
// Calculate handedness.
|
||||
vec3 c;
|
||||
glm_vec3_cross(t->normals[i], sdir, c);
|
||||
float w = (glm_vec3_dot(c, tdir) < 0.0) ? -1.0 : 1.0;
|
||||
|
||||
glm_vec4(s, w, tangent[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void *buf_alloc(uint8_t **ptr, int size)
|
||||
{
|
||||
void *p = *ptr;
|
||||
*ptr += size;
|
||||
return p;
|
||||
}
|
||||
|
||||
static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_group_index, int material)
|
||||
{
|
||||
bool has_light_map = m->light_uvs && model->materials[material].light_map;
|
||||
bool has_vertex_colors = m->nr_colors > 0 || m->nr_alphas > 0;
|
||||
bool has_normal_map = model->materials[material].normal_map != 0;
|
||||
bool has_bones = !!model->bone_map;
|
||||
bool has_blend = m->blend_weights && model->materials[material].blend_texture;
|
||||
|
||||
GLsizei stride = sizeof(struct vertex_common);
|
||||
if (has_light_map)
|
||||
stride += sizeof(struct vertex_light_uv);
|
||||
if (has_vertex_colors)
|
||||
stride += sizeof(struct vertex_color);
|
||||
if (has_normal_map)
|
||||
stride += sizeof(struct vertex_tangent);
|
||||
if (has_bones)
|
||||
stride += sizeof(struct vertex_bones);
|
||||
if (has_blend)
|
||||
stride += sizeof(struct vertex_blend);
|
||||
|
||||
void *buffer = xmalloc(m->nr_triangles * 3 * stride);
|
||||
uint8_t *ptr = buffer;
|
||||
|
||||
int nr_vertices = 0;
|
||||
for (uint32_t i = 0; i < m->nr_triangles; i++) {
|
||||
struct pol_triangle *t = &m->triangles[i];
|
||||
if (t->material_group_index != material_group_index)
|
||||
continue;
|
||||
vec4 tangent[3];
|
||||
if (has_normal_map)
|
||||
calc_tangent(m, t, tangent);
|
||||
for (int j = 0; j < 3; j++) {
|
||||
struct pol_vertex *vert = &m->vertices[t->vert_index[j]];
|
||||
struct vertex_common *v_common = buf_alloc(&ptr, sizeof(struct vertex_common));
|
||||
glm_vec3_copy(vert->pos, v_common->pos);
|
||||
glm_vec3_copy(t->normals[j], v_common->normal);
|
||||
glm_vec2_copy(m->uvs[t->uv_index[j]], v_common->uv);
|
||||
if (has_light_map) {
|
||||
struct vertex_light_uv *v_light_uv = buf_alloc(&ptr, sizeof(struct vertex_light_uv));
|
||||
glm_vec2_copy(m->light_uvs[t->light_uv_index[j]], v_light_uv->uv);
|
||||
}
|
||||
if (has_vertex_colors) {
|
||||
struct vertex_color *v_color = buf_alloc(&ptr, sizeof(struct vertex_color));
|
||||
if (m->nr_colors > 0)
|
||||
glm_vec3_copy(m->colors[t->color_index[j]], v_color->color);
|
||||
else
|
||||
glm_vec3_one(v_color->color);
|
||||
v_color->color[3] = m->nr_alphas > 0 ? m->alphas[t->alpha_index[j]] : 1.f;
|
||||
}
|
||||
if (has_normal_map) {
|
||||
struct vertex_tangent *v_tangent = buf_alloc(&ptr, sizeof(struct vertex_tangent));
|
||||
glm_vec4_ucopy(tangent[j], v_tangent->tangent);
|
||||
}
|
||||
if (has_bones) {
|
||||
struct vertex_bones *v_bones = buf_alloc(&ptr, sizeof(struct vertex_bones));
|
||||
sort_and_normalize_bone_weights(vert);
|
||||
for (uint32_t k = 0; k < NR_WEIGHTS; k++) {
|
||||
if (k < vert->nr_weights) {
|
||||
struct bone *bone = ht_get_int(model->bone_map, vert->weights[k].bone, NULL);
|
||||
if (!bone)
|
||||
WARNING("%s: invalid bone id in vertex data", model->path);
|
||||
v_bones->bone_id[k] = bone ? bone->index : -1;
|
||||
v_bones->bone_weight[k] = vert->weights[k].weight;
|
||||
} else {
|
||||
v_bones->bone_id[k] = -1;
|
||||
v_bones->bone_weight[k] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (has_blend) {
|
||||
struct vertex_blend *v_blend = buf_alloc(&ptr, sizeof(struct vertex_blend));
|
||||
v_blend->blend_weight = m->blend_weights[t->blend_weight_index[j]];
|
||||
if (m->blend_uvs)
|
||||
glm_vec2_copy(m->blend_uvs[t->blend_uv_index[j]], v_blend->blend_uv);
|
||||
else
|
||||
glm_vec2_copy(m->uvs[t->uv_index[j]], v_blend->blend_uv);
|
||||
}
|
||||
nr_vertices++;
|
||||
}
|
||||
}
|
||||
assert(ptr == (uint8_t *)buffer + nr_vertices * stride);
|
||||
|
||||
if (nr_vertices == 0) {
|
||||
free(buffer);
|
||||
return;
|
||||
}
|
||||
model->meshes = xrealloc_array(model->meshes, model->nr_meshes, model->nr_meshes + 1, sizeof(struct mesh));
|
||||
struct mesh *mesh = &model->meshes[model->nr_meshes++];
|
||||
mesh->name = xstrdup(m->name);
|
||||
mesh->flags = m->flags;
|
||||
mesh->material = material;
|
||||
if (re_plugin_version == RE_REIGN_PLUGIN)
|
||||
mesh->is_transparent = model->materials[material].is_transparent && is_transparent_mesh(m);
|
||||
else
|
||||
mesh->is_transparent = model->materials[material].is_transparent || is_transparent_mesh(m);
|
||||
if (mesh->is_transparent)
|
||||
model->has_transparent_mesh = true;
|
||||
mesh->outline_color[0] = m->edge_color.r / 255.f;
|
||||
mesh->outline_color[1] = m->edge_color.g / 255.f;
|
||||
mesh->outline_color[2] = m->edge_color.b / 255.f;
|
||||
mesh->outline_thickness = m->edge_size ? m->edge_size : DEFAULT_OUTLINE_THICKNESS;
|
||||
glm_vec2_copy(m->uv_scroll, mesh->uv_scroll);
|
||||
glm_vec3_copy(m->specular_color, mesh->specular_color);
|
||||
mesh->specular_power = m->specular_power;
|
||||
mesh->nr_vertices = nr_vertices;
|
||||
|
||||
glGenVertexArrays(1, &mesh->vao);
|
||||
glBindVertexArray(mesh->vao);
|
||||
glGenBuffers(1, &mesh->attr_buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, mesh->attr_buffer);
|
||||
|
||||
const uint8_t *base = (const uint8_t *)0;
|
||||
glEnableVertexAttribArray(VATTR_POS);
|
||||
glVertexAttribPointer(VATTR_POS, 3, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_common, pos));
|
||||
glEnableVertexAttribArray(VATTR_NORMAL);
|
||||
glVertexAttribPointer(VATTR_NORMAL, 3, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_common, normal));
|
||||
glEnableVertexAttribArray(VATTR_UV);
|
||||
glVertexAttribPointer(VATTR_UV, 2, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_common, uv));
|
||||
base += sizeof(struct vertex_common);
|
||||
if (has_light_map) {
|
||||
glEnableVertexAttribArray(VATTR_LIGHT_UV);
|
||||
glVertexAttribPointer(VATTR_LIGHT_UV, 2, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_light_uv, uv));
|
||||
base += sizeof(struct vertex_light_uv);
|
||||
} else {
|
||||
glDisableVertexAttribArray(VATTR_LIGHT_UV);
|
||||
glVertexAttrib2f(VATTR_LIGHT_UV, 0.0, 0.0);
|
||||
}
|
||||
if (has_vertex_colors) {
|
||||
glEnableVertexAttribArray(VATTR_COLOR);
|
||||
glVertexAttribPointer(VATTR_COLOR, 4, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_color, color));
|
||||
base += sizeof(struct vertex_color);
|
||||
} else {
|
||||
glDisableVertexAttribArray(VATTR_COLOR);
|
||||
glVertexAttrib4f(VATTR_COLOR, 1.0, 1.0, 1.0, 1.0);
|
||||
}
|
||||
if (has_normal_map) {
|
||||
glEnableVertexAttribArray(VATTR_TANGENT);
|
||||
glVertexAttribPointer(VATTR_TANGENT, 4, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_tangent, tangent));
|
||||
base += sizeof(struct vertex_tangent);
|
||||
} else {
|
||||
glDisableVertexAttribArray(VATTR_TANGENT);
|
||||
glVertexAttrib3f(VATTR_TANGENT, 1.0, 0.0, 0.0);
|
||||
}
|
||||
if (has_bones) {
|
||||
glEnableVertexAttribArray(VATTR_BONE_INDEX);
|
||||
glVertexAttribIPointer(VATTR_BONE_INDEX, NR_WEIGHTS, GL_INT, stride, base + offsetof(struct vertex_bones, bone_id));
|
||||
glEnableVertexAttribArray(VATTR_BONE_WEIGHT);
|
||||
glVertexAttribPointer(VATTR_BONE_WEIGHT, NR_WEIGHTS, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_bones, bone_weight));
|
||||
base += sizeof(struct vertex_bones);
|
||||
} else {
|
||||
glDisableVertexAttribArray(VATTR_BONE_INDEX);
|
||||
glVertexAttribI4i(VATTR_BONE_INDEX, 0, 0, 0, 0);
|
||||
glDisableVertexAttribArray(VATTR_BONE_WEIGHT);
|
||||
glVertexAttrib4f(VATTR_BONE_WEIGHT, 0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
if (has_blend) {
|
||||
glEnableVertexAttribArray(VATTR_BLEND_WEIGHT);
|
||||
glVertexAttribPointer(VATTR_BLEND_WEIGHT, 1, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_blend, blend_weight));
|
||||
glEnableVertexAttribArray(VATTR_BLEND_UV);
|
||||
glVertexAttribPointer(VATTR_BLEND_UV, 2, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_blend, blend_uv));
|
||||
base += sizeof(struct vertex_blend);
|
||||
} else {
|
||||
glDisableVertexAttribArray(VATTR_BLEND_WEIGHT);
|
||||
glVertexAttrib1f(VATTR_BLEND_WEIGHT, 0.0);
|
||||
glDisableVertexAttribArray(VATTR_BLEND_UV);
|
||||
glVertexAttrib2f(VATTR_BLEND_UV, 0.0, 0.0);
|
||||
}
|
||||
assert((intptr_t)base == stride);
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, mesh->nr_vertices * stride, buffer, GL_STATIC_DRAW);
|
||||
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static void destroy_mesh(struct mesh *mesh)
|
||||
{
|
||||
free(mesh->name);
|
||||
glDeleteVertexArrays(1, &mesh->vao);
|
||||
glDeleteBuffers(1, &mesh->attr_buffer);
|
||||
if (mesh->index_buffer)
|
||||
glDeleteBuffers(1, &mesh->index_buffer);
|
||||
}
|
||||
|
||||
static struct bone *add_bone(struct model *model, struct pol *pol, struct pol_bone *pol_bone)
|
||||
{
|
||||
struct bone *bone = ht_get_int(model->bone_map, pol_bone->id, NULL);
|
||||
if (bone)
|
||||
return bone; // already added.
|
||||
|
||||
struct bone *parent = NULL;
|
||||
if (pol_bone->parent >= 0) {
|
||||
// Parent bone must appear before its children in model->bones.
|
||||
struct pol_bone *pol_parent = pol_find_bone(pol, pol_bone->parent);
|
||||
if (!pol_parent)
|
||||
ERROR("Parent bone of \"%s\" is not found", pol_bone->name);
|
||||
parent = add_bone(model, pol, pol_parent);
|
||||
}
|
||||
|
||||
bone = &model->bones[model->nr_bones];
|
||||
ht_put_int(model->bone_map, pol_bone->id, bone);
|
||||
bone->name = strdup(pol_bone->name);
|
||||
bone->index = model->nr_bones;
|
||||
bone->parent = parent ? parent->index : -1;
|
||||
|
||||
glm_quat_mat4(pol_bone->rotq, bone->inverse_bind_matrix);
|
||||
glm_translate(bone->inverse_bind_matrix, pol_bone->pos);
|
||||
|
||||
model->nr_bones++;
|
||||
return bone;
|
||||
}
|
||||
|
||||
static void destroy_bone(struct bone *bone)
|
||||
{
|
||||
free(bone->name);
|
||||
}
|
||||
|
||||
struct model *model_load(struct archive *aar, const char *path)
|
||||
{
|
||||
const char *basename = strrchr(path, '\\');
|
||||
basename = basename ? basename + 1 : path;
|
||||
|
||||
// Load .POL file
|
||||
struct archive_data *pol_file = RE_get_aar_entry(aar, path, basename, ".POL");
|
||||
if (!pol_file) {
|
||||
WARNING("%s\\%s.POL: not found", path, basename);
|
||||
return NULL;
|
||||
}
|
||||
struct pol *pol = pol_parse(pol_file->data, pol_file->size);
|
||||
if (!pol) {
|
||||
WARNING("%s: parse error", pol_file->name);
|
||||
archive_free_data(pol_file);
|
||||
return NULL;
|
||||
}
|
||||
archive_free_data(pol_file);
|
||||
|
||||
// Load .amt file, if any
|
||||
struct amt *amt = NULL;
|
||||
struct archive_data *amt_file = RE_get_aar_entry(aar, path, basename, ".amt");
|
||||
if (amt_file) {
|
||||
amt = amt_parse(amt_file->data, amt_file->size);
|
||||
if (!amt)
|
||||
WARNING("%s: parse error", amt_file->name);
|
||||
archive_free_data(amt_file);
|
||||
}
|
||||
|
||||
struct model *model = xcalloc(1, sizeof(struct model));
|
||||
model->path = strdup(path);
|
||||
|
||||
// Load .opr file, if any
|
||||
struct archive_data *opr_file = RE_get_aar_entry(aar, path, basename, ".opr");
|
||||
if (opr_file) {
|
||||
opr_load(opr_file->data, opr_file->size, pol);
|
||||
archive_free_data(opr_file);
|
||||
}
|
||||
|
||||
// Bones
|
||||
if (pol->nr_bones > 0) {
|
||||
if (pol->nr_bones > MAX_BONES)
|
||||
ERROR("%s: Too many bones (%u)", model->path, pol->nr_bones);
|
||||
model->bone_map = ht_create(pol->nr_bones * 3 / 2);
|
||||
model->mot_cache = ht_create(16);
|
||||
model->bones = xcalloc_aligned(pol->nr_bones, struct bone);
|
||||
model->bones_by_pol_index = xcalloc(pol->nr_bones, sizeof(struct bone *));
|
||||
for (uint32_t i = 0; i < pol->nr_bones; i++) {
|
||||
model->bones_by_pol_index[i] = add_bone(model, pol, &pol->bones[i]);
|
||||
}
|
||||
if (model->nr_bones != (int)pol->nr_bones)
|
||||
ERROR("%s: Broken bone data", model->path);
|
||||
}
|
||||
|
||||
// Materials
|
||||
int *material_offsets = xmalloc(pol->nr_materials * sizeof(int));
|
||||
for (uint32_t i = 0; i < pol->nr_materials; i++) {
|
||||
material_offsets[i] = model->nr_materials;
|
||||
if (pol->materials[i].nr_children)
|
||||
model->nr_materials += pol->materials[i].nr_children;
|
||||
else
|
||||
model->nr_materials++;
|
||||
}
|
||||
model->materials = xcalloc(model->nr_materials, sizeof(struct material));
|
||||
for (uint32_t i = 0; i < pol->nr_materials; i++) {
|
||||
if (pol->materials[i].nr_children == 0) {
|
||||
init_material(&model->materials[material_offsets[i]],
|
||||
&pol->materials[i].m, amt, aar, path);
|
||||
continue;
|
||||
}
|
||||
for (uint32_t j = 0; j < pol->materials[i].nr_children; j++) {
|
||||
struct pol_material_group *child = &pol->materials[i].children[j];
|
||||
if (child->nr_children >= 2) {
|
||||
// Group node: texture blending (base + blend)
|
||||
init_material(&model->materials[material_offsets[i] + j],
|
||||
&child->children[0].m, amt, aar, path);
|
||||
if (child->children[1].m.textures[COLOR_MAP]) {
|
||||
model->materials[material_offsets[i] + j].blend_texture =
|
||||
load_texture(aar, path, child->children[1].m.textures[COLOR_MAP], NULL);
|
||||
glm_vec2_copy(child->children[1].m.uv_tiling,
|
||||
model->materials[material_offsets[i] + j].blend_uv_tiling);
|
||||
}
|
||||
} else {
|
||||
init_material(&model->materials[material_offsets[i] + j],
|
||||
&child->m, amt, aar, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Meshes
|
||||
struct pol_mesh **hd_meshes = xmalloc(pol->nr_meshes * sizeof(*hd_meshes));
|
||||
int nr_hd_meshes = 0;
|
||||
for (uint32_t i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!pol->meshes[i])
|
||||
continue;
|
||||
if (!strcmp(pol->meshes[i]->name, "collision")) {
|
||||
if (model->collider)
|
||||
WARNING("multiple collision meshes");
|
||||
else
|
||||
model->collider = collider_create(pol->meshes[i]);
|
||||
continue;
|
||||
}
|
||||
if (pol->meshes[i]->flags & MESH_HEIGHT_DETECTION)
|
||||
hd_meshes[nr_hd_meshes++] = pol->meshes[i];
|
||||
struct pol_material_group *mg = &pol->materials[pol->meshes[i]->material];
|
||||
int m_off = material_offsets[pol->meshes[i]->material];
|
||||
if (mg->nr_children == 0) {
|
||||
add_mesh(model, pol->meshes[i], 0, m_off);
|
||||
continue;
|
||||
}
|
||||
for (uint32_t j = 0; j < mg->nr_children; j++) {
|
||||
add_mesh(model, pol->meshes[i], j, m_off + j);
|
||||
}
|
||||
}
|
||||
|
||||
if (!model->collider && nr_hd_meshes > 0)
|
||||
model->collider = collider_create_raycast(hd_meshes, nr_hd_meshes);
|
||||
free(hd_meshes);
|
||||
|
||||
pol_compute_aabb(pol, model->aabb);
|
||||
|
||||
free(material_offsets);
|
||||
if (amt)
|
||||
amt_free(amt);
|
||||
pol_free(pol);
|
||||
return model;
|
||||
}
|
||||
|
||||
void model_free(struct model *model)
|
||||
{
|
||||
if (model->collider)
|
||||
collider_free(model->collider);
|
||||
|
||||
for (int i = 0; i < model->nr_meshes; i++)
|
||||
destroy_mesh(&model->meshes[i]);
|
||||
free(model->meshes);
|
||||
|
||||
for (int i = 0; i < model->nr_materials; i++)
|
||||
destroy_material(&model->materials[i]);
|
||||
free(model->materials);
|
||||
|
||||
for (int i = 0; i < model->nr_bones; i++)
|
||||
destroy_bone(&model->bones[i]);
|
||||
xfree_aligned(model->bones);
|
||||
free(model->bones_by_pol_index);
|
||||
if (model->bone_map)
|
||||
ht_free_int(model->bone_map);
|
||||
if (model->mot_cache) {
|
||||
ht_foreach_value(model->mot_cache, (void(*)(void*))mot_free);
|
||||
ht_free(model->mot_cache);
|
||||
}
|
||||
|
||||
free(model->path);
|
||||
free(model);
|
||||
}
|
||||
|
||||
static void init_sphere_mesh(struct mesh *mesh)
|
||||
{
|
||||
const int w_segments = 16;
|
||||
const int h_segments = 16;
|
||||
const int nr_vertices = (w_segments + 1) * (h_segments + 1);
|
||||
struct vertex_common *vertices = xcalloc(nr_vertices, sizeof(struct vertex_common));
|
||||
struct vertex_common *v = vertices;
|
||||
for (int y = 0; y <= h_segments; y++) {
|
||||
float theta = GLM_PIf * y / h_segments;
|
||||
for (int x = 0; x <= w_segments; x++, v++) {
|
||||
float phi = 2.0f * GLM_PIf * x / w_segments;
|
||||
v->pos[0] = -cosf(phi) * sinf(theta);
|
||||
v->pos[1] = cosf(theta);
|
||||
v->pos[2] = sinf(phi) * sinf(theta);
|
||||
glm_vec3_copy(v->pos, v->normal);
|
||||
v->uv[0] = v->uv[1] = 0.0f;
|
||||
}
|
||||
}
|
||||
assert(v == vertices + nr_vertices);
|
||||
|
||||
const int nr_indices = 3 * 2 * w_segments * (h_segments - 1);
|
||||
GLushort *indices = xcalloc(nr_indices, sizeof(GLushort));
|
||||
GLushort *pi = indices;
|
||||
for (int y = 0; y < h_segments; y++) {
|
||||
for (int x = 0; x < w_segments; x++) {
|
||||
GLushort a = y * w_segments + x + 1;
|
||||
GLushort b = y * w_segments + x;
|
||||
GLushort c = (y + 1) * w_segments + x;
|
||||
GLushort d = (y + 1) * w_segments + x + 1;
|
||||
if (y > 0) {
|
||||
*pi++ = a;
|
||||
*pi++ = b;
|
||||
*pi++ = d;
|
||||
}
|
||||
if (y < h_segments - 1) {
|
||||
*pi++ = b;
|
||||
*pi++ = c;
|
||||
*pi++ = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(pi == indices + nr_indices);
|
||||
|
||||
mesh->flags = MESH_NOLIGHTING;
|
||||
mesh->nr_vertices = nr_vertices;
|
||||
mesh->nr_indices = nr_indices;
|
||||
glGenVertexArrays(1, &mesh->vao);
|
||||
glBindVertexArray(mesh->vao);
|
||||
glGenBuffers(1, &mesh->attr_buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, mesh->attr_buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, nr_vertices * sizeof(struct vertex_common), vertices, GL_STATIC_DRAW);
|
||||
glEnableVertexAttribArray(VATTR_POS);
|
||||
glVertexAttribPointer(VATTR_POS, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex_common), (void*)offsetof(struct vertex_common, pos));
|
||||
glEnableVertexAttribArray(VATTR_NORMAL);
|
||||
glVertexAttribPointer(VATTR_NORMAL, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex_common), (void*)offsetof(struct vertex_common, normal));
|
||||
glEnableVertexAttribArray(VATTR_UV);
|
||||
glVertexAttribPointer(VATTR_UV, 2, GL_FLOAT, GL_FALSE, sizeof(struct vertex_common), (void*)offsetof(struct vertex_common, uv));
|
||||
|
||||
glGenBuffers(1, &mesh->index_buffer);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->index_buffer);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, nr_indices * sizeof(GLushort), indices, GL_STATIC_DRAW);
|
||||
glBindVertexArray(0);
|
||||
|
||||
free(vertices);
|
||||
free(indices);
|
||||
}
|
||||
|
||||
struct model *model_create_sphere(int r, int g, int b, int a)
|
||||
{
|
||||
struct model *model = xcalloc(1, sizeof(struct model));
|
||||
|
||||
model->nr_meshes = 1;
|
||||
model->meshes = xcalloc(1, sizeof(struct mesh));
|
||||
init_sphere_mesh(&model->meshes[0]);
|
||||
model->aabb[0][0] = -1.0f;
|
||||
model->aabb[0][1] = -1.0f;
|
||||
model->aabb[0][2] = -1.0f;
|
||||
model->aabb[1][0] = 1.0f;
|
||||
model->aabb[1][1] = 1.0f;
|
||||
model->aabb[1][2] = 1.0f;
|
||||
|
||||
model->nr_materials = 1;
|
||||
model->materials = xcalloc(1, sizeof(struct material));
|
||||
struct material *material = &model->materials[0];
|
||||
material->is_transparent = true;
|
||||
glm_vec2_one(material->uv_tiling);
|
||||
glm_vec2_one(material->blend_uv_tiling);
|
||||
material->color_maps = xmalloc(sizeof(GLuint));
|
||||
material->nr_color_maps = 1;
|
||||
glGenTextures(1, material->color_maps);
|
||||
glBindTexture(GL_TEXTURE_2D, material->color_maps[0]);
|
||||
uint8_t pixel[4] = {r, g, b, a};
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
model->has_transparent_mesh = true;
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
static struct mot *mot_load(const char *name, struct model *model, struct archive *aar)
|
||||
{
|
||||
struct archive_data *mot_file = RE_get_aar_entry(aar, model->path, name, ".MOT");
|
||||
if (!mot_file) {
|
||||
WARNING("%s\\%s.MOT: not found", model->path, name);
|
||||
return NULL;
|
||||
}
|
||||
struct mot *mot = mot_parse(mot_file->data, mot_file->size, name);
|
||||
if (!mot) {
|
||||
WARNING("%s: parse error", mot_file->name);
|
||||
archive_free_data(mot_file);
|
||||
return NULL;
|
||||
}
|
||||
archive_free_data(mot_file);
|
||||
|
||||
if (model->nr_bones != (int)mot->nr_bones)
|
||||
ERROR("%s: wrong number of bones. Expected %d but got %d", name, model->nr_bones, mot->nr_bones);
|
||||
|
||||
// Reorder mot->motions so that motion for model->bones[i] can be
|
||||
// accessed by mot->motions[i]. MOT bones are applied to POL bones purely
|
||||
// by array index; bone name and bone id are never used for matching.
|
||||
struct mot_bone **reordered = xmalloc(mot->nr_bones * sizeof(struct mot_bone *));
|
||||
for (uint32_t i = 0; i < mot->nr_bones; i++) {
|
||||
reordered[model->bones_by_pol_index[i]->index] = mot->motions[i];
|
||||
}
|
||||
memcpy(mot->motions, reordered, mot->nr_bones * sizeof(struct mot_bone *));
|
||||
free(reordered);
|
||||
|
||||
// Load optional sidecar file.
|
||||
if (re_plugin_version <= RE_TAPIR_PLUGIN) {
|
||||
struct archive_data *txa_file = RE_get_aar_entry(aar, model->path, name, ".txa");
|
||||
if (txa_file) {
|
||||
txa_load(txa_file->data, txa_file->size, mot);
|
||||
archive_free_data(txa_file);
|
||||
}
|
||||
} else {
|
||||
struct archive_data *mpr_file = RE_get_aar_entry(aar, model->path, name, ".mpr");
|
||||
if (mpr_file) {
|
||||
mot->mpr = mpr_load(mpr_file->data, mpr_file->size, model);
|
||||
archive_free_data(mpr_file);
|
||||
}
|
||||
}
|
||||
|
||||
return mot;
|
||||
}
|
||||
|
||||
struct motion *motion_load(const char *name, struct RE_instance *instance, struct archive *aar)
|
||||
{
|
||||
struct model *model = instance->model;
|
||||
if (!model || !model->mot_cache)
|
||||
return NULL;
|
||||
struct mot *mot = ht_get(model->mot_cache, name, NULL);
|
||||
if (!mot) {
|
||||
mot = mot_load(name, model, aar);
|
||||
if (!mot)
|
||||
return NULL;
|
||||
ht_put(model->mot_cache, name, mot);
|
||||
}
|
||||
struct motion *motion = xcalloc(1, sizeof(struct motion));
|
||||
motion->instance = instance;
|
||||
motion->mot = mot;
|
||||
return motion;
|
||||
}
|
||||
|
||||
void motion_free(struct motion *motion)
|
||||
{
|
||||
free(motion);
|
||||
}
|
||||
+294
@@ -0,0 +1,294 @@
|
||||
/* Copyright (C) 2026 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#include "system4.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
|
||||
// .mpr parser.
|
||||
|
||||
static int resolve_mesh_index(struct model *model, const char *name)
|
||||
{
|
||||
for (int i = 0; i < model->nr_meshes; i++) {
|
||||
if (!strcmp(model->meshes[i].name, name))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void append_float_key(struct mpr_track_set *ts, int frame, float v)
|
||||
{
|
||||
ts->mul_alpha = xrealloc(ts->mul_alpha, (ts->nr_mul_alpha + 1) * sizeof(struct mpr_float_key));
|
||||
ts->mul_alpha[ts->nr_mul_alpha].frame = frame;
|
||||
ts->mul_alpha[ts->nr_mul_alpha].v = v;
|
||||
ts->nr_mul_alpha++;
|
||||
}
|
||||
|
||||
static void append_vec3_key(struct mpr_vec3_key **arr, int *nr, int frame, float r, float g, float b)
|
||||
{
|
||||
*arr = xrealloc(*arr, (*nr + 1) * sizeof(struct mpr_vec3_key));
|
||||
(*arr)[*nr].frame = frame;
|
||||
(*arr)[*nr].v[0] = r;
|
||||
(*arr)[*nr].v[1] = g;
|
||||
(*arr)[*nr].v[2] = b;
|
||||
(*nr)++;
|
||||
}
|
||||
|
||||
static void append_int_key(struct mpr_int_key **arr, int *nr, int frame, int v)
|
||||
{
|
||||
*arr = xrealloc(*arr, (*nr + 1) * sizeof(struct mpr_int_key));
|
||||
(*arr)[*nr].frame = frame;
|
||||
(*arr)[*nr].v = v;
|
||||
(*nr)++;
|
||||
}
|
||||
|
||||
// All mpr_*_key structs have `int frame` as their first field.
|
||||
static int cmp_key_by_frame(const void *a, const void *b)
|
||||
{
|
||||
return *(const int *)a - *(const int *)b;
|
||||
}
|
||||
|
||||
static void sort_track_set(struct mpr_track_set *ts)
|
||||
{
|
||||
if (ts->nr_mul_alpha)
|
||||
qsort(ts->mul_alpha, ts->nr_mul_alpha, sizeof(struct mpr_float_key), cmp_key_by_frame);
|
||||
if (ts->nr_mul_diffuse)
|
||||
qsort(ts->mul_diffuse, ts->nr_mul_diffuse, sizeof(struct mpr_vec3_key), cmp_key_by_frame);
|
||||
if (ts->nr_add_ambient)
|
||||
qsort(ts->add_ambient, ts->nr_add_ambient, sizeof(struct mpr_vec3_key), cmp_key_by_frame);
|
||||
if (ts->nr_texture_anime)
|
||||
qsort(ts->texture_anime, ts->nr_texture_anime, sizeof(struct mpr_int_key), cmp_key_by_frame);
|
||||
}
|
||||
|
||||
struct mpr *mpr_load(uint8_t *data, size_t size, struct model *model)
|
||||
{
|
||||
struct mpr *mpr = xcalloc(1, sizeof(struct mpr));
|
||||
mpr->nr_meshes = model->nr_meshes;
|
||||
mpr->mesh_tracks = xcalloc(model->nr_meshes, sizeof(struct mpr_track_set *));
|
||||
struct mpr_track_set *current = NULL; // current Mesh-scope track set
|
||||
|
||||
while (size > 0) {
|
||||
char line[200];
|
||||
uint8_t *nl = memchr(data, '\n', size);
|
||||
if (nl)
|
||||
nl++;
|
||||
else
|
||||
nl = data + size;
|
||||
int copylen = min(nl - data, sizeof(line) - 1);
|
||||
memcpy(line, (char *)data, copylen);
|
||||
line[copylen] = '\0';
|
||||
size -= nl - data;
|
||||
data = nl;
|
||||
|
||||
char s[200];
|
||||
int frame, ti;
|
||||
float a, r, g, b;
|
||||
if (sscanf(line, "Mesh = \"%[^\"]\"", s) == 1) {
|
||||
int mi = resolve_mesh_index(model, s);
|
||||
if (mi < 0) {
|
||||
WARNING("mpr: unknown mesh \"%s\"", s);
|
||||
current = NULL;
|
||||
} else {
|
||||
if (!mpr->mesh_tracks[mi])
|
||||
mpr->mesh_tracks[mi] = xcalloc(1, sizeof(struct mpr_track_set));
|
||||
current = mpr->mesh_tracks[mi];
|
||||
}
|
||||
} else if (sscanf(line, "MeshMulAlpha = ( %d , %f )", &frame, &a) == 2) {
|
||||
if (current)
|
||||
append_float_key(current, frame, a);
|
||||
else
|
||||
WARNING("mpr: MeshMulAlpha outside Mesh scope");
|
||||
} else if (sscanf(line, "MeshMulDiffuse = ( %d , %f , %f , %f )", &frame, &r, &g, &b) == 4) {
|
||||
if (current)
|
||||
append_vec3_key(¤t->mul_diffuse, ¤t->nr_mul_diffuse, frame, r, g, b);
|
||||
else
|
||||
WARNING("mpr: MeshMulDiffuse outside Mesh scope");
|
||||
} else if (sscanf(line, "MeshAddAmbient = ( %d , %f , %f , %f )", &frame, &r, &g, &b) == 4) {
|
||||
if (current)
|
||||
append_vec3_key(¤t->add_ambient, ¤t->nr_add_ambient, frame, r, g, b);
|
||||
else
|
||||
WARNING("mpr: MeshAddAmbient outside Mesh scope");
|
||||
} else if (sscanf(line, "MeshTextureAnime = ( %d , %d )", &frame, &ti) == 2) {
|
||||
if (current)
|
||||
append_int_key(¤t->texture_anime, ¤t->nr_texture_anime, frame, ti);
|
||||
else
|
||||
WARNING("mpr: MeshTextureAnime outside Mesh scope");
|
||||
} else if (sscanf(line, "ObjectMulAlpha = ( %d , %f )", &frame, &a) == 2) {
|
||||
append_float_key(&mpr->object, frame, a);
|
||||
} else if (sscanf(line, "ObjectMulDiffuse = ( %d , %f , %f , %f )", &frame, &r, &g, &b) == 4) {
|
||||
append_vec3_key(&mpr->object.mul_diffuse, &mpr->object.nr_mul_diffuse, frame, r, g, b);
|
||||
} else if (sscanf(line, "ObjectAddAmbient = ( %d , %f , %f , %f )", &frame, &r, &g, &b) == 4) {
|
||||
append_vec3_key(&mpr->object.add_ambient, &mpr->object.nr_add_ambient, frame, r, g, b);
|
||||
} else if (strchr(line, '=')) {
|
||||
WARNING("mpr: unknown field: %s", line);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < mpr->nr_meshes; i++) {
|
||||
if (!mpr->mesh_tracks[i])
|
||||
continue;
|
||||
sort_track_set(mpr->mesh_tracks[i]);
|
||||
if (mpr->mesh_tracks[i]->nr_mul_alpha > 0)
|
||||
mpr->has_mesh_alpha = true;
|
||||
}
|
||||
sort_track_set(&mpr->object);
|
||||
return mpr;
|
||||
}
|
||||
|
||||
static void destroy_track_set(struct mpr_track_set *ts)
|
||||
{
|
||||
free(ts->mul_alpha);
|
||||
free(ts->mul_diffuse);
|
||||
free(ts->add_ambient);
|
||||
free(ts->texture_anime);
|
||||
}
|
||||
|
||||
void mpr_free(struct mpr *mpr)
|
||||
{
|
||||
if (!mpr)
|
||||
return;
|
||||
for (int i = 0; i < mpr->nr_meshes; i++) {
|
||||
if (mpr->mesh_tracks[i]) {
|
||||
destroy_track_set(mpr->mesh_tracks[i]);
|
||||
free(mpr->mesh_tracks[i]);
|
||||
}
|
||||
}
|
||||
free(mpr->mesh_tracks);
|
||||
destroy_track_set(&mpr->object);
|
||||
free(mpr);
|
||||
}
|
||||
|
||||
// Keyframe evaluation.
|
||||
|
||||
// Returns the largest index i with keys[i].frame <= frame.
|
||||
// Returns -1 if frame < keys[0].frame.
|
||||
#define DEFINE_FIND_KEY(NAME, KEY_TYPE) \
|
||||
static int NAME(const KEY_TYPE *keys, int nr, float frame) \
|
||||
{ \
|
||||
if (frame < (float)keys[0].frame) \
|
||||
return -1; \
|
||||
int lo = 0, hi = nr - 1; \
|
||||
while (lo < hi) { \
|
||||
int mid = (lo + hi + 1) / 2; \
|
||||
if ((float)keys[mid].frame <= frame) \
|
||||
lo = mid; \
|
||||
else \
|
||||
hi = mid - 1; \
|
||||
} \
|
||||
return lo; \
|
||||
}
|
||||
|
||||
DEFINE_FIND_KEY(find_float_key, struct mpr_float_key)
|
||||
DEFINE_FIND_KEY(find_vec3_key, struct mpr_vec3_key)
|
||||
DEFINE_FIND_KEY(find_int_key, struct mpr_int_key)
|
||||
|
||||
static float eval_float(const struct mpr_float_key *keys, int nr, float frame, float fallback)
|
||||
{
|
||||
if (nr == 0)
|
||||
return fallback;
|
||||
int i = find_float_key(keys, nr, frame);
|
||||
if (i < 0)
|
||||
return keys[0].v;
|
||||
if (i >= nr - 1)
|
||||
return keys[nr - 1].v;
|
||||
float t = (frame - keys[i].frame) / (float)(keys[i + 1].frame - keys[i].frame);
|
||||
return glm_lerp(keys[i].v, keys[i + 1].v, t);
|
||||
}
|
||||
|
||||
static void eval_vec3(const struct mpr_vec3_key *keys, int nr, float frame, const vec3 fallback, vec3 out)
|
||||
{
|
||||
if (nr == 0) {
|
||||
glm_vec3_copy((float *)fallback, out);
|
||||
return;
|
||||
}
|
||||
int i = find_vec3_key(keys, nr, frame);
|
||||
if (i < 0) {
|
||||
glm_vec3_copy((float *)keys[0].v, out);
|
||||
return;
|
||||
}
|
||||
if (i >= nr - 1) {
|
||||
glm_vec3_copy((float *)keys[nr - 1].v, out);
|
||||
return;
|
||||
}
|
||||
float t = (frame - keys[i].frame) / (float)(keys[i + 1].frame - keys[i].frame);
|
||||
glm_vec3_lerp((float *)keys[i].v, (float *)keys[i + 1].v, t, out);
|
||||
}
|
||||
|
||||
static int eval_int(const struct mpr_int_key *keys, int nr, float frame, int fallback)
|
||||
{
|
||||
if (nr == 0)
|
||||
return fallback;
|
||||
int i = find_int_key(keys, nr, frame);
|
||||
if (i < 0)
|
||||
return keys[0].v;
|
||||
return keys[i].v; // step
|
||||
}
|
||||
|
||||
// Combine inst defaults with .mpr Object-scope keyframes.
|
||||
void mpr_evaluate_object(const struct mpr *mpr, float frame,
|
||||
struct RE_instance *inst, struct mpr_modulation *out)
|
||||
{
|
||||
out->alpha = inst->alpha;
|
||||
glm_vec3_copy(inst->ambient, out->ambient);
|
||||
glm_vec3_copy(inst->diffuse, out->diffuse);
|
||||
if (!mpr)
|
||||
return;
|
||||
out->alpha *= eval_float(mpr->object.mul_alpha, mpr->object.nr_mul_alpha, frame, 1.f);
|
||||
vec3 add;
|
||||
eval_vec3(mpr->object.add_ambient, mpr->object.nr_add_ambient, frame, (vec3){0,0,0}, add);
|
||||
glm_vec3_add(out->ambient, add, out->ambient);
|
||||
eval_vec3(mpr->object.mul_diffuse, mpr->object.nr_mul_diffuse, frame, (vec3){1,1,1}, out->diffuse);
|
||||
}
|
||||
|
||||
// Combine the object-scope values with .mpr Mesh-scope keyframes for one mesh.
|
||||
void mpr_evaluate_mesh(const struct mpr_track_set *mt, float frame,
|
||||
const struct mpr_modulation *obj, struct mpr_modulation *out)
|
||||
{
|
||||
*out = *obj;
|
||||
if (!mt)
|
||||
return;
|
||||
out->alpha *= eval_float(mt->mul_alpha, mt->nr_mul_alpha, frame, 1.f);
|
||||
vec3 add;
|
||||
eval_vec3(mt->add_ambient, mt->nr_add_ambient, frame, (vec3){0,0,0}, add);
|
||||
glm_vec3_add(out->ambient, add, out->ambient);
|
||||
vec3 mul;
|
||||
eval_vec3(mt->mul_diffuse, mt->nr_mul_diffuse, frame, (vec3){1,1,1}, mul);
|
||||
glm_vec3_mul(out->diffuse, mul, out->diffuse);
|
||||
}
|
||||
|
||||
// MeshTextureAnime writes to the shared material slot, so all meshes
|
||||
// referencing the same material pick up the animation even if .mpr only names
|
||||
// one of them. Caller supplies the output buffer sized to model->nr_materials.
|
||||
void mpr_build_mat_tex_index(const struct model *model, const struct RE_instance *inst,
|
||||
const struct mpr *mpr, float frame, int *mat_tex_index)
|
||||
{
|
||||
for (int m = 0; m < model->nr_materials; m++)
|
||||
mat_tex_index[m] = inst->texture_animation_index;
|
||||
if (!mpr)
|
||||
return;
|
||||
for (int i = 0; i < mpr->nr_meshes; i++) {
|
||||
const struct mpr_track_set *t = mpr->mesh_tracks[i];
|
||||
if (!t || t->nr_texture_anime == 0)
|
||||
continue;
|
||||
int mat = model->meshes[i].material;
|
||||
mat_tex_index[mat] = eval_int(t->texture_anime, t->nr_texture_anime, frame, mat_tex_index[mat]);
|
||||
}
|
||||
}
|
||||
+771
@@ -0,0 +1,771 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/buffer.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
|
||||
#define METERS_PER_INCH 0.0254
|
||||
|
||||
static char *read_cstring(struct buffer *r)
|
||||
{
|
||||
char *s = strdup(buffer_strdata(r));
|
||||
buffer_skip(r, strlen(s) + 1);
|
||||
return s;
|
||||
}
|
||||
|
||||
static void read_position(struct buffer *r, vec3 v)
|
||||
{
|
||||
// left->right handed system
|
||||
v[0] = buffer_read_float(r) * METERS_PER_INCH;
|
||||
v[1] = buffer_read_float(r) * METERS_PER_INCH;
|
||||
v[2] = -buffer_read_float(r) * METERS_PER_INCH;
|
||||
}
|
||||
|
||||
static void read_direction(struct buffer *r, vec3 v)
|
||||
{
|
||||
// left->right handed system
|
||||
v[0] = buffer_read_float(r);
|
||||
v[1] = buffer_read_float(r);
|
||||
v[2] = -buffer_read_float(r);
|
||||
}
|
||||
|
||||
static void read_quaternion(struct buffer *r, versor q)
|
||||
{
|
||||
// left->right handed system
|
||||
float w = buffer_read_float(r);
|
||||
float x = -buffer_read_float(r);
|
||||
float y = -buffer_read_float(r);
|
||||
float z = buffer_read_float(r);
|
||||
glm_quat_init(q, x, y, z, w);
|
||||
glm_quat_normalize(q);
|
||||
}
|
||||
|
||||
static uint32_t parse_material_attributes(const char *name)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
if (strstr(name, "(sprite)"))
|
||||
flags |= MATERIAL_SPRITE;
|
||||
if (strstr(name, "(alpha)"))
|
||||
flags |= MATERIAL_ALPHA;
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void parse_textures(struct buffer *r, int pol_version, struct pol_material *m)
|
||||
{
|
||||
glm_vec2_one(m->uv_tiling);
|
||||
int nr_textures = buffer_read_int32(r);
|
||||
for (int i = 0; i < nr_textures; i++) {
|
||||
char *filename = read_cstring(r);
|
||||
int type = buffer_read_int32(r);
|
||||
vec2 uv_tiling = { 1.0f, 1.0f };
|
||||
if (pol_version >= 3) {
|
||||
uv_tiling[0] = buffer_read_float(r);
|
||||
uv_tiling[1] = buffer_read_float(r);
|
||||
}
|
||||
if ((unsigned)type < MAX_TEXTURE_TYPE) {
|
||||
m->textures[type] = filename;
|
||||
if (type == COLOR_MAP)
|
||||
glm_vec2_copy(uv_tiling, m->uv_tiling);
|
||||
} else {
|
||||
WARNING("invalid texture type %d", type);
|
||||
free(filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy_material(struct pol_material *m)
|
||||
{
|
||||
free(m->name);
|
||||
for (int i = 0; i < MAX_TEXTURE_TYPE; i++)
|
||||
free(m->textures[i]);
|
||||
}
|
||||
|
||||
static void destroy_material_group(struct pol_material_group *mg)
|
||||
{
|
||||
destroy_material(&mg->m);
|
||||
if (mg->children) {
|
||||
for (uint32_t i = 0; i < mg->nr_children; i++)
|
||||
destroy_material_group(&mg->children[i]);
|
||||
free(mg->children);
|
||||
}
|
||||
}
|
||||
|
||||
// Material tree layout:
|
||||
// level 0: top-level material per pol->materials[]. Either a textured leaf
|
||||
// or a group containing sub-materials, never both.
|
||||
// level 1: sub-material. Either a textured leaf or, in v4 only, a "blend
|
||||
// group" (is_group=1) with exactly 2 leaf children.
|
||||
// level 2: leaf children of a v4 blend group; cannot themselves be a group.
|
||||
static void parse_material_group(struct buffer *r, int pol_version,
|
||||
struct pol_material_group *mg, int level)
|
||||
{
|
||||
mg->m.name = read_cstring(r);
|
||||
mg->m.flags = parse_material_attributes(mg->m.name);
|
||||
|
||||
bool is_group = false;
|
||||
if (pol_version >= 4)
|
||||
is_group = buffer_read_int32(r);
|
||||
|
||||
if (is_group) {
|
||||
if (level >= 2)
|
||||
ERROR("material group nesting too deep");
|
||||
uint32_t uk = buffer_read_int32(r);
|
||||
if (uk != 0)
|
||||
ERROR("unexpected nonzero value in material group: %u", uk);
|
||||
|
||||
mg->nr_children = buffer_read_int32(r);
|
||||
if (level == 1 && mg->nr_children != 2)
|
||||
ERROR("blend group must have 2 children, got %u", mg->nr_children);
|
||||
} else {
|
||||
parse_textures(r, pol_version, &mg->m);
|
||||
|
||||
if (pol_version >= 4) {
|
||||
uint32_t uk = buffer_read_int32(r);
|
||||
if (uk != 0)
|
||||
ERROR("unexpected nonzero value in material leaf: %u", uk);
|
||||
} else if (level == 0) {
|
||||
mg->nr_children = buffer_read_int32(r);
|
||||
}
|
||||
}
|
||||
if (mg->nr_children > 0) {
|
||||
mg->children = xcalloc(mg->nr_children, sizeof(struct pol_material_group));
|
||||
for (uint32_t i = 0; i < mg->nr_children; i++)
|
||||
parse_material_group(r, pol_version, &mg->children[i], level + 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_vertex(struct buffer *r, int pol_version, struct pol_vertex *v)
|
||||
{
|
||||
read_position(r, v->pos);
|
||||
v->nr_weights = pol_version == 1 ? buffer_read_int32(r) : buffer_read_u16(r);
|
||||
if (v->nr_weights) {
|
||||
v->weights = xcalloc(v->nr_weights, sizeof(struct pol_bone_weight));
|
||||
for (uint32_t i = 0; i < v->nr_weights; i++) {
|
||||
v->weights[i].bone = pol_version == 1 ? buffer_read_int32(r) : buffer_read_u16(r);
|
||||
v->weights[i].weight = buffer_read_float(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy_vertex(struct pol_vertex *v)
|
||||
{
|
||||
if (v->weights)
|
||||
free(v->weights);
|
||||
}
|
||||
|
||||
static void parse_triangle(struct buffer *r, struct pol_mesh *mesh, int triangle_index, const struct pol_material_group *mg)
|
||||
{
|
||||
struct pol_triangle *t = &mesh->triangles[triangle_index];
|
||||
t->vert_index[0] = buffer_read_int32(r);
|
||||
t->vert_index[1] = buffer_read_int32(r);
|
||||
t->vert_index[2] = buffer_read_int32(r);
|
||||
t->uv_index[0] = buffer_read_int32(r);
|
||||
t->uv_index[1] = buffer_read_int32(r);
|
||||
t->uv_index[2] = buffer_read_int32(r);
|
||||
if (mesh->light_uvs) {
|
||||
t->light_uv_index[0] = buffer_read_int32(r) - mesh->nr_uvs;
|
||||
t->light_uv_index[1] = buffer_read_int32(r) - mesh->nr_uvs;
|
||||
t->light_uv_index[2] = buffer_read_int32(r) - mesh->nr_uvs;
|
||||
}
|
||||
if (mesh->blend_uvs) {
|
||||
t->blend_uv_index[0] = buffer_read_int32(r) - mesh->nr_uvs - mesh->nr_light_uvs;
|
||||
t->blend_uv_index[1] = buffer_read_int32(r) - mesh->nr_uvs - mesh->nr_light_uvs;
|
||||
t->blend_uv_index[2] = buffer_read_int32(r) - mesh->nr_uvs - mesh->nr_light_uvs;
|
||||
}
|
||||
|
||||
t->color_index[0] = buffer_read_int32(r);
|
||||
t->color_index[1] = buffer_read_int32(r);
|
||||
t->color_index[2] = buffer_read_int32(r);
|
||||
if (mesh->alphas) {
|
||||
t->alpha_index[0] = buffer_read_int32(r);
|
||||
t->alpha_index[1] = buffer_read_int32(r);
|
||||
t->alpha_index[2] = buffer_read_int32(r);
|
||||
}
|
||||
if (mesh->blend_weights) {
|
||||
t->blend_weight_index[0] = buffer_read_int32(r);
|
||||
t->blend_weight_index[1] = buffer_read_int32(r);
|
||||
t->blend_weight_index[2] = buffer_read_int32(r);
|
||||
}
|
||||
|
||||
read_direction(r, t->normals[0]);
|
||||
read_direction(r, t->normals[1]);
|
||||
read_direction(r, t->normals[2]);
|
||||
t->material_group_index = buffer_read_int32(r);
|
||||
if (t->material_group_index >= mg->nr_children)
|
||||
t->material_group_index = 0;
|
||||
}
|
||||
|
||||
static uint32_t parse_mesh_attributes(const char *name)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
if (strstr(name, "(nolighting)"))
|
||||
flags |= MESH_NOLIGHTING;
|
||||
if (strstr(name, "(nomakeshadow)"))
|
||||
flags |= MESH_NOMAKESHADOW;
|
||||
if (strstr(name, "(env)"))
|
||||
flags |= MESH_ENVMAP;
|
||||
if (strstr(name, "(both)"))
|
||||
flags |= MESH_BOTH;
|
||||
if (strstr(name, "(sprite)"))
|
||||
flags |= MESH_SPRITE;
|
||||
if (strstr(name, "(alpha)"))
|
||||
flags |= MESH_ALPHA;
|
||||
return flags;
|
||||
}
|
||||
|
||||
static struct pol_mesh *parse_mesh(struct buffer *r, const struct pol *pol)
|
||||
{
|
||||
int type = buffer_read_int32(r);
|
||||
if (type != 0) {
|
||||
if (type != -1)
|
||||
WARNING("unknown mesh type: %d", type);
|
||||
return NULL;
|
||||
}
|
||||
struct pol_mesh *mesh = xcalloc(1, sizeof(struct pol_mesh));
|
||||
mesh->name = read_cstring(r);
|
||||
mesh->flags = parse_mesh_attributes(mesh->name);
|
||||
mesh->material = buffer_read_int32(r);
|
||||
if (mesh->material >= pol->nr_materials) {
|
||||
WARNING("material index out of range (%d >= %d)", mesh->material, pol->nr_materials);
|
||||
mesh->material = 0;
|
||||
}
|
||||
|
||||
mesh->nr_vertices = buffer_read_int32(r);
|
||||
mesh->vertices = xcalloc(mesh->nr_vertices, sizeof(struct pol_vertex));
|
||||
for (uint32_t i = 0; i < mesh->nr_vertices; i++) {
|
||||
parse_vertex(r, pol->version, &mesh->vertices[i]);
|
||||
}
|
||||
|
||||
mesh->nr_uvs = buffer_read_int32(r);
|
||||
mesh->uvs = xcalloc(mesh->nr_uvs, sizeof(vec2));
|
||||
for (uint32_t i = 0; i < mesh->nr_uvs; i++) {
|
||||
mesh->uvs[i][0] = buffer_read_float(r);
|
||||
mesh->uvs[i][1] = buffer_read_float(r);
|
||||
}
|
||||
|
||||
mesh->nr_light_uvs = buffer_read_int32(r);
|
||||
if (mesh->nr_light_uvs > 0) {
|
||||
mesh->flags |= MESH_HAS_LIGHT_UV;
|
||||
mesh->light_uvs = xcalloc(mesh->nr_light_uvs, sizeof(vec2));
|
||||
for (uint32_t i = 0; i < mesh->nr_light_uvs; i++) {
|
||||
mesh->light_uvs[i][0] = buffer_read_float(r);
|
||||
mesh->light_uvs[i][1] = buffer_read_float(r);
|
||||
}
|
||||
}
|
||||
|
||||
if (pol->version >= 4) {
|
||||
mesh->nr_blend_uvs = buffer_read_int32(r);
|
||||
if (mesh->nr_blend_uvs > 0) {
|
||||
mesh->blend_uvs = xcalloc(mesh->nr_blend_uvs, sizeof(vec2));
|
||||
for (uint32_t i = 0; i < mesh->nr_blend_uvs; i++) {
|
||||
mesh->blend_uvs[i][0] = buffer_read_float(r);
|
||||
mesh->blend_uvs[i][1] = buffer_read_float(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mesh->nr_colors = buffer_read_int32(r);
|
||||
if (mesh->nr_colors > 0) {
|
||||
mesh->colors = xcalloc(mesh->nr_colors, sizeof(vec3));
|
||||
for (uint32_t i = 0; i < mesh->nr_colors; i++) {
|
||||
if (pol->version == 1) {
|
||||
mesh->colors[i][0] = buffer_read_float(r);
|
||||
mesh->colors[i][1] = buffer_read_float(r);
|
||||
mesh->colors[i][2] = buffer_read_float(r);
|
||||
} else {
|
||||
mesh->colors[i][0] = buffer_read_u8(r) / 255.f;
|
||||
mesh->colors[i][1] = buffer_read_u8(r) / 255.f;
|
||||
mesh->colors[i][2] = buffer_read_u8(r) / 255.f;
|
||||
buffer_skip(r, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pol->version >= 2) {
|
||||
mesh->nr_alphas = buffer_read_int32(r);
|
||||
if (mesh->nr_alphas > 0) {
|
||||
mesh->alphas = xcalloc(mesh->nr_alphas, sizeof(float));
|
||||
for (uint32_t i = 0; i < mesh->nr_alphas; i++) {
|
||||
mesh->alphas[i] = buffer_read_u8(r) / 255.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pol->version >= 4) {
|
||||
mesh->nr_blend_weights = buffer_read_int32(r);
|
||||
if (mesh->nr_blend_weights > 0) {
|
||||
mesh->blend_weights = xcalloc(mesh->nr_blend_weights, sizeof(float));
|
||||
for (uint32_t i = 0; i < mesh->nr_blend_weights; i++) {
|
||||
mesh->blend_weights[i] = buffer_read_u8(r) / 255.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mesh->nr_triangles = buffer_read_int32(r);
|
||||
mesh->triangles = xcalloc(mesh->nr_triangles, sizeof(struct pol_triangle));
|
||||
for (uint32_t i = 0; i < mesh->nr_triangles; i++) {
|
||||
parse_triangle(r, mesh, i, &pol->materials[mesh->material]);
|
||||
}
|
||||
|
||||
if (pol->version == 1) {
|
||||
if (buffer_read_int32(r) != 1)
|
||||
WARNING("unexpected mesh footer");
|
||||
if (buffer_read_int32(r) != 0)
|
||||
WARNING("unexpected mesh footer");
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
static void free_mesh(struct pol_mesh *mesh)
|
||||
{
|
||||
if (!mesh)
|
||||
return;
|
||||
free(mesh->name);
|
||||
for (uint32_t i = 0; i < mesh->nr_vertices; i++)
|
||||
destroy_vertex(&mesh->vertices[i]);
|
||||
free(mesh->vertices);
|
||||
free(mesh->uvs);
|
||||
free(mesh->light_uvs);
|
||||
free(mesh->blend_uvs);
|
||||
free(mesh->colors);
|
||||
free(mesh->alphas);
|
||||
free(mesh->blend_weights);
|
||||
free(mesh->triangles);
|
||||
free(mesh);
|
||||
}
|
||||
|
||||
static void parse_bone(struct buffer *r, struct pol_bone *bone)
|
||||
{
|
||||
bone->name = read_cstring(r);
|
||||
bone->id = buffer_read_int32(r);
|
||||
bone->parent = buffer_read_int32(r);
|
||||
read_position(r, bone->pos);
|
||||
read_quaternion(r, bone->rotq);
|
||||
}
|
||||
|
||||
static void destroy_bone(struct pol_bone *bone)
|
||||
{
|
||||
free(bone->name);
|
||||
}
|
||||
|
||||
struct pol *pol_parse(uint8_t *data, size_t size)
|
||||
{
|
||||
struct buffer r;
|
||||
buffer_init(&r, data, size);
|
||||
if (memcmp(buffer_strdata(&r), "POL\0", 4))
|
||||
return NULL;
|
||||
buffer_skip(&r, 4);
|
||||
|
||||
struct pol *pol = xcalloc(1, sizeof(struct pol));
|
||||
pol->version = buffer_read_int32(&r);
|
||||
if (pol->version != 1 && pol->version != 2 && pol->version != 4) {
|
||||
WARNING("unknown POL version: %d", pol->version);
|
||||
free(pol);
|
||||
return NULL;
|
||||
}
|
||||
pol->nr_materials = buffer_read_int32(&r);
|
||||
pol->materials = xcalloc(pol->nr_materials, sizeof(struct pol_material_group));
|
||||
for (uint32_t i = 0; i < pol->nr_materials; i++) {
|
||||
parse_material_group(&r, pol->version, &pol->materials[i], 0);
|
||||
}
|
||||
|
||||
pol->nr_meshes = buffer_read_int32(&r);
|
||||
pol->meshes = xcalloc(pol->nr_meshes, sizeof(struct pol_mesh *));
|
||||
for (uint32_t i = 0; i < pol->nr_meshes; i++) {
|
||||
pol->meshes[i] = parse_mesh(&r, pol);
|
||||
}
|
||||
|
||||
pol->nr_bones = buffer_read_int32(&r);
|
||||
if (pol->nr_bones) {
|
||||
pol->bones = xcalloc(pol->nr_bones, sizeof(struct pol_bone));
|
||||
for (uint32_t i = 0; i < pol->nr_bones; i++) {
|
||||
parse_bone(&r, &pol->bones[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer_remaining(&r) != 0) {
|
||||
WARNING("extra data at end");
|
||||
}
|
||||
return pol;
|
||||
}
|
||||
|
||||
void pol_free(struct pol *pol)
|
||||
{
|
||||
for (uint32_t i = 0; i < pol->nr_materials; i++)
|
||||
destroy_material_group(&pol->materials[i]);
|
||||
free(pol->materials);
|
||||
for (uint32_t i = 0; i < pol->nr_meshes; i++)
|
||||
free_mesh(pol->meshes[i]);
|
||||
free(pol->meshes);
|
||||
for (uint32_t i = 0; i < pol->nr_bones; i++)
|
||||
destroy_bone(&pol->bones[i]);
|
||||
free(pol->bones);
|
||||
free(pol);
|
||||
}
|
||||
|
||||
void pol_compute_aabb(struct pol *pol, vec3 dest[2])
|
||||
{
|
||||
vec3 aabb[2];
|
||||
glm_aabb_invalidate(aabb);
|
||||
for (uint32_t i = 0; i < pol->nr_meshes; i++) {
|
||||
struct pol_mesh *mesh = pol->meshes[i];
|
||||
if (!mesh)
|
||||
continue;
|
||||
for (uint32_t j = 0; j < mesh->nr_vertices; j++) {
|
||||
glm_vec3_minv(mesh->vertices[j].pos, aabb[0], aabb[0]);
|
||||
glm_vec3_maxv(mesh->vertices[j].pos, aabb[1], aabb[1]);
|
||||
}
|
||||
}
|
||||
glm_vec3_copy(aabb[0], dest[0]);
|
||||
glm_vec3_copy(aabb[1], dest[1]);
|
||||
}
|
||||
|
||||
struct pol_bone *pol_find_bone(struct pol *pol, uint32_t id)
|
||||
{
|
||||
for (uint32_t i = 0; i < pol->nr_bones; i++) {
|
||||
if (pol->bones[i].id == id)
|
||||
return &pol->bones[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mot *mot_parse(uint8_t *data, size_t size, const char *name)
|
||||
{
|
||||
struct buffer r;
|
||||
buffer_init(&r, data, size);
|
||||
if (memcmp(buffer_strdata(&r), "MOT\0", 4))
|
||||
return NULL;
|
||||
buffer_skip(&r, 4);
|
||||
uint32_t version = buffer_read_int32(&r);
|
||||
if (version != 0) {
|
||||
WARNING("unknown MOT version: %d", version);
|
||||
return NULL;
|
||||
}
|
||||
uint32_t nr_frames = buffer_read_int32(&r);
|
||||
uint32_t nr_bones = buffer_read_int32(&r);
|
||||
|
||||
struct mot *mot = xcalloc(1, sizeof(struct mot) + nr_bones * sizeof(struct mot_bone *));
|
||||
mot->name = strdup(name);
|
||||
mot->nr_frames = nr_frames;
|
||||
mot->nr_bones = nr_bones;
|
||||
for (uint32_t i = 0; i < nr_bones; i++) {
|
||||
struct mot_bone *m = xcalloc(1, sizeof(struct mot_bone) + sizeof(struct mot_frame) * nr_frames);
|
||||
m->name = read_cstring(&r);
|
||||
m->id = buffer_read_int32(&r);
|
||||
m->parent = buffer_read_int32(&r);
|
||||
for (uint32_t j = 0; j < nr_frames; j++) {
|
||||
read_position(&r, m->frames[j].pos);
|
||||
read_quaternion(&r, m->frames[j].rotq);
|
||||
buffer_skip(&r, 16); // another quaternion?
|
||||
}
|
||||
mot->motions[i] = m;
|
||||
}
|
||||
if (buffer_remaining(&r) != 0) {
|
||||
WARNING("extra data at end");
|
||||
}
|
||||
return mot;
|
||||
}
|
||||
|
||||
void mot_free(struct mot *mot)
|
||||
{
|
||||
free(mot->name);
|
||||
for (uint32_t i = 0; i < mot->nr_bones; i++) {
|
||||
free(mot->motions[i]->name);
|
||||
free(mot->motions[i]);
|
||||
}
|
||||
if (mot->texture_indices) {
|
||||
free(mot->texture_indices);
|
||||
}
|
||||
mpr_free(mot->mpr);
|
||||
free(mot);
|
||||
}
|
||||
|
||||
struct amt *amt_parse(uint8_t *data, size_t size)
|
||||
{
|
||||
struct buffer r;
|
||||
buffer_init(&r, data, size);
|
||||
if (memcmp(buffer_strdata(&r), "AMT\0", 4))
|
||||
return NULL;
|
||||
buffer_skip(&r, 4);
|
||||
|
||||
uint32_t version = buffer_read_int32(&r);
|
||||
int nr_fields;
|
||||
switch (version) {
|
||||
case 1: nr_fields = 3; break;
|
||||
case 2: nr_fields = 5; break;
|
||||
case 3: nr_fields = 6; break;
|
||||
case 4: nr_fields = 7; break;
|
||||
case 5: nr_fields = 11; break;
|
||||
case 6: nr_fields = 15; break;
|
||||
default:
|
||||
WARNING("unknown MOT version: %d", version);
|
||||
return NULL;
|
||||
}
|
||||
int nr_materials = buffer_read_int32(&r);
|
||||
|
||||
struct amt *amt = xmalloc(sizeof(struct amt) + sizeof(struct amt_material *) * nr_materials);
|
||||
amt->version = version;
|
||||
amt->nr_materials = nr_materials;
|
||||
for (int i = 0; i < nr_materials; i++) {
|
||||
struct amt_material *m = amt->materials[i] =
|
||||
xmalloc(sizeof(struct amt_material) + sizeof(float) * nr_fields);
|
||||
m->name = read_cstring(&r);
|
||||
for (int j = 0; j < nr_fields; j++)
|
||||
m->fields[j] = buffer_read_float(&r);
|
||||
}
|
||||
|
||||
if (buffer_remaining(&r) != 0) {
|
||||
WARNING("extra data at end");
|
||||
}
|
||||
return amt;
|
||||
}
|
||||
|
||||
void amt_free(struct amt *amt)
|
||||
{
|
||||
for (int i = 0; i < amt->nr_materials; i++) {
|
||||
free(amt->materials[i]->name);
|
||||
free(amt->materials[i]);
|
||||
}
|
||||
free(amt);
|
||||
}
|
||||
|
||||
struct amt_material *amt_find_material(struct amt *amt, const char *name)
|
||||
{
|
||||
for (int i = 0; i < amt->nr_materials; i++) {
|
||||
if (!strcmp(amt->materials[i]->name, name))
|
||||
return amt->materials[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void opr_load(uint8_t *data, size_t size, struct pol *pol)
|
||||
{
|
||||
bool *selected = xmalloc(pol->nr_meshes * sizeof(bool));
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
selected[i] = !!pol->meshes[i];
|
||||
}
|
||||
while (size > 0) {
|
||||
char line[200];
|
||||
uint8_t *nl = memchr(data, '\n', size);
|
||||
if (nl)
|
||||
nl++;
|
||||
else
|
||||
nl = data + size;
|
||||
int copylen = min(nl - data, sizeof(line) - 1);
|
||||
memcpy(line, (char *)data, copylen);
|
||||
line[copylen] = '\0';
|
||||
size -= nl - data;
|
||||
data = nl;
|
||||
|
||||
char s[200];
|
||||
int i1, i2, i3;
|
||||
float f1, f2, f3;
|
||||
if (sscanf(line, "Mesh = \"%[^\"]\"", s) == 1) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
selected[i] = pol->meshes[i] && !strcmp(pol->meshes[i]->name, s);
|
||||
}
|
||||
} else if (sscanf(line, "MeshPart = \"%[^\"]\"", s) == 1) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
selected[i] = pol->meshes[i] && strstr(pol->meshes[i]->name, s);
|
||||
}
|
||||
} else if (sscanf(line, "BlendMode = %s", s) == 1) {
|
||||
if (!strcmp(s, "Add")) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_BLEND_ADDITIVE;
|
||||
}
|
||||
} else {
|
||||
WARNING("unknown BlendMode: %s", s);
|
||||
}
|
||||
} else if (sscanf(line, "Edge = %d", &i1) == 1) {
|
||||
if (i1 == 0) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_NO_EDGE;
|
||||
}
|
||||
} else {
|
||||
WARNING("invalid Edge value: %d", i1);
|
||||
}
|
||||
} else if (sscanf(line, "EdgeColor = ( %d , %d , %d )", &i1, &i2, &i3) == 3) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->edge_color = COLOR(i1, i2, i3, 255);
|
||||
}
|
||||
} else if (sscanf(line, "EdgeSize = %f", &f1) == 1) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->edge_size = f1;
|
||||
}
|
||||
} else if (sscanf(line, "HeightDetection = %s", s) == 1) {
|
||||
if (!strcmp(s, "true")) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_HEIGHT_DETECTION;
|
||||
}
|
||||
} else if (!strcmp(s, "false")) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_NO_HEIGHT_DETECTION;
|
||||
}
|
||||
} else {
|
||||
WARNING("invalid HeightDetection: %s", s);
|
||||
}
|
||||
} else if (sscanf(line, "DrawShadow = %s", s) == 1) {
|
||||
if (!strcmp(s, "false")) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_NO_DRAWSHADOW;
|
||||
}
|
||||
} else if (strcmp(s, "true")) {
|
||||
WARNING("invalid DrawShadow: %s", s);
|
||||
}
|
||||
} else if (sscanf(line, "ZWrite = %s", s) == 1) {
|
||||
if (!strcmp(s, "false")) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_NO_ZWRITE;
|
||||
}
|
||||
} else if (strcmp(s, "true")) {
|
||||
WARNING("invalid ZWrite: %s", s);
|
||||
}
|
||||
} else if (sscanf(line, "UVScroll = ( %f , %f )", &f1, &f2) == 2) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->uv_scroll[0] = f1;
|
||||
pol->meshes[i]->uv_scroll[1] = f2;
|
||||
}
|
||||
} else if (sscanf(line, "SpecularColor = ( %f , %f , %f )", &f1, &f2, &f3) == 3) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_HAS_SPECULAR_COLOR;
|
||||
pol->meshes[i]->specular_color[0] = f1;
|
||||
pol->meshes[i]->specular_color[1] = f2;
|
||||
pol->meshes[i]->specular_color[2] = f3;
|
||||
}
|
||||
} else if (sscanf(line, "SpecularPower = %f", &f1) == 1) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_HAS_SPECULAR_POWER;
|
||||
pol->meshes[i]->specular_power = f1;
|
||||
}
|
||||
} else if (sscanf(line, "ParallaxScale = %f", &f1) == 1) {
|
||||
// parallax mapping is not implemented
|
||||
} else if (sscanf(line, "ReliefScale = %f", &f1) == 1) {
|
||||
// relief mapping is not implemented
|
||||
} else if (sscanf(line, "MeshCombinable = %s", s) == 1) {
|
||||
if (strcmp(s, "true") && strcmp(s, "false"))
|
||||
WARNING("invalid MeshCombinable: %s", s);
|
||||
} else if (strchr(line, '=')) {
|
||||
WARNING("unknown field: %s", line);
|
||||
}
|
||||
}
|
||||
free(selected);
|
||||
}
|
||||
|
||||
void txa_load(uint8_t *data, size_t size, struct mot *mot)
|
||||
{
|
||||
uint32_t count = 0;
|
||||
uint32_t *indices = xmalloc(100 * sizeof(uint32_t));
|
||||
const char *p = (const char *)data;
|
||||
while (p < (const char *)data + size) {
|
||||
if (isdigit(*p)) {
|
||||
uint32_t n = 0;
|
||||
while (p < (const char *)data + size && isdigit(*p)) {
|
||||
n = n * 10 + (*p - '0');
|
||||
p++;
|
||||
}
|
||||
indices[count++] = n;
|
||||
if (count % 100 == 0) {
|
||||
indices = xrealloc(indices, (count + 100) * sizeof(uint32_t));
|
||||
}
|
||||
} else if (isspace(*p)) {
|
||||
p++;
|
||||
} else {
|
||||
WARNING("invalid character in txa file: '%c'", *p);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
mot->nr_texture_indices = count;
|
||||
mot->texture_indices = indices;
|
||||
}
|
||||
|
||||
void lit_reset(float *out)
|
||||
{
|
||||
static const float lit_param_defaults[RE_NR_LIGHT_PARAMS] = {
|
||||
[SEAL_LP_TONEMAP_EXPOSURE_BIAS] = 1.0f,
|
||||
[SEAL_LP_TONEMAP_WHITE_POINT] = 1.0f,
|
||||
[SEAL_LP_TONEMAP_A] = 0.22f,
|
||||
[SEAL_LP_TONEMAP_B] = 0.30f,
|
||||
[SEAL_LP_TONEMAP_C] = 0.10f,
|
||||
[SEAL_LP_TONEMAP_D] = 0.20f,
|
||||
[SEAL_LP_TONEMAP_E] = 0.01f,
|
||||
[SEAL_LP_TONEMAP_F] = 0.30f,
|
||||
[SEAL_LP_HEMI_VEC + 0] = -1.0f,
|
||||
[SEAL_LP_HEMI_VEC + 1] = -1.0f,
|
||||
[SEAL_LP_HEMI_VEC + 2] = 1.0f,
|
||||
[SEAL_LP_HEMI_SKY_COLOR + 0] = 1.2f,
|
||||
[SEAL_LP_HEMI_SKY_COLOR + 1] = 1.2f,
|
||||
[SEAL_LP_HEMI_SKY_COLOR + 2] = 1.2f,
|
||||
[SEAL_LP_HEMI_MID_COLOR + 0] = 0.5f,
|
||||
[SEAL_LP_HEMI_MID_COLOR + 1] = 0.5f,
|
||||
[SEAL_LP_HEMI_MID_COLOR + 2] = 0.5f,
|
||||
[SEAL_LP_HEMI_GROUND_COLOR + 0] = 0.3f,
|
||||
[SEAL_LP_HEMI_GROUND_COLOR + 1] = 0.3f,
|
||||
[SEAL_LP_HEMI_GROUND_COLOR + 2] = 0.3f,
|
||||
[SEAL_LP_LS_BETA_R] = 0.25f,
|
||||
[SEAL_LP_LS_BETA_M] = 1.25f,
|
||||
[SEAL_LP_LS_G] = 0.05f,
|
||||
[SEAL_LP_LS_DISTANCE] = 140.0f,
|
||||
[SEAL_LP_LS_LIGHT_VEC + 0] = -1.0f,
|
||||
[SEAL_LP_LS_LIGHT_VEC + 1] = -1.0f,
|
||||
[SEAL_LP_LS_LIGHT_VEC + 2] = 1.0f,
|
||||
[SEAL_LP_LS_LIGHT_COLOR + 0] = 1.0f,
|
||||
[SEAL_LP_LS_LIGHT_COLOR + 1] = 1.0f,
|
||||
[SEAL_LP_LS_LIGHT_COLOR + 2] = 1.0f,
|
||||
[SEAL_LP_LS_SUN_COLOR + 0] = 0.5f,
|
||||
[SEAL_LP_LS_SUN_COLOR + 1] = 0.5f,
|
||||
[SEAL_LP_LS_SUN_COLOR + 2] = 0.5f,
|
||||
};
|
||||
memcpy(out, lit_param_defaults, sizeof(lit_param_defaults));
|
||||
}
|
||||
|
||||
bool lit_parse(uint8_t *data, size_t size, float *out)
|
||||
{
|
||||
if (size != 8 + RE_NR_LIGHT_PARAMS * 4 || memcmp(data, "LITP", 4) != 0) {
|
||||
WARNING("invalid .lit file");
|
||||
return false;
|
||||
}
|
||||
struct buffer r;
|
||||
buffer_init(&r, data, size);
|
||||
buffer_skip(&r, 4); // "LITP"
|
||||
int32_t version = buffer_read_int32(&r);
|
||||
if (version != 0) {
|
||||
WARNING("unsupported .lit version %d", version);
|
||||
return false;
|
||||
}
|
||||
lit_reset(out);
|
||||
for (int i = 0; i < RE_NR_LIGHT_PARAMS; i++)
|
||||
out[i] = buffer_read_float(&r);
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,995 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/aar.h"
|
||||
#include "system4/cg.h"
|
||||
#include "system4/hashtable.h"
|
||||
#include "system4/utfsjis.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
|
||||
static inline float randf(void)
|
||||
{
|
||||
return rand() / (float)RAND_MAX;
|
||||
}
|
||||
|
||||
static void parse_error(const char *msg, char *input)
|
||||
{
|
||||
char *eol = strchr(input, '\n');
|
||||
*eol = '\0';
|
||||
WARNING("Parse error: %s Near: \"%s\"", msg, input);
|
||||
}
|
||||
|
||||
static enum particle_type parse_particle_type(const char *s)
|
||||
{
|
||||
if (!strcmp(s, "ビルボード"))
|
||||
return PARTICLE_TYPE_BILLBOARD;
|
||||
else if (!strcmp(s, "ポリゴンオブジェクト"))
|
||||
return PARTICLE_TYPE_POLYGON_OBJECT;
|
||||
else if (!strcmp(s, "剣ブラー"))
|
||||
return PARTICLE_TYPE_SWORD_BLUR;
|
||||
else if (!strcmp(s, "カメラ振動"))
|
||||
return PARTICLE_TYPE_CAMERA_QUAKE;
|
||||
WARNING("unknown object type \"%s\"", s);
|
||||
return (enum particle_type)-1;
|
||||
}
|
||||
|
||||
static enum particle_move_type parse_particle_move_type(const char *s)
|
||||
{
|
||||
if (!strcmp(s, "固定"))
|
||||
return PARTICLE_MOVE_FIXED;
|
||||
else if (!strcmp(s, "直線"))
|
||||
return PARTICLE_MOVE_LINEAR;
|
||||
else if (!strcmp(s, "放射"))
|
||||
return PARTICLE_MOVE_EMITTER;
|
||||
WARNING("unknown move type \"%s\"", s);
|
||||
return (enum particle_move_type)-1;
|
||||
}
|
||||
|
||||
static enum particle_blend_type parse_particle_blend_type(const char *s)
|
||||
{
|
||||
if (!strcmp(s, "標準"))
|
||||
return PARTICLE_BLEND_NORMAL;
|
||||
else if (!strcmp(s, "加算"))
|
||||
return PARTICLE_BLEND_ADDITIVE;
|
||||
WARNING("unknown blend type \"%s\"", s);
|
||||
return (enum particle_blend_type)-1;
|
||||
}
|
||||
|
||||
static enum particle_frame_ref_type parse_particle_frame_ref_type(const char *s, int *param)
|
||||
{
|
||||
if (!strcmp(s, "エフェクト"))
|
||||
return PARTICLE_FRAME_REF_EFFECT;
|
||||
else if (sscanf(s, "ターゲット[%d]", param) == 1)
|
||||
return PARTICLE_FRAME_REF_TARGET;
|
||||
WARNING("unknown frame ref type \"%s\"", s);
|
||||
return (enum particle_frame_ref_type)-1;
|
||||
}
|
||||
|
||||
static char *parse_quoted_string(char **ptext)
|
||||
{
|
||||
char *text = *ptext;
|
||||
|
||||
if (*text++ != '"') {
|
||||
parse_error("quoted string expected.", text);
|
||||
return NULL;
|
||||
}
|
||||
char *p = strchr(text, '"');
|
||||
if (!p) {
|
||||
parse_error("unfinished string.", text);
|
||||
return NULL;
|
||||
}
|
||||
*p = '\0';
|
||||
*ptext = p + 1;
|
||||
return utf2sjis(text, 0);
|
||||
}
|
||||
|
||||
#define MATCH(fmt, ...) (len = 0, sscanf(text, fmt " %n", ##__VA_ARGS__, &len), text += len, len)
|
||||
#define STRING_BUF_SIZE 64
|
||||
#define KEYWORD "%63s"
|
||||
#define QUOTED_STRING "\"%63[^\"]\""
|
||||
|
||||
static float *parse_float_list(char **ptext, int *nr)
|
||||
{
|
||||
char *text = *ptext;
|
||||
int len;
|
||||
float f;
|
||||
|
||||
if (!MATCH("%f", &f)) {
|
||||
parse_error("float expected.", text);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int cap = 16;
|
||||
float *floats = xmalloc(cap * sizeof(float));
|
||||
floats[0] = f;
|
||||
*nr = 1;
|
||||
while (MATCH(" , %f", &f)) {
|
||||
if (*nr == cap) {
|
||||
cap *= 2;
|
||||
floats = xrealloc(floats, cap * sizeof(float));
|
||||
}
|
||||
floats[(*nr)++] = f;
|
||||
}
|
||||
*ptext = text;
|
||||
return floats;
|
||||
}
|
||||
|
||||
static int *parse_int_list(char **ptext, int *nr)
|
||||
{
|
||||
char *text = *ptext;
|
||||
int len;
|
||||
int n;
|
||||
|
||||
if (!MATCH("%d", &n)) {
|
||||
parse_error("int expected.", text);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int cap = 16;
|
||||
int *ints = xmalloc(cap * sizeof(int));
|
||||
ints[0] = n;
|
||||
*nr = 1;
|
||||
while (MATCH(" , %d", &n)) {
|
||||
if (*nr == cap) {
|
||||
cap *= 2;
|
||||
ints = xrealloc(ints, cap * sizeof(int));
|
||||
}
|
||||
ints[(*nr)++] = n;
|
||||
}
|
||||
*ptext = text;
|
||||
return ints;
|
||||
}
|
||||
|
||||
static char **parse_qstr_list(char **ptext, int *nr, bool ignore_empty)
|
||||
{
|
||||
char *text = *ptext;
|
||||
int len;
|
||||
|
||||
int cap = 16;
|
||||
char **strs = xmalloc(cap * sizeof(char *));
|
||||
*nr = 0;
|
||||
do {
|
||||
if (*nr == cap) {
|
||||
cap *= 2;
|
||||
strs = xrealloc(strs, cap * sizeof(char *));
|
||||
}
|
||||
char *s = parse_quoted_string(&text);
|
||||
if (!s) {
|
||||
for (int i = 0; i < *nr; i++)
|
||||
free(strs[i]);
|
||||
free(strs);
|
||||
return NULL;
|
||||
}
|
||||
if (s[0] == '\0' && ignore_empty)
|
||||
free(s);
|
||||
else
|
||||
strs[(*nr)++] = s;
|
||||
} while (MATCH(" , "));
|
||||
*ptext = text;
|
||||
return strs;
|
||||
}
|
||||
|
||||
static struct particle_position *parse_position(char **ptext)
|
||||
{
|
||||
char *text = *ptext;
|
||||
int len;
|
||||
|
||||
struct particle_position *epos = xcalloc(1, sizeof(struct particle_position));
|
||||
|
||||
int n;
|
||||
float x, y, z;
|
||||
char string_buf[STRING_BUF_SIZE];
|
||||
for (int i = 0; i < NR_PARTICLE_POSITION_UNITS; i++) {
|
||||
struct particle_position_unit *unit = &epos->units[i];
|
||||
if (MATCH("なし")) {
|
||||
unit->type = PARTICLE_POS_UNIT_NONE;
|
||||
} else if (MATCH("絶対位置( %f , %f , %f )", &x, &y, &z)) {
|
||||
unit->type = PARTICLE_POS_UNIT_ABSOLUTE;
|
||||
unit->absolute[0] = x;
|
||||
unit->absolute[1] = y;
|
||||
unit->absolute[2] = -z;
|
||||
} else if (MATCH("ボーン[ %d , "QUOTED_STRING" , %f , %f , %f ]", &n, string_buf, &x, &y, &z) ||
|
||||
MATCH("ボーン[ %d , \"\" , %f , %f , %f ]", &n, &x, &y, &z)) {
|
||||
unit->type = PARTICLE_POS_UNIT_BONE;
|
||||
unit->bone.target_index = n;
|
||||
unit->bone.name = utf2sjis(string_buf, 0);
|
||||
unit->bone.offset[0] = x;
|
||||
unit->bone.offset[1] = y;
|
||||
unit->bone.offset[2] = -z;
|
||||
} else if (MATCH("ターゲット[ %d , %f , %f , %f ]", &n, &x, &y, &z)) {
|
||||
unit->type = PARTICLE_POS_UNIT_TARGET;
|
||||
unit->target.index = n;
|
||||
unit->target.offset[0] = x;
|
||||
unit->target.offset[1] = y;
|
||||
unit->target.offset[2] = -z;
|
||||
} else if (MATCH("ランダム方向( %f )", &x)) {
|
||||
unit->type = PARTICLE_POS_UNIT_RAND;
|
||||
unit->rand.f = x;
|
||||
} else if (MATCH("ランダム方向Y+( %f )", &x)) {
|
||||
unit->type = PARTICLE_POS_UNIT_RAND_POSITIVE_Y;
|
||||
unit->rand.f = x;
|
||||
} else {
|
||||
parse_error("unknown position unit.", text);
|
||||
free(epos);
|
||||
return NULL;
|
||||
}
|
||||
if (!MATCH(" + "))
|
||||
break;
|
||||
}
|
||||
|
||||
*ptext = text;
|
||||
return epos;
|
||||
}
|
||||
|
||||
static struct pae *parse_pae(char *text)
|
||||
{
|
||||
struct pae *pae = xcalloc(1, sizeof(struct pae));
|
||||
struct pae_object *current_object = NULL;
|
||||
|
||||
for (;;) {
|
||||
// Skip whitespaces and comments
|
||||
for (;;) {
|
||||
while (isspace(*text))
|
||||
text++;
|
||||
if (text[0] == '/' && text[1] == '/') {
|
||||
char *newline = strchr(text, '\n');
|
||||
if (newline)
|
||||
text = newline + 1;
|
||||
else
|
||||
text += strlen(text);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int len;
|
||||
int n, n2;
|
||||
float f, f2;
|
||||
char string_buf[STRING_BUF_SIZE];
|
||||
if (!current_object) {
|
||||
// Toplevel
|
||||
if (!*text) { // EOF
|
||||
return pae;
|
||||
} else if (MATCH("オブジェクト " QUOTED_STRING " = {", string_buf)) {
|
||||
pae->objects = xrealloc_array(pae->objects, pae->nr_objects, pae->nr_objects + 1, sizeof(struct pae_object));
|
||||
current_object = &pae->objects[pae->nr_objects++];
|
||||
current_object->name = utf2sjis(string_buf, 0);
|
||||
// Default values.
|
||||
current_object->move_type = PARTICLE_MOVE_LINEAR;
|
||||
current_object->texture_anime_frame = 1.0;
|
||||
current_object->child_end_slope = 1.0;
|
||||
} else {
|
||||
parse_error("unknown toplevel.", text);
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
// Object context
|
||||
if (!*text) {
|
||||
WARNING("unexpected EOF");
|
||||
return false;
|
||||
} else if (MATCH("}")) {
|
||||
current_object = NULL;
|
||||
} else if (MATCH("種類 = " KEYWORD, string_buf)) {
|
||||
current_object->type = parse_particle_type(string_buf);
|
||||
if (current_object->type < 0)
|
||||
goto err;
|
||||
} else if (MATCH("移動タイプ = " KEYWORD, string_buf)) {
|
||||
current_object->move_type = parse_particle_move_type(string_buf);
|
||||
if (current_object->move_type < 0)
|
||||
goto err;
|
||||
} else if (MATCH("アップベクトルタイプ = %d", &n)) {
|
||||
if (n > PARTICLE_UP_VECTOR_TYPE_MAX) {
|
||||
WARNING("unknown up vector type %d", n);
|
||||
n = PARTICLE_UP_VECTOR_TYPE_MAX;
|
||||
}
|
||||
current_object->up_vector_type = n;
|
||||
} else if (MATCH("移動カーブ = %f", &f)) {
|
||||
current_object->move_curve = f;
|
||||
} else if (MATCH("位置 = ")) {
|
||||
current_object->position[0] = parse_position(&text);
|
||||
if (!current_object->position[0])
|
||||
goto err;
|
||||
if (MATCH(" , ")) {
|
||||
current_object->position[1] = parse_position(&text);
|
||||
if (!current_object->position[1])
|
||||
goto err;
|
||||
}
|
||||
} else if (MATCH("サイズ = %f", &f)) {
|
||||
current_object->size[0] = f;
|
||||
if (MATCH(" , %f", &f2))
|
||||
current_object->size[1] = f2;
|
||||
else
|
||||
current_object->size[1] = f;
|
||||
} else if (MATCH("サイズ2 = ")) {
|
||||
current_object->sizes2 = parse_float_list(&text, ¤t_object->nr_sizes2);
|
||||
if (!current_object->sizes2)
|
||||
goto err;
|
||||
} else if (MATCH("サイズX = ")) {
|
||||
current_object->sizes_x = parse_float_list(&text, ¤t_object->nr_sizes_x);
|
||||
if (!current_object->sizes_x)
|
||||
goto err;
|
||||
} else if (MATCH("サイズY = ")) {
|
||||
current_object->sizes_y = parse_float_list(&text, ¤t_object->nr_sizes_y);
|
||||
if (!current_object->sizes_y)
|
||||
goto err;
|
||||
} else if (MATCH("サイズタイプ = ")) {
|
||||
current_object->size_types = parse_int_list(&text, ¤t_object->nr_size_types);
|
||||
if (!current_object->size_types)
|
||||
goto err;
|
||||
} else if (MATCH("サイズXタイプ = ")) {
|
||||
current_object->size_x_types = parse_int_list(&text, ¤t_object->nr_size_x_types);
|
||||
if (!current_object->size_x_types)
|
||||
goto err;
|
||||
} else if (MATCH("サイズYタイプ = ")) {
|
||||
current_object->size_y_types = parse_int_list(&text, ¤t_object->nr_size_y_types);
|
||||
if (!current_object->size_y_types)
|
||||
goto err;
|
||||
} else if (MATCH("テクスチャ = ")) {
|
||||
current_object->textures = parse_qstr_list(&text, ¤t_object->nr_textures, true);
|
||||
if (!current_object->textures)
|
||||
goto err;
|
||||
} else if (MATCH("ブレンドタイプ = " KEYWORD, string_buf)) {
|
||||
current_object->blend_type = parse_particle_blend_type(string_buf);
|
||||
if (current_object->blend_type < 0)
|
||||
goto err;
|
||||
} else if (MATCH("テクスチャアニメフレーム = %f", &f)) {
|
||||
current_object->texture_anime_frame = f;
|
||||
} else if (MATCH("テクスチャアニメ時間 = %d", &n)) {
|
||||
current_object->texture_anime_time = n;
|
||||
} else if (MATCH("ポリゴン = ")) {
|
||||
current_object->polygon_name = parse_quoted_string(&text);
|
||||
if (!current_object->polygon_name)
|
||||
goto err;
|
||||
} else if (MATCH("フレーム = %d , %d", &n, &n2)) {
|
||||
current_object->begin_frame = n;
|
||||
current_object->end_frame = n2;
|
||||
} else if (MATCH("停止フレーム = %d", &n)) {
|
||||
current_object->stop_frame = n;
|
||||
} else if (MATCH("フレーム参照 = " KEYWORD, string_buf)) {
|
||||
current_object->frame_ref_type = parse_particle_frame_ref_type(string_buf, ¤t_object->frame_ref_param);
|
||||
if (current_object->frame_ref_type < 0)
|
||||
goto err;
|
||||
} else if (MATCH("個数 = %d", &n)) {
|
||||
current_object->nr_particles = n;
|
||||
} else if (MATCH("アルファフェードインフレーム = %f", &f)) {
|
||||
current_object->alpha_fadein_frame = f;
|
||||
} else if (MATCH("アルファフェードイン時間 = %d", &n)) {
|
||||
current_object->alpha_fadein_time = n;
|
||||
} else if (MATCH("アルファフェードアウトフレーム = %f", &f)) {
|
||||
current_object->alpha_fadeout_frame = f;
|
||||
} else if (MATCH("アルファフェードアウト時間 = %d", &n)) {
|
||||
current_object->alpha_fadeout_time = n;
|
||||
} else if (MATCH("X軸自転角度 = %f , %f", &f, &f2)) {
|
||||
current_object->rotation.begin[0] = f;
|
||||
current_object->rotation.end[0] = f2;
|
||||
} else if (MATCH("Y軸自転角度 = %f , %f", &f, &f2)) {
|
||||
current_object->rotation.begin[1] = f;
|
||||
current_object->rotation.end[1] = f2;
|
||||
} else if (MATCH("Z軸自転角度 = %f , %f", &f, &f2)) {
|
||||
current_object->rotation.begin[2] = f;
|
||||
current_object->rotation.end[2] = f2;
|
||||
} else if (MATCH("X軸公転角度 = %f , %f", &f, &f2)) {
|
||||
current_object->revolution_angle.begin[0] = f;
|
||||
current_object->revolution_angle.end[0] = f2;
|
||||
} else if (MATCH("Y軸公転角度 = %f , %f", &f, &f2)) {
|
||||
current_object->revolution_angle.begin[1] = f;
|
||||
current_object->revolution_angle.end[1] = f2;
|
||||
} else if (MATCH("Z軸公転角度 = %f , %f", &f, &f2)) {
|
||||
current_object->revolution_angle.begin[2] = f;
|
||||
current_object->revolution_angle.end[2] = f2;
|
||||
} else if (MATCH("X軸公転距離 = %f , %f", &f, &f2)) {
|
||||
current_object->revolution_distance.begin[0] = f;
|
||||
current_object->revolution_distance.end[0] = f2;
|
||||
} else if (MATCH("Y軸公転距離 = %f , %f", &f, &f2)) {
|
||||
current_object->revolution_distance.begin[1] = f;
|
||||
current_object->revolution_distance.end[1] = f2;
|
||||
} else if (MATCH("Z軸公転距離 = %f , %f", &f, &f2)) {
|
||||
current_object->revolution_distance.begin[2] = f;
|
||||
current_object->revolution_distance.end[2] = f2;
|
||||
} else if (MATCH("カーブX距離 = %f", &f)) {
|
||||
current_object->curve_length[0] = f;
|
||||
} else if (MATCH("カーブY距離 = %f", &f)) {
|
||||
current_object->curve_length[1] = f;
|
||||
} else if (MATCH("カーブZ距離 = %f", &f)) {
|
||||
current_object->curve_length[2] = -f;
|
||||
} else if (MATCH("子フレーム = %d", &n)) {
|
||||
current_object->child_frame = n;
|
||||
} else if (MATCH("子距離 = %f", &f)) {
|
||||
current_object->child_length = f;
|
||||
} else if (MATCH("子傾き = %f", &f)) {
|
||||
current_object->child_begin_slope = f;
|
||||
} else if (MATCH("子傾き2 = %f", &f)) {
|
||||
current_object->child_end_slope = f;
|
||||
} else if (MATCH("子生成開始フレーム = %f", &f)) {
|
||||
current_object->child_create_begin_frame = f;
|
||||
} else if (MATCH("子生成終了フレーム = %f", &f)) {
|
||||
current_object->child_create_end_frame = f;
|
||||
} else if (MATCH("子移動方向タイプ = %d", &n)) {
|
||||
if (n > PARTICLE_CHILD_MOVE_DIR_TYPE_MAX) {
|
||||
WARNING("unknown child move dir type %d", n);
|
||||
n = PARTICLE_CHILD_MOVE_DIR_TYPE_MAX;
|
||||
}
|
||||
current_object->child_move_dir_type = n;
|
||||
} else if (MATCH("方向タイプ = %d", &n)) {
|
||||
if (n > PARTICLE_DIR_TYPE_MAX) {
|
||||
WARNING("unknown dir type %d", n);
|
||||
n = PARTICLE_DIR_TYPE_MAX;
|
||||
}
|
||||
current_object->dir_type = n;
|
||||
} else if (MATCH("ダメージ = ")) {
|
||||
current_object->damages = parse_int_list(&text, ¤t_object->nr_damages);
|
||||
if (!current_object->damages)
|
||||
goto err;
|
||||
} else if (MATCH("オフセットX = %f", &f)) {
|
||||
current_object->offset_x = f;
|
||||
} else if (MATCH("オフセットY = %f", &f)) {
|
||||
current_object->offset_y = f;
|
||||
} else {
|
||||
parse_error("unknown object field.", text);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
err:
|
||||
pae_free(pae);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void load_textures(struct pae *effect, struct archive *aar)
|
||||
{
|
||||
effect->textures = ht_create(16);
|
||||
for (int i = 0; i < effect->nr_objects; i++) {
|
||||
struct pae_object *po = &effect->objects[i];
|
||||
for (int j = 0; j < po->nr_textures; j++) {
|
||||
const char *name = po->textures[j];
|
||||
if (ht_get(effect->textures, name, NULL))
|
||||
continue; // already loaded.
|
||||
|
||||
struct archive_data *dfile = RE_get_aar_entry(aar, effect->path, name, ".bmp");
|
||||
if (!dfile)
|
||||
dfile = RE_get_aar_entry(aar, effect->path, name, ".tga");
|
||||
if (!dfile) {
|
||||
WARNING("cannot load texture %s\\%s", effect->path, name);
|
||||
continue;
|
||||
}
|
||||
struct cg *cg = cg_load_data(dfile);
|
||||
if (!cg) {
|
||||
WARNING("cg_load_data failed: %s", dfile->name);
|
||||
archive_free_data(dfile);
|
||||
continue;
|
||||
}
|
||||
archive_free_data(dfile);
|
||||
|
||||
GLuint texture;
|
||||
glGenTextures(1, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cg->metrics.w, cg->metrics.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, cg->pixels);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
struct billboard_texture *bt = xcalloc(1, sizeof(struct billboard_texture));
|
||||
bt->texture = texture;
|
||||
bt->has_alpha = cg->metrics.has_alpha;
|
||||
ht_put(effect->textures, name, bt);
|
||||
cg_free(cg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct RE_instance *get_target(struct RE_instance *inst, int index)
|
||||
{
|
||||
int target_id = inst->target[index];
|
||||
if (target_id < 0 || target_id >= inst->plugin->nr_instances)
|
||||
return NULL;
|
||||
return inst->plugin->instances[target_id];
|
||||
}
|
||||
|
||||
static void eval_pos_for_object(struct RE_instance *inst, struct particle_position *pp, vec3 dest)
|
||||
{
|
||||
glm_vec3_zero(dest);
|
||||
if (!pp)
|
||||
return;
|
||||
for (int i = 0; i < NR_PARTICLE_POSITION_UNITS; i++) {
|
||||
struct particle_position_unit *unit = &pp->units[i];
|
||||
switch (unit->type) {
|
||||
case PARTICLE_POS_UNIT_NONE:
|
||||
break;
|
||||
case PARTICLE_POS_UNIT_RAND:
|
||||
case PARTICLE_POS_UNIT_RAND_POSITIVE_Y:
|
||||
// These are computed per instance, in eval_pos_for_instance().
|
||||
break;
|
||||
case PARTICLE_POS_UNIT_ABSOLUTE:
|
||||
glm_vec3_add(dest, unit->absolute, dest);
|
||||
break;
|
||||
case PARTICLE_POS_UNIT_BONE:
|
||||
{
|
||||
struct RE_instance *target = get_target(inst, unit->bone.target_index);
|
||||
int bone = RE_instance_get_bone_index(target, unit->bone.name);
|
||||
vec3 target_pos;
|
||||
if (RE_instance_trans_local_pos_to_world_pos_by_bone(target, bone, unit->bone.offset, target_pos))
|
||||
glm_vec3_add(dest, target_pos, dest);
|
||||
else
|
||||
glm_vec3_add(dest, unit->bone.offset, dest);
|
||||
}
|
||||
break;
|
||||
case PARTICLE_POS_UNIT_TARGET:
|
||||
{
|
||||
struct RE_instance *target = get_target(inst, unit->target.index);
|
||||
if (!target)
|
||||
break;
|
||||
vec3 scale = {
|
||||
target->column_radius,
|
||||
target->column_height,
|
||||
target->column_radius
|
||||
};
|
||||
mat4 transform;
|
||||
glm_scale_make(transform, scale);
|
||||
glm_rotate_y(transform, -glm_rad(target->column_angle), transform);
|
||||
vec3 target_pos;
|
||||
glm_mat4_mulv3(transform, unit->target.offset, 1.0f, target_pos);
|
||||
|
||||
glm_vec3_addadd(target->pos, target->column_pos, target_pos);
|
||||
glm_vec3_add(dest, target_pos, dest);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void eval_pos_for_instance(struct particle_position *pp, vec3 dest)
|
||||
{
|
||||
glm_vec3_zero(dest);
|
||||
if (!pp)
|
||||
return;
|
||||
for (int i = 0; i < NR_PARTICLE_POSITION_UNITS; i++) {
|
||||
struct particle_position_unit *unit = &pp->units[i];
|
||||
switch (unit->type) {
|
||||
case PARTICLE_POS_UNIT_NONE:
|
||||
break;
|
||||
case PARTICLE_POS_UNIT_ABSOLUTE:
|
||||
case PARTICLE_POS_UNIT_BONE:
|
||||
case PARTICLE_POS_UNIT_TARGET:
|
||||
// These are computed per object, in eval_pos_for_object().
|
||||
break;
|
||||
case PARTICLE_POS_UNIT_RAND:
|
||||
dest[0] += (randf() * 2.0 - 1.0) * unit->rand.f;
|
||||
dest[1] += (randf() * 2.0 - 1.0) * unit->rand.f;
|
||||
dest[2] += (randf() * 2.0 - 1.0) * unit->rand.f;
|
||||
break;
|
||||
case PARTICLE_POS_UNIT_RAND_POSITIVE_Y:
|
||||
dest[0] += (randf() * 2.0 - 1.0) * unit->rand.f;
|
||||
dest[1] += randf() * unit->rand.f;
|
||||
dest[2] += (randf() * 2.0 - 1.0) * unit->rand.f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static float eval_child_slope(struct pae_object *po)
|
||||
{
|
||||
float slope = glm_lerp(po->child_begin_slope, po->child_end_slope, randf());
|
||||
if (slope > 1.0)
|
||||
slope = 0.5 + 0.5 / slope; // ???
|
||||
return (1.0 + slope) * GLM_PI_2;
|
||||
}
|
||||
|
||||
static void interpolate_pos(vec3 begin, vec3 end, float move_curve, float t, vec3 dest)
|
||||
{
|
||||
if (move_curve == 0.0f) {
|
||||
glm_vec3_copy(end, dest);
|
||||
return;
|
||||
}
|
||||
float x = move_curve > 0.0f ? powf(t, move_curve) : 1.0f - powf(1.0f - t, -move_curve);
|
||||
glm_vec3_lerp(begin, end, x, dest);
|
||||
}
|
||||
|
||||
static void init_particle_instances(struct particle_object *po)
|
||||
{
|
||||
struct pae_object *pae_obj = po->pae_obj;
|
||||
po->instances = xcalloc(pae_obj->nr_particles, sizeof(struct particle_instance));
|
||||
|
||||
for (int i = 0; i < pae_obj->nr_particles; i++) {
|
||||
struct particle_instance *pi = &po->instances[i];
|
||||
switch (pae_obj->move_type) {
|
||||
case PARTICLE_MOVE_FIXED:
|
||||
case PARTICLE_MOVE_LINEAR:
|
||||
pi->begin_frame = pae_obj->begin_frame;
|
||||
pi->end_frame = pae_obj->end_frame;
|
||||
break;
|
||||
case PARTICLE_MOVE_EMITTER:
|
||||
pi->begin_frame = glm_lerp(pae_obj->child_create_begin_frame, pae_obj->child_create_end_frame, (float)i / pae_obj->nr_particles);
|
||||
pi->end_frame = pi->begin_frame + pae_obj->child_frame;
|
||||
pi->roll_angle = eval_child_slope(pae_obj);
|
||||
pi->pitch_angle = randf() * (2.0f * GLM_PIf);
|
||||
break;
|
||||
}
|
||||
eval_pos_for_instance(pae_obj->position[0], pi->random_offset.begin);
|
||||
eval_pos_for_instance(pae_obj->position[1], pi->random_offset.end);
|
||||
}
|
||||
}
|
||||
|
||||
struct pae *pae_load(struct archive *aar, const char *path)
|
||||
{
|
||||
const char *basename = strrchr(path, '\\');
|
||||
basename = basename ? basename + 1 : path;
|
||||
|
||||
struct archive_data *dfile = RE_get_aar_entry(aar, path, basename, ".pae");
|
||||
if (!dfile) {
|
||||
WARNING("%s\\%s.pae: not found", path, basename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *sjis_text = xmalloc(dfile->size + 1);
|
||||
memcpy(sjis_text, dfile->data, dfile->size);
|
||||
// Replace null characters (written by buggy .pae formatter) with spaces.
|
||||
for (size_t i = 0; i < dfile->size; i++) {
|
||||
if (sjis_text[i] == '\0')
|
||||
sjis_text[i] = ' ';
|
||||
}
|
||||
sjis_text[dfile->size] = '\0';
|
||||
|
||||
// We could parse the SJIS text directly, but convert it to UTF-8 for code
|
||||
// clarity.
|
||||
char *text = sjis2utf(sjis_text, dfile->size);
|
||||
free(sjis_text);
|
||||
struct pae *pae = parse_pae(text);
|
||||
free(text);
|
||||
if (!pae) {
|
||||
WARNING("could not parse %s", dfile->name);
|
||||
archive_free_data(dfile);
|
||||
return NULL;
|
||||
}
|
||||
archive_free_data(dfile);
|
||||
|
||||
pae->path = strdup(path);
|
||||
load_textures(pae, aar);
|
||||
for (int i = 0; i < pae->nr_objects; i++) {
|
||||
struct pae_object *po = &pae->objects[i];
|
||||
|
||||
if (po->polygon_name && *po->polygon_name) {
|
||||
char *pol_path = xmalloc(strlen(path) + strlen(po->polygon_name) + 2);
|
||||
sprintf(pol_path, "%s\\%s", path, po->polygon_name);
|
||||
po->model = model_load(aar, pol_path);
|
||||
free(pol_path);
|
||||
}
|
||||
}
|
||||
|
||||
return pae;
|
||||
}
|
||||
|
||||
struct particle_effect *particle_effect_create(struct pae *pae)
|
||||
{
|
||||
struct particle_effect *effect = xcalloc(1, sizeof(struct particle_effect));
|
||||
effect->pae = pae;
|
||||
effect->objects = xcalloc(pae->nr_objects, sizeof(struct particle_object));
|
||||
effect->camera_quake_enabled = true;
|
||||
for (int i = 0; i < pae->nr_objects; i++) {
|
||||
struct particle_object *po = &effect->objects[i];
|
||||
po->pae_obj = &pae->objects[i];
|
||||
init_particle_instances(po);
|
||||
}
|
||||
return effect;
|
||||
}
|
||||
|
||||
static void free_billboard_texture(void *value)
|
||||
{
|
||||
struct billboard_texture *bt = value;
|
||||
glDeleteTextures(1, &bt->texture);
|
||||
free(bt);
|
||||
}
|
||||
|
||||
static void particle_position_free(struct particle_position *pos)
|
||||
{
|
||||
if (!pos)
|
||||
return;
|
||||
for (int i = 0; i < NR_PARTICLE_POSITION_UNITS; i++) {
|
||||
if (pos->units[i].type == PARTICLE_POS_UNIT_BONE)
|
||||
free(pos->units[i].bone.name);
|
||||
}
|
||||
free(pos);
|
||||
}
|
||||
|
||||
void pae_free(struct pae *pae)
|
||||
{
|
||||
for (int i = 0; i < pae->nr_objects; i++) {
|
||||
struct pae_object *po = &pae->objects[i];
|
||||
free(po->name);
|
||||
for (int j = 0; j < NR_PARTICLE_POSITIONS; j++)
|
||||
particle_position_free(po->position[j]);
|
||||
free(po->sizes2);
|
||||
free(po->sizes_x);
|
||||
free(po->sizes_y);
|
||||
free(po->size_types);
|
||||
free(po->size_x_types);
|
||||
free(po->size_y_types);
|
||||
for (int j = 0; j < po->nr_textures; j++)
|
||||
free(po->textures[j]);
|
||||
free(po->textures);
|
||||
free(po->polygon_name);
|
||||
free(po->damages);
|
||||
if (po->model)
|
||||
model_free(po->model);
|
||||
}
|
||||
ht_foreach_value(pae->textures, free_billboard_texture);
|
||||
ht_free(pae->textures);
|
||||
free(pae->objects);
|
||||
free(pae->path);
|
||||
free(pae);
|
||||
}
|
||||
|
||||
void particle_effect_free(struct particle_effect *effect)
|
||||
{
|
||||
for (int i = 0; i < effect->pae->nr_objects; i++) {
|
||||
struct particle_object *obj = &effect->objects[i];
|
||||
free(obj->instances);
|
||||
}
|
||||
free(effect->objects);
|
||||
free(effect);
|
||||
}
|
||||
|
||||
void pae_calc_frame_range(struct pae *pae, struct motion *motion)
|
||||
{
|
||||
int begin_frame = INT_MAX;
|
||||
int end_frame = INT_MIN;
|
||||
for (int i = 0; i < pae->nr_objects; i++) {
|
||||
struct pae_object *po = &pae->objects[i];
|
||||
int obj_begin = po->begin_frame;
|
||||
int obj_end = max(po->end_frame, po->child_create_end_frame + po->child_frame);
|
||||
if (begin_frame > obj_begin)
|
||||
begin_frame = obj_begin;
|
||||
if (end_frame < obj_end)
|
||||
end_frame = obj_end;
|
||||
}
|
||||
motion->frame_begin = motion->loop_frame_begin = begin_frame;
|
||||
motion->frame_end = motion->loop_frame_end = end_frame;
|
||||
}
|
||||
|
||||
static void update_camera_quake(struct RE_instance *inst, struct pae_object *po)
|
||||
{
|
||||
float frame = inst->motion->current_frame - po->begin_frame;
|
||||
float total_frames = po->end_frame - po->begin_frame;
|
||||
if (frame < 0 || frame >= total_frames)
|
||||
return;
|
||||
|
||||
const float ease_in_duration = 4.0f;
|
||||
const float ease_out_duration = 4.0f;
|
||||
float amplitude = 1.0f;
|
||||
if (total_frames - frame < ease_out_duration)
|
||||
amplitude *= (total_frames - frame) / ease_out_duration;
|
||||
else if (frame < ease_in_duration)
|
||||
amplitude *= frame / ease_in_duration;
|
||||
|
||||
inst->plugin->camera.quake_pitch = sinf(frame * 1.1) * amplitude * -8.0f;
|
||||
inst->plugin->camera.quake_yaw = sinf(frame * 1.7) * amplitude * 12.0f;
|
||||
}
|
||||
|
||||
void particle_effect_update(struct RE_instance *inst)
|
||||
{
|
||||
struct particle_effect *effect = inst->effect;
|
||||
if (!effect)
|
||||
return;
|
||||
for (int i = 0; i < effect->pae->nr_objects; i++) {
|
||||
struct particle_object *po = &effect->objects[i];
|
||||
struct pae_object *pae_obj = po->pae_obj;
|
||||
if (pae_obj->type == PARTICLE_TYPE_CAMERA_QUAKE) {
|
||||
if (effect->camera_quake_enabled)
|
||||
update_camera_quake(inst, pae_obj);
|
||||
continue;
|
||||
}
|
||||
// These cannot be precomputed, because they may depend on the target's
|
||||
// *current* bone matrix.
|
||||
eval_pos_for_object(inst, pae_obj->position[0], po->pos.begin);
|
||||
eval_pos_for_object(inst, pae_obj->position[1], po->pos.end);
|
||||
glm_vec3_sub(po->pos.end, po->pos.begin, po->move_vec);
|
||||
switch (pae_obj->dir_type) {
|
||||
case PARTICLE_DIR_TYPE_NORMAL:
|
||||
glm_vec3_normalize(po->move_vec);
|
||||
glm_vec3_ortho(po->move_vec, po->move_ortho);
|
||||
glm_vec3_normalize(po->move_ortho);
|
||||
break;
|
||||
case PARTICLE_DIR_TYPE_XY_PLANE:
|
||||
po->move_vec[2] = 0.0f;
|
||||
glm_vec3_normalize(po->move_vec);
|
||||
glm_vec3_copy(GLM_ZUP, po->move_ortho);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_emitter_pos(struct particle_object *po, struct particle_instance *pi, float t, vec3 dest, vec3 move_vec)
|
||||
{
|
||||
struct pae_object *pae_obj = po->pae_obj;
|
||||
vec3 begin, end;
|
||||
glm_vec3_add(po->pos.begin, pi->random_offset.begin, begin);
|
||||
glm_vec3_add(po->pos.end, pi->random_offset.end, end);
|
||||
interpolate_pos(begin, end, pae_obj->move_curve, t, dest);
|
||||
if (move_vec)
|
||||
glm_vec3_sub(end, begin, move_vec);
|
||||
|
||||
// Orbital rotation.
|
||||
vec3 angle, dist;
|
||||
glm_vec3_lerp(pae_obj->revolution_angle.begin, pae_obj->revolution_angle.end, t, angle);
|
||||
glm_vec3_lerp(pae_obj->revolution_distance.begin, pae_obj->revolution_distance.end, t, dist);
|
||||
glm_vec3_scale(angle, GLM_PIf / 180.0f, angle); // deg -> rad
|
||||
if (pae_obj->revolution_angle.begin[0] != 0.0f || pae_obj->revolution_angle.end[0] != 0.0f) {
|
||||
dest[1] += dist[0] * sinf(angle[0]);
|
||||
dest[2] -= dist[0] * cosf(angle[0]);
|
||||
}
|
||||
if (pae_obj->revolution_angle.begin[1] != 0.0f || pae_obj->revolution_angle.end[1] != 0.0f) {
|
||||
dest[0] += dist[1] * sinf(angle[1]);
|
||||
dest[2] -= dist[1] * cosf(angle[1]);
|
||||
}
|
||||
if (pae_obj->revolution_angle.begin[2] != 0.0f || pae_obj->revolution_angle.end[2] != 0.0f) {
|
||||
dest[1] += dist[2] * sinf(angle[2]);
|
||||
dest[0] += dist[2] * cosf(angle[2]);
|
||||
}
|
||||
|
||||
// Apply the curve.
|
||||
glm_vec3_muladds(pae_obj->curve_length, 4.9f * t * (1.0f - t), dest);
|
||||
}
|
||||
|
||||
static void calc_pos_and_up_vector(struct particle_object *po, struct particle_instance *pi, float t, vec3 pos, vec3 up_vector)
|
||||
{
|
||||
struct pae_object *pae_obj = po->pae_obj;
|
||||
switch (pae_obj->move_type) {
|
||||
case PARTICLE_MOVE_FIXED:
|
||||
glm_vec3_add(po->pos.begin, pi->random_offset.begin, pos);
|
||||
glm_vec3_copy(GLM_YUP, up_vector);
|
||||
break;
|
||||
case PARTICLE_MOVE_LINEAR:
|
||||
calc_emitter_pos(po, pi, t, pos, up_vector);
|
||||
switch (pae_obj->up_vector_type) {
|
||||
case PARTICLE_UP_VECTOR_Y_AXIS:
|
||||
case PARTICLE_UP_VECTOR_INSTANCE_MOVE:
|
||||
glm_vec3_copy(GLM_YUP, up_vector);
|
||||
break;
|
||||
case PARTICLE_UP_VECTOR_OBJECT_MOVE:
|
||||
if (glm_vec3_norm2(up_vector) == 0.0f)
|
||||
glm_vec3_copy(GLM_XUP, up_vector);
|
||||
else
|
||||
glm_vec3_normalize(up_vector);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case PARTICLE_MOVE_EMITTER:
|
||||
{
|
||||
// Calculate the instance's initial and final position.
|
||||
float t0 = glm_clamp_zo(glm_percent(pae_obj->begin_frame, pae_obj->end_frame, pi->begin_frame));
|
||||
vec3_range inst_pos;
|
||||
calc_emitter_pos(po, pi, t0, inst_pos.begin, NULL);
|
||||
vec3 inst_move;
|
||||
glm_vec3_copy(po->move_vec, inst_move);
|
||||
switch (pae_obj->dir_type) {
|
||||
case PARTICLE_DIR_TYPE_NORMAL:
|
||||
glm_vec3_rotate(inst_move, pi->roll_angle, po->move_ortho);
|
||||
glm_vec3_rotate(inst_move, pi->pitch_angle, po->move_vec);
|
||||
break;
|
||||
case PARTICLE_DIR_TYPE_XY_PLANE:
|
||||
glm_vec3_rotate(inst_move, (pi->pitch_angle < GLM_PIf ? -1.0f : 1.0f) * pi->roll_angle, po->move_ortho);
|
||||
break;
|
||||
}
|
||||
glm_vec3_copy(inst_pos.begin, inst_pos.end);
|
||||
switch (pae_obj->child_move_dir_type) {
|
||||
case PARTICLE_CHILD_MOVE_DIR_NORMAL:
|
||||
glm_vec3_muladds(inst_move, pae_obj->child_length, inst_pos.end);
|
||||
break;
|
||||
case PARTICLE_CHILD_MOVE_DIR_REVERSE:
|
||||
glm_vec3_muladds(inst_move, pae_obj->child_length, inst_pos.begin);
|
||||
glm_vec3_negate(inst_move);
|
||||
break;
|
||||
}
|
||||
// Current position of the instance.
|
||||
glm_vec3_lerp(inst_pos.begin, inst_pos.end, t, pos);
|
||||
|
||||
// Calculate the up vector.
|
||||
switch (pae_obj->up_vector_type) {
|
||||
case PARTICLE_UP_VECTOR_Y_AXIS:
|
||||
glm_vec3_copy(GLM_YUP, up_vector);
|
||||
break;
|
||||
case PARTICLE_UP_VECTOR_OBJECT_MOVE:
|
||||
glm_vec3_copy(po->move_vec, up_vector);
|
||||
break;
|
||||
case PARTICLE_UP_VECTOR_INSTANCE_MOVE:
|
||||
if (pae_obj->child_length != 0.0f)
|
||||
glm_vec3_copy(inst_move, up_vector);
|
||||
else
|
||||
glm_vec3_copy(GLM_XUP, up_vector);
|
||||
break;
|
||||
}
|
||||
if (pae_obj->dir_type == PARTICLE_DIR_TYPE_XY_PLANE) {
|
||||
mat3 rot = {{ inst_move[1], -inst_move[0], 0.0f },
|
||||
{ inst_move[0], inst_move[1], 0.0f },
|
||||
{ 0.0f, 0.0f, 1.0f }};
|
||||
glm_mat3_mulv(rot, up_vector, up_vector);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_scale(struct pae_object *po, int frame, vec3 dest)
|
||||
{
|
||||
glm_vec3_broadcast(po->sizes2[min(frame, po->nr_sizes2 - 1)], dest);
|
||||
if (po->nr_sizes_x)
|
||||
dest[0] *= po->sizes_x[min(frame, po->nr_sizes_x - 1)];
|
||||
if (po->nr_sizes_y)
|
||||
dest[1] *= po->sizes_y[min(frame, po->nr_sizes_y - 1)];
|
||||
}
|
||||
|
||||
void particle_object_calc_local_transform(struct RE_instance *inst, struct particle_object *po, struct particle_instance *pi, float frame, mat4 dest)
|
||||
{
|
||||
struct pae_object *pae_obj = po->pae_obj;
|
||||
float t = glm_percent(pi->begin_frame, pi->end_frame, frame);
|
||||
|
||||
vec3 pos = {0}, up_vector;
|
||||
calc_pos_and_up_vector(po, pi, t, pos, up_vector);
|
||||
|
||||
float rel_frame = frame - pi->begin_frame;
|
||||
int cur_step = rel_frame;
|
||||
int next_step = ceilf(rel_frame);
|
||||
float step_frac = rel_frame - cur_step;
|
||||
|
||||
glm_translate_make(dest, pos);
|
||||
if (pae_obj->type == PARTICLE_TYPE_BILLBOARD) {
|
||||
mat4 view_mat;
|
||||
RE_calc_view_matrix(&inst->plugin->camera, up_vector, view_mat);
|
||||
mat3 rot;
|
||||
glm_mat4_pick3t(view_mat, rot);
|
||||
glm_mat4_ins3(rot, dest);
|
||||
}
|
||||
|
||||
vec3 angles;
|
||||
glm_vec3_lerp(pae_obj->rotation.begin, pae_obj->rotation.end, t, angles);
|
||||
angles[0] *= -GLM_PIf / 180.0f;
|
||||
angles[1] *= -GLM_PIf / 180.0f;
|
||||
angles[2] *= GLM_PIf / 180.0f;
|
||||
mat4 rotation_mat;
|
||||
glm_euler_zxy(angles, rotation_mat);
|
||||
glm_mat4_mul(dest, rotation_mat, dest);
|
||||
|
||||
if (pae_obj->nr_sizes2 > 0) {
|
||||
vec3 scale, scale2;
|
||||
calc_scale(pae_obj, cur_step, scale);
|
||||
calc_scale(pae_obj, next_step, scale2);
|
||||
glm_vec3_lerp(scale, scale2, step_frac, scale);
|
||||
glm_scale(dest, scale);
|
||||
} else {
|
||||
glm_scale_uni(dest, glm_lerp(pae_obj->size[0], pae_obj->size[1], t));
|
||||
}
|
||||
|
||||
glm_translate_x(dest, -pae_obj->offset_x);
|
||||
glm_translate_y(dest, -pae_obj->offset_y);
|
||||
}
|
||||
|
||||
float pae_object_calc_alpha(struct pae_object *po, struct particle_instance *pi, float frame)
|
||||
{
|
||||
frame -= pi->begin_frame;
|
||||
if (frame < po->alpha_fadein_frame)
|
||||
return glm_clamp_zo(frame / po->alpha_fadein_frame);
|
||||
float total_frames = pi->end_frame - pi->begin_frame;
|
||||
if (total_frames - po->alpha_fadeout_frame < frame)
|
||||
return glm_clamp_zo((total_frames - frame) / po->alpha_fadeout_frame);
|
||||
return 1.0;
|
||||
}
|
||||
+1477
File diff suppressed because it is too large
Load Diff
+1564
File diff suppressed because it is too large
Load Diff
+1599
File diff suppressed because it is too large
Load Diff
+388
@@ -0,0 +1,388 @@
|
||||
/* Copyright (C) 2026 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/string.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
#include "reign.h"
|
||||
#include "../hll/iarray.h"
|
||||
|
||||
static void write_vec3(struct iarray_writer *w, vec3 v)
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
iarray_write_float(w, v[i]);
|
||||
}
|
||||
|
||||
static void read_vec3(struct iarray_reader *r, vec3 v)
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
v[i] = iarray_read_float(r);
|
||||
}
|
||||
|
||||
static void write_cstring(struct iarray_writer *w, const char *s)
|
||||
{
|
||||
struct string *tmp = cstr_to_string(s);
|
||||
iarray_write_string(w, tmp);
|
||||
free_string(tmp);
|
||||
}
|
||||
|
||||
static void save_motion(struct iarray_writer *w, struct motion *m)
|
||||
{
|
||||
iarray_write(w, !!m);
|
||||
if (!m)
|
||||
return;
|
||||
write_cstring(w, m->mot ? m->mot->name : "");
|
||||
iarray_write(w, m->state);
|
||||
iarray_write_float(w, m->current_frame);
|
||||
iarray_write_float(w, m->frame_begin);
|
||||
iarray_write_float(w, m->frame_end);
|
||||
iarray_write_float(w, m->loop_frame_begin);
|
||||
iarray_write_float(w, m->loop_frame_end);
|
||||
}
|
||||
|
||||
static void load_motion(struct iarray_reader *r, struct RE_instance *inst, bool next)
|
||||
{
|
||||
if (!iarray_read(r))
|
||||
return;
|
||||
struct string *name = iarray_read_string(r);
|
||||
if (name->size > 0) {
|
||||
if (next)
|
||||
RE_instance_load_next_motion(inst, name->text);
|
||||
else
|
||||
RE_instance_load_motion(inst, name->text);
|
||||
}
|
||||
free_string(name);
|
||||
|
||||
int state = iarray_read(r);
|
||||
float current_frame = iarray_read_float(r);
|
||||
float frame_begin = iarray_read_float(r);
|
||||
float frame_end = iarray_read_float(r);
|
||||
float loop_frame_begin = iarray_read_float(r);
|
||||
float loop_frame_end = iarray_read_float(r);
|
||||
|
||||
struct motion *m = next ? inst->next_motion : inst->motion;
|
||||
if (!m)
|
||||
return;
|
||||
m->state = state;
|
||||
m->current_frame = current_frame;
|
||||
m->frame_begin = frame_begin;
|
||||
m->frame_end = frame_end;
|
||||
m->loop_frame_begin = loop_frame_begin;
|
||||
m->loop_frame_end = loop_frame_end;
|
||||
}
|
||||
|
||||
static void save_instance(struct iarray_writer *w, struct RE_instance *inst)
|
||||
{
|
||||
iarray_write(w, inst->type);
|
||||
iarray_write_string_or_null(w, inst->name);
|
||||
for (int i = 0; i < RE_NR_INSTANCE_TARGETS; i++)
|
||||
iarray_write(w, inst->target[i]);
|
||||
write_vec3(w, inst->pos);
|
||||
iarray_write_float(w, inst->pitch);
|
||||
iarray_write_float(w, inst->roll);
|
||||
iarray_write_float(w, inst->yaw);
|
||||
write_vec3(w, inst->scale);
|
||||
iarray_write_float(w, inst->alpha);
|
||||
iarray_write_float(w, inst->grayscale_rate);
|
||||
iarray_write(w, inst->draw);
|
||||
iarray_write(w, inst->draw_edge);
|
||||
iarray_write(w, inst->draw_shadow);
|
||||
iarray_write(w, inst->make_shadow);
|
||||
iarray_write(w, inst->draw_bump);
|
||||
iarray_write_float(w, inst->shadow_volume_bone_radius);
|
||||
iarray_write_float(w, inst->fps);
|
||||
iarray_write(w, inst->use_mag_speed);
|
||||
iarray_write(w, inst->motion_blend);
|
||||
iarray_write_float(w, inst->motion_blend_rate);
|
||||
write_vec3(w, inst->diffuse);
|
||||
write_vec3(w, inst->ambient);
|
||||
write_vec3(w, inst->column_pos);
|
||||
iarray_write_float(w, inst->column_height);
|
||||
iarray_write_float(w, inst->column_radius);
|
||||
iarray_write_float(w, inst->column_angle);
|
||||
|
||||
iarray_write(w, inst->draw_type);
|
||||
for (int i = 0; i < 4; i++)
|
||||
write_vec3(w, inst->vertex_pos[i]);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
iarray_write_float(w, inst->vertex_uv[i][0]);
|
||||
iarray_write_float(w, inst->vertex_uv[i][1]);
|
||||
}
|
||||
|
||||
write_vec3(w, inst->vec);
|
||||
write_vec3(w, inst->globe_diffuse);
|
||||
iarray_write(w, !!inst->light_params);
|
||||
if (inst->light_params) {
|
||||
iarray_write(w, RE_NR_LIGHT_PARAMS);
|
||||
for (int i = 0; i < RE_NR_LIGHT_PARAMS; i++)
|
||||
iarray_write_float(w, inst->light_params[i]);
|
||||
}
|
||||
|
||||
save_motion(w, inst->motion);
|
||||
save_motion(w, inst->next_motion);
|
||||
}
|
||||
|
||||
static void load_instance(struct iarray_reader *r, struct RE_instance *inst)
|
||||
{
|
||||
int type = iarray_read(r);
|
||||
if (type != RE_ITYPE_UNINITIALIZED)
|
||||
RE_instance_set_type(inst, type);
|
||||
struct string *name = iarray_read_string_or_null(r);
|
||||
if (name) {
|
||||
RE_instance_load(inst, name->text);
|
||||
free_string(name);
|
||||
}
|
||||
for (int i = 0; i < RE_NR_INSTANCE_TARGETS; i++)
|
||||
inst->target[i] = iarray_read(r);
|
||||
read_vec3(r, inst->pos);
|
||||
inst->pitch = iarray_read_float(r);
|
||||
inst->roll = iarray_read_float(r);
|
||||
inst->yaw = iarray_read_float(r);
|
||||
read_vec3(r, inst->scale);
|
||||
inst->alpha = iarray_read_float(r);
|
||||
inst->grayscale_rate = iarray_read_float(r);
|
||||
inst->draw = iarray_read(r);
|
||||
inst->draw_edge = iarray_read(r);
|
||||
inst->draw_shadow = iarray_read(r);
|
||||
inst->make_shadow = iarray_read(r);
|
||||
inst->draw_bump = iarray_read(r);
|
||||
inst->shadow_volume_bone_radius = iarray_read_float(r);
|
||||
inst->fps = iarray_read_float(r);
|
||||
inst->use_mag_speed = iarray_read(r);
|
||||
inst->motion_blend = iarray_read(r);
|
||||
inst->motion_blend_rate = iarray_read_float(r);
|
||||
read_vec3(r, inst->diffuse);
|
||||
read_vec3(r, inst->ambient);
|
||||
read_vec3(r, inst->column_pos);
|
||||
inst->column_height = iarray_read_float(r);
|
||||
inst->column_radius = iarray_read_float(r);
|
||||
inst->column_angle = iarray_read_float(r);
|
||||
inst->local_transform_needs_update = true;
|
||||
|
||||
inst->draw_type = iarray_read(r);
|
||||
for (int i = 0; i < 4; i++)
|
||||
read_vec3(r, inst->vertex_pos[i]);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
inst->vertex_uv[i][0] = iarray_read_float(r);
|
||||
inst->vertex_uv[i][1] = iarray_read_float(r);
|
||||
}
|
||||
|
||||
read_vec3(r, inst->vec);
|
||||
read_vec3(r, inst->globe_diffuse);
|
||||
if (iarray_read(r)) {
|
||||
int nr_params = iarray_read(r);
|
||||
for (int i = 0; i < nr_params; i++) {
|
||||
float param = iarray_read_float(r);
|
||||
if (inst->light_params && i < RE_NR_LIGHT_PARAMS)
|
||||
inst->light_params[i] = param;
|
||||
}
|
||||
}
|
||||
|
||||
load_motion(r, inst, false);
|
||||
load_motion(r, inst, true);
|
||||
}
|
||||
|
||||
static void save_back_cg(struct iarray_writer *w, struct RE_back_cg *bcg)
|
||||
{
|
||||
iarray_write_string_or_null(w, bcg->name);
|
||||
iarray_write(w, bcg->no);
|
||||
iarray_write_float(w, bcg->blend_rate);
|
||||
iarray_write_float(w, bcg->x);
|
||||
iarray_write_float(w, bcg->y);
|
||||
iarray_write_float(w, bcg->mag);
|
||||
iarray_write(w, bcg->show);
|
||||
}
|
||||
|
||||
static void load_back_cg(struct iarray_reader *r, struct RE_back_cg *bcg, struct archive *aar)
|
||||
{
|
||||
struct string *name = iarray_read_string_or_null(r);
|
||||
int no = iarray_read(r);
|
||||
float blend_rate = iarray_read_float(r);
|
||||
float x = iarray_read_float(r);
|
||||
float y = iarray_read_float(r);
|
||||
float mag = iarray_read_float(r);
|
||||
bool show = iarray_read(r);
|
||||
if (bcg) {
|
||||
if (name)
|
||||
RE_back_cg_set_name(bcg, name, aar);
|
||||
else if (no)
|
||||
RE_back_cg_set(bcg, no);
|
||||
bcg->blend_rate = blend_rate;
|
||||
bcg->x = x;
|
||||
bcg->y = y;
|
||||
bcg->mag = mag;
|
||||
bcg->show = show;
|
||||
}
|
||||
if (name)
|
||||
free_string(name);
|
||||
}
|
||||
|
||||
void RE_plugin_serialize(struct RE_plugin *plugin, struct iarray_writer *w)
|
||||
{
|
||||
write_vec3(w, plugin->camera.pos);
|
||||
iarray_write_float(w, plugin->camera.pitch);
|
||||
iarray_write_float(w, plugin->camera.roll);
|
||||
iarray_write_float(w, plugin->camera.yaw);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++)
|
||||
iarray_write_float(w, plugin->proj_transform[i][j]);
|
||||
}
|
||||
iarray_write(w, plugin->viewport_x);
|
||||
iarray_write(w, plugin->viewport_y);
|
||||
iarray_write(w, plugin->viewport_width);
|
||||
iarray_write(w, plugin->viewport_height);
|
||||
|
||||
iarray_write(w, RE_NR_BACK_CGS);
|
||||
for (int i = 0; i < RE_NR_BACK_CGS; i++)
|
||||
save_back_cg(w, &plugin->back_cg[i]);
|
||||
|
||||
iarray_write(w, plugin->render_mode);
|
||||
iarray_write(w, plugin->shadow_mode);
|
||||
write_vec3(w, plugin->shadow_map_light_dir);
|
||||
iarray_write_float(w, plugin->shadow_bias);
|
||||
iarray_write(w, plugin->bump_mode);
|
||||
iarray_write(w, plugin->bloom_mode);
|
||||
iarray_write(w, plugin->glare_mode);
|
||||
iarray_write(w, plugin->fog_mode);
|
||||
iarray_write(w, plugin->fog_type);
|
||||
iarray_write_float(w, plugin->fog_near);
|
||||
iarray_write_float(w, plugin->fog_far);
|
||||
write_vec3(w, plugin->fog_color);
|
||||
iarray_write_float(w, plugin->ls_beta_r);
|
||||
iarray_write_float(w, plugin->ls_beta_m);
|
||||
iarray_write_float(w, plugin->ls_g);
|
||||
iarray_write_float(w, plugin->ls_distance);
|
||||
write_vec3(w, plugin->ls_light_dir);
|
||||
write_vec3(w, plugin->ls_light_color);
|
||||
write_vec3(w, plugin->ls_sun_color);
|
||||
iarray_write(w, plugin->specular_mode);
|
||||
iarray_write(w, plugin->light_map_mode);
|
||||
iarray_write(w, plugin->soft_fog_edge_mode);
|
||||
iarray_write(w, plugin->ssao_mode);
|
||||
iarray_write(w, plugin->vertex_blend_mode);
|
||||
iarray_write(w, plugin->shader_precision_mode);
|
||||
iarray_write(w, plugin->texture_filter_mode);
|
||||
iarray_write(w, plugin->use_power2_texture);
|
||||
iarray_write(w, plugin->shadow_map_resolution_level);
|
||||
iarray_write(w, plugin->texture_resolution_level);
|
||||
write_vec3(w, plugin->global_ambient);
|
||||
iarray_write_float(w, plugin->post_effect_filter_y);
|
||||
iarray_write_float(w, plugin->post_effect_filter_cb);
|
||||
iarray_write_float(w, plugin->post_effect_filter_cr);
|
||||
iarray_write(w, RE_DRAW_OPTION_MAX);
|
||||
for (int i = 0; i < RE_DRAW_OPTION_MAX; i++)
|
||||
iarray_write(w, plugin->draw_options[i]);
|
||||
iarray_write(w, plugin->suspended);
|
||||
|
||||
iarray_write(w, plugin->mag_speed);
|
||||
iarray_write(w, RE_NR_LIGHT_PARAMS);
|
||||
for (int i = 0; i < RE_NR_LIGHT_PARAMS; i++)
|
||||
iarray_write_float(w, plugin->light_params[i]);
|
||||
iarray_write_float(w, plugin->edge_length);
|
||||
iarray_write_float(w, plugin->edge_reduction_rate);
|
||||
write_vec3(w, plugin->edge_color);
|
||||
|
||||
iarray_write(w, plugin->nr_instances);
|
||||
for (int i = 0; i < plugin->nr_instances; i++) {
|
||||
iarray_write(w, !!plugin->instances[i]);
|
||||
if (plugin->instances[i])
|
||||
save_instance(w, plugin->instances[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void RE_plugin_deserialize(struct RE_plugin *plugin, struct iarray_reader *r, int version)
|
||||
{
|
||||
read_vec3(r, plugin->camera.pos);
|
||||
plugin->camera.pitch = iarray_read_float(r);
|
||||
plugin->camera.roll = iarray_read_float(r);
|
||||
plugin->camera.yaw = iarray_read_float(r);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++)
|
||||
plugin->proj_transform[i][j] = iarray_read_float(r);
|
||||
}
|
||||
int viewport_x = iarray_read(r);
|
||||
int viewport_y = iarray_read(r);
|
||||
int viewport_width = iarray_read(r);
|
||||
int viewport_height = iarray_read(r);
|
||||
if (viewport_width > 0 && viewport_height > 0)
|
||||
RE_set_viewport(plugin, viewport_x, viewport_y, viewport_width, viewport_height);
|
||||
|
||||
int nr_back_cgs = iarray_read(r);
|
||||
for (int i = 0; i < nr_back_cgs; i++)
|
||||
load_back_cg(r, i < RE_NR_BACK_CGS ? &plugin->back_cg[i] : NULL, plugin->aar);
|
||||
|
||||
plugin->render_mode = iarray_read(r);
|
||||
plugin->shadow_mode = iarray_read(r);
|
||||
read_vec3(r, plugin->shadow_map_light_dir);
|
||||
plugin->shadow_bias = iarray_read_float(r);
|
||||
plugin->bump_mode = iarray_read(r);
|
||||
plugin->bloom_mode = iarray_read(r);
|
||||
plugin->glare_mode = iarray_read(r);
|
||||
plugin->fog_mode = iarray_read(r);
|
||||
plugin->fog_type = iarray_read(r);
|
||||
plugin->fog_near = iarray_read_float(r);
|
||||
plugin->fog_far = iarray_read_float(r);
|
||||
read_vec3(r, plugin->fog_color);
|
||||
plugin->ls_beta_r = iarray_read_float(r);
|
||||
plugin->ls_beta_m = iarray_read_float(r);
|
||||
plugin->ls_g = iarray_read_float(r);
|
||||
plugin->ls_distance = iarray_read_float(r);
|
||||
read_vec3(r, plugin->ls_light_dir);
|
||||
read_vec3(r, plugin->ls_light_color);
|
||||
read_vec3(r, plugin->ls_sun_color);
|
||||
plugin->specular_mode = iarray_read(r);
|
||||
plugin->light_map_mode = iarray_read(r);
|
||||
plugin->soft_fog_edge_mode = iarray_read(r);
|
||||
plugin->ssao_mode = iarray_read(r);
|
||||
plugin->vertex_blend_mode = iarray_read(r);
|
||||
plugin->shader_precision_mode = iarray_read(r);
|
||||
plugin->texture_filter_mode = iarray_read(r);
|
||||
plugin->use_power2_texture = iarray_read(r);
|
||||
plugin->shadow_map_resolution_level = iarray_read(r);
|
||||
plugin->texture_resolution_level = iarray_read(r);
|
||||
read_vec3(r, plugin->global_ambient);
|
||||
plugin->post_effect_filter_y = iarray_read_float(r);
|
||||
plugin->post_effect_filter_cb = iarray_read_float(r);
|
||||
plugin->post_effect_filter_cr = iarray_read_float(r);
|
||||
int nr_draw_options = iarray_read(r);
|
||||
for (int i = 0; i < nr_draw_options; i++) {
|
||||
int option = iarray_read(r);
|
||||
if (i < RE_DRAW_OPTION_MAX)
|
||||
plugin->draw_options[i] = option;
|
||||
}
|
||||
plugin->suspended = iarray_read(r);
|
||||
|
||||
plugin->mag_speed = iarray_read(r);
|
||||
int nr_light_params = iarray_read(r);
|
||||
for (int i = 0; i < nr_light_params; i++) {
|
||||
float param = iarray_read_float(r);
|
||||
if (i < RE_NR_LIGHT_PARAMS)
|
||||
plugin->light_params[i] = param;
|
||||
}
|
||||
plugin->edge_length = iarray_read_float(r);
|
||||
plugin->edge_reduction_rate = iarray_read_float(r);
|
||||
read_vec3(r, plugin->edge_color);
|
||||
|
||||
int nr_instances = iarray_read(r);
|
||||
for (int i = 0; i < nr_instances; i++) {
|
||||
if (!iarray_read(r))
|
||||
continue;
|
||||
load_instance(r, RE_create_instance_at(plugin, i));
|
||||
}
|
||||
}
|
||||
+243
-148
@@ -18,7 +18,6 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <libgen.h>
|
||||
#include <dirent.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
@@ -29,10 +28,12 @@
|
||||
#include "system4/cg.h"
|
||||
#include "system4/file.h"
|
||||
#include "system4/hashtable.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/utfsjis.h"
|
||||
|
||||
#include "xsystem4.h"
|
||||
#include "asset_manager.h"
|
||||
#include "gfx/font.h"
|
||||
|
||||
enum archive_type {
|
||||
AR_TYPE_ALD,
|
||||
@@ -50,22 +51,235 @@ const char *asset_strtype(enum asset_type type)
|
||||
case ASSET_FLAT: return "Flat";
|
||||
case ASSET_PACT: return "Pact";
|
||||
case ASSET_DATA: return "Data";
|
||||
case ASSET_FLASH: return "Flash";
|
||||
}
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
static struct archive *archives[ASSET_TYPE_MAX] = {0};
|
||||
#define MAX_ARCHIVES 8
|
||||
|
||||
struct asset_manager {
|
||||
bool (*load_archive)(struct asset_manager *manager, const char *name);
|
||||
bool (*exists_by_id)(struct asset_manager *manager, int id);
|
||||
bool (*exists_by_name)(struct asset_manager *manager, const char *name, int *id_out);
|
||||
struct archive_data *(*get_by_id)(struct asset_manager *manager, int id);
|
||||
struct archive_data *(*get_by_name)(struct asset_manager *manager, const char *name, int *id_out);
|
||||
};
|
||||
|
||||
// XXX: also used for AFAv1 in Daiteikoku, Shaman's Sanctuary
|
||||
struct asset_manager_ald {
|
||||
struct asset_manager manager;
|
||||
struct archive *archive;
|
||||
};
|
||||
|
||||
struct asset_manager_afa {
|
||||
struct asset_manager manager;
|
||||
struct afa_archive *archives[MAX_ARCHIVES];
|
||||
};
|
||||
|
||||
static struct asset_manager *assets[ASSET_TYPE_MAX] = {0};
|
||||
|
||||
bool asset_manager_load_archive(enum asset_type type, const char *archive_name)
|
||||
{
|
||||
if (!assets[type])
|
||||
return false;
|
||||
if (!assets[type]->load_archive)
|
||||
ERROR("load_archive not supported on this archive type");
|
||||
char *path = gamedir_path(archive_name);
|
||||
bool r = assets[type]->load_archive(assets[type], path);
|
||||
free(path);
|
||||
return r;
|
||||
}
|
||||
|
||||
bool asset_exists(enum asset_type type, int id)
|
||||
{
|
||||
if (!assets[type])
|
||||
return false;
|
||||
return assets[type]->exists_by_id(assets[type], id);
|
||||
}
|
||||
|
||||
bool asset_exists_by_name(enum asset_type type, const char *name, int *id_out)
|
||||
{
|
||||
if (!assets[type])
|
||||
return false;
|
||||
if (!assets[type]->exists_by_name)
|
||||
ERROR("exists_by_name not supported on this archive type");
|
||||
return assets[type]->exists_by_name(assets[type], name, id_out);
|
||||
}
|
||||
|
||||
struct archive_data *asset_get(enum asset_type type, int id)
|
||||
{
|
||||
if (!assets[type])
|
||||
return NULL;
|
||||
return assets[type]->get_by_id(assets[type], id);
|
||||
}
|
||||
|
||||
struct archive_data *asset_get_by_name(enum asset_type type, const char *name, int *id_out)
|
||||
{
|
||||
if (!assets[type])
|
||||
return NULL;
|
||||
if (!assets[type]->get_by_name)
|
||||
ERROR("get_by_name not supported on this archive type");
|
||||
return assets[type]->get_by_name(assets[type], name, id_out);
|
||||
}
|
||||
|
||||
struct cg *asset_cg_load(int id)
|
||||
{
|
||||
struct archive_data *data = asset_get(ASSET_CG, id);
|
||||
if (!data)
|
||||
return NULL;
|
||||
struct cg *cg = cg_load_data(data);
|
||||
archive_free_data(data);
|
||||
return cg;
|
||||
}
|
||||
|
||||
struct cg *asset_cg_load_by_name(const char *name, int *id_out)
|
||||
{
|
||||
struct archive_data *data = asset_get_by_name(ASSET_CG, name, id_out);
|
||||
if (!data)
|
||||
return NULL;
|
||||
struct cg *cg = cg_load_data(data);
|
||||
archive_free_data(data);
|
||||
return cg;
|
||||
}
|
||||
|
||||
bool asset_cg_get_metrics(int id, struct cg_metrics *metrics)
|
||||
{
|
||||
struct archive_data *data = asset_get(ASSET_CG, id);
|
||||
if (!data)
|
||||
return false;
|
||||
bool r = cg_get_metrics_data(data, metrics);
|
||||
archive_free_data(data);
|
||||
return r;
|
||||
}
|
||||
|
||||
bool asset_cg_get_metrics_by_name(const char *name, struct cg_metrics *metrics)
|
||||
{
|
||||
struct archive_data *data = asset_get_by_name(ASSET_CG, name, NULL);
|
||||
if (!data)
|
||||
return false;
|
||||
bool r = cg_get_metrics_data(data, metrics);
|
||||
archive_free_data(data);
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool ald_exists_by_id(struct asset_manager *_manager, int id)
|
||||
{
|
||||
struct asset_manager_ald *manager = (struct asset_manager_ald*)_manager;
|
||||
return archive_exists(manager->archive, id - 1);
|
||||
}
|
||||
|
||||
static struct archive_data *ald_get_by_id(struct asset_manager *_manager, int id)
|
||||
{
|
||||
struct asset_manager_ald *manager = (struct asset_manager_ald*)_manager;
|
||||
return archive_get(manager->archive, id - 1);
|
||||
}
|
||||
|
||||
static bool afa_load_archive(struct asset_manager *_manager, const char *name)
|
||||
{
|
||||
struct asset_manager_afa *manager = (struct asset_manager_afa*)_manager;
|
||||
if (manager->archives[MAX_ARCHIVES-1])
|
||||
ERROR("Archive limit exceeded");
|
||||
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, PATH_MAX, "%s.afa", name);
|
||||
|
||||
int error;
|
||||
struct afa_archive *ar = afa_open(path, MMAP_IF_64BIT, &error);
|
||||
if (!ar) {
|
||||
snprintf(path, PATH_MAX, "%s.AFA", name);
|
||||
ar = afa_open(path, MMAP_IF_64BIT, &error);
|
||||
if (!ar) {
|
||||
WARNING("Failed to open archive: %s", display_utf0(path));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = MAX_ARCHIVES - 1; i > 0; i--) {
|
||||
manager->archives[i] = manager->archives[i-1];
|
||||
}
|
||||
manager->archives[0] = ar;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool afa_exists_by_id(struct asset_manager *_manager, int id)
|
||||
{
|
||||
struct asset_manager_afa *manager = (struct asset_manager_afa*)_manager;
|
||||
for (int i = 0; i < MAX_ARCHIVES && manager->archives[i]; i++) {
|
||||
if (archive_exists(&manager->archives[i]->ar, id - 1))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct archive_data *afa_get_by_id(struct asset_manager *_manager, int id)
|
||||
{
|
||||
struct asset_manager_afa *manager = (struct asset_manager_afa*)_manager;
|
||||
for (int i = 0; i < MAX_ARCHIVES && manager->archives[i]; i++) {
|
||||
struct archive_data *data = archive_get(&manager->archives[i]->ar, id - 1);
|
||||
if (data)
|
||||
return data;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct archive_data *afa_get_by_name(struct asset_manager *_manager, const char *name,
|
||||
int *id_out)
|
||||
{
|
||||
struct asset_manager_afa *manager = (struct asset_manager_afa*)_manager;
|
||||
|
||||
for (unsigned i = 0; i < MAX_ARCHIVES && manager->archives[i]; i++) {
|
||||
struct archive_data *data;
|
||||
data = archive_get_by_basename(&manager->archives[i]->ar, name);
|
||||
if (data) {
|
||||
if (id_out)
|
||||
*id_out = data->no + 1;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool afa_exists_by_name(struct asset_manager *_manager, const char *name, int *id_out)
|
||||
{
|
||||
struct asset_manager_afa *manager = (struct asset_manager_afa*)_manager;
|
||||
|
||||
for (unsigned i = 0; i < MAX_ARCHIVES && manager->archives[i]; i++) {
|
||||
if (archive_exists_by_basename(&manager->archives[i]->ar, name, id_out)) {
|
||||
if (id_out)
|
||||
*id_out += 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void _ald_init(enum asset_type type, struct archive *ar)
|
||||
{
|
||||
struct asset_manager_ald *manager = xcalloc(1, sizeof(struct asset_manager_ald));
|
||||
manager->manager.exists_by_id = ald_exists_by_id;
|
||||
manager->manager.get_by_id = ald_get_by_id;
|
||||
manager->manager.load_archive = NULL;
|
||||
manager->manager.exists_by_name = NULL;
|
||||
manager->manager.get_by_name = NULL;
|
||||
manager->archive = ar;
|
||||
assets[type] = &manager->manager;
|
||||
}
|
||||
|
||||
static void ald_init(enum asset_type type, char **files, int count)
|
||||
{
|
||||
if (count < 1)
|
||||
return;
|
||||
if (archives[type])
|
||||
if (assets[type])
|
||||
WARNING("Multiple asset archives for type %s", asset_strtype(type));
|
||||
int error = ARCHIVE_SUCCESS;
|
||||
archives[type] = ald_open(files, count, ARCHIVE_MMAP, &error);
|
||||
if (error)
|
||||
|
||||
int error;
|
||||
struct archive *ar = ald_open(files, count, MMAP_IF_64BIT, &error);
|
||||
if (!ar)
|
||||
ERROR("Failed to open ALD file: %s", archive_strerror(error));
|
||||
|
||||
_ald_init(type, ar);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
free(files[i]);
|
||||
}
|
||||
@@ -75,13 +289,23 @@ static void afa_init(enum asset_type type, char *file)
|
||||
{
|
||||
if (!file)
|
||||
return;
|
||||
if (archives[type])
|
||||
if (assets[type])
|
||||
WARNING("Multiple asset archives for type %s", asset_strtype(type));
|
||||
int error = ARCHIVE_SUCCESS;
|
||||
archives[type] = (struct archive*)afa_open(file, ARCHIVE_MMAP, &error);
|
||||
if (error != ARCHIVE_SUCCESS) {
|
||||
|
||||
int error;
|
||||
struct afa_archive *ar = afa_open(file, MMAP_IF_64BIT, &error);
|
||||
if (!ar)
|
||||
ERROR("Failed to open AFA file: %s", archive_strerror(error));
|
||||
}
|
||||
|
||||
struct asset_manager_afa *manager = xcalloc(1, sizeof(struct asset_manager_afa));
|
||||
manager->manager.load_archive = afa_load_archive;
|
||||
manager->manager.exists_by_id = afa_exists_by_id;
|
||||
manager->manager.get_by_id = afa_get_by_id;
|
||||
manager->manager.exists_by_name = afa_exists_by_name;
|
||||
manager->manager.get_by_name = afa_get_by_name;
|
||||
manager->archives[0] = ar;
|
||||
assets[type] = &manager->manager;
|
||||
|
||||
free(file);
|
||||
}
|
||||
|
||||
@@ -113,7 +337,7 @@ void asset_manager_init(void)
|
||||
// get ALD filenames
|
||||
while ((d_name = readdir_utf8(dir))) {
|
||||
// archive filename must begin with ain filename
|
||||
if (strncmp(base, d_name, base_len))
|
||||
if (strncasecmp(base, d_name, base_len))
|
||||
goto loop_next;
|
||||
const char *ext = file_extension(d_name);
|
||||
if (!ext)
|
||||
@@ -182,7 +406,12 @@ void asset_manager_init(void)
|
||||
afa_filenames[ASSET_FLAT] = path_join(config.game_dir, d_name);
|
||||
} else if (!strcmp(type, "Pact.afa")) {
|
||||
afa_filenames[ASSET_PACT] = path_join(config.game_dir, d_name);
|
||||
} else if (!strcmp(type, "AFF.afa")) {
|
||||
afa_filenames[ASSET_FLASH] = path_join(config.game_dir, d_name);
|
||||
}
|
||||
} else if (!strcasecmp(ext, "fnl")) {
|
||||
if (!config.fnl_path)
|
||||
config.fnl_path = path_join(config.game_dir, d_name);
|
||||
}
|
||||
loop_next:
|
||||
free(d_name);
|
||||
@@ -198,6 +427,7 @@ void asset_manager_init(void)
|
||||
ald_init(ASSET_FLAT, ald_filenames[ASSET_FLAT], ald_count[ASSET_FLAT]);
|
||||
ald_init(ASSET_PACT, ald_filenames[ASSET_PACT], ald_count[ASSET_PACT]);
|
||||
ald_init(ASSET_DATA, ald_filenames[ASSET_DATA], ald_count[ASSET_DATA]);
|
||||
ald_init(ASSET_FLASH, ald_filenames[ASSET_FLASH], ald_count[ASSET_FLASH]);
|
||||
|
||||
// open AFA archives
|
||||
afa_init(ASSET_BGM, afa_filenames[ASSET_BGM]);
|
||||
@@ -207,140 +437,5 @@ void asset_manager_init(void)
|
||||
afa_init(ASSET_FLAT, afa_filenames[ASSET_FLAT]);
|
||||
afa_init(ASSET_PACT, afa_filenames[ASSET_PACT]);
|
||||
afa_init(ASSET_DATA, afa_filenames[ASSET_DATA]);
|
||||
afa_init(ASSET_FLASH, afa_filenames[ASSET_FLASH]);
|
||||
}
|
||||
|
||||
bool asset_exists(enum asset_type type, int no)
|
||||
{
|
||||
return archives[type] && archive_exists(archives[type], no);
|
||||
}
|
||||
|
||||
struct archive_data *asset_get(enum asset_type type, int no)
|
||||
{
|
||||
if (!archives[type])
|
||||
return NULL;
|
||||
return archive_get(archives[type], no);
|
||||
}
|
||||
|
||||
static struct hash_table *cg_name_index = NULL;
|
||||
static struct hash_table *cg_id_index = NULL;
|
||||
|
||||
// Convert the last sequence of digits in a string to an integer.
|
||||
static int cg_name_to_int(const char *name)
|
||||
{
|
||||
size_t len = strlen(name);
|
||||
int i;
|
||||
for (i = len - 1; i >= 0 && !isdigit(name[i]); i--);
|
||||
if (i < 0)
|
||||
return -1;
|
||||
assert(isdigit(name[i]));
|
||||
for (; i > 0 && isdigit(name[i-1]); i--);
|
||||
assert(isdigit(name[i]));
|
||||
|
||||
if (!(i = atoi(name+i)))
|
||||
return -1;
|
||||
return i;
|
||||
}
|
||||
|
||||
static void add_cg_to_index(struct archive_data *data, possibly_unused void *_)
|
||||
{
|
||||
int logical_no = cg_name_to_int(data->name);
|
||||
if (logical_no < 0) {
|
||||
WARNING("Can't determine logical index for CG: %s", display_sjis0(data->name));
|
||||
return;
|
||||
}
|
||||
|
||||
struct ht_slot *slot = ht_put_int(cg_id_index, logical_no, NULL);
|
||||
if (slot->value)
|
||||
ERROR("Duplicate CG numbers");
|
||||
slot->value = (void*)(uintptr_t)(data->no + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: Starting in Shaman's Sanctuary, .afa files are used for CGs but the
|
||||
* library APIs still use integers rather than names to reference them.
|
||||
* Thus it is necessary to parse file names and create an index.
|
||||
*/
|
||||
void asset_cg_index_init(void)
|
||||
{
|
||||
if (!archives[ASSET_CG])
|
||||
return;
|
||||
|
||||
cg_id_index = ht_create(4096);
|
||||
archive_for_each(archives[ASSET_CG], add_cg_to_index, NULL);
|
||||
}
|
||||
|
||||
static void add_cg_name_to_index(struct archive_data *data, possibly_unused void *_)
|
||||
{
|
||||
// copy name and strip file extension
|
||||
char *name = strdup(data->name);
|
||||
char *dot = strrchr(name, '.');
|
||||
if (dot)
|
||||
*dot = '\0';
|
||||
|
||||
struct ht_slot *slot = ht_put(cg_name_index, name, NULL);
|
||||
if (slot->value)
|
||||
ERROR("Duplicate CG names");
|
||||
slot->value = (void*)(uintptr_t)(data->no + 1);
|
||||
|
||||
free(name);
|
||||
}
|
||||
|
||||
static void init_cg_name_index(void)
|
||||
{
|
||||
if (!archives[ASSET_CG])
|
||||
return;
|
||||
|
||||
cg_name_index = ht_create(4096);
|
||||
archive_for_each(archives[ASSET_CG], add_cg_name_to_index, NULL);
|
||||
}
|
||||
|
||||
int asset_cg_name_to_index(const char *name)
|
||||
{
|
||||
if (!cg_name_index)
|
||||
init_cg_name_index();
|
||||
return (uintptr_t)ht_get(cg_name_index, name, NULL);
|
||||
}
|
||||
|
||||
static int cg_translate_index(int no)
|
||||
{
|
||||
if (!cg_id_index)
|
||||
return no;
|
||||
return (uintptr_t)ht_get_int(cg_id_index, no, NULL);
|
||||
}
|
||||
|
||||
struct cg *asset_cg_load(int no)
|
||||
{
|
||||
if (!archives[ASSET_CG])
|
||||
return NULL;
|
||||
if (!(no = cg_translate_index(no)))
|
||||
return NULL;
|
||||
return cg_load(archives[ASSET_CG], no - 1);
|
||||
}
|
||||
|
||||
struct cg *asset_cg_load_by_name(const char *name, int *no)
|
||||
{
|
||||
if (!archives[ASSET_CG])
|
||||
return NULL;
|
||||
if (!(*no = asset_cg_name_to_index(name)))
|
||||
return NULL;
|
||||
return cg_load(archives[ASSET_CG], *no - 1);
|
||||
}
|
||||
|
||||
bool asset_cg_exists(int no)
|
||||
{
|
||||
if (!archives[ASSET_CG])
|
||||
return false;
|
||||
if (!(no = cg_translate_index(no)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asset_cg_get_metrics(int no, struct cg_metrics *metrics)
|
||||
{
|
||||
if (!archives[ASSET_CG])
|
||||
return false;
|
||||
if (!(no = cg_translate_index(no)))
|
||||
return NULL;
|
||||
return cg_get_metrics(archives[ASSET_CG], no - 1, metrics);
|
||||
}
|
||||
|
||||
|
||||
+104
-14
@@ -39,14 +39,32 @@ static int anonymous_channels[NR_ANONYMOUS_CHANNELS];
|
||||
|
||||
void audio_init(void)
|
||||
{
|
||||
id_pool_init(&wav);
|
||||
id_pool_init(&bgm);
|
||||
static bool audio_initialized = false;
|
||||
if (audio_initialized)
|
||||
return;
|
||||
|
||||
id_pool_init(&wav, 0);
|
||||
id_pool_init(&bgm, 0);
|
||||
|
||||
for (int i = 0; i < NR_ANONYMOUS_CHANNELS; i++) {
|
||||
anonymous_channels[i] = -1;
|
||||
}
|
||||
|
||||
mixer_init();
|
||||
audio_initialized = true;
|
||||
}
|
||||
|
||||
void audio_reset(void)
|
||||
{
|
||||
for (int i = id_pool_get_first(&wav); i >= 0; i = id_pool_get_next(&wav, i)) {
|
||||
wav_unprepare(i);
|
||||
}
|
||||
for (int i = id_pool_get_first(&bgm); i >= 0; i = id_pool_get_next(&bgm, i)) {
|
||||
bgm_unprepare(i);
|
||||
}
|
||||
for (int i = 0; i < NR_ANONYMOUS_CHANNELS; i++) {
|
||||
anonymous_channels[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void audio_update(void)
|
||||
@@ -80,8 +98,27 @@ bool audio_play_sound(int sound_no)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool wav_exists(int no) { return asset_exists(ASSET_SOUND, no-1); }
|
||||
bool bgm_exists(int no) { return asset_exists(ASSET_BGM, no-1); }
|
||||
bool audio_play_archive_data(struct archive_data *dfile)
|
||||
{
|
||||
for (int i = 0; i < NR_ANONYMOUS_CHANNELS; i++) {
|
||||
if (anonymous_channels[i] == -1) {
|
||||
int ch = wav_get_unused_channel();
|
||||
if (!wav_prepare_from_archive_data(ch, AUDIO_NO_METADATA, dfile))
|
||||
return false;
|
||||
if (!wav_play(ch)) {
|
||||
wav_unprepare(ch);
|
||||
return false;
|
||||
}
|
||||
anonymous_channels[i] = ch;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
WARNING("Failed to allocate anonymous audio channel");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool wav_exists(int no) { return asset_exists(ASSET_SOUND, no); }
|
||||
bool bgm_exists(int no) { return asset_exists(ASSET_BGM, no); }
|
||||
|
||||
static int audio_prepare(struct id_pool *pool, int id, enum asset_type type, int no)
|
||||
{
|
||||
@@ -99,11 +136,10 @@ int bgm_prepare(int id, int no) { return audio_prepare(&bgm, id, ASSET_BGM, no);
|
||||
|
||||
static int audio_unprepare(struct id_pool *pool, int id)
|
||||
{
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
struct channel *ch = id_pool_release(pool, id);
|
||||
if (!ch)
|
||||
return 0;
|
||||
channel_close(ch);
|
||||
id_pool_release(pool, id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -325,22 +361,76 @@ static int audio_get_time_length(struct id_pool *pool, int id)
|
||||
int wav_get_time_length(int id) { return audio_get_time_length(&wav, id); }
|
||||
int bgm_get_time_length(int id) { return audio_get_time_length(&bgm, id); }
|
||||
|
||||
//int wav_get_group_num(int channel);
|
||||
//int bgm_get_group_num(int channel);
|
||||
//int wav_prepare_from_file(int channel, char *filename);
|
||||
//int bgm_prepare_from_file(int channel, char *filename);
|
||||
static int audio_get_data_length(enum asset_type type, int no) {
|
||||
struct channel *ch = channel_open(type, no);
|
||||
if (!ch)
|
||||
return 0;
|
||||
int len = channel_get_time_length(ch);
|
||||
channel_close(ch);
|
||||
return len;
|
||||
}
|
||||
|
||||
static int audio_prepare_from_archive_data(struct id_pool *pool, int id, struct archive_data *dfile)
|
||||
int wav_get_data_length(int no) { return audio_get_data_length(ASSET_SOUND, no); }
|
||||
int bgm_get_data_length(int no) { return audio_get_data_length(ASSET_BGM, no); }
|
||||
|
||||
static int audio_get_data_no(struct id_pool *pool, int id)
|
||||
{
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch)
|
||||
return -1;
|
||||
return channel_get_data_no(ch);
|
||||
}
|
||||
|
||||
int wav_get_group_num(int id)
|
||||
{
|
||||
int no = audio_get_data_no(&wav, id);
|
||||
if (no < 0)
|
||||
return 0;
|
||||
return wav_get_group_num_from_data_num(no);
|
||||
}
|
||||
//int bgm_get_group_num(int channel);
|
||||
|
||||
static int audio_prepare_from_file(struct id_pool *pool, int id, char *filename)
|
||||
{
|
||||
if (id < 0)
|
||||
return 0;
|
||||
struct channel *ch = channel_open_archive_data(dfile);
|
||||
struct channel *ch = channel_open_file(filename);
|
||||
struct channel *old = id_pool_set(pool, id, ch);
|
||||
if (old)
|
||||
channel_close(old);
|
||||
return !!ch;
|
||||
}
|
||||
|
||||
int wav_prepare_from_archive_data(int id, struct archive_data *dfile) {
|
||||
return audio_prepare_from_archive_data(&wav, id, dfile);
|
||||
int wav_prepare_from_file(int id, char *filename) { return audio_prepare_from_file(&wav, id, filename); }
|
||||
int bgm_prepare_from_file(int id, char *filename) { return audio_prepare_from_file(&bgm, id, filename); }
|
||||
|
||||
static int audio_prepare_from_archive_data(struct id_pool *pool, int id,
|
||||
enum asset_type type, int metadata_no,
|
||||
struct archive_data *dfile)
|
||||
{
|
||||
if (id < 0)
|
||||
return 0;
|
||||
struct channel *ch = channel_open_archive_data(dfile, type, metadata_no);
|
||||
struct channel *old = id_pool_set(pool, id, ch);
|
||||
if (old)
|
||||
channel_close(old);
|
||||
return !!ch;
|
||||
}
|
||||
|
||||
int wav_prepare_from_archive_data(int id, int metadata_no, struct archive_data *dfile)
|
||||
{
|
||||
return audio_prepare_from_archive_data(
|
||||
&wav, id, ASSET_SOUND, metadata_no, dfile);
|
||||
}
|
||||
|
||||
int bgm_prepare_from_archive_data(int id, int metadata_no, struct archive_data *dfile)
|
||||
{
|
||||
return audio_prepare_from_archive_data(
|
||||
&bgm, id, ASSET_BGM, metadata_no, dfile);
|
||||
}
|
||||
|
||||
int wav_get_group_num_from_data_num(int no)
|
||||
{
|
||||
struct wai *wai = wai_get(no);
|
||||
return wai ? wai->channel : 0;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user