mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-09-25 00:17:56 +03:00
Compare commits
810
Commits
alpha-411022b
...
1.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
ac248c310c | ||
|
|
38df5ea1c1 | ||
|
|
b45e8e73dc | ||
|
|
48b53881de | ||
|
|
11e4880def | ||
|
|
e88ae62c08 | ||
|
|
8a131d416b | ||
|
|
2ca1ce14c5 | ||
|
|
5fe61c14d9 | ||
|
|
1f239c766f | ||
|
|
6c8b674f0e | ||
|
|
89d8210c14 | ||
|
|
6118eef06a | ||
|
|
888b491b38 | ||
|
|
26c8f399cb | ||
|
|
c73842ae75 | ||
|
|
fbfa5bbb5f | ||
|
|
f01f002a54 | ||
|
|
5713742a99 | ||
|
|
47cb99927d | ||
|
|
ae0832921f | ||
|
|
9fbc9ec5b7 | ||
|
|
0780442e13 | ||
|
|
ba0b97e163 | ||
|
|
73eb1d0e34 | ||
|
|
5dfb82afd7 | ||
|
|
5b2f9d7536 | ||
|
|
9904754f06 | ||
|
|
6f750e4007 | ||
|
|
f0fdb714e5 |
+222
@@ -0,0 +1,222 @@
|
||||
# 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/parser.c
|
||||
src/3d/particle.c
|
||||
src/3d/reign.c
|
||||
src/3d/renderer.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/input.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/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/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/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/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/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.
|
||||
|
||||
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,84 @@
|
||||
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 バインドシーカー | Unsupported | |
|
||||
| どらぺこ! ~おねだりドラゴンとおっぱい勇者~ | Unsupported | |
|
||||
| ランス01 光をもとめて | Supported | |
|
||||
| Rance 01 - Quest for Hikari (EN) | Supported | |
|
||||
| All games released after Rance 01 | Unsupported | |
|
||||
@@ -30,20 +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);
|
||||
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 */
|
||||
|
||||
+9
-5
@@ -19,10 +19,14 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
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,12 +78,12 @@ 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_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);
|
||||
|
||||
@@ -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,7 +210,9 @@ 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);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
|
||||
|
||||
+95
-3
@@ -18,16 +18,108 @@
|
||||
#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;
|
||||
void (*cb)(struct breakpoint*);
|
||||
char *message;
|
||||
int count;
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
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(void);
|
||||
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);
|
||||
|
||||
enum opcode dbg_handle_breakpoint(unsigned bp_no);
|
||||
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);
|
||||
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);
|
||||
|
||||
|
||||
+9
-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,15 @@ enum effect {
|
||||
EFFECT_BLUR_FADEOUT = 50,
|
||||
EFFECT_BLUR_CROSSFADE = 51,
|
||||
EFFECT_2ROT_ZOOM_BLEND_BLUR = 52,
|
||||
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,183 @@
|
||||
/* 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(struct text_style *ts, const char *ch);
|
||||
float gfx_size_char_kerning(struct text_style *ts, uint32_t code, uint32_t code_next);
|
||||
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_width(struct text_style *ts, const char *ch)
|
||||
{
|
||||
return (gfx_size_char(ts, ch) + (ts->bold_width * 2) + ts->edge_left + ts->edge_right)
|
||||
* ts->scale_x;
|
||||
}
|
||||
|
||||
static inline float text_style_height(struct text_style *ts)
|
||||
{
|
||||
return ts->size + (ts->bold_width * 2) + ts->edge_up + ts->edge_down;
|
||||
}
|
||||
|
||||
#endif /* SYSTEM4_FONT_H */
|
||||
+58
-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,19 +47,31 @@ 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);
|
||||
|
||||
@@ -70,17 +79,24 @@ Texture *gfx_main_surface(void);
|
||||
void gfx_set_window_logical_size(int w, int h);
|
||||
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 +104,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 +128,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 +180,12 @@ 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 font_weight {
|
||||
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;
|
||||
enum font_weight 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(enum font_weight 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);
|
||||
enum font_weight 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_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);
|
||||
|
||||
|
||||
+3
-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);
|
||||
|
||||
@@ -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 */
|
||||
@@ -43,11 +43,17 @@ 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;
|
||||
@@ -55,6 +61,7 @@ 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);
|
||||
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 +84,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__
|
||||
+235
@@ -0,0 +1,235 @@
|
||||
/* 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_PARTS_H
|
||||
#define SYSTEM4_PARTS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct page;
|
||||
struct string;
|
||||
|
||||
// parts.c
|
||||
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);
|
||||
int PE_GetPartsCGNumber(int parts_no, 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_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_SetPos(int parts_no, int x, int y);
|
||||
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);
|
||||
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);
|
||||
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 parts_no);
|
||||
void PE_SetSpeedupRateByMessageSkip(int parts_no, int rate);
|
||||
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);
|
||||
float PE_GetPartsRotateZ(int parts_no);
|
||||
void PE_SetPartsPixelDecide(int PartsNumber, bool pixel_decide);
|
||||
bool PE_SetThumbnailReductionSize(int reduction_size);
|
||||
bool PE_SetThumbnailMode(bool Mode);
|
||||
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);
|
||||
// GUIEngine
|
||||
int PE_GetFreeNumber(void);
|
||||
bool PE_IsExist(int parts_no);
|
||||
|
||||
// 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_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_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_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_SetClickable(int parts_no, bool clickable);
|
||||
bool PE_GetPartsClickable(int parts_no);
|
||||
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);
|
||||
|
||||
// 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_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);
|
||||
|
||||
// text.c
|
||||
bool PE_SetText(int parts_no, struct string *text, 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);
|
||||
bool PE_SetPartsFontColor(int parts_no, int r, int g, int b, int state);
|
||||
bool PE_SetPartsFontBoldWeight(int parts_no, float bold_weight, int state);
|
||||
bool PE_SetPartsFontEdgeColor(int parts_no, int r, int g, int b, int state);
|
||||
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_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);
|
||||
|
||||
#endif /* SYSTEM4_PARTS_H */
|
||||
+4265
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 */
|
||||
+309
@@ -0,0 +1,309 @@
|
||||
/* 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
|
||||
|
||||
#include <cglm/types.h>
|
||||
|
||||
#include "gfx/gfx.h"
|
||||
#include "plugin.h"
|
||||
|
||||
typedef struct cJSON cJSON;
|
||||
struct hash_table;
|
||||
|
||||
enum RE_plugin_version {
|
||||
RE_REIGN_PLUGIN, // Toushin Toshi 3
|
||||
RE_TAPIR_PLUGIN, // Rance Quest
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
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_MAX
|
||||
};
|
||||
|
||||
enum RE_draw_edge_mode {
|
||||
RE_DRAW_EDGE_NONE = 0,
|
||||
RE_DRAW_EDGE_CHARACTERS_ONLY = 1,
|
||||
RE_DRAW_EDGE_ALL = 2,
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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;
|
||||
int sprite;
|
||||
int nr_instances;
|
||||
struct RE_instance **instances;
|
||||
struct archive *aar;
|
||||
struct hash_table *model_cache;
|
||||
struct hash_table *pae_cache;
|
||||
struct RE_renderer *renderer;
|
||||
struct RE_camera camera;
|
||||
mat4 proj_transform;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
struct RE_instance {
|
||||
struct RE_plugin *plugin;
|
||||
struct model *model;
|
||||
struct particle_effect *effect;
|
||||
struct motion *motion;
|
||||
struct motion *next_motion;
|
||||
struct height_detector *height_detector;
|
||||
|
||||
enum RE_instance_type type;
|
||||
int target[RE_NR_INSTANCE_TARGETS];
|
||||
vec3 pos;
|
||||
float pitch, roll, yaw; // in degrees
|
||||
vec3 scale;
|
||||
float alpha;
|
||||
bool draw;
|
||||
bool draw_shadow;
|
||||
bool make_shadow;
|
||||
float shadow_volume_bone_radius;
|
||||
bool draw_bump;
|
||||
float fps;
|
||||
bool motion_blend;
|
||||
float motion_blend_rate;
|
||||
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;
|
||||
|
||||
// Lights
|
||||
vec3 vec;
|
||||
vec3 diffuse;
|
||||
vec3 globe_diffuse;
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
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);
|
||||
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_load(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);
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
#endif /* SYSTEM4_REIGN_H */
|
||||
+24
-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,12 @@ 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;
|
||||
bool StoatSpriteEngine_SP_SetTextSprite(int sp_no, struct string *text);
|
||||
void StoatSpriteEngine_SP_SetTextSpriteType(int type);
|
||||
void StoatSpriteEngine_SP_SetTextSpriteSize(int size);
|
||||
@@ -109,6 +126,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);
|
||||
|
||||
|
||||
+12
-1
@@ -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;
|
||||
@@ -44,12 +51,16 @@ extern bool scene_is_dirty;
|
||||
void scene_register_sprite(struct sprite *sp);
|
||||
void scene_unregister_sprite(struct sprite *sp);
|
||||
void scene_render(void);
|
||||
void scene_flip(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;
|
||||
|
||||
|
||||
+18
-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,9 +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,22 +43,58 @@ 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;
|
||||
|
||||
// Convenience functions to avoid cumbersome memory management when passing
|
||||
// text to printf-like functions
|
||||
const char *display_sjis0(const char *sjis);
|
||||
const char *display_sjis1(const char *sjis);
|
||||
const char *display_sjis2(const char *sjis);
|
||||
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 */
|
||||
|
||||
+32
-15
@@ -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') / 'share' / 'xsystem4"', language : 'c')
|
||||
add_project_arguments('-DXSYS4_DATA_DIR="' + get_option('prefix') / get_option('datadir') / 'xsystem4"', language : 'c')
|
||||
|
||||
zlib = dependency('zlib')
|
||||
if get_option('buildtype').startswith('release')
|
||||
add_project_arguments('-DRELEASE', language : 'c')
|
||||
endif
|
||||
|
||||
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')
|
||||
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')
|
||||
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)
|
||||
|
||||
if get_option('use_gles')
|
||||
gles = dependency('glesv2')
|
||||
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,7 +39,8 @@ else
|
||||
gl_deps = [gl, glew]
|
||||
endif
|
||||
|
||||
chibi = dependency('chibi-scheme', required : get_option('debugger'))
|
||||
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')
|
||||
@@ -36,5 +52,6 @@ libsys4_dep = libsys4_proj.get_variable('libsys4_dep')
|
||||
|
||||
subdir('src')
|
||||
|
||||
install_subdir('shaders', install_dir : 'share' / 'xsystem4')
|
||||
install_subdir('fonts', install_dir : 'share' / 'xsystem4')
|
||||
install_subdir('shaders', install_dir : get_option('datadir') / 'xsystem4')
|
||||
install_subdir('fonts', install_dir : get_option('datadir') / 'xsystem4')
|
||||
install_data('debugger.scm', install_dir : get_option('datadir') / 'xsystem4')
|
||||
|
||||
+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,25 @@
|
||||
/* 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() {
|
||||
float gray = dot(texture(tex, tex_coord).rgb, vec3(0.299, 0.587, 0.114));
|
||||
frag_color = vec4(vec3(gray), 1);
|
||||
}
|
||||
@@ -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) 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 EFFECT_WIDTH 210.0
|
||||
|
||||
void main() {
|
||||
// start position of effect
|
||||
float lhs = ((resolution.x + EFFECT_WIDTH) * progress) - EFFECT_WIDTH;
|
||||
// current pixel x-coordinate
|
||||
float p = tex_coord.x * resolution.x;
|
||||
// calculate alpha value
|
||||
float alpha = clamp((p - lhs) / EFFECT_WIDTH, 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,36 @@
|
||||
/* 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 EFFECT_HEIGHT 210.0
|
||||
|
||||
void main() {
|
||||
// start position of effect
|
||||
float top = ((resolution.y + EFFECT_HEIGHT) * progress) - EFFECT_HEIGHT;
|
||||
// current pixel y-coordinate
|
||||
float p = tex_coord.y * resolution.y;
|
||||
// calculate alpha value
|
||||
float alpha = 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);
|
||||
}
|
||||
@@ -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,39 @@
|
||||
/* 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;
|
||||
|
||||
vec3 get_pixel(vec2 xy) {
|
||||
return mix(texture(old, xy).rgb, texture(tex, xy).rgb, progress);
|
||||
}
|
||||
|
||||
float triangle(float f) {
|
||||
return 1.0 - abs(f * 2.0 - 1.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 p = tex_coord * resolution;
|
||||
float scale = triangle(progress) * 80.0; // (0..0.5..1) -> (0..80..0);
|
||||
float wobble = progress * 24.0;
|
||||
float offset = sin((p.y / 100.0) - wobble) * scale;
|
||||
frag_color = vec4(get_pixel(fract(vec2(p.x + offset, p.y) / resolution)), 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,40 @@
|
||||
/* 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;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
float point_in_rect(vec2 p, vec2 bot_left, vec2 top_right) {
|
||||
vec2 s = step(bot_left, p) - step(top_right, p);
|
||||
return s.x * s.y;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 size = vec2(textureSize(tex, 0));
|
||||
vec2 bl = bot_left / size;
|
||||
vec2 tr = top_right / size;
|
||||
|
||||
vec4 tex_color = texture(tex, tex_coord);
|
||||
vec3 mod_color = (tex_color.rgb + add_color) * multiply_color;
|
||||
frag_color = vec4(mod_color, tex_color.a * blend_rate) * point_in_rect(tex_coord, bl, tr);
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/* 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
|
||||
|
||||
#if ENGINE == REIGN_ENGINE
|
||||
|
||||
#define NR_DIR_LIGHTS 3
|
||||
#define FOG_LINEAR 1
|
||||
#define FOG_LIGHT_SCATTERING 2
|
||||
|
||||
struct dir_light {
|
||||
vec3 dir;
|
||||
vec3 diffuse;
|
||||
vec3 globe_diffuse;
|
||||
};
|
||||
|
||||
uniform sampler2D specular_texture;
|
||||
uniform dir_light dir_lights[NR_DIR_LIGHTS];
|
||||
uniform float specular_strength;
|
||||
uniform float specular_shininess;
|
||||
uniform bool use_specular_map;
|
||||
uniform float rim_exponent;
|
||||
uniform vec3 rim_color;
|
||||
uniform int fog_type;
|
||||
uniform float fog_near;
|
||||
uniform float fog_far;
|
||||
uniform vec3 fog_color;
|
||||
in vec3 light_dir[NR_DIR_LIGHTS];
|
||||
in vec3 specular_dir;
|
||||
in vec3 ls_ex;
|
||||
in vec3 ls_in;
|
||||
|
||||
uniform vec3 global_ambient;
|
||||
|
||||
vec3 dir_lights_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;
|
||||
}
|
||||
|
||||
#else // ENGINE == REIGN_ENGINE
|
||||
|
||||
const vec3 global_ambient = vec3(0.0);
|
||||
|
||||
vec3 dir_lights_diffuse(vec3 norm)
|
||||
{
|
||||
return vec3(1.0);
|
||||
}
|
||||
|
||||
#endif // ENGINE == REIGN_ENGINE
|
||||
|
||||
uniform sampler2D tex;
|
||||
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 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;
|
||||
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;
|
||||
if (diffuse_type == DIFFUSE_ENV_MAP) {
|
||||
texel = texture(tex, (vec2(normal.x, -normal.y) + 1.0) / 2.0);
|
||||
frag_rgb = texel.rgb;
|
||||
} else {
|
||||
texel = texture(tex, tex_coord);
|
||||
if (diffuse_type == DIFFUSE_EMISSIVE) {
|
||||
frag_rgb = texel.rgb;
|
||||
} else {
|
||||
vec3 diffuse = dir_lights_diffuse(norm);
|
||||
if (diffuse_type == DIFFUSE_LIGHT_MAP) {
|
||||
diffuse *= texture(light_texture, light_tex_coord).rgb;
|
||||
}
|
||||
frag_rgb += texel.rgb * diffuse * color_mod.rgb;
|
||||
}
|
||||
}
|
||||
|
||||
#if ENGINE == REIGN_ENGINE
|
||||
// Specular lighting
|
||||
if (specular_strength > 0.0) {
|
||||
vec3 reflect_dir = reflect(specular_dir, norm);
|
||||
float specular = pow(max(dot(view_dir, reflect_dir), 0.0), specular_shininess) * specular_strength;
|
||||
if (use_specular_map) {
|
||||
specular *= texture(specular_texture, tex_coord).r;
|
||||
}
|
||||
frag_rgb += vec3(specular);
|
||||
}
|
||||
|
||||
// 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);
|
||||
} else 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;
|
||||
}
|
||||
|
||||
// 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,153 @@
|
||||
/* 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;
|
||||
|
||||
#if ENGINE == REIGN_ENGINE
|
||||
|
||||
#define NR_DIR_LIGHTS 3
|
||||
#define FOG_LIGHT_SCATTERING 2
|
||||
|
||||
struct dir_light {
|
||||
vec3 dir;
|
||||
vec3 diffuse;
|
||||
vec3 globe_diffuse;
|
||||
};
|
||||
|
||||
uniform dir_light dir_lights[NR_DIR_LIGHTS];
|
||||
uniform vec3 specular_light_dir;
|
||||
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 light_dir[NR_DIR_LIGHTS];
|
||||
out vec3 specular_dir;
|
||||
out vec3 ls_ex;
|
||||
out vec3 ls_in;
|
||||
|
||||
const vec2 uv_scroll = vec2(0.0);
|
||||
|
||||
#else // ENGINE == REIGN_ENGINE
|
||||
|
||||
uniform vec2 uv_scroll;
|
||||
|
||||
#endif // ENGINE == REIGN_ENGINE
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
light_tex_coord = vertex_light_uv + uv_scroll;
|
||||
color_mod = vertex_color;
|
||||
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 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;
|
||||
specular_dir = TBN * specular_light_dir;
|
||||
|
||||
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,582 @@
|
||||
/* 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;
|
||||
struct hash_table *bone_map; // bone id in POL/MOT -> struct bone *
|
||||
struct hash_table *bone_name_map; // bone name -> (struct bone * | NULL)
|
||||
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;
|
||||
};
|
||||
|
||||
struct material {
|
||||
uint32_t flags;
|
||||
bool is_transparent;
|
||||
int nr_color_maps;
|
||||
GLuint *color_maps;
|
||||
GLuint specular_map;
|
||||
GLuint alpha_map;
|
||||
GLuint light_map;
|
||||
GLuint normal_map;
|
||||
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,
|
||||
};
|
||||
|
||||
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;
|
||||
struct {
|
||||
GLint dir;
|
||||
GLint diffuse;
|
||||
GLint globe_diffuse;
|
||||
GLint ambient;
|
||||
} dir_lights[NR_DIR_LIGHTS];
|
||||
GLint specular_light_dir;
|
||||
GLint specular_strength;
|
||||
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 alpha_mode;
|
||||
GLint alpha_texture;
|
||||
GLint uv_scroll;
|
||||
|
||||
GLuint billboard_vao;
|
||||
GLuint billboard_attr_buffer;
|
||||
struct hash_table *billboard_textures; // cg_no -> 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;
|
||||
|
||||
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(struct RE_renderer *r, int cg_no);
|
||||
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);
|
||||
|
||||
// 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];
|
||||
};
|
||||
|
||||
struct pol_material_group {
|
||||
struct pol_material m;
|
||||
uint32_t nr_children;
|
||||
struct pol_material *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,
|
||||
};
|
||||
|
||||
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_triangles;
|
||||
struct pol_triangle *triangles;
|
||||
// Parameters in .opr file.
|
||||
Color edge_color;
|
||||
float edge_size;
|
||||
vec2 uv_scroll;
|
||||
};
|
||||
|
||||
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];
|
||||
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 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);
|
||||
|
||||
// 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);
|
||||
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,509 @@
|
||||
/* 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 <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 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));
|
||||
struct collider_triangle *t = collider->triangles;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)) {
|
||||
glm_vec3_scale(direction, d, out);
|
||||
glm_vec3_add(origin, out, out);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
/* 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 "json.h"
|
||||
#include "reign.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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
cJSON *RE_to_json(struct sact_sprite *sp, bool verbose)
|
||||
{
|
||||
struct RE_plugin *p = (struct RE_plugin*)sp->plugin;
|
||||
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;
|
||||
}
|
||||
+776
@@ -0,0 +1,776 @@
|
||||
/* 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];
|
||||
};
|
||||
|
||||
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, const struct pol_material *m, struct amt *amt, struct archive *aar, const char *path)
|
||||
{
|
||||
material->flags = m->flags;
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
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);
|
||||
}
|
||||
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);
|
||||
|
||||
// Update bone_name_map. If the bone name is not unique in the POL, set the
|
||||
// map value to NULL so that ID matching will be used.
|
||||
struct ht_slot *slot = ht_put(model->bone_name_map, pol_bone->name, bone);
|
||||
if (slot->value != bone) {
|
||||
NOTICE("%s: non-unique bone %s", model->path, pol_bone->name);
|
||||
slot->value = NULL;
|
||||
}
|
||||
|
||||
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->bone_name_map = ht_create(pol->nr_bones * 3 / 2);
|
||||
model->mot_cache = ht_create(16);
|
||||
model->bones = xcalloc(pol->nr_bones, sizeof(struct bone));
|
||||
for (uint32_t i = 0; i < pol->nr_bones; 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++) {
|
||||
init_material(&model->materials[material_offsets[i] + j],
|
||||
&pol->materials[i].children[j], amt, aar, path);
|
||||
}
|
||||
}
|
||||
|
||||
// Meshes
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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]);
|
||||
free(model->bones);
|
||||
if (model->bone_map)
|
||||
ht_free_int(model->bone_map);
|
||||
if (model->bone_name_map)
|
||||
ht_free(model->bone_name_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;
|
||||
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 int cmp_motions_by_bone_id(const void *lhs, const void *rhs)
|
||||
{
|
||||
return (*(struct mot_bone **)lhs)->id - (*(struct mot_bone **)rhs)->id;
|
||||
}
|
||||
|
||||
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].
|
||||
for (uint32_t i = 0; i < mot->nr_bones; i++) {
|
||||
// Match by name first, since some MOT have wrong bone IDs (e.g. maidsan_ahoge_*).
|
||||
struct bone *bone = ht_get(model->bone_name_map, mot->motions[i]->name, NULL);
|
||||
// If it is not found or is NULL (non-unique bone name), match by bone ID.
|
||||
if (!bone)
|
||||
bone = ht_get_int(model->bone_map, mot->motions[i]->id, NULL);
|
||||
if (!bone)
|
||||
ERROR("%s: invalid bone \"%s\" (%d)", name, mot->motions[i]->name, mot->motions[i]->id);
|
||||
mot->motions[i]->id = bone->index;
|
||||
}
|
||||
qsort(mot->motions, mot->nr_bones, sizeof(struct mot_bone *), cmp_motions_by_bone_id);
|
||||
|
||||
// Load optional .txa file.
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
+597
@@ -0,0 +1,597 @@
|
||||
/* 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_material(struct buffer *r, struct pol_material *m)
|
||||
{
|
||||
m->name = read_cstring(r);
|
||||
m->flags = parse_material_attributes(m->name);
|
||||
|
||||
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);
|
||||
if ((unsigned)type < MAX_TEXTURE_TYPE) {
|
||||
m->textures[type] = filename;
|
||||
} 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 parse_material_group(struct buffer *r, struct pol_material_group *mg)
|
||||
{
|
||||
parse_material(r, &mg->m);
|
||||
|
||||
mg->nr_children = buffer_read_int32(r);
|
||||
if (mg->nr_children > 0) {
|
||||
mg->children = xcalloc(mg->nr_children, sizeof(struct pol_material));
|
||||
for (uint32_t i = 0; i < mg->nr_children; i++) {
|
||||
parse_material(r, &mg->children[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(&mg->children[i]);
|
||||
free(mg->children);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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->colors);
|
||||
free(mesh->alphas);
|
||||
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) {
|
||||
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->materials[i]);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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;
|
||||
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, "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, "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 (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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
+1218
File diff suppressed because it is too large
Load Diff
+1140
File diff suppressed because it is too large
Load Diff
+274
-139
@@ -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,232 @@ 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");
|
||||
return assets[type]->load_archive(assets[type], archive_name);
|
||||
}
|
||||
|
||||
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 +286,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);
|
||||
}
|
||||
|
||||
@@ -100,91 +321,100 @@ void asset_manager_init(void)
|
||||
int ald_count[ASSET_TYPE_MAX] = {0};
|
||||
char *afa_filenames[ASSET_TYPE_MAX] = {0};
|
||||
|
||||
DIR *dir;
|
||||
struct dirent *d;
|
||||
UDIR *dir;
|
||||
char *d_name;
|
||||
|
||||
if (!(dir = opendir(config.game_dir))) {
|
||||
ERROR("Failed to open directory: %s", config.game_dir);
|
||||
if (!(dir = opendir_utf8(config.game_dir))) {
|
||||
ERROR("Failed to open directory: %s", display_utf0(config.game_dir));
|
||||
}
|
||||
|
||||
char *base = get_base_name(config.ain_filename);
|
||||
size_t base_len = strlen(base);
|
||||
|
||||
// get ALD filenames
|
||||
while ((d = readdir(dir))) {
|
||||
while ((d_name = readdir_utf8(dir))) {
|
||||
// archive filename must begin with ain filename
|
||||
if (strncmp(base, d->d_name, base_len))
|
||||
continue;
|
||||
const char *ext = file_extension(d->d_name);
|
||||
if (strncasecmp(base, d_name, base_len))
|
||||
goto loop_next;
|
||||
const char *ext = file_extension(d_name);
|
||||
if (!ext)
|
||||
continue;
|
||||
goto loop_next;
|
||||
if (!strcasecmp(ext, "ald")) {
|
||||
int dno = toupper(d->d_name[base_len+1]) - 'A';
|
||||
int dno = toupper(d_name[base_len+1]) - 'A';
|
||||
if (dno < 0 || dno >= ALD_FILEMAX) {
|
||||
WARNING("Invalid ALD index: %s", d->d_name);
|
||||
continue;
|
||||
WARNING("Invalid ALD index: %s", display_utf0(d_name));
|
||||
goto loop_next;
|
||||
}
|
||||
|
||||
switch (d->d_name[base_len]) {
|
||||
switch (d_name[base_len]) {
|
||||
case 'b':
|
||||
case 'B':
|
||||
ald_filenames[ASSET_BGM][dno] = path_join(config.game_dir, d->d_name);
|
||||
ald_filenames[ASSET_BGM][dno] = path_join(config.game_dir, d_name);
|
||||
ald_count[ASSET_BGM] = max(ald_count[ASSET_BGM], dno+1);
|
||||
break;
|
||||
case 'g':
|
||||
case 'G':
|
||||
ald_filenames[ASSET_CG][dno] = path_join(config.game_dir, d->d_name);
|
||||
ald_filenames[ASSET_CG][dno] = path_join(config.game_dir, d_name);
|
||||
ald_count[ASSET_CG] = max(ald_count[ASSET_CG], dno+1);
|
||||
break;
|
||||
case 'w':
|
||||
case 'W':
|
||||
ald_filenames[ASSET_SOUND][dno] = path_join(config.game_dir, d->d_name);
|
||||
ald_filenames[ASSET_SOUND][dno] = path_join(config.game_dir, d_name);
|
||||
ald_count[ASSET_SOUND] = max(ald_count[ASSET_SOUND], dno+1);
|
||||
break;
|
||||
case 'd':
|
||||
case 'D':
|
||||
ald_filenames[ASSET_DATA][dno] = path_join(config.game_dir, d->d_name);
|
||||
ald_filenames[ASSET_DATA][dno] = path_join(config.game_dir, d_name);
|
||||
ald_count[ASSET_DATA] = max(ald_count[ASSET_DATA], dno+1);
|
||||
break;
|
||||
default:
|
||||
WARNING("Unhandled ALD file: %s", d->d_name);
|
||||
WARNING("Unhandled ALD file: %s", display_utf0(d_name));
|
||||
break;
|
||||
}
|
||||
} else if (!strcasecmp(ext, "bgi")) {
|
||||
if (config.bgi_path) {
|
||||
WARNING("Multiple bgi files");
|
||||
continue;
|
||||
goto loop_next;
|
||||
}
|
||||
config.bgi_path = path_join(config.game_dir, d->d_name);
|
||||
config.bgi_path = path_join(config.game_dir, d_name);
|
||||
} else if (!strcasecmp(ext, "wai")) {
|
||||
if (config.wai_path) {
|
||||
WARNING("Multiple wai files");
|
||||
continue;
|
||||
goto loop_next;
|
||||
}
|
||||
config.wai_path = path_join(config.game_dir, d->d_name);
|
||||
config.wai_path = path_join(config.game_dir, d_name);
|
||||
} else if (!strcasecmp(ext, "ex")) {
|
||||
if (config.ex_path) {
|
||||
WARNING("Multiple ex files");
|
||||
continue;
|
||||
goto loop_next;
|
||||
}
|
||||
config.ex_path = path_join(config.game_dir, d->d_name);
|
||||
config.ex_path = path_join(config.game_dir, d_name);
|
||||
} else if (!strcasecmp(ext, "afa")) {
|
||||
const char *type = d->d_name + base_len;
|
||||
const char *type = d_name + base_len;
|
||||
if (!strcmp(type, "BA.afa")) {
|
||||
afa_filenames[ASSET_BGM] = path_join(config.game_dir, d->d_name);
|
||||
afa_filenames[ASSET_BGM] = path_join(config.game_dir, d_name);
|
||||
} else if (!strcmp(type, "WA.afa") || !strcmp(type, "Sound.afa")) {
|
||||
afa_filenames[ASSET_SOUND] = path_join(config.game_dir, d->d_name);
|
||||
afa_filenames[ASSET_SOUND] = path_join(config.game_dir, d_name);
|
||||
} else if (!strcmp(type, "Voice.afa")) {
|
||||
afa_filenames[ASSET_VOICE] = path_join(config.game_dir, d->d_name);
|
||||
afa_filenames[ASSET_VOICE] = path_join(config.game_dir, d_name);
|
||||
} else if (!strcmp(type, "CG.afa")) {
|
||||
afa_filenames[ASSET_CG] = path_join(config.game_dir, d->d_name);
|
||||
afa_filenames[ASSET_CG] = path_join(config.game_dir, d_name);
|
||||
} else if (!strcmp(type, "Flat.afa")) {
|
||||
afa_filenames[ASSET_FLAT] = path_join(config.game_dir, d->d_name);
|
||||
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->d_name);
|
||||
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);
|
||||
}
|
||||
closedir_utf8(dir);
|
||||
free(base);
|
||||
|
||||
// open ALD archives
|
||||
ald_init(ASSET_BGM, ald_filenames[ASSET_BGM], ald_count[ASSET_BGM]);
|
||||
@@ -194,6 +424,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]);
|
||||
@@ -203,101 +434,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]);
|
||||
|
||||
free(base);
|
||||
closedir(dir);
|
||||
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_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", data->name);
|
||||
return;
|
||||
}
|
||||
|
||||
struct ht_slot *slot = ht_put_int(cg_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_index = ht_create(4096);
|
||||
archive_for_each(archives[ASSET_CG], add_cg_to_index, NULL);
|
||||
}
|
||||
|
||||
static int cg_translate_index(int no)
|
||||
{
|
||||
if (!cg_index)
|
||||
return no;
|
||||
return (uintptr_t)ht_get_int(cg_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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
+77
-9
@@ -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, 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,10 +361,36 @@ 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);
|
||||
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);
|
||||
//int wav_prepare_from_file(int channel, char *filename);
|
||||
//int bgm_prepare_from_file(int channel, char *filename);
|
||||
|
||||
static int audio_prepare_from_file(struct id_pool *pool, int id, char *filename)
|
||||
{
|
||||
if (id < 0)
|
||||
return 0;
|
||||
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_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, struct archive_data *dfile)
|
||||
{
|
||||
@@ -344,3 +406,9 @@ static int audio_prepare_from_archive_data(struct id_pool *pool, int id, struct
|
||||
int wav_prepare_from_archive_data(int id, struct archive_data *dfile) {
|
||||
return audio_prepare_from_archive_data(&wav, id, dfile);
|
||||
}
|
||||
|
||||
int wav_get_group_num_from_data_num(int no)
|
||||
{
|
||||
struct wai *wai = wai_get(no);
|
||||
return wai ? wai->channel : 0;
|
||||
}
|
||||
|
||||
+7
-5
@@ -18,8 +18,10 @@
|
||||
#include <stdlib.h>
|
||||
#include "system4.h"
|
||||
#include "system4/file.h"
|
||||
#include "system4/little_endian.h"
|
||||
|
||||
#include "mixer.h"
|
||||
#include "little_endian.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
#define BGI_MAX 100
|
||||
|
||||
@@ -29,7 +31,7 @@ static struct bgi bgi_data[BGI_MAX];
|
||||
static char *bgi_gets(char *buf, int n, FILE *fp)
|
||||
{
|
||||
char *s = buf;
|
||||
int c;
|
||||
int c = EOF;
|
||||
while (--n > 0 && (c = fgetc(fp)) != EOF) {
|
||||
c = c >> 4 | (c & 0xf) << 4; // decrypt
|
||||
*s++ = c;
|
||||
@@ -44,9 +46,9 @@ static char *bgi_gets(char *buf, int n, FILE *fp)
|
||||
|
||||
void bgi_read(const char *path)
|
||||
{
|
||||
FILE *fp = fopen(path, "rb");
|
||||
FILE *fp = file_open_utf8(path, "rb");
|
||||
if (!fp) {
|
||||
WARNING("Failed to open bgi file: %s", path);
|
||||
WARNING("Failed to open bgi file: %s", display_utf0(path));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -104,7 +106,7 @@ void wai_load(const char *path)
|
||||
uint8_t *file = file_read(path, &len);
|
||||
|
||||
if (len < 48 || file[0] != 'X' || file[1] != 'I' || file[2] != '2' || file[3] != '\0') {
|
||||
WARNING("Not a .wai file: %s", path);
|
||||
WARNING("Not a .wai file: %s", display_utf0(path));
|
||||
goto end;
|
||||
}
|
||||
int count = LittleEndian_getDW(file, 8) - 1;
|
||||
|
||||
+161
-59
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/archive.h"
|
||||
#include "system4/utfsjis.h"
|
||||
|
||||
#include "asset_manager.h"
|
||||
#include "audio.h"
|
||||
@@ -29,6 +30,7 @@
|
||||
#include "xsystem4.h"
|
||||
|
||||
#define clamp(min_value, max_value, value) min(max_value, max(min_value, value))
|
||||
#define muldiv(x, y, denom) ((int64_t)(x) * (int64_t)(y) / (int64_t)(denom))
|
||||
|
||||
/*
|
||||
* The actual mixer implementation is contained in this header.
|
||||
@@ -44,7 +46,8 @@ struct fade {
|
||||
atomic_bool fading;
|
||||
bool stop;
|
||||
uint_least32_t start_pos;
|
||||
uint_least32_t end_pos;
|
||||
uint_least32_t frames;
|
||||
uint_least32_t elapsed;
|
||||
float start_volume;
|
||||
float end_volume;
|
||||
};
|
||||
@@ -67,8 +70,8 @@ struct channel {
|
||||
|
||||
// main thread read-only
|
||||
atomic_uint_least32_t frame;
|
||||
atomic_uint volume;
|
||||
|
||||
atomic_uint volume;
|
||||
atomic_bool swapped;
|
||||
uint_least32_t loop_start;
|
||||
uint_least32_t loop_end;
|
||||
@@ -143,19 +146,19 @@ static bool cb_loop(struct channel *ch)
|
||||
* Callback to refill the stream's audio data.
|
||||
* Called from sys_mixer_mix_audio.
|
||||
*/
|
||||
static int cb_read_frames(struct channel *ch, float *out, sf_count_t frame_count)
|
||||
static int cb_read_frames(struct channel *ch, float *out, sf_count_t frame_count, uint_least32_t *num_read)
|
||||
{
|
||||
sf_count_t num_read = 0;
|
||||
*num_read = 0;
|
||||
|
||||
// handle case where chunk crosses loop point (seamless)
|
||||
// NOTE: it's assumed that the length of the loop is greater than the chunk length
|
||||
if (ch->frame + frame_count >= ch->loop_end) {
|
||||
// read frames up to loop_end
|
||||
num_read = sf_readf_float(ch->file, out, ch->loop_end - ch->frame);
|
||||
*num_read = sf_readf_float(ch->file, out, ch->loop_end - ch->frame);
|
||||
// adjust parameters for later
|
||||
ch->frame += num_read;
|
||||
out += num_read;
|
||||
frame_count -= num_read;
|
||||
ch->frame += *num_read;
|
||||
out += *num_read;
|
||||
frame_count -= *num_read;
|
||||
// seek to loop_start
|
||||
if (!cb_loop(ch))
|
||||
return STS_STREAM_COMPLETE;
|
||||
@@ -167,10 +170,10 @@ static int cb_read_frames(struct channel *ch, float *out, sf_count_t frame_count
|
||||
|
||||
// read remaining data
|
||||
sf_count_t n = sf_readf_float(ch->file, out, frame_count);
|
||||
num_read += n;
|
||||
*num_read += n;
|
||||
ch->frame += n;
|
||||
out += num_read;
|
||||
frame_count -= num_read;
|
||||
out += *num_read;
|
||||
frame_count -= *num_read;
|
||||
|
||||
// XXX: This *shouldn't* be necessary, but sometimes libsndfile seems to stop reading
|
||||
// just before the end of file (i.e. ch->frame + frame_count is < ch->info.frames,
|
||||
@@ -179,7 +182,7 @@ static int cb_read_frames(struct channel *ch, float *out, sf_count_t frame_count
|
||||
if (frame_count > 0) {
|
||||
if (!cb_loop(ch))
|
||||
return STS_STREAM_COMPLETE;
|
||||
num_read += sf_readf_float(ch->file, out, frame_count);
|
||||
*num_read += sf_readf_float(ch->file, out, frame_count);
|
||||
}
|
||||
|
||||
return STS_STREAM_CONTINUE;
|
||||
@@ -188,13 +191,12 @@ static int cb_read_frames(struct channel *ch, float *out, sf_count_t frame_count
|
||||
/*
|
||||
* Calculate the gain for a fade at a given sample.
|
||||
*/
|
||||
static float cb_calc_fade(struct fade *fade, uint_least32_t sample)
|
||||
static float cb_calc_fade(struct fade *fade)
|
||||
{
|
||||
if (sample >= fade->end_pos) {
|
||||
if (fade->elapsed >= fade->frames)
|
||||
return fade->stop ? 0.0 : fade->end_volume;
|
||||
}
|
||||
|
||||
float progress = (float)(sample - fade->start_pos) / (float)(fade->end_pos - fade->start_pos);
|
||||
float progress = (float)fade->elapsed / (float)fade->frames;
|
||||
float delta_v = fade->end_volume - fade->start_volume;
|
||||
float gain = fade->start_volume + delta_v * progress;
|
||||
if (gain < 0.0) return 0.0;
|
||||
@@ -205,10 +207,11 @@ static float cb_calc_fade(struct fade *fade, uint_least32_t sample)
|
||||
static int refill_stream(sts_mixer_sample_t *sample, void *data)
|
||||
{
|
||||
struct channel *ch = data;
|
||||
uint_least32_t frames_read;
|
||||
memset(ch->data, 0, sizeof(float) * sample->length);
|
||||
|
||||
// read audio data from file
|
||||
int r = cb_read_frames(ch, ch->data, CHUNK_SIZE);
|
||||
int r = cb_read_frames(ch, ch->data, CHUNK_SIZE, &frames_read);
|
||||
|
||||
// convert mono to stereo
|
||||
if (ch->info.channels == 1) {
|
||||
@@ -229,16 +232,22 @@ static int refill_stream(sts_mixer_sample_t *sample, void *data)
|
||||
|
||||
// set gain for fade
|
||||
if (ch->fade.fading) {
|
||||
float gain = cb_calc_fade(&ch->fade, ch->frame);
|
||||
float gain = cb_calc_fade(&ch->fade);
|
||||
mixers[ch->mixer_no].mixer.voices[ch->voice].gain = gain;
|
||||
ch->volume = gain * 100.0;
|
||||
|
||||
if (ch->frame >= ch->fade.end_pos) {
|
||||
ch->fade.elapsed += frames_read;
|
||||
if (ch->fade.elapsed >= ch->fade.frames) {
|
||||
ch->fade.fading = false;
|
||||
ch->volume = ch->fade.end_volume * 100.0;
|
||||
if (ch->fade.stop) {
|
||||
cb_seek(ch, 0);
|
||||
r = STS_STREAM_COMPLETE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
float gain = ch->volume / 100.0;
|
||||
mixers[ch->mixer_no].mixer.voices[ch->voice].gain = gain;
|
||||
}
|
||||
|
||||
if (r == STS_STREAM_COMPLETE) {
|
||||
@@ -264,8 +273,9 @@ int channel_play(struct channel *ch)
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
if (ch->voice >= 0) {
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
memset(ch->data, 0, sizeof(ch->data));
|
||||
ch->voice = sts_mixer_play_stream(&mixers[ch->mixer_no].mixer, &ch->stream, 1.0f);
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return 1;
|
||||
@@ -276,8 +286,9 @@ int channel_stop(struct channel *ch)
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
if (ch->voice < 0) {
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
cb_seek(ch, 0);
|
||||
sts_mixer_stop_voice(&mixers[ch->mixer_no].mixer, ch->voice);
|
||||
ch->voice = -1;
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
@@ -320,13 +331,30 @@ int channel_set_loop_end_pos(struct channel *ch, int pos)
|
||||
|
||||
int channel_fade(struct channel *ch, int time, int volume, bool stop)
|
||||
{
|
||||
if (!time && stop)
|
||||
return channel_stop(ch);
|
||||
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
ch->fade.fading = true;
|
||||
ch->fade.stop = stop;
|
||||
ch->fade.start_pos = ch->frame;
|
||||
ch->fade.start_volume = (float)ch->volume / 100.0;
|
||||
ch->fade.end_pos = ch->fade.start_pos + ((time * ch->info.samplerate) / 1000);
|
||||
ch->fade.end_volume = clamp(0.0f, 1.0f, (float)volume / 100.0f);
|
||||
if (!time) {
|
||||
// XXX: Fade with time=0 is used to set volume. This needs to
|
||||
// take effect immediately, not via the audio callback
|
||||
// because the stream isn't necessarily playing yet.
|
||||
// E.g. the below call sequence should fade from 0 -> 33:
|
||||
//
|
||||
// SACT2.Music_Fade(ch, 0, 0, 0);
|
||||
// SACT2.Music_Fade(ch, 33, 4000, 0);
|
||||
// SACT2.Music_Play(ch);
|
||||
ch->fade.fading = false;
|
||||
ch->volume = max(0, min(100, volume));
|
||||
} else {
|
||||
ch->fade.fading = true;
|
||||
ch->fade.stop = stop;
|
||||
ch->fade.start_pos = ch->frame;
|
||||
ch->fade.start_volume = (float)ch->volume / 100.0;
|
||||
ch->fade.frames = muldiv(time, ch->info.samplerate, 1000);
|
||||
ch->fade.elapsed = 0;
|
||||
ch->fade.end_volume = clamp(0.0f, 1.0f, (float)volume / 100.0f);
|
||||
}
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return 1;
|
||||
}
|
||||
@@ -336,7 +364,7 @@ int channel_stop_fade(struct channel *ch)
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
// XXX: we need to set the volume to end_volume and potentially stop the
|
||||
// stream here; better to let the callback do it
|
||||
ch->fade.end_pos = ch->frame;
|
||||
ch->fade.elapsed = ch->fade.frames;
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return 1;
|
||||
}
|
||||
@@ -367,13 +395,13 @@ int channel_is_paused(possibly_unused struct channel *ch)
|
||||
|
||||
int channel_get_pos(struct channel *ch)
|
||||
{
|
||||
return (ch->frame * 1000) / ch->info.samplerate;
|
||||
return muldiv(ch->frame, 1000, ch->info.samplerate);
|
||||
}
|
||||
|
||||
int channel_get_length(struct channel *ch)
|
||||
{
|
||||
// FIXME: how is this different than channel_get_time_length?
|
||||
return (ch->info.frames * 1000) / ch->info.samplerate;
|
||||
return muldiv(ch->info.frames, 1000, ch->info.samplerate);
|
||||
}
|
||||
|
||||
int channel_get_sample_pos(struct channel *ch)
|
||||
@@ -390,7 +418,7 @@ int channel_seek(struct channel *ch, int pos)
|
||||
{
|
||||
// NOTE: SACT2.Music_Seek doesn't seem to do anything in Sengoku Rance...
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
int r = cb_seek(ch, (pos * ch->info.samplerate) / 1000);
|
||||
int r = cb_seek(ch, muldiv(pos, ch->info.samplerate, 1000));
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return r;
|
||||
}
|
||||
@@ -408,7 +436,12 @@ int channel_get_volume(struct channel *ch)
|
||||
|
||||
int channel_get_time_length(struct channel *ch)
|
||||
{
|
||||
return (ch->info.frames * 1000) / ch->info.samplerate;
|
||||
return muldiv(ch->info.frames, 1000, ch->info.samplerate);
|
||||
}
|
||||
|
||||
int channel_get_data_no(struct channel *ch)
|
||||
{
|
||||
return ch->dfile ? ch->dfile->no : -1;
|
||||
}
|
||||
|
||||
static sf_count_t channel_vio_get_filelen(void *data)
|
||||
@@ -466,7 +499,7 @@ static SF_VIRTUAL_IO channel_vio = {
|
||||
struct channel *channel_open(enum asset_type type, int no)
|
||||
{
|
||||
// get file from archive
|
||||
struct archive_data *dfile = asset_get(type, no-1);
|
||||
struct archive_data *dfile = asset_get(type, no);
|
||||
if (!dfile) {
|
||||
WARNING("Failed to load %s %d", type == ASSET_SOUND ? "WAV" : "BGM", no);
|
||||
return NULL;
|
||||
@@ -493,6 +526,8 @@ struct channel *channel_open(enum asset_type type, int no)
|
||||
ch->loop_end = clamp(0, ch->info.frames, bgi->loop_end);
|
||||
ch->loop_count = max(0, bgi->loop_count);
|
||||
ch->mixer_no = clamp(0, nr_mixers, bgi->channel);
|
||||
} else {
|
||||
ch->loop_count = 0;
|
||||
}
|
||||
}
|
||||
ch->no = no;
|
||||
@@ -500,20 +535,15 @@ struct channel *channel_open(enum asset_type type, int no)
|
||||
return ch;
|
||||
}
|
||||
|
||||
struct channel *channel_open_archive_data(struct archive_data *dfile)
|
||||
static bool init_channel(struct channel *ch)
|
||||
{
|
||||
struct channel *ch = xcalloc(1, sizeof(struct channel));
|
||||
ch->dfile = dfile;
|
||||
|
||||
// open file
|
||||
ch->file = sf_open_virtual(&channel_vio, SFM_READ, &ch->info, ch);
|
||||
if (sf_error(ch->file) != SF_ERR_NO_ERROR) {
|
||||
WARNING("sf_open_virtual failed: %s", sf_strerror(ch->file));
|
||||
goto error;
|
||||
return false;
|
||||
}
|
||||
if (ch->info.channels > 2) {
|
||||
WARNING("Audio file has more than 2 channels");
|
||||
goto error;
|
||||
return false;
|
||||
}
|
||||
|
||||
// create stream
|
||||
@@ -528,23 +558,57 @@ struct channel *channel_open_archive_data(struct archive_data *dfile)
|
||||
ch->volume = 100;
|
||||
ch->loop_start = 0;
|
||||
ch->loop_end = ch->info.frames;
|
||||
ch->loop_count = 0;
|
||||
ch->loop_count = 1;
|
||||
ch->mixer_no = 0;
|
||||
|
||||
ch->no = -1;
|
||||
return ch;
|
||||
return true;
|
||||
}
|
||||
|
||||
error:
|
||||
archive_free_data(dfile);
|
||||
free(ch);
|
||||
return NULL;
|
||||
struct channel *channel_open_archive_data(struct archive_data *dfile)
|
||||
{
|
||||
struct channel *ch = xcalloc(1, sizeof(struct channel));
|
||||
ch->dfile = dfile;
|
||||
|
||||
// open file
|
||||
ch->file = sf_open_virtual(&channel_vio, SFM_READ, &ch->info, ch);
|
||||
|
||||
if (!init_channel(ch)) {
|
||||
archive_free_data(dfile);
|
||||
if (ch->file)
|
||||
sf_close(ch->file);
|
||||
free(ch);
|
||||
return NULL;
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
|
||||
struct channel *channel_open_file(const char *path)
|
||||
{
|
||||
struct channel *ch = xcalloc(1, sizeof(struct channel));
|
||||
#ifdef _WIN32
|
||||
wchar_t *wpath = utf8_to_wchar(path);
|
||||
ch->file = sf_wchar_open(wpath, SFM_READ, &ch->info);
|
||||
free(wpath);
|
||||
#else
|
||||
ch->file = sf_open(path, SFM_READ, &ch->info);
|
||||
#endif
|
||||
|
||||
if (!init_channel(ch)) {
|
||||
if (ch->file)
|
||||
sf_close(ch->file);
|
||||
free(ch);
|
||||
return NULL;
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
|
||||
void channel_close(struct channel *ch)
|
||||
{
|
||||
channel_stop(ch);
|
||||
sf_close(ch->file);
|
||||
archive_free_data(ch->dfile);
|
||||
if (ch->dfile)
|
||||
archive_free_data(ch->dfile);
|
||||
free(ch);
|
||||
}
|
||||
|
||||
@@ -557,9 +621,10 @@ void mixer_init(void)
|
||||
if (!config.mixer_nr_channels) {
|
||||
nr_mixers = 3;
|
||||
mixers = xcalloc(nr_mixers, sizeof(struct mixer));
|
||||
mixers[0].name = "Music";
|
||||
mixers[1].name = "Sound";
|
||||
mixers[2].name = "Master";
|
||||
mixers[0].name = strdup("Music");
|
||||
mixers[1].name = strdup("Sound");
|
||||
mixers[2].name = strdup("Master");
|
||||
master = &mixers[2];
|
||||
} else {
|
||||
// NOTE: Older games don't have an explicit master channel.
|
||||
// We add an extra mixer in this case for consistency.
|
||||
@@ -570,10 +635,10 @@ void mixer_init(void)
|
||||
nr_mixers = config.mixer_nr_channels + need_master;
|
||||
mixers = xcalloc(nr_mixers, sizeof(struct mixer));
|
||||
for (unsigned i = 0; i < config.mixer_nr_channels; i++) {
|
||||
mixers[i].name = config.mixer_channels[i];
|
||||
mixers[i].name = strdup(config.mixer_channels[i]);
|
||||
}
|
||||
if (need_master) {
|
||||
mixers[nr_mixers-1].name = "Master";
|
||||
mixers[nr_mixers-1].name = strdup("Master");
|
||||
master = &mixers[nr_mixers-1];
|
||||
} else {
|
||||
master = &mixers[0];
|
||||
@@ -637,19 +702,30 @@ void mixer_init(void)
|
||||
|
||||
int mixer_get_numof(void)
|
||||
{
|
||||
return nr_mixers;
|
||||
// Return the number of mixers specified in System40.ini, even if
|
||||
// xsystem4 added more mixers.
|
||||
return config.mixer_nr_channels;
|
||||
}
|
||||
|
||||
const char *mixer_get_name(int n)
|
||||
{
|
||||
if (n < 0 || n >= nr_mixers)
|
||||
if (n < 0 || n >= config.mixer_nr_channels)
|
||||
return NULL;
|
||||
return mixers[n].name;
|
||||
}
|
||||
|
||||
int mixer_set_name(int n, const char *name)
|
||||
{
|
||||
if (n < 0 || n >= config.mixer_nr_channels)
|
||||
return 0;
|
||||
free(mixers[n].name);
|
||||
mixers[n].name = strdup(name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mixer_get_volume(int n, int *volume)
|
||||
{
|
||||
if (n < 0 || n >= nr_mixers) {
|
||||
if (n < 0 || n >= config.mixer_nr_channels) {
|
||||
return 0;
|
||||
}
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
@@ -660,7 +736,7 @@ int mixer_get_volume(int n, int *volume)
|
||||
|
||||
int mixer_set_volume(int n, int volume)
|
||||
{
|
||||
if (n < 0 || n >= nr_mixers)
|
||||
if (n < 0 || n >= config.mixer_nr_channels)
|
||||
return 0;
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
mixers[n].mixer.gain = clamp(0.0f, 1.0f, (float)volume / 100.0f);
|
||||
@@ -669,7 +745,7 @@ int mixer_set_volume(int n, int volume)
|
||||
}
|
||||
int mixer_get_mute(int n, int *mute)
|
||||
{
|
||||
if (n < 0 || n >= nr_mixers)
|
||||
if (n < 0 || n >= config.mixer_nr_channels)
|
||||
return 0;
|
||||
*mute = mixers[n].muted;
|
||||
return 1;
|
||||
@@ -677,8 +753,34 @@ int mixer_get_mute(int n, int *mute)
|
||||
|
||||
int mixer_set_mute(int n, int mute)
|
||||
{
|
||||
if (n < 0 || n >= nr_mixers)
|
||||
if (n < 0 || n >= config.mixer_nr_channels)
|
||||
return 0;
|
||||
mixers[n].muted = !!mute;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mixer_stream_play(sts_mixer_stream_t* stream, int volume)
|
||||
{
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
float gain = clamp(0.0f, 1.0f, (float)volume / 100.0f);
|
||||
int voice = sts_mixer_play_stream(&master->mixer, stream, gain);
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return voice;
|
||||
}
|
||||
|
||||
bool mixer_stream_set_volume(int voice, int volume)
|
||||
{
|
||||
if (voice < 0 || voice >= STS_MIXER_VOICES)
|
||||
return false;
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
master->mixer.voices[voice].gain = clamp(0.0f, 1.0f, (float)volume / 100.0f);
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return true;
|
||||
}
|
||||
|
||||
void mixer_stream_stop(int voice)
|
||||
{
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
sts_mixer_stop_voice(&master->mixer, voice);
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Base64 encoding/decoding (RFC1341)
|
||||
* Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
*/
|
||||
|
||||
#include "system4.h"
|
||||
#include "base64.h"
|
||||
|
||||
static const unsigned char base64_table[65] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
/**
|
||||
* base64_encode - Base64 encode
|
||||
* @src: Data to be encoded
|
||||
* @len: Length of the data to be encoded
|
||||
* @out_len: Pointer to output length variable, or %NULL if not used
|
||||
* Returns: Allocated buffer of out_len bytes of encoded data,
|
||||
* or %NULL on failure
|
||||
*
|
||||
* Caller is responsible for freeing the returned buffer. Returned buffer is
|
||||
* nul terminated to make it easier to use as a C string. The nul terminator is
|
||||
* not included in out_len.
|
||||
*/
|
||||
unsigned char *base64_encode(const unsigned char *src, size_t len,
|
||||
size_t *out_len)
|
||||
{
|
||||
unsigned char *out, *pos;
|
||||
const unsigned char *end, *in;
|
||||
size_t olen;
|
||||
int line_len;
|
||||
|
||||
olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
|
||||
olen += olen / 72; /* line feeds */
|
||||
olen++; /* nul termination */
|
||||
if (olen < len)
|
||||
return NULL; /* integer overflow */
|
||||
out = xmalloc(olen);
|
||||
if (out == NULL)
|
||||
return NULL;
|
||||
|
||||
end = src + len;
|
||||
in = src;
|
||||
pos = out;
|
||||
line_len = 0;
|
||||
while (end - in >= 3) {
|
||||
*pos++ = base64_table[in[0] >> 2];
|
||||
*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
|
||||
*pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
|
||||
*pos++ = base64_table[in[2] & 0x3f];
|
||||
in += 3;
|
||||
line_len += 4;
|
||||
if (line_len >= 72) {
|
||||
*pos++ = '\n';
|
||||
line_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (end - in) {
|
||||
*pos++ = base64_table[in[0] >> 2];
|
||||
if (end - in == 1) {
|
||||
*pos++ = base64_table[(in[0] & 0x03) << 4];
|
||||
*pos++ = '=';
|
||||
} else {
|
||||
*pos++ = base64_table[((in[0] & 0x03) << 4) |
|
||||
(in[1] >> 4)];
|
||||
*pos++ = base64_table[(in[1] & 0x0f) << 2];
|
||||
}
|
||||
*pos++ = '=';
|
||||
line_len += 4;
|
||||
}
|
||||
|
||||
if (line_len)
|
||||
*pos++ = '\n';
|
||||
|
||||
*pos = '\0';
|
||||
if (out_len)
|
||||
*out_len = pos - out;
|
||||
return out;
|
||||
}
|
||||
+72
@@ -2452,6 +2452,78 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
|
||||
return a;
|
||||
}
|
||||
|
||||
/* XXX: xsystem4 addition: callback-based CreateIntArray. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray_cb(int count, int (*get_number)(int,void*), void *data)
|
||||
{
|
||||
size_t i = 0;
|
||||
cJSON *n = NULL;
|
||||
cJSON *p = NULL;
|
||||
cJSON *a = NULL;
|
||||
|
||||
if (count < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
a = cJSON_CreateArray();
|
||||
for(i = 0; a && (i < (size_t)count); i++)
|
||||
{
|
||||
n = cJSON_CreateNumber(get_number(i, data));
|
||||
if (!n)
|
||||
{
|
||||
cJSON_Delete(a);
|
||||
return NULL;
|
||||
}
|
||||
if(!i)
|
||||
{
|
||||
a->child = n;
|
||||
}
|
||||
else
|
||||
{
|
||||
suffix_object(p, n);
|
||||
}
|
||||
p = n;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
/* XXX: xsystem4 addition: callback-based CreateArray. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateArray_cb(int count, cJSON *(*get_item)(int,void*), void *data)
|
||||
{
|
||||
size_t i = 0;
|
||||
cJSON *n = NULL;
|
||||
cJSON *p = NULL;
|
||||
cJSON *a = NULL;
|
||||
|
||||
if (count < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
a = cJSON_CreateArray();
|
||||
for(i = 0; a && (i < (size_t)count); i++)
|
||||
{
|
||||
n = get_item(i, data);;
|
||||
if (!n)
|
||||
{
|
||||
// XXX: NULL return is ignored
|
||||
continue;
|
||||
}
|
||||
if(!i)
|
||||
{
|
||||
a->child = n;
|
||||
}
|
||||
else
|
||||
{
|
||||
suffix_object(p, n);
|
||||
}
|
||||
p = n;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ sexp sexp_get_page_stub (sexp ctx, sexp self, sexp_sint_t n, sexp arg0) {
|
||||
|
||||
sexp sexp_backtrace_stub (sexp ctx, sexp self, sexp_sint_t n) {
|
||||
sexp res;
|
||||
res = ((print_stack_trace()), SEXP_VOID);
|
||||
res = ((dbg_print_stack_trace()), SEXP_VOID);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -82,7 +82,7 @@
|
||||
(int varno variable-varno)
|
||||
(vm_value value variable-vm-value))
|
||||
|
||||
(define-c void (backtrace print_stack_trace) ())
|
||||
(define-c void (backtrace dbg_print_stack_trace) ())
|
||||
|
||||
(define-c (maybe-null page) (get-page get_page) (int))
|
||||
(define-c (maybe-null page) (frame-page frame_page) (int))
|
||||
|
||||
+1194
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user