mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-09-22 23:18:01 +03:00
Compare commits
756
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04333ad689 | ||
|
|
960c97ecc9 | ||
|
|
e61f4b3c42 | ||
|
|
03f9e7e87e | ||
|
|
87e4bebb12 | ||
|
|
3a0dc49340 | ||
|
|
e93cddad76 | ||
|
|
461eca1a85 | ||
|
|
6ea8af95ba | ||
|
|
167d75d092 | ||
|
|
275d71712e | ||
|
|
d68baa74a0 | ||
|
|
267f7bb762 | ||
|
|
addc26ca45 | ||
|
|
bc745035e1 | ||
|
|
11ad7a583a | ||
|
|
9654950ab4 | ||
|
|
021187752b | ||
|
|
5cc163ab16 | ||
|
|
e14b4bca79 | ||
|
|
02f29b6c6d | ||
|
|
fa09b0059f | ||
|
|
4233c7d294 | ||
|
|
b30a107fda | ||
|
|
bf3430dbad | ||
|
|
135cae216f | ||
|
|
05820dc536 | ||
|
|
e76762d11d | ||
|
|
5fb3c49f75 | ||
|
|
5d032d739e | ||
|
|
e3bf653165 | ||
|
|
a19c200870 | ||
|
|
5cbc697f23 | ||
|
|
4e20a499d4 | ||
|
|
0d4eb2a2a7 | ||
|
|
d800e12f59 | ||
|
|
b1f8948b74 | ||
|
|
ebc319994e | ||
|
|
9bb871d9fd | ||
|
|
62d38aaf59 | ||
|
|
5bb36c7524 | ||
|
|
4dc9792632 | ||
|
|
a7e973c0a9 | ||
|
|
33f1295237 | ||
|
|
e699fb0aa2 | ||
|
|
4e0dd82776 | ||
|
|
77d9e77053 | ||
|
|
d65a1e6d9b | ||
|
|
68e8257c27 | ||
|
|
8e339d1537 | ||
|
|
a2524bafd5 | ||
|
|
f146320e36 | ||
|
|
09a0e3329a | ||
|
|
b68e1622fd | ||
|
|
433e3be3e6 | ||
|
|
d5b27a0a47 | ||
|
|
a38c55602d | ||
|
|
b3bcd79bae | ||
|
|
f0c4db718d | ||
|
|
d17fc30731 | ||
|
|
ca2254360c | ||
|
|
81f9113906 | ||
|
|
d63e71e85d | ||
|
|
6d05546c2c | ||
|
|
e260b82828 | ||
|
|
c0cdf4f070 | ||
|
|
74461f5b9c | ||
|
|
d8479ad80a | ||
|
|
e8bd5ab036 | ||
|
|
05fee4ba83 | ||
|
|
bf3b3416fe | ||
|
|
fb3d0bbd3e | ||
|
|
507b65fc9c | ||
|
|
24df15b9e9 | ||
|
|
fab789df65 | ||
|
|
3012faa6ff | ||
|
|
6dbb5baf19 | ||
|
|
db8279ebc3 | ||
|
|
250bc82818 | ||
|
|
4843e4e87a | ||
|
|
1051294247 | ||
|
|
fa98a76c42 | ||
|
|
d073875d94 | ||
|
|
2b67a9e000 | ||
|
|
b62edb7188 | ||
|
|
85547ef4f4 | ||
|
|
f108b7f436 | ||
|
|
90c3ab5008 | ||
|
|
7b9565fe25 | ||
|
|
4dcc50d6ca | ||
|
|
5c47ad5aae | ||
|
|
062713f70e | ||
|
|
0e8bd36407 | ||
|
|
28554137cc | ||
|
|
32024bc805 | ||
|
|
ec9bd95dc9 | ||
|
|
8dee7d8658 | ||
|
|
0f73ded483 | ||
|
|
847987ec1b | ||
|
|
ec4b608877 | ||
|
|
12735d5c8c | ||
|
|
22d58d977a | ||
|
|
20e0d00661 | ||
|
|
a706a41dcb | ||
|
|
bf0b2bb44f | ||
|
|
916daec6b2 | ||
|
|
1fcccb766c | ||
|
|
aa112849de | ||
|
|
8ca1a31ccc | ||
|
|
2c622bb331 | ||
|
|
c01a27583c | ||
|
|
ced4daa331 | ||
|
|
9eee82c13b | ||
|
|
f53ec98d53 | ||
|
|
32f4253948 | ||
|
|
cb0b969492 | ||
|
|
0030e28471 | ||
|
|
224db2f9cc | ||
|
|
33d07c190e | ||
|
|
a4dd6a42df | ||
|
|
da268288c3 | ||
|
|
f535223988 | ||
|
|
f9087eb7ac | ||
|
|
636743478c | ||
|
|
a2f087b7f9 | ||
|
|
07bd9c7438 | ||
|
|
028c907a12 | ||
|
|
c45bb3a736 | ||
|
|
484f383826 | ||
|
|
af12e19859 | ||
|
|
e83cb1c91e | ||
|
|
4c4a62e1ed | ||
|
|
f01e41be12 | ||
|
|
6bb00a268c | ||
|
|
9ed1f52627 | ||
|
|
59789c0a8a | ||
|
|
13d0c51351 | ||
|
|
46bdbb05ef | ||
|
|
234b04b460 | ||
|
|
ae9544a1ac | ||
|
|
6509dca693 | ||
|
|
bf455de35d | ||
|
|
511f6a4bfa | ||
|
|
4239345289 | ||
|
|
955950198e | ||
|
|
63ab438391 | ||
|
|
1dd37559bb | ||
|
|
f52f4bd1f0 | ||
|
|
1b87d3f9a5 | ||
|
|
15f70f29a0 | ||
|
|
df6998c0f9 | ||
|
|
2f436ce5bf | ||
|
|
c4f89d040a | ||
|
|
93c790dcc6 | ||
|
|
88f199a8f2 | ||
|
|
28c8c277c9 | ||
|
|
e5087f0757 | ||
|
|
6dc62bffc8 | ||
|
|
0ea5d90797 | ||
|
|
d20f52cf79 | ||
|
|
dcbd149409 | ||
|
|
1655c8a8e0 | ||
|
|
2a99c75e4d | ||
|
|
70336546ed | ||
|
|
cb24db29c2 | ||
|
|
03ef51e76a | ||
|
|
a0d78a024c | ||
|
|
dc105aeb16 | ||
|
|
c877e261c5 | ||
|
|
39c0094ce8 | ||
|
|
93ca13a50b | ||
|
|
c540eb9796 | ||
|
|
753e202cea | ||
|
|
f884a58a77 | ||
|
|
2bc9a3d2cb | ||
|
|
1e29930f4c | ||
|
|
887eebf31d | ||
|
|
96d46069ce | ||
|
|
9192bc80c5 | ||
|
|
003ab16c72 | ||
|
|
03062d5d61 | ||
|
|
0bf5aaa8c8 | ||
|
|
fe952354c5 | ||
|
|
f43ca3a31a | ||
|
|
52f2948e19 | ||
|
|
92b2dd1990 | ||
|
|
f8ce39a324 | ||
|
|
9562e0d1c3 | ||
|
|
623b3d3407 | ||
|
|
39886c30bd | ||
|
|
1ad496c28d | ||
|
|
039f1c9a2e | ||
|
|
caa593d00b | ||
|
|
38610d93bb | ||
|
|
65fa898d9f | ||
|
|
547e8158a2 | ||
|
|
197b89620c | ||
|
|
ecc04bc9bb | ||
|
|
65ff73ca06 | ||
|
|
2ad0de6663 | ||
|
|
5f318e859e | ||
|
|
efddc4f128 | ||
|
|
d93bb91d72 | ||
|
|
49ee259d2c | ||
|
|
3c362e9db6 | ||
|
|
1ad7cbb2c5 | ||
|
|
a476b1d892 | ||
|
|
d4121032db | ||
|
|
9474e321e1 | ||
|
|
d130f38e74 | ||
|
|
e66fe928e5 | ||
|
|
134168d864 | ||
|
|
6e755b21ef | ||
|
|
a5d488564a | ||
|
|
205e0a3ed0 | ||
|
|
b758a04760 | ||
|
|
9c962e3467 | ||
|
|
02129edc10 | ||
|
|
5ac79311b9 | ||
|
|
75cfc9f18d | ||
|
|
eaa7f3d8d0 | ||
|
|
0d7ef9b577 | ||
|
|
4a7fc4d12a | ||
|
|
e662955a1d | ||
|
|
a3c23387f4 | ||
|
|
f23125cb77 | ||
|
|
30ac11e74b | ||
|
|
f40132497c | ||
|
|
7540712de1 | ||
|
|
6a75bc5a6b | ||
|
|
a1a809faef | ||
|
|
3929de81f1 | ||
|
|
65ccb29423 | ||
|
|
9f3af7b256 | ||
|
|
249bc06f53 | ||
|
|
621eb49be8 | ||
|
|
5f13b3e9d0 | ||
|
|
15cfc5463b | ||
|
|
ed2239fb1b | ||
|
|
5980e49a96 | ||
|
|
a6871623f2 | ||
|
|
477bbdb949 | ||
|
|
0baa493f79 | ||
|
|
2c51bec001 | ||
|
|
d6b5bde80c | ||
|
|
2510fa6b9b | ||
|
|
8fecf72281 | ||
|
|
93ce40bcdd | ||
|
|
4f2d367b19 | ||
|
|
74bdbd4f37 | ||
|
|
2c8fc425db | ||
|
|
f59158648f | ||
|
|
484b643f02 | ||
|
|
d1fdab4cde | ||
|
|
43c6a2804c | ||
|
|
180c81e5f5 | ||
|
|
e29739f0be | ||
|
|
1e682bad09 | ||
|
|
0a4981533b | ||
|
|
998ab23dab | ||
|
|
dc8342cbab | ||
|
|
8d9eb931f6 | ||
|
|
1621378ba1 | ||
|
|
d3cb6b4001 | ||
|
|
2198bf5970 | ||
|
|
0a152f0e34 | ||
|
|
2a3b636bb0 | ||
|
|
1506d085db | ||
|
|
729957b842 | ||
|
|
6f661fb370 | ||
|
|
57a73cd924 | ||
|
|
6424efa6ab | ||
|
|
dc4a6f1dfa | ||
|
|
b53c511b4a | ||
|
|
0c75f21985 | ||
|
|
b4c3818d6b | ||
|
|
fe61704b90 | ||
|
|
199c91dd6c | ||
|
|
828971ce19 | ||
|
|
e1b1208438 | ||
|
|
b42e59c494 | ||
|
|
0d689ac1c2 | ||
|
|
aba2a53afe | ||
|
|
30d599f161 | ||
|
|
7817592728 | ||
|
|
85244a304a | ||
|
|
f2d26c2a46 | ||
|
|
d48f637744 | ||
|
|
008d43be1b | ||
|
|
05b78e6dc6 | ||
|
|
b28624203a | ||
|
|
e943ef8ea9 | ||
|
|
d547919713 | ||
|
|
ddf6ac5088 | ||
|
|
037616f38d | ||
|
|
af70ee09de | ||
|
|
8d67c87f6f | ||
|
|
94c8329d7a | ||
|
|
2c771acfe6 | ||
|
|
eef99c0910 | ||
|
|
44c73f644f | ||
|
|
6cada80d34 | ||
|
|
96a560f6a2 | ||
|
|
e13583a75e | ||
|
|
f0e25e53d7 | ||
|
|
eb0c642ab1 | ||
|
|
acb4d92705 | ||
|
|
b6c523f4f3 | ||
|
|
6b9edcdd69 | ||
|
|
538480b348 | ||
|
|
d9be9a7ad3 | ||
|
|
554f742a00 | ||
|
|
eda8ad35f3 | ||
|
|
59a66a8a5a | ||
|
|
02eab7149c | ||
|
|
1042b328a1 | ||
|
|
f3484643a6 | ||
|
|
46469e8732 | ||
|
|
62ec0be41a | ||
|
|
0972c60558 | ||
|
|
ba2805b09f | ||
|
|
20711ffa57 | ||
|
|
7adbf9d6e6 | ||
|
|
2dcc9b2aa9 | ||
|
|
441419d0f9 | ||
|
|
a32e3b9401 | ||
|
|
d579374a9c | ||
|
|
f0a54b337d | ||
|
|
80f4246095 | ||
|
|
b71b61c1d6 | ||
|
|
8c791db5d4 | ||
|
|
d404324e7b | ||
|
|
aeb5dbb08c | ||
|
|
9db75ab870 | ||
|
|
67387e88d0 | ||
|
|
d57c8cc9f4 | ||
|
|
677f20628c | ||
|
|
e68c41a0b9 | ||
|
|
fbd105b697 | ||
|
|
3c7634522a | ||
|
|
5ed05ba685 | ||
|
|
11fe6f6dda | ||
|
|
3057c2a96b | ||
|
|
5041d817d1 | ||
|
|
cb4e44a551 | ||
|
|
74ae7223c3 | ||
|
|
7dc6760928 | ||
|
|
e1ca00a27b | ||
|
|
7989ffc542 | ||
|
|
6e14285038 | ||
|
|
55a214553a | ||
|
|
bf9900d8aa | ||
|
|
e00ba69546 | ||
|
|
b88abe375a | ||
|
|
c86977ad1b | ||
|
|
ffb9fbae31 | ||
|
|
3ef7ce926b | ||
|
|
bf6dcef2a8 | ||
|
|
8a62ec6dc5 | ||
|
|
152af8685f | ||
|
|
c5d6afdd08 | ||
|
|
9d6861b10b | ||
|
|
c7ba379173 | ||
|
|
f4d4e8f90a | ||
|
|
379bbfeb07 | ||
|
|
117c72cf09 | ||
|
|
3577079a04 | ||
|
|
8d120b8a4d | ||
|
|
e2440a5a44 | ||
|
|
bba3f96a1b | ||
|
|
f4f7adb618 | ||
|
|
8730475f1c | ||
|
|
a84d78ac5d | ||
|
|
92322c27f0 | ||
|
|
50cb0f8063 | ||
|
|
13e76f67b3 | ||
|
|
37f1bc2723 | ||
|
|
0d7830f24c | ||
|
|
c70e5e028e | ||
|
|
a4b4e7f5bb | ||
|
|
9ccc4ab0c7 | ||
|
|
13a00c131d | ||
|
|
929a2dd3e7 | ||
|
|
b5b92bc367 | ||
|
|
e5fadf2b73 | ||
|
|
2713290ac9 | ||
|
|
3bfb92e1fb | ||
|
|
378450c625 | ||
|
|
919b86916c | ||
|
|
6fbd147c54 | ||
|
|
bcae91003a | ||
|
|
7e584c5fd2 | ||
|
|
b0c7aad6c3 | ||
|
|
5d0819a115 | ||
|
|
724bbcdfdd | ||
|
|
dc0daf3329 | ||
|
|
5d271deec0 | ||
|
|
2ede7e6bb1 | ||
|
|
e6d069b757 | ||
|
|
d6abcc2f92 | ||
|
|
de0646e430 | ||
|
|
c04fd2280d | ||
|
|
71c58e85ae | ||
|
|
61650308b7 | ||
|
|
bb26326929 | ||
|
|
f50c9f6d77 | ||
|
|
ea081e0eb7 | ||
|
|
e8ecad598a | ||
|
|
cb551e8f06 | ||
|
|
002bca4e24 | ||
|
|
0bbd9171b5 | ||
|
|
7dfc9926be | ||
|
|
1dd10ecc45 | ||
|
|
ba11900350 | ||
|
|
508839ed4c | ||
|
|
55f826db36 | ||
|
|
882354c692 | ||
|
|
071d7d2137 | ||
|
|
eddac54fa0 | ||
|
|
2e1059cbac | ||
|
|
f9177b89b1 | ||
|
|
7bae2cdf8a | ||
|
|
769bdca725 | ||
|
|
6517303e2a | ||
|
|
0e388c74c0 | ||
|
|
7ff5ec89e7 | ||
|
|
33403cbd3d | ||
|
|
40fe2068e7 | ||
|
|
40914a7cef | ||
|
|
0946900e02 | ||
|
|
56d9b607b2 | ||
|
|
155c801804 | ||
|
|
4942e41fb8 | ||
|
|
ac8a37f67b | ||
|
|
331563a165 | ||
|
|
c30510e688 | ||
|
|
81c0c494b0 | ||
|
|
e9bec3c2fe | ||
|
|
298e9bddf5 | ||
|
|
bf3c4feb9d | ||
|
|
6a36fc8f53 | ||
|
|
45876a9921 | ||
|
|
2461c1e52d | ||
|
|
41c7830558 | ||
|
|
009bafae40 | ||
|
|
e891e74bcf | ||
|
|
5576f14a0f | ||
|
|
90075f2e25 | ||
|
|
c0cc3f729b | ||
|
|
4d303b291d | ||
|
|
e73abc3cd6 | ||
|
|
579c158477 | ||
|
|
842bac92f3 | ||
|
|
dc8a9379d0 | ||
|
|
f8f02a1c99 | ||
|
|
d7d9bb70f2 | ||
|
|
dcdb82e2a8 | ||
|
|
f1a2904262 | ||
|
|
ecd71896b3 | ||
|
|
d7b7879747 | ||
|
|
1cbf6125ce | ||
|
|
9b03741288 | ||
|
|
4e04db0045 | ||
|
|
abd3a276a3 | ||
|
|
9ca3d3e9c6 | ||
|
|
d8ad22b638 | ||
|
|
3f02a22a8d | ||
|
|
bff491bbe9 | ||
|
|
f566539e4f | ||
|
|
c04aa8a844 | ||
|
|
87b1e2abe2 | ||
|
|
a0a68a022c | ||
|
|
3fb2dc3650 | ||
|
|
c1ce2c88c6 | ||
|
|
222d2700a9 | ||
|
|
dd18d68be5 | ||
|
|
db7c3a608c | ||
|
|
899c6c02a1 | ||
|
|
8ff5ffe30b | ||
|
|
ff9175c1fd | ||
|
|
74666c9b90 | ||
|
|
68bd33e099 | ||
|
|
84ab9a41da | ||
|
|
22a06b1da9 | ||
|
|
128b2fd6d5 | ||
|
|
09dc9470ae | ||
|
|
0f5a39c292 | ||
|
|
73f7acde40 | ||
|
|
3afe4e4453 | ||
|
|
8f69f1911a | ||
|
|
e466b3f06b | ||
|
|
a2ae28c1c1 | ||
|
|
41626b5018 | ||
|
|
9a76d472db | ||
|
|
964bba1823 | ||
|
|
49b85f885d | ||
|
|
93e9357f84 | ||
|
|
2a7f58c187 | ||
|
|
08e47b8d04 | ||
|
|
217c4d5d65 | ||
|
|
ea762de937 | ||
|
|
e6105ca654 | ||
|
|
7a87ffc20a | ||
|
|
f53ad7d557 | ||
|
|
ffe4d0d4ae | ||
|
|
9e2f03d8be | ||
|
|
489cfe5a96 | ||
|
|
71debfbb65 | ||
|
|
34258511b9 | ||
|
|
0f121860fe | ||
|
|
70a6f547cf | ||
|
|
886f15aa9f | ||
|
|
d895301116 | ||
|
|
fdaa2d1dba | ||
|
|
da52b312ca | ||
|
|
8cc2334692 | ||
|
|
486d89669f | ||
|
|
c3614c9280 | ||
|
|
00aa0c2ae6 | ||
|
|
240c2c9395 | ||
|
|
88236b5a0b | ||
|
|
041cbafef1 | ||
|
|
c5b0916279 | ||
|
|
3c61f7cebf | ||
|
|
a13bd155dc | ||
|
|
09bae3d423 | ||
|
|
1f0fc09294 | ||
|
|
acc869086f | ||
|
|
0cdf793b42 | ||
|
|
d1ba60b673 | ||
|
|
ade7ea9235 | ||
|
|
7365e8a7ca | ||
|
|
c4207e4226 | ||
|
|
ddaef07fb5 | ||
|
|
f8174231cb | ||
|
|
0c8e52f18c | ||
|
|
559ccae1ad | ||
|
|
0597bfa513 | ||
|
|
921c0b699c | ||
|
|
3f36bf29b1 | ||
|
|
c6e1f8f155 | ||
|
|
65c178c04c | ||
|
|
69ab413676 | ||
|
|
71a4f5079e | ||
|
|
caeba1536a | ||
|
|
fd16bab763 | ||
|
|
2046ad4d06 | ||
|
|
12ddb585fb | ||
|
|
0a78489993 | ||
|
|
21abe6ff9a | ||
|
|
ab810e8f57 | ||
|
|
ac298baaec | ||
|
|
cf3fe0e852 | ||
|
|
79ebe443f5 | ||
|
|
f886f0a64c | ||
|
|
6b78f53bd7 | ||
|
|
b21e28ce82 | ||
|
|
35bd6292bb | ||
|
|
c4e17c672d | ||
|
|
d06d0ff8b5 | ||
|
|
07290cc8df | ||
|
|
bc65a40eb4 | ||
|
|
bc04af131a | ||
|
|
d48cd2545d | ||
|
|
133ff5421f | ||
|
|
69deb9ee91 | ||
|
|
f2700398dd | ||
|
|
8f7d9886c2 | ||
|
|
a2c225bf64 | ||
|
|
dd77de027b | ||
|
|
bcf81af71d | ||
|
|
c3c241adf5 | ||
|
|
cb4a04ce14 | ||
|
|
877ca1f74e | ||
|
|
d3bd28a0c1 | ||
|
|
4e4f0b8683 | ||
|
|
dfad543f6e | ||
|
|
9a1f9e763a | ||
|
|
1a9021e1ed | ||
|
|
5cca12b0ce | ||
|
|
0aed62f448 | ||
|
|
37a5f977c3 | ||
|
|
d7c550ea62 | ||
|
|
da86fd4875 | ||
|
|
aba32d7fab | ||
|
|
103976b2b0 | ||
|
|
0330006b89 | ||
|
|
12f3b5d241 | ||
|
|
673827cf70 | ||
|
|
5212d5b7f9 | ||
|
|
bfa2deeffb | ||
|
|
635cce877f | ||
|
|
0bab7544cd | ||
|
|
48b6e24135 | ||
|
|
423ceb5abc | ||
|
|
9b6a032d24 | ||
|
|
525006d716 | ||
|
|
083fd23503 | ||
|
|
3f18b181a3 | ||
|
|
50ec61ba37 | ||
|
|
f1ad3ddf25 | ||
|
|
936167ee9c | ||
|
|
52a624009d | ||
|
|
71ead2bf85 | ||
|
|
d56f075ee3 | ||
|
|
04d5237020 | ||
|
|
6379dbad4b | ||
|
|
b155b4b79f | ||
|
|
d7fd6d4e1e | ||
|
|
64fd2a6858 | ||
|
|
1ec06a4b80 | ||
|
|
2840b21946 | ||
|
|
69b1826d63 | ||
|
|
69cd1cb630 | ||
|
|
012f8f8af4 | ||
|
|
b85b426872 | ||
|
|
10169951c0 | ||
|
|
8eefbcec95 | ||
|
|
f73dd38dab | ||
|
|
9e821a754c | ||
|
|
f2caed46a8 | ||
|
|
7ef9719cc7 | ||
|
|
f47e5e5a82 | ||
|
|
f0fc0b6699 | ||
|
|
8cfc20999f | ||
|
|
bb72ad60b2 | ||
|
|
2b3e100cba | ||
|
|
5f432ce672 | ||
|
|
c3e4cd5e9f | ||
|
|
0f4529e1a6 | ||
|
|
58366e199e | ||
|
|
d512275f99 | ||
|
|
7e54e159df | ||
|
|
a92e26c49c | ||
|
|
e65a506dac | ||
|
|
6e78f05d75 | ||
|
|
f0f84a05ce | ||
|
|
18a71960e8 | ||
|
|
5e80c96587 | ||
|
|
9ec354c198 | ||
|
|
b7304f747b | ||
|
|
b7ab1ed476 | ||
|
|
bc522b27e0 | ||
|
|
3e9f975213 | ||
|
|
ba9e42fad6 | ||
|
|
05226453d9 | ||
|
|
4e2c050ffc | ||
|
|
39701c7657 | ||
|
|
6bdd22f875 | ||
|
|
20411f3518 | ||
|
|
27e733758d | ||
|
|
288381d8f6 | ||
|
|
c3f39aeb96 | ||
|
|
05d2ad8600 | ||
|
|
0911103949 | ||
|
|
9c8c290d83 | ||
|
|
bf3700ea8e | ||
|
|
c521f1faf7 | ||
|
|
ef7b789810 | ||
|
|
530f606657 | ||
|
|
120b9888f1 | ||
|
|
87884d4230 | ||
|
|
0b4eb92e95 | ||
|
|
51e55644f5 | ||
|
|
296496e895 | ||
|
|
afae3fc9a6 | ||
|
|
a301ca1311 | ||
|
|
eb97693e3f | ||
|
|
a6f01f7f82 | ||
|
|
a27996def8 | ||
|
|
fa96987eca | ||
|
|
192e65a827 | ||
|
|
a536912b38 | ||
|
|
fcf26a9ca7 | ||
|
|
4cb374dd5d | ||
|
|
9543bf1761 | ||
|
|
1527a1cb85 | ||
|
|
588d4f1024 | ||
|
|
c815d48274 | ||
|
|
1730ddebae | ||
|
|
1694f5cb89 | ||
|
|
1855cd4167 | ||
|
|
a4ff641366 | ||
|
|
2085c28bd1 | ||
|
|
6c2a55c67a | ||
|
|
2ffb4a4ab0 | ||
|
|
0e5d41a728 | ||
|
|
e1d5eba1f2 | ||
|
|
c99e9842ad | ||
|
|
20fabf40fa | ||
|
|
d8bba6e391 | ||
|
|
01429fe2a8 | ||
|
|
3496f45e39 | ||
|
|
88c37a9165 | ||
|
|
6d3abdaa02 | ||
|
|
eaa08b6991 | ||
|
|
1887ceff7f | ||
|
|
ff26a7f646 | ||
|
|
7dd85e445e | ||
|
|
4f22907004 | ||
|
|
ddc9f1ae12 | ||
|
|
7392d74efe | ||
|
|
535a1f1804 | ||
|
|
d387ee2816 | ||
|
|
dc174fa91c | ||
|
|
cf318bb47c | ||
|
|
d0a2c5ec40 | ||
|
|
cff6272c18 | ||
|
|
e808d770a9 | ||
|
|
63067d32d3 | ||
|
|
f9e9f20c05 | ||
|
|
3ee2f703a4 | ||
|
|
7b512485e6 | ||
|
|
d4e06db8e2 | ||
|
|
4ad893c147 | ||
|
|
381e644a42 | ||
|
|
ba81077c2e | ||
|
|
f7cf2264fa | ||
|
|
9bb4665fa9 | ||
|
|
4c9c23f620 | ||
|
|
75d6386e25 | ||
|
|
983944a776 | ||
|
|
373903cdb5 | ||
|
|
4b1257fc13 | ||
|
|
0416d4f1f7 | ||
|
|
911f82cda0 | ||
|
|
e4ed459376 | ||
|
|
46f25755c5 | ||
|
|
249e447920 | ||
|
|
cc199dcf4e | ||
|
|
baae8f5237 | ||
|
|
8437a87b54 | ||
|
|
b11a173b67 | ||
|
|
0e18f69cbf | ||
|
|
c3f9d93917 | ||
|
|
ac411754a0 | ||
|
|
45ebaa2c7f | ||
|
|
a923b0ce23 | ||
|
|
1be2cffd22 | ||
|
|
50af8bd3ff | ||
|
|
32ef29ee20 | ||
|
|
8620be452e | ||
|
|
cc79f9caee | ||
|
|
6a2eae7274 | ||
|
|
66002f7ceb | ||
|
|
d1b18f39dd | ||
|
|
dbd1e54c46 | ||
|
|
3c0ee51714 | ||
|
|
d4f7ab06cf | ||
|
|
66d8def3f8 | ||
|
|
8f29146c28 | ||
|
|
e53dc6ad0e | ||
|
|
90a96eb8bb | ||
|
|
3ff2c33d3c | ||
|
|
2237bb814c | ||
|
|
6b62e1db23 |
@@ -0,0 +1,194 @@
|
||||
name: Build
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
linux-build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
build-type: ["debug", "release"]
|
||||
|
||||
name: Linux ${{ matrix.build-type }}
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install flex bison meson libfreetype-dev libglew-dev libffi-dev libpng-dev libsndfile-dev libturbojpeg0-dev libwebp-dev libsdl2-dev zlib1g-dev
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir -p out/${{ matrix.build-type }}
|
||||
meson -Dbuildtype=${{ matrix.build-type }} out/${{ matrix.build-type }}
|
||||
ninja -C out/${{ matrix.build-type }}
|
||||
|
||||
- name: Test
|
||||
run: out/${{ matrix.build-type }}/src/xsystem4 test/Run/test.ain
|
||||
|
||||
flatpak-build:
|
||||
name: Flatpak
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-48
|
||||
options: --privileged
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
||||
with:
|
||||
bundle: xsystem4-linux-x86_64-nightly.flatpak
|
||||
manifest-path: technology.haniwa.xsystem4.yml
|
||||
cache-key: flatpak-builder-${{ github.sha }}
|
||||
|
||||
windows-build:
|
||||
runs-on: windows-latest
|
||||
name: Windows
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup MSYS2
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
update: true
|
||||
install: >-
|
||||
base-devel
|
||||
git
|
||||
nasm
|
||||
diffutils
|
||||
zip
|
||||
mingw-w64-x86_64-gcc
|
||||
mingw-w64-x86_64-meson
|
||||
mingw-w64-x86_64-pkg-config
|
||||
mingw-w64-x86_64-SDL2
|
||||
mingw-w64-x86_64-freetype
|
||||
mingw-w64-x86_64-libjpeg-turbo
|
||||
mingw-w64-x86_64-libwebp
|
||||
mingw-w64-x86_64-libpng
|
||||
mingw-w64-x86_64-libsndfile
|
||||
mingw-w64-x86_64-glew
|
||||
|
||||
- name: Cache FFmpeg
|
||||
id: cache-ffmpeg
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: FFmpeg/destdir
|
||||
key: ${{ runner.os }}-ffmpeg-n7.1.2
|
||||
|
||||
- name: Build FFmpeg
|
||||
if: ${{ steps.cache-ffmpeg.outputs.cache-hit != 'true' }}
|
||||
run: |
|
||||
git clone --depth 1 --branch n7.1.2 https://github.com/FFmpeg/FFmpeg.git
|
||||
cd FFmpeg
|
||||
./configure --disable-everything \
|
||||
--enable-decoder=mpegvideo \
|
||||
--enable-decoder=mpeg1video \
|
||||
--enable-decoder=mpeg2video \
|
||||
--enable-decoder=mp2 \
|
||||
--enable-parser=mpegaudio \
|
||||
--enable-parser=mpegvideo \
|
||||
--enable-demuxer=mpegps \
|
||||
--enable-demuxer=mpegts \
|
||||
--enable-demuxer=mpegtsraw \
|
||||
--enable-demuxer=mpegvideo \
|
||||
--enable-decoder=vc1 \
|
||||
--enable-decoder=wmapro \
|
||||
--enable-parser=vc1 \
|
||||
--enable-hwaccel=vc1_d3d11va \
|
||||
--enable-hwaccel=vc1_d3d11va2 \
|
||||
--enable-hwaccel=vc1_dxva2 \
|
||||
--enable-demuxer=asf \
|
||||
--enable-protocol=file \
|
||||
--enable-filter=scale \
|
||||
--enable-static \
|
||||
--disable-shared \
|
||||
--extra-libs=-static \
|
||||
--extra-cflags=--static \
|
||||
--prefix=/mingw64
|
||||
make -j$(nproc)
|
||||
mkdir destdir
|
||||
DESTDIR=destdir make install
|
||||
cd ..
|
||||
|
||||
- name: Install FFmpeg
|
||||
run: |
|
||||
cp -a FFmpeg/destdir/. /
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir debug
|
||||
meson debug -Dbuildtype=debug
|
||||
ninja -C debug
|
||||
mkdir release
|
||||
meson release -Dbuildtype=release
|
||||
ninja -C release
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
debug/src/xsystem4.exe test/Run/test.ain
|
||||
release/src/xsystem4.exe test/Run/test.ain
|
||||
|
||||
- name: Copy licenses
|
||||
run: |
|
||||
ls /mingw64/share/licenses
|
||||
mkdir dist
|
||||
cp -r /mingw64/share/licenses dist/
|
||||
|
||||
- name: Package
|
||||
run: |
|
||||
cp release/src/xsystem4.exe COPYING README.md game_compatibility.md dist/
|
||||
cp debug/src/xsystem4.exe dist/xsystem4-debug.exe
|
||||
cp /mingw64/bin/glew32.dll dist/
|
||||
cp extra/*.lnk dist/
|
||||
cp -r fonts dist/
|
||||
cp -r shaders dist/
|
||||
mv dist xsystem4
|
||||
zip -r -9 xsystem4-windows-x86_64-nightly.zip xsystem4
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: xsystem4
|
||||
path: xsystem4-windows-x86_64-nightly.zip
|
||||
|
||||
nightly:
|
||||
name: Update nightly release
|
||||
needs: [windows-build, flatpak-build]
|
||||
if: ${{ github.repository == 'nunuhara/xsystem4' && github.ref == 'refs/heads/master' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download release artifacts
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
merge-multiple: true
|
||||
|
||||
- name: Update nightly tag
|
||||
uses: richardsimko/update-tag@v1
|
||||
with:
|
||||
tag_name: nightly
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Release nightly
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
prerelease: true
|
||||
name: nightly
|
||||
tag_name: nightly
|
||||
files: |
|
||||
xsystem4-windows-x86_64-nightly.zip
|
||||
xsystem4-linux-x86_64-nightly.flatpak
|
||||
fail_on_unmatched_files: true
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
# CMakeLists.txt for Android build
|
||||
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(xsystem4 LANGUAGES C)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
cglm
|
||||
URL https://github.com/recp/cglm/archive/refs/tags/v0.9.2.tar.gz
|
||||
URL_HASH SHA1=cb8472aa8c2ab67b66378dbaf10c2c7368d4e4c3
|
||||
)
|
||||
FetchContent_MakeAvailable(cglm)
|
||||
|
||||
add_library(ffi STATIC IMPORTED)
|
||||
set_target_properties(ffi PROPERTIES
|
||||
IMPORTED_LOCATION ${CMAKE_STAGING_PREFIX}/lib/libffi.a
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_STAGING_PREFIX}/include)
|
||||
|
||||
find_package(Freetype REQUIRED)
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_package(SndFile REQUIRED)
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
add_subdirectory(subprojects/libsys4)
|
||||
|
||||
add_library(xsystem4 SHARED)
|
||||
|
||||
target_compile_definitions(xsystem4 PRIVATE
|
||||
_DEFAULT_SOURCE
|
||||
USE_GLES
|
||||
)
|
||||
|
||||
target_include_directories(xsystem4 PRIVATE include)
|
||||
|
||||
execute_process(
|
||||
COMMAND git rev-parse --short HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE VCS_TAG
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
configure_file(src/version.h.in version.h)
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
target_sources(xsystem4 PRIVATE
|
||||
src/audio.c
|
||||
src/audio_meta.c
|
||||
src/audio_mixer.c
|
||||
src/asset_manager.c
|
||||
src/base64.c
|
||||
src/cJSON.c
|
||||
src/draw.c
|
||||
src/effect.c
|
||||
src/ffi.c
|
||||
src/font_freetype.c
|
||||
src/font_fnl.c
|
||||
src/format.c
|
||||
src/hacks.c
|
||||
src/heap.c
|
||||
src/icon.c
|
||||
src/id_pool.c
|
||||
src/input.c
|
||||
src/json.c
|
||||
src/movie_plmpeg.c
|
||||
src/msgqueue.c
|
||||
src/page.c
|
||||
src/resume.c
|
||||
src/savedata.c
|
||||
src/scene.c
|
||||
src/screenshot.c
|
||||
src/sprite.c
|
||||
src/swf.c
|
||||
src/system4.c
|
||||
src/text.c
|
||||
src/util.c
|
||||
src/video.c
|
||||
src/vm.c
|
||||
|
||||
src/3d/collision.c
|
||||
src/3d/debug.c
|
||||
src/3d/model.c
|
||||
src/3d/mpr.c
|
||||
src/3d/parser.c
|
||||
src/3d/particle.c
|
||||
src/3d/reign.c
|
||||
src/3d/renderer.c
|
||||
src/3d/s3de.c
|
||||
src/3d/save.c
|
||||
|
||||
src/dungeon/dgn.c
|
||||
src/dungeon/dtx.c
|
||||
src/dungeon/dungeon.c
|
||||
src/dungeon/generator.c
|
||||
src/dungeon/map.c
|
||||
src/dungeon/mtrand43.c
|
||||
src/dungeon/polyobj.c
|
||||
src/dungeon/renderer.c
|
||||
src/dungeon/skybox.c
|
||||
src/dungeon/tes.c
|
||||
|
||||
src/parts/construction.c
|
||||
src/parts/debug.c
|
||||
src/parts/flash.c
|
||||
src/parts/flat.c
|
||||
src/parts/input.c
|
||||
src/parts/layoutbox.c
|
||||
src/parts/message.c
|
||||
src/parts/motion.c
|
||||
src/parts/parts.c
|
||||
src/parts/render.c
|
||||
src/parts/text.c
|
||||
src/parts/save.c
|
||||
|
||||
src/hll/ACXLoader.c
|
||||
src/hll/ADVSYS.c
|
||||
src/hll/AFAFactory.c
|
||||
src/hll/AliceLogo.c
|
||||
src/hll/AliceLogo2.c
|
||||
src/hll/AliceLogo3.c
|
||||
src/hll/AliceLogo4.c
|
||||
src/hll/AliceLogo5.c
|
||||
src/hll/AnteaterADVEngine.c
|
||||
src/hll/Array.c
|
||||
src/hll/BanMisc.c
|
||||
src/hll/Bitarray.c
|
||||
src/hll/CalcTable.c
|
||||
src/hll/CGManager.c
|
||||
src/hll/CharSpriteManager.c
|
||||
src/hll/ChipmunkSpriteEngine.c
|
||||
src/hll/ChrLoader.c
|
||||
src/hll/CommonSystemData.c
|
||||
src/hll/Confirm.c
|
||||
src/hll/Confirm2.c
|
||||
src/hll/Confirm3.c
|
||||
src/hll/CrayfishLogViewer.c
|
||||
src/hll/Cursor.c
|
||||
src/hll/DALKDemo.c
|
||||
src/hll/DALKEDemo.c
|
||||
src/hll/Data.c
|
||||
src/hll/DataFile.c
|
||||
src/hll/Discord.c
|
||||
src/hll/DrawDungeon.c
|
||||
src/hll/DrawEffect.c
|
||||
src/hll/DrawGraph.c
|
||||
src/hll/DrawMovie.c
|
||||
src/hll/DrawNumeral.c
|
||||
src/hll/DrawPluginManager.c
|
||||
src/hll/DrawRain.c
|
||||
src/hll/DrawRipple.c
|
||||
src/hll/DrawSimpleText.c
|
||||
src/hll/DrawSnow.c
|
||||
src/hll/File.c
|
||||
src/hll/File2.c
|
||||
src/hll/FileOperation.c
|
||||
src/hll/FillAngle.c
|
||||
src/hll/GoatGUIEngine.c
|
||||
src/hll/Gpx2Plus.c
|
||||
src/hll/GUIEngine.c
|
||||
src/hll/HTTPDownloader.c
|
||||
src/hll/iarray.c
|
||||
src/hll/IbisInputEngine.c
|
||||
src/hll/InputDevice.c
|
||||
src/hll/InputString.c
|
||||
src/hll/KiwiSoundEngine.c
|
||||
src/hll/LoadCG.c
|
||||
src/hll/MADLoader.c
|
||||
src/hll/MainEXFile.c
|
||||
src/hll/MainSurface.c
|
||||
src/hll/MamanyoDemo.c
|
||||
src/hll/MamanyoSDemo.c
|
||||
src/hll/MarmotModelEngine.c
|
||||
src/hll/Math.c
|
||||
src/hll/MapLoader.c
|
||||
src/hll/MenuMsg.c
|
||||
src/hll/MonsterInfo.c
|
||||
src/hll/MsgLogManager.c
|
||||
src/hll/MsgLogViewer.c
|
||||
src/hll/MsgSkip.c
|
||||
src/hll/MusicSystem.c
|
||||
src/hll/NewFont.c
|
||||
src/hll/OutputLog.c
|
||||
src/hll/P3MapSprite.c
|
||||
src/hll/P3SquareSprite.c
|
||||
src/hll/PassRegister.c
|
||||
src/hll/PastelChime2.c
|
||||
src/hll/PartsEngine.c
|
||||
src/hll/PixelRestore.c
|
||||
src/hll/PlayDemo.c
|
||||
src/hll/PlayMovie.c
|
||||
src/hll/ReignEngine.c
|
||||
src/hll/SACT2.c
|
||||
src/hll/SengokuRanceFont.c
|
||||
src/hll/SoundFilePlayer.c
|
||||
src/hll/Sound2ex.c
|
||||
src/hll/StoatSpriteEngine.c
|
||||
src/hll/StretchHelper.c
|
||||
src/hll/SystemService.c
|
||||
src/hll/SystemServiceEx.c
|
||||
src/hll/Timer.c
|
||||
src/hll/Toushin3Loader.c
|
||||
src/hll/vmAnime.c
|
||||
src/hll/vmArray.c
|
||||
src/hll/vmCG.c
|
||||
src/hll/vmChrLoader.c
|
||||
src/hll/vmCursor.c
|
||||
src/hll/vmDalkGaiden.c
|
||||
src/hll/vmData.c
|
||||
src/hll/vmDialog.c
|
||||
src/hll/vmDrawGauge.c
|
||||
src/hll/vmDrawMsg.c
|
||||
src/hll/vmDrawNumber.c
|
||||
src/hll/vmFile.c
|
||||
src/hll/vmGraph.c
|
||||
src/hll/vmGraphQuake.c
|
||||
src/hll/vmKey.c
|
||||
src/hll/vmMapLoader.c
|
||||
src/hll/vmMsgLog.c
|
||||
src/hll/vmMusic.c
|
||||
src/hll/vmSound.c
|
||||
src/hll/vmSprite.c
|
||||
src/hll/vmString.c
|
||||
src/hll/vmSurface.c
|
||||
src/hll/vmSystem.c
|
||||
src/hll/vmTimer.c
|
||||
src/hll/ValueEncryption.c
|
||||
src/hll/VSFile.c
|
||||
)
|
||||
|
||||
target_link_libraries(xsystem4 PRIVATE
|
||||
m ZLIB::ZLIB SDL2::SDL2 Freetype::Freetype ffi GLESv3 cglm SndFile::sndfile sys4)
|
||||
|
||||
target_compile_options(xsystem4 PRIVATE -Wno-unused-parameter)
|
||||
|
||||
install(TARGETS xsystem4)
|
||||
@@ -47,6 +47,72 @@ 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
|
||||
----------
|
||||
|
||||
|
||||
Binary file not shown.
Generated
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1757244434,
|
||||
"narHash": "sha256-AeqTqY0Y95K1Fgs6wuT1LafBNcmKxcOkWnm4alD9pqM=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "092c565d333be1e17b4779ac22104338941d913f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-25.05",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
{
|
||||
description = "Open source implementation of AliceSoft's System 4 game engine for unix-like operating systems";
|
||||
|
||||
# Nixpkgs / NixOS version to use.
|
||||
inputs.nixpkgs.url = "nixpkgs/nixos-25.05";
|
||||
|
||||
outputs = { self, nixpkgs }:
|
||||
let
|
||||
|
||||
# System types to support.
|
||||
supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ];
|
||||
|
||||
# Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'.
|
||||
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||
|
||||
# Nixpkgs instantiated for supported system types.
|
||||
nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; });
|
||||
|
||||
in {
|
||||
|
||||
# Set up xsystem4 development environment:
|
||||
# nix develop # create shell environment with dependencies available
|
||||
devShell = forAllSystems (system:
|
||||
let pkgs = nixpkgsFor.${system}; in with pkgs; pkgs.mkShell {
|
||||
nativeBuildInputs = [ meson ninja pkg-config ];
|
||||
buildInputs = [
|
||||
cglm
|
||||
SDL2
|
||||
freetype
|
||||
libffi
|
||||
libjpeg
|
||||
libwebp
|
||||
libsndfile
|
||||
glew
|
||||
readline
|
||||
chibi
|
||||
flex
|
||||
bison
|
||||
ffmpeg
|
||||
];
|
||||
}
|
||||
);
|
||||
|
||||
# Build xsystem4:
|
||||
# nix build .?submodules=1 # build xsystem4 (outputs to ./result/)
|
||||
# nix shell .?submodules=1 # create shell environment with xsystem4 available
|
||||
# Install xsystem4 to user profile:
|
||||
# nix profile install .?submodules=1
|
||||
packages = forAllSystems (system:
|
||||
let pkgs = nixpkgsFor.${system}; in {
|
||||
default = with pkgs; stdenv.mkDerivation rec {
|
||||
name = "xsystem4";
|
||||
src = ./.;
|
||||
mesonAutoFeatures = "auto";
|
||||
nativeBuildInputs = [ meson ninja pkg-config ];
|
||||
buildInputs = [
|
||||
cglm
|
||||
SDL2
|
||||
freetype
|
||||
libffi
|
||||
libjpeg
|
||||
libwebp
|
||||
libsndfile
|
||||
glew
|
||||
readline
|
||||
chibi
|
||||
flex
|
||||
bison
|
||||
ffmpeg
|
||||
];
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
+53
-31
@@ -14,52 +14,52 @@ issues, please let me know or open a pull request to update this table.
|
||||
|
||||
| Game | Status | Notes |
|
||||
| ------------------------------------------------------- | ----------- | ----- |
|
||||
| DALK外伝 | Unsupported | |
|
||||
| ままにょにょ | Unsupported | |
|
||||
| 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 | Unknown | |
|
||||
| 鈴宮刑務娼館 | Unknown | From ALICEの館7 |
|
||||
| しまいま | Unknown | From ALICEの館7 |
|
||||
| DUNGEONS & DOLLS | Unsupported | From ALICEの館7 |
|
||||
| こすぷれれもん | Unknown | From ALICEの館7 |
|
||||
| エルメイフラワー | Unknown | From ALICEの館7 |
|
||||
| CGは17年ゼミ | Unknown | From ALICEの館7 |
|
||||
| でーぴーえすVer_N | Unknown | From ALICEの館7 |
|
||||
| 大番長外伝 | Unknown | From ALICEの館7 |
|
||||
| ZERO | Unknown | From ALICEの館7 |
|
||||
| ぷろGRX | Unknown | From ALICEの館7 |
|
||||
| Girls | Unknown | From ALICEの館7 |
|
||||
| 神武 -カムナガラ- | Unknown | From ALICEの館7 |
|
||||
| オニむス | Unknown | From ALICEの館7 |
|
||||
| 透色 | Unknown | From ALICEの館7 |
|
||||
| DEBUT | Unknown | From ALICEの館7 |
|
||||
| ランス6その後 | Unknown | From ALICEの館7 |
|
||||
| ほいっぷハニー | Unknown | From ALICEの館7 |
|
||||
| Text Jam Session | Unknown | From ALICEの館7 |
|
||||
| 没ロゴの哀しみ | Unknown | From ALICEの館7 |
|
||||
| スタッフコーナー | Unknown | From ALICEの館7 |
|
||||
| ぱすてるチャイムContinue | Unsupported | |
|
||||
| ウルトラ魔法少女まなな 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++ | Unknown | |
|
||||
| ぱすちゃC++ | Supported | |
|
||||
| よくばりサボテン | Supported | |
|
||||
| 妻しぼり | Unknown | |
|
||||
| 戦国ランス | Supported | |
|
||||
| Sengoku Rance (EN FanTL) | Supported | |
|
||||
| Sengoku Rance (MangaGamer) | Supported | |
|
||||
| だぶる先生らいふっ | Unknown | |
|
||||
| お嬢様をいいなりにするゲーム | Unknown | |
|
||||
| お嬢様をいいなりにするゲーム | Supported | |
|
||||
| 超昂閃忍ハルカ | Unknown | |
|
||||
| 戦国ランス~三匹が斬ったり突いたり燃やしたり | Unknown | |
|
||||
| Sengoku Rance -Three for the Slash! Thrust! Burn!- (EN) | Unknown | |
|
||||
| 戦国ランス~三匹が斬ったり突いたり燃やしたり | Supported | |
|
||||
| Sengoku Rance -Three for the Slash! Thrust! Burn!- (EN) | Supported | |
|
||||
| AliveZ | Unknown | |
|
||||
| エドランゼ ~Come on!水戸姫様~ | Unknown | |
|
||||
| エドランゼ ~Come on!水戸姫様~ | Supported | |
|
||||
| 闘神都市III | Supported | |
|
||||
| ばにしゅ!~おっぱいの消えた王国~ | Unknown | |
|
||||
| 僕だけの保健室 | Unknown | |
|
||||
| 僕だけの保健室 | Supported | |
|
||||
| ももいろガーディアン | Unknown | |
|
||||
| 超昂閃忍ハルカ ハルカVSエスカレイヤー | Unknown | From Alice 2010 |
|
||||
| 超昂閃忍ハルカ ―疾風!?忍者大作戦― | Unknown | From Alice 2010 |
|
||||
@@ -72,4 +72,26 @@ issues, please let me know or open a pull request to update this table.
|
||||
| ランス02 -反逆の少女たち- | Supported | From Alice 2010 |
|
||||
| Rance 02 -The Rebellious Maidens- (Fan TL) | Supported | |
|
||||
| Rance 02 -The Rebellious Maidens- (MangaGamer) | Supported | |
|
||||
| All games released after Alice 2010 | Unsupported | |
|
||||
| しゃーまんず・さんくちゅあり-巫女の聖域- | Unknown | |
|
||||
| 大帝国 | Supported | |
|
||||
| ランス・クエスト | Supported | |
|
||||
| ランス・クエスト マグナム | Supported | |
|
||||
| 母娘乱館 | Unknown | |
|
||||
| パステルチャイム3 バインドシーカー | Supported | |
|
||||
| パスチャ3 プラスコンテンツ | Supported | |
|
||||
| どらぺこ! ~おねだりドラゴンとおっぱい勇者~ | Unsupported | |
|
||||
| ランス01 光をもとめて | Supported | |
|
||||
| Rance 01 - Quest for Hikari (EN) | Supported | |
|
||||
| ランスIX-ヘルマン革命- | Supported | |
|
||||
| Rance IX - The Helmanian Revolution (EN) | Unknown | |
|
||||
| 武想少女隊ぶれいど☆ブライダーズ | Supported | |
|
||||
| イブニクル | Unsupported | |
|
||||
| Evenicle (EN) | Unsupported | |
|
||||
| ランス03 リーザス陥落 | Unsupported | |
|
||||
| Rance 03 -The Fall of Leazas- (EN) | Unsupported | |
|
||||
| 妻みぐい3 | Unsupported | |
|
||||
| は~とふるママン | Unsupported | |
|
||||
| 超昂神騎エクシール | Unsupported | |
|
||||
| Beat Valkyrie Ixseal (EN) | Unsupported | |
|
||||
| Rance X -決戦- | Unsupported | |
|
||||
| All games released after Rance X | Unsupported | |
|
||||
|
||||
@@ -30,9 +30,10 @@ 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);
|
||||
|
||||
+14
-7
@@ -19,10 +19,16 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#define AUDIO_NO_METADATA -1
|
||||
|
||||
struct archive_data;
|
||||
|
||||
void audio_init(void);
|
||||
void audio_fini(void);
|
||||
void audio_reset(void);
|
||||
void audio_update(void);
|
||||
bool audio_play_sound(int sound_no);
|
||||
// Takes ownership of dfile.
|
||||
bool audio_play_archive_data(struct archive_data *dfile);
|
||||
|
||||
bool wav_exists(int no);
|
||||
bool bgm_exists(int no);
|
||||
@@ -74,16 +80,17 @@ int wav_get_volume(int id);
|
||||
int bgm_get_volume(int id);
|
||||
int wav_get_time_length(int id);
|
||||
int bgm_get_time_length(int id);
|
||||
//int wav_get_group_num(int id);
|
||||
int wav_get_data_length(int no);
|
||||
int bgm_get_data_length(int no);
|
||||
int wav_get_group_num(int id);
|
||||
//int bgm_get_group_num(int id);
|
||||
//int wav_prepare_from_file(int id, char *filename);
|
||||
//int bgm_prepare_from_file(int id, char *filename);
|
||||
int wav_prepare_from_file(int id, char *filename);
|
||||
int bgm_prepare_from_file(int id, char *filename);
|
||||
|
||||
int wav_get_group_num_from_data_num(int no);
|
||||
|
||||
struct archive_data;
|
||||
|
||||
// Takes ownership of dfile.
|
||||
int wav_prepare_from_archive_data(int id, struct archive_data *dfile);
|
||||
int wav_prepare_from_archive_data(int id, int metadata_no, struct archive_data *dfile);
|
||||
int bgm_prepare_from_archive_data(int id, int metadata_no, struct archive_data *dfile);
|
||||
|
||||
#endif /* SYSTEM4_AUDIO_H */
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/* Copyright (C) 2023 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM4_BASE64_H
|
||||
#define SYSTEM4_BASE64_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
unsigned char *base64_encode(const unsigned char *src, size_t len,
|
||||
size_t *out_len);
|
||||
|
||||
#endif /* SYSTEM4_BASE64_H */
|
||||
+45
-2
@@ -18,6 +18,7 @@
|
||||
#define SYSTEM4_DEBUGGER_H
|
||||
#ifdef DEBUGGER_ENABLED
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "system4/instructions.h"
|
||||
@@ -28,6 +29,18 @@
|
||||
|
||||
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;
|
||||
@@ -46,13 +59,19 @@ struct dbg_cmd {
|
||||
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);
|
||||
|
||||
void dbg_log(const char *log, const char *fmt, va_list ap);
|
||||
|
||||
void dbg_continue(void);
|
||||
void dbg_quit(void);
|
||||
@@ -61,6 +80,8 @@ 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);
|
||||
@@ -70,13 +91,35 @@ 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,20 +20,25 @@
|
||||
#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;
|
||||
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 {
|
||||
@@ -62,19 +67,34 @@ 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_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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#ifndef SYSTEM4_EFFECT_H
|
||||
#define SYSTEM4_EFFECT_H
|
||||
|
||||
struct texture;
|
||||
|
||||
enum effect {
|
||||
EFFECT_CROSSFADE = 1,
|
||||
EFFECT_FADEOUT = 2,
|
||||
@@ -70,9 +72,16 @@ enum effect {
|
||||
EFFECT_BLUR_FADEOUT = 50,
|
||||
EFFECT_BLUR_CROSSFADE = 51,
|
||||
EFFECT_2ROT_ZOOM_BLEND_BLUR = 52,
|
||||
EFFECT_VWAVE_SCROLL_CROSSFADE = 55,
|
||||
EFFECT_VWAVE_CROSSFADE = 60,
|
||||
NR_EFFECTS
|
||||
};
|
||||
|
||||
extern const char *effect_names[NR_EFFECTS];
|
||||
|
||||
int effect_init(enum effect type);
|
||||
void effect_update_texture(int type, struct texture *dst, struct texture *old, struct texture *new, float rate);
|
||||
int effect_update(float rate);
|
||||
int effect_fini(void);
|
||||
|
||||
#endif /* SYSTEM4_EFFECT_H */
|
||||
|
||||
+34
-2
@@ -68,6 +68,7 @@ struct font {
|
||||
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 {
|
||||
@@ -89,6 +90,27 @@ struct text_style {
|
||||
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));
|
||||
@@ -132,14 +154,24 @@ 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, float x, int y, char *msg, struct text_style *ts);
|
||||
struct font_size *gfx_font_get_size(unsigned face, float size);
|
||||
enum font_weight gfx_int_to_font_weight(int weight);
|
||||
float _gfx_render_text(Texture *dst, char *msg, struct text_render_metrics *tm);
|
||||
|
||||
int gfx_render_text(Texture *dst, float x, int y, char *msg, struct text_style *ts, bool blend);
|
||||
float gfx_render_textf(Texture *dst, float x, int y, char *msg, struct text_style *ts, bool blend);
|
||||
void gfx_render_dash_text(Texture *dst, struct text_style *ts);
|
||||
void gfx_draw_text_to_amap(Texture *dst, int x, int y, char *text);
|
||||
void gfx_draw_text_to_pmap(Texture *dst, int x, int y, char *text);
|
||||
float gfx_size_char_unstyled(struct text_style *ts, const char *ch);
|
||||
float gfx_size_char(struct text_style *ts, const char *ch);
|
||||
float gfx_size_text(struct text_style *ts, const char *text);
|
||||
float gfx_get_actual_font_size(unsigned face, float size);
|
||||
float gfx_get_actual_font_size_round_down(unsigned face, float size);
|
||||
|
||||
void gfx_print_text_style(struct text_style *style, int indent);
|
||||
static inline float text_style_height(struct text_style *ts)
|
||||
{
|
||||
return ts->size + ceilf(ts->bold_width)*2 + ts->edge_up + ts->edge_down;
|
||||
}
|
||||
|
||||
#endif /* SYSTEM4_FONT_H */
|
||||
|
||||
+36
-14
@@ -36,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;
|
||||
@@ -51,38 +47,59 @@ typedef struct shader {
|
||||
GLuint world_transform;
|
||||
GLuint view_transform;
|
||||
GLuint texture;
|
||||
GLuint vertex;
|
||||
GLuint alpha_mod;
|
||||
GLuint vertex_pos;
|
||||
GLuint vertex_uv;
|
||||
void (*prepare)(struct gfx_render_job *, void *);
|
||||
} Shader;
|
||||
|
||||
enum gfx_shape {
|
||||
GFX_RECTANGLE,
|
||||
GFX_LINE,
|
||||
GFX_QUADRILATERAL,
|
||||
};
|
||||
|
||||
struct gfx_render_job {
|
||||
struct shader *shader;
|
||||
enum gfx_shape shape;
|
||||
GLuint texture;
|
||||
GLfloat *world_transform;
|
||||
GLfloat *view_transform;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct gfx_vertex {
|
||||
GLfloat x, y, z, w;
|
||||
GLfloat u, v;
|
||||
};
|
||||
|
||||
int gfx_init(void);
|
||||
void gfx_fini(void);
|
||||
|
||||
Texture *gfx_main_surface(void);
|
||||
void gfx_set_window_logical_size(int w, int h);
|
||||
bool gfx_set_fullscreen(bool enable);
|
||||
bool gfx_is_fullscreen(void);
|
||||
void gfx_toggle_fullscreen(void);
|
||||
void gfx_update_screen_scale(void);
|
||||
void gfx_set_wait_vsync(bool wait);
|
||||
float gfx_get_frame_rate(void);
|
||||
|
||||
void gfx_load_shader(struct shader *dst, const char *vertex_shader_path, const char *fragment_shader_path);
|
||||
GLuint gfx_load_shader_file(const char *path, GLenum type);
|
||||
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);
|
||||
@@ -92,6 +109,7 @@ 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);
|
||||
@@ -143,13 +161,17 @@ 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_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);
|
||||
@@ -161,14 +183,14 @@ void gfx_copy_amap_height_blur(Texture *dst, int dx, int dy, Texture *src, int s
|
||||
void gfx_copy_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_fill_with_alpha(Texture *dst, int x, int y, int w, int h, int r, int g, int b, int a);
|
||||
void gfx_copy_stretch_with_alpha_map(Texture *dst, int dx, int dy, int dw, int dh, Texture *src, int sx, int sy, int sw, int sh);
|
||||
void gfx_draw_glyph(Texture *dst, float dx, int dy, Texture *glyph, SDL_Color color, float scale_x, float bold_width);
|
||||
void gfx_copy_grayscale(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_copy_grayscale_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_copy_grayscale_reverse_LR_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_draw_line(Texture *dst, int x0, int y0, int x1, int y1, int r, int g, int b);
|
||||
void gfx_draw_line_to_amap(Texture *dst, int x0, int y0, int x1, int y1, int a);
|
||||
void gfx_draw_glyph(Texture *dst, float dx, int dy, Texture *glyph, SDL_Color color, float scale_x, float bold_width, bool blend);
|
||||
void gfx_draw_glyph_to_pmap(Texture *dst, float dx, int dy, Texture *glyph, Rectangle glyph_pos, SDL_Color color, float scale_x);
|
||||
void gfx_draw_glyph_to_amap(Texture *dst, float dx, int dy, Texture *glyph, Rectangle glyph_pos, float scale_x);
|
||||
|
||||
// debug printing
|
||||
void gfx_print_color(SDL_Color *c);
|
||||
void gfx_print_rectangle(Rectangle *r);
|
||||
void gfx_print_point(Point *p);
|
||||
void gfx_print_texture(struct texture *t, int indent);
|
||||
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);
|
||||
|
||||
|
||||
+4
-1
@@ -137,9 +137,10 @@ 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);
|
||||
@@ -152,5 +153,7 @@ void register_input_handler(void(*handler)(const char*));
|
||||
void clear_input_handler(void);
|
||||
void register_editing_handler(void(*handler)(const char*, int, int));
|
||||
void clear_editing_handler(void);
|
||||
void register_key_handler(void(*handler)(int));
|
||||
void clear_key_handler(void);
|
||||
|
||||
#endif /* SYSTEM4_INPUT_H */
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/* Copyright (C) 2023 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef XSYSTEM4_JSON_H
|
||||
#define XSYSTEM4_JSON_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "gfx/types.h"
|
||||
|
||||
typedef struct cJSON cJSON;
|
||||
struct texture;
|
||||
struct text_style;
|
||||
struct sact_sprite;
|
||||
struct sprite;
|
||||
|
||||
cJSON *num2_to_json(double a, double b);
|
||||
cJSON *num2_to_json_object(const char *a_name, double a, const char *b_name, double b);
|
||||
cJSON *num3_to_json(double a, double b, double c);
|
||||
cJSON *num3_to_json_object(const char *a_name, double a, const char *b_name, double b,
|
||||
const char *c_name, double c);
|
||||
cJSON *num4_to_json(double a, double b, double c, double d);
|
||||
cJSON *num4_to_json_object(const char *a_name, double a, const char *b_name, double b,
|
||||
const char *c_name, double c, const char *d_name, double d);
|
||||
cJSON *texture_to_json(struct texture *t, bool verbose);
|
||||
cJSON *texture_to_json_with_pixels(struct texture *t);
|
||||
cJSON *text_style_to_json(struct text_style *ts, bool verbose);
|
||||
cJSON *scene_sprite_to_json(struct sprite *sp, bool verbose);
|
||||
cJSON *sprite_to_json(struct sact_sprite *sp, bool verbose);
|
||||
|
||||
static inline cJSON *xy_to_json(double x, double y, bool verbose)
|
||||
{
|
||||
if (!verbose)
|
||||
return num2_to_json(x, y);
|
||||
return num2_to_json_object("x", x, "y", y);
|
||||
}
|
||||
|
||||
static inline cJSON *wh_to_json(double w, double h, bool verbose)
|
||||
{
|
||||
if (!verbose)
|
||||
return num2_to_json(w, h);
|
||||
return num2_to_json_object("w", w, "h", h);
|
||||
}
|
||||
|
||||
static inline cJSON *point_to_json(Point *p, bool verbose)
|
||||
{
|
||||
return xy_to_json(p->x, p->y, verbose);
|
||||
}
|
||||
|
||||
static inline cJSON *rectangle_to_json(Rectangle *r, bool verbose)
|
||||
{
|
||||
if (!verbose)
|
||||
return num4_to_json(r->x, r->y, r->w, r->h);
|
||||
return num4_to_json_object("x", r->x, "y", r->y, "w", r->w, "h", r->h);
|
||||
}
|
||||
|
||||
static inline cJSON *color_to_json(Color *c, bool verbose)
|
||||
{
|
||||
if (!verbose)
|
||||
return num4_to_json(c->r, c->g, c->b, c->a);
|
||||
return num4_to_json_object("r", c->r, "g", c->g, "b", c->b, "a", c->a);
|
||||
}
|
||||
|
||||
static inline cJSON *vec3_point_to_json(vec3 v, bool verbose)
|
||||
{
|
||||
if (!verbose)
|
||||
return num3_to_json(v[0], v[1], v[2]);
|
||||
return num3_to_json_object("x", v[0], "y", v[1], "z", v[2]);
|
||||
}
|
||||
|
||||
static inline cJSON *vec3_color_to_json(vec3 v, bool verbose)
|
||||
{
|
||||
if (!verbose)
|
||||
return num3_to_json(v[0], v[1], v[2]);
|
||||
return num3_to_json_object("r", v[0], "g", v[1], "b", v[2]);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,76 +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>
|
||||
|
||||
// no need to decode on byte-addressable, little-endian arch
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
#define LittleEndian_getDW(b, i) (*((int32_t*)((uint8_t*)(b)+(i))))
|
||||
#define LittleEndian_getW(b, i) (*((int16_t*)((uint8_t*)(b)+(i))))
|
||||
#else
|
||||
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 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);
|
||||
}
|
||||
#endif /* defined(__i386__) || defined(__x86_64__) */
|
||||
|
||||
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 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 */
|
||||
+5
-2
@@ -59,8 +59,10 @@ struct channel;
|
||||
enum asset_type;
|
||||
|
||||
struct channel *channel_open(enum asset_type type, int no);
|
||||
// Takes ownership of dfile.
|
||||
struct channel *channel_open_archive_data(struct archive_data *dfile);
|
||||
// Takes ownership of dfile. Metadata is not applied if metadata_no is negative.
|
||||
struct channel *channel_open_archive_data(struct archive_data *dfile,
|
||||
enum asset_type type, int metadata_no);
|
||||
struct channel *channel_open_file(const char *path);
|
||||
void channel_close(struct channel *ch);
|
||||
int channel_play(struct channel *ch);
|
||||
int channel_stop(struct channel *ch);
|
||||
@@ -83,5 +85,6 @@ int channel_seek(struct channel *ch, int pos);
|
||||
int channel_reverse_LR(struct channel *ch);
|
||||
int channel_get_volume(struct channel *ch);
|
||||
int channel_get_time_length(struct channel *ch);
|
||||
int channel_get_data_no(struct channel *ch);
|
||||
|
||||
#endif /* SYSTEM4_MIXER_H */
|
||||
|
||||
@@ -0,0 +1,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__
|
||||
+156
-28
@@ -23,7 +23,9 @@ struct page;
|
||||
struct string;
|
||||
|
||||
// parts.c
|
||||
void PE_enable_multi_controller(void);
|
||||
bool PE_Init(void);
|
||||
void PE_Reset(void);
|
||||
void PE_Update(int passed_time, bool message_window_show);
|
||||
void PE_UpdateComponent(int passed_time);
|
||||
void PE_UpdateParts(int passed_time, bool is_skip, bool message_window_show);
|
||||
@@ -31,8 +33,11 @@ void PE_SetDelegateIndex(int parts_no, int delegate_index);
|
||||
int PE_GetDelegateIndex(int parts_no);
|
||||
bool PE_SetPartsCG(int parts_no, struct string *cg_name, int sprite_deform, int state);
|
||||
bool PE_SetPartsCG_by_index(int parts_no, int cg_no, int sprite_deform, int state);
|
||||
bool PE_SetPartsCG_by_string_index(int parts_no, struct string *cg_no,
|
||||
int sprite_deform, int state);
|
||||
void PE_GetPartsCGName(int parts_no, struct string **cg_name, int state);
|
||||
bool PE_SetPartsCGSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
void PE_GetPartsCGSurfaceArea(int parts_no, int *x, int *y, int *w, int *h, int state);
|
||||
int PE_GetPartsCGNumber(int parts_no, int state);
|
||||
bool PE_SetLoopCG_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,
|
||||
@@ -40,12 +45,15 @@ bool PE_SetLoopCG(int parts_no, struct string *cg_name, int start_no, int nr_fra
|
||||
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, int numerator, int denominator, 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, int numerator, int denominator, 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,
|
||||
@@ -58,52 +66,80 @@ 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);
|
||||
bool PE_SetPartsRectangleDetectionSize(int PartsNumber, int Width, int Height, int State);
|
||||
bool PE_SetPartsFlash(int PartsNumber, struct string *pIFlashFileName, int State);
|
||||
bool PE_IsPartsFlashEnd(int PartsNumber, int State);
|
||||
int PE_GetPartsFlashCurrentFrameNumber(int PartsNumber, int State);
|
||||
bool PE_BackPartsFlashBeginFrame(int PartsNumber, int State);
|
||||
bool PE_StepPartsFlashFinalFrame(int PartsNumber, int State);
|
||||
void PE_ReleaseParts(int parts_no);
|
||||
void PE_ReleaseAllParts(void);
|
||||
void PE_ReleaseAllPartsWithoutSystem(void);
|
||||
void PE_ReleaseAllWithoutSystem(struct page **erase_number_list);
|
||||
void PE_SetPos(int parts_no, int x, int y);
|
||||
void PE_SetZ(int parts_no, int z);
|
||||
void PE_SetShow(int parts_no, bool show);
|
||||
void PE_SetAlpha(int parts_no, int alpha);
|
||||
void PE_SetPartsDrawFilter(int PartsNumber, int DrawFilter);
|
||||
void PE_SetAddColor(int parts_no, int r, int g, int b);
|
||||
void PE_SetMultiplyColor(int parts_no, int r, int g, int b);
|
||||
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);
|
||||
int PE_GetPartsHeight(int parts_no, int state);
|
||||
bool PE_GetPartsShow(int parts_no);
|
||||
int PE_GetPartsAlpha(int parts_no);
|
||||
void PE_SetPartsOriginPosMode(int parts_no, int origin_pos_mode);
|
||||
int PE_GetPartsOriginPosMode(int parts_no);
|
||||
void PE_SetParentPartsNumber(int parts_no, int parent_parts_no);
|
||||
bool PE_SetPartsGroupNumber(int PartsNumber, int GroupNumber);
|
||||
int PE_GetParentPartsNumber(int parts_no);
|
||||
bool PE_SetPartsGroupNumber(int parts_no, int group_no);
|
||||
void PE_SetPartsMessageWindowShowLink(int parts_no, bool message_window_show_link);
|
||||
bool PE_GetPartsMessageWindowShowLink(int PartsNumber);
|
||||
bool PE_GetPartsMessageWindowShowLink(int parts_no);
|
||||
void PE_SetSpeedupRateByMessageSkip(int parts_no, int rate);
|
||||
int PE_GetSpeedupRateByMessageSkip(int parts_no);
|
||||
void PE_SetPartsMagX(int parts_no, float scale_x);
|
||||
float PE_GetPartsMagX(int parts_no);
|
||||
void PE_SetPartsMagY(int parts_no, float scale_y);
|
||||
float PE_GetPartsMagY(int parts_no);
|
||||
void PE_SetPartsRotateX(int parts_no, float rot_x);
|
||||
void PE_SetPartsRotateY(int parts_no, float rot_y);
|
||||
void PE_SetPartsRotateZ(int parts_no, float rot_z);
|
||||
void PE_SetPartsAlphaClipperPartsNumber(int PartsNumber, int AlphaClipperPartsNumber);
|
||||
void PE_SetPartsPixelDecide(int PartsNumber, bool PixelDecide);
|
||||
bool PE_SetThumbnailReductionSize(int ReductionSize);
|
||||
float PE_GetPartsRotateZ(int parts_no);
|
||||
void PE_SetPartsAlphaClipperPartsNumber(int parts_no, int alpha_clipper_parts_no);
|
||||
void PE_SetPartsPixelDecide(int PartsNumber, bool pixel_decide);
|
||||
bool PE_SetThumbnailReductionSize(int reduction_size);
|
||||
bool PE_SetThumbnailMode(bool Mode);
|
||||
void PE_SetComponentType(int parts_no, int type, int state);
|
||||
int PE_GetComponentType(int parts_no, int state);
|
||||
void PE_SetInputState(int parts_no, int state);
|
||||
int PE_GetInputState(int parts_no);
|
||||
bool PE_SetPartsRectangleDetectionSize(int parts_no, int w, int h, int state);
|
||||
bool PE_SetPartsCGDetectionSize(int parts_no, struct string *cg_name, int state);
|
||||
bool PE_Save(struct page **buffer);
|
||||
bool PE_SaveWithoutHideParts(struct page **buffer);
|
||||
bool PE_Load(struct page **buffer);
|
||||
int PE_AddController(int index);
|
||||
void PE_RemoveController(struct page **erase_number_list, int index);
|
||||
bool PE_CreateParts3DLayerPluginID(int parts_no, int state);
|
||||
int PE_GetParts3DLayerPluginID(int parts_no, int state);
|
||||
bool PE_ReleaseParts3DLayerPluginID(int parts_no, int state);
|
||||
// GUIEngine
|
||||
int PE_GetFreeNumber(void);
|
||||
bool PE_IsExist(int parts_no);
|
||||
// PartsFunc interface
|
||||
void PE_set_active_controller(int controller_no);
|
||||
int PE_get_active_controller(void);
|
||||
int PE_get_system_controller(void);
|
||||
int PE_get_nr_controllers(void);
|
||||
void PE_parts_set_want_save(int parts_no, bool want_save);
|
||||
bool PE_save_thumbnail(struct string *filename, int reduction_factor);
|
||||
bool PE_init_parts_movie(int parts_no, int width, int height, int bg_r, int bg_g, int bg_b, int state);
|
||||
int PE_get_movie_sprite(int parts_no, int state);
|
||||
float PE_parts_get_absolute_x(int parts_no);
|
||||
float PE_parts_get_absolute_y(int parts_no);
|
||||
int PE_parts_get_absolute_z(int parts_no);
|
||||
void PE_parts_set_lock_input_state(int parts_no, bool lock);
|
||||
|
||||
// construction.c
|
||||
bool PE_AddCreateToPartsConstructionProcess(int parts_no, int w, int h, int state);
|
||||
@@ -114,10 +150,16 @@ bool PE_AddFillToPartsConstructionProcess(int parts_no, int x, int y, int w, int
|
||||
bool PE_AddFillAlphaColorToPartsConstructionProcess(int parts_no, int x, int y, int w, int h,
|
||||
int r, int g, int b, int a, int state);
|
||||
bool PE_AddFillAMapToPartsConstructionProcess(int parts_no, int x, int y, int w, int h, int a, int state);
|
||||
bool PE_AddFillWithAlphaToPartsConstructionProcess(int parts_no, int x, int y, int w, int h,
|
||||
int r, int g, int b, int a, int state);
|
||||
bool PE_AddDrawRectToPartsConstructionProcess(int parts_no, int x, int y, int w, int h,
|
||||
int r, int g, int b, int state);
|
||||
bool PE_AddDrawCutCGToPartsConstructionProcess(int parts_no, struct string *cg_name,
|
||||
int dx, int dy, int dw, int dh, int sx, int sy, int sw, int sh, int interp_type, int state);
|
||||
bool PE_AddCopyCutCGToPartsConstructionProcess(int parts_no, struct string *cg_name,
|
||||
int dx, int dy, int dw, int dh, int sx, int sy, int sw, int sh, int interp_type, int state);
|
||||
bool PE_AddGrayFilterToPartsConstructionProcess(int parts_no, int x, int y, int w, int h,
|
||||
bool full_size, int state);
|
||||
bool PE_AddCopyTextToPartsConstructionProcess(int parts_no, int x, int y, struct string *text,
|
||||
int type, int size, int r, int g, int b, float bold_weight,
|
||||
int edge_r, int edge_g, int edge_b, float edge_weight,
|
||||
@@ -127,15 +169,20 @@ bool PE_AddDrawTextToPartsConstructionProcess(int parts_no, int x, int y, struct
|
||||
int edge_r, int edge_g, int edge_b, float edge_weight,
|
||||
int char_space, int line_space, int state);
|
||||
bool PE_BuildPartsConstructionProcess(int parts_no, int state);
|
||||
bool PE_ClearPartsConstructionProcess(int parts_no, int state);
|
||||
bool PE_SetPartsConstructionSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
|
||||
// input.c
|
||||
void PE_UpdateInputState(int passed_time);
|
||||
void PE_SetPassCursor(int parts_no, bool pass);
|
||||
bool PE_GetPartsPassCursor(int parts_no);
|
||||
void PE_SetClickable(int parts_no, bool clickable);
|
||||
bool PE_GetPartsClickable(int parts_no);
|
||||
void PE_SetPartsGroupDecideOnCursor(int GroupNumber, bool DecideOnCursor);
|
||||
void PE_SetPartsGroupDecideClick(int GroupNumber, bool DecideClick);
|
||||
void PE_SetDrag(int parts_no, bool enable);
|
||||
void PE_SetPartsGroupDecideOnCursor(int group_no, bool decide_on_cursor);
|
||||
void PE_SetPartsGroupDecideClick(int group_no, bool decide_click);
|
||||
void PE_SetOnCursorShowLinkPartsNumber(int parts_no, int link_parts_no);
|
||||
int PE_GetOnCursorShowLinkPartsNumber(int parts_no);
|
||||
bool PE_SetPartsOnCursorSoundNumber(int parts_no, int sound_no);
|
||||
bool PE_SetPartsClickSoundNumber(int parts_no, int sound_no);
|
||||
bool PE_SetClickMissSoundNumber(int sound_no);
|
||||
@@ -144,6 +191,19 @@ void PE_EndInput(void);
|
||||
int PE_GetClickPartsNumber(void);
|
||||
bool PE_IsCursorIn(int parts_no, int mouse_x, int mouse_y, int state);
|
||||
|
||||
// message.c
|
||||
void PE_ReleaseMessage(void);
|
||||
void PE_PopMessage(void);
|
||||
int PE_GetMessageType(void);
|
||||
int PE_GetMessagePartsNumber(void);
|
||||
int PE_GetMessageDelegateIndex(void);
|
||||
int PE_GetMessageVariableCount(void);
|
||||
int PE_GetMessageVariableType(int index);
|
||||
int PE_GetMessageVariableInt(int index);
|
||||
float PE_GetMessageVariableFloat(int index);
|
||||
bool PE_GetMessageVariableBool(int index);
|
||||
void PE_GetMessageVariableString(int index, struct string **out);
|
||||
|
||||
// motion.c
|
||||
void PE_AddMotionPos(int parts_no, int begin_x, int begin_y, int end_x, int end_y, int begin_t, int end_t);
|
||||
void PE_AddMotionPos_curve(int parts_no, int begin_x, int begin_y, int end_x, int end_y,
|
||||
@@ -152,27 +212,49 @@ void PE_AddMotionAlpha(int parts_no, int begin_a, int end_a, int begin_t, int en
|
||||
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, int begin_numerator, int begin_denominator,
|
||||
int end_numerator, int end_denominator, int begin_t, int end_t);
|
||||
void PE_AddMotionVGaugeRate(int parts_no, int begin_numerator, int begin_denominator,
|
||||
int end_numerator, int end_denominator, int begin_t, int end_t);
|
||||
void PE_AddMotionHGaugeRate(int parts_no, float begin_numerator, float begin_denominator,
|
||||
float end_numerator, float end_denominator, int begin_t, int end_t);
|
||||
void PE_AddMotionHGaugeRate_curve(int parts_no, float begin_numerator, float begin_denominator,
|
||||
float end_numerator, float end_denominator, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionVGaugeRate(int parts_no, float begin_numerator, float begin_denominator,
|
||||
float end_numerator, float end_denominator, int begin_t, int end_t);
|
||||
void PE_AddMotionVGaugeRate_curve(int parts_no, float begin_numerator, float begin_denominator,
|
||||
float end_numerator, float end_denominator, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionNumeralNumber(int parts_no, int begin_n, int end_n, int begin_t, int end_t);
|
||||
void PE_AddMotionNumeralNumber_curve(int parts_no, int begin_n, int end_n, int begin_t,
|
||||
int end_t, struct string *curve_name);
|
||||
void PE_AddMotionMagX(int parts_no, float begin, float end, int begin_t, int end_t);
|
||||
void PE_AddMotionMagX_curve(int parts_no, float begin, float end, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionMagY(int parts_no, float begin, float end, int begin_t, int end_t);
|
||||
void PE_AddMotionMagY_curve(int parts_no, float begin, float end, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionRotateX(int parts_no, float begin, float end, int begin_t, int end_t);
|
||||
void PE_AddMotionRotateX_curve(int parts_no, float begin, float end, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionRotateY(int parts_no, float begin, float end, int begin_t, int end_t);
|
||||
void PE_AddMotionRotateY_curve(int parts_no, float begin, float end, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionRotateZ(int parts_no, float begin, float end, int begin_t, int end_t);
|
||||
void PE_AddMotionRotateZ_curve(int parts_no, float begin, float end, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionVibrationSize(int parts_no, int begin_w, int begin_h, int begin_t, int end_t);
|
||||
void PE_AddMotionSound(int sound_no, int begin_t);
|
||||
void PE_AddWholeMotionVibrationSize(int begin_w, int begin_h, int begin_t, int end_t);
|
||||
void PE_BeginMotion(void);
|
||||
void PE_EndMotion(void);
|
||||
void PE_SetMotionTime(int t);
|
||||
void PE_SeekEndMotion(void);
|
||||
void PE_UpdateMotionTime(int time, bool skip);
|
||||
bool PE_IsMotion(void);
|
||||
int PE_GetMotionEndTime(void);
|
||||
void PE_PauseMotion(bool pause);
|
||||
|
||||
// text.c
|
||||
bool PE_SetText(int parts_no, struct string *text, int state);
|
||||
struct string *PE_GetText(int parts_no, int state);
|
||||
bool PE_AddPartsText(int parts_no, struct string *text, int state);
|
||||
bool PE_SetPartsTextSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
bool PE_SetFont(int parts_no, int type, int size, int r, int g, int b, float bold_weight, int edge_r, int edge_g, int edge_b, float edge_weight, int state);
|
||||
@@ -185,4 +267,50 @@ bool PE_SetPartsFontEdgeWeight(int parts_no, float edge_weight, int state);
|
||||
bool PE_SetTextCharSpace(int parts_no, int char_space, int state);
|
||||
bool PE_SetTextLineSpace(int parts_no, int line_space, int state);
|
||||
|
||||
// flash.c
|
||||
bool PE_ExistsFlashFile(struct string *flash_filename);
|
||||
bool PE_SetPartsFlash(int parts_no, struct string *flash_filename, int state);
|
||||
bool PE_IsPartsFlashEnd(int parts_no, int state);
|
||||
int PE_GetPartsFlashCurrentFrameNumber(int parts_no, int state);
|
||||
bool PE_BackPartsFlashBeginFrame(int parts_no, int state);
|
||||
bool PE_StepPartsFlashFinalFrame(int parts_no, int state);
|
||||
bool PE_SetPartsFlashAndStop(int parts_no, struct string *flash_filename, int state);
|
||||
bool PE_StopPartsFlash(int parts_no, int state);
|
||||
bool PE_StartPartsFlash(int parts_no, int state);
|
||||
bool PE_GoFramePartsFlash(int parts_no, int frame_no, int state);
|
||||
int PE_GetPartsFlashEndFrame(int parts_no, int state);
|
||||
|
||||
// flat.c
|
||||
bool PE_ExistsFlatFile(struct string *filename);
|
||||
bool PE_SetPartsFlat(int parts_no, struct string *filename, int state);
|
||||
bool PE_IsPartsFlatEnd(int parts_no, int state);
|
||||
int PE_GetPartsFlatCurrentFrameNumber(int parts_no, int state);
|
||||
bool PE_BackPartsFlatBeginFrame(int parts_no, int state);
|
||||
bool PE_StepPartsFlatFinalFrame(int parts_no, int state);
|
||||
bool PE_SetPartsFlatSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
bool PE_SetPartsFlatAndStop(int parts_no, struct string *filename, int state);
|
||||
bool PE_StopPartsFlat(int parts_no, int state);
|
||||
bool PE_StartPartsFlat(int parts_no, int state);
|
||||
bool PE_GoFramePartsFlat(int parts_no, int frame_no, int state);
|
||||
int PE_GetPartsFlatEndFrame(int parts_no, int state);
|
||||
|
||||
// layoutbox.c
|
||||
void PE_SetLayoutBoxLayoutType(int parts_no, int type);
|
||||
int PE_GetLayoutBoxLayoutType(int parts_no);
|
||||
void PE_SetLayoutBoxReturn(int parts_no, bool return_flag, int return_size);
|
||||
bool PE_IsLayoutBoxReturn(int parts_no);
|
||||
int PE_GetLayoutBoxReturnSize(int parts_no);
|
||||
void PE_SetLayoutBoxAlign(int parts_no, int align);
|
||||
int PE_GetLayoutBoxAlign(int parts_no);
|
||||
void PE_SetComponentMargin(int parts_no, int top, int bottom, int left, int right);
|
||||
int PE_GetComponentMarginTop(int parts_no);
|
||||
int PE_GetComponentMarginBottom(int parts_no);
|
||||
int PE_GetComponentMarginLeft(int parts_no);
|
||||
int PE_GetComponentMarginRight(int parts_no);
|
||||
void PE_set_layoutbox_padding(int parts_no, int top, int bottom, int left, int right);
|
||||
int PE_get_layoutbox_padding_top(int parts_no);
|
||||
int PE_get_layoutbox_padding_bottom(int parts_no);
|
||||
int PE_get_layoutbox_padding_left(int parts_no);
|
||||
int PE_get_layoutbox_padding_right(int parts_no);
|
||||
|
||||
#endif /* SYSTEM4_PARTS_H */
|
||||
|
||||
+106
-10
@@ -1669,6 +1669,9 @@ uint16_t plm_buffer_read_vlc_uint(plm_buffer_t *self, const plm_vlc_uint_t *tabl
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// plm_demux implementation
|
||||
//
|
||||
// xsystem4 change: The demuxer accepts MPEG-1 and MPEG-2 Systems pack/PES headers.
|
||||
// The video and audio decoders still only support MPEG-1 Video and MP2 Audio.
|
||||
|
||||
static const int PLM_START_PACK = 0xBA;
|
||||
static const int PLM_START_END = 0xB9;
|
||||
@@ -1685,6 +1688,7 @@ struct plm_demux_t {
|
||||
double duration;
|
||||
|
||||
int start_code;
|
||||
int is_mpeg2;
|
||||
int has_pack_header;
|
||||
int has_system_header;
|
||||
int has_headers;
|
||||
@@ -1738,19 +1742,52 @@ int plm_demux_has_headers(plm_demux_t *self) {
|
||||
}
|
||||
|
||||
self->start_code = PLM_START_PACK;
|
||||
if (!plm_buffer_has(self->buffer, 64)) {
|
||||
return FALSE;
|
||||
}
|
||||
self->start_code = -1;
|
||||
|
||||
if (plm_buffer_read(self->buffer, 4) != 0x02) {
|
||||
if (!plm_buffer_has(self->buffer, 8)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
self->system_clock_ref = plm_demux_decode_time(self);
|
||||
plm_buffer_skip(self->buffer, 1);
|
||||
plm_buffer_skip(self->buffer, 22); // mux_rate * 50
|
||||
plm_buffer_skip(self->buffer, 1);
|
||||
size_t byte_index = self->buffer->bit_index >> 3;
|
||||
int marker = self->buffer->bytes[byte_index];
|
||||
if ((marker & 0xf0) == 0x20) {
|
||||
if (!plm_buffer_has(self->buffer, 64)) {
|
||||
return FALSE;
|
||||
}
|
||||
self->start_code = -1;
|
||||
|
||||
plm_buffer_skip(self->buffer, 4); // MPEG-1 marker
|
||||
self->system_clock_ref = plm_demux_decode_time(self);
|
||||
plm_buffer_skip(self->buffer, 1);
|
||||
plm_buffer_skip(self->buffer, 22); // mux_rate * 50
|
||||
plm_buffer_skip(self->buffer, 1);
|
||||
}
|
||||
else if ((marker & 0xc0) == 0x40) {
|
||||
// MPEG-2 pack headers have a fixed 10-byte part followed by up to
|
||||
// seven stuffing bytes. Read the stuffing length before consuming
|
||||
// anything so that a partial input can be retried.
|
||||
if (!plm_buffer_has(self->buffer, 80)) {
|
||||
return FALSE;
|
||||
}
|
||||
byte_index = self->buffer->bit_index >> 3;
|
||||
int stuffing_length = self->buffer->bytes[byte_index + 9] & 0x07;
|
||||
if (!plm_buffer_has(self->buffer, (10 + stuffing_length) << 3)) {
|
||||
return FALSE;
|
||||
}
|
||||
self->start_code = -1;
|
||||
self->is_mpeg2 = TRUE;
|
||||
|
||||
plm_buffer_skip(self->buffer, 2); // MPEG-2 marker
|
||||
self->system_clock_ref = plm_demux_decode_time(self);
|
||||
plm_buffer_skip(self->buffer, 9); // SCR extension
|
||||
plm_buffer_skip(self->buffer, 1);
|
||||
plm_buffer_skip(self->buffer, 22); // program mux rate
|
||||
plm_buffer_skip(self->buffer, 2); // marker bits
|
||||
plm_buffer_skip(self->buffer, 5); // reserved
|
||||
plm_buffer_skip(self->buffer, 3); // stuffing length
|
||||
plm_buffer_skip(self->buffer, stuffing_length << 3);
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
self->has_pack_header = TRUE;
|
||||
}
|
||||
@@ -2085,6 +2122,65 @@ plm_packet_t *plm_demux_decode_packet(plm_demux_t *self, int type) {
|
||||
|
||||
self->next_packet.type = type;
|
||||
self->next_packet.length = plm_buffer_read(self->buffer, 16);
|
||||
self->next_packet.pts = PLM_PACKET_INVALID_TS;
|
||||
|
||||
if (self->is_mpeg2) {
|
||||
// MPEG-2 PES packets have three fixed header bytes followed by a
|
||||
// variable-length optional header. This decoder only needs PTS; all
|
||||
// other optional fields can be skipped using the header length.
|
||||
if (self->next_packet.length < 3 ||
|
||||
plm_buffer_read(self->buffer, 2) != 0x02
|
||||
) {
|
||||
self->next_packet.length = 0;
|
||||
return NULL;
|
||||
}
|
||||
plm_buffer_skip(self->buffer, 6); // scrambling and control flags
|
||||
int pts_dts_marker = plm_buffer_read(self->buffer, 2);
|
||||
plm_buffer_skip(self->buffer, 6); // remaining optional-field flags
|
||||
int header_length = plm_buffer_read(self->buffer, 8);
|
||||
if (
|
||||
self->next_packet.length < (size_t)(3 + header_length) ||
|
||||
!plm_buffer_has(self->buffer, header_length << 3)
|
||||
) {
|
||||
self->next_packet.length = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int header_bytes_read = 0;
|
||||
if (pts_dts_marker == 0x02) {
|
||||
if (header_length < 5 || plm_buffer_read(self->buffer, 4) != 0x02) {
|
||||
self->next_packet.length = 0;
|
||||
return NULL;
|
||||
}
|
||||
self->next_packet.pts = plm_demux_decode_time(self);
|
||||
header_bytes_read = 5;
|
||||
}
|
||||
else if (pts_dts_marker == 0x03) {
|
||||
if (header_length < 10 || plm_buffer_read(self->buffer, 4) != 0x03) {
|
||||
self->next_packet.length = 0;
|
||||
return NULL;
|
||||
}
|
||||
self->next_packet.pts = plm_demux_decode_time(self);
|
||||
if (plm_buffer_read(self->buffer, 4) != 0x01) {
|
||||
self->next_packet.length = 0;
|
||||
return NULL;
|
||||
}
|
||||
plm_demux_decode_time(self); // DTS
|
||||
header_bytes_read = 10;
|
||||
}
|
||||
else if (pts_dts_marker != 0x00) {
|
||||
self->next_packet.length = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (self->next_packet.pts != PLM_PACKET_INVALID_TS) {
|
||||
self->last_decoded_pts = self->next_packet.pts;
|
||||
}
|
||||
plm_buffer_skip(self->buffer, (header_length - header_bytes_read) << 3);
|
||||
self->next_packet.length -= 3 + header_length;
|
||||
return plm_demux_get_packet(self);
|
||||
}
|
||||
|
||||
self->next_packet.length -= plm_buffer_skip_bytes(self->buffer, 0xff); // stuffing
|
||||
|
||||
// skip P-STD
|
||||
|
||||
+6
-1
@@ -17,12 +17,17 @@
|
||||
#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 (*debug_print)(struct sact_sprite *, int indent);
|
||||
void (*render)(struct sact_sprite *);
|
||||
cJSON *(*to_json)(struct sact_sprite *, bool);
|
||||
};
|
||||
|
||||
#endif /* SYSTEM4_PLUGIN_H */
|
||||
|
||||
+114
-5
@@ -19,13 +19,24 @@
|
||||
|
||||
#define RE_NR_BACK_CGS 16
|
||||
#define RE_NR_INSTANCE_TARGETS 2
|
||||
#define RE_NR_LIGHT_PARAMS 41 // .lit parameter count
|
||||
|
||||
#include <cglm/types.h>
|
||||
|
||||
#include "gfx/gfx.h"
|
||||
#include "plugin.h"
|
||||
|
||||
typedef struct cJSON cJSON;
|
||||
struct hash_table;
|
||||
struct billboard_texture;
|
||||
struct iarray_reader;
|
||||
struct iarray_writer;
|
||||
|
||||
enum RE_plugin_version {
|
||||
RE_REIGN_PLUGIN, // Toushin Toshi 3
|
||||
RE_TAPIR_PLUGIN, // Rance Quest
|
||||
RE_SEAL_PLUGIN, // Rance 9
|
||||
};
|
||||
|
||||
enum RE_instance_type {
|
||||
RE_ITYPE_UNINITIALIZED = 0,
|
||||
@@ -35,6 +46,7 @@ enum RE_instance_type {
|
||||
RE_ITYPE_DIRECTIONAL_LIGHT = 4,
|
||||
RE_ITYPE_SPECULAR_LIGHT = 6,
|
||||
RE_ITYPE_PARTICLE_EFFECT = 8,
|
||||
RE_ITYPE_PATH_LINE = 9,
|
||||
};
|
||||
|
||||
enum RE_motion_state {
|
||||
@@ -49,12 +61,41 @@ enum RE_fog_type {
|
||||
RE_FOG_LIGHT_SCATTERING = 2,
|
||||
};
|
||||
|
||||
// Indices into light_params. Vector entries name their first component.
|
||||
enum seal_light_param {
|
||||
SEAL_LP_TONEMAP_EXPOSURE_BIAS = 0,
|
||||
SEAL_LP_TONEMAP_WHITE_POINT = 1,
|
||||
SEAL_LP_TONEMAP_A = 2,
|
||||
SEAL_LP_TONEMAP_B = 3,
|
||||
SEAL_LP_TONEMAP_C = 4,
|
||||
SEAL_LP_TONEMAP_D = 5,
|
||||
SEAL_LP_TONEMAP_E = 6,
|
||||
SEAL_LP_TONEMAP_F = 7,
|
||||
SEAL_LP_HEMI_VEC = 8, // x,y,z
|
||||
SEAL_LP_HEMI_SKY_COLOR = 11, // r,g,b
|
||||
SEAL_LP_HEMI_MID_COLOR = 14, // r,g,b
|
||||
SEAL_LP_HEMI_GROUND_COLOR = 17, // r,g,b
|
||||
SEAL_LP_LS_BETA_R = 20,
|
||||
SEAL_LP_LS_BETA_M = 21,
|
||||
SEAL_LP_LS_G = 22,
|
||||
SEAL_LP_LS_DISTANCE = 23,
|
||||
SEAL_LP_LS_LIGHT_VEC = 24, // x,y,z
|
||||
SEAL_LP_LS_LIGHT_COLOR = 27, // r,g,b
|
||||
SEAL_LP_LS_SUN_COLOR = 30, // r,g,b
|
||||
};
|
||||
|
||||
enum RE_draw_type {
|
||||
RE_DRAW_TYPE_NORMAL = 0,
|
||||
RE_DRAW_TYPE_ADDITIVE = 1,
|
||||
RE_DRAW_TYPE_MAX = RE_DRAW_TYPE_ADDITIVE
|
||||
};
|
||||
|
||||
enum RE_draw_options {
|
||||
RE_DRAW_OPTION_EDGE = 0,
|
||||
RE_DRAW_OPTION_LIGHTING = 1,
|
||||
RE_DRAW_OPTION_MAX
|
||||
};
|
||||
|
||||
struct RE_options {
|
||||
int anti_aliasing;
|
||||
int wait_vsync;
|
||||
@@ -65,6 +106,12 @@ struct RE_camera {
|
||||
vec3 pos;
|
||||
float pitch, roll, yaw; // in degrees
|
||||
float quake_pitch, quake_yaw;
|
||||
// Per-frame full override from an 3de "camera" object. Cleared at the
|
||||
// start of every RE_build_model tick and re-asserted by s3de_effect_update
|
||||
// if any active camera object selects this frame.
|
||||
bool override_active;
|
||||
vec3 override_pos;
|
||||
float override_pitch, override_yaw, override_roll;
|
||||
};
|
||||
|
||||
struct RE_back_cg {
|
||||
@@ -80,15 +127,17 @@ struct RE_back_cg {
|
||||
|
||||
struct RE_plugin {
|
||||
struct draw_plugin plugin;
|
||||
enum RE_plugin_version version;
|
||||
int sprite;
|
||||
int nr_instances;
|
||||
struct RE_instance **instances;
|
||||
struct archive *aar;
|
||||
struct hash_table *model_cache;
|
||||
struct hash_table *pae_cache;
|
||||
struct hash_table *effect_cache;
|
||||
struct RE_renderer *renderer;
|
||||
struct RE_camera camera;
|
||||
mat4 proj_transform;
|
||||
int viewport_x, viewport_y, viewport_width, viewport_height;
|
||||
|
||||
struct RE_back_cg back_cg[RE_NR_BACK_CGS];
|
||||
|
||||
@@ -126,30 +175,47 @@ struct RE_plugin {
|
||||
float post_effect_filter_y;
|
||||
float post_effect_filter_cb;
|
||||
float post_effect_filter_cr;
|
||||
int draw_options[RE_DRAW_OPTION_MAX];
|
||||
|
||||
// TapirEngine
|
||||
bool suspended;
|
||||
|
||||
// SealEngine
|
||||
int mag_speed;
|
||||
float light_params[RE_NR_LIGHT_PARAMS];
|
||||
float edge_length;
|
||||
float edge_reduction_rate;
|
||||
vec3 edge_color;
|
||||
};
|
||||
|
||||
struct RE_instance {
|
||||
struct RE_plugin *plugin;
|
||||
struct model *model;
|
||||
struct particle_effect *effect;
|
||||
struct s3de_effect *s3de_effect;
|
||||
struct motion *motion;
|
||||
struct motion *next_motion;
|
||||
struct height_detector *height_detector;
|
||||
|
||||
enum RE_instance_type type;
|
||||
struct string *name;
|
||||
int target[RE_NR_INSTANCE_TARGETS];
|
||||
vec3 pos;
|
||||
float pitch, roll, yaw; // in degrees
|
||||
vec3 scale;
|
||||
float alpha;
|
||||
float grayscale_rate;
|
||||
bool draw;
|
||||
bool draw_edge;
|
||||
bool draw_shadow;
|
||||
bool make_shadow;
|
||||
float shadow_volume_bone_radius;
|
||||
bool draw_bump;
|
||||
float fps;
|
||||
bool use_mag_speed;
|
||||
bool motion_blend;
|
||||
float motion_blend_rate;
|
||||
vec3 diffuse; // color multiplier for meshes, light color for lights
|
||||
vec3 ambient;
|
||||
vec3 column_pos;
|
||||
float column_height;
|
||||
@@ -158,41 +224,69 @@ struct RE_instance {
|
||||
|
||||
// Billboards
|
||||
enum RE_draw_type draw_type;
|
||||
vec3 vertex_pos[4];
|
||||
vec2 vertex_uv[4];
|
||||
struct billboard_texture **billboard_frames;
|
||||
int nr_billboard_frames;
|
||||
|
||||
// Lights
|
||||
vec3 vec;
|
||||
vec3 diffuse;
|
||||
vec3 globe_diffuse;
|
||||
float *light_params;
|
||||
|
||||
// Private
|
||||
bool local_transform_needs_update;
|
||||
mat4 local_transform;
|
||||
mat3 normal_transform;
|
||||
mat4 *bone_transforms; // model->nr_bones elements
|
||||
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(void);
|
||||
#define RE_MAX_PLUGINS 2
|
||||
struct RE_plugin *RE_get_plugin(int handle);
|
||||
|
||||
struct RE_plugin *RE_plugin_new(enum RE_plugin_version version);
|
||||
void RE_plugin_free(struct RE_plugin *plugin);
|
||||
bool RE_plugin_bind(struct RE_plugin *plugin, int sprite);
|
||||
bool RE_plugin_unbind(struct RE_plugin *plugin);
|
||||
bool RE_plugin_suspend(struct RE_plugin *plugin);
|
||||
bool RE_plugin_resume(struct RE_plugin *plugin);
|
||||
bool RE_build_model(struct RE_plugin *plugin, int elapsed_ms);
|
||||
void RE_update_model(struct RE_plugin *plugin);
|
||||
bool RE_set_viewport(struct RE_plugin *plugin, int x, int y, int width, int height);
|
||||
bool RE_set_projection(struct RE_plugin *plugin, float width, float height, float near, float far, float deg);
|
||||
int RE_create_instance(struct RE_plugin *plugin);
|
||||
bool RE_release_instance(struct RE_plugin *plugin, int instance);
|
||||
|
||||
bool RE_instance_set_type(struct RE_instance *instance, int type);
|
||||
bool RE_instance_data_exists(struct RE_instance *instance, const char *name);
|
||||
bool RE_instance_load(struct RE_instance *instance, const char *name);
|
||||
bool RE_instance_motion_exists(struct RE_instance *instance, const char *name);
|
||||
bool RE_instance_load_motion(struct RE_instance *instance, const char *name);
|
||||
bool RE_instance_load_next_motion(struct RE_instance *instance, const char *name);
|
||||
bool RE_instance_free_next_motion(struct RE_instance *instance);
|
||||
bool RE_instance_set_mesh_show(struct RE_instance *instance, const char *mesh_name, bool show);
|
||||
bool RE_instance_set_vertex_pos(struct RE_instance *instance, int index, float x, float y, float z);
|
||||
bool RE_instance_set_vertex_uv(struct RE_instance *instance, int index, float u, float v);
|
||||
int RE_instance_get_bone_index(struct RE_instance *instance, const char *name);
|
||||
bool RE_instance_trans_local_pos_to_world_pos_by_bone(struct RE_instance *instance, int bone, vec3 offset, vec3 out);
|
||||
float RE_instance_calc_height(struct RE_instance *instance, float x, float z);
|
||||
float RE_instance_calc_2d_detection_height(struct RE_instance *instance, float x, float z);
|
||||
bool RE_instance_calc_2d_detection(struct RE_instance *instance, float x0, float y0, float z0, float x1, float y1, float z1, float *x2, float *y2, float *z2, float radius);
|
||||
bool RE_instance_set_debug_draw_shadow_volume(struct RE_instance *instance, bool draw);
|
||||
bool RE_instance_find_path(struct RE_instance *instance, vec3 start, vec3 goal);
|
||||
const vec3 *RE_instance_get_path_line(struct RE_instance *instance, int *nr_path_points);
|
||||
bool RE_instance_optimize_path_line(struct RE_instance *instance);
|
||||
bool RE_instance_calc_path_finder_intersect_eye_vec(struct RE_instance *instance, int mouse_x, int mouse_y, vec3 out);
|
||||
bool RE_plugin_transform_pos_to_view_pos(struct RE_plugin *plugin, float x, float y, float z, int *view_x, int *view_y);
|
||||
bool RE_plugin_get_camera_z_vector(struct RE_plugin *plugin, vec3 out);
|
||||
void RE_plugin_reset_light_param(struct RE_plugin *plugin);
|
||||
bool RE_plugin_set_fog_type(struct RE_plugin *plugin, int type);
|
||||
int RE_plugin_get_fog_type(struct RE_plugin *plugin);
|
||||
|
||||
int RE_motion_get_state(struct motion *motion);
|
||||
bool RE_motion_set_state(struct motion *motion, int state);
|
||||
@@ -270,6 +364,21 @@ 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);
|
||||
void RE_debug_print(struct sact_sprite *sp, int indent);
|
||||
cJSON *RE_to_json(struct sact_sprite *sp, bool verbose);
|
||||
|
||||
// save.c
|
||||
void RE_plugin_serialize(struct RE_plugin *plugin, struct iarray_writer *w);
|
||||
void RE_plugin_deserialize(struct RE_plugin *plugin, struct iarray_reader *r, int version);
|
||||
|
||||
// Exposed for PartsEngine 3DLayer
|
||||
int ReignEngine_create_plugin(enum RE_plugin_version version);
|
||||
bool ReignEngine_ReleasePlugin(int handle);
|
||||
bool ReignEngine_BindPlugin(int handle, int sprite);
|
||||
|
||||
// Exposed for MarmotModelEngine
|
||||
void ReignEngine_update_models(void);
|
||||
|
||||
// debug.c
|
||||
void re_debug_init(void);
|
||||
|
||||
#endif /* SYSTEM4_REIGN_H */
|
||||
|
||||
+23
-2
@@ -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);
|
||||
@@ -42,6 +50,7 @@ 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);
|
||||
@@ -50,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);
|
||||
@@ -82,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);
|
||||
@@ -92,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);
|
||||
@@ -100,8 +112,14 @@ void sact_CG_BlendAMapBin(int dst, int dx, int dy, int src, int sx, int sy, int
|
||||
int sact_TRANS_Begin(int type);
|
||||
int sact_TRANS_Update(float rate);
|
||||
int sact_TRANS_End(void);
|
||||
bool sact_VIEW_SetMode(int mode);
|
||||
int sact_VIEW_GetMode(void);
|
||||
bool sact_DX_GetUsePower2Texture(void);
|
||||
void sact_DX_SetUsePower2Texture(bool use);
|
||||
|
||||
extern struct text_style text_sprite_ts;
|
||||
int StoatSpriteEngine_SP_SetDrawMethod(int sp_no, int method);
|
||||
int StoatSpriteEngine_SP_GetDrawMethod(int sp_no);
|
||||
bool StoatSpriteEngine_SP_SetTextSprite(int sp_no, struct string *text);
|
||||
void StoatSpriteEngine_SP_SetTextSpriteType(int type);
|
||||
void StoatSpriteEngine_SP_SetTextSpriteSize(int size);
|
||||
@@ -110,6 +128,9 @@ void StoatSpriteEngine_SP_SetTextSpriteBoldWeight(float weight);
|
||||
void StoatSpriteEngine_SP_SetTextSpriteEdgeWeight(float weight);
|
||||
void StoatSpriteEngine_SP_SetTextSpriteEdgeColor(int r, int g, int b);
|
||||
bool StoatSpriteEngine_SP_SetDashTextSprite(int sp_no, int width, int height);
|
||||
void StoatSpriteEngine_FPS_SetShow(bool show);
|
||||
bool StoatSpriteEngine_FPS_GetShow(void);
|
||||
float StoatSpriteEngine_FPS_Get(void);
|
||||
//int StoatSpriteEngine_SP_SetBrightness(int sp_no, int brightness);
|
||||
//int StoatSpriteEngine_SP_GetBrightness(int sp_no);
|
||||
int StoatSpriteEngine_SYSTEM_IsResetOnce(void);
|
||||
|
||||
+2
-5
@@ -20,14 +20,11 @@
|
||||
#include "cJSON.h"
|
||||
#include "vm.h"
|
||||
|
||||
cJSON *page_to_json(struct page *page);
|
||||
cJSON *vm_value_to_json(enum ain_data_type type, union vm_value val);
|
||||
void json_load_page(struct page *page, cJSON *vars, bool call_dtors);
|
||||
union vm_value json_to_vm_value(enum ain_data_type type, enum ain_data_type struct_type, int array_rank, cJSON *json);
|
||||
|
||||
int save_json(const char *filename, cJSON *json);
|
||||
int save_globals(const char *keyname, const char *filename);
|
||||
int save_group(const char *keyname, const char *filename, const char *group_name, int *n);
|
||||
cJSON *load_json(const char *filename);
|
||||
int save_globals(const char *keyname, const char *filename, const char *group_name, int *n);
|
||||
int load_globals(const char *keyname, const char *filename, const char *group_name, int *n);
|
||||
int delete_save_file(const char *filename);
|
||||
|
||||
|
||||
+6
-1
@@ -20,6 +20,7 @@
|
||||
#include <stdbool.h>
|
||||
#include "queue.h"
|
||||
|
||||
typedef struct cJSON cJSON;
|
||||
struct texture;
|
||||
|
||||
struct sprite {
|
||||
@@ -37,10 +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
|
||||
void (*debug_print)(struct sprite*);
|
||||
cJSON *(*to_json)(struct sprite*, bool);
|
||||
};
|
||||
|
||||
extern bool scene_is_dirty;
|
||||
@@ -55,6 +58,8 @@ 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)
|
||||
{
|
||||
|
||||
+29
-4
@@ -35,8 +35,16 @@ struct sact_sprite {
|
||||
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 {
|
||||
@@ -46,15 +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);
|
||||
@@ -69,14 +91,19 @@ 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);
|
||||
@@ -110,6 +137,4 @@ void sprite_get_pixel_value(struct sact_sprite *sp, int x, int y, int *r, int *g
|
||||
void sprite_bind_plugin(struct sact_sprite *sp, struct draw_plugin *plugin);
|
||||
void sprite_call_plugins(void);
|
||||
|
||||
void sprite_print(struct sact_sprite *sp);
|
||||
|
||||
#endif /* SYSTEM4_SPRITE_H */
|
||||
|
||||
+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 */
|
||||
+25
-2
@@ -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;
|
||||
@@ -106,6 +111,7 @@ 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);
|
||||
bool library_exists(int libno);
|
||||
@@ -113,17 +119,34 @@ bool library_function_exists(int libno, int fno);
|
||||
void init_libraries(void);
|
||||
void exit_libraries(void);
|
||||
|
||||
struct string *string_format(struct string *fmt, union vm_value arg, enum ain_data_type type);
|
||||
// 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 "little_endian.h"
|
||||
#include "system4/little_endian.h"
|
||||
#include "system4/ain.h"
|
||||
|
||||
struct function_call {
|
||||
|
||||
+4
-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;
|
||||
@@ -60,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);
|
||||
@@ -75,17 +78,9 @@ int32_t heap_alloc_string(struct string *s);
|
||||
|
||||
void heap_describe_slot(int slot);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
#ifdef VM_PRIVATE
|
||||
|
||||
extern uint32_t heap_next_seq;
|
||||
extern int32_t *heap_free_stack;
|
||||
extern size_t heap_free_ptr;
|
||||
|
||||
|
||||
+15
-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);
|
||||
|
||||
@@ -139,6 +145,6 @@ 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 */
|
||||
|
||||
+22
-3
@@ -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;
|
||||
@@ -43,6 +50,8 @@ struct config {
|
||||
bool echo;
|
||||
float text_x_scale;
|
||||
bool manual_text_x_scale;
|
||||
enum resume_save_format save_format;
|
||||
int msgskip_delay;
|
||||
};
|
||||
|
||||
extern struct config config;
|
||||
@@ -56,15 +65,24 @@ const char *display_utf0(const char *utf);
|
||||
const char *display_utf1(const char *utf);
|
||||
const char *display_utf2(const char *utf);
|
||||
|
||||
void indent_printf(int indent, const char *fmt, ...);
|
||||
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
|
||||
@@ -75,7 +93,8 @@ extern bool game_daibanchou_en;
|
||||
extern bool game_rance02_mg;
|
||||
extern bool game_rance6_mg;
|
||||
extern bool game_rance7_mg;
|
||||
|
||||
extern bool id_indexed_afa;
|
||||
extern bool game_rance8;
|
||||
extern bool game_rance8_mg;
|
||||
extern bool game_dungeons_and_dolls;
|
||||
|
||||
#endif /* XSYSTEM4_H */
|
||||
|
||||
+26
-11
@@ -1,18 +1,33 @@
|
||||
project('system4', 'c',
|
||||
meson_version: '>= 0.59',
|
||||
default_options : ['c_std=c11'])
|
||||
add_project_arguments('-D_DEFAULT_SOURCE', language : 'c')
|
||||
add_project_arguments('-DXSYS4_DATA_DIR="' + get_option('prefix') / get_option('datadir') / 'xsystem4"', language : 'c')
|
||||
|
||||
zlib = dependency('zlib')
|
||||
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')
|
||||
ft2 = dependency('freetype2')
|
||||
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)
|
||||
|
||||
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()
|
||||
@@ -24,8 +39,8 @@ else
|
||||
gl_deps = [gl, glew]
|
||||
endif
|
||||
|
||||
chibi = dependency('chibi-scheme', required : false)
|
||||
readline = dependency('readline', required : false)
|
||||
chibi = dependency('chibi-scheme', required : false, static : static_libs)
|
||||
readline = dependency('readline', required : false, static : static_libs)
|
||||
|
||||
flex = find_program('flex')
|
||||
bison = find_program('bison')
|
||||
|
||||
@@ -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,12 +15,14 @@
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform vec4 color;
|
||||
uniform vec4 color; // .a is the discard threshold
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
vec4 texel = texture(tex, tex_coord);
|
||||
frag_color = vec4(color.rgb, texel.r * color.a);
|
||||
vec4 texel = texture(tex, tex_coord);
|
||||
if (texel.r < color.a)
|
||||
discard;
|
||||
frag_color = vec4(color.rgb, texel.r);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/* Copyright (C) 2023 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
vec4 texel = texture(tex, tex_coord);
|
||||
float gray = dot(texel.rgb, vec3(0.299, 0.587, 0.114));
|
||||
frag_color = vec4(vec3(gray), texel.a);
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float threshold;
|
||||
uniform vec4 color;
|
||||
uniform vec4 color; // .a is the discard threshold
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
@@ -26,15 +26,16 @@ void main() {
|
||||
// edge pixels
|
||||
int size = int(floor(threshold));
|
||||
int cutoff = size + 1;
|
||||
float edge_weight = fract(threshold);
|
||||
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 > threshold)
|
||||
if (d > t)
|
||||
continue;
|
||||
float a = texture(tex, tex_coord + vec2(x, y) / tex_size).r;
|
||||
if (d > float(size))
|
||||
@@ -43,5 +44,7 @@ void main() {
|
||||
}
|
||||
}
|
||||
|
||||
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,36 @@
|
||||
/* Copyright (C) 2026 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex; // the new scene
|
||||
uniform sampler2D old; // the old scene
|
||||
uniform vec2 resolution; // the screen resolution
|
||||
uniform float progress; // effect progress (0..1)
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
#define EFFECT_HEIGHT 210.0
|
||||
|
||||
void main() {
|
||||
// start position of effect
|
||||
float top = ((resolution.y + EFFECT_HEIGHT) * (1.0 - progress)) - EFFECT_HEIGHT;
|
||||
// current pixel y-coordinate
|
||||
float p = tex_coord.y * resolution.y;
|
||||
// calculate alpha value
|
||||
float alpha = 1.0 - clamp((p - top) / EFFECT_HEIGHT, 0.0, 1.0);
|
||||
vec3 col = mix(texture(tex, tex_coord).rgb, texture(old, tex_coord).rgb, alpha);
|
||||
frag_color = vec4(col, 1.0);
|
||||
}
|
||||
@@ -22,47 +22,30 @@ uniform float progress; // effect progress (0..1)
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
#define MAX_STRIDE 128.0
|
||||
#define MAX_CHUNK 128.0
|
||||
|
||||
vec3 get_pixel(vec2 xy) {
|
||||
return mix(texture(old, xy).rgb, texture(tex, xy).rgb, progress);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// size of each chunk
|
||||
float stride = MAX_STRIDE * (1.0 - abs(progress * 2.0 - 1.0)); // progress=(0..0.5..1) -> stride=(0..max..0)
|
||||
// offset of frag coordinate relative to chunk
|
||||
vec2 offset = mod(gl_FragCoord.xy, stride);
|
||||
// the chunk coordinate
|
||||
vec2 chunk = tex_coord - offset / resolution;
|
||||
// coordinate of chunk center
|
||||
vec2 center = chunk + vec2(stride/2.0, stride/2.0) / resolution;
|
||||
|
||||
// FIXME: In this implementation, the anchor point is at the top left of the screen.
|
||||
// It should be anchored at the center to match SACT2.dll.
|
||||
frag_color = vec4(get_pixel(center), 1.0);
|
||||
}
|
||||
|
||||
/* NOTE: This is an alternate implementation which computes an average for each chunk.
|
||||
SACT2.dll does not do this.
|
||||
|
||||
#define SAMPLES 8.0
|
||||
#define MAX_STRIDE 16.0
|
||||
|
||||
vec3 get_pixel(vec2 xy) {
|
||||
return mix(texture(old, xy).xyz, texture(tex, xy).xyz, progress);
|
||||
float triangle(float f) {
|
||||
return 1.0 - abs(f * 2.0 - 1.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
float stride = MAX_STRIDE * (1.0 - abs(progress * 2.0 - 1.0)); // progress=(0..0.5..1) -> stride=(0..max..0)
|
||||
vec3 avg = vec3(0, 0, 0);
|
||||
vec2 xy = floor(gl_FragCoord.xy / (SAMPLES * stride)) * (SAMPLES * stride);
|
||||
for (float j = 0.0; j < SAMPLES; j += 1.0) {
|
||||
for (float i = 0.0; i < SAMPLES; i+= 1.0) {
|
||||
avg += get_pixel((xy + stride * vec2(i, j)) / resolution);
|
||||
}
|
||||
}
|
||||
avg /= SAMPLES * SAMPLES;
|
||||
frag_color = vec4(avg, 1.0);
|
||||
// center of the screen
|
||||
vec2 center = resolution * 0.5;
|
||||
// size of each chunk
|
||||
float chunk_size = floor(triangle(progress) * MAX_CHUNK);
|
||||
// number of chunks in each screen quadrant (including partial chunks at edges)
|
||||
vec2 chunks_per_quadrant = floor(center / chunk_size);
|
||||
// offset from screen to edge of grid (negative or zero)
|
||||
vec2 grid_off = center - (chunks_per_quadrant * chunk_size);
|
||||
// offset of pixel in chunk
|
||||
vec2 chunk_off = mod(gl_FragCoord.xy - grid_off, chunk_size);
|
||||
// the chunk coordinate
|
||||
vec2 chunk = tex_coord - vec2(chunk_off) / resolution;
|
||||
// coordinate of chunk center
|
||||
vec2 chunk_center = chunk + vec2(chunk_size/2.0, chunk_size/2.0) / resolution;
|
||||
frag_color = vec4(get_pixel(chunk_center), 1.0);
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -50,7 +50,7 @@ void main() {
|
||||
vec2 p = tex_coord * resolution;
|
||||
|
||||
vec2 p1 = resolution;
|
||||
vec2 p2 = vec2((1.0 - progress*2.0) * p1.x, (1.0 - progress*2.0) * p1.y);
|
||||
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);
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex; // the new scene
|
||||
uniform sampler2D old; // the old scene
|
||||
uniform vec2 resolution; // the screen resolution
|
||||
uniform float progress; // effect progress (0..1)
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
#define M_PI 3.1415926535897932384626433832795
|
||||
#define OLD_PERIOD ((2.0 * M_PI) / 360.0)
|
||||
#define NEW_PERIOD ((2.0 * M_PI) / 720.0)
|
||||
#define OLD_SHIFT (-M_PI)
|
||||
#define NEW_SHIFT (M_PI / 2.0)
|
||||
|
||||
vec3 get_pixel(vec2 old_xy, vec2 new_xy) {
|
||||
// influence of old color is described by: C_old = (1-t)^2
|
||||
// influence of new color increases linearly: C_new = t
|
||||
// the resulting blend is not normalized
|
||||
vec3 old = texture(old, old_xy).rgb;
|
||||
vec3 new = texture(tex, new_xy).rgb;
|
||||
float old_weight = (1.0 - progress) * (1.0 - progress);
|
||||
float new_weight = progress;
|
||||
return (old * old_weight + new * new_weight);
|
||||
}
|
||||
|
||||
float triangle(float f) {
|
||||
return 1.0 - abs(f * 2.0 - 1.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// old: x = amp * sin((2pi/360)*y + phase + -pi)
|
||||
// new: x = amp * sin((2pi/720)*y + phase + pi/2)
|
||||
vec2 p = tex_coord * resolution;
|
||||
float amp = triangle(progress) * 80.0; // (0..0.5..1) -> (0..80..0);
|
||||
float phase = progress * M_PI * 8.0;
|
||||
float old_off = amp * sin(OLD_PERIOD * p.y + OLD_SHIFT + phase);
|
||||
float new_off = amp * sin(NEW_PERIOD * p.y + NEW_SHIFT + phase);
|
||||
vec2 old_xy = fract(vec2(p.x + old_off, p.y) / resolution);
|
||||
vec2 new_xy = fract(vec2(p.x - new_off, p.y) / resolution);
|
||||
frag_color = vec4(get_pixel(old_xy, new_xy), 1.0);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex; // the new scene
|
||||
uniform sampler2D old; // the old scene
|
||||
uniform vec2 resolution; // the screen resolution
|
||||
uniform float progress; // effect progress (0..1)
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
#define M_PI 3.1415926535897932384626433832795
|
||||
#define OLD_PERIOD ((2.0 * M_PI) / 360.0)
|
||||
#define NEW_PERIOD ((2.0 * M_PI) / 720.0)
|
||||
#define OLD_SHIFT (-M_PI)
|
||||
#define NEW_SHIFT (M_PI / 2.0)
|
||||
|
||||
vec3 get_pixel(vec2 old_xy, vec2 new_xy) {
|
||||
// influence of old color is described by: C_old = (1-t)^2
|
||||
// influence of new color increases linearly: C_new = t
|
||||
// the resulting blend is not normalized
|
||||
vec3 old = texture(old, old_xy).rgb;
|
||||
vec3 new = texture(tex, new_xy).rgb;
|
||||
float old_weight = (1.0 - progress) * (1.0 - progress);
|
||||
float new_weight = progress;
|
||||
return (old * old_weight + new * new_weight);
|
||||
}
|
||||
|
||||
float triangle(float f) {
|
||||
return 1.0 - abs(f * 2.0 - 1.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
// old: x = amp * sin((2pi/360)*y + phase + -pi)
|
||||
// new: x = amp * sin((2pi/720)*y + phase + pi/2)
|
||||
vec2 p = tex_coord * resolution;
|
||||
float amp = triangle(progress) * 160.0; // (0..0.5..1) -> (0..160..0);
|
||||
float phase = progress * M_PI * 8.0;
|
||||
float old_off_x = amp * sin(OLD_PERIOD * p.y + OLD_SHIFT + phase);
|
||||
float new_off_x = amp * sin(NEW_PERIOD * p.y + NEW_SHIFT + phase);
|
||||
float off_y = resolution.y * progress;
|
||||
vec2 old_xy = fract(vec2(p.x + old_off_x, p.y + off_y) / resolution);
|
||||
vec2 new_xy = fract(vec2(p.x - new_off_x, p.y + off_y) / resolution);
|
||||
frag_color = vec4(get_pixel(old_xy, new_xy), 1.0);
|
||||
}
|
||||
+33
-8
@@ -15,26 +15,51 @@
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float alpha_mod;
|
||||
uniform float blend_rate;
|
||||
uniform vec2 bot_left;
|
||||
uniform vec2 top_right;
|
||||
uniform vec3 add_color;
|
||||
uniform vec3 multiply_color;
|
||||
uniform int draw_filter;
|
||||
|
||||
uniform int use_clipper;
|
||||
uniform sampler2D clipper_tex;
|
||||
|
||||
in vec2 tex_coord;
|
||||
in vec2 clip_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
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;
|
||||
const int DRAW_FILTER_MULTIPLY = 2;
|
||||
const int DRAW_FILTER_SCREEN = 3;
|
||||
|
||||
bool inside_rect(vec2 p, vec2 bl, vec2 tr) {
|
||||
return all(greaterThanEqual(p, bl)) && all(lessThan(p, tr));
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 size = vec2(textureSize(tex, 0));
|
||||
vec2 bl = bot_left / size;
|
||||
vec2 tr = top_right / size;
|
||||
vec2 size = vec2(textureSize(tex, 0));
|
||||
if (!inside_rect(tex_coord, bot_left / size, top_right / size))
|
||||
discard;
|
||||
|
||||
vec4 tex_color = texture(tex, tex_coord);
|
||||
vec3 mod_color = (tex_color.rgb + add_color) * multiply_color;
|
||||
frag_color = vec4(mod_color, tex_color.a * alpha_mod) * point_in_rect(tex_coord, bl, tr);
|
||||
float alpha = tex_color.a * blend_rate;
|
||||
|
||||
if (use_clipper != 0) {
|
||||
if (!inside_rect(clip_coord, vec2(0.0), vec2(1.0)))
|
||||
discard;
|
||||
alpha *= texture(clipper_tex, clip_coord).a;
|
||||
}
|
||||
|
||||
if (draw_filter == DRAW_FILTER_MULTIPLY) {
|
||||
// result = mix(dst, mod_color*dst, alpha), used with (GL_DST_COLOR, GL_ZERO)
|
||||
frag_color = vec4(mix(vec3(1.0), mod_color, alpha), 1.0);
|
||||
} else if (draw_filter == DRAW_FILTER_SCREEN) {
|
||||
// result = mod_color*alpha + (1 - mod_color*alpha)*dst,
|
||||
// used with (GL_ONE, GL_ONE_MINUS_SRC_COLOR)
|
||||
frag_color = vec4(mod_color * alpha, 1.0);
|
||||
} else {
|
||||
// normal or additive — blend func handles alpha
|
||||
frag_color = vec4(mod_color, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform mat4 world_transform;
|
||||
uniform mat4 view_transform;
|
||||
uniform mat4 inv_clipper_transform;
|
||||
|
||||
in vec4 vertex_pos;
|
||||
in vec2 vertex_uv;
|
||||
out vec2 tex_coord;
|
||||
out vec2 clip_coord;
|
||||
|
||||
void main() {
|
||||
vec4 world_pos = world_transform * vertex_pos;
|
||||
gl_Position = view_transform * world_pos;
|
||||
tex_coord = vertex_uv;
|
||||
clip_coord = (inv_clipper_transform * world_pos).xy;
|
||||
}
|
||||
+137
-38
@@ -14,19 +14,25 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define NR_DIR_LIGHTS 3
|
||||
|
||||
#define DIFFUSE_EMISSIVE 0
|
||||
#define DIFFUSE_NORMAL 1
|
||||
#define DIFFUSE_LIGHT_MAP 2
|
||||
#define DIFFUSE_ENV_MAP 3
|
||||
|
||||
#define FOG_LINEAR 1
|
||||
#define FOG_LIGHT_SCATTERING 2
|
||||
|
||||
#define ALPHA_BLEND 0
|
||||
#define ALPHA_TEST 1
|
||||
#define ALPHA_MAP_BLEND 2
|
||||
#define ALPHA_MAP_TEST 3
|
||||
|
||||
#define FOG_LINEAR 1
|
||||
#define FOG_LIGHT_SCATTERING 2
|
||||
|
||||
#define ENABLE_SPECULAR (ENGINE == REIGN_ENGINE || ENGINE == SEAL_ENGINE)
|
||||
#define ENABLE_LIGHT_SCATTERING (ENGINE == REIGN_ENGINE || ENGINE == SEAL_ENGINE)
|
||||
|
||||
#if ENGINE == REIGN_ENGINE
|
||||
|
||||
#define NR_DIR_LIGHTS 3
|
||||
|
||||
struct dir_light {
|
||||
vec3 dir;
|
||||
@@ -34,8 +40,95 @@ struct dir_light {
|
||||
vec3 globe_diffuse;
|
||||
};
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform dir_light dir_lights[NR_DIR_LIGHTS];
|
||||
uniform float rim_exponent;
|
||||
uniform vec3 rim_color;
|
||||
uniform float fog_near;
|
||||
uniform float fog_far;
|
||||
uniform vec3 fog_color;
|
||||
in vec3 light_dir[NR_DIR_LIGHTS];
|
||||
|
||||
uniform vec3 global_ambient;
|
||||
|
||||
vec3 compute_diffuse(vec3 norm)
|
||||
{
|
||||
vec3 diffuse = vec3(0.0);
|
||||
for (int i = 0; i < NR_DIR_LIGHTS; i++) {
|
||||
float half_lambert = dot(norm, -light_dir[i]) * 0.5 + 0.5;
|
||||
diffuse += mix(dir_lights[i].globe_diffuse, dir_lights[i].diffuse, half_lambert);
|
||||
}
|
||||
return diffuse;
|
||||
}
|
||||
|
||||
#elif ENGINE == SEAL_ENGINE
|
||||
|
||||
const vec3 global_ambient = vec3(0.0);
|
||||
|
||||
// Hemisphere lighting (sky/middle/ground).
|
||||
uniform vec3 hemi_light_dir;
|
||||
uniform vec3 hemi_sky_color;
|
||||
uniform vec3 hemi_mid_color;
|
||||
uniform vec3 hemi_ground_color;
|
||||
|
||||
vec3 compute_diffuse(vec3 norm)
|
||||
{
|
||||
float t = 0.5 * dot(norm, hemi_light_dir) + 0.5;
|
||||
vec3 low_mid = mix(hemi_ground_color, hemi_mid_color, t);
|
||||
vec3 mid_high = mix(hemi_mid_color, hemi_sky_color, t);
|
||||
return mix(low_mid, mid_high, t);
|
||||
}
|
||||
|
||||
#else // TAPIR_ENGINE
|
||||
|
||||
const vec3 global_ambient = vec3(0.0);
|
||||
|
||||
vec3 compute_diffuse(vec3 norm)
|
||||
{
|
||||
return vec3(1.0);
|
||||
}
|
||||
|
||||
#endif // ENGINE == REIGN_ENGINE
|
||||
|
||||
#if ENABLE_LIGHT_SCATTERING
|
||||
uniform int fog_type;
|
||||
in vec3 ls_ex;
|
||||
in vec3 ls_in;
|
||||
#endif
|
||||
|
||||
#if ENABLE_SPECULAR
|
||||
uniform sampler2D specular_texture;
|
||||
uniform vec3 specular_color;
|
||||
uniform float specular_shininess;
|
||||
uniform bool use_specular_map;
|
||||
in vec3 specular_dir;
|
||||
#endif
|
||||
|
||||
#if ENGINE == SEAL_ENGINE
|
||||
// Uncharted 2 (Hable) filmic tone mapping.
|
||||
uniform vec4 tonemap_param; // (ExposureBias, WhitePoint, A, B)
|
||||
uniform vec4 tonemap_param2; // (C, D, E, F)
|
||||
uniform bool nolighting;
|
||||
uniform float grayscale_rate;
|
||||
|
||||
vec3 hable(vec3 v)
|
||||
{
|
||||
float a = tonemap_param.z, b = tonemap_param.w;
|
||||
float c = tonemap_param2.x, d = tonemap_param2.y;
|
||||
float e = tonemap_param2.z, f = tonemap_param2.w;
|
||||
return ((v * (a * v + c * b) + d * e) / (v * (a * v + b) + d * f)) - e / f;
|
||||
}
|
||||
|
||||
vec3 tone_map(vec3 color)
|
||||
{
|
||||
vec3 curr = hable(color * tonemap_param.x);
|
||||
vec3 white = hable(vec3(tonemap_param.y));
|
||||
return curr / white;
|
||||
}
|
||||
#endif
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform sampler2D blend_tex;
|
||||
uniform bool use_blend_texture;
|
||||
uniform sampler2D alpha_texture;
|
||||
uniform sampler2D light_texture;
|
||||
uniform sampler2D shadow_texture;
|
||||
@@ -44,33 +137,23 @@ uniform float alpha_mod;
|
||||
uniform bool use_normal_map;
|
||||
uniform sampler2D normal_texture;
|
||||
|
||||
uniform vec3 ambient;
|
||||
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 vec3 instance_ambient;
|
||||
uniform vec3 diffuse_mod;
|
||||
uniform int diffuse_type;
|
||||
uniform float shadow_darkness;
|
||||
uniform float shadow_bias;
|
||||
uniform int fog_type;
|
||||
uniform float fog_near;
|
||||
uniform float fog_far;
|
||||
uniform vec3 fog_color;
|
||||
uniform int alpha_mode;
|
||||
|
||||
in vec2 tex_coord;
|
||||
in vec2 light_tex_coord;
|
||||
in vec4 color_mod;
|
||||
in vec3 frag_pos;
|
||||
in vec4 shadow_frag_pos;
|
||||
in float dist;
|
||||
in vec3 eye;
|
||||
in vec3 normal;
|
||||
in vec3 light_dir[NR_DIR_LIGHTS];
|
||||
in vec3 specular_dir;
|
||||
in vec3 ls_ex;
|
||||
in vec3 ls_in;
|
||||
in float blend_weight;
|
||||
in vec2 blend_tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
@@ -83,40 +166,40 @@ void main() {
|
||||
}
|
||||
|
||||
vec3 view_dir = normalize(eye - frag_pos);
|
||||
vec3 frag_rgb = ambient;
|
||||
vec3 frag_rgb = global_ambient + instance_ambient;
|
||||
|
||||
// Diffuse lighting
|
||||
vec4 texel;
|
||||
vec3 diffuse = vec3(1.0);
|
||||
if (diffuse_type == DIFFUSE_ENV_MAP) {
|
||||
texel = texture(tex, (vec2(normal.x, -normal.y) + 1.0) / 2.0);
|
||||
frag_rgb = texel.rgb;
|
||||
} else {
|
||||
texel = texture(tex, tex_coord);
|
||||
if (diffuse_type == DIFFUSE_EMISSIVE) {
|
||||
frag_rgb = texel.rgb;
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
if (use_blend_texture) {
|
||||
texel = mix(texel, texture(blend_tex, blend_tex_coord), blend_weight);
|
||||
}
|
||||
if (diffuse_type != DIFFUSE_EMISSIVE) {
|
||||
diffuse = compute_diffuse(norm);
|
||||
if (diffuse_type == DIFFUSE_LIGHT_MAP) {
|
||||
diffuse *= texture(light_texture, light_tex_coord).rgb;
|
||||
}
|
||||
frag_rgb += texel.rgb * diffuse;
|
||||
}
|
||||
}
|
||||
frag_rgb += texel.rgb * diffuse * color_mod.rgb * diffuse_mod;
|
||||
|
||||
#if ENABLE_SPECULAR
|
||||
// Specular lighting
|
||||
if (specular_strength > 0.0) {
|
||||
if (any(greaterThan(specular_color, vec3(0.0)))) {
|
||||
vec3 reflect_dir = reflect(specular_dir, norm);
|
||||
float specular = pow(max(dot(view_dir, reflect_dir), 0.0), specular_shininess) * specular_strength;
|
||||
float specular = pow(max(dot(view_dir, reflect_dir), 0.0), specular_shininess);
|
||||
if (use_specular_map) {
|
||||
specular *= texture(specular_texture, tex_coord).r;
|
||||
}
|
||||
frag_rgb += vec3(specular);
|
||||
frag_rgb += specular_color * specular;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENGINE == REIGN_ENGINE
|
||||
// Rim lighting
|
||||
if (rim_exponent > 0.0) {
|
||||
// Normal map is not used here.
|
||||
@@ -128,9 +211,14 @@ void main() {
|
||||
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) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_LIGHT_SCATTERING
|
||||
if (fog_type == FOG_LIGHT_SCATTERING) {
|
||||
frag_rgb = frag_rgb * ls_ex + ls_in;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Shadow mapping
|
||||
if (shadow_darkness > 0.0) {
|
||||
@@ -149,13 +237,24 @@ void main() {
|
||||
frag_rgb *= brightness;
|
||||
}
|
||||
|
||||
#if ENGINE == SEAL_ENGINE
|
||||
if (!nolighting)
|
||||
frag_rgb = tone_map(frag_rgb);
|
||||
|
||||
if (grayscale_rate != 0.0)
|
||||
frag_rgb = mix(frag_rgb, vec3(dot(frag_rgb, vec3(0.299, 0.587, 0.114))), grayscale_rate);
|
||||
#endif
|
||||
|
||||
// Alpha mapping
|
||||
float alpha = texel.a * alpha_mod;
|
||||
float alpha = texel.a * color_mod.a * alpha_mod;
|
||||
if (alpha_mode == ALPHA_TEST) {
|
||||
if (alpha < 0.8)
|
||||
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);
|
||||
|
||||
+71
-27
@@ -14,10 +14,16 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define NR_DIR_LIGHTS 3
|
||||
const float PI = 3.14159265358979323846;
|
||||
|
||||
#define FOG_LIGHT_SCATTERING 2
|
||||
|
||||
const float PI = 3.14159265358979323846;
|
||||
#define ENABLE_SPECULAR (ENGINE == REIGN_ENGINE || ENGINE == SEAL_ENGINE)
|
||||
#define ENABLE_LIGHT_SCATTERING (ENGINE == REIGN_ENGINE || ENGINE == SEAL_ENGINE)
|
||||
|
||||
#if ENGINE == REIGN_ENGINE
|
||||
|
||||
#define NR_DIR_LIGHTS 3
|
||||
|
||||
struct dir_light {
|
||||
vec3 dir;
|
||||
@@ -25,59 +31,88 @@ struct dir_light {
|
||||
vec3 globe_diffuse;
|
||||
};
|
||||
|
||||
uniform mat4 local_transform;
|
||||
uniform mat4 view_transform;
|
||||
uniform mat4 proj_transform;
|
||||
uniform mat3 normal_transform;
|
||||
|
||||
const int MAX_BONES = 211; // see 3d_internal.h
|
||||
const int NR_WEIGHTS = 4;
|
||||
uniform bool has_bones;
|
||||
uniform mat4 bone_matrices[MAX_BONES];
|
||||
|
||||
uniform bool use_normal_map;
|
||||
|
||||
uniform vec3 camera_pos;
|
||||
uniform dir_light dir_lights[NR_DIR_LIGHTS];
|
||||
uniform vec3 specular_light_dir;
|
||||
uniform mat4 shadow_transform;
|
||||
out vec3 light_dir[NR_DIR_LIGHTS];
|
||||
|
||||
const vec2 uv_scroll = vec2(0.0);
|
||||
|
||||
#else // ENGINE == REIGN_ENGINE
|
||||
|
||||
uniform vec2 uv_scroll;
|
||||
|
||||
#endif // ENGINE == REIGN_ENGINE
|
||||
|
||||
uniform vec4 uv_tiling;
|
||||
|
||||
#if ENABLE_LIGHT_SCATTERING
|
||||
uniform int fog_type;
|
||||
uniform vec4 ls_params; // (beta_r, beta_m, g, distance)
|
||||
uniform vec3 ls_light_dir;
|
||||
uniform vec3 ls_light_color;
|
||||
uniform vec3 ls_sun_color;
|
||||
out vec3 ls_ex;
|
||||
out vec3 ls_in;
|
||||
#endif
|
||||
|
||||
#if ENABLE_SPECULAR
|
||||
uniform vec3 specular_light_dir;
|
||||
out vec3 specular_dir;
|
||||
#endif
|
||||
|
||||
uniform mat4 local_transform;
|
||||
uniform mat4 view_transform;
|
||||
uniform mat4 proj_transform;
|
||||
uniform mat3 normal_transform;
|
||||
|
||||
const int MAX_BONES = 308; // see 3d_internal.h
|
||||
const int NR_WEIGHTS = 4;
|
||||
uniform bool has_bones;
|
||||
layout(std140) uniform BoneTransforms {
|
||||
mat3x4 bone_matrices[MAX_BONES]; // transposed
|
||||
};
|
||||
|
||||
uniform bool use_normal_map;
|
||||
|
||||
uniform vec3 camera_pos;
|
||||
uniform mat4 shadow_transform;
|
||||
|
||||
in vec3 vertex_pos;
|
||||
in vec3 vertex_normal;
|
||||
in vec2 vertex_uv;
|
||||
in vec2 vertex_light_uv;
|
||||
in vec4 vertex_color;
|
||||
in vec4 vertex_tangent;
|
||||
in ivec4 vertex_bone_index;
|
||||
in vec4 vertex_bone_weight;
|
||||
in float vertex_blend_weight;
|
||||
in vec2 vertex_blend_uv;
|
||||
|
||||
out vec2 tex_coord;
|
||||
out vec2 light_tex_coord;
|
||||
out vec4 color_mod;
|
||||
out vec3 frag_pos;
|
||||
out vec4 shadow_frag_pos;
|
||||
out float dist;
|
||||
out vec3 eye;
|
||||
out vec3 normal;
|
||||
out vec3 light_dir[NR_DIR_LIGHTS];
|
||||
out vec3 specular_dir;
|
||||
out vec3 ls_ex;
|
||||
out vec3 ls_in;
|
||||
out float blend_weight;
|
||||
out vec2 blend_tex_coord;
|
||||
|
||||
void main() {
|
||||
mat4 local_bone_transform = local_transform;
|
||||
mat3 normal_bone_transform = normal_transform;
|
||||
if (has_bones) {
|
||||
mat4 bone_transform = mat4(0.f);
|
||||
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 *= bone_transform;
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -95,8 +130,11 @@ void main() {
|
||||
vec4 view_pos = view_transform * world_pos;
|
||||
gl_Position = proj_transform * view_pos;
|
||||
|
||||
tex_coord = vertex_uv;
|
||||
light_tex_coord = vertex_light_uv;
|
||||
tex_coord = (vertex_uv + uv_scroll) * uv_tiling.xy;
|
||||
light_tex_coord = vertex_light_uv + uv_scroll;
|
||||
color_mod = vertex_color;
|
||||
blend_weight = vertex_blend_weight;
|
||||
blend_tex_coord = (vertex_blend_uv + uv_scroll) * uv_tiling.zw;
|
||||
dist = -view_pos.z;
|
||||
shadow_frag_pos = shadow_transform * world_pos;
|
||||
|
||||
@@ -104,11 +142,16 @@ void main() {
|
||||
// otherwise.
|
||||
frag_pos = TBN * vec3(world_pos);
|
||||
eye = TBN * camera_pos;
|
||||
|
||||
#if ENABLE_SPECULAR
|
||||
specular_dir = TBN * specular_light_dir;
|
||||
#endif
|
||||
#if ENGINE == REIGN_ENGINE
|
||||
light_dir[0] = TBN * dir_lights[0].dir;
|
||||
light_dir[1] = TBN * dir_lights[1].dir;
|
||||
light_dir[2] = TBN * dir_lights[2].dir;
|
||||
specular_dir = TBN * specular_light_dir;
|
||||
|
||||
#endif
|
||||
#if ENABLE_LIGHT_SCATTERING
|
||||
if (fog_type == FOG_LIGHT_SCATTERING) {
|
||||
float beta_r = ls_params.x;
|
||||
float beta_m = ls_params.y;
|
||||
@@ -127,4 +170,5 @@ void main() {
|
||||
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);
|
||||
}
|
||||
@@ -14,29 +14,35 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform mat4 world_transform;
|
||||
uniform mat4 local_transform;
|
||||
uniform mat4 view_transform;
|
||||
|
||||
const int MAX_BONES = 211; // see 3d_internal.h
|
||||
const int MAX_BONES = 308; // see 3d_internal.h
|
||||
const int NR_WEIGHTS = 4;
|
||||
uniform bool has_bones;
|
||||
uniform mat4 bone_matrices[MAX_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 world_bone_transform = world_transform;
|
||||
mat4 local_bone_transform = local_transform;
|
||||
if (has_bones) {
|
||||
mat4 bone_transform = mat4(0.f);
|
||||
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];
|
||||
}
|
||||
}
|
||||
world_bone_transform *= bone_transform;
|
||||
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 * world_bone_transform * vec4(vertex_pos, 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;
|
||||
}
|
||||
+391
-23
@@ -26,7 +26,7 @@
|
||||
#include "reign.h"
|
||||
|
||||
#define NR_DIR_LIGHTS 3
|
||||
#define MAX_BONES 211 // Maximum in TT3
|
||||
#define MAX_BONES 308 // Maximum in Rance Quest
|
||||
|
||||
typedef struct vec3_range {
|
||||
vec3 begin;
|
||||
@@ -40,32 +40,45 @@ struct model {
|
||||
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 bone *bones; // ordered so that parents precede their children
|
||||
struct bone **bones_by_pol_index; // POL bone array index -> struct bone *
|
||||
struct hash_table *bone_map; // bone id in POL -> struct bone *
|
||||
struct hash_table *mot_cache; // name -> struct mot *
|
||||
struct collider *collider;
|
||||
vec3 aabb[2]; // axis-aligned bounding box
|
||||
bool has_transparent_material;
|
||||
bool has_transparent_mesh;
|
||||
};
|
||||
|
||||
struct mesh {
|
||||
char *name;
|
||||
uint32_t flags;
|
||||
bool is_transparent;
|
||||
bool hidden;
|
||||
GLuint vao;
|
||||
GLuint attr_buffer;
|
||||
GLuint index_buffer;
|
||||
int nr_vertices;
|
||||
int nr_indices;
|
||||
int material;
|
||||
vec3 outline_color;
|
||||
float outline_thickness;
|
||||
vec2 uv_scroll;
|
||||
vec3 specular_color;
|
||||
float specular_power;
|
||||
};
|
||||
|
||||
struct material {
|
||||
uint32_t flags;
|
||||
bool is_transparent;
|
||||
GLuint color_map;
|
||||
vec2 uv_tiling;
|
||||
vec2 blend_uv_tiling;
|
||||
int nr_color_maps;
|
||||
GLuint *color_maps;
|
||||
GLuint specular_map;
|
||||
GLuint alpha_map;
|
||||
GLuint light_map;
|
||||
GLuint normal_map;
|
||||
GLuint blend_texture;
|
||||
float specular_strength;
|
||||
float specular_shininess;
|
||||
float shadow_darkness;
|
||||
@@ -87,6 +100,7 @@ struct motion {
|
||||
float current_frame;
|
||||
float frame_begin, frame_end;
|
||||
float loop_frame_begin, loop_frame_end;
|
||||
int texture_index;
|
||||
};
|
||||
|
||||
// Attribute variable locations
|
||||
@@ -97,7 +111,10 @@ enum RE_attribute_location {
|
||||
VATTR_BONE_INDEX,
|
||||
VATTR_BONE_WEIGHT,
|
||||
VATTR_LIGHT_UV,
|
||||
VATTR_COLOR,
|
||||
VATTR_TANGENT,
|
||||
VATTR_BLEND_WEIGHT,
|
||||
VATTR_BLEND_UV,
|
||||
};
|
||||
|
||||
struct shadow_renderer {
|
||||
@@ -106,19 +123,33 @@ struct shadow_renderer {
|
||||
GLuint texture;
|
||||
|
||||
// Uniform variable locations
|
||||
GLint world_transform;
|
||||
GLint local_transform;
|
||||
GLint view_transform;
|
||||
GLint has_bones;
|
||||
GLint bone_matrices;
|
||||
};
|
||||
|
||||
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 world_transform;
|
||||
GLint view_transform;
|
||||
GLint texture;
|
||||
GLint local_transform;
|
||||
@@ -126,8 +157,9 @@ struct RE_renderer {
|
||||
GLint normal_transform;
|
||||
GLint alpha_mod;
|
||||
GLint has_bones;
|
||||
GLint bone_matrices;
|
||||
GLint ambient;
|
||||
GLint global_ambient;
|
||||
GLint instance_ambient;
|
||||
GLint diffuse_mod;
|
||||
struct {
|
||||
GLint dir;
|
||||
GLint diffuse;
|
||||
@@ -135,7 +167,7 @@ struct RE_renderer {
|
||||
GLint ambient;
|
||||
} dir_lights[NR_DIR_LIGHTS];
|
||||
GLint specular_light_dir;
|
||||
GLint specular_strength;
|
||||
GLint specular_color;
|
||||
GLint specular_shininess;
|
||||
GLint use_specular_map;
|
||||
GLint specular_texture;
|
||||
@@ -158,12 +190,29 @@ struct RE_renderer {
|
||||
GLint ls_light_dir;
|
||||
GLint ls_light_color;
|
||||
GLint ls_sun_color;
|
||||
GLint hemi_light_dir;
|
||||
GLint hemi_sky_color;
|
||||
GLint hemi_mid_color;
|
||||
GLint hemi_ground_color;
|
||||
GLint tonemap_param;
|
||||
GLint tonemap_param2;
|
||||
GLint nolighting;
|
||||
GLint grayscale_rate;
|
||||
GLint alpha_mode;
|
||||
GLint alpha_texture;
|
||||
GLint uv_scroll;
|
||||
GLint uv_tiling;
|
||||
GLint blend_tex;
|
||||
GLint use_blend_texture;
|
||||
|
||||
GLuint billboard_vao;
|
||||
GLuint billboard_attr_buffer;
|
||||
struct hash_table *billboard_textures; // cg_no -> struct billboard_texture*
|
||||
GLuint billboard_particle_vao;
|
||||
GLuint billboard_particle_attr_buffer;
|
||||
GLuint billboard_instance_vao;
|
||||
GLuint billboard_instance_attr_buffer;
|
||||
struct hash_table *billboard_textures_by_no; // cg_no -> struct billboard_texture*
|
||||
struct hash_table *billboard_textures_by_path; // path -> struct billboard_texture*
|
||||
|
||||
uint32_t last_frame_timestamp;
|
||||
};
|
||||
|
||||
struct billboard_texture {
|
||||
@@ -173,6 +222,9 @@ struct billboard_texture {
|
||||
|
||||
// reign.c
|
||||
|
||||
extern enum RE_plugin_version re_plugin_version;
|
||||
|
||||
struct RE_instance *RE_create_instance_at(struct RE_plugin *plugin, int index);
|
||||
void RE_instance_update_local_transform(struct RE_instance *inst);
|
||||
|
||||
// model.c
|
||||
@@ -188,9 +240,11 @@ struct archive_data *RE_get_aar_entry(struct archive *aar, const char *dir, cons
|
||||
|
||||
// renderer.c
|
||||
|
||||
struct RE_renderer *RE_renderer_new(struct texture *texture);
|
||||
struct RE_renderer *RE_renderer_new(void);
|
||||
void RE_renderer_free(struct RE_renderer *r);
|
||||
bool RE_renderer_load_billboard_texture(struct RE_renderer *r, int cg_no);
|
||||
void RE_renderer_set_viewport_size(struct RE_renderer *r, int width, int height);
|
||||
bool RE_renderer_load_billboard_texture_by_no(struct RE_renderer *r, int cg_no);
|
||||
struct billboard_texture *RE_renderer_load_billboard_texture_by_path(struct RE_renderer *r, struct archive *aar, const char *path);
|
||||
struct height_detector *RE_renderer_create_height_detector(struct RE_renderer *r, struct model *model);
|
||||
void RE_renderer_free_height_detector(struct height_detector *hd);
|
||||
bool RE_renderer_detect_height(struct height_detector *hd, float x, float z, float *y_out);
|
||||
@@ -366,6 +420,218 @@ void particle_effect_free(struct particle_effect *effect);
|
||||
void particle_effect_update(struct RE_instance *inst);
|
||||
void particle_object_calc_local_transform(struct RE_instance *inst, struct particle_object *po, struct particle_instance *pi, float frame, mat4 dest);
|
||||
|
||||
// s3de.c
|
||||
|
||||
// .3de effect system. The 's' prefix is just to avoid an identifier starting
|
||||
// with a digit; it stands for SealEngine.
|
||||
|
||||
enum s3de_object_type {
|
||||
S3DE_OBJ_BILLBOARD,
|
||||
S3DE_OBJ_POLYGON,
|
||||
S3DE_OBJ_CAMERA,
|
||||
};
|
||||
|
||||
enum s3de_move_type {
|
||||
S3DE_MOVE_LINEAR,
|
||||
S3DE_MOVE_BEZIER,
|
||||
};
|
||||
|
||||
enum s3de_spawn_position_type {
|
||||
S3DE_SPAWN_NORMAL,
|
||||
S3DE_SPAWN_SPHERE,
|
||||
S3DE_SPAWN_HORIZONTAL,
|
||||
};
|
||||
|
||||
enum s3de_blend_type {
|
||||
S3DE_BLEND_NORMAL,
|
||||
S3DE_BLEND_ADDITIVE,
|
||||
};
|
||||
|
||||
enum s3de_emitter_link_type {
|
||||
S3DE_LINK_NONE = 0, // frozen at spawn-time world pos
|
||||
S3DE_LINK_FOLLOW = 1, // particle follows current emitter pos
|
||||
S3DE_LINK_ROTATE_AT_SPAWN = 2, // emitter rotation around spawn pos
|
||||
S3DE_LINK_ROTATE_AT_CURRENT = 3, // emitter rotation around current pos
|
||||
S3DE_LINK_MAX = S3DE_LINK_ROTATE_AT_CURRENT
|
||||
};
|
||||
|
||||
enum s3de_direction_type {
|
||||
S3DE_DIR_RANDOM,
|
||||
S3DE_DIR_SPECIFIED,
|
||||
S3DE_DIR_EMITTER,
|
||||
S3DE_DIR_EMITTER_REVERSE,
|
||||
S3DE_DIR_EMITTER_COORD,
|
||||
S3DE_DIR_ARBITRARY_PLANE,
|
||||
S3DE_DIR_SPAWN,
|
||||
};
|
||||
|
||||
enum s3de_posture_type {
|
||||
S3DE_POSE_NONE,
|
||||
S3DE_POSE_CAMERA,
|
||||
S3DE_POSE_MOVE_DIR,
|
||||
S3DE_POSE_MOVE_AND_FLY,
|
||||
S3DE_POSE_FLY_AND_EMITTER_MOVE,
|
||||
};
|
||||
|
||||
enum s3de_interp_type {
|
||||
S3DE_INTERP_NONE,
|
||||
S3DE_INTERP_LINEAR,
|
||||
S3DE_INTERP_SLERP,
|
||||
};
|
||||
|
||||
struct s3de_range {
|
||||
float base;
|
||||
float random;
|
||||
};
|
||||
|
||||
struct s3de_scalar_entry {
|
||||
int frame;
|
||||
float value;
|
||||
enum s3de_interp_type interp;
|
||||
};
|
||||
|
||||
struct s3de_vec3_entry {
|
||||
int frame;
|
||||
vec3 v;
|
||||
enum s3de_interp_type interp;
|
||||
};
|
||||
|
||||
struct s3de_position_entry {
|
||||
int frame;
|
||||
vec3 pos;
|
||||
enum s3de_interp_type interp;
|
||||
float spawn_radius;
|
||||
float spawn_angle_deg;
|
||||
};
|
||||
|
||||
struct s3de_object {
|
||||
char *name;
|
||||
enum s3de_object_type type;
|
||||
enum s3de_move_type movement_type;
|
||||
enum s3de_spawn_position_type spawn_position_type;
|
||||
float spawn_distance;
|
||||
int particle_count;
|
||||
|
||||
char *texture;
|
||||
|
||||
enum s3de_blend_type blend_type;
|
||||
char *polygon_name;
|
||||
enum s3de_emitter_link_type emitter_link_type;
|
||||
bool soft_fog_edge;
|
||||
|
||||
enum s3de_posture_type posture_type;
|
||||
enum s3de_direction_type direction_type;
|
||||
vec3 direction;
|
||||
float direction_angle;
|
||||
|
||||
struct s3de_range start_size, end_size;
|
||||
struct s3de_range start_x_size, end_x_size;
|
||||
struct s3de_range start_y_size, end_y_size;
|
||||
struct s3de_range offset_x, offset_y, offset_z;
|
||||
struct s3de_range speed, acceleration;
|
||||
struct s3de_range movement_distance, movement_curve;
|
||||
|
||||
bool free_fall;
|
||||
float mass, air_resistance;
|
||||
|
||||
struct s3de_range start_x_rotation, end_x_rotation;
|
||||
struct s3de_range start_y_rotation, end_y_rotation;
|
||||
struct s3de_range start_z_rotation, end_z_rotation;
|
||||
|
||||
float x_revolution_angle[2], y_revolution_angle[2], z_revolution_angle[2];
|
||||
float x_revolution_distance[2], y_revolution_distance[2], z_revolution_distance[2];
|
||||
|
||||
int child_frame;
|
||||
float texture_anime_frame;
|
||||
|
||||
struct s3de_range alpha_fade_in_frame, alpha_fade_out_frame;
|
||||
|
||||
int *damages;
|
||||
int nr_damages;
|
||||
|
||||
struct s3de_position_entry *position_list;
|
||||
int nr_position_entries;
|
||||
struct s3de_scalar_entry *size_list;
|
||||
int nr_size_entries;
|
||||
struct s3de_scalar_entry *x_size_list;
|
||||
int nr_x_size_entries;
|
||||
struct s3de_scalar_entry *y_size_list;
|
||||
int nr_y_size_entries;
|
||||
struct s3de_scalar_entry *alpha_list;
|
||||
int nr_alpha_entries;
|
||||
struct s3de_vec3_entry *rotation_list;
|
||||
int nr_rotation_entries;
|
||||
struct s3de_vec3_entry *multiply_color_list;
|
||||
int nr_multiply_color_entries;
|
||||
struct s3de_vec3_entry *additive_color_list;
|
||||
int nr_additive_color_entries;
|
||||
|
||||
struct model *model;
|
||||
};
|
||||
|
||||
struct s3de {
|
||||
char *path;
|
||||
int loop_start_frame;
|
||||
int loop_end_frame;
|
||||
struct s3de_object *objects;
|
||||
int nr_objects;
|
||||
struct hash_table *textures; // name -> struct billboard_texture*
|
||||
};
|
||||
|
||||
struct s3de *s3de_load(struct archive *aar, const char *path);
|
||||
void s3de_free(struct s3de *s);
|
||||
|
||||
struct s3de_particle {
|
||||
float begin_frame, end_frame;
|
||||
vec3 world_pos; // spawn-time world position
|
||||
vec3 spawn_offset; // offset from emitter pos
|
||||
vec3 direction;
|
||||
vec3 spawn_dir; // emitter motion direction at spawn frame
|
||||
float speed, accel;
|
||||
float movement_distance, movement_curve;
|
||||
float start_size_scale, end_size_scale;
|
||||
float start_x_scale, end_x_scale;
|
||||
float start_y_scale, end_y_scale;
|
||||
float offset_x, offset_y; // per-particle 2D quad pivot shift
|
||||
vec3 start_rotation_deg, end_rotation_deg;
|
||||
float fade_in_frames, fade_out_frames;
|
||||
};
|
||||
|
||||
// Emitter-level values interpolated once per frame by s3de_effect_update,
|
||||
// then read (and combined with per-particle values) during rendering.
|
||||
struct s3de_object_state {
|
||||
vec3 emitter_pos;
|
||||
float emitter_size, emitter_x_size, emitter_y_size;
|
||||
float emitter_alpha;
|
||||
vec3 emitter_rotation_deg;
|
||||
vec3 multiply_color;
|
||||
vec3 additive_color;
|
||||
struct s3de_particle *particles; // length = obj->particle_count
|
||||
};
|
||||
|
||||
struct s3de_effect {
|
||||
struct s3de *s3de;
|
||||
struct s3de_object_state *objects; // length = s3de->nr_objects
|
||||
int wav_channel; // -1 if no sound
|
||||
bool sound_started;
|
||||
float last_frame;
|
||||
};
|
||||
|
||||
struct s3de_effect *s3de_effect_create(struct s3de *s, struct archive *aar);
|
||||
void s3de_effect_free(struct s3de_effect *eff);
|
||||
void s3de_effect_update(struct RE_instance *inst);
|
||||
void s3de_calc_frame_range(struct s3de *s, struct motion *motion);
|
||||
bool s3de_particle_alpha(struct s3de_object_state *st, struct s3de_particle *p,
|
||||
float frame, float *alpha_out);
|
||||
bool s3de_billboard_world_transform(struct RE_instance *inst,
|
||||
struct s3de_object *obj, struct s3de_object_state *st,
|
||||
struct s3de_particle *p, float frame, mat3 camera_rot,
|
||||
mat4 out);
|
||||
bool s3de_mesh_world_transform(struct RE_instance *inst,
|
||||
struct s3de_object *obj, struct s3de_object_state *st,
|
||||
struct s3de_particle *p, float frame, mat3 camera_rot, vec3 camera_pos,
|
||||
mat4 out);
|
||||
|
||||
// parser.c
|
||||
|
||||
struct pol {
|
||||
@@ -389,26 +655,38 @@ enum pol_texture_type {
|
||||
|
||||
enum material_flags {
|
||||
MATERIAL_SPRITE = 1 << 0,
|
||||
MATERIAL_ALPHA = 1 << 1,
|
||||
};
|
||||
|
||||
struct pol_material {
|
||||
char *name;
|
||||
uint32_t flags;
|
||||
char *textures[MAX_TEXTURE_TYPE];
|
||||
vec2 uv_tiling;
|
||||
};
|
||||
|
||||
struct pol_material_group {
|
||||
struct pol_material m;
|
||||
uint32_t nr_children;
|
||||
struct pol_material *children;
|
||||
struct pol_material_group *children;
|
||||
};
|
||||
|
||||
enum mesh_flags {
|
||||
MESH_NOLIGHTING = 1 << 0,
|
||||
MESH_NOMAKESHADOW = 1 << 1,
|
||||
MESH_ENVMAP = 1 << 2,
|
||||
MESH_BOTH = 1 << 3,
|
||||
MESH_SPRITE = 1 << 4,
|
||||
MESH_NOLIGHTING = 1 << 0,
|
||||
MESH_NOMAKESHADOW = 1 << 1,
|
||||
MESH_ENVMAP = 1 << 2,
|
||||
MESH_BOTH = 1 << 3,
|
||||
MESH_SPRITE = 1 << 4,
|
||||
MESH_BLEND_ADDITIVE = 1 << 5,
|
||||
MESH_NO_EDGE = 1 << 6,
|
||||
MESH_NO_HEIGHT_DETECTION = 1 << 7,
|
||||
MESH_ALPHA = 1 << 8,
|
||||
MESH_HAS_LIGHT_UV = 1 << 9,
|
||||
MESH_NO_ZWRITE = 1 << 10,
|
||||
MESH_HEIGHT_DETECTION = 1 << 11,
|
||||
MESH_HAS_SPECULAR_COLOR = 1 << 12,
|
||||
MESH_HAS_SPECULAR_POWER = 1 << 13,
|
||||
MESH_NO_DRAWSHADOW = 1 << 14,
|
||||
};
|
||||
|
||||
struct pol_mesh {
|
||||
@@ -421,8 +699,22 @@ struct pol_mesh {
|
||||
vec2 *uvs;
|
||||
uint32_t nr_light_uvs;
|
||||
vec2 *light_uvs;
|
||||
uint32_t nr_colors;
|
||||
vec3 *colors;
|
||||
uint32_t nr_alphas;
|
||||
float *alphas;
|
||||
uint32_t nr_blend_uvs;
|
||||
vec2 *blend_uvs;
|
||||
uint32_t nr_blend_weights;
|
||||
float *blend_weights;
|
||||
uint32_t nr_triangles;
|
||||
struct pol_triangle *triangles;
|
||||
// Parameters in .opr file.
|
||||
Color edge_color;
|
||||
float edge_size;
|
||||
vec2 uv_scroll;
|
||||
vec3 specular_color;
|
||||
float specular_power;
|
||||
};
|
||||
|
||||
struct pol_vertex {
|
||||
@@ -440,6 +732,10 @@ struct pol_triangle {
|
||||
uint32_t vert_index[3];
|
||||
uint32_t uv_index[3];
|
||||
uint32_t light_uv_index[3];
|
||||
uint32_t color_index[3];
|
||||
uint32_t alpha_index[3];
|
||||
uint32_t blend_uv_index[3];
|
||||
uint32_t blend_weight_index[3];
|
||||
vec3 normals[3];
|
||||
uint32_t material_group_index;
|
||||
};
|
||||
@@ -456,6 +752,9 @@ struct mot {
|
||||
char *name;
|
||||
uint32_t nr_frames;
|
||||
uint32_t nr_bones;
|
||||
uint32_t nr_texture_indices;
|
||||
uint32_t *texture_indices; // texture index for each frame
|
||||
struct mpr *mpr; // optional
|
||||
struct mot_bone *motions[];
|
||||
};
|
||||
|
||||
@@ -506,4 +805,73 @@ struct amt *amt_parse(uint8_t *data, size_t size);
|
||||
void amt_free(struct amt *amt);
|
||||
struct amt_material *amt_find_material(struct amt *amt, const char *name);
|
||||
|
||||
void opr_load(uint8_t *data, size_t size, struct pol *pol);
|
||||
void txa_load(uint8_t *data, size_t size, struct mot *mot);
|
||||
|
||||
void lit_reset(float *out);
|
||||
bool lit_parse(uint8_t *data, size_t size, float *out);
|
||||
|
||||
// mpr.c
|
||||
|
||||
struct mpr_float_key { int frame; float v; };
|
||||
struct mpr_vec3_key { int frame; vec3 v; };
|
||||
struct mpr_int_key { int frame; int v; };
|
||||
|
||||
struct mpr_track_set {
|
||||
int nr_mul_alpha;
|
||||
struct mpr_float_key *mul_alpha;
|
||||
int nr_mul_diffuse;
|
||||
struct mpr_vec3_key *mul_diffuse;
|
||||
int nr_add_ambient;
|
||||
struct mpr_vec3_key *add_ambient;
|
||||
int nr_texture_anime;
|
||||
struct mpr_int_key *texture_anime; // mesh-scope only
|
||||
};
|
||||
|
||||
struct mpr {
|
||||
int nr_meshes; // == model->nr_meshes
|
||||
struct mpr_track_set **mesh_tracks; // sparse: indexed by mesh index, NULL if absent
|
||||
struct mpr_track_set object;
|
||||
bool has_mesh_alpha; // any mesh_tracks[i]->nr_mul_alpha > 0
|
||||
};
|
||||
|
||||
// Per-draw material modulation.
|
||||
struct mpr_modulation {
|
||||
float alpha;
|
||||
vec3 ambient;
|
||||
vec3 diffuse;
|
||||
};
|
||||
|
||||
struct mpr *mpr_load(uint8_t *data, size_t size, struct model *model);
|
||||
void mpr_free(struct mpr *mpr);
|
||||
void mpr_evaluate_object(const struct mpr *mpr, float frame,
|
||||
struct RE_instance *inst, struct mpr_modulation *out);
|
||||
void mpr_evaluate_mesh(const struct mpr_track_set *mt, float frame,
|
||||
const struct mpr_modulation *obj, struct mpr_modulation *out);
|
||||
void mpr_build_mat_tex_index(const struct model *model, const struct RE_instance *inst,
|
||||
const struct mpr *mpr, float frame, int *mat_tex_index);
|
||||
|
||||
// collision.c
|
||||
|
||||
struct collider_triangle;
|
||||
struct collider_edge;
|
||||
|
||||
struct collider {
|
||||
struct collider_triangle *triangles;
|
||||
uint32_t nr_triangles;
|
||||
struct collider_edge *edges; // boundary edges
|
||||
uint32_t nr_edges;
|
||||
vec3 *path_points;
|
||||
uint32_t nr_path_points;
|
||||
};
|
||||
|
||||
struct collider *collider_create(struct pol_mesh *mesh);
|
||||
struct collider *collider_create_raycast(struct pol_mesh **meshes, int nr_meshes);
|
||||
void collider_free(struct collider *collider);
|
||||
bool collider_height(struct collider *collider, vec2 xz, float *h_out);
|
||||
bool check_collision(struct collider *collider, vec2 p0, vec2 p1, float radius, vec2 out);
|
||||
bool collider_raycast(struct collider *collider, vec3 origin, vec3 direction, vec3 out);
|
||||
bool collider_find_path(struct collider *collider, vec3 start, vec3 goal, mat4 vp_transform);
|
||||
bool collider_optimize_path(struct collider *collider);
|
||||
|
||||
#endif /* SYSTEM4_3D_3D_INTERNAL_H */
|
||||
|
||||
@@ -0,0 +1,534 @@
|
||||
/* Copyright (C) 2024 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <float.h>
|
||||
#include <stdlib.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#include "system4.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
|
||||
struct collider_triangle {
|
||||
vec3 vertices[3];
|
||||
vec3 center;
|
||||
vec2 aabb[2]; // xz coordinates
|
||||
vec2 slope;
|
||||
float intercept;
|
||||
int neighbors[3];
|
||||
};
|
||||
|
||||
struct collider_edge {
|
||||
vec2 vertices[2]; // xz coordinates
|
||||
vec2 aabb[2];
|
||||
};
|
||||
|
||||
static struct collider_triangle *fill_triangles(struct collider_triangle *t, struct pol_mesh *mesh)
|
||||
{
|
||||
for (int tri_i = 0; tri_i < mesh->nr_triangles; tri_i++, t++) {
|
||||
glm_aabb2d_invalidate(t->aabb);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
glm_vec3_copy(mesh->vertices[mesh->triangles[tri_i].vert_index[i]].pos, t->vertices[i]);
|
||||
glm_vec3_add(t->center, t->vertices[i], t->center);
|
||||
vec2 xz = { t->vertices[i][0], t->vertices[i][2] };
|
||||
glm_vec2_minv(xz, t->aabb[0], t->aabb[0]);
|
||||
glm_vec2_maxv(xz, t->aabb[1], t->aabb[1]);
|
||||
}
|
||||
glm_vec3_divs(t->center, 3.f, t->center);
|
||||
|
||||
// Do some precomputation so that we can calculate height(x, z) quickly.
|
||||
vec3 v1, v2, normal;
|
||||
glm_vec3_sub(t->vertices[1], t->vertices[0], v1);
|
||||
glm_vec3_sub(t->vertices[2], t->vertices[0], v2);
|
||||
glm_vec3_cross(v1, v2, normal);
|
||||
t->slope[0] = -normal[0] / normal[1];
|
||||
t->slope[1] = -normal[2] / normal[1];
|
||||
t->intercept = glm_vec3_dot(normal, t->vertices[0]) / normal[1];
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
t->neighbors[i] = -1;
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
static void init_triangles(struct collider *collider, struct pol_mesh *mesh)
|
||||
{
|
||||
collider->nr_triangles = mesh->nr_triangles;
|
||||
collider->triangles = xcalloc(mesh->nr_triangles, sizeof(struct collider_triangle));
|
||||
fill_triangles(collider->triangles, mesh);
|
||||
}
|
||||
|
||||
static void link_neighbors(struct collider *collider, int t1_index, int t2_index)
|
||||
{
|
||||
struct collider_triangle *t1 = &collider->triangles[t1_index];
|
||||
struct collider_triangle *t2 = &collider->triangles[t2_index];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (t1->neighbors[i] == -1) {
|
||||
t1->neighbors[i] = t2_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (t2->neighbors[i] == -1) {
|
||||
t2->neighbors[i] = t1_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void init_edges(struct collider *collider, struct pol_mesh *mesh)
|
||||
{
|
||||
// Link neighboring triangles.
|
||||
if (mesh->nr_triangles >= 65535)
|
||||
ERROR("Too many triangles in collision mesh: %d", mesh->nr_triangles);
|
||||
const int nv = mesh->nr_vertices;
|
||||
const int table_size = nv * nv;
|
||||
uint16_t *table = xcalloc(table_size, sizeof(uint16_t));
|
||||
int nr_edges = 0;
|
||||
for (int i = 0; i < mesh->nr_triangles; i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
int v1 = mesh->triangles[i].vert_index[j];
|
||||
int v2 = mesh->triangles[i].vert_index[(j + 1) % 3];
|
||||
int k = v2 * nv + v1;
|
||||
if (table[k]) {
|
||||
link_neighbors(collider, table[k] - 1, i);
|
||||
table[k] = 0;
|
||||
nr_edges--;
|
||||
} else {
|
||||
table[v1 * nv + v2] = i + 1;
|
||||
nr_edges++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Collect boundary edges, i.e., edges that belong to only one triangle.
|
||||
collider->nr_edges = nr_edges;
|
||||
collider->edges = xcalloc(nr_edges, sizeof(struct collider_edge));
|
||||
struct collider_edge* edge = collider->edges;
|
||||
for (int i = 0; i < table_size; i++) {
|
||||
if (!table[i]) continue;
|
||||
int v1 = i / nv;
|
||||
int v2 = i % nv;
|
||||
edge->vertices[0][0] = mesh->vertices[v1].pos[0];
|
||||
edge->vertices[0][1] = mesh->vertices[v1].pos[2];
|
||||
edge->vertices[1][0] = mesh->vertices[v2].pos[0];
|
||||
edge->vertices[1][1] = mesh->vertices[v2].pos[2];
|
||||
glm_vec2_minv(edge->vertices[0], edge->vertices[1], edge->aabb[0]);
|
||||
glm_vec2_maxv(edge->vertices[0], edge->vertices[1], edge->aabb[1]);
|
||||
edge++;
|
||||
}
|
||||
assert(edge == collider->edges + nr_edges);
|
||||
free(table);
|
||||
}
|
||||
|
||||
struct collider *collider_create(struct pol_mesh *mesh)
|
||||
{
|
||||
struct collider *collider = xcalloc(1, sizeof(struct collider));
|
||||
init_triangles(collider, mesh);
|
||||
init_edges(collider, mesh);
|
||||
return collider;
|
||||
}
|
||||
|
||||
// Creates a collider for raycasting only (no edges / neighbor links), from the
|
||||
// union of the given meshes' triangles. Used for SealEngine HeightDetection meshes.
|
||||
struct collider *collider_create_raycast(struct pol_mesh **meshes, int nr_meshes)
|
||||
{
|
||||
struct collider *collider = xcalloc(1, sizeof(struct collider));
|
||||
for (int i = 0; i < nr_meshes; i++)
|
||||
collider->nr_triangles += meshes[i]->nr_triangles;
|
||||
collider->triangles = xcalloc(collider->nr_triangles, sizeof(struct collider_triangle));
|
||||
struct collider_triangle *t = collider->triangles;
|
||||
for (int i = 0; i < nr_meshes; i++)
|
||||
t = fill_triangles(t, meshes[i]);
|
||||
return collider;
|
||||
}
|
||||
|
||||
void collider_free(struct collider *collider)
|
||||
{
|
||||
free(collider->triangles);
|
||||
free(collider->edges);
|
||||
free(collider->path_points);
|
||||
free(collider);
|
||||
}
|
||||
|
||||
static bool in_triangle(struct collider_triangle* t, vec2 xz)
|
||||
{
|
||||
vec2 v[3];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
v[i][0] = t->vertices[i][0];
|
||||
v[i][1] = t->vertices[i][2];
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
vec2 a, b;
|
||||
glm_vec2_sub(v[(i + 1) % 3], v[i], a);
|
||||
glm_vec2_sub(xz, v[i], b);
|
||||
if (glm_vec2_cross(a, b) > 0.f)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct collider_triangle* find_triangle(struct collider *collider, vec2 xz)
|
||||
{
|
||||
struct collider_triangle* t = collider->triangles;
|
||||
for (; t < collider->triangles + collider->nr_triangles; t++) {
|
||||
if (!glm_aabb2d_point(t->aabb, xz))
|
||||
continue;
|
||||
if (in_triangle(t, xz))
|
||||
return t;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool collider_height(struct collider *collider, vec2 xz, float *h_out)
|
||||
{
|
||||
struct collider_triangle* t = find_triangle(collider, xz);
|
||||
if (!t)
|
||||
return false;
|
||||
*h_out = glm_vec2_dot(t->slope, xz) + t->intercept;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Calculates the squared distance from a point to a line segment in 2D space.
|
||||
static float distance2_point_to_segment(vec2 p, vec2 a, vec2 b, vec2 closest_point_out)
|
||||
{
|
||||
vec2 v, w;
|
||||
glm_vec2_sub(b, a, v);
|
||||
glm_vec2_sub(p, a, w);
|
||||
|
||||
float c1 = glm_vec2_dot(w, v);
|
||||
if (c1 < 0.f) {
|
||||
glm_vec2_copy(a, closest_point_out);
|
||||
return glm_vec2_distance2(p, a);
|
||||
}
|
||||
float c2 = glm_vec2_norm2(v);
|
||||
if (c2 <= c1) {
|
||||
glm_vec2_copy(b, closest_point_out);
|
||||
return glm_vec2_distance2(p, b);
|
||||
}
|
||||
float t = c1 / c2;
|
||||
glm_vec2_copy(a, closest_point_out);
|
||||
glm_vec2_muladds(v, t, closest_point_out);
|
||||
return glm_vec2_distance2(p, closest_point_out);
|
||||
}
|
||||
|
||||
static bool segments_intersect(vec2 p0, vec2 p1, vec2 q0, vec2 q1)
|
||||
{
|
||||
vec2 r, s;
|
||||
glm_vec2_sub(p1, p0, r);
|
||||
glm_vec2_sub(q1, q0, s);
|
||||
float r_cross_s = glm_vec2_cross(r, s);
|
||||
if (r_cross_s == 0.f) {
|
||||
// The segments are collinear.
|
||||
return false;
|
||||
}
|
||||
vec2 q0_p0;
|
||||
glm_vec2_sub(q0, p0, q0_p0);
|
||||
float t = glm_vec2_cross(q0_p0, s) / r_cross_s;
|
||||
float u = glm_vec2_cross(q0_p0, r) / r_cross_s;
|
||||
return (t >= 0.f && t <= 1.f && u >= 0.f && u <= 1.f);
|
||||
}
|
||||
|
||||
bool check_collision(struct collider *collider, vec2 p0, vec2 p1, float radius, vec2 out)
|
||||
{
|
||||
if (find_triangle(collider, p1)) {
|
||||
glm_vec2_copy(p1, out);
|
||||
} else {
|
||||
if (!find_triangle(collider, p0))
|
||||
return false;
|
||||
// Find a point on the "collision" mesh, using binary search.
|
||||
vec2 good, bad;
|
||||
glm_vec2_copy(p0, good);
|
||||
glm_vec2_copy(p1, bad);
|
||||
float w = glm_vec2_distance(good, bad);
|
||||
while (w > radius) {
|
||||
vec2 mid;
|
||||
glm_vec2_center(good, bad, mid);
|
||||
if (find_triangle(collider, mid)) {
|
||||
glm_vec2_copy(mid, good);
|
||||
} else {
|
||||
glm_vec2_copy(mid, bad);
|
||||
}
|
||||
w /= 2.f;
|
||||
}
|
||||
glm_vec2_copy(good, out);
|
||||
}
|
||||
|
||||
// Ensure that `out` is at least `radius` away from any boundary edge.
|
||||
vec2 aabb[2];
|
||||
glm_vec2_subs(out, radius, aabb[0]);
|
||||
glm_vec2_adds(out, radius, aabb[1]);
|
||||
for (int i = 0; i < collider->nr_edges; i++) {
|
||||
struct collider_edge *e = &collider->edges[i];
|
||||
if (!glm_aabb2d_aabb(e->aabb, aabb))
|
||||
continue;
|
||||
vec2 q;
|
||||
float sq_distance = distance2_point_to_segment(out, e->vertices[0], e->vertices[1], q);
|
||||
if (sq_distance < radius * radius) {
|
||||
// out = q + normalize(out - q) * radius
|
||||
vec2 v;
|
||||
glm_vec2_sub(out, q, v);
|
||||
glm_vec2_normalize(v);
|
||||
glm_vec2_copy(q, out);
|
||||
glm_vec2_muladds(v, radius, out);
|
||||
// update AABB
|
||||
glm_vec2_subs(out, radius, aabb[0]);
|
||||
glm_vec2_adds(out, radius, aabb[1]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool collider_raycast(struct collider *collider, vec3 origin, vec3 direction, vec3 out)
|
||||
{
|
||||
// Return the intersection nearest to the ray origin (closest to the camera).
|
||||
float nearest = FLT_MAX;
|
||||
struct collider_triangle* t = collider->triangles;
|
||||
for (; t < collider->triangles + collider->nr_triangles; t++) {
|
||||
float d;
|
||||
if (glm_ray_triangle(origin, direction, t->vertices[0], t->vertices[1], t->vertices[2], &d)) {
|
||||
if (d < nearest)
|
||||
nearest = d;
|
||||
}
|
||||
}
|
||||
if (nearest == FLT_MAX)
|
||||
return false;
|
||||
glm_vec3_scale(direction, nearest, out);
|
||||
glm_vec3_add(origin, out, out);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct pathfinder_node {
|
||||
int pred;
|
||||
float g_score;
|
||||
enum { UNDISCOVERED, IN_FRONTIER, VISITED, NOT_VISIBLE } state;
|
||||
};
|
||||
|
||||
struct frontier {
|
||||
int triangle_index;
|
||||
float f_score;
|
||||
};
|
||||
|
||||
struct pathfinder {
|
||||
struct pathfinder_node *nodes; // indexed by triangle index
|
||||
struct frontier *frontiers; // min-heap
|
||||
int nr_frontiers;
|
||||
};
|
||||
|
||||
static int frontier_pop(struct pathfinder *pf)
|
||||
{
|
||||
if (pf->nr_frontiers == 0) {
|
||||
return -1;
|
||||
}
|
||||
int index = pf->frontiers[0].triangle_index;
|
||||
if (--pf->nr_frontiers > 0) {
|
||||
pf->frontiers[0] = pf->frontiers[pf->nr_frontiers];
|
||||
int i = 0;
|
||||
while (i < pf->nr_frontiers) {
|
||||
int left = 2 * i + 1;
|
||||
int right = 2 * i + 2;
|
||||
int smallest = i;
|
||||
if (left < pf->nr_frontiers && pf->frontiers[left].f_score < pf->frontiers[smallest].f_score) {
|
||||
smallest = left;
|
||||
}
|
||||
if (right < pf->nr_frontiers && pf->frontiers[right].f_score < pf->frontiers[smallest].f_score) {
|
||||
smallest = right;
|
||||
}
|
||||
if (smallest == i) {
|
||||
break;
|
||||
}
|
||||
struct frontier tmp = pf->frontiers[i];
|
||||
pf->frontiers[i] = pf->frontiers[smallest];
|
||||
pf->frontiers[smallest] = tmp;
|
||||
i = smallest;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
static void frontier_push(struct pathfinder *pf, int index, float f_score)
|
||||
{
|
||||
pf->frontiers[pf->nr_frontiers++] = (struct frontier){ index, f_score };
|
||||
int i = pf->nr_frontiers - 1;
|
||||
while (i > 0) {
|
||||
int parent = (i - 1) / 2;
|
||||
if (pf->frontiers[i].f_score >= pf->frontiers[parent].f_score) {
|
||||
break;
|
||||
}
|
||||
struct frontier tmp = pf->frontiers[i];
|
||||
pf->frontiers[i] = pf->frontiers[parent];
|
||||
pf->frontiers[parent] = tmp;
|
||||
i = parent;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_point_visible(vec3 point, mat4 vp_transform)
|
||||
{
|
||||
vec4 p = { point[0], point[1], point[2], 1.f };
|
||||
glm_mat4_mulv(vp_transform, p, p);
|
||||
// Check if the point is in the view frustum.
|
||||
return p[0] >= -p[3] && p[0] <= p[3] &&
|
||||
p[1] >= -p[3] && p[1] <= p[3] &&
|
||||
p[2] >= -p[3] && p[2] <= p[3];
|
||||
}
|
||||
|
||||
bool collider_find_path(struct collider *collider, vec3 start, vec3 goal, mat4 vp_transform)
|
||||
{
|
||||
if (collider->path_points) {
|
||||
collider->nr_path_points = 0;
|
||||
free(collider->path_points);
|
||||
collider->path_points = NULL;
|
||||
}
|
||||
|
||||
// A* search for a graph with triangle centers as nodes and neighbors as edges.
|
||||
struct collider_triangle* start_t = find_triangle(collider, (vec2){ start[0], start[2] });
|
||||
if (!start_t || !is_point_visible(start_t->center, vp_transform))
|
||||
return false;
|
||||
struct collider_triangle* goal_t = find_triangle(collider, (vec2){ goal[0], goal[2] });
|
||||
if (!goal_t || !is_point_visible(goal_t->center, vp_transform))
|
||||
return false;
|
||||
|
||||
int start_i = start_t - collider->triangles;
|
||||
int goal_i = goal_t - collider->triangles;
|
||||
struct pathfinder pf;
|
||||
pf.nodes = xcalloc(collider->nr_triangles, sizeof(struct pathfinder_node));
|
||||
pf.nodes[goal_i].pred = -2;
|
||||
pf.nodes[start_i].pred = -1;
|
||||
pf.nodes[start_i].g_score = 0.f;
|
||||
pf.nodes[start_i].state = IN_FRONTIER;
|
||||
pf.frontiers = xcalloc(collider->nr_triangles, sizeof(struct frontier));
|
||||
pf.nr_frontiers = 0;
|
||||
frontier_push(&pf, start_i, glm_vec3_distance(start_t->center, goal_t->center));
|
||||
|
||||
while (pf.nr_frontiers > 0) {
|
||||
int current_i = frontier_pop(&pf);
|
||||
if (pf.nodes[current_i].state == VISITED)
|
||||
continue;
|
||||
struct collider_triangle *current_t = &collider->triangles[current_i];
|
||||
if (current_i == goal_i)
|
||||
break;
|
||||
pf.nodes[current_i].state = VISITED;
|
||||
for (int j = 0; j < 3; j++) {
|
||||
int neighbor_i = current_t->neighbors[j];
|
||||
if (neighbor_i == -1)
|
||||
continue;
|
||||
struct collider_triangle *neighbor_t = &collider->triangles[neighbor_i];
|
||||
struct pathfinder_node *neighbor_n = &pf.nodes[neighbor_i];
|
||||
if (neighbor_n->state == NOT_VISIBLE)
|
||||
continue;
|
||||
if (neighbor_n->state == UNDISCOVERED && !is_point_visible(neighbor_t->center, vp_transform)) {
|
||||
neighbor_n->state = NOT_VISIBLE;
|
||||
continue;
|
||||
}
|
||||
float g = pf.nodes[current_i].g_score + glm_vec3_distance(current_t->center, neighbor_t->center);
|
||||
if (neighbor_n->state == UNDISCOVERED || g < neighbor_n->g_score) {
|
||||
neighbor_n->pred = current_i;
|
||||
neighbor_n->g_score = g;
|
||||
neighbor_n->state = IN_FRONTIER;
|
||||
float f_score = g + glm_vec3_distance(neighbor_t->center, goal_t->center);
|
||||
frontier_push(&pf, neighbor_i, f_score);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pf.nodes[goal_i].pred != -2) {
|
||||
// Reconstruct the path.
|
||||
int path_length = 0;
|
||||
int current_i = goal_i;
|
||||
while (current_i != -1) {
|
||||
path_length++;
|
||||
current_i = pf.nodes[current_i].pred;
|
||||
}
|
||||
collider->nr_path_points = path_length + 2;
|
||||
collider->path_points = xcalloc(path_length + 2, sizeof(vec3));
|
||||
glm_vec3_copy(start, collider->path_points[0]);
|
||||
glm_vec3_copy(goal, collider->path_points[path_length + 1]);
|
||||
current_i = goal_i;
|
||||
for (int i = path_length - 1; i >= 0; i--) {
|
||||
glm_vec3_copy(collider->triangles[current_i].center, collider->path_points[i + 1]);
|
||||
current_i = pf.nodes[current_i].pred;
|
||||
}
|
||||
}
|
||||
free(pf.nodes);
|
||||
free(pf.frontiers);
|
||||
return !!collider->path_points;
|
||||
}
|
||||
|
||||
// determines if a segment intersects with any collider edges, or is close enough to them.
|
||||
static bool test_segment(struct collider *collider, vec2 p0, vec2 p1, float threshold)
|
||||
{
|
||||
vec2 aabb[2];
|
||||
glm_vec2_minv(p0, p1, aabb[0]);
|
||||
glm_vec2_maxv(p0, p1, aabb[1]);
|
||||
aabb[0][0] -= threshold;
|
||||
aabb[0][1] -= threshold;
|
||||
aabb[1][0] += threshold;
|
||||
aabb[1][1] += threshold;
|
||||
float threshold2 = threshold * threshold;
|
||||
for (int i = 0; i < collider->nr_edges; i++) {
|
||||
struct collider_edge *e = &collider->edges[i];
|
||||
if (!glm_aabb2d_aabb(e->aabb, aabb))
|
||||
continue;
|
||||
if (segments_intersect(p0, p1, e->vertices[0], e->vertices[1]))
|
||||
return true;
|
||||
vec2 closest_point;
|
||||
float distance2 = distance2_point_to_segment(p0, e->vertices[0], e->vertices[1], closest_point);
|
||||
if (distance2 < threshold2)
|
||||
return true;
|
||||
distance2 = distance2_point_to_segment(p1, e->vertices[0], e->vertices[1], closest_point);
|
||||
if (distance2 < threshold2)
|
||||
return true;
|
||||
distance2 = distance2_point_to_segment(p0, p1, e->vertices[0], closest_point);
|
||||
if (distance2 < threshold2)
|
||||
return true;
|
||||
distance2 = distance2_point_to_segment(p0, p1, e->vertices[1], closest_point);
|
||||
if (distance2 < threshold2)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void optimize_path_rec(struct collider *collider, vec3 *points, int start, int end)
|
||||
{
|
||||
if (end - start >= 2) {
|
||||
// If the segment (points[start], points[end]) does not intersect any
|
||||
// collider edges, we can skip all points in between.
|
||||
vec2 p0 = { points[start][0], points[start][2] };
|
||||
vec2 p1 = { points[end][0], points[end][2] };
|
||||
if (test_segment(collider, p0, p1, 0.5f)) {
|
||||
int mid = (start + end) / 2;
|
||||
optimize_path_rec(collider, points, start, mid);
|
||||
optimize_path_rec(collider, points, mid, end);
|
||||
return;
|
||||
}
|
||||
}
|
||||
glm_vec3_copy(points[start], collider->path_points[collider->nr_path_points++]);
|
||||
}
|
||||
|
||||
bool collider_optimize_path(struct collider *collider)
|
||||
{
|
||||
if (collider->nr_path_points < 3)
|
||||
return collider->nr_path_points > 0; // nothing to optimize
|
||||
|
||||
int nr_points = collider->nr_path_points;
|
||||
vec3 *points = xmalloc(nr_points * sizeof(vec3));
|
||||
memcpy(points, collider->path_points, nr_points * sizeof(vec3));
|
||||
collider->nr_path_points = 0;
|
||||
optimize_path_rec(collider, points, 0, nr_points - 1);
|
||||
glm_vec3_copy(points[nr_points - 1], collider->path_points[collider->nr_path_points++]);
|
||||
free(points);
|
||||
return true;
|
||||
}
|
||||
+356
-72
@@ -17,7 +17,13 @@
|
||||
#include "system4/string.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
#include "cJSON.h"
|
||||
#include "debugger.h"
|
||||
#include "gfx/gfx.h"
|
||||
#include "json.h"
|
||||
#include "reign.h"
|
||||
#include "sact.h"
|
||||
#include "scene.h"
|
||||
#include "sprite.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
@@ -33,6 +39,7 @@ static const char *instance_type_name(enum RE_instance_type type)
|
||||
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";
|
||||
}
|
||||
@@ -57,107 +64,384 @@ static const char *fog_type_name(enum RE_fog_type type)
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
static void print_motion(const char *name, struct motion *m, int indent)
|
||||
static cJSON *motion_to_json(struct motion *m)
|
||||
{
|
||||
if (m->instance->type == RE_ITYPE_BILLBOARD) {
|
||||
indent_printf(indent, "%s = {state=%s, frame=%f, range=(%d,%d), loop_range=(%d,%d)},\n",
|
||||
name, motion_state_name(m->state), m->current_frame,
|
||||
(int)m->frame_begin, (int)m->frame_end, (int)m->loop_frame_begin, (int)m->loop_frame_end);
|
||||
} else if (m->mot) {
|
||||
indent_printf(indent, "%s = {name=\"%s\", state=%s, frame=%f},\n",
|
||||
name, m->mot->name, motion_state_name(m->state), m->current_frame);
|
||||
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 void print_instance(struct RE_instance *inst, int index, int indent)
|
||||
static cJSON *instance_to_json(struct RE_instance *inst, int index, bool verbose)
|
||||
{
|
||||
if (!inst)
|
||||
return;
|
||||
indent_printf(indent, "instance[%d] = {\n", index);
|
||||
indent++;
|
||||
|
||||
indent_printf(indent, "type = %s,\n", instance_type_name(inst->type));
|
||||
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) {
|
||||
indent_printf(indent, "vec = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(inst->vec));
|
||||
indent_printf(indent, "diffuse = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(inst->diffuse));
|
||||
indent_printf(indent, "globe_diffuse = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(inst->globe_diffuse));
|
||||
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 {
|
||||
indent_printf(indent, "draw = %d,\n", inst->draw);
|
||||
indent_printf(indent, "pos = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(inst->pos));
|
||||
indent_printf(indent, "scale = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(inst->scale));
|
||||
indent_printf(indent, "ambient = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(inst->ambient));
|
||||
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) {
|
||||
indent_printf(indent, "path = \"%s\",\n", inst->model->path);
|
||||
indent_printf(indent, "aabb = {min = {x=%f, y=%f, z=%f}, max = {x=%f, y=%f, z=%f}},\n",
|
||||
SPREAD_VEC3(inst->model->aabb[0]), SPREAD_VEC3(inst->model->aabb[1]));
|
||||
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) {
|
||||
indent_printf(indent, "fps = %f,\n", inst->fps);
|
||||
print_motion("motion", inst->motion, indent);
|
||||
cJSON_AddNumberToObject(obj, "fps", inst->fps);
|
||||
cJSON_AddItemToObjectCS(obj, "motion", motion_to_json(inst->motion));
|
||||
}
|
||||
if (inst->next_motion) {
|
||||
cJSON_AddItemToObjectCS(obj, "next_motion", motion_to_json(inst->next_motion));
|
||||
}
|
||||
if (inst->motion_blend) {
|
||||
cJSON_AddNumberToObject(obj, "motion_blend_rate", inst->motion_blend_rate);
|
||||
}
|
||||
if (inst->effect) {
|
||||
cJSON_AddStringToObject(obj, "path", inst->effect->pae->path);
|
||||
}
|
||||
if (inst->s3de_effect && inst->s3de_effect->s3de) {
|
||||
cJSON_AddStringToObject(obj, "path", inst->s3de_effect->s3de->path);
|
||||
}
|
||||
if (inst->next_motion)
|
||||
print_motion("next_motion", inst->next_motion, indent);
|
||||
if (inst->motion_blend)
|
||||
indent_printf(indent, "motion_blend_rate = %f,\n", inst->motion_blend_rate);
|
||||
if (inst->effect)
|
||||
indent_printf(indent, "path = \"%s\",\n", inst->effect->pae->path);
|
||||
|
||||
indent--;
|
||||
indent_printf(indent, "},\n");
|
||||
return obj;
|
||||
}
|
||||
|
||||
static void print_back_cg(struct RE_back_cg *bcg, int index, int indent)
|
||||
static cJSON *back_cg_to_json(struct RE_back_cg *bcg, int index, bool verbose)
|
||||
{
|
||||
if (!bcg->name && !bcg->no)
|
||||
return;
|
||||
indent_printf(indent, "backCG[%d] = {\n", index);
|
||||
indent++;
|
||||
|
||||
if (bcg->no)
|
||||
indent_printf(indent, "no = %d,\n", bcg->no);
|
||||
if (bcg->name)
|
||||
indent_printf(indent, "name = \"%s\",\n", bcg->name ? bcg->name->text : "");
|
||||
indent_printf(indent, "pos = {x=%f, y=%f},\n", bcg->x, bcg->y);
|
||||
indent_printf(indent, "blend_rate = %f,\n", bcg->blend_rate);
|
||||
indent_printf(indent, "mag = %f,\n", bcg->mag);
|
||||
indent_printf(indent, "show = %d,\n", bcg->show);
|
||||
|
||||
indent--;
|
||||
indent_printf(indent, "},\n");
|
||||
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;
|
||||
}
|
||||
|
||||
void RE_debug_print(struct sact_sprite *sp, int indent)
|
||||
static cJSON *re_plugin_to_json(struct RE_plugin *p, bool verbose)
|
||||
{
|
||||
struct RE_plugin *p = (struct RE_plugin *)sp->plugin;
|
||||
cJSON *obj, *cam, *a;
|
||||
|
||||
indent_printf(indent, "name = \"%s\",\n", p->plugin.name);
|
||||
indent_printf(indent, "camera = {x=%f, y=%f, z=%f, pitch=%f, roll=%f, yaw=%f},\n",
|
||||
SPREAD_VEC3(p->camera.pos), p->camera.pitch, p->camera.roll, p->camera.yaw);
|
||||
indent_printf(indent, "shadow_map_light_dir = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(p->shadow_map_light_dir));
|
||||
indent_printf(indent, "shadow_bias = %f,\n", p->shadow_bias);
|
||||
|
||||
indent_printf(indent, "fog_type = %s,\n", fog_type_name(p->fog_type));
|
||||
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:
|
||||
indent_printf(indent, "fog_near = %f, fog_far = %f,\n", p->fog_near, p->fog_far);
|
||||
indent_printf(indent, "fog_color = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(p->fog_color));
|
||||
cJSON_AddNumberToObject(obj, "fog_near", p->fog_near);
|
||||
cJSON_AddNumberToObject(obj, "fog_far", p->fog_far);
|
||||
break;
|
||||
case RE_FOG_LIGHT_SCATTERING:
|
||||
indent_printf(indent, "ls_beta_r = %f, ls_beta_m = %f,\n", p->ls_beta_r, p->ls_beta_m);
|
||||
indent_printf(indent, "ls_g = %f,\n", p->ls_g);
|
||||
indent_printf(indent, "ls_distance = %f,\n", p->ls_distance);
|
||||
indent_printf(indent, "ls_light_dir = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(p->ls_light_dir));
|
||||
indent_printf(indent, "ls_light_color = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(p->ls_light_color));
|
||||
indent_printf(indent, "ls_sun_color = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(p->ls_sun_color));
|
||||
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;
|
||||
}
|
||||
|
||||
for (int i = 0; i < p->nr_instances; i++)
|
||||
print_instance(p->instances[i], i, indent);
|
||||
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));
|
||||
|
||||
}
|
||||
|
||||
for (int i = 0; i < RE_NR_BACK_CGS; i++)
|
||||
print_back_cg(&p->back_cg[i], i, indent);
|
||||
cJSON_AddItemToObjectCS(obj, "back_cgs", a = cJSON_CreateArray());
|
||||
for (int i = 0; i < RE_NR_BACK_CGS; i++) {
|
||||
struct RE_back_cg *bcg = &p->back_cg[i];
|
||||
if (!bcg->name && !bcg->no)
|
||||
continue;
|
||||
cJSON_AddItemToArray(a, back_cg_to_json(bcg, i, verbose));
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
cJSON *RE_to_json(struct sact_sprite *sp, bool verbose)
|
||||
{
|
||||
return re_plugin_to_json((struct RE_plugin*)sp->plugin, verbose);
|
||||
}
|
||||
|
||||
#ifdef DEBUGGER_ENABLED
|
||||
|
||||
static int dbg_current_plugin = -1;
|
||||
|
||||
// Returns the plugin number to use. Falls back to the only existing plugin
|
||||
// if no current is set. Prints an error and returns -1 if neither holds.
|
||||
static int dbg_resolve_plugin(void)
|
||||
{
|
||||
if (dbg_current_plugin >= 0) {
|
||||
if (RE_get_plugin(dbg_current_plugin))
|
||||
return dbg_current_plugin;
|
||||
DBG_ERROR("Current plugin %d no longer exists", dbg_current_plugin);
|
||||
dbg_current_plugin = -1;
|
||||
return -1;
|
||||
}
|
||||
int found = -1;
|
||||
for (int h = 0; h < RE_MAX_PLUGINS; h++) {
|
||||
if (!RE_get_plugin(h))
|
||||
continue;
|
||||
if (found >= 0) {
|
||||
DBG_ERROR("Multiple plugins exist; use '3d plugin <no>' to choose one");
|
||||
return -1;
|
||||
}
|
||||
found = h;
|
||||
}
|
||||
if (found < 0) {
|
||||
DBG_ERROR("No ReignEngine plugins");
|
||||
return -1;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
static struct RE_plugin *dbg_current_re_plugin(void)
|
||||
{
|
||||
int no = dbg_resolve_plugin();
|
||||
return no < 0 ? NULL : RE_get_plugin(no);
|
||||
}
|
||||
|
||||
static bool instance_has_content(struct RE_instance *inst)
|
||||
{
|
||||
if (inst->type == RE_ITYPE_UNINITIALIZED)
|
||||
return false;
|
||||
if (inst->model && inst->model->path)
|
||||
return true;
|
||||
if (inst->motion && inst->motion->mot)
|
||||
return true;
|
||||
if (inst->effect && inst->effect->pae)
|
||||
return true;
|
||||
if (inst->s3de_effect && inst->s3de_effect->s3de)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void instance_list_print(struct RE_instance *inst, int index, int indent)
|
||||
{
|
||||
indent_message(indent, inst->draw ? "+ " : "- ");
|
||||
sys_message("instance %d (%s)", index, instance_type_name(inst->type));
|
||||
if (inst->model && inst->model->path)
|
||||
sys_message(" model=%s", display_sjis0(inst->model->path));
|
||||
if (inst->motion && inst->motion->mot)
|
||||
sys_message(" motion=%s/%s frame=%.2f",
|
||||
display_sjis0(inst->motion->mot->name),
|
||||
motion_state_name(inst->motion->state),
|
||||
inst->motion->current_frame);
|
||||
if (inst->effect && inst->effect->pae)
|
||||
sys_message(" effect=%s", display_sjis0(inst->effect->pae->path));
|
||||
if (inst->s3de_effect && inst->s3de_effect->s3de)
|
||||
sys_message(" s3de=%s", display_sjis0(inst->s3de_effect->s3de->path));
|
||||
sys_message("\n");
|
||||
}
|
||||
|
||||
static void re_cmd_list(unsigned nr_args, char **args)
|
||||
{
|
||||
bool show_all = nr_args >= 1 && !strcmp(args[0], "all");
|
||||
for (int h = 0; h < RE_MAX_PLUGINS; h++) {
|
||||
struct RE_plugin *p = RE_get_plugin(h);
|
||||
if (!p)
|
||||
continue;
|
||||
int nr_hidden = 0;
|
||||
sys_message("plugin %d (%s sprite=%d nr_instances=%d)\n",
|
||||
h, p->plugin.name, p->sprite, p->nr_instances);
|
||||
for (int i = 0; i < p->nr_instances; i++) {
|
||||
struct RE_instance *inst = p->instances[i];
|
||||
if (!inst)
|
||||
continue;
|
||||
if (!show_all && !instance_has_content(inst)) {
|
||||
nr_hidden++;
|
||||
continue;
|
||||
}
|
||||
instance_list_print(inst, i, 1);
|
||||
}
|
||||
if (nr_hidden)
|
||||
indent_message(1, "(%d empty instance(s) hidden; use 'list all' to show)\n",
|
||||
nr_hidden);
|
||||
}
|
||||
}
|
||||
|
||||
static void re_cmd_plugin(unsigned nr_args, char **args)
|
||||
{
|
||||
if (nr_args == 0) {
|
||||
int no = dbg_resolve_plugin();
|
||||
if (no < 0)
|
||||
return;
|
||||
struct RE_plugin *p = RE_get_plugin(no);
|
||||
sys_message("plugin %d (%s)%s\n", no, p->plugin.name,
|
||||
dbg_current_plugin < 0 ? " (auto-selected)" : "");
|
||||
return;
|
||||
}
|
||||
int no = atoi(args[0]);
|
||||
if (!RE_get_plugin(no)) {
|
||||
DBG_ERROR("No ReignEngine plugin with handle '%s'", args[0]);
|
||||
return;
|
||||
}
|
||||
dbg_current_plugin = no;
|
||||
}
|
||||
|
||||
static void re_cmd_show(unsigned nr_args, char **args)
|
||||
{
|
||||
struct RE_plugin *p = dbg_current_re_plugin();
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
cJSON *json;
|
||||
if (nr_args >= 1) {
|
||||
int idx = atoi(args[0]);
|
||||
if (idx < 0 || idx >= p->nr_instances || !p->instances[idx]) {
|
||||
DBG_ERROR("No instance with index '%s'", args[0]);
|
||||
return;
|
||||
}
|
||||
json = instance_to_json(p->instances[idx], idx, true);
|
||||
} else {
|
||||
json = re_plugin_to_json(p, true);
|
||||
}
|
||||
char *text = cJSON_Print(json);
|
||||
|
||||
sys_message("%s\n", text);
|
||||
|
||||
free(text);
|
||||
cJSON_Delete(json);
|
||||
}
|
||||
|
||||
static void re_cmd_set(unsigned nr_args, char **args)
|
||||
{
|
||||
struct RE_plugin *p = dbg_current_re_plugin();
|
||||
if (!p)
|
||||
return;
|
||||
int idx = atoi(args[0]);
|
||||
if (idx < 0 || idx >= p->nr_instances || !p->instances[idx]) {
|
||||
DBG_ERROR("No instance with index '%s'", args[0]);
|
||||
return;
|
||||
}
|
||||
struct RE_instance *inst = p->instances[idx];
|
||||
const char *prop = args[1];
|
||||
unsigned nr_values = nr_args - 2;
|
||||
char **values = &args[2];
|
||||
|
||||
if (!strcmp(prop, "draw")) {
|
||||
if (nr_values != 1) {
|
||||
DBG_ERROR("'draw' requires 1 value (0 or 1)");
|
||||
return;
|
||||
}
|
||||
inst->draw = atoi(values[0]) != 0;
|
||||
} else if (!strcmp(prop, "pos")) {
|
||||
if (nr_values != 3) {
|
||||
DBG_ERROR("'pos' requires 3 values (x y z)");
|
||||
return;
|
||||
}
|
||||
inst->pos[0] = strtof(values[0], NULL);
|
||||
inst->pos[1] = strtof(values[1], NULL);
|
||||
inst->pos[2] = strtof(values[2], NULL);
|
||||
inst->local_transform_needs_update = true;
|
||||
} else {
|
||||
DBG_ERROR("Unknown property '%s'", prop);
|
||||
return;
|
||||
}
|
||||
|
||||
sprite_call_plugins();
|
||||
scene_render();
|
||||
gfx_swap();
|
||||
}
|
||||
|
||||
static void re_cmd_help(unsigned nr_args, char **args);
|
||||
|
||||
static struct dbg_cmd re_cmds[] = {
|
||||
{ "list", NULL, "[all]",
|
||||
"Display ReignEngine plugins and their instances (use 'all' to include uninitialized)",
|
||||
0, 1, re_cmd_list },
|
||||
{ "plugin", NULL, "[<plugin-no>]",
|
||||
"Show or set the current plugin used by 'show' and 'set'",
|
||||
0, 1, re_cmd_plugin },
|
||||
{ "show", NULL, "[<instance-no>]",
|
||||
"Display current plugin or instance state as JSON",
|
||||
0, 1, re_cmd_show },
|
||||
{ "set", NULL, "<instance-no> <property> <value...>",
|
||||
"Set an instance property (draw, pos)",
|
||||
3, 5, re_cmd_set },
|
||||
{ "help", "h", NULL, "List 3d commands",
|
||||
0, 0, re_cmd_help },
|
||||
};
|
||||
|
||||
static void re_cmd_help(unsigned nr_args, char **args)
|
||||
{
|
||||
int name_len = 0;
|
||||
for (unsigned i = 0; i < sizeof(re_cmds)/sizeof(*re_cmds); i++) {
|
||||
int n = strlen(re_cmds[i].fullname);
|
||||
if (n > name_len)
|
||||
name_len = n;
|
||||
}
|
||||
sys_message("\n3d commands\n===========\n");
|
||||
for (unsigned i = 0; i < sizeof(re_cmds)/sizeof(*re_cmds); i++) {
|
||||
struct dbg_cmd *c = &re_cmds[i];
|
||||
sys_message("%*s", name_len, c->fullname);
|
||||
if (c->shortname)
|
||||
sys_message(" (%s)", c->shortname);
|
||||
if (c->arg_description)
|
||||
sys_message(" %s", c->arg_description);
|
||||
sys_message(" -- %s\n", c->description);
|
||||
}
|
||||
}
|
||||
|
||||
void re_debug_init(void)
|
||||
{
|
||||
static bool initialized = false;
|
||||
if (initialized)
|
||||
return;
|
||||
initialized = true;
|
||||
|
||||
dbg_cmd_add_module("3d", sizeof(re_cmds)/sizeof(*re_cmds), re_cmds);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void re_debug_init(void) {}
|
||||
|
||||
#endif // DEBUGGER_ENABLED
|
||||
|
||||
+210
-55
@@ -29,6 +29,7 @@
|
||||
|
||||
#define FP16_MIN 6.103516e-5f
|
||||
#define NR_WEIGHTS 4
|
||||
#define DEFAULT_OUTLINE_THICKNESS 0.003
|
||||
|
||||
struct vertex_common {
|
||||
GLfloat pos[3];
|
||||
@@ -40,6 +41,10 @@ struct vertex_light_uv {
|
||||
GLfloat uv[2];
|
||||
};
|
||||
|
||||
struct vertex_color {
|
||||
GLfloat color[4];
|
||||
};
|
||||
|
||||
struct vertex_tangent {
|
||||
GLfloat tangent[4];
|
||||
};
|
||||
@@ -49,6 +54,27 @@ struct vertex_bones {
|
||||
GLfloat bone_weight[NR_WEIGHTS];
|
||||
};
|
||||
|
||||
struct vertex_blend {
|
||||
GLfloat blend_weight;
|
||||
GLfloat blend_uv[2];
|
||||
};
|
||||
|
||||
static bool is_transparent_mesh(const struct pol_mesh *mesh)
|
||||
{
|
||||
if (re_plugin_version == RE_REIGN_PLUGIN)
|
||||
return !(mesh->flags & MESH_SPRITE);
|
||||
else
|
||||
return mesh->flags & MESH_ALPHA;
|
||||
}
|
||||
|
||||
static bool is_transparent_material(const struct pol_material *material)
|
||||
{
|
||||
if (re_plugin_version == RE_REIGN_PLUGIN)
|
||||
return !(material->flags & MATERIAL_SPRITE);
|
||||
else
|
||||
return material->flags & MATERIAL_ALPHA;
|
||||
}
|
||||
|
||||
struct archive_data *RE_get_aar_entry(struct archive *aar, const char *dir, const char *name, const char *ext)
|
||||
{
|
||||
char *path = xmalloc(strlen(dir) + strlen(name) + strlen(ext) + 2);
|
||||
@@ -62,7 +88,6 @@ static GLuint load_texture(struct archive *aar, const char *path, const char *na
|
||||
{
|
||||
struct archive_data *dfile = RE_get_aar_entry(aar, path, name, "");
|
||||
if (!dfile) {
|
||||
WARNING("cannot load texture %s\\%s", path, name);
|
||||
return 0;
|
||||
}
|
||||
struct cg *cg = cg_load_data(dfile);
|
||||
@@ -89,28 +114,64 @@ static GLuint load_texture(struct archive *aar, const char *path, const char *na
|
||||
return texture;
|
||||
}
|
||||
|
||||
static bool init_material(struct material *material, const struct pol_material *m, struct amt *amt, struct archive *aar, const char *path)
|
||||
static GLuint *load_texture_list(struct archive *aar, const char *path, const char *name, int *nr_textures_out, bool *has_alpha_out)
|
||||
{
|
||||
// Load the base texture (e.g. face.png)
|
||||
GLuint tex = load_texture(aar, path, name, has_alpha_out);
|
||||
if (!tex)
|
||||
return NULL;
|
||||
GLuint *textures = xmalloc(10 * sizeof(GLuint));
|
||||
int nr_textures = 0;
|
||||
textures[nr_textures++] = tex;
|
||||
|
||||
// Try to load additional textures (e.g. face[1].png, face[2].png, etc.)
|
||||
const char *ext = strrchr(name, '.');
|
||||
if (ext) {
|
||||
for (;;) {
|
||||
char next_name[100];
|
||||
snprintf(next_name, sizeof(next_name), "%.*s[%d]%s", (int)(ext - name), name, nr_textures, ext);
|
||||
tex = load_texture(aar, path, next_name, NULL);
|
||||
if (!tex)
|
||||
break;
|
||||
textures[nr_textures++] = tex;
|
||||
if (nr_textures % 10 == 0) {
|
||||
textures = xrealloc(textures, (nr_textures + 10) * sizeof(GLuint));
|
||||
}
|
||||
}
|
||||
}
|
||||
*nr_textures_out = nr_textures;
|
||||
return textures;
|
||||
}
|
||||
|
||||
static bool init_material(struct material *material, struct pol_material *m, struct amt *amt, struct archive *aar, const char *path)
|
||||
{
|
||||
material->flags = m->flags;
|
||||
glm_vec2_copy(m->uv_tiling, material->uv_tiling);
|
||||
glm_vec2_one(material->blend_uv_tiling);
|
||||
if (!m->textures[COLOR_MAP]) {
|
||||
WARNING("No color texture");
|
||||
return false;
|
||||
}
|
||||
bool has_alpha;
|
||||
material->color_map = load_texture(aar, path, m->textures[COLOR_MAP], &has_alpha);
|
||||
if (!material->color_map)
|
||||
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])
|
||||
material->alpha_map = load_texture(aar, path, m->textures[ALPHA_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) && !(material->flags & MATERIAL_SPRITE);
|
||||
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;
|
||||
@@ -136,8 +197,10 @@ static bool init_material(struct material *material, const struct pol_material *
|
||||
|
||||
static void destroy_material(struct material *material)
|
||||
{
|
||||
if (material->color_map)
|
||||
glDeleteTextures(1, &material->color_map);
|
||||
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)
|
||||
@@ -146,6 +209,8 @@ static void destroy_material(struct material *material)
|
||||
glDeleteTextures(1, &material->light_map);
|
||||
if (material->normal_map)
|
||||
glDeleteTextures(1, &material->normal_map);
|
||||
if (material->blend_texture)
|
||||
glDeleteTextures(1, &material->blend_texture);
|
||||
}
|
||||
|
||||
static int cmp_by_bone_weight(const void *lhs, const void *rhs)
|
||||
@@ -217,16 +282,22 @@ static void *buf_alloc(uint8_t **ptr, int size)
|
||||
static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_group_index, int material)
|
||||
{
|
||||
bool has_light_map = m->light_uvs && model->materials[material].light_map;
|
||||
bool has_vertex_colors = m->nr_colors > 0 || m->nr_alphas > 0;
|
||||
bool has_normal_map = model->materials[material].normal_map != 0;
|
||||
bool has_bones = !!model->bone_map;
|
||||
bool has_blend = m->blend_weights && model->materials[material].blend_texture;
|
||||
|
||||
GLsizei stride = sizeof(struct vertex_common);
|
||||
if (has_light_map)
|
||||
stride += sizeof(struct vertex_light_uv);
|
||||
if (has_vertex_colors)
|
||||
stride += sizeof(struct vertex_color);
|
||||
if (has_normal_map)
|
||||
stride += sizeof(struct vertex_tangent);
|
||||
if (has_bones)
|
||||
stride += sizeof(struct vertex_bones);
|
||||
if (has_blend)
|
||||
stride += sizeof(struct vertex_blend);
|
||||
|
||||
void *buffer = xmalloc(m->nr_triangles * 3 * stride);
|
||||
uint8_t *ptr = buffer;
|
||||
@@ -249,6 +320,14 @@ static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_
|
||||
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);
|
||||
@@ -269,6 +348,14 @@ static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_
|
||||
}
|
||||
}
|
||||
}
|
||||
if (has_blend) {
|
||||
struct vertex_blend *v_blend = buf_alloc(&ptr, sizeof(struct vertex_blend));
|
||||
v_blend->blend_weight = m->blend_weights[t->blend_weight_index[j]];
|
||||
if (m->blend_uvs)
|
||||
glm_vec2_copy(m->blend_uvs[t->blend_uv_index[j]], v_blend->blend_uv);
|
||||
else
|
||||
glm_vec2_copy(m->uvs[t->uv_index[j]], v_blend->blend_uv);
|
||||
}
|
||||
nr_vertices++;
|
||||
}
|
||||
}
|
||||
@@ -280,8 +367,22 @@ static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_
|
||||
}
|
||||
model->meshes = xrealloc_array(model->meshes, model->nr_meshes, model->nr_meshes + 1, sizeof(struct mesh));
|
||||
struct mesh *mesh = &model->meshes[model->nr_meshes++];
|
||||
mesh->name = xstrdup(m->name);
|
||||
mesh->flags = m->flags;
|
||||
mesh->material = material;
|
||||
if (re_plugin_version == RE_REIGN_PLUGIN)
|
||||
mesh->is_transparent = model->materials[material].is_transparent && is_transparent_mesh(m);
|
||||
else
|
||||
mesh->is_transparent = model->materials[material].is_transparent || is_transparent_mesh(m);
|
||||
if (mesh->is_transparent)
|
||||
model->has_transparent_mesh = true;
|
||||
mesh->outline_color[0] = m->edge_color.r / 255.f;
|
||||
mesh->outline_color[1] = m->edge_color.g / 255.f;
|
||||
mesh->outline_color[2] = m->edge_color.b / 255.f;
|
||||
mesh->outline_thickness = m->edge_size ? m->edge_size : DEFAULT_OUTLINE_THICKNESS;
|
||||
glm_vec2_copy(m->uv_scroll, mesh->uv_scroll);
|
||||
glm_vec3_copy(m->specular_color, mesh->specular_color);
|
||||
mesh->specular_power = m->specular_power;
|
||||
mesh->nr_vertices = nr_vertices;
|
||||
|
||||
glGenVertexArrays(1, &mesh->vao);
|
||||
@@ -305,6 +406,14 @@ static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_
|
||||
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));
|
||||
@@ -325,6 +434,18 @@ static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_
|
||||
glDisableVertexAttribArray(VATTR_BONE_WEIGHT);
|
||||
glVertexAttrib4f(VATTR_BONE_WEIGHT, 0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
if (has_blend) {
|
||||
glEnableVertexAttribArray(VATTR_BLEND_WEIGHT);
|
||||
glVertexAttribPointer(VATTR_BLEND_WEIGHT, 1, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_blend, blend_weight));
|
||||
glEnableVertexAttribArray(VATTR_BLEND_UV);
|
||||
glVertexAttribPointer(VATTR_BLEND_UV, 2, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_blend, blend_uv));
|
||||
base += sizeof(struct vertex_blend);
|
||||
} else {
|
||||
glDisableVertexAttribArray(VATTR_BLEND_WEIGHT);
|
||||
glVertexAttrib1f(VATTR_BLEND_WEIGHT, 0.0);
|
||||
glDisableVertexAttribArray(VATTR_BLEND_UV);
|
||||
glVertexAttrib2f(VATTR_BLEND_UV, 0.0, 0.0);
|
||||
}
|
||||
assert((intptr_t)base == stride);
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, mesh->nr_vertices * stride, buffer, GL_STATIC_DRAW);
|
||||
@@ -337,6 +458,7 @@ static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_
|
||||
|
||||
static void destroy_mesh(struct mesh *mesh)
|
||||
{
|
||||
free(mesh->name);
|
||||
glDeleteVertexArrays(1, &mesh->vao);
|
||||
glDeleteBuffers(1, &mesh->attr_buffer);
|
||||
if (mesh->index_buffer)
|
||||
@@ -367,14 +489,6 @@ static struct bone *add_bone(struct model *model, struct pol *pol, struct pol_bo
|
||||
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;
|
||||
}
|
||||
@@ -416,16 +530,23 @@ struct model *model_load(struct archive *aar, const char *path)
|
||||
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));
|
||||
model->bones = xcalloc_aligned(pol->nr_bones, struct bone);
|
||||
model->bones_by_pol_index = xcalloc(pol->nr_bones, sizeof(struct bone *));
|
||||
for (uint32_t i = 0; i < pol->nr_bones; i++) {
|
||||
add_bone(model, pol, &pol->bones[i]);
|
||||
model->bones_by_pol_index[i] = add_bone(model, pol, &pol->bones[i]);
|
||||
}
|
||||
if (model->nr_bones != (int)pol->nr_bones)
|
||||
ERROR("%s: Broken bone data", model->path);
|
||||
@@ -448,21 +569,39 @@ struct model *model_load(struct archive *aar, const char *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);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < model->nr_materials; i++) {
|
||||
if (model->materials[i].is_transparent) {
|
||||
model->has_transparent_material = true;
|
||||
break;
|
||||
struct pol_material_group *child = &pol->materials[i].children[j];
|
||||
if (child->nr_children >= 2) {
|
||||
// Group node: texture blending (base + blend)
|
||||
init_material(&model->materials[material_offsets[i] + j],
|
||||
&child->children[0].m, amt, aar, path);
|
||||
if (child->children[1].m.textures[COLOR_MAP]) {
|
||||
model->materials[material_offsets[i] + j].blend_texture =
|
||||
load_texture(aar, path, child->children[1].m.textures[COLOR_MAP], NULL);
|
||||
glm_vec2_copy(child->children[1].m.uv_tiling,
|
||||
model->materials[material_offsets[i] + j].blend_uv_tiling);
|
||||
}
|
||||
} else {
|
||||
init_material(&model->materials[material_offsets[i] + j],
|
||||
&child->m, amt, aar, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Meshes
|
||||
struct pol_mesh **hd_meshes = xmalloc(pol->nr_meshes * sizeof(*hd_meshes));
|
||||
int nr_hd_meshes = 0;
|
||||
for (uint32_t i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!pol->meshes[i])
|
||||
continue;
|
||||
if (!strcmp(pol->meshes[i]->name, "collision")) {
|
||||
if (model->collider)
|
||||
WARNING("multiple collision meshes");
|
||||
else
|
||||
model->collider = collider_create(pol->meshes[i]);
|
||||
continue;
|
||||
}
|
||||
if (pol->meshes[i]->flags & MESH_HEIGHT_DETECTION)
|
||||
hd_meshes[nr_hd_meshes++] = pol->meshes[i];
|
||||
struct pol_material_group *mg = &pol->materials[pol->meshes[i]->material];
|
||||
int m_off = material_offsets[pol->meshes[i]->material];
|
||||
if (mg->nr_children == 0) {
|
||||
@@ -474,6 +613,10 @@ struct model *model_load(struct archive *aar, const char *path)
|
||||
}
|
||||
}
|
||||
|
||||
if (!model->collider && nr_hd_meshes > 0)
|
||||
model->collider = collider_create_raycast(hd_meshes, nr_hd_meshes);
|
||||
free(hd_meshes);
|
||||
|
||||
pol_compute_aabb(pol, model->aabb);
|
||||
|
||||
free(material_offsets);
|
||||
@@ -485,6 +628,9 @@ struct model *model_load(struct archive *aar, const char *path)
|
||||
|
||||
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);
|
||||
@@ -495,11 +641,10 @@ void model_free(struct model *model)
|
||||
|
||||
for (int i = 0; i < model->nr_bones; i++)
|
||||
destroy_bone(&model->bones[i]);
|
||||
free(model->bones);
|
||||
xfree_aligned(model->bones);
|
||||
free(model->bones_by_pol_index);
|
||||
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);
|
||||
@@ -594,52 +739,62 @@ struct model *model_create_sphere(int r, int g, int b, int a)
|
||||
model->materials = xcalloc(1, sizeof(struct material));
|
||||
struct material *material = &model->materials[0];
|
||||
material->is_transparent = true;
|
||||
glGenTextures(1, &material->color_map);
|
||||
glBindTexture(GL_TEXTURE_2D, material->color_map);
|
||||
glm_vec2_one(material->uv_tiling);
|
||||
glm_vec2_one(material->blend_uv_tiling);
|
||||
material->color_maps = xmalloc(sizeof(GLuint));
|
||||
material->nr_color_maps = 1;
|
||||
glGenTextures(1, material->color_maps);
|
||||
glBindTexture(GL_TEXTURE_2D, material->color_maps[0]);
|
||||
uint8_t pixel[4] = {r, g, b, a};
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
model->has_transparent_material = true;
|
||||
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 *dfile = RE_get_aar_entry(aar, model->path, name, ".MOT");
|
||||
if (!dfile) {
|
||||
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(dfile->data, dfile->size, name);
|
||||
struct mot *mot = mot_parse(mot_file->data, mot_file->size, name);
|
||||
if (!mot) {
|
||||
WARNING("%s: parse error", dfile->name);
|
||||
archive_free_data(dfile);
|
||||
WARNING("%s: parse error", mot_file->name);
|
||||
archive_free_data(mot_file);
|
||||
return NULL;
|
||||
}
|
||||
archive_free_data(dfile);
|
||||
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].
|
||||
// accessed by mot->motions[i]. MOT bones are applied to POL bones purely
|
||||
// by array index; bone name and bone id are never used for matching.
|
||||
struct mot_bone **reordered = xmalloc(mot->nr_bones * sizeof(struct mot_bone *));
|
||||
for (uint32_t i = 0; i < mot->nr_bones; i++) {
|
||||
// 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;
|
||||
reordered[model->bones_by_pol_index[i]->index] = mot->motions[i];
|
||||
}
|
||||
memcpy(mot->motions, reordered, mot->nr_bones * sizeof(struct mot_bone *));
|
||||
free(reordered);
|
||||
|
||||
// Load optional sidecar file.
|
||||
if (re_plugin_version <= RE_TAPIR_PLUGIN) {
|
||||
struct archive_data *txa_file = RE_get_aar_entry(aar, model->path, name, ".txa");
|
||||
if (txa_file) {
|
||||
txa_load(txa_file->data, txa_file->size, mot);
|
||||
archive_free_data(txa_file);
|
||||
}
|
||||
} else {
|
||||
struct archive_data *mpr_file = RE_get_aar_entry(aar, model->path, name, ".mpr");
|
||||
if (mpr_file) {
|
||||
mot->mpr = mpr_load(mpr_file->data, mpr_file->size, model);
|
||||
archive_free_data(mpr_file);
|
||||
}
|
||||
}
|
||||
qsort(mot->motions, mot->nr_bones, sizeof(struct mot_bone *), cmp_motions_by_bone_id);
|
||||
|
||||
return mot;
|
||||
}
|
||||
|
||||
+294
@@ -0,0 +1,294 @@
|
||||
/* Copyright (C) 2026 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#include "system4.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
|
||||
// .mpr parser.
|
||||
|
||||
static int resolve_mesh_index(struct model *model, const char *name)
|
||||
{
|
||||
for (int i = 0; i < model->nr_meshes; i++) {
|
||||
if (!strcmp(model->meshes[i].name, name))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void append_float_key(struct mpr_track_set *ts, int frame, float v)
|
||||
{
|
||||
ts->mul_alpha = xrealloc(ts->mul_alpha, (ts->nr_mul_alpha + 1) * sizeof(struct mpr_float_key));
|
||||
ts->mul_alpha[ts->nr_mul_alpha].frame = frame;
|
||||
ts->mul_alpha[ts->nr_mul_alpha].v = v;
|
||||
ts->nr_mul_alpha++;
|
||||
}
|
||||
|
||||
static void append_vec3_key(struct mpr_vec3_key **arr, int *nr, int frame, float r, float g, float b)
|
||||
{
|
||||
*arr = xrealloc(*arr, (*nr + 1) * sizeof(struct mpr_vec3_key));
|
||||
(*arr)[*nr].frame = frame;
|
||||
(*arr)[*nr].v[0] = r;
|
||||
(*arr)[*nr].v[1] = g;
|
||||
(*arr)[*nr].v[2] = b;
|
||||
(*nr)++;
|
||||
}
|
||||
|
||||
static void append_int_key(struct mpr_int_key **arr, int *nr, int frame, int v)
|
||||
{
|
||||
*arr = xrealloc(*arr, (*nr + 1) * sizeof(struct mpr_int_key));
|
||||
(*arr)[*nr].frame = frame;
|
||||
(*arr)[*nr].v = v;
|
||||
(*nr)++;
|
||||
}
|
||||
|
||||
// All mpr_*_key structs have `int frame` as their first field.
|
||||
static int cmp_key_by_frame(const void *a, const void *b)
|
||||
{
|
||||
return *(const int *)a - *(const int *)b;
|
||||
}
|
||||
|
||||
static void sort_track_set(struct mpr_track_set *ts)
|
||||
{
|
||||
if (ts->nr_mul_alpha)
|
||||
qsort(ts->mul_alpha, ts->nr_mul_alpha, sizeof(struct mpr_float_key), cmp_key_by_frame);
|
||||
if (ts->nr_mul_diffuse)
|
||||
qsort(ts->mul_diffuse, ts->nr_mul_diffuse, sizeof(struct mpr_vec3_key), cmp_key_by_frame);
|
||||
if (ts->nr_add_ambient)
|
||||
qsort(ts->add_ambient, ts->nr_add_ambient, sizeof(struct mpr_vec3_key), cmp_key_by_frame);
|
||||
if (ts->nr_texture_anime)
|
||||
qsort(ts->texture_anime, ts->nr_texture_anime, sizeof(struct mpr_int_key), cmp_key_by_frame);
|
||||
}
|
||||
|
||||
struct mpr *mpr_load(uint8_t *data, size_t size, struct model *model)
|
||||
{
|
||||
struct mpr *mpr = xcalloc(1, sizeof(struct mpr));
|
||||
mpr->nr_meshes = model->nr_meshes;
|
||||
mpr->mesh_tracks = xcalloc(model->nr_meshes, sizeof(struct mpr_track_set *));
|
||||
struct mpr_track_set *current = NULL; // current Mesh-scope track set
|
||||
|
||||
while (size > 0) {
|
||||
char line[200];
|
||||
uint8_t *nl = memchr(data, '\n', size);
|
||||
if (nl)
|
||||
nl++;
|
||||
else
|
||||
nl = data + size;
|
||||
int copylen = min(nl - data, sizeof(line) - 1);
|
||||
memcpy(line, (char *)data, copylen);
|
||||
line[copylen] = '\0';
|
||||
size -= nl - data;
|
||||
data = nl;
|
||||
|
||||
char s[200];
|
||||
int frame, ti;
|
||||
float a, r, g, b;
|
||||
if (sscanf(line, "Mesh = \"%[^\"]\"", s) == 1) {
|
||||
int mi = resolve_mesh_index(model, s);
|
||||
if (mi < 0) {
|
||||
WARNING("mpr: unknown mesh \"%s\"", s);
|
||||
current = NULL;
|
||||
} else {
|
||||
if (!mpr->mesh_tracks[mi])
|
||||
mpr->mesh_tracks[mi] = xcalloc(1, sizeof(struct mpr_track_set));
|
||||
current = mpr->mesh_tracks[mi];
|
||||
}
|
||||
} else if (sscanf(line, "MeshMulAlpha = ( %d , %f )", &frame, &a) == 2) {
|
||||
if (current)
|
||||
append_float_key(current, frame, a);
|
||||
else
|
||||
WARNING("mpr: MeshMulAlpha outside Mesh scope");
|
||||
} else if (sscanf(line, "MeshMulDiffuse = ( %d , %f , %f , %f )", &frame, &r, &g, &b) == 4) {
|
||||
if (current)
|
||||
append_vec3_key(¤t->mul_diffuse, ¤t->nr_mul_diffuse, frame, r, g, b);
|
||||
else
|
||||
WARNING("mpr: MeshMulDiffuse outside Mesh scope");
|
||||
} else if (sscanf(line, "MeshAddAmbient = ( %d , %f , %f , %f )", &frame, &r, &g, &b) == 4) {
|
||||
if (current)
|
||||
append_vec3_key(¤t->add_ambient, ¤t->nr_add_ambient, frame, r, g, b);
|
||||
else
|
||||
WARNING("mpr: MeshAddAmbient outside Mesh scope");
|
||||
} else if (sscanf(line, "MeshTextureAnime = ( %d , %d )", &frame, &ti) == 2) {
|
||||
if (current)
|
||||
append_int_key(¤t->texture_anime, ¤t->nr_texture_anime, frame, ti);
|
||||
else
|
||||
WARNING("mpr: MeshTextureAnime outside Mesh scope");
|
||||
} else if (sscanf(line, "ObjectMulAlpha = ( %d , %f )", &frame, &a) == 2) {
|
||||
append_float_key(&mpr->object, frame, a);
|
||||
} else if (sscanf(line, "ObjectMulDiffuse = ( %d , %f , %f , %f )", &frame, &r, &g, &b) == 4) {
|
||||
append_vec3_key(&mpr->object.mul_diffuse, &mpr->object.nr_mul_diffuse, frame, r, g, b);
|
||||
} else if (sscanf(line, "ObjectAddAmbient = ( %d , %f , %f , %f )", &frame, &r, &g, &b) == 4) {
|
||||
append_vec3_key(&mpr->object.add_ambient, &mpr->object.nr_add_ambient, frame, r, g, b);
|
||||
} else if (strchr(line, '=')) {
|
||||
WARNING("mpr: unknown field: %s", line);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < mpr->nr_meshes; i++) {
|
||||
if (!mpr->mesh_tracks[i])
|
||||
continue;
|
||||
sort_track_set(mpr->mesh_tracks[i]);
|
||||
if (mpr->mesh_tracks[i]->nr_mul_alpha > 0)
|
||||
mpr->has_mesh_alpha = true;
|
||||
}
|
||||
sort_track_set(&mpr->object);
|
||||
return mpr;
|
||||
}
|
||||
|
||||
static void destroy_track_set(struct mpr_track_set *ts)
|
||||
{
|
||||
free(ts->mul_alpha);
|
||||
free(ts->mul_diffuse);
|
||||
free(ts->add_ambient);
|
||||
free(ts->texture_anime);
|
||||
}
|
||||
|
||||
void mpr_free(struct mpr *mpr)
|
||||
{
|
||||
if (!mpr)
|
||||
return;
|
||||
for (int i = 0; i < mpr->nr_meshes; i++) {
|
||||
if (mpr->mesh_tracks[i]) {
|
||||
destroy_track_set(mpr->mesh_tracks[i]);
|
||||
free(mpr->mesh_tracks[i]);
|
||||
}
|
||||
}
|
||||
free(mpr->mesh_tracks);
|
||||
destroy_track_set(&mpr->object);
|
||||
free(mpr);
|
||||
}
|
||||
|
||||
// Keyframe evaluation.
|
||||
|
||||
// Returns the largest index i with keys[i].frame <= frame.
|
||||
// Returns -1 if frame < keys[0].frame.
|
||||
#define DEFINE_FIND_KEY(NAME, KEY_TYPE) \
|
||||
static int NAME(const KEY_TYPE *keys, int nr, float frame) \
|
||||
{ \
|
||||
if (frame < (float)keys[0].frame) \
|
||||
return -1; \
|
||||
int lo = 0, hi = nr - 1; \
|
||||
while (lo < hi) { \
|
||||
int mid = (lo + hi + 1) / 2; \
|
||||
if ((float)keys[mid].frame <= frame) \
|
||||
lo = mid; \
|
||||
else \
|
||||
hi = mid - 1; \
|
||||
} \
|
||||
return lo; \
|
||||
}
|
||||
|
||||
DEFINE_FIND_KEY(find_float_key, struct mpr_float_key)
|
||||
DEFINE_FIND_KEY(find_vec3_key, struct mpr_vec3_key)
|
||||
DEFINE_FIND_KEY(find_int_key, struct mpr_int_key)
|
||||
|
||||
static float eval_float(const struct mpr_float_key *keys, int nr, float frame, float fallback)
|
||||
{
|
||||
if (nr == 0)
|
||||
return fallback;
|
||||
int i = find_float_key(keys, nr, frame);
|
||||
if (i < 0)
|
||||
return keys[0].v;
|
||||
if (i >= nr - 1)
|
||||
return keys[nr - 1].v;
|
||||
float t = (frame - keys[i].frame) / (float)(keys[i + 1].frame - keys[i].frame);
|
||||
return glm_lerp(keys[i].v, keys[i + 1].v, t);
|
||||
}
|
||||
|
||||
static void eval_vec3(const struct mpr_vec3_key *keys, int nr, float frame, const vec3 fallback, vec3 out)
|
||||
{
|
||||
if (nr == 0) {
|
||||
glm_vec3_copy((float *)fallback, out);
|
||||
return;
|
||||
}
|
||||
int i = find_vec3_key(keys, nr, frame);
|
||||
if (i < 0) {
|
||||
glm_vec3_copy((float *)keys[0].v, out);
|
||||
return;
|
||||
}
|
||||
if (i >= nr - 1) {
|
||||
glm_vec3_copy((float *)keys[nr - 1].v, out);
|
||||
return;
|
||||
}
|
||||
float t = (frame - keys[i].frame) / (float)(keys[i + 1].frame - keys[i].frame);
|
||||
glm_vec3_lerp((float *)keys[i].v, (float *)keys[i + 1].v, t, out);
|
||||
}
|
||||
|
||||
static int eval_int(const struct mpr_int_key *keys, int nr, float frame, int fallback)
|
||||
{
|
||||
if (nr == 0)
|
||||
return fallback;
|
||||
int i = find_int_key(keys, nr, frame);
|
||||
if (i < 0)
|
||||
return keys[0].v;
|
||||
return keys[i].v; // step
|
||||
}
|
||||
|
||||
// Combine inst defaults with .mpr Object-scope keyframes.
|
||||
void mpr_evaluate_object(const struct mpr *mpr, float frame,
|
||||
struct RE_instance *inst, struct mpr_modulation *out)
|
||||
{
|
||||
out->alpha = inst->alpha;
|
||||
glm_vec3_copy(inst->ambient, out->ambient);
|
||||
glm_vec3_copy(inst->diffuse, out->diffuse);
|
||||
if (!mpr)
|
||||
return;
|
||||
out->alpha *= eval_float(mpr->object.mul_alpha, mpr->object.nr_mul_alpha, frame, 1.f);
|
||||
vec3 add;
|
||||
eval_vec3(mpr->object.add_ambient, mpr->object.nr_add_ambient, frame, (vec3){0,0,0}, add);
|
||||
glm_vec3_add(out->ambient, add, out->ambient);
|
||||
eval_vec3(mpr->object.mul_diffuse, mpr->object.nr_mul_diffuse, frame, (vec3){1,1,1}, out->diffuse);
|
||||
}
|
||||
|
||||
// Combine the object-scope values with .mpr Mesh-scope keyframes for one mesh.
|
||||
void mpr_evaluate_mesh(const struct mpr_track_set *mt, float frame,
|
||||
const struct mpr_modulation *obj, struct mpr_modulation *out)
|
||||
{
|
||||
*out = *obj;
|
||||
if (!mt)
|
||||
return;
|
||||
out->alpha *= eval_float(mt->mul_alpha, mt->nr_mul_alpha, frame, 1.f);
|
||||
vec3 add;
|
||||
eval_vec3(mt->add_ambient, mt->nr_add_ambient, frame, (vec3){0,0,0}, add);
|
||||
glm_vec3_add(out->ambient, add, out->ambient);
|
||||
vec3 mul;
|
||||
eval_vec3(mt->mul_diffuse, mt->nr_mul_diffuse, frame, (vec3){1,1,1}, mul);
|
||||
glm_vec3_mul(out->diffuse, mul, out->diffuse);
|
||||
}
|
||||
|
||||
// MeshTextureAnime writes to the shared material slot, so all meshes
|
||||
// referencing the same material pick up the animation even if .mpr only names
|
||||
// one of them. Caller supplies the output buffer sized to model->nr_materials.
|
||||
void mpr_build_mat_tex_index(const struct model *model, const struct RE_instance *inst,
|
||||
const struct mpr *mpr, float frame, int *mat_tex_index)
|
||||
{
|
||||
for (int m = 0; m < model->nr_materials; m++)
|
||||
mat_tex_index[m] = inst->texture_animation_index;
|
||||
if (!mpr)
|
||||
return;
|
||||
for (int i = 0; i < mpr->nr_meshes; i++) {
|
||||
const struct mpr_track_set *t = mpr->mesh_tracks[i];
|
||||
if (!t || t->nr_texture_anime == 0)
|
||||
continue;
|
||||
int mat = model->meshes[i].material;
|
||||
mat_tex_index[mat] = eval_int(t->texture_anime, t->nr_texture_anime, frame, mat_tex_index[mat]);
|
||||
}
|
||||
}
|
||||
+346
-34
@@ -14,6 +14,7 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cglm/cglm.h>
|
||||
@@ -64,20 +65,27 @@ 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)
|
||||
static void parse_textures(struct buffer *r, int pol_version, struct pol_material *m)
|
||||
{
|
||||
m->name = read_cstring(r);
|
||||
m->flags = parse_material_attributes(m->name);
|
||||
|
||||
glm_vec2_one(m->uv_tiling);
|
||||
int nr_textures = buffer_read_int32(r);
|
||||
for (int i = 0; i < nr_textures; i++) {
|
||||
char *filename = read_cstring(r);
|
||||
int type = buffer_read_int32(r);
|
||||
vec2 uv_tiling = { 1.0f, 1.0f };
|
||||
if (pol_version >= 3) {
|
||||
uv_tiling[0] = buffer_read_float(r);
|
||||
uv_tiling[1] = buffer_read_float(r);
|
||||
}
|
||||
if ((unsigned)type < MAX_TEXTURE_TYPE) {
|
||||
m->textures[type] = filename;
|
||||
if (type == COLOR_MAP)
|
||||
glm_vec2_copy(uv_tiling, m->uv_tiling);
|
||||
} else {
|
||||
WARNING("invalid texture type %d", type);
|
||||
free(filename);
|
||||
@@ -92,29 +100,60 @@ static void destroy_material(struct pol_material *m)
|
||||
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]);
|
||||
destroy_material_group(&mg->children[i]);
|
||||
free(mg->children);
|
||||
}
|
||||
}
|
||||
|
||||
// Material tree layout:
|
||||
// level 0: top-level material per pol->materials[]. Either a textured leaf
|
||||
// or a group containing sub-materials, never both.
|
||||
// level 1: sub-material. Either a textured leaf or, in v4 only, a "blend
|
||||
// group" (is_group=1) with exactly 2 leaf children.
|
||||
// level 2: leaf children of a v4 blend group; cannot themselves be a group.
|
||||
static void parse_material_group(struct buffer *r, int pol_version,
|
||||
struct pol_material_group *mg, int level)
|
||||
{
|
||||
mg->m.name = read_cstring(r);
|
||||
mg->m.flags = parse_material_attributes(mg->m.name);
|
||||
|
||||
bool is_group = false;
|
||||
if (pol_version >= 4)
|
||||
is_group = buffer_read_int32(r);
|
||||
|
||||
if (is_group) {
|
||||
if (level >= 2)
|
||||
ERROR("material group nesting too deep");
|
||||
uint32_t uk = buffer_read_int32(r);
|
||||
if (uk != 0)
|
||||
ERROR("unexpected nonzero value in material group: %u", uk);
|
||||
|
||||
mg->nr_children = buffer_read_int32(r);
|
||||
if (level == 1 && mg->nr_children != 2)
|
||||
ERROR("blend group must have 2 children, got %u", mg->nr_children);
|
||||
} else {
|
||||
parse_textures(r, pol_version, &mg->m);
|
||||
|
||||
if (pol_version >= 4) {
|
||||
uint32_t uk = buffer_read_int32(r);
|
||||
if (uk != 0)
|
||||
ERROR("unexpected nonzero value in material leaf: %u", uk);
|
||||
} else if (level == 0) {
|
||||
mg->nr_children = buffer_read_int32(r);
|
||||
}
|
||||
}
|
||||
if (mg->nr_children > 0) {
|
||||
mg->children = xcalloc(mg->nr_children, sizeof(struct pol_material_group));
|
||||
for (uint32_t i = 0; i < mg->nr_children; i++)
|
||||
parse_material_group(r, pol_version, &mg->children[i], level + 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_vertex(struct buffer *r, int pol_version, struct pol_vertex *v)
|
||||
{
|
||||
read_position(r, v->pos);
|
||||
@@ -134,7 +173,7 @@ static void destroy_vertex(struct pol_vertex *v)
|
||||
free(v->weights);
|
||||
}
|
||||
|
||||
static void parse_triangle(struct buffer *r, struct pol_mesh *mesh, int triangle_index, int unknowns_length, const struct pol_material_group *mg)
|
||||
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);
|
||||
@@ -148,8 +187,25 @@ static void parse_triangle(struct buffer *r, struct pol_mesh *mesh, int triangle
|
||||
t->light_uv_index[1] = buffer_read_int32(r) - mesh->nr_uvs;
|
||||
t->light_uv_index[2] = buffer_read_int32(r) - mesh->nr_uvs;
|
||||
}
|
||||
if (mesh->blend_uvs) {
|
||||
t->blend_uv_index[0] = buffer_read_int32(r) - mesh->nr_uvs - mesh->nr_light_uvs;
|
||||
t->blend_uv_index[1] = buffer_read_int32(r) - mesh->nr_uvs - mesh->nr_light_uvs;
|
||||
t->blend_uv_index[2] = buffer_read_int32(r) - mesh->nr_uvs - mesh->nr_light_uvs;
|
||||
}
|
||||
|
||||
buffer_skip(r, unknowns_length);
|
||||
t->color_index[0] = buffer_read_int32(r);
|
||||
t->color_index[1] = buffer_read_int32(r);
|
||||
t->color_index[2] = buffer_read_int32(r);
|
||||
if (mesh->alphas) {
|
||||
t->alpha_index[0] = buffer_read_int32(r);
|
||||
t->alpha_index[1] = buffer_read_int32(r);
|
||||
t->alpha_index[2] = buffer_read_int32(r);
|
||||
}
|
||||
if (mesh->blend_weights) {
|
||||
t->blend_weight_index[0] = buffer_read_int32(r);
|
||||
t->blend_weight_index[1] = buffer_read_int32(r);
|
||||
t->blend_weight_index[2] = buffer_read_int32(r);
|
||||
}
|
||||
|
||||
read_direction(r, t->normals[0]);
|
||||
read_direction(r, t->normals[1]);
|
||||
@@ -172,6 +228,8 @@ static uint32_t parse_mesh_attributes(const char *name)
|
||||
flags |= MESH_BOTH;
|
||||
if (strstr(name, "(sprite)"))
|
||||
flags |= MESH_SPRITE;
|
||||
if (strstr(name, "(alpha)"))
|
||||
flags |= MESH_ALPHA;
|
||||
return flags;
|
||||
}
|
||||
|
||||
@@ -207,6 +265,7 @@ static struct pol_mesh *parse_mesh(struct buffer *r, const struct pol *pol)
|
||||
|
||||
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);
|
||||
@@ -214,24 +273,58 @@ static struct pol_mesh *parse_mesh(struct buffer *r, const struct pol *pol)
|
||||
}
|
||||
}
|
||||
|
||||
int triangle_unknowns_length = 12;
|
||||
if (pol->version == 1) {
|
||||
int nr_unknown_vecs = buffer_read_int32(r);
|
||||
buffer_skip(r, nr_unknown_vecs * 12);
|
||||
} else {
|
||||
int nr_unknown_ints = buffer_read_int32(r);
|
||||
buffer_skip(r, nr_unknown_ints * 4);
|
||||
int nr_unknown_bytes = buffer_read_int32(r);
|
||||
if (nr_unknown_bytes) {
|
||||
buffer_skip(r, nr_unknown_bytes);
|
||||
triangle_unknowns_length += 12;
|
||||
if (pol->version >= 4) {
|
||||
mesh->nr_blend_uvs = buffer_read_int32(r);
|
||||
if (mesh->nr_blend_uvs > 0) {
|
||||
mesh->blend_uvs = xcalloc(mesh->nr_blend_uvs, sizeof(vec2));
|
||||
for (uint32_t i = 0; i < mesh->nr_blend_uvs; i++) {
|
||||
mesh->blend_uvs[i][0] = buffer_read_float(r);
|
||||
mesh->blend_uvs[i][1] = buffer_read_float(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mesh->nr_colors = buffer_read_int32(r);
|
||||
if (mesh->nr_colors > 0) {
|
||||
mesh->colors = xcalloc(mesh->nr_colors, sizeof(vec3));
|
||||
for (uint32_t i = 0; i < mesh->nr_colors; i++) {
|
||||
if (pol->version == 1) {
|
||||
mesh->colors[i][0] = buffer_read_float(r);
|
||||
mesh->colors[i][1] = buffer_read_float(r);
|
||||
mesh->colors[i][2] = buffer_read_float(r);
|
||||
} else {
|
||||
mesh->colors[i][0] = buffer_read_u8(r) / 255.f;
|
||||
mesh->colors[i][1] = buffer_read_u8(r) / 255.f;
|
||||
mesh->colors[i][2] = buffer_read_u8(r) / 255.f;
|
||||
buffer_skip(r, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pol->version >= 2) {
|
||||
mesh->nr_alphas = buffer_read_int32(r);
|
||||
if (mesh->nr_alphas > 0) {
|
||||
mesh->alphas = xcalloc(mesh->nr_alphas, sizeof(float));
|
||||
for (uint32_t i = 0; i < mesh->nr_alphas; i++) {
|
||||
mesh->alphas[i] = buffer_read_u8(r) / 255.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pol->version >= 4) {
|
||||
mesh->nr_blend_weights = buffer_read_int32(r);
|
||||
if (mesh->nr_blend_weights > 0) {
|
||||
mesh->blend_weights = xcalloc(mesh->nr_blend_weights, sizeof(float));
|
||||
for (uint32_t i = 0; i < mesh->nr_blend_weights; i++) {
|
||||
mesh->blend_weights[i] = buffer_read_u8(r) / 255.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mesh->nr_triangles = buffer_read_int32(r);
|
||||
mesh->triangles = xcalloc(mesh->nr_triangles, sizeof(struct pol_triangle));
|
||||
for (uint32_t i = 0; i < mesh->nr_triangles; i++) {
|
||||
parse_triangle(r, mesh, i, triangle_unknowns_length, &pol->materials[mesh->material]);
|
||||
parse_triangle(r, mesh, i, &pol->materials[mesh->material]);
|
||||
}
|
||||
|
||||
if (pol->version == 1) {
|
||||
@@ -254,6 +347,10 @@ static void free_mesh(struct pol_mesh *mesh)
|
||||
free(mesh->vertices);
|
||||
free(mesh->uvs);
|
||||
free(mesh->light_uvs);
|
||||
free(mesh->blend_uvs);
|
||||
free(mesh->colors);
|
||||
free(mesh->alphas);
|
||||
free(mesh->blend_weights);
|
||||
free(mesh->triangles);
|
||||
free(mesh);
|
||||
}
|
||||
@@ -282,7 +379,7 @@ struct pol *pol_parse(uint8_t *data, size_t size)
|
||||
|
||||
struct pol *pol = xcalloc(1, sizeof(struct pol));
|
||||
pol->version = buffer_read_int32(&r);
|
||||
if (pol->version != 1 && pol->version != 2) {
|
||||
if (pol->version != 1 && pol->version != 2 && pol->version != 4) {
|
||||
WARNING("unknown POL version: %d", pol->version);
|
||||
free(pol);
|
||||
return NULL;
|
||||
@@ -290,7 +387,7 @@ struct pol *pol_parse(uint8_t *data, size_t size)
|
||||
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]);
|
||||
parse_material_group(&r, pol->version, &pol->materials[i], 0);
|
||||
}
|
||||
|
||||
pol->nr_meshes = buffer_read_int32(&r);
|
||||
@@ -397,6 +494,10 @@ void mot_free(struct mot *mot)
|
||||
free(mot->motions[i]->name);
|
||||
free(mot->motions[i]);
|
||||
}
|
||||
if (mot->texture_indices) {
|
||||
free(mot->texture_indices);
|
||||
}
|
||||
mpr_free(mot->mpr);
|
||||
free(mot);
|
||||
}
|
||||
|
||||
@@ -457,3 +558,214 @@ struct amt_material *amt_find_material(struct amt *amt, const char *name)
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void opr_load(uint8_t *data, size_t size, struct pol *pol)
|
||||
{
|
||||
bool *selected = xmalloc(pol->nr_meshes * sizeof(bool));
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
selected[i] = !!pol->meshes[i];
|
||||
}
|
||||
while (size > 0) {
|
||||
char line[200];
|
||||
uint8_t *nl = memchr(data, '\n', size);
|
||||
if (nl)
|
||||
nl++;
|
||||
else
|
||||
nl = data + size;
|
||||
int copylen = min(nl - data, sizeof(line) - 1);
|
||||
memcpy(line, (char *)data, copylen);
|
||||
line[copylen] = '\0';
|
||||
size -= nl - data;
|
||||
data = nl;
|
||||
|
||||
char s[200];
|
||||
int i1, i2, i3;
|
||||
float f1, f2, f3;
|
||||
if (sscanf(line, "Mesh = \"%[^\"]\"", s) == 1) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
selected[i] = pol->meshes[i] && !strcmp(pol->meshes[i]->name, s);
|
||||
}
|
||||
} else if (sscanf(line, "MeshPart = \"%[^\"]\"", s) == 1) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
selected[i] = pol->meshes[i] && strstr(pol->meshes[i]->name, s);
|
||||
}
|
||||
} else if (sscanf(line, "BlendMode = %s", s) == 1) {
|
||||
if (!strcmp(s, "Add")) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_BLEND_ADDITIVE;
|
||||
}
|
||||
} else {
|
||||
WARNING("unknown BlendMode: %s", s);
|
||||
}
|
||||
} else if (sscanf(line, "Edge = %d", &i1) == 1) {
|
||||
if (i1 == 0) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_NO_EDGE;
|
||||
}
|
||||
} else {
|
||||
WARNING("invalid Edge value: %d", i1);
|
||||
}
|
||||
} else if (sscanf(line, "EdgeColor = ( %d , %d , %d )", &i1, &i2, &i3) == 3) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->edge_color = COLOR(i1, i2, i3, 255);
|
||||
}
|
||||
} else if (sscanf(line, "EdgeSize = %f", &f1) == 1) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->edge_size = f1;
|
||||
}
|
||||
} else if (sscanf(line, "HeightDetection = %s", s) == 1) {
|
||||
if (!strcmp(s, "true")) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_HEIGHT_DETECTION;
|
||||
}
|
||||
} else if (!strcmp(s, "false")) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_NO_HEIGHT_DETECTION;
|
||||
}
|
||||
} else {
|
||||
WARNING("invalid HeightDetection: %s", s);
|
||||
}
|
||||
} else if (sscanf(line, "DrawShadow = %s", s) == 1) {
|
||||
if (!strcmp(s, "false")) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_NO_DRAWSHADOW;
|
||||
}
|
||||
} else if (strcmp(s, "true")) {
|
||||
WARNING("invalid DrawShadow: %s", s);
|
||||
}
|
||||
} else if (sscanf(line, "ZWrite = %s", s) == 1) {
|
||||
if (!strcmp(s, "false")) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_NO_ZWRITE;
|
||||
}
|
||||
} else if (strcmp(s, "true")) {
|
||||
WARNING("invalid ZWrite: %s", s);
|
||||
}
|
||||
} else if (sscanf(line, "UVScroll = ( %f , %f )", &f1, &f2) == 2) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->uv_scroll[0] = f1;
|
||||
pol->meshes[i]->uv_scroll[1] = f2;
|
||||
}
|
||||
} else if (sscanf(line, "SpecularColor = ( %f , %f , %f )", &f1, &f2, &f3) == 3) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_HAS_SPECULAR_COLOR;
|
||||
pol->meshes[i]->specular_color[0] = f1;
|
||||
pol->meshes[i]->specular_color[1] = f2;
|
||||
pol->meshes[i]->specular_color[2] = f3;
|
||||
}
|
||||
} else if (sscanf(line, "SpecularPower = %f", &f1) == 1) {
|
||||
for (int i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!selected[i]) continue;
|
||||
pol->meshes[i]->flags |= MESH_HAS_SPECULAR_POWER;
|
||||
pol->meshes[i]->specular_power = f1;
|
||||
}
|
||||
} else if (sscanf(line, "ParallaxScale = %f", &f1) == 1) {
|
||||
// parallax mapping is not implemented
|
||||
} else if (sscanf(line, "ReliefScale = %f", &f1) == 1) {
|
||||
// relief mapping is not implemented
|
||||
} else if (sscanf(line, "MeshCombinable = %s", s) == 1) {
|
||||
if (strcmp(s, "true") && strcmp(s, "false"))
|
||||
WARNING("invalid MeshCombinable: %s", s);
|
||||
} else if (strchr(line, '=')) {
|
||||
WARNING("unknown field: %s", line);
|
||||
}
|
||||
}
|
||||
free(selected);
|
||||
}
|
||||
|
||||
void txa_load(uint8_t *data, size_t size, struct mot *mot)
|
||||
{
|
||||
uint32_t count = 0;
|
||||
uint32_t *indices = xmalloc(100 * sizeof(uint32_t));
|
||||
const char *p = (const char *)data;
|
||||
while (p < (const char *)data + size) {
|
||||
if (isdigit(*p)) {
|
||||
uint32_t n = 0;
|
||||
while (p < (const char *)data + size && isdigit(*p)) {
|
||||
n = n * 10 + (*p - '0');
|
||||
p++;
|
||||
}
|
||||
indices[count++] = n;
|
||||
if (count % 100 == 0) {
|
||||
indices = xrealloc(indices, (count + 100) * sizeof(uint32_t));
|
||||
}
|
||||
} else if (isspace(*p)) {
|
||||
p++;
|
||||
} else {
|
||||
WARNING("invalid character in txa file: '%c'", *p);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
mot->nr_texture_indices = count;
|
||||
mot->texture_indices = indices;
|
||||
}
|
||||
|
||||
void lit_reset(float *out)
|
||||
{
|
||||
static const float lit_param_defaults[RE_NR_LIGHT_PARAMS] = {
|
||||
[SEAL_LP_TONEMAP_EXPOSURE_BIAS] = 1.0f,
|
||||
[SEAL_LP_TONEMAP_WHITE_POINT] = 1.0f,
|
||||
[SEAL_LP_TONEMAP_A] = 0.22f,
|
||||
[SEAL_LP_TONEMAP_B] = 0.30f,
|
||||
[SEAL_LP_TONEMAP_C] = 0.10f,
|
||||
[SEAL_LP_TONEMAP_D] = 0.20f,
|
||||
[SEAL_LP_TONEMAP_E] = 0.01f,
|
||||
[SEAL_LP_TONEMAP_F] = 0.30f,
|
||||
[SEAL_LP_HEMI_VEC + 0] = -1.0f,
|
||||
[SEAL_LP_HEMI_VEC + 1] = -1.0f,
|
||||
[SEAL_LP_HEMI_VEC + 2] = 1.0f,
|
||||
[SEAL_LP_HEMI_SKY_COLOR + 0] = 1.2f,
|
||||
[SEAL_LP_HEMI_SKY_COLOR + 1] = 1.2f,
|
||||
[SEAL_LP_HEMI_SKY_COLOR + 2] = 1.2f,
|
||||
[SEAL_LP_HEMI_MID_COLOR + 0] = 0.5f,
|
||||
[SEAL_LP_HEMI_MID_COLOR + 1] = 0.5f,
|
||||
[SEAL_LP_HEMI_MID_COLOR + 2] = 0.5f,
|
||||
[SEAL_LP_HEMI_GROUND_COLOR + 0] = 0.3f,
|
||||
[SEAL_LP_HEMI_GROUND_COLOR + 1] = 0.3f,
|
||||
[SEAL_LP_HEMI_GROUND_COLOR + 2] = 0.3f,
|
||||
[SEAL_LP_LS_BETA_R] = 0.25f,
|
||||
[SEAL_LP_LS_BETA_M] = 1.25f,
|
||||
[SEAL_LP_LS_G] = 0.05f,
|
||||
[SEAL_LP_LS_DISTANCE] = 140.0f,
|
||||
[SEAL_LP_LS_LIGHT_VEC + 0] = -1.0f,
|
||||
[SEAL_LP_LS_LIGHT_VEC + 1] = -1.0f,
|
||||
[SEAL_LP_LS_LIGHT_VEC + 2] = 1.0f,
|
||||
[SEAL_LP_LS_LIGHT_COLOR + 0] = 1.0f,
|
||||
[SEAL_LP_LS_LIGHT_COLOR + 1] = 1.0f,
|
||||
[SEAL_LP_LS_LIGHT_COLOR + 2] = 1.0f,
|
||||
[SEAL_LP_LS_SUN_COLOR + 0] = 0.5f,
|
||||
[SEAL_LP_LS_SUN_COLOR + 1] = 0.5f,
|
||||
[SEAL_LP_LS_SUN_COLOR + 2] = 0.5f,
|
||||
};
|
||||
memcpy(out, lit_param_defaults, sizeof(lit_param_defaults));
|
||||
}
|
||||
|
||||
bool lit_parse(uint8_t *data, size_t size, float *out)
|
||||
{
|
||||
if (size != 8 + RE_NR_LIGHT_PARAMS * 4 || memcmp(data, "LITP", 4) != 0) {
|
||||
WARNING("invalid .lit file");
|
||||
return false;
|
||||
}
|
||||
struct buffer r;
|
||||
buffer_init(&r, data, size);
|
||||
buffer_skip(&r, 4); // "LITP"
|
||||
int32_t version = buffer_read_int32(&r);
|
||||
if (version != 0) {
|
||||
WARNING("unsupported .lit version %d", version);
|
||||
return false;
|
||||
}
|
||||
lit_reset(out);
|
||||
for (int i = 0; i < RE_NR_LIGHT_PARAMS; i++)
|
||||
out[i] = buffer_read_float(&r);
|
||||
return true;
|
||||
}
|
||||
|
||||
+1
-1
@@ -943,7 +943,7 @@ void particle_object_calc_local_transform(struct RE_instance *inst, struct parti
|
||||
struct pae_object *pae_obj = po->pae_obj;
|
||||
float t = glm_percent(pi->begin_frame, pi->end_frame, frame);
|
||||
|
||||
vec3 pos, up_vector;
|
||||
vec3 pos = {0}, up_vector;
|
||||
calc_pos_and_up_vector(po, pi, t, pos, up_vector);
|
||||
|
||||
float rel_frame = frame - pi->begin_frame;
|
||||
|
||||
+479
-63
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/aar.h"
|
||||
#include "system4/archive.h"
|
||||
#include "system4/cg.h"
|
||||
#include "system4/hashtable.h"
|
||||
#include "system4/string.h"
|
||||
@@ -34,21 +35,32 @@
|
||||
#include "vm.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
enum RE_plugin_version re_plugin_version;
|
||||
|
||||
static struct RE_instance *create_instance(struct RE_plugin *plugin)
|
||||
{
|
||||
struct RE_instance *instance = xcalloc(1, sizeof(struct RE_instance));
|
||||
struct RE_instance *instance = xcalloc_aligned(1, struct RE_instance);
|
||||
instance->plugin = plugin;
|
||||
instance->draw_edge = plugin->version == RE_TAPIR_PLUGIN;
|
||||
for (int i = 0; i < RE_NR_INSTANCE_TARGETS; i++)
|
||||
instance->target[i] = -1;
|
||||
instance->scale[0] = 1.0f;
|
||||
instance->scale[1] = 1.0f;
|
||||
instance->scale[2] = 1.0f;
|
||||
glm_vec3_one(instance->scale);
|
||||
instance->alpha = 1.0f;
|
||||
glm_vec3_one(instance->diffuse);
|
||||
instance->shadow_volume_bone_radius = 0.1f;
|
||||
instance->draw_bump = true;
|
||||
instance->fps = 30.0f;
|
||||
instance->use_mag_speed = true;
|
||||
instance->column_height = 1.0f;
|
||||
instance->column_radius = 1.0f;
|
||||
glm_vec3_copy((vec3){-1.0f, 2.0f, 0.0f}, instance->vertex_pos[0]);
|
||||
glm_vec3_copy((vec3){-1.0f, 0.0f, 0.0f}, instance->vertex_pos[1]);
|
||||
glm_vec3_copy((vec3){ 1.0f, 2.0f, 0.0f}, instance->vertex_pos[2]);
|
||||
glm_vec3_copy((vec3){ 1.0f, 0.0f, 0.0f}, instance->vertex_pos[3]);
|
||||
glm_vec2_copy((vec2){0.0f, 0.0f}, instance->vertex_uv[0]);
|
||||
glm_vec2_copy((vec2){0.0f, 1.0f}, instance->vertex_uv[1]);
|
||||
glm_vec2_copy((vec2){1.0f, 0.0f}, instance->vertex_uv[2]);
|
||||
glm_vec2_copy((vec2){1.0f, 1.0f}, instance->vertex_uv[3]);
|
||||
glm_mat4_identity(instance->local_transform);
|
||||
glm_mat3_identity(instance->normal_transform);
|
||||
return instance;
|
||||
@@ -58,7 +70,13 @@ static void free_instance(struct RE_instance *instance);
|
||||
|
||||
static void unload_instance(struct RE_instance *instance)
|
||||
{
|
||||
if (instance->motion) {
|
||||
if (instance->name) {
|
||||
free_string(instance->name);
|
||||
instance->name = NULL;
|
||||
}
|
||||
// A billboard's motion holds frame animation state that the game can set
|
||||
// before loading the instance data, so it must survive unloads.
|
||||
if (instance->motion && instance->type != RE_ITYPE_BILLBOARD) {
|
||||
motion_free(instance->motion);
|
||||
instance->motion = NULL;
|
||||
}
|
||||
@@ -74,10 +92,21 @@ static void unload_instance(struct RE_instance *instance)
|
||||
particle_effect_free(instance->effect);
|
||||
instance->effect = NULL;
|
||||
}
|
||||
if (instance->s3de_effect) {
|
||||
s3de_effect_free(instance->s3de_effect);
|
||||
instance->s3de_effect = NULL;
|
||||
}
|
||||
if (instance->bone_transforms) {
|
||||
glDeleteBuffers(1, &instance->bone_transforms_ubo);
|
||||
free(instance->bone_transforms);
|
||||
instance->bone_transforms = NULL;
|
||||
}
|
||||
if (instance->billboard_frames) {
|
||||
// No need to free the textures, they are owned by the renderer.
|
||||
free(instance->billboard_frames);
|
||||
instance->billboard_frames = NULL;
|
||||
instance->nr_billboard_frames = 0;
|
||||
}
|
||||
if (instance->height_detector) {
|
||||
RE_renderer_free_height_detector(instance->height_detector);
|
||||
instance->height_detector = NULL;
|
||||
@@ -91,7 +120,10 @@ static void unload_instance(struct RE_instance *instance)
|
||||
static void free_instance(struct RE_instance *instance)
|
||||
{
|
||||
unload_instance(instance);
|
||||
free(instance);
|
||||
if (instance->motion)
|
||||
motion_free(instance->motion);
|
||||
free(instance->light_params);
|
||||
xfree_aligned(instance);
|
||||
}
|
||||
|
||||
static void RE_back_cg_init(struct RE_back_cg *bcg)
|
||||
@@ -128,12 +160,18 @@ static void update_motion(struct motion *m, float delta_frame)
|
||||
}
|
||||
break;
|
||||
case RE_MOTION_STATE_LOOP:
|
||||
if (m->current_frame > m->loop_frame_end)
|
||||
if (m->loop_frame_begin == m->loop_frame_end)
|
||||
m->current_frame = m->loop_frame_begin;
|
||||
else if (m->current_frame > m->loop_frame_end)
|
||||
m->current_frame = m->loop_frame_begin + fmodf(m->current_frame - m->loop_frame_begin, m->loop_frame_end - m->loop_frame_begin);
|
||||
break;
|
||||
default:
|
||||
VM_ERROR("Invalid motion state %d", m->state);
|
||||
}
|
||||
|
||||
uint32_t anim_frame = m->current_frame - m->frame_begin;
|
||||
m->texture_index = (m->mot && anim_frame < m->mot->nr_texture_indices)
|
||||
? m->mot->texture_indices[anim_frame] : 0;
|
||||
}
|
||||
|
||||
static void calc_motion_frame(struct motion *m, int bone, struct mot_frame *out)
|
||||
@@ -177,11 +215,16 @@ static void update_bones(struct RE_instance *inst)
|
||||
}
|
||||
glm_mat4_copy(bone_transform, parent_transforms[i]);
|
||||
|
||||
glm_mat4_mul(bone_transform, inst->model->bones[i].inverse_bind_matrix, inst->bone_transforms[i]);
|
||||
|
||||
glm_vec3_minv(aabb[0], bone_transform[3], aabb[0]);
|
||||
glm_vec3_maxv(aabb[1], bone_transform[3], aabb[1]);
|
||||
|
||||
glm_mat4_mul(bone_transform, inst->model->bones[i].inverse_bind_matrix, bone_transform);
|
||||
glm_mat4_transpose(bone_transform); // column major -> row major
|
||||
glm_mat3x4_copy(bone_transform, inst->bone_transforms[i]);
|
||||
}
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, inst->bone_transforms_ubo);
|
||||
glBufferSubData(GL_UNIFORM_BUFFER, 0, inst->model->nr_bones * sizeof(mat3x4), inst->bone_transforms);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
|
||||
// Update inst->bounding_sphere.
|
||||
vec3 center;
|
||||
@@ -199,9 +242,12 @@ static void update_bones(struct RE_instance *inst)
|
||||
}
|
||||
}
|
||||
|
||||
struct RE_plugin *RE_plugin_new(void)
|
||||
struct RE_plugin *RE_plugin_new(enum RE_plugin_version version)
|
||||
{
|
||||
char *aar_path = gamedir_path("Data/ReignData.red");
|
||||
re_debug_init();
|
||||
re_plugin_version = version;
|
||||
|
||||
char *aar_path = gamedir_path(version <= RE_TAPIR_PLUGIN ? "Data/ReignData.red" : "Data/3DData.red");
|
||||
int error = ARCHIVE_SUCCESS;
|
||||
struct archive *aar = (struct archive *)aar_open(aar_path, MMAP_IF_64BIT, &error);
|
||||
if (error == ARCHIVE_FILE_ERROR) {
|
||||
@@ -213,26 +259,34 @@ struct RE_plugin *RE_plugin_new(void)
|
||||
if (!aar)
|
||||
return NULL;
|
||||
|
||||
struct RE_plugin *plugin = xcalloc(1, sizeof(struct RE_plugin));
|
||||
struct RE_plugin *plugin = xcalloc_aligned(1, struct RE_plugin);
|
||||
plugin->version = version;
|
||||
plugin->plugin.name = "ReignEngine";
|
||||
plugin->plugin.update = RE_render;
|
||||
plugin->plugin.debug_print = RE_debug_print;
|
||||
plugin->plugin.to_json = RE_to_json;
|
||||
plugin->nr_instances = 16;
|
||||
plugin->instances = xcalloc(plugin->nr_instances, sizeof(struct RE_instance *));
|
||||
plugin->aar = aar;
|
||||
plugin->model_cache = ht_create(32);
|
||||
plugin->pae_cache = ht_create(32);
|
||||
plugin->effect_cache = ht_create(32);
|
||||
for (int i = 0; i < RE_NR_BACK_CGS; i++)
|
||||
RE_back_cg_init(&plugin->back_cg[i]);
|
||||
plugin->mag_speed = 1;
|
||||
if (version == RE_TAPIR_PLUGIN)
|
||||
plugin->draw_options[RE_DRAW_OPTION_EDGE] = 1;
|
||||
plugin->draw_options[RE_DRAW_OPTION_LIGHTING] = 1;
|
||||
plugin->edge_length = 0.02f;
|
||||
plugin->fog_type = RE_FOG_NONE;
|
||||
plugin->fog_near = 1.0;
|
||||
plugin->fog_far = 10.0;
|
||||
plugin->fog_near = 1.0f;
|
||||
plugin->fog_far = 10.0f;
|
||||
glm_vec3_one(plugin->fog_color);
|
||||
lit_reset(plugin->light_params);
|
||||
return plugin;
|
||||
}
|
||||
|
||||
void RE_plugin_free(struct RE_plugin *plugin)
|
||||
{
|
||||
RE_plugin_unbind(plugin);
|
||||
for (int i = 0; i < plugin->nr_instances; i++) {
|
||||
if (plugin->instances[i])
|
||||
free_instance(plugin->instances[i]);
|
||||
@@ -241,13 +295,12 @@ void RE_plugin_free(struct RE_plugin *plugin)
|
||||
archive_free(plugin->aar);
|
||||
ht_foreach_value(plugin->model_cache, (void(*)(void*))model_free);
|
||||
ht_free(plugin->model_cache);
|
||||
ht_foreach_value(plugin->pae_cache, (void(*)(void*))pae_free);
|
||||
ht_free(plugin->pae_cache);
|
||||
if (plugin->renderer)
|
||||
RE_renderer_free(plugin->renderer);
|
||||
ht_foreach_value(plugin->effect_cache, re_plugin_version < RE_SEAL_PLUGIN ?
|
||||
(void(*)(void*))pae_free : (void(*)(void*))s3de_free);
|
||||
ht_free(plugin->effect_cache);
|
||||
for (int i = 0; i < RE_NR_BACK_CGS; i++)
|
||||
RE_back_cg_destroy(&plugin->back_cg[i]);
|
||||
free(plugin);
|
||||
xfree_aligned(plugin);
|
||||
}
|
||||
|
||||
bool RE_plugin_bind(struct RE_plugin *plugin, int sprite)
|
||||
@@ -255,9 +308,11 @@ bool RE_plugin_bind(struct RE_plugin *plugin, int sprite)
|
||||
struct sact_sprite *sp = sact_try_get_sprite(sprite);
|
||||
if (!sp)
|
||||
return false;
|
||||
plugin->renderer = RE_renderer_new(sprite_get_texture(sp));
|
||||
plugin->renderer = RE_renderer_new();
|
||||
plugin->sprite = sprite;
|
||||
sprite_bind_plugin(sp, &plugin->plugin);
|
||||
struct texture *texture = sprite_get_texture(sp);
|
||||
RE_renderer_set_viewport_size(plugin->renderer, texture->w, texture->h);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -274,12 +329,35 @@ bool RE_plugin_unbind(struct RE_plugin *plugin)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_plugin_suspend(struct RE_plugin *plugin)
|
||||
{
|
||||
if (!plugin)
|
||||
return false;
|
||||
plugin->suspended = true;
|
||||
struct sact_sprite *sp = sact_try_get_sprite(plugin->sprite);
|
||||
if (sp)
|
||||
sprite_set_show(sp, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_plugin_resume(struct RE_plugin *plugin)
|
||||
{
|
||||
if (!plugin)
|
||||
return false;
|
||||
plugin->suspended = false;
|
||||
struct sact_sprite *sp = sact_try_get_sprite(plugin->sprite);
|
||||
if (sp)
|
||||
sprite_set_show(sp, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_build_model(struct RE_plugin *plugin, int elapsed_ms)
|
||||
{
|
||||
if (!plugin)
|
||||
return false;
|
||||
|
||||
plugin->camera.quake_pitch = plugin->camera.quake_yaw = 0.0;
|
||||
plugin->camera.override_active = false;
|
||||
|
||||
for (int i = 0; i < plugin->nr_instances; i++) {
|
||||
struct RE_instance *inst = plugin->instances[i];
|
||||
@@ -287,23 +365,49 @@ bool RE_build_model(struct RE_plugin *plugin, int elapsed_ms)
|
||||
continue;
|
||||
|
||||
float delta_frame = inst->fps * elapsed_ms / 1000.0;
|
||||
if (inst->use_mag_speed)
|
||||
delta_frame *= plugin->mag_speed;
|
||||
update_motion(inst->motion, delta_frame);
|
||||
update_motion(inst->next_motion, delta_frame);
|
||||
|
||||
if (inst->type == RE_ITYPE_SKINNED)
|
||||
update_bones(inst);
|
||||
inst->texture_animation_index = inst->motion ? inst->motion->texture_index : 0;
|
||||
}
|
||||
|
||||
// Update particle effects in a second pass, because it uses the results of
|
||||
// update_bones().
|
||||
for (int i = 0; i < plugin->nr_instances; i++) {
|
||||
struct RE_instance *inst = plugin->instances[i];
|
||||
if (inst && inst->type == RE_ITYPE_PARTICLE_EFFECT)
|
||||
if (!inst || inst->type != RE_ITYPE_PARTICLE_EFFECT)
|
||||
continue;
|
||||
if (inst->s3de_effect)
|
||||
s3de_effect_update(inst);
|
||||
else
|
||||
particle_effect_update(inst);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_set_viewport(struct RE_plugin *plugin, int x, int y, int width, int height)
|
||||
{
|
||||
if (!plugin)
|
||||
return false;
|
||||
struct sact_sprite *sp = sact_try_get_sprite(plugin->sprite);
|
||||
if (!sp || sp->plugin != &plugin->plugin) {
|
||||
WARNING("No sprite bound to plugin");
|
||||
return false;
|
||||
}
|
||||
sprite_init_color(sp, width, height, 0, 0, 0, 255);
|
||||
sprite_set_pos(sp, x, y);
|
||||
RE_renderer_set_viewport_size(plugin->renderer, width, height);
|
||||
plugin->viewport_x = x;
|
||||
plugin->viewport_y = y;
|
||||
plugin->viewport_width = width;
|
||||
plugin->viewport_height = height;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_set_projection(struct RE_plugin *plugin, float width, float height, float near, float far, float deg)
|
||||
{
|
||||
if (!plugin)
|
||||
@@ -324,17 +428,29 @@ int RE_create_instance(struct RE_plugin *plugin)
|
||||
{
|
||||
if (!plugin)
|
||||
return -1;
|
||||
for (int i = 0; i < plugin->nr_instances; i++) {
|
||||
if (!plugin->instances[i]) {
|
||||
plugin->instances[i] = create_instance(plugin);
|
||||
return i;
|
||||
}
|
||||
int index;
|
||||
for (index = 0; index < plugin->nr_instances; index++) {
|
||||
if (!plugin->instances[index])
|
||||
break;
|
||||
}
|
||||
int old_size = plugin->nr_instances;
|
||||
plugin->nr_instances *= 2;
|
||||
plugin->instances = xrealloc_array(plugin->instances, old_size, plugin->nr_instances, sizeof(struct RE_instance *));
|
||||
plugin->instances[old_size] = create_instance(plugin);
|
||||
return old_size;
|
||||
RE_create_instance_at(plugin, index);
|
||||
return index;
|
||||
}
|
||||
|
||||
struct RE_instance *RE_create_instance_at(struct RE_plugin *plugin, int index)
|
||||
{
|
||||
if (index >= plugin->nr_instances) {
|
||||
int old_size = plugin->nr_instances;
|
||||
int new_size = old_size;
|
||||
while (new_size <= index)
|
||||
new_size *= 2;
|
||||
plugin->instances = xrealloc_array(plugin->instances, old_size, new_size, sizeof(struct RE_instance *));
|
||||
plugin->nr_instances = new_size;
|
||||
}
|
||||
if (plugin->instances[index])
|
||||
free_instance(plugin->instances[index]);
|
||||
plugin->instances[index] = create_instance(plugin);
|
||||
return plugin->instances[index];
|
||||
}
|
||||
|
||||
bool RE_release_instance(struct RE_plugin *plugin, int instance)
|
||||
@@ -363,27 +479,89 @@ bool RE_instance_set_type(struct RE_instance *instance, int type)
|
||||
instance->motion->instance = instance;
|
||||
break;
|
||||
case RE_ITYPE_DIRECTIONAL_LIGHT:
|
||||
if (re_plugin_version >= RE_SEAL_PLUGIN) {
|
||||
instance->light_params = xmalloc(RE_NR_LIGHT_PARAMS * sizeof(float));
|
||||
lit_reset(instance->light_params);
|
||||
}
|
||||
break;
|
||||
case RE_ITYPE_SPECULAR_LIGHT:
|
||||
break;
|
||||
case RE_ITYPE_PARTICLE_EFFECT:
|
||||
break;
|
||||
case RE_ITYPE_PATH_LINE:
|
||||
break;
|
||||
default:
|
||||
ERROR("unknown instance type %d", type);
|
||||
VM_ERROR("unknown instance type %d", type);
|
||||
}
|
||||
instance->type = type;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_instance_data_exists(struct RE_instance *instance, const char *name)
|
||||
{
|
||||
if (!instance)
|
||||
return false;
|
||||
const char *ext;
|
||||
switch (instance->type) {
|
||||
case RE_ITYPE_STATIC:
|
||||
case RE_ITYPE_SKINNED:
|
||||
ext = ".POL";
|
||||
break;
|
||||
case RE_ITYPE_PARTICLE_EFFECT:
|
||||
ext = ".3de";
|
||||
break;
|
||||
default:
|
||||
return archive_exists_by_name(instance->plugin->aar, name, NULL);
|
||||
}
|
||||
const char *basename = strrchr(name, '\\');
|
||||
basename = basename ? basename + 1 : name;
|
||||
char *path = xmalloc(strlen(name) + strlen(basename) + strlen(ext) + 2);
|
||||
sprintf(path, "%s\\%s%s", name, basename, ext);
|
||||
bool exists = archive_exists_by_name(instance->plugin->aar, path, NULL);
|
||||
free(path);
|
||||
return exists;
|
||||
}
|
||||
|
||||
// Loads the frame images of a billboard instance. They are stored in the
|
||||
// archive as "<name>\<basename>.png", "<name>\<basename>[1].png", ...
|
||||
static bool load_billboard_frames(struct RE_instance *instance, const char *name)
|
||||
{
|
||||
const char *basename = strrchr(name, '\\');
|
||||
basename = basename ? basename + 1 : name;
|
||||
char *path = xmalloc(strlen(name) + strlen(basename) + 22);
|
||||
|
||||
int capacity = 8;
|
||||
instance->billboard_frames = xcalloc(capacity, sizeof(struct billboard_texture *));
|
||||
for (int i = 0;; i++) {
|
||||
if (i == 0)
|
||||
sprintf(path, "%s\\%s.png", name, basename);
|
||||
else
|
||||
sprintf(path, "%s\\%s[%d].png", name, basename, i);
|
||||
struct billboard_texture *bt = RE_renderer_load_billboard_texture_by_path(
|
||||
instance->plugin->renderer, instance->plugin->aar, path);
|
||||
if (!bt)
|
||||
break;
|
||||
if (i == capacity) {
|
||||
instance->billboard_frames = xrealloc_array(
|
||||
instance->billboard_frames, capacity, capacity * 2, sizeof(struct billboard_texture *));
|
||||
capacity *= 2;
|
||||
}
|
||||
instance->billboard_frames[i] = bt;
|
||||
instance->nr_billboard_frames = i + 1;
|
||||
}
|
||||
free(path);
|
||||
return instance->nr_billboard_frames > 0;
|
||||
}
|
||||
|
||||
bool RE_instance_load(struct RE_instance *instance, const char *name)
|
||||
{
|
||||
if (!instance)
|
||||
return false;
|
||||
unload_instance(instance);
|
||||
if (name[0] == '\0')
|
||||
return false;
|
||||
return true; // empty name just unloads the instance
|
||||
instance->name = cstr_to_string(name);
|
||||
|
||||
struct pae *pae;
|
||||
switch (instance->type) {
|
||||
case RE_ITYPE_STATIC:
|
||||
case RE_ITYPE_SKINNED:
|
||||
@@ -393,30 +571,77 @@ bool RE_instance_load(struct RE_instance *instance, const char *name)
|
||||
if (instance->model)
|
||||
ht_put(instance->plugin->model_cache, name, instance->model);
|
||||
}
|
||||
if (instance->model && instance->model->nr_bones > 0)
|
||||
instance->bone_transforms = xcalloc(instance->model->nr_bones, sizeof(mat4));
|
||||
if (instance->model && instance->model->nr_bones > 0) {
|
||||
instance->bone_transforms = xcalloc(instance->model->nr_bones, sizeof(mat3x4));
|
||||
glGenBuffers(1, &instance->bone_transforms_ubo);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, instance->bone_transforms_ubo);
|
||||
glBufferData(GL_UNIFORM_BUFFER, MAX_BONES * sizeof(mat3x4), NULL, GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
}
|
||||
return !!instance->model;
|
||||
case RE_ITYPE_PARTICLE_EFFECT:
|
||||
pae = ht_get(instance->plugin->pae_cache, name, NULL);
|
||||
if (!pae) {
|
||||
pae = pae_load(instance->plugin->aar, name);
|
||||
if (!pae)
|
||||
return false;
|
||||
ht_put(instance->plugin->pae_cache, name, pae);
|
||||
}
|
||||
instance->effect = particle_effect_create(pae);
|
||||
if (!instance->motion) {
|
||||
instance->motion = xcalloc(1, sizeof(struct motion));
|
||||
instance->motion->instance = instance;
|
||||
pae_calc_frame_range(instance->effect->pae, instance->motion);
|
||||
if (re_plugin_version >= RE_SEAL_PLUGIN) {
|
||||
struct s3de *s3de = ht_get(instance->plugin->effect_cache, name, NULL);
|
||||
if (!s3de) {
|
||||
s3de = s3de_load(instance->plugin->aar, name);
|
||||
if (!s3de)
|
||||
return false;
|
||||
ht_put(instance->plugin->effect_cache, name, s3de);
|
||||
}
|
||||
instance->s3de_effect = s3de_effect_create(s3de, instance->plugin->aar);
|
||||
if (!instance->motion) {
|
||||
instance->motion = xcalloc(1, sizeof(struct motion));
|
||||
instance->motion->instance = instance;
|
||||
s3de_calc_frame_range(instance->s3de_effect->s3de, instance->motion);
|
||||
}
|
||||
} else {
|
||||
struct pae *pae = ht_get(instance->plugin->effect_cache, name, NULL);
|
||||
if (!pae) {
|
||||
pae = pae_load(instance->plugin->aar, name);
|
||||
if (!pae)
|
||||
return false;
|
||||
ht_put(instance->plugin->effect_cache, name, pae);
|
||||
}
|
||||
instance->effect = particle_effect_create(pae);
|
||||
if (!instance->motion) {
|
||||
instance->motion = xcalloc(1, sizeof(struct motion));
|
||||
instance->motion->instance = instance;
|
||||
pae_calc_frame_range(instance->effect->pae, instance->motion);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
case RE_ITYPE_BILLBOARD:
|
||||
if (re_plugin_version >= RE_SEAL_PLUGIN)
|
||||
return load_billboard_frames(instance, name);
|
||||
case RE_ITYPE_DIRECTIONAL_LIGHT:
|
||||
if (re_plugin_version >= RE_SEAL_PLUGIN) {
|
||||
struct archive_data *dfile = archive_get_by_name(instance->plugin->aar, name);
|
||||
if (!dfile)
|
||||
return false;
|
||||
bool ok = lit_parse(dfile->data, dfile->size, instance->light_params);
|
||||
archive_free_data(dfile);
|
||||
return ok;
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
WARNING("Invalid instance type %d", instance->type);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool RE_instance_motion_exists(struct RE_instance *instance, const char *name)
|
||||
{
|
||||
if (!instance || !instance->model)
|
||||
return false;
|
||||
if (instance->model->mot_cache && ht_get(instance->model->mot_cache, name, NULL))
|
||||
return true;
|
||||
char *path = xmalloc(strlen(instance->model->path) + strlen(name) + 6);
|
||||
sprintf(path, "%s\\%s.MOT", instance->model->path, name);
|
||||
bool exists = archive_exists_by_name(instance->plugin->aar, path, NULL);
|
||||
free(path);
|
||||
return exists;
|
||||
}
|
||||
|
||||
bool RE_instance_load_motion(struct RE_instance *instance, const char *name)
|
||||
{
|
||||
if (!instance)
|
||||
@@ -446,19 +671,41 @@ bool RE_instance_free_next_motion(struct RE_instance *instance)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_instance_set_mesh_show(struct RE_instance *instance, const char *mesh_name, bool show)
|
||||
{
|
||||
if (!instance || !instance->model)
|
||||
return false;
|
||||
for (int i = 0; i < instance->model->nr_meshes; i++) {
|
||||
if (!strcmp(instance->model->meshes[i].name, mesh_name)) {
|
||||
instance->model->meshes[i].hidden = !show;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Converts from the (top-left, top-right, bottom-left, bottom-right) order used
|
||||
// by the API to the vertex order of the billboard mesh.
|
||||
static const int billboard_vertex_index[4] = {0, 2, 1, 3};
|
||||
|
||||
bool RE_instance_set_vertex_pos(struct RE_instance *instance, int index, float x, float y, float z)
|
||||
{
|
||||
if (!instance)
|
||||
if (!instance || (unsigned)index >= 4)
|
||||
return false;
|
||||
if (instance->type != RE_ITYPE_BILLBOARD)
|
||||
ERROR("not implemented");
|
||||
// Hack: This works because SetInstanceVertexPos is only used to scale
|
||||
// billboards, and SetInstanceScale is not used for billboards.
|
||||
if (index == 1) {
|
||||
instance->scale[0] = x;
|
||||
instance->scale[1] = y / 2.0;
|
||||
}
|
||||
return false;
|
||||
glm_vec3_copy((vec3){x, y, z}, instance->vertex_pos[billboard_vertex_index[index]]);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_instance_set_vertex_uv(struct RE_instance *instance, int index, float u, float v)
|
||||
{
|
||||
if (!instance || (unsigned)index >= 4)
|
||||
return false;
|
||||
if (instance->type != RE_ITYPE_BILLBOARD)
|
||||
ERROR("not implemented");
|
||||
glm_vec2_copy((vec2){u, v}, instance->vertex_uv[billboard_vertex_index[index]]);
|
||||
return true;
|
||||
}
|
||||
|
||||
int RE_instance_get_bone_index(struct RE_instance *instance, const char *name)
|
||||
@@ -480,7 +727,10 @@ bool RE_instance_trans_local_pos_to_world_pos_by_bone(struct RE_instance *instan
|
||||
if (instance->local_transform_needs_update)
|
||||
RE_instance_update_local_transform(instance);
|
||||
|
||||
glm_mat4_mulv3(instance->bone_transforms[bone], offset, 1.0, out);
|
||||
mat4 bone_transform = GLM_MAT4_IDENTITY_INIT;
|
||||
glm_mat3x4_copy(instance->bone_transforms[bone], bone_transform);
|
||||
glm_mat4_transpose(bone_transform); // row major -> column major
|
||||
glm_mat4_mulv3(bone_transform, offset, 1.0, out);
|
||||
glm_mat4_mulv3(instance->local_transform, out, 1.0, out);
|
||||
return true;
|
||||
}
|
||||
@@ -507,6 +757,31 @@ float RE_instance_calc_height(struct RE_instance *instance, float x, float z)
|
||||
return cnt ? total / cnt : 0.0f;
|
||||
}
|
||||
|
||||
float RE_instance_calc_2d_detection_height(struct RE_instance *instance, float x, float z)
|
||||
{
|
||||
if (!instance || !instance->model->collider)
|
||||
return false;
|
||||
vec2 xz = { x, -z };
|
||||
float h;
|
||||
if (collider_height(instance->model->collider, xz, &h))
|
||||
return h;
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (!instance || !instance->model->collider)
|
||||
return false;
|
||||
vec2 p0 = { x0, -z0 };
|
||||
vec2 p1 = { x1, -z1 };
|
||||
vec2 p2;
|
||||
if (!check_collision(instance->model->collider, p0, p1, radius, p2))
|
||||
return false;
|
||||
*x2 = p2[0];
|
||||
*z2 = -p2[1];
|
||||
return collider_height(instance->model->collider, p2, y2);
|
||||
}
|
||||
|
||||
bool RE_instance_set_debug_draw_shadow_volume(struct RE_instance *inst, bool draw)
|
||||
{
|
||||
if (!inst)
|
||||
@@ -520,6 +795,147 @@ bool RE_instance_set_debug_draw_shadow_volume(struct RE_instance *inst, bool dra
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_instance_find_path(struct RE_instance *inst, vec3 start, vec3 goal)
|
||||
{
|
||||
if (!inst || !inst->model->collider)
|
||||
return false;
|
||||
mat4 vp_transform;
|
||||
RE_calc_view_matrix(&inst->plugin->camera, NULL, vp_transform);
|
||||
glm_mat4_mul(inst->plugin->proj_transform, vp_transform, vp_transform);
|
||||
return collider_find_path(inst->model->collider, start, goal, vp_transform);
|
||||
}
|
||||
|
||||
const vec3 *RE_instance_get_path_line(struct RE_instance *inst, int *nr_path_points)
|
||||
{
|
||||
if (!inst || !inst->model->collider)
|
||||
return NULL;
|
||||
*nr_path_points = inst->model->collider->nr_path_points;
|
||||
return inst->model->collider->path_points;
|
||||
}
|
||||
|
||||
bool RE_instance_optimize_path_line(struct RE_instance *inst)
|
||||
{
|
||||
if (!inst || !inst->model->collider)
|
||||
return false;
|
||||
return collider_optimize_path(inst->model->collider);
|
||||
}
|
||||
|
||||
bool RE_instance_calc_path_finder_intersect_eye_vec(struct RE_instance *inst, int mouse_x, int mouse_y, vec3 out)
|
||||
{
|
||||
if (!inst || !inst->model || !inst->model->collider)
|
||||
return false;
|
||||
|
||||
float ndc_x = 2.f * mouse_x / inst->plugin->renderer->viewport_width - 1.f;
|
||||
float ndc_y = 1.f - 2.f * mouse_y / inst->plugin->renderer->viewport_height;
|
||||
vec4 near = { ndc_x, ndc_y, -1.f, 1.f };
|
||||
vec4 far = { ndc_x, ndc_y, 1.f, 1.f };
|
||||
|
||||
mat4 inv_vp;
|
||||
RE_calc_view_matrix(&inst->plugin->camera, NULL, inv_vp);
|
||||
glm_mat4_mul(inst->plugin->proj_transform, inv_vp, inv_vp);
|
||||
glm_mat4_inv(inv_vp, inv_vp);
|
||||
|
||||
glm_mat4_mulv(inv_vp, near, near);
|
||||
glm_mat4_mulv(inv_vp, far, far);
|
||||
glm_vec3_divs(near, near[3], near);
|
||||
glm_vec3_divs(far, far[3], far);
|
||||
|
||||
vec3 origin, direction;
|
||||
glm_vec3_copy(near, origin);
|
||||
glm_vec3_sub(far, near, direction);
|
||||
glm_vec3_normalize(direction);
|
||||
|
||||
return collider_raycast(inst->model->collider, origin, direction, out);
|
||||
}
|
||||
|
||||
bool RE_plugin_transform_pos_to_view_pos(struct RE_plugin *plugin, float x, float y, float z, int *view_x, int *view_y)
|
||||
{
|
||||
if (!plugin)
|
||||
return false;
|
||||
|
||||
mat4 vp;
|
||||
RE_calc_view_matrix(&plugin->camera, NULL, vp);
|
||||
glm_mat4_mul(plugin->proj_transform, vp, vp);
|
||||
|
||||
vec4 pos = { x, y, z, 1.f };
|
||||
glm_mat4_mulv(vp, pos, pos);
|
||||
|
||||
if (pos[3] == 0.f)
|
||||
return false;
|
||||
|
||||
float ndc_x = pos[0] / pos[3];
|
||||
float ndc_y = pos[1] / pos[3];
|
||||
|
||||
*view_x = (int)((ndc_x + 1.f) * 0.5f * plugin->renderer->viewport_width);
|
||||
*view_y = (int)((1.f - ndc_y) * 0.5f * plugin->renderer->viewport_height);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_plugin_get_camera_z_vector(struct RE_plugin *plugin, vec3 out)
|
||||
{
|
||||
if (!plugin)
|
||||
return false;
|
||||
|
||||
vec3 euler;
|
||||
if (plugin->camera.override_active) {
|
||||
euler[0] = glm_rad(plugin->camera.override_pitch);
|
||||
euler[1] = glm_rad(plugin->camera.override_yaw);
|
||||
euler[2] = glm_rad(plugin->camera.override_roll);
|
||||
} else {
|
||||
euler[0] = glm_rad(plugin->camera.pitch + plugin->camera.quake_pitch);
|
||||
euler[1] = glm_rad(plugin->camera.yaw + plugin->camera.quake_yaw);
|
||||
euler[2] = glm_rad(plugin->camera.roll);
|
||||
}
|
||||
mat4 rot;
|
||||
glm_euler_yxz(euler, rot);
|
||||
glm_mat4_mulv3(rot, GLM_FORWARD, 0.f, out);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RE_plugin_reset_light_param(struct RE_plugin *plugin)
|
||||
{
|
||||
lit_reset(plugin->light_params);
|
||||
}
|
||||
|
||||
bool RE_plugin_set_fog_type(struct RE_plugin *plugin, int type)
|
||||
{
|
||||
if (re_plugin_version == RE_REIGN_PLUGIN) {
|
||||
switch (type) {
|
||||
case 0: plugin->fog_type = RE_FOG_NONE; return true;
|
||||
case 1: plugin->fog_type = RE_FOG_LINEAR; return true;
|
||||
case 2: plugin->fog_type = RE_FOG_LIGHT_SCATTERING; return true;
|
||||
default: break;
|
||||
}
|
||||
} else {
|
||||
switch (type) {
|
||||
case 0: plugin->fog_type = RE_FOG_NONE; return true;
|
||||
case 1: plugin->fog_type = RE_FOG_LIGHT_SCATTERING; return true;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
WARNING("unknown fog type %d", type);
|
||||
return false;
|
||||
}
|
||||
|
||||
int RE_plugin_get_fog_type(struct RE_plugin *plugin)
|
||||
{
|
||||
if (re_plugin_version == RE_REIGN_PLUGIN) {
|
||||
switch (plugin->fog_type) {
|
||||
case RE_FOG_NONE: return 0;
|
||||
case RE_FOG_LINEAR: return 1;
|
||||
case RE_FOG_LIGHT_SCATTERING: return 2;
|
||||
default: break;
|
||||
}
|
||||
} else {
|
||||
switch (plugin->fog_type) {
|
||||
case RE_FOG_NONE: return 0;
|
||||
case RE_FOG_LIGHT_SCATTERING: return 1;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
ERROR("[BUG] unexpected fog type %d", plugin->fog_type);
|
||||
}
|
||||
|
||||
void RE_instance_update_local_transform(struct RE_instance *inst)
|
||||
{
|
||||
vec3 euler = {
|
||||
@@ -574,9 +990,9 @@ bool RE_motion_set_frame_range(struct motion *motion, float begin, float end)
|
||||
return false;
|
||||
motion->frame_begin = begin;
|
||||
motion->frame_end = end;
|
||||
if (motion->instance->type == RE_ITYPE_BILLBOARD) {
|
||||
if (motion->instance->type == RE_ITYPE_BILLBOARD && re_plugin_version < RE_SEAL_PLUGIN) {
|
||||
for (int i = begin; i < end; i++) {
|
||||
if (!RE_renderer_load_billboard_texture(motion->instance->plugin->renderer, i))
|
||||
if (!RE_renderer_load_billboard_texture_by_no(motion->instance->plugin->renderer, i))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -589,9 +1005,9 @@ bool RE_motion_set_loop_frame_range(struct motion *motion, float begin, float en
|
||||
return false;
|
||||
motion->loop_frame_begin = begin;
|
||||
motion->loop_frame_end = end;
|
||||
if (motion->instance->type == RE_ITYPE_BILLBOARD) {
|
||||
if (motion->instance->type == RE_ITYPE_BILLBOARD && re_plugin_version < RE_SEAL_PLUGIN) {
|
||||
for (int i = begin; i < end; i++) {
|
||||
if (!RE_renderer_load_billboard_texture(motion->instance->plugin->renderer, i))
|
||||
if (!RE_renderer_load_billboard_texture_by_no(motion->instance->plugin->renderer, i))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+692
-118
File diff suppressed because it is too large
Load Diff
+1599
File diff suppressed because it is too large
Load Diff
+388
@@ -0,0 +1,388 @@
|
||||
/* Copyright (C) 2026 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/string.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
#include "reign.h"
|
||||
#include "../hll/iarray.h"
|
||||
|
||||
static void write_vec3(struct iarray_writer *w, vec3 v)
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
iarray_write_float(w, v[i]);
|
||||
}
|
||||
|
||||
static void read_vec3(struct iarray_reader *r, vec3 v)
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
v[i] = iarray_read_float(r);
|
||||
}
|
||||
|
||||
static void write_cstring(struct iarray_writer *w, const char *s)
|
||||
{
|
||||
struct string *tmp = cstr_to_string(s);
|
||||
iarray_write_string(w, tmp);
|
||||
free_string(tmp);
|
||||
}
|
||||
|
||||
static void save_motion(struct iarray_writer *w, struct motion *m)
|
||||
{
|
||||
iarray_write(w, !!m);
|
||||
if (!m)
|
||||
return;
|
||||
write_cstring(w, m->mot ? m->mot->name : "");
|
||||
iarray_write(w, m->state);
|
||||
iarray_write_float(w, m->current_frame);
|
||||
iarray_write_float(w, m->frame_begin);
|
||||
iarray_write_float(w, m->frame_end);
|
||||
iarray_write_float(w, m->loop_frame_begin);
|
||||
iarray_write_float(w, m->loop_frame_end);
|
||||
}
|
||||
|
||||
static void load_motion(struct iarray_reader *r, struct RE_instance *inst, bool next)
|
||||
{
|
||||
if (!iarray_read(r))
|
||||
return;
|
||||
struct string *name = iarray_read_string(r);
|
||||
if (name->size > 0) {
|
||||
if (next)
|
||||
RE_instance_load_next_motion(inst, name->text);
|
||||
else
|
||||
RE_instance_load_motion(inst, name->text);
|
||||
}
|
||||
free_string(name);
|
||||
|
||||
int state = iarray_read(r);
|
||||
float current_frame = iarray_read_float(r);
|
||||
float frame_begin = iarray_read_float(r);
|
||||
float frame_end = iarray_read_float(r);
|
||||
float loop_frame_begin = iarray_read_float(r);
|
||||
float loop_frame_end = iarray_read_float(r);
|
||||
|
||||
struct motion *m = next ? inst->next_motion : inst->motion;
|
||||
if (!m)
|
||||
return;
|
||||
m->state = state;
|
||||
m->current_frame = current_frame;
|
||||
m->frame_begin = frame_begin;
|
||||
m->frame_end = frame_end;
|
||||
m->loop_frame_begin = loop_frame_begin;
|
||||
m->loop_frame_end = loop_frame_end;
|
||||
}
|
||||
|
||||
static void save_instance(struct iarray_writer *w, struct RE_instance *inst)
|
||||
{
|
||||
iarray_write(w, inst->type);
|
||||
iarray_write_string_or_null(w, inst->name);
|
||||
for (int i = 0; i < RE_NR_INSTANCE_TARGETS; i++)
|
||||
iarray_write(w, inst->target[i]);
|
||||
write_vec3(w, inst->pos);
|
||||
iarray_write_float(w, inst->pitch);
|
||||
iarray_write_float(w, inst->roll);
|
||||
iarray_write_float(w, inst->yaw);
|
||||
write_vec3(w, inst->scale);
|
||||
iarray_write_float(w, inst->alpha);
|
||||
iarray_write_float(w, inst->grayscale_rate);
|
||||
iarray_write(w, inst->draw);
|
||||
iarray_write(w, inst->draw_edge);
|
||||
iarray_write(w, inst->draw_shadow);
|
||||
iarray_write(w, inst->make_shadow);
|
||||
iarray_write(w, inst->draw_bump);
|
||||
iarray_write_float(w, inst->shadow_volume_bone_radius);
|
||||
iarray_write_float(w, inst->fps);
|
||||
iarray_write(w, inst->use_mag_speed);
|
||||
iarray_write(w, inst->motion_blend);
|
||||
iarray_write_float(w, inst->motion_blend_rate);
|
||||
write_vec3(w, inst->diffuse);
|
||||
write_vec3(w, inst->ambient);
|
||||
write_vec3(w, inst->column_pos);
|
||||
iarray_write_float(w, inst->column_height);
|
||||
iarray_write_float(w, inst->column_radius);
|
||||
iarray_write_float(w, inst->column_angle);
|
||||
|
||||
iarray_write(w, inst->draw_type);
|
||||
for (int i = 0; i < 4; i++)
|
||||
write_vec3(w, inst->vertex_pos[i]);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
iarray_write_float(w, inst->vertex_uv[i][0]);
|
||||
iarray_write_float(w, inst->vertex_uv[i][1]);
|
||||
}
|
||||
|
||||
write_vec3(w, inst->vec);
|
||||
write_vec3(w, inst->globe_diffuse);
|
||||
iarray_write(w, !!inst->light_params);
|
||||
if (inst->light_params) {
|
||||
iarray_write(w, RE_NR_LIGHT_PARAMS);
|
||||
for (int i = 0; i < RE_NR_LIGHT_PARAMS; i++)
|
||||
iarray_write_float(w, inst->light_params[i]);
|
||||
}
|
||||
|
||||
save_motion(w, inst->motion);
|
||||
save_motion(w, inst->next_motion);
|
||||
}
|
||||
|
||||
static void load_instance(struct iarray_reader *r, struct RE_instance *inst)
|
||||
{
|
||||
int type = iarray_read(r);
|
||||
if (type != RE_ITYPE_UNINITIALIZED)
|
||||
RE_instance_set_type(inst, type);
|
||||
struct string *name = iarray_read_string_or_null(r);
|
||||
if (name) {
|
||||
RE_instance_load(inst, name->text);
|
||||
free_string(name);
|
||||
}
|
||||
for (int i = 0; i < RE_NR_INSTANCE_TARGETS; i++)
|
||||
inst->target[i] = iarray_read(r);
|
||||
read_vec3(r, inst->pos);
|
||||
inst->pitch = iarray_read_float(r);
|
||||
inst->roll = iarray_read_float(r);
|
||||
inst->yaw = iarray_read_float(r);
|
||||
read_vec3(r, inst->scale);
|
||||
inst->alpha = iarray_read_float(r);
|
||||
inst->grayscale_rate = iarray_read_float(r);
|
||||
inst->draw = iarray_read(r);
|
||||
inst->draw_edge = iarray_read(r);
|
||||
inst->draw_shadow = iarray_read(r);
|
||||
inst->make_shadow = iarray_read(r);
|
||||
inst->draw_bump = iarray_read(r);
|
||||
inst->shadow_volume_bone_radius = iarray_read_float(r);
|
||||
inst->fps = iarray_read_float(r);
|
||||
inst->use_mag_speed = iarray_read(r);
|
||||
inst->motion_blend = iarray_read(r);
|
||||
inst->motion_blend_rate = iarray_read_float(r);
|
||||
read_vec3(r, inst->diffuse);
|
||||
read_vec3(r, inst->ambient);
|
||||
read_vec3(r, inst->column_pos);
|
||||
inst->column_height = iarray_read_float(r);
|
||||
inst->column_radius = iarray_read_float(r);
|
||||
inst->column_angle = iarray_read_float(r);
|
||||
inst->local_transform_needs_update = true;
|
||||
|
||||
inst->draw_type = iarray_read(r);
|
||||
for (int i = 0; i < 4; i++)
|
||||
read_vec3(r, inst->vertex_pos[i]);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
inst->vertex_uv[i][0] = iarray_read_float(r);
|
||||
inst->vertex_uv[i][1] = iarray_read_float(r);
|
||||
}
|
||||
|
||||
read_vec3(r, inst->vec);
|
||||
read_vec3(r, inst->globe_diffuse);
|
||||
if (iarray_read(r)) {
|
||||
int nr_params = iarray_read(r);
|
||||
for (int i = 0; i < nr_params; i++) {
|
||||
float param = iarray_read_float(r);
|
||||
if (inst->light_params && i < RE_NR_LIGHT_PARAMS)
|
||||
inst->light_params[i] = param;
|
||||
}
|
||||
}
|
||||
|
||||
load_motion(r, inst, false);
|
||||
load_motion(r, inst, true);
|
||||
}
|
||||
|
||||
static void save_back_cg(struct iarray_writer *w, struct RE_back_cg *bcg)
|
||||
{
|
||||
iarray_write_string_or_null(w, bcg->name);
|
||||
iarray_write(w, bcg->no);
|
||||
iarray_write_float(w, bcg->blend_rate);
|
||||
iarray_write_float(w, bcg->x);
|
||||
iarray_write_float(w, bcg->y);
|
||||
iarray_write_float(w, bcg->mag);
|
||||
iarray_write(w, bcg->show);
|
||||
}
|
||||
|
||||
static void load_back_cg(struct iarray_reader *r, struct RE_back_cg *bcg, struct archive *aar)
|
||||
{
|
||||
struct string *name = iarray_read_string_or_null(r);
|
||||
int no = iarray_read(r);
|
||||
float blend_rate = iarray_read_float(r);
|
||||
float x = iarray_read_float(r);
|
||||
float y = iarray_read_float(r);
|
||||
float mag = iarray_read_float(r);
|
||||
bool show = iarray_read(r);
|
||||
if (bcg) {
|
||||
if (name)
|
||||
RE_back_cg_set_name(bcg, name, aar);
|
||||
else if (no)
|
||||
RE_back_cg_set(bcg, no);
|
||||
bcg->blend_rate = blend_rate;
|
||||
bcg->x = x;
|
||||
bcg->y = y;
|
||||
bcg->mag = mag;
|
||||
bcg->show = show;
|
||||
}
|
||||
if (name)
|
||||
free_string(name);
|
||||
}
|
||||
|
||||
void RE_plugin_serialize(struct RE_plugin *plugin, struct iarray_writer *w)
|
||||
{
|
||||
write_vec3(w, plugin->camera.pos);
|
||||
iarray_write_float(w, plugin->camera.pitch);
|
||||
iarray_write_float(w, plugin->camera.roll);
|
||||
iarray_write_float(w, plugin->camera.yaw);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++)
|
||||
iarray_write_float(w, plugin->proj_transform[i][j]);
|
||||
}
|
||||
iarray_write(w, plugin->viewport_x);
|
||||
iarray_write(w, plugin->viewport_y);
|
||||
iarray_write(w, plugin->viewport_width);
|
||||
iarray_write(w, plugin->viewport_height);
|
||||
|
||||
iarray_write(w, RE_NR_BACK_CGS);
|
||||
for (int i = 0; i < RE_NR_BACK_CGS; i++)
|
||||
save_back_cg(w, &plugin->back_cg[i]);
|
||||
|
||||
iarray_write(w, plugin->render_mode);
|
||||
iarray_write(w, plugin->shadow_mode);
|
||||
write_vec3(w, plugin->shadow_map_light_dir);
|
||||
iarray_write_float(w, plugin->shadow_bias);
|
||||
iarray_write(w, plugin->bump_mode);
|
||||
iarray_write(w, plugin->bloom_mode);
|
||||
iarray_write(w, plugin->glare_mode);
|
||||
iarray_write(w, plugin->fog_mode);
|
||||
iarray_write(w, plugin->fog_type);
|
||||
iarray_write_float(w, plugin->fog_near);
|
||||
iarray_write_float(w, plugin->fog_far);
|
||||
write_vec3(w, plugin->fog_color);
|
||||
iarray_write_float(w, plugin->ls_beta_r);
|
||||
iarray_write_float(w, plugin->ls_beta_m);
|
||||
iarray_write_float(w, plugin->ls_g);
|
||||
iarray_write_float(w, plugin->ls_distance);
|
||||
write_vec3(w, plugin->ls_light_dir);
|
||||
write_vec3(w, plugin->ls_light_color);
|
||||
write_vec3(w, plugin->ls_sun_color);
|
||||
iarray_write(w, plugin->specular_mode);
|
||||
iarray_write(w, plugin->light_map_mode);
|
||||
iarray_write(w, plugin->soft_fog_edge_mode);
|
||||
iarray_write(w, plugin->ssao_mode);
|
||||
iarray_write(w, plugin->vertex_blend_mode);
|
||||
iarray_write(w, plugin->shader_precision_mode);
|
||||
iarray_write(w, plugin->texture_filter_mode);
|
||||
iarray_write(w, plugin->use_power2_texture);
|
||||
iarray_write(w, plugin->shadow_map_resolution_level);
|
||||
iarray_write(w, plugin->texture_resolution_level);
|
||||
write_vec3(w, plugin->global_ambient);
|
||||
iarray_write_float(w, plugin->post_effect_filter_y);
|
||||
iarray_write_float(w, plugin->post_effect_filter_cb);
|
||||
iarray_write_float(w, plugin->post_effect_filter_cr);
|
||||
iarray_write(w, RE_DRAW_OPTION_MAX);
|
||||
for (int i = 0; i < RE_DRAW_OPTION_MAX; i++)
|
||||
iarray_write(w, plugin->draw_options[i]);
|
||||
iarray_write(w, plugin->suspended);
|
||||
|
||||
iarray_write(w, plugin->mag_speed);
|
||||
iarray_write(w, RE_NR_LIGHT_PARAMS);
|
||||
for (int i = 0; i < RE_NR_LIGHT_PARAMS; i++)
|
||||
iarray_write_float(w, plugin->light_params[i]);
|
||||
iarray_write_float(w, plugin->edge_length);
|
||||
iarray_write_float(w, plugin->edge_reduction_rate);
|
||||
write_vec3(w, plugin->edge_color);
|
||||
|
||||
iarray_write(w, plugin->nr_instances);
|
||||
for (int i = 0; i < plugin->nr_instances; i++) {
|
||||
iarray_write(w, !!plugin->instances[i]);
|
||||
if (plugin->instances[i])
|
||||
save_instance(w, plugin->instances[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void RE_plugin_deserialize(struct RE_plugin *plugin, struct iarray_reader *r, int version)
|
||||
{
|
||||
read_vec3(r, plugin->camera.pos);
|
||||
plugin->camera.pitch = iarray_read_float(r);
|
||||
plugin->camera.roll = iarray_read_float(r);
|
||||
plugin->camera.yaw = iarray_read_float(r);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++)
|
||||
plugin->proj_transform[i][j] = iarray_read_float(r);
|
||||
}
|
||||
int viewport_x = iarray_read(r);
|
||||
int viewport_y = iarray_read(r);
|
||||
int viewport_width = iarray_read(r);
|
||||
int viewport_height = iarray_read(r);
|
||||
if (viewport_width > 0 && viewport_height > 0)
|
||||
RE_set_viewport(plugin, viewport_x, viewport_y, viewport_width, viewport_height);
|
||||
|
||||
int nr_back_cgs = iarray_read(r);
|
||||
for (int i = 0; i < nr_back_cgs; i++)
|
||||
load_back_cg(r, i < RE_NR_BACK_CGS ? &plugin->back_cg[i] : NULL, plugin->aar);
|
||||
|
||||
plugin->render_mode = iarray_read(r);
|
||||
plugin->shadow_mode = iarray_read(r);
|
||||
read_vec3(r, plugin->shadow_map_light_dir);
|
||||
plugin->shadow_bias = iarray_read_float(r);
|
||||
plugin->bump_mode = iarray_read(r);
|
||||
plugin->bloom_mode = iarray_read(r);
|
||||
plugin->glare_mode = iarray_read(r);
|
||||
plugin->fog_mode = iarray_read(r);
|
||||
plugin->fog_type = iarray_read(r);
|
||||
plugin->fog_near = iarray_read_float(r);
|
||||
plugin->fog_far = iarray_read_float(r);
|
||||
read_vec3(r, plugin->fog_color);
|
||||
plugin->ls_beta_r = iarray_read_float(r);
|
||||
plugin->ls_beta_m = iarray_read_float(r);
|
||||
plugin->ls_g = iarray_read_float(r);
|
||||
plugin->ls_distance = iarray_read_float(r);
|
||||
read_vec3(r, plugin->ls_light_dir);
|
||||
read_vec3(r, plugin->ls_light_color);
|
||||
read_vec3(r, plugin->ls_sun_color);
|
||||
plugin->specular_mode = iarray_read(r);
|
||||
plugin->light_map_mode = iarray_read(r);
|
||||
plugin->soft_fog_edge_mode = iarray_read(r);
|
||||
plugin->ssao_mode = iarray_read(r);
|
||||
plugin->vertex_blend_mode = iarray_read(r);
|
||||
plugin->shader_precision_mode = iarray_read(r);
|
||||
plugin->texture_filter_mode = iarray_read(r);
|
||||
plugin->use_power2_texture = iarray_read(r);
|
||||
plugin->shadow_map_resolution_level = iarray_read(r);
|
||||
plugin->texture_resolution_level = iarray_read(r);
|
||||
read_vec3(r, plugin->global_ambient);
|
||||
plugin->post_effect_filter_y = iarray_read_float(r);
|
||||
plugin->post_effect_filter_cb = iarray_read_float(r);
|
||||
plugin->post_effect_filter_cr = iarray_read_float(r);
|
||||
int nr_draw_options = iarray_read(r);
|
||||
for (int i = 0; i < nr_draw_options; i++) {
|
||||
int option = iarray_read(r);
|
||||
if (i < RE_DRAW_OPTION_MAX)
|
||||
plugin->draw_options[i] = option;
|
||||
}
|
||||
plugin->suspended = iarray_read(r);
|
||||
|
||||
plugin->mag_speed = iarray_read(r);
|
||||
int nr_light_params = iarray_read(r);
|
||||
for (int i = 0; i < nr_light_params; i++) {
|
||||
float param = iarray_read_float(r);
|
||||
if (i < RE_NR_LIGHT_PARAMS)
|
||||
plugin->light_params[i] = param;
|
||||
}
|
||||
plugin->edge_length = iarray_read_float(r);
|
||||
plugin->edge_reduction_rate = iarray_read_float(r);
|
||||
read_vec3(r, plugin->edge_color);
|
||||
|
||||
int nr_instances = iarray_read(r);
|
||||
for (int i = 0; i < nr_instances; i++) {
|
||||
if (!iarray_read(r))
|
||||
continue;
|
||||
load_instance(r, RE_create_instance_at(plugin, i));
|
||||
}
|
||||
}
|
||||
+52
-102
@@ -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>
|
||||
@@ -52,6 +51,7 @@ 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";
|
||||
}
|
||||
@@ -75,7 +75,6 @@ struct asset_manager_ald {
|
||||
struct asset_manager_afa {
|
||||
struct asset_manager manager;
|
||||
struct afa_archive *archives[MAX_ARCHIVES];
|
||||
struct hash_table *index;
|
||||
};
|
||||
|
||||
static struct asset_manager *assets[ASSET_TYPE_MAX] = {0};
|
||||
@@ -86,7 +85,10 @@ bool asset_manager_load_archive(enum asset_type type, const char *archive_name)
|
||||
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);
|
||||
char *path = gamedir_path(archive_name);
|
||||
bool r = assets[type]->load_archive(assets[type], path);
|
||||
free(path);
|
||||
return r;
|
||||
}
|
||||
|
||||
bool asset_exists(enum asset_type type, int id)
|
||||
@@ -197,111 +199,59 @@ static bool afa_load_archive(struct asset_manager *_manager, const char *name)
|
||||
manager->archives[i] = manager->archives[i-1];
|
||||
}
|
||||
manager->archives[0] = ar;
|
||||
|
||||
// index needs to be rebuilt
|
||||
if (manager->index) {
|
||||
ht_free(manager->index);
|
||||
manager->index = NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _afa_get_by_id(struct asset_manager *_manager, int id, struct afa_archive **ar_out, int *no_out)
|
||||
static bool afa_exists_by_id(struct asset_manager *_manager, int id)
|
||||
{
|
||||
struct asset_manager_afa *manager = (struct asset_manager_afa*)_manager;
|
||||
uint32_t no = id - 1;
|
||||
for (int i = 0; i < MAX_ARCHIVES && manager->archives[i]; i++) {
|
||||
if (no < manager->archives[i]->nr_files) {
|
||||
*ar_out = manager->archives[i];
|
||||
*no_out = no;
|
||||
if (archive_exists(&manager->archives[i]->ar, id - 1))
|
||||
return true;
|
||||
}
|
||||
no -= manager->archives[i]->nr_files;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool afa_exists_by_id(struct asset_manager *manager, int id)
|
||||
{
|
||||
int no;
|
||||
struct afa_archive *ar;
|
||||
return _afa_get_by_id(manager, id, &ar, &no);
|
||||
}
|
||||
|
||||
static struct archive_data *afa_get_by_id(struct asset_manager *manager, int id)
|
||||
{
|
||||
int no;
|
||||
struct afa_archive *ar;
|
||||
if (!_afa_get_by_id(manager, id, &ar, &no))
|
||||
return NULL;
|
||||
// XXX: we don't use archive_get here because it will return the wrong thing
|
||||
// for AFAv1 archives when not using integer indices
|
||||
struct archive_data *data = afa_entry_to_descriptor(ar, &ar->files[no]);
|
||||
if (data)
|
||||
archive_load_file(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
static void afa_index_archive(struct hash_table *index, struct afa_archive *ar, unsigned base)
|
||||
{
|
||||
for (unsigned i = 0; i < ar->nr_files; i++) {
|
||||
// normalize case and remove file extension from file name
|
||||
char *name = strdup(ar->files[i].name->text);
|
||||
char *dot = strrchr(name, '.');
|
||||
if (dot)
|
||||
*dot = '\0';
|
||||
sjis_toupper(name);
|
||||
// add file to index
|
||||
ht_put(index, name, (void*)((uintptr_t)base + i + 1));
|
||||
free(name);
|
||||
}
|
||||
}
|
||||
|
||||
static bool _afa_get_by_name(struct asset_manager *_manager, const char *_name,
|
||||
struct afa_archive **ar_out, int *no_out, int *id_out)
|
||||
static struct archive_data *afa_get_by_id(struct asset_manager *_manager, int id)
|
||||
{
|
||||
struct asset_manager_afa *manager = (struct asset_manager_afa*)_manager;
|
||||
// initialize index lazily
|
||||
if (!manager->index) {
|
||||
manager->index = ht_create(4096);
|
||||
unsigned base = 0;
|
||||
for (unsigned i = 0; i < MAX_ARCHIVES && manager->archives[i]; i++) {
|
||||
afa_index_archive(manager->index, manager->archives[i], base);
|
||||
base += manager->archives[i]->nr_files;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// normalize name and get id from index
|
||||
char *name = strdup(_name);
|
||||
sjis_toupper(name);
|
||||
char *dot = strrchr(name, '.');
|
||||
if (dot)
|
||||
*dot = '\0';
|
||||
uintptr_t id = (uintptr_t)ht_get(manager->index, name, NULL);
|
||||
free(name);
|
||||
|
||||
if (!id)
|
||||
return false;
|
||||
|
||||
if (id_out)
|
||||
*id_out = id;
|
||||
return _afa_get_by_id(_manager, id, ar_out, no_out);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool afa_exists_by_name(struct asset_manager *manager, const char *name, int *id_out)
|
||||
static bool afa_exists_by_name(struct asset_manager *_manager, const char *name, int *id_out)
|
||||
{
|
||||
int no;
|
||||
struct afa_archive *ar;
|
||||
return _afa_get_by_name(manager, name, &ar, &no, id_out);
|
||||
}
|
||||
struct asset_manager_afa *manager = (struct asset_manager_afa*)_manager;
|
||||
|
||||
static struct archive_data *afa_get_by_name(struct asset_manager *manager, const char *name, int *id_out)
|
||||
{
|
||||
int no;
|
||||
struct afa_archive *ar;
|
||||
if (!_afa_get_by_name(manager, name, &ar, &no, id_out))
|
||||
return NULL;
|
||||
return archive_get(&ar->ar, no);
|
||||
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)
|
||||
@@ -347,18 +297,14 @@ static void afa_init(enum asset_type type, char *file)
|
||||
if (!ar)
|
||||
ERROR("Failed to open AFA file: %s", archive_strerror(error));
|
||||
|
||||
if (id_indexed_afa) {
|
||||
_ald_init(type, &ar->ar);
|
||||
} else {
|
||||
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.exists_by_name = afa_exists_by_name;
|
||||
manager->manager.get_by_id = afa_get_by_id;
|
||||
manager->manager.get_by_name = afa_get_by_name;
|
||||
manager->archives[0] = ar;
|
||||
assets[type] = &manager->manager;
|
||||
}
|
||||
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);
|
||||
}
|
||||
@@ -391,7 +337,7 @@ void asset_manager_init(void)
|
||||
// get ALD filenames
|
||||
while ((d_name = readdir_utf8(dir))) {
|
||||
// archive filename must begin with ain filename
|
||||
if (strncmp(base, d_name, base_len))
|
||||
if (strncasecmp(base, d_name, base_len))
|
||||
goto loop_next;
|
||||
const char *ext = file_extension(d_name);
|
||||
if (!ext)
|
||||
@@ -460,6 +406,8 @@ void asset_manager_init(void)
|
||||
afa_filenames[ASSET_FLAT] = path_join(config.game_dir, d_name);
|
||||
} else if (!strcmp(type, "Pact.afa")) {
|
||||
afa_filenames[ASSET_PACT] = path_join(config.game_dir, d_name);
|
||||
} else if (!strcmp(type, "AFF.afa")) {
|
||||
afa_filenames[ASSET_FLASH] = path_join(config.game_dir, d_name);
|
||||
}
|
||||
} else if (!strcasecmp(ext, "fnl")) {
|
||||
if (!config.fnl_path)
|
||||
@@ -479,6 +427,7 @@ void asset_manager_init(void)
|
||||
ald_init(ASSET_FLAT, ald_filenames[ASSET_FLAT], ald_count[ASSET_FLAT]);
|
||||
ald_init(ASSET_PACT, ald_filenames[ASSET_PACT], ald_count[ASSET_PACT]);
|
||||
ald_init(ASSET_DATA, ald_filenames[ASSET_DATA], ald_count[ASSET_DATA]);
|
||||
ald_init(ASSET_FLASH, ald_filenames[ASSET_FLASH], ald_count[ASSET_FLASH]);
|
||||
|
||||
// open AFA archives
|
||||
afa_init(ASSET_BGM, afa_filenames[ASSET_BGM]);
|
||||
@@ -488,4 +437,5 @@ void asset_manager_init(void)
|
||||
afa_init(ASSET_FLAT, afa_filenames[ASSET_FLAT]);
|
||||
afa_init(ASSET_PACT, afa_filenames[ASSET_PACT]);
|
||||
afa_init(ASSET_DATA, afa_filenames[ASSET_DATA]);
|
||||
afa_init(ASSET_FLASH, afa_filenames[ASSET_FLASH]);
|
||||
}
|
||||
|
||||
+91
-12
@@ -43,8 +43,8 @@ void audio_init(void)
|
||||
if (audio_initialized)
|
||||
return;
|
||||
|
||||
id_pool_init(&wav);
|
||||
id_pool_init(&bgm);
|
||||
id_pool_init(&wav, 0);
|
||||
id_pool_init(&bgm, 0);
|
||||
|
||||
for (int i = 0; i < NR_ANONYMOUS_CHANNELS; i++) {
|
||||
anonymous_channels[i] = -1;
|
||||
@@ -54,6 +54,19 @@ void audio_init(void)
|
||||
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)
|
||||
{
|
||||
for (int i = 0; i < NR_ANONYMOUS_CHANNELS; i++) {
|
||||
@@ -85,6 +98,25 @@ bool audio_play_sound(int sound_no)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool audio_play_archive_data(struct archive_data *dfile)
|
||||
{
|
||||
for (int i = 0; i < NR_ANONYMOUS_CHANNELS; i++) {
|
||||
if (anonymous_channels[i] == -1) {
|
||||
int ch = wav_get_unused_channel();
|
||||
if (!wav_prepare_from_archive_data(ch, AUDIO_NO_METADATA, dfile))
|
||||
return false;
|
||||
if (!wav_play(ch)) {
|
||||
wav_unprepare(ch);
|
||||
return false;
|
||||
}
|
||||
anonymous_channels[i] = ch;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
WARNING("Failed to allocate anonymous audio channel");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool wav_exists(int no) { return asset_exists(ASSET_SOUND, no); }
|
||||
bool bgm_exists(int no) { return asset_exists(ASSET_BGM, no); }
|
||||
|
||||
@@ -104,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;
|
||||
}
|
||||
|
||||
@@ -330,24 +361,72 @@ static int audio_get_time_length(struct id_pool *pool, int id)
|
||||
int wav_get_time_length(int id) { return audio_get_time_length(&wav, id); }
|
||||
int bgm_get_time_length(int id) { return audio_get_time_length(&bgm, id); }
|
||||
|
||||
//int wav_get_group_num(int channel);
|
||||
//int bgm_get_group_num(int channel);
|
||||
//int wav_prepare_from_file(int channel, char *filename);
|
||||
//int bgm_prepare_from_file(int channel, char *filename);
|
||||
static int audio_get_data_length(enum asset_type type, int no) {
|
||||
struct channel *ch = channel_open(type, no);
|
||||
if (!ch)
|
||||
return 0;
|
||||
int len = channel_get_time_length(ch);
|
||||
channel_close(ch);
|
||||
return len;
|
||||
}
|
||||
|
||||
static int audio_prepare_from_archive_data(struct id_pool *pool, int id, struct archive_data *dfile)
|
||||
int wav_get_data_length(int no) { return audio_get_data_length(ASSET_SOUND, no); }
|
||||
int bgm_get_data_length(int no) { return audio_get_data_length(ASSET_BGM, no); }
|
||||
|
||||
static int audio_get_data_no(struct id_pool *pool, int id)
|
||||
{
|
||||
struct channel *ch = id_pool_get(pool, id);
|
||||
if (!ch)
|
||||
return -1;
|
||||
return channel_get_data_no(ch);
|
||||
}
|
||||
|
||||
int wav_get_group_num(int id)
|
||||
{
|
||||
int no = audio_get_data_no(&wav, id);
|
||||
if (no < 0)
|
||||
return 0;
|
||||
return wav_get_group_num_from_data_num(no);
|
||||
}
|
||||
//int bgm_get_group_num(int channel);
|
||||
|
||||
static int audio_prepare_from_file(struct id_pool *pool, int id, char *filename)
|
||||
{
|
||||
if (id < 0)
|
||||
return 0;
|
||||
struct channel *ch = channel_open_archive_data(dfile);
|
||||
struct channel *ch = channel_open_file(filename);
|
||||
struct channel *old = id_pool_set(pool, id, ch);
|
||||
if (old)
|
||||
channel_close(old);
|
||||
return !!ch;
|
||||
}
|
||||
|
||||
int wav_prepare_from_archive_data(int id, struct archive_data *dfile) {
|
||||
return audio_prepare_from_archive_data(&wav, id, dfile);
|
||||
int wav_prepare_from_file(int id, char *filename) { return audio_prepare_from_file(&wav, id, filename); }
|
||||
int bgm_prepare_from_file(int id, char *filename) { return audio_prepare_from_file(&bgm, id, filename); }
|
||||
|
||||
static int audio_prepare_from_archive_data(struct id_pool *pool, int id,
|
||||
enum asset_type type, int metadata_no,
|
||||
struct archive_data *dfile)
|
||||
{
|
||||
if (id < 0)
|
||||
return 0;
|
||||
struct channel *ch = channel_open_archive_data(dfile, type, metadata_no);
|
||||
struct channel *old = id_pool_set(pool, id, ch);
|
||||
if (old)
|
||||
channel_close(old);
|
||||
return !!ch;
|
||||
}
|
||||
|
||||
int wav_prepare_from_archive_data(int id, int metadata_no, struct archive_data *dfile)
|
||||
{
|
||||
return audio_prepare_from_archive_data(
|
||||
&wav, id, ASSET_SOUND, metadata_no, dfile);
|
||||
}
|
||||
|
||||
int bgm_prepare_from_archive_data(int id, int metadata_no, struct archive_data *dfile)
|
||||
{
|
||||
return audio_prepare_from_archive_data(
|
||||
&bgm, id, ASSET_BGM, metadata_no, dfile);
|
||||
}
|
||||
|
||||
int wav_get_group_num_from_data_num(int no)
|
||||
|
||||
+2
-2
@@ -18,9 +18,9 @@
|
||||
#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
|
||||
@@ -31,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;
|
||||
|
||||
+81
-42
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/archive.h"
|
||||
#include "system4/utfsjis.h"
|
||||
|
||||
#include "asset_manager.h"
|
||||
#include "audio.h"
|
||||
@@ -238,6 +239,7 @@ static int refill_stream(sts_mixer_sample_t *sample, void *data)
|
||||
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;
|
||||
@@ -437,6 +439,11 @@ int channel_get_time_length(struct channel *ch)
|
||||
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)
|
||||
{
|
||||
return ((struct channel*)data)->dfile->size;
|
||||
@@ -498,48 +505,24 @@ struct channel *channel_open(enum asset_type type, int no)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct channel *ch = channel_open_archive_data(dfile);
|
||||
struct channel *ch = channel_open_archive_data(dfile, type, no);
|
||||
if (!ch) {
|
||||
WARNING("Failed to open %s %d", type == ASSET_SOUND ? "WAV" : "BGM", no);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (type == ASSET_SOUND) {
|
||||
struct wai *wai = wai_get(no);
|
||||
ch->volume = 100;
|
||||
ch->loop_start = 0;
|
||||
ch->loop_end = ch->info.frames;
|
||||
ch->loop_count = 1;
|
||||
ch->mixer_no = wai ? wai->channel : 1;
|
||||
} else {
|
||||
struct bgi *bgi = bgi_get(no);
|
||||
if (bgi) {
|
||||
ch->volume = clamp(0, 100, bgi->volume);
|
||||
ch->loop_start = clamp(0, ch->info.frames, bgi->loop_start);
|
||||
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);
|
||||
}
|
||||
}
|
||||
ch->no = 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
|
||||
@@ -554,23 +537,76 @@ 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,
|
||||
enum asset_type type, int metadata_no)
|
||||
{
|
||||
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;
|
||||
}
|
||||
if (metadata_no >= 0) {
|
||||
if (type == ASSET_SOUND) {
|
||||
struct wai *wai = wai_get(metadata_no);
|
||||
ch->mixer_no = wai ? wai->channel : 1;
|
||||
} else if (type == ASSET_BGM) {
|
||||
struct bgi *bgi = bgi_get(metadata_no);
|
||||
if (bgi) {
|
||||
ch->volume = clamp(0, 100, bgi->volume);
|
||||
ch->loop_start = clamp(0, ch->info.frames, bgi->loop_start);
|
||||
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 = metadata_no;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -586,6 +622,7 @@ void mixer_init(void)
|
||||
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.
|
||||
@@ -663,19 +700,21 @@ 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 >= nr_mixers)
|
||||
if (n < 0 || n >= config.mixer_nr_channels)
|
||||
return 0;
|
||||
free(mixers[n].name);
|
||||
mixers[n].name = strdup(name);
|
||||
@@ -684,7 +723,7 @@ int mixer_set_name(int n, const char *name)
|
||||
|
||||
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);
|
||||
@@ -695,7 +734,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);
|
||||
@@ -704,7 +743,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;
|
||||
@@ -712,7 +751,7 @@ 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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
+142
-15
@@ -24,7 +24,9 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include "system4/dasm.h"
|
||||
#include "system4/file.h"
|
||||
#include "system4/hashtable.h"
|
||||
#include "system4/little_endian.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/utfsjis.h"
|
||||
|
||||
@@ -33,11 +35,12 @@
|
||||
#include "vm/page.h"
|
||||
|
||||
#include "debugger.h"
|
||||
#include "little_endian.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
bool dbg_dap = false;
|
||||
bool dbg_enabled = true;
|
||||
bool dbg_start_in_debugger = false;
|
||||
struct dbg_info *dbg_info = NULL;
|
||||
unsigned dbg_current_frame = 0;
|
||||
static jmp_buf dbg_continuation;
|
||||
|
||||
@@ -72,24 +75,50 @@ void dbg_start(void(*fun)(void*), void *data)
|
||||
fun(data);
|
||||
}
|
||||
|
||||
static void _dbg_repl(void *_)
|
||||
static void _dbg_repl(void *stop)
|
||||
{
|
||||
dbg_cmd_repl();
|
||||
if (dbg_dap)
|
||||
dbg_dap_repl(stop);
|
||||
else
|
||||
dbg_cmd_repl();
|
||||
}
|
||||
|
||||
void dbg_repl(void)
|
||||
void dbg_repl(enum dbg_stop_type type, const char *msg)
|
||||
{
|
||||
if (!dbg_enabled)
|
||||
return;
|
||||
dbg_start(_dbg_repl, NULL);
|
||||
|
||||
struct dbg_stop stop = {
|
||||
.type = type,
|
||||
.message = msg,
|
||||
};
|
||||
|
||||
dbg_start(_dbg_repl, &stop);
|
||||
}
|
||||
|
||||
void dbg_init(void)
|
||||
void dbg_init(const char *debug_info_path)
|
||||
{
|
||||
dbg_cmd_init();
|
||||
if (debug_info_path) {
|
||||
dbg_info = dbg_info_load(debug_info_path);
|
||||
} else {
|
||||
// Try to load from the default location
|
||||
char *path = gamedir_path("src/debug_info.json");
|
||||
if (file_exists(path)) {
|
||||
dbg_info = dbg_info_load(path);
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
|
||||
if (dbg_dap)
|
||||
dbg_dap_init();
|
||||
else
|
||||
dbg_cmd_init();
|
||||
#ifdef HAVE_SCHEME
|
||||
dbg_scm_init();
|
||||
#endif
|
||||
if (!dbg_dap && dbg_start_in_debugger) {
|
||||
dbg_repl(DBG_STOP_PAUSE, "");
|
||||
}
|
||||
}
|
||||
|
||||
void dbg_fini(void)
|
||||
@@ -135,6 +164,38 @@ static struct breakpoint *get_breakpoint(uint32_t addr)
|
||||
return ht_get_int(bp_table, addr, NULL);
|
||||
}
|
||||
|
||||
bool dbg_clear_breakpoint(uint32_t addr, void(*free_data)(void*))
|
||||
{
|
||||
struct breakpoint *bp = get_breakpoint(addr);
|
||||
if (!bp)
|
||||
return false;
|
||||
if (free_data)
|
||||
free_data(bp->data);
|
||||
delete_breakpoint(addr, bp);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct dbg_foreach_data {
|
||||
void (*fun)(int addr, struct breakpoint*, void *data);
|
||||
void *data;
|
||||
};
|
||||
|
||||
void dbg_foreach_breakpoint_cb(struct ht_slot *slot, void *d_)
|
||||
{
|
||||
if (!slot->value)
|
||||
return;
|
||||
struct dbg_foreach_data *d = d_;
|
||||
d->fun(slot->ikey, slot->value, d->data);
|
||||
}
|
||||
|
||||
void dbg_foreach_breakpoint(void (*fun)(int addr, struct breakpoint*, void *data), void *data)
|
||||
{
|
||||
if (!bp_table)
|
||||
return;
|
||||
struct dbg_foreach_data d = { fun, data };
|
||||
ht_foreach(bp_table, dbg_foreach_breakpoint_cb, &d);
|
||||
}
|
||||
|
||||
bool dbg_set_function_breakpoint(const char *_name, void(*cb)(struct breakpoint*), void *data)
|
||||
{
|
||||
char *name = utf2sjis(_name, 0);
|
||||
@@ -157,7 +218,7 @@ bool dbg_set_function_breakpoint(const char *_name, void(*cb)(struct breakpoint*
|
||||
LittleEndian_putW(ain->code, f->address, BREAKPOINT | bp->restore_op);
|
||||
add_breakpoint(f->address, bp);
|
||||
|
||||
printf("Set breakpoint at function '%s' (0x%08x)\n", display_utf0(_name), f->address);
|
||||
log_message("debug", "Set breakpoint at function '%s' (0x%08x)\n", display_utf0(_name), f->address);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -185,7 +246,7 @@ bool dbg_set_address_breakpoint(uint32_t address, void(*cb)(struct breakpoint*),
|
||||
LittleEndian_putW(ain->code, address, BREAKPOINT | bp->restore_op);
|
||||
add_breakpoint(address, bp);
|
||||
|
||||
printf("Set breakpoint at 0x%08x\n", address);
|
||||
log_message("debug", "Set breakpoint at 0x%08x\n", address);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -196,7 +257,8 @@ static void dbg_step_breakpoint_cb(struct breakpoint *bp)
|
||||
if ((intptr_t)bp->data != call_stack_ptr)
|
||||
return;
|
||||
delete_breakpoint(instr_ptr, bp);
|
||||
dbg_cmd_repl();
|
||||
struct dbg_stop stop = { DBG_STOP_STEP, NULL };
|
||||
_dbg_repl(&stop);
|
||||
}
|
||||
|
||||
static void dbg_set_step_breakpoint(int32_t address, int call_index)
|
||||
@@ -384,8 +446,9 @@ static void _dbg_handle_breakpoint(void *data)
|
||||
if (bp->cb) {
|
||||
bp->cb(bp);
|
||||
} else {
|
||||
printf("%s\n", bp->message);
|
||||
dbg_cmd_repl();
|
||||
log_message("debug", "%s\n", bp->message);
|
||||
struct dbg_stop stop = { DBG_STOP_BREAKPOINT, NULL };
|
||||
_dbg_repl(&stop);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,8 +471,14 @@ void dbg_print_frame(unsigned no)
|
||||
unsigned cs_no = call_stack_ptr - (1 + no);
|
||||
struct ain_function *f = &ain->functions[call_stack[cs_no].fno];
|
||||
uint32_t addr = no ? call_stack[cs_no+1].call_address : instr_ptr;
|
||||
printf("%c #%d 0x%08x in %s\n", no == dbg_current_frame ? '*' : ' ',
|
||||
sys_message("%c #%d 0x%08x in %s", no == dbg_current_frame ? '*' : ' ',
|
||||
no, addr, display_sjis0(f->name));
|
||||
int file, line;
|
||||
if (dbg_info && dbg_info_addr2line(dbg_info, addr, &file, &line)) {
|
||||
sys_message(" at %s:%d\n", display_utf0(dbg_info_source_name(dbg_info, file)), line);
|
||||
} else {
|
||||
sys_message("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void dbg_print_stack_trace(void)
|
||||
@@ -570,7 +639,7 @@ error:
|
||||
static union vm_value dbg_eval_object(struct parse_object *object, struct ain_type *type_out)
|
||||
{
|
||||
if (!strcmp(object->ident->text, "this")) {
|
||||
int32_t page_i = call_stack[call_stack_ptr-1].struct_page;
|
||||
int32_t page_i = call_stack[call_stack_ptr- (dbg_current_frame + 1)].struct_page;
|
||||
if (page_i < 0) {
|
||||
DBG_ERROR("'this' used outside of method");
|
||||
goto error;
|
||||
@@ -695,6 +764,8 @@ static void dbg_indent(struct string **s, int indent_level)
|
||||
}
|
||||
static struct string *_dbg_value_to_string(struct ain_type *type, union vm_value value,
|
||||
int recursive, int indent);
|
||||
static struct string *_dbg_variable_to_string(
|
||||
struct ain_type *type, struct page *page, int slot, int recursive, int indent);
|
||||
|
||||
static struct string *dbg_string_to_string(struct string *str)
|
||||
{
|
||||
@@ -719,10 +790,12 @@ static struct string *dbg_struct_to_string(struct ain_type *type, union vm_value
|
||||
struct string *out = cstr_to_string("{\n");
|
||||
for (int i = 0; i < page->nr_vars; i++) {
|
||||
struct ain_variable *m = &ain->structures[type->struc].members[i];
|
||||
if (m->type.data == AIN_VOID)
|
||||
continue;
|
||||
dbg_indent(&out, indent + 1);
|
||||
string_append_cstr(&out, m->name, strlen(m->name));
|
||||
string_append_cstr(&out, " = ", 3);
|
||||
struct string *tmp = _dbg_value_to_string(&m->type, page->values[i],
|
||||
struct string *tmp = _dbg_variable_to_string(&m->type, page, i,
|
||||
recursive - 1, indent + 1);
|
||||
string_append(&out, tmp);
|
||||
free_string(tmp);
|
||||
@@ -802,6 +875,34 @@ struct string *dbg_value_to_string(struct ain_type *type, union vm_value value,
|
||||
return _dbg_value_to_string(type, value, recursive, 0);
|
||||
}
|
||||
|
||||
static struct string *_dbg_variable_to_string(
|
||||
struct ain_type *type, struct page *page, int slot, int recursive, int indent)
|
||||
{
|
||||
enum ain_data_type value_type;
|
||||
switch (type->data) {
|
||||
case AIN_REF_INT: value_type = AIN_INT; break;
|
||||
case AIN_REF_LONG_INT: value_type = AIN_LONG_INT; break;
|
||||
case AIN_REF_BOOL: value_type = AIN_BOOL; break;
|
||||
case AIN_REF_FLOAT: value_type = AIN_FLOAT; break;
|
||||
default:
|
||||
return _dbg_value_to_string(type, page->values[slot], recursive, indent);
|
||||
}
|
||||
|
||||
// get referenced value
|
||||
int pageno = page->values[slot].i;
|
||||
int varno = page->values[slot + 1].i;
|
||||
if (pageno < 0) {
|
||||
return cstr_to_string("NULL");
|
||||
}
|
||||
struct ain_type t = {.data = value_type};
|
||||
return _dbg_value_to_string(&t, heap[pageno].page->values[varno], recursive, indent);
|
||||
}
|
||||
|
||||
struct string *dbg_variable_to_string(struct ain_type *type, struct page *page, int slot, int recursive)
|
||||
{
|
||||
return _dbg_variable_to_string(type, page, slot, recursive, 0);
|
||||
}
|
||||
|
||||
// the maximum number of instructions preceeding the instruction pointer to be displayed
|
||||
#define DASM_REWIND 16
|
||||
// the number of instructions following the instruction pointer to be displayed
|
||||
@@ -1065,3 +1166,29 @@ void dbg_print_vm_state(void)
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
void dbg_print_current_line(void)
|
||||
{
|
||||
if (call_stack_ptr < 1) {
|
||||
DBG_ERROR("VM not running");
|
||||
return;
|
||||
}
|
||||
if (!dbg_info) {
|
||||
DBG_ERROR("No debug information available");
|
||||
return;
|
||||
}
|
||||
|
||||
int file, line;
|
||||
if (!dbg_info_addr2line(dbg_info, instr_ptr, &file, &line)) {
|
||||
DBG_ERROR("No line number information for address: 0x%08x", instr_ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
const char *src_line = dbg_info_source_line(dbg_info, file, line);
|
||||
if (!src_line) {
|
||||
DBG_ERROR("No source text for %s:%d", display_utf0(dbg_info_source_name(dbg_info, file)), line);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("%s:%d\t%s\n", display_utf0(dbg_info_source_name(dbg_info, file)), line, display_utf1(src_line));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
/* 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 <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "system4/file.h"
|
||||
|
||||
#include "cJSON.h"
|
||||
#include "debugger.h"
|
||||
|
||||
struct mapping {
|
||||
int addr;
|
||||
int file;
|
||||
int line;
|
||||
};
|
||||
|
||||
struct source_content {
|
||||
int nr_lines;
|
||||
char *lines[];
|
||||
};
|
||||
|
||||
struct dbg_info {
|
||||
char *src_root;
|
||||
int nr_sources;
|
||||
char **sources;
|
||||
struct source_content **source_contents;
|
||||
int nr_mappings;
|
||||
struct mapping *mappings;
|
||||
};
|
||||
|
||||
struct dbg_info *dbg_info_load(const char *path)
|
||||
{
|
||||
char *json_text = file_read(path, NULL);
|
||||
if (!json_text) {
|
||||
DBG_ERROR("Cannot load debug information file %s: %s", path, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON *root = cJSON_Parse(json_text);
|
||||
free(json_text);
|
||||
|
||||
cJSON *version = cJSON_GetObjectItem(root, "version");
|
||||
if (!cJSON_IsString(version) || strcmp(version->valuestring, "alpha-1")) {
|
||||
DBG_ERROR("%s: Unsupported debug information format", display_utf0(path));
|
||||
cJSON_Delete(root);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dbg_info *info = xcalloc(1, sizeof(struct dbg_info));
|
||||
cJSON *sources = cJSON_GetObjectItem(root, "sources");
|
||||
info->nr_sources = cJSON_GetArraySize(sources);
|
||||
info->sources = xcalloc(info->nr_sources, sizeof(char *));
|
||||
cJSON *source;
|
||||
int i = 0;
|
||||
cJSON_ArrayForEach(source, sources) {
|
||||
info->sources[i++] = xstrdup(source->valuestring);
|
||||
}
|
||||
|
||||
cJSON *mappings = cJSON_GetObjectItem(root, "mappings");
|
||||
info->nr_mappings = cJSON_GetArraySize(mappings);
|
||||
info->mappings = xcalloc(info->nr_mappings, sizeof(struct mapping));
|
||||
|
||||
struct mapping *m = info->mappings;
|
||||
cJSON *item;
|
||||
cJSON_ArrayForEach(item, mappings) {
|
||||
m->addr = cJSON_GetArrayItem(item, 0)->valueint;
|
||||
m->file = cJSON_GetArrayItem(item, 1)->valueint;
|
||||
m->line = cJSON_GetArrayItem(item, 2)->valueint;
|
||||
m++;
|
||||
}
|
||||
|
||||
cJSON_Delete(root);
|
||||
|
||||
info->src_root = xstrdup(path_dirname(path));
|
||||
info->source_contents = xcalloc(info->nr_sources, sizeof(struct source_content *));
|
||||
return info;
|
||||
}
|
||||
|
||||
static struct source_content *load_source_content(const char *path)
|
||||
{
|
||||
char *text = file_read(path, NULL);
|
||||
if (!text) {
|
||||
DBG_ERROR("Cannot load source file %s", path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int nr_lines = 1; // count the last line even if it doesn't end with '\n'
|
||||
for (char *p = text; *p; p++) {
|
||||
if (*p == '\n')
|
||||
nr_lines++;
|
||||
}
|
||||
struct source_content *content = xcalloc(1, sizeof(struct source_content) + nr_lines * sizeof(char *));
|
||||
content->nr_lines = nr_lines;
|
||||
for (int i = 0;; i++) {
|
||||
content->lines[i] = text;
|
||||
char *next = strchr(text, '\n');
|
||||
if (!next)
|
||||
break;
|
||||
*next = '\0';
|
||||
text = next + 1;
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
const char *dbg_info_source_name(const struct dbg_info *info, int file)
|
||||
{
|
||||
if (file < 0 || file >= info->nr_sources)
|
||||
return NULL;
|
||||
return info->sources[file];
|
||||
}
|
||||
|
||||
char *dbg_info_source_path(const struct dbg_info *info, int file)
|
||||
{
|
||||
if (file < 0 || file >= info->nr_sources)
|
||||
return NULL;
|
||||
return path_join(info->src_root, info->sources[file]);
|
||||
}
|
||||
|
||||
const char *dbg_info_source_line(const struct dbg_info *info, int file, int line)
|
||||
{
|
||||
if (file < 0 || file >= info->nr_sources)
|
||||
return NULL;
|
||||
if (!info->source_contents[file]) {
|
||||
char *path = dbg_info_source_path(info, file);
|
||||
info->source_contents[file] = load_source_content(path);
|
||||
free(path);
|
||||
if (!info->source_contents[file])
|
||||
return NULL;
|
||||
}
|
||||
if (line <= 0 || line > info->source_contents[file]->nr_lines)
|
||||
return NULL;
|
||||
return info->source_contents[file]->lines[line - 1];
|
||||
}
|
||||
|
||||
int dbg_info_find_file(const struct dbg_info *info, const char *filename)
|
||||
{
|
||||
if (is_absolute_path(filename)) {
|
||||
char *target = realpath_utf8(filename); // Convert '\\' to '/' on Windows
|
||||
char *prefix = realpath_utf8(info->src_root);
|
||||
for (int i = 0; i < info->nr_sources; i++) {
|
||||
char *path = path_join(prefix, info->sources[i]);
|
||||
if (!strcasecmp(path, target)) {
|
||||
free(path);
|
||||
free(prefix);
|
||||
free(target);
|
||||
return i;
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
free(prefix);
|
||||
free(target);
|
||||
} else {
|
||||
for (int i = 0; i < info->nr_sources; i++) {
|
||||
if (!strcasecmp(info->sources[i], filename) ||
|
||||
!strcasecmp(path_basename(info->sources[i]), filename)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool dbg_info_addr2line(const struct dbg_info *info, int addr, int *file, int *line)
|
||||
{
|
||||
// Find the last mapping whose address is less than or equal to `addr`.
|
||||
int left = 0, right = info->nr_mappings;
|
||||
while (left < right) {
|
||||
int mid = (left + right) / 2;
|
||||
if (info->mappings[mid].addr <= addr)
|
||||
left = mid + 1;
|
||||
else
|
||||
right = mid;
|
||||
}
|
||||
if (left == 0) {
|
||||
assert(info->nr_mappings == 0 || addr < info->mappings[0].addr);
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(info->mappings[left - 1].addr <= addr);
|
||||
assert(left == info->nr_mappings || addr < info->mappings[left].addr);
|
||||
|
||||
if (file)
|
||||
*file = info->mappings[left - 1].file;
|
||||
if (line)
|
||||
*line = info->mappings[left - 1].line;
|
||||
return true;
|
||||
}
|
||||
|
||||
int dbg_info_line2addr(const struct dbg_info *info, int file, int line)
|
||||
{
|
||||
if (file < 0 || file >= info->nr_sources || line <= 0)
|
||||
return -1;
|
||||
|
||||
// Find the first mapping whose file is `file` and line is greater than or equal to `line`.
|
||||
int left = 0, right = info->nr_mappings;
|
||||
while (left < right) {
|
||||
int mid = (left + right) / 2;
|
||||
if (info->mappings[mid].file < file || (info->mappings[mid].file == file && info->mappings[mid].line < line))
|
||||
left = mid + 1;
|
||||
else
|
||||
right = mid;
|
||||
}
|
||||
if (left == info->nr_mappings) {
|
||||
assert(info->nr_mappings == 0 || info->mappings[info->nr_mappings - 1].file < file ||
|
||||
(info->mappings[info->nr_mappings - 1].file == file && info->mappings[info->nr_mappings - 1].line < line));
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(left == 0 || info->mappings[left - 1].file < file ||
|
||||
(info->mappings[left - 1].file == file && info->mappings[left - 1].line < line));
|
||||
if (file < info->mappings[left].file)
|
||||
return -1;
|
||||
assert(file == info->mappings[left].file && line <= info->mappings[left].line);
|
||||
|
||||
return info->mappings[left].addr;
|
||||
}
|
||||
+80
-16
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/file.h"
|
||||
#include "system4/little_endian.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/utfsjis.h"
|
||||
|
||||
@@ -35,11 +36,18 @@
|
||||
|
||||
#include "scene.h"
|
||||
#include "debugger.h"
|
||||
#include "little_endian.h"
|
||||
#include "input.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
struct dbg_cmd_node;
|
||||
static bool stepping = false;
|
||||
static enum {
|
||||
NOT_STEPPING = 0,
|
||||
STEPPING_INTO,
|
||||
STEPPING_OVER,
|
||||
STEPPING_FINISH
|
||||
} stepping = NOT_STEPPING;
|
||||
static int stepping_file = 0;
|
||||
static int stepping_line = 0;
|
||||
|
||||
struct dbg_cmd_list {
|
||||
unsigned nr_commands;
|
||||
@@ -66,17 +74,33 @@ static void dbg_cmd_backtrace(unsigned nr_args, char **args)
|
||||
|
||||
static void dbg_cmd_breakpoint(unsigned nr_args, char **args)
|
||||
{
|
||||
int addr = strtol(args[0], NULL, 0);
|
||||
if (addr > 0) {
|
||||
if (nr_args == 1) {
|
||||
int addr = strtol(args[0], NULL, 0);
|
||||
if (addr > 0) {
|
||||
dbg_set_address_breakpoint(addr, NULL, NULL);
|
||||
} else {
|
||||
dbg_set_function_breakpoint(args[0], NULL, NULL);
|
||||
}
|
||||
} else if (nr_args == 2 && dbg_info) {
|
||||
int file = dbg_info_find_file(dbg_info, args[0]);
|
||||
if (file < 0) {
|
||||
DBG_ERROR("No such file: %s", args[0]);
|
||||
return;
|
||||
}
|
||||
int line = atoi(args[1]);
|
||||
int addr = dbg_info_line2addr(dbg_info, file, line);
|
||||
if (addr < 0) {
|
||||
DBG_ERROR("No such line: %s:%d", args[0], line);
|
||||
return;
|
||||
}
|
||||
dbg_set_address_breakpoint(addr, NULL, NULL);
|
||||
} else {
|
||||
dbg_set_function_breakpoint(args[0], NULL, NULL);
|
||||
DBG_ERROR("Invalid breakpoint command");
|
||||
}
|
||||
}
|
||||
|
||||
static void dbg_cmd_continue(unsigned nr_args, char **args)
|
||||
{
|
||||
stepping = false;
|
||||
dbg_continue();
|
||||
}
|
||||
|
||||
@@ -102,7 +126,9 @@ static void dbg_cmd_locals(unsigned nr_args, char **args)
|
||||
struct page *page = heap_get_page(call_stack[call_stack_ptr - (frame_no+1)].page_slot);
|
||||
struct ain_function *f = &ain->functions[page->index];
|
||||
for (int i = 0; i < f->nr_vars; i++) {
|
||||
struct string *value = dbg_value_to_string(&f->vars[i].type, page->values[i], 1);
|
||||
if (f->vars[i].type.data == AIN_VOID)
|
||||
continue;
|
||||
struct string *value = dbg_variable_to_string(&f->vars[i].type, page, i, 1);
|
||||
printf("[%d] %s: %s\n", i, display_sjis0(f->vars[i].name), display_sjis1(value->text));
|
||||
free_string(value);
|
||||
}
|
||||
@@ -114,7 +140,9 @@ static void log_handler(struct breakpoint *bp)
|
||||
struct page *locals = local_page();
|
||||
printf("%s(", display_sjis0(f->name));
|
||||
for (int i = 0; i < f->nr_args; i++) {
|
||||
struct string *value = dbg_value_to_string(&f->vars[i].type, locals->values[i], 1);
|
||||
if (f->vars[i].type.data == AIN_VOID)
|
||||
continue;
|
||||
struct string *value = dbg_variable_to_string(&f->vars[i].type, locals, i, 1);
|
||||
printf(i > 0 ? ", %s" : "%s", display_sjis0(value->text));
|
||||
free_string(value);
|
||||
}
|
||||
@@ -151,7 +179,9 @@ static void dbg_cmd_members(unsigned nr_args, char **args)
|
||||
assert(page->nr_vars == s->nr_members);
|
||||
|
||||
for (int i = 0; i < s->nr_members; i++) {
|
||||
struct string *value = dbg_value_to_string(&s->members[i].type, page->values[i], 1);
|
||||
if (s->members[i].type.data == AIN_VOID)
|
||||
continue;
|
||||
struct string *value = dbg_variable_to_string(&s->members[i].type, page, i, 1);
|
||||
printf("[%d] %s: %s\n", i, display_sjis0(s->members[i].name), display_sjis1(value->text));
|
||||
free_string(value);
|
||||
}
|
||||
@@ -183,21 +213,29 @@ static void dbg_cmd_scene(unsigned nr_args, char **args)
|
||||
|
||||
static void dbg_cmd_next(unsigned nr_args, char **args)
|
||||
{
|
||||
stepping_file = stepping_line = 0;
|
||||
if (dbg_info)
|
||||
dbg_info_addr2line(dbg_info, instr_ptr, &stepping_file, &stepping_line);
|
||||
|
||||
if (!dbg_set_step_over_breakpoint()) {
|
||||
DBG_ERROR("Can't step over this instruction");
|
||||
return;
|
||||
}
|
||||
stepping = true;
|
||||
stepping = STEPPING_OVER;
|
||||
dbg_continue();
|
||||
}
|
||||
|
||||
static void dbg_cmd_step(unsigned nr_args, char **args)
|
||||
{
|
||||
stepping_file = stepping_line = 0;
|
||||
if (dbg_info)
|
||||
dbg_info_addr2line(dbg_info, instr_ptr, &stepping_file, &stepping_line);
|
||||
|
||||
if (!dbg_set_step_into_breakpoint()) {
|
||||
DBG_ERROR("Can't step into this instruction");
|
||||
return;
|
||||
}
|
||||
stepping = true;
|
||||
stepping = STEPPING_INTO;
|
||||
dbg_continue();
|
||||
}
|
||||
|
||||
@@ -207,7 +245,7 @@ static void dbg_cmd_finish(unsigned nr_args, char **args)
|
||||
DBG_ERROR("Can't set finish breakpoint from this location");
|
||||
return;
|
||||
}
|
||||
stepping = true;
|
||||
stepping = STEPPING_FINISH;
|
||||
dbg_continue();
|
||||
}
|
||||
|
||||
@@ -225,7 +263,7 @@ static void dbg_cmd_vm_state(unsigned nr_args, char **args)
|
||||
|
||||
static struct dbg_cmd dbg_default_commands[] = {
|
||||
{ "backtrace", "bt", NULL, "Display stack trace", 0, 0, dbg_cmd_backtrace },
|
||||
{ "breakpoint", "bp", "<function-or-address>", "Set breakpoint at a function or address", 1, 1, dbg_cmd_breakpoint },
|
||||
{ "breakpoint", "bp", "<function> | <address> | <file> <line>", "Set breakpoint", 1, 2, dbg_cmd_breakpoint },
|
||||
{ "continue", "c", NULL, "Resume execution", 0, 0, dbg_cmd_continue },
|
||||
{ "finish", "fin", NULL, "Execute until the current function returns", 0, 0, dbg_cmd_finish },
|
||||
{ "frame", "f", "<frame-number>", "Set the current frame", 1, 1, dbg_cmd_frame },
|
||||
@@ -374,11 +412,19 @@ void dbg_cmd_add_module(const char *name, unsigned nr_commands, struct dbg_cmd *
|
||||
#ifdef HAVE_READLINE
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
|
||||
static int cmd_rl_event_hook(void)
|
||||
{
|
||||
handle_window_events();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a string from the user. The result can be modified but shouldn't be free'd.
|
||||
*/
|
||||
static char *cmd_gets(void)
|
||||
{
|
||||
rl_event_hook = cmd_rl_event_hook;
|
||||
static char *line_read = NULL;
|
||||
free(line_read);
|
||||
line_read = readline("dbg(cmd)> ");
|
||||
@@ -394,6 +440,7 @@ static char *cmd_gets(void)
|
||||
{
|
||||
static char line[1024];
|
||||
|
||||
handle_window_events();
|
||||
printf("dbg(cmd)> ");
|
||||
fflush(stdout);
|
||||
fgets(line, 1024, stdin);
|
||||
@@ -467,10 +514,27 @@ static void execute_line(char *line)
|
||||
|
||||
void dbg_cmd_repl(void)
|
||||
{
|
||||
if (stepping)
|
||||
dbg_print_vm_state();
|
||||
else
|
||||
if (stepping) {
|
||||
if (stepping_line) {
|
||||
int file, line;
|
||||
dbg_info_addr2line(dbg_info, instr_ptr, &file, &line);
|
||||
if (file == stepping_file && line == stepping_line) {
|
||||
if (stepping == STEPPING_INTO)
|
||||
dbg_set_step_into_breakpoint();
|
||||
else if (stepping == STEPPING_OVER)
|
||||
dbg_set_step_over_breakpoint();
|
||||
dbg_continue();
|
||||
}
|
||||
}
|
||||
stepping = NOT_STEPPING;
|
||||
stepping_file = stepping_line = 0;
|
||||
if (dbg_info)
|
||||
dbg_print_current_line();
|
||||
else
|
||||
dbg_print_vm_state();
|
||||
} else {
|
||||
puts("Entering the debugger REPL. Type 'help' for a list of commands.");
|
||||
}
|
||||
while (1) {
|
||||
char *line = cmd_gets();
|
||||
if (line)
|
||||
|
||||
+1110
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -23,6 +23,7 @@
|
||||
#include "system4/ain.h"
|
||||
#include "system4/file.h"
|
||||
#include "system4/instructions.h"
|
||||
#include "system4/little_endian.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/utfsjis.h"
|
||||
#include "vm.h"
|
||||
@@ -30,7 +31,6 @@
|
||||
#include "vm/page.h"
|
||||
|
||||
#include "debugger.h"
|
||||
#include "little_endian.h"
|
||||
|
||||
struct variable {
|
||||
enum ain_data_type data_type;
|
||||
|
||||
+187
-17
@@ -64,6 +64,7 @@ static struct copy_shader copy_key_shader;
|
||||
static struct copy_shader copy_alpha_key_shader;
|
||||
static struct copy_shader copy_use_amap_under_shader;
|
||||
static struct copy_shader copy_use_amap_border_shader;
|
||||
static struct copy_shader copy_grayscale_shader;
|
||||
static struct copy_shader blend_amap_color_shader;
|
||||
static struct copy_shader blend_amap_alpha_bright_shader;
|
||||
static struct copy_shader blend_use_amap_color_shader;
|
||||
@@ -121,6 +122,9 @@ void gfx_draw_init(void)
|
||||
// copy shader that only keeps fragments above a certain alpha threshold
|
||||
load_copy_shader(©_use_amap_border_shader, "shaders/render.v.glsl", "shaders/copy_use_amap_border.f.glsl");
|
||||
|
||||
// copy shader with grayscale conversion
|
||||
load_copy_shader(©_grayscale_shader, "shaders/render.v.glsl", "shaders/copy_grayscale.f.glsl");
|
||||
|
||||
// copy shader that sets source RGB to a constant
|
||||
load_copy_shader(&blend_amap_color_shader, "shaders/render.v.glsl", "shaders/blend_amap_color.f.glsl");
|
||||
|
||||
@@ -163,6 +167,7 @@ static void run_draw_shader(Shader *s, Texture *dst, Texture *src, mat4 mw_trans
|
||||
|
||||
struct gfx_render_job job = {
|
||||
.shader = s,
|
||||
.shape = GFX_RECTANGLE,
|
||||
.texture = src ? src->handle : 0,
|
||||
.world_transform = mw_transform[0],
|
||||
.view_transform = wv_transform[0],
|
||||
@@ -702,6 +707,16 @@ void gfx_copy_stretch_blend(struct texture *dst, int dx, int dy, int dw, int dh,
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_COLOR, GL_ZERO, GL_ONE);
|
||||
|
||||
struct copy_data data = STRETCH_DATA(dx, dy, dw, dh, sx, sy, sw, sh);
|
||||
run_copy_shader(©_shader.s, dst, src, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
@@ -729,6 +744,8 @@ void gfx_copy_stretch_blend_amap_alpha(struct texture *dst, int dx, int dy, int
|
||||
// FIXME: this doesn't work correctly when the src rectangle crosses the edge of the CG.
|
||||
static void copy_rot_zoom(Texture *dst, Texture *src, int sx, int sy, int w, int h, float rotate, float mag, Shader *shader)
|
||||
{
|
||||
w = min(w, src->w - sx);
|
||||
h = min(h, src->h - sy);
|
||||
// 1. scale src vertices to texture size
|
||||
// 2. translate so that center point of copy region is at origin
|
||||
// 3. rotate
|
||||
@@ -768,18 +785,45 @@ void gfx_copy_rot_zoom_use_amap(Texture *dst, Texture *src, int sx, int sy, int
|
||||
copy_rot_zoom(dst, src, sx, sy, w, h, rotate, mag, &hitbox_shader.s);
|
||||
}
|
||||
|
||||
void gfx_copy_rot_zoom2(Texture *dst, float cx, float cy, Texture *src, float scx, float scy, float rot, float mag)
|
||||
{
|
||||
gfx_fill_amap(dst, 0, 0, dst->w, dst->h, 0);
|
||||
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);
|
||||
|
||||
// 1. scale src vertices to texture size
|
||||
// 2. translate so that center point of copy region is at origin
|
||||
// 3. rotate
|
||||
// 4. scale
|
||||
// 4. tranlate to center point of dst texture
|
||||
// (applied in reverse order)
|
||||
mat4 mw_transform = GLM_MAT4_IDENTITY_INIT;
|
||||
glm_translate(mw_transform, (vec3){ cx, cy, 0 });
|
||||
glm_scale(mw_transform, (vec3){ mag, mag, 0 });
|
||||
glm_rotate_z(mw_transform, -rot * (M_PI/180.0), mw_transform);
|
||||
glm_translate(mw_transform, (vec3){ -scx, -scy, 0 });
|
||||
glm_scale(mw_transform, (vec3){ src->w, src->h, 1 });
|
||||
|
||||
mat4 wv_transform = WV_TRANSFORM(dst->w, dst->h);
|
||||
|
||||
struct copy_data data = ROTATE_DATA(dst, 0, 0, src->w, src->h);
|
||||
run_draw_shader(&hitbox_shader.s, dst, src, mw_transform, wv_transform, &data);
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
static void copy_rotate_y(Texture *dst, Texture *front, Texture *back, int sx, int sy, int w, int h, float rot, float mag, Shader *shader)
|
||||
{
|
||||
Texture *src = front;
|
||||
vec3 scale = { src->w * mag, src->h * mag, 0 };
|
||||
if (rot > 90.0 && rot <= 270.0) {
|
||||
src = back;
|
||||
scale[0] *= -1.0f;
|
||||
}
|
||||
|
||||
gfx_fill_amap(dst, 0, 0, dst->w, dst->h, 0);
|
||||
|
||||
mat4 mw_transform = GLM_MAT4_IDENTITY_INIT;
|
||||
glm_rotate_y(mw_transform, rot * (M_PI/180.0), mw_transform);
|
||||
glm_scale(mw_transform, (vec3){ src->w * mag, src->h * mag, 0 });
|
||||
glm_scale(mw_transform, scale);
|
||||
glm_translate(mw_transform, (vec3){ -0.5, -0.5, 0});
|
||||
|
||||
mat4 proj_transform;
|
||||
@@ -859,6 +903,23 @@ void gfx_copy_reverse_LR(Texture *dst, int dx, int dy, Texture *src, int sx, int
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_copy_reverse_UD(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h)
|
||||
{
|
||||
mat4 mw_transform = MAT4(
|
||||
src->w, 0, 0, -sx,
|
||||
0, -src->h, 0, sy + h,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1);
|
||||
mat4 wv_transform = WV_TRANSFORM(w, h);
|
||||
|
||||
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ZERO, GL_ONE);
|
||||
|
||||
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
|
||||
run_draw_shader(©_shader.s, dst, src, mw_transform, wv_transform, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_copy_reverse_amap_LR(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h)
|
||||
{
|
||||
mat4 mw_transform = MAT4(
|
||||
@@ -876,6 +937,23 @@ void gfx_copy_reverse_amap_LR(Texture *dst, int dx, int dy, Texture *src, int sx
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_copy_reverse_LR_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h)
|
||||
{
|
||||
mat4 mw_transform = MAT4(
|
||||
-src->w, 0, 0, sx + w,
|
||||
0, src->h, 0, -sy,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1);
|
||||
mat4 wv_transform = WV_TRANSFORM(w, h);
|
||||
|
||||
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);
|
||||
|
||||
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
|
||||
run_draw_shader(©_shader.s, dst, src, mw_transform, wv_transform, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_copy_width_blur(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int blur)
|
||||
{
|
||||
GLfloat f_rate = 1.0 / (blur * 2 + 1);
|
||||
@@ -1006,30 +1084,122 @@ void gfx_copy_stretch_with_alpha_map(Texture *dst, int dx, int dy, int dw, int d
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
// XXX: Not an actual DrawGraph function; used for rendering text
|
||||
void gfx_draw_glyph(Texture *dst, float dx, int dy, Texture *glyph, SDL_Color color, float scale_x, float bold_width)
|
||||
void gfx_copy_grayscale(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h)
|
||||
{
|
||||
dx = roundf(dx);
|
||||
|
||||
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ZERO, GL_ONE);
|
||||
struct copy_data fill_data = COPY_DATA(dx, dy, dx, dy, glyph->w * scale_x, glyph->h);
|
||||
fill_data.r = color.r / 255.0;
|
||||
fill_data.g = color.g / 255.0;
|
||||
fill_data.b = color.b / 255.0;
|
||||
fill_data.a = 0.0;
|
||||
fill_data.threshold = 0.001;
|
||||
run_copy_shader(&fill_amap_under_border_shader.s, dst, dst, &fill_data);
|
||||
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glBlendEquationSeparate(GL_FUNC_ADD, GL_MAX);
|
||||
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
|
||||
run_copy_shader(©_grayscale_shader.s, dst, src, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_copy_grayscale_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h)
|
||||
{
|
||||
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);
|
||||
|
||||
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
|
||||
run_copy_shader(©_grayscale_shader.s, dst, src, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_copy_grayscale_reverse_LR_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h)
|
||||
{
|
||||
mat4 mw_transform = MAT4(
|
||||
-src->w, 0, 0, sx + w,
|
||||
0, src->h, 0, -sy,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1);
|
||||
mat4 wv_transform = WV_TRANSFORM(w, h);
|
||||
|
||||
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);
|
||||
|
||||
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
|
||||
run_draw_shader(©_grayscale_shader.s, dst, src, mw_transform, wv_transform, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
static void draw_line(Texture *dst, int x0, int y0, int x1, int y1, struct copy_data *data)
|
||||
{
|
||||
GLfloat w = dst->w;
|
||||
GLfloat h = dst->h;
|
||||
|
||||
mat4 mw_transform = MAT4(
|
||||
x1 - x0, 0, 0, x0,
|
||||
0, y1 - y0, 0, y0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1);
|
||||
mat4 wv_transform = WV_TRANSFORM(w, h);
|
||||
|
||||
GLuint fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, dst, 0, 0, w, h);
|
||||
|
||||
struct gfx_render_job job = {
|
||||
.shader = &fill_shader.s,
|
||||
.shape = GFX_LINE,
|
||||
.texture = 0,
|
||||
.world_transform = mw_transform[0],
|
||||
.view_transform = wv_transform[0],
|
||||
.data = data
|
||||
};
|
||||
gfx_render(&job);
|
||||
|
||||
gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo);
|
||||
}
|
||||
|
||||
void gfx_draw_line(Texture *dst, int x0, int y0, int x1, int y1, int r, int g, int b)
|
||||
{
|
||||
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ZERO, GL_ONE);
|
||||
|
||||
struct copy_data data = {
|
||||
.r = r / 255.0,
|
||||
.g = g / 255.0,
|
||||
.b = b / 255.0,
|
||||
.a = 1
|
||||
};
|
||||
draw_line(dst, x0, y0, x1, y1, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_draw_line_to_amap(Texture *dst, int x0, int y0, int x1, int y1, int a)
|
||||
{
|
||||
glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_ONE, GL_ZERO);
|
||||
|
||||
struct copy_data data = { .a = a / 255.0 };
|
||||
draw_line(dst, x0, y0, x1, y1, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_draw_quadrilateral(Texture *dst, Texture *src, struct gfx_vertex vertices[4])
|
||||
{
|
||||
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ZERO, GL_ONE);
|
||||
GLuint fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, dst, 0, 0, dst->w, dst->h);
|
||||
gfx_render_quadrilateral(src, vertices);
|
||||
gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo);
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
// XXX: Not an actual DrawGraph function; used for rendering text
|
||||
void gfx_draw_glyph(Texture *dst, float dx, int dy, Texture *glyph, SDL_Color color, float scale_x, float bold_width, bool blend)
|
||||
{
|
||||
if (blend) {
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
|
||||
} else {
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
glBlendEquationSeparate(GL_FUNC_ADD, GL_MAX);
|
||||
}
|
||||
|
||||
dx = roundf(dx);
|
||||
struct copy_data data = STRETCH_DATA(
|
||||
dx, dy, glyph->w * scale_x, glyph->h,
|
||||
0, 0, glyph->w, glyph->h);
|
||||
data.r = color.r / 255.0;
|
||||
data.g = color.g / 255.0;
|
||||
data.b = color.b / 255.0;
|
||||
data.a = 1.0;
|
||||
data.a = 0.01; // discard threshold
|
||||
|
||||
if (bold_width < 0.01) {
|
||||
run_copy_shader(&blend_rmap_color_shader.s, dst, glyph, &data);
|
||||
@@ -1049,7 +1219,7 @@ void gfx_draw_glyph_to_pmap(Texture *dst, float dx, int dy, Texture *glyph, Rect
|
||||
data.r = color.r / 255.0;
|
||||
data.g = color.g / 255.0;
|
||||
data.b = color.b / 255.0;
|
||||
data.a = 1.0;
|
||||
data.a = 0.01; // discard threshold
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
|
||||
run_copy_shader(&blend_rmap_color_shader.s, dst, glyph, &data);
|
||||
restore_blend_mode();
|
||||
@@ -1063,7 +1233,7 @@ void gfx_draw_glyph_to_amap(Texture *dst, float dx, int dy, Texture *glyph, Rect
|
||||
data.r = 1.0;
|
||||
data.g = 1.0;
|
||||
data.b = 1.0;
|
||||
data.a = 1.0;
|
||||
data.a = 0.0; // discard threshold
|
||||
glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_ONE, GL_ZERO);
|
||||
run_copy_shader(&blend_rmap_color_shader.s, dst, glyph, &data);
|
||||
restore_blend_mode();
|
||||
|
||||
+465
-73
@@ -22,26 +22,12 @@
|
||||
#include "dungeon/dgn.h"
|
||||
#include "vm.h"
|
||||
|
||||
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 buffer r;
|
||||
buffer_init(&r, data, size);
|
||||
if (strncmp(buffer_strdata(&r), "DUGN", 4))
|
||||
return NULL;
|
||||
buffer_skip(&r, 4);
|
||||
|
||||
struct dgn *dgn = xcalloc(1, sizeof(struct dgn));
|
||||
dgn->version = buffer_read_int32(&r);
|
||||
if (dgn->version != DGN_VER_RANCE6 && dgn->version != DGN_VER_GALZOO) {
|
||||
WARNING("unknown DGN version: %d", dgn->version);
|
||||
free(dgn);
|
||||
return NULL;
|
||||
}
|
||||
dgn->size_x = buffer_read_int32(&r);
|
||||
dgn->size_y = buffer_read_int32(&r);
|
||||
dgn->size_z = buffer_read_int32(&r);
|
||||
buffer_skip(&r, 40);
|
||||
|
||||
dgn->size_x = size_x;
|
||||
dgn->size_y = size_y;
|
||||
dgn->size_z = size_z;
|
||||
int nr_cells = dgn_nr_cells(dgn);
|
||||
dgn->cells = xcalloc(nr_cells, sizeof(struct dgn_cell));
|
||||
uint32_t x = 0, y = 0, z = 0;
|
||||
@@ -50,15 +36,62 @@ struct dgn *dgn_parse(uint8_t *data, size_t size)
|
||||
cell->x = x;
|
||||
cell->y = y;
|
||||
cell->z = z;
|
||||
if (++x == dgn->size_x) {
|
||||
if (++x == size_x) {
|
||||
x = 0;
|
||||
if (++y == dgn->size_y) {
|
||||
if (++y == size_y) {
|
||||
y = 0;
|
||||
z++;
|
||||
}
|
||||
}
|
||||
cell->event_blend_rate = 255;
|
||||
|
||||
cell->floor = -1;
|
||||
cell->ceiling = -1;
|
||||
cell->north_wall = -1;
|
||||
cell->south_wall = -1;
|
||||
cell->east_wall = -1;
|
||||
cell->west_wall = -1;
|
||||
cell->north_door = -1;
|
||||
cell->south_door = -1;
|
||||
cell->east_door = -1;
|
||||
cell->west_door = -1;
|
||||
cell->stairs_texture = -1;
|
||||
cell->stairs_orientation = -1;
|
||||
cell->lightmap_floor = -1;
|
||||
cell->lightmap_ceiling = -1;
|
||||
cell->lightmap_north = -1;
|
||||
cell->lightmap_south = -1;
|
||||
cell->lightmap_east = -1;
|
||||
cell->lightmap_west = -1;
|
||||
cell->battle_background = -1;
|
||||
cell->polyobj_index = -1;
|
||||
cell->roof_orientation = -1;
|
||||
cell->roof_texture = -1;
|
||||
cell->roof_underside_texture = -1;
|
||||
cell->pathfinding_cost = -1;
|
||||
}
|
||||
return dgn;
|
||||
}
|
||||
|
||||
struct dgn *dgn_parse(uint8_t *data, size_t size, bool for_draw_field)
|
||||
{
|
||||
struct buffer r;
|
||||
buffer_init(&r, data, size);
|
||||
if (strncmp(buffer_strdata(&r), "DUGN", 4))
|
||||
return NULL;
|
||||
buffer_skip(&r, 4);
|
||||
|
||||
uint32_t version = buffer_read_int32(&r);
|
||||
uint32_t size_x = buffer_read_int32(&r);
|
||||
uint32_t size_y = buffer_read_int32(&r);
|
||||
uint32_t size_z = buffer_read_int32(&r);
|
||||
buffer_skip(&r, 40);
|
||||
|
||||
struct dgn *dgn = dgn_new(size_x, size_y, size_z);
|
||||
int nr_cells = dgn_nr_cells(dgn);
|
||||
for (int i = 0; i < nr_cells; i++) {
|
||||
struct dgn_cell *cell = &dgn->cells[i];
|
||||
|
||||
cell->floor = buffer_read_int32(&r);
|
||||
cell->ceiling = buffer_read_int32(&r);
|
||||
cell->north_wall = buffer_read_int32(&r);
|
||||
@@ -71,7 +104,13 @@ struct dgn *dgn_parse(uint8_t *data, size_t size)
|
||||
cell->west_door = buffer_read_int32(&r);
|
||||
cell->stairs_texture = buffer_read_int32(&r);
|
||||
cell->stairs_orientation = buffer_read_int32(&r);
|
||||
buffer_skip(&r, 13 * 4);
|
||||
cell->lightmap_floor = buffer_read_int32(&r);
|
||||
cell->lightmap_ceiling = buffer_read_int32(&r);
|
||||
cell->lightmap_north = buffer_read_int32(&r);
|
||||
cell->lightmap_south = buffer_read_int32(&r);
|
||||
cell->lightmap_east = buffer_read_int32(&r);
|
||||
cell->lightmap_west = buffer_read_int32(&r);
|
||||
buffer_skip(&r, 7 * 4); // lightmap parameters for doors / stairs, 2 unknowns
|
||||
cell->enterable = buffer_read_int32(&r);
|
||||
cell->enterable_north = buffer_read_int32(&r);
|
||||
cell->enterable_south = buffer_read_int32(&r);
|
||||
@@ -87,64 +126,98 @@ struct dgn *dgn_parse(uint8_t *data, size_t size)
|
||||
buffer_skip(&r, 4);
|
||||
buffer_skip(&r, strlen(buffer_strdata(&r)) + 1);
|
||||
}
|
||||
buffer_skip(&r, 4);
|
||||
cell->battle_background = buffer_read_int32(&r);
|
||||
if (dgn->version != DGN_VER_GALZOO) {
|
||||
cell->polyobj_index = -1;
|
||||
cell->roof_orientation = -1;
|
||||
cell->roof_texture = -1;
|
||||
cell->roof_underside_texture = -1;
|
||||
continue;
|
||||
if (for_draw_field) {
|
||||
if (version >= 9) {
|
||||
cell->north_door_lock = buffer_read_int32(&r);
|
||||
cell->west_door_lock = buffer_read_int32(&r);
|
||||
cell->south_door_lock = buffer_read_int32(&r);
|
||||
cell->east_door_lock = buffer_read_int32(&r);
|
||||
cell->north_door_angle = buffer_read_float(&r);
|
||||
cell->west_door_angle = buffer_read_float(&r);
|
||||
cell->south_door_angle = buffer_read_float(&r);
|
||||
cell->east_door_angle = buffer_read_float(&r);
|
||||
cell->walked = buffer_read_int32(&r);
|
||||
}
|
||||
if (version >= 11) {
|
||||
cell->polyobj_index = buffer_read_int32(&r);
|
||||
cell->polyobj_scale = buffer_read_float(&r);
|
||||
cell->polyobj_rotation_y = buffer_read_float(&r);
|
||||
cell->polyobj_rotation_z = buffer_read_float(&r);
|
||||
cell->polyobj_rotation_x = buffer_read_float(&r);
|
||||
cell->polyobj_position_x = buffer_read_float(&r);
|
||||
cell->polyobj_position_y = buffer_read_float(&r);
|
||||
cell->polyobj_position_z = buffer_read_float(&r);
|
||||
}
|
||||
} else {
|
||||
buffer_skip(&r, 4); // unknown
|
||||
cell->battle_background = buffer_read_int32(&r);
|
||||
if (version != DGN_VER_GALZOO) {
|
||||
continue;
|
||||
}
|
||||
cell->polyobj_index = buffer_read_int32(&r);
|
||||
cell->polyobj_scale = buffer_read_float(&r);
|
||||
cell->polyobj_rotation_y = buffer_read_float(&r);
|
||||
cell->polyobj_rotation_z = buffer_read_float(&r);
|
||||
cell->polyobj_rotation_x = buffer_read_float(&r);
|
||||
cell->polyobj_position_x = buffer_read_float(&r);
|
||||
cell->polyobj_position_y = buffer_read_float(&r);
|
||||
cell->polyobj_position_z = buffer_read_float(&r);
|
||||
cell->roof_orientation = buffer_read_int32(&r);
|
||||
cell->roof_texture = buffer_read_int32(&r);
|
||||
buffer_skip(&r, 4); // unknown
|
||||
cell->roof_underside_texture = buffer_read_int32(&r);
|
||||
buffer_skip(&r, 4); // unknown
|
||||
}
|
||||
cell->polyobj_index = buffer_read_int32(&r);
|
||||
cell->polyobj_scale = buffer_read_float(&r);
|
||||
cell->polyobj_rotation_y = buffer_read_float(&r);
|
||||
cell->polyobj_rotation_z = buffer_read_float(&r);
|
||||
cell->polyobj_rotation_x = buffer_read_float(&r);
|
||||
cell->polyobj_position_x = buffer_read_float(&r);
|
||||
cell->polyobj_position_y = buffer_read_float(&r);
|
||||
cell->polyobj_position_z = buffer_read_float(&r);
|
||||
cell->roof_orientation = buffer_read_int32(&r);
|
||||
cell->roof_texture = buffer_read_int32(&r);
|
||||
buffer_skip(&r, 4);
|
||||
cell->roof_underside_texture = buffer_read_int32(&r);
|
||||
buffer_skip(&r, 4);
|
||||
}
|
||||
if (buffer_read_u8(&r) != 0 || buffer_read_int32(&r) != 1) {
|
||||
if (buffer_read_u8(&r) != 0) {
|
||||
dgn_free(dgn);
|
||||
return NULL;
|
||||
}
|
||||
dgn->pvs = xcalloc(nr_cells, sizeof(struct packed_pvs));
|
||||
for (int i = 0; i < nr_cells; i++) {
|
||||
struct packed_pvs *pvs = &dgn->pvs[i];
|
||||
int32_t len = buffer_read_int32(&r);
|
||||
if (len % 8 != 4) {
|
||||
WARNING("dgn_parse: unexpected PVS length");
|
||||
dgn_free(dgn);
|
||||
return NULL;
|
||||
}
|
||||
if (buffer_read_int32(&r) != nr_cells) {
|
||||
WARNING("dgn_parse: bad PVS");
|
||||
dgn_free(dgn);
|
||||
return NULL;
|
||||
}
|
||||
pvs->nr_run_lengths = (len - 4) / 8;
|
||||
pvs->run_lengths = xmalloc(pvs->nr_run_lengths * sizeof(struct pvs_run_lengths));
|
||||
int32_t total = 0;
|
||||
for (int j = 0; j < pvs->nr_run_lengths; j++) {
|
||||
int32_t invisible = buffer_read_int32(&r);
|
||||
int32_t visible = buffer_read_int32(&r);
|
||||
pvs->run_lengths[j].invisible_cells = invisible;
|
||||
pvs->run_lengths[j].visible_cells = visible;
|
||||
pvs->nr_visible_cells += visible;
|
||||
total += invisible + visible;
|
||||
}
|
||||
if (total != nr_cells) {
|
||||
WARNING("dgn_parse: bad PVS");
|
||||
dgn_free(dgn);
|
||||
return NULL;
|
||||
int has_pvs = buffer_read_int32(&r);
|
||||
if (has_pvs) {
|
||||
dgn->pvs = xcalloc(nr_cells, sizeof(struct packed_pvs));
|
||||
for (int i = 0; i < nr_cells; i++) {
|
||||
struct packed_pvs *pvs = &dgn->pvs[i];
|
||||
int32_t len = buffer_read_int32(&r);
|
||||
if (len % 8 != 4) {
|
||||
WARNING("dgn_parse: unexpected PVS length");
|
||||
dgn_free(dgn);
|
||||
return NULL;
|
||||
}
|
||||
if (buffer_read_int32(&r) != nr_cells) {
|
||||
WARNING("dgn_parse: bad PVS");
|
||||
dgn_free(dgn);
|
||||
return NULL;
|
||||
}
|
||||
pvs->nr_run_lengths = (len - 4) / 8;
|
||||
pvs->run_lengths = xmalloc(pvs->nr_run_lengths * sizeof(struct pvs_run_lengths));
|
||||
int32_t total = 0;
|
||||
for (int j = 0; j < pvs->nr_run_lengths; j++) {
|
||||
int32_t invisible = buffer_read_int32(&r);
|
||||
int32_t visible = buffer_read_int32(&r);
|
||||
pvs->run_lengths[j].invisible_cells = invisible;
|
||||
pvs->run_lengths[j].visible_cells = visible;
|
||||
pvs->nr_visible_cells += visible;
|
||||
total += invisible + visible;
|
||||
}
|
||||
if (total != nr_cells) {
|
||||
WARNING("dgn_parse: bad PVS");
|
||||
dgn_free(dgn);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
dgn->sphere_theta[0] = buffer_read_float(&r);
|
||||
dgn->sphere_theta[1] = buffer_read_float(&r);
|
||||
dgn->sphere_theta[2] = buffer_read_float(&r);
|
||||
dgn->sphere_color_top = buffer_read_float(&r);
|
||||
dgn->sphere_color_bottom = buffer_read_float(&r);
|
||||
buffer_skip(&r, 4); // unknown
|
||||
if (for_draw_field && version >= 10) {
|
||||
dgn->back_color_r = buffer_read_int32(&r);
|
||||
dgn->back_color_g = buffer_read_int32(&r);
|
||||
dgn->back_color_b = buffer_read_int32(&r);
|
||||
}
|
||||
return dgn;
|
||||
}
|
||||
|
||||
@@ -204,7 +277,7 @@ static int pvs_cell_compare(const void *a, const void *b)
|
||||
return ((struct pvs_cell *)a)->distance - ((struct pvs_cell *)b)->distance;
|
||||
}
|
||||
|
||||
struct dgn_cell **dgn_get_visible_cells(struct dgn *dgn, int x, int y, int z, int *nr_cells_out)
|
||||
static struct dgn_cell **dgn_get_visible_cells_pvs(struct dgn *dgn, int x, int y, int z, int *nr_cells_out)
|
||||
{
|
||||
int cell_index = dgn_cell_index(dgn, x, y, z);
|
||||
struct dgn_cell *cell = &dgn->cells[cell_index];
|
||||
@@ -236,3 +309,322 @@ struct dgn_cell **dgn_get_visible_cells(struct dgn *dgn, int x, int y, int z, in
|
||||
*nr_cells_out = pvs->nr_visible_cells;
|
||||
return cell->visible_cells;
|
||||
}
|
||||
|
||||
static struct dgn_cell **dgn_get_visible_cells_nopvs(struct dgn *dgn, int x, int y, int z, int *nr_cells_out)
|
||||
{
|
||||
int cell_index = dgn_cell_index(dgn, x, y, z);
|
||||
struct dgn_cell *cell = &dgn->cells[cell_index];
|
||||
int nr_cells = dgn_nr_cells(dgn);
|
||||
|
||||
if (!cell->visible_cells) {
|
||||
struct pvs_cell *pvs_cells = xmalloc(nr_cells * sizeof(struct pvs_cell));
|
||||
for (int i = 0; i < nr_cells; i++) {
|
||||
struct dgn_cell *c = &dgn->cells[i];
|
||||
int dx = c->x - x;
|
||||
int dy = c->y - y;
|
||||
int dz = c->z - z;
|
||||
pvs_cells[i].cell = c;
|
||||
pvs_cells[i].distance = dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
qsort(pvs_cells, nr_cells, sizeof(struct pvs_cell), pvs_cell_compare);
|
||||
|
||||
cell->visible_cells = xmalloc(nr_cells * sizeof(struct dgn_cell *));
|
||||
for (int i = 0; i < nr_cells; i++) {
|
||||
cell->visible_cells[i] = pvs_cells[i].cell;
|
||||
}
|
||||
free(pvs_cells);
|
||||
}
|
||||
*nr_cells_out = nr_cells;
|
||||
return cell->visible_cells;
|
||||
}
|
||||
|
||||
struct dgn_cell **dgn_get_visible_cells(struct dgn *dgn, int x, int y, int z, int *nr_cells_out)
|
||||
{
|
||||
if (dgn->pvs)
|
||||
return dgn_get_visible_cells_pvs(dgn, x, y, z, nr_cells_out);
|
||||
else
|
||||
return dgn_get_visible_cells_nopvs(dgn, x, y, z, nr_cells_out);
|
||||
}
|
||||
|
||||
#define LM_N (1 << 0)
|
||||
#define LM_S (1 << 1)
|
||||
#define LM_W (1 << 2)
|
||||
#define LM_E (1 << 3)
|
||||
#define LM_NW (1 << 4)
|
||||
#define LM_NE (1 << 5)
|
||||
#define LM_SW (1 << 6)
|
||||
#define LM_SE (1 << 7)
|
||||
|
||||
static int lightmap_texture_index(uint8_t flags)
|
||||
{
|
||||
if (flags & LM_N || flags & LM_W) flags &= ~LM_NW;
|
||||
if (flags & LM_N || flags & LM_E) flags &= ~LM_NE;
|
||||
if (flags & LM_S || flags & LM_W) flags &= ~LM_SW;
|
||||
if (flags & LM_S || flags & LM_E) flags &= ~LM_SE;
|
||||
|
||||
switch (flags) {
|
||||
case LM_N | LM_W | LM_E: return 0;
|
||||
case LM_W | LM_E: return 1;
|
||||
case LM_S | LM_W | LM_E: return 2;
|
||||
case LM_N | LM_S | LM_W: return 3;
|
||||
case LM_N | LM_S: return 4;
|
||||
case LM_N | LM_S | LM_E: return 5;
|
||||
case LM_N | LM_W: return 6;
|
||||
case LM_N: return 7;
|
||||
case LM_N | LM_E: return 8;
|
||||
case LM_W: return 9;
|
||||
case LM_E: return 10;
|
||||
case LM_S | LM_W: return 11;
|
||||
case LM_S: return 12;
|
||||
case LM_S | LM_E: return 13;
|
||||
case LM_N | LM_S | LM_W | LM_E: return 14;
|
||||
case LM_W | LM_NE | LM_SE: return 15;
|
||||
case LM_N | LM_SW | LM_SE: return 16;
|
||||
case LM_E | LM_NW | LM_SW: return 17;
|
||||
case LM_S | LM_NW | LM_NE: return 18;
|
||||
case LM_NW | LM_NE | LM_SW | LM_SE: return 19;
|
||||
case LM_W | LM_NE: return 20;
|
||||
case LM_N | LM_SE: return 21;
|
||||
case LM_E | LM_SW: return 22;
|
||||
case LM_S | LM_NW: return 23;
|
||||
case LM_NE: return 24;
|
||||
case LM_W | LM_SE: return 25;
|
||||
case LM_N | LM_SW: return 26;
|
||||
case LM_E | LM_NW: return 27;
|
||||
case LM_S | LM_NE: return 28;
|
||||
case LM_SE: return 29;
|
||||
case LM_NE | LM_SE: return 30;
|
||||
case LM_SW | LM_SE: return 31;
|
||||
case LM_NW | LM_SW: return 32;
|
||||
case LM_NW | LM_NE: return 33;
|
||||
case LM_SW: return 34;
|
||||
case LM_NW: return 35;
|
||||
case LM_N | LM_W | LM_SE: return 36;
|
||||
case LM_N | LM_E | LM_SW: return 37;
|
||||
case LM_S | LM_E | LM_NW: return 38;
|
||||
case LM_S | LM_W | LM_NE: return 39;
|
||||
case LM_NW | LM_NE | LM_SW: return 40;
|
||||
case LM_NW | LM_NE | LM_SE: return 41;
|
||||
case LM_NE | LM_SW | LM_SE: return 42;
|
||||
case LM_NW | LM_SW | LM_SE: return 43;
|
||||
case LM_NE | LM_SW: return 44;
|
||||
case LM_NW | LM_SE: return 45;
|
||||
default: return -1;
|
||||
};
|
||||
}
|
||||
|
||||
static int calc_floor_lightmap(struct dgn *dgn, struct dgn_cell *cell,
|
||||
struct dgn_cell *north_cell, struct dgn_cell *south_cell,
|
||||
struct dgn_cell *west_cell, struct dgn_cell *east_cell)
|
||||
{
|
||||
if (cell->floor == -1)
|
||||
return -1;
|
||||
|
||||
uint8_t flags = 0;
|
||||
if (cell->north_wall != -1 || cell->north_door != -1 ||
|
||||
(north_cell && north_cell->stairs_texture != -1 && north_cell->stairs_orientation == 0))
|
||||
flags |= LM_N;
|
||||
if (cell->south_wall != -1 || cell->south_door != -1 ||
|
||||
(south_cell && south_cell->stairs_texture != -1 && south_cell->stairs_orientation == 2))
|
||||
flags |= LM_S;
|
||||
if (cell->west_wall != -1 || cell->west_door != -1 ||
|
||||
(west_cell && west_cell->stairs_texture != -1 && west_cell->stairs_orientation == 1))
|
||||
flags |= LM_W;
|
||||
if (cell->east_wall != -1 || cell->east_door != -1 ||
|
||||
(east_cell && east_cell->stairs_texture != -1 && east_cell->stairs_orientation == 3))
|
||||
flags |= LM_E;
|
||||
if ((north_cell && (north_cell->west_wall != -1 || north_cell->west_door != -1)) ||
|
||||
(west_cell && (west_cell->north_wall != -1 || west_cell->north_door != -1)))
|
||||
flags |= LM_NW;
|
||||
if ((north_cell && (north_cell->east_wall != -1 || north_cell->east_door != -1)) ||
|
||||
(east_cell && (east_cell->north_wall != -1 || east_cell->north_door != -1)))
|
||||
flags |= LM_NE;
|
||||
if ((south_cell && (south_cell->west_wall != -1 || south_cell->west_door != -1)) ||
|
||||
(west_cell && (west_cell->south_wall != -1 || west_cell->south_door != -1)))
|
||||
flags |= LM_SW;
|
||||
if ((south_cell && (south_cell->east_wall != -1 || south_cell->east_door != -1)) ||
|
||||
(east_cell && (east_cell->south_wall != -1 || east_cell->south_door != -1)))
|
||||
flags |= LM_SE;
|
||||
return lightmap_texture_index(flags);
|
||||
}
|
||||
|
||||
static int calc_ceiling_lightmap(struct dgn *dgn, struct dgn_cell *cell,
|
||||
struct dgn_cell *north_cell, struct dgn_cell *south_cell,
|
||||
struct dgn_cell *west_cell, struct dgn_cell *east_cell)
|
||||
{
|
||||
if (cell->ceiling == -1)
|
||||
return -1;
|
||||
|
||||
uint8_t flags = 0;
|
||||
if (cell->north_wall != -1 || cell->north_door != -1)
|
||||
flags |= LM_N;
|
||||
if (cell->south_wall != -1 || cell->south_door != -1)
|
||||
flags |= LM_S;
|
||||
if (cell->west_wall != -1 || cell->west_door != -1)
|
||||
flags |= LM_E;
|
||||
if (cell->east_wall != -1 || cell->east_door != -1)
|
||||
flags |= LM_W;
|
||||
if ((north_cell && (north_cell->east_wall != -1 || north_cell->east_door != -1)) ||
|
||||
(east_cell && (east_cell->north_wall != -1 || east_cell->north_door != -1)))
|
||||
flags |= LM_NW;
|
||||
if ((north_cell && (north_cell->west_wall != -1 || north_cell->west_door != -1)) ||
|
||||
(west_cell && (west_cell->north_wall != -1 || west_cell->north_door != -1)))
|
||||
flags |= LM_NE;
|
||||
if ((south_cell && (south_cell->east_wall != -1 || south_cell->east_door != -1)) ||
|
||||
(east_cell && (east_cell->south_wall != -1 || east_cell->south_door != -1)))
|
||||
flags |= LM_SW;
|
||||
if ((south_cell && (south_cell->west_wall != -1 || south_cell->west_door != -1)) ||
|
||||
(west_cell && (west_cell->south_wall != -1 || west_cell->south_door != -1)))
|
||||
flags |= LM_SE;
|
||||
return lightmap_texture_index(flags);
|
||||
}
|
||||
|
||||
static int calc_north_lightmap(struct dgn *dgn, struct dgn_cell *cell,
|
||||
struct dgn_cell *up_cell, struct dgn_cell *down_cell,
|
||||
struct dgn_cell *west_cell, struct dgn_cell *east_cell)
|
||||
{
|
||||
if (cell->north_wall == -1)
|
||||
return -1;
|
||||
|
||||
uint8_t flags = 0;
|
||||
if (cell->ceiling != -1)
|
||||
flags |= LM_N;
|
||||
if (cell->floor != -1)
|
||||
flags |= LM_S;
|
||||
if (cell->west_wall != -1 || cell->west_door != -1)
|
||||
flags |= LM_W;
|
||||
if (cell->east_wall != -1 || cell->east_door != -1)
|
||||
flags |= LM_E;
|
||||
if ((up_cell && (up_cell->west_wall != -1 || up_cell->west_door != -1)) ||
|
||||
(west_cell && (west_cell->ceiling != -1)))
|
||||
flags |= LM_NW;
|
||||
if ((up_cell && (up_cell->east_wall != -1 || up_cell->east_door != -1)) ||
|
||||
(east_cell && (east_cell->ceiling != -1)))
|
||||
flags |= LM_NE;
|
||||
if ((down_cell && (down_cell->west_wall != -1 || down_cell->west_door != -1)) ||
|
||||
(west_cell && (west_cell->floor != -1)))
|
||||
flags |= LM_SW;
|
||||
if ((down_cell && (down_cell->east_wall != -1 || down_cell->east_door != -1)) ||
|
||||
(east_cell && (east_cell->floor != -1)))
|
||||
flags |= LM_SE;
|
||||
return lightmap_texture_index(flags);
|
||||
}
|
||||
|
||||
static int calc_south_lightmap(struct dgn *dgn, struct dgn_cell *cell,
|
||||
struct dgn_cell *up_cell, struct dgn_cell *down_cell,
|
||||
struct dgn_cell *west_cell, struct dgn_cell *east_cell)
|
||||
{
|
||||
if (cell->south_wall == -1)
|
||||
return -1;
|
||||
|
||||
uint8_t flags = 0;
|
||||
if (cell->ceiling != -1)
|
||||
flags |= LM_N;
|
||||
if (cell->floor != -1)
|
||||
flags |= LM_S;
|
||||
if (cell->east_wall != -1 || cell->east_door != -1)
|
||||
flags |= LM_W;
|
||||
if (cell->west_wall != -1 || cell->west_door != -1)
|
||||
flags |= LM_E;
|
||||
if ((up_cell && (up_cell->east_wall != -1 || up_cell->east_door != -1)) ||
|
||||
(east_cell && (east_cell->ceiling != -1)))
|
||||
flags |= LM_NW;
|
||||
if ((up_cell && (up_cell->west_wall != -1 || up_cell->west_door != -1)) ||
|
||||
(west_cell && (west_cell->ceiling != -1)))
|
||||
flags |= LM_NE;
|
||||
if ((down_cell && (down_cell->east_wall != -1 || down_cell->east_door != -1)) ||
|
||||
(east_cell && (east_cell->floor != -1)))
|
||||
flags |= LM_SW;
|
||||
if ((down_cell && (down_cell->west_wall != -1 || down_cell->west_door != -1)) ||
|
||||
(west_cell && (west_cell->floor != -1)))
|
||||
flags |= LM_SE;
|
||||
return lightmap_texture_index(flags);
|
||||
}
|
||||
|
||||
static int calc_west_lightmap(struct dgn *dgn, struct dgn_cell *cell,
|
||||
struct dgn_cell *up_cell, struct dgn_cell *down_cell,
|
||||
struct dgn_cell *north_cell, struct dgn_cell *south_cell)
|
||||
{
|
||||
if (cell->west_wall == -1)
|
||||
return -1;
|
||||
|
||||
uint8_t flags = 0;
|
||||
if (cell->ceiling != -1)
|
||||
flags |= LM_N;
|
||||
if (cell->floor != -1)
|
||||
flags |= LM_S;
|
||||
if (cell->south_wall != -1 || cell->south_door != -1)
|
||||
flags |= LM_W;
|
||||
if (cell->north_wall != -1 || cell->north_door != -1)
|
||||
flags |= LM_E;
|
||||
if ((up_cell && (up_cell->south_wall != -1 || up_cell->south_door != -1)) ||
|
||||
(south_cell && (south_cell->ceiling != -1)))
|
||||
flags |= LM_NW;
|
||||
if ((up_cell && (up_cell->north_wall != -1 || up_cell->north_door != -1)) ||
|
||||
(north_cell && (north_cell->ceiling != -1)))
|
||||
flags |= LM_NE;
|
||||
if ((down_cell && (down_cell->south_wall != -1 || down_cell->south_door != -1)) ||
|
||||
(south_cell && (south_cell->floor != -1)))
|
||||
flags |= LM_SW;
|
||||
if ((down_cell && (down_cell->north_wall != -1 || down_cell->north_door != -1)) ||
|
||||
(north_cell && (north_cell->floor != -1)))
|
||||
flags |= LM_SE;
|
||||
return lightmap_texture_index(flags);
|
||||
}
|
||||
|
||||
static int calc_east_lightmap(struct dgn *dgn, struct dgn_cell *cell,
|
||||
struct dgn_cell *up_cell, struct dgn_cell *down_cell,
|
||||
struct dgn_cell *north_cell, struct dgn_cell *south_cell)
|
||||
{
|
||||
if (cell->east_wall == -1)
|
||||
return -1;
|
||||
|
||||
uint8_t flags = 0;
|
||||
if (cell->ceiling != -1)
|
||||
flags |= LM_N;
|
||||
if (cell->floor != -1)
|
||||
flags |= LM_S;
|
||||
if (cell->north_wall != -1 || cell->north_door != -1)
|
||||
flags |= LM_W;
|
||||
if (cell->south_wall != -1 || cell->south_door != -1)
|
||||
flags |= LM_E;
|
||||
if ((up_cell && (up_cell->north_wall != -1 || up_cell->north_door != -1)) ||
|
||||
(north_cell && (north_cell->ceiling != -1)))
|
||||
flags |= LM_NW;
|
||||
if ((up_cell && (up_cell->south_wall != -1 || up_cell->south_door != -1)) ||
|
||||
(south_cell && (south_cell->ceiling != -1)))
|
||||
flags |= LM_NE;
|
||||
if ((down_cell && (down_cell->north_wall != -1 || down_cell->north_door != -1)) ||
|
||||
(north_cell && (north_cell->floor != -1)))
|
||||
flags |= LM_SW;
|
||||
if ((down_cell && (down_cell->south_wall != -1 || down_cell->south_door != -1)) ||
|
||||
(south_cell && (south_cell->floor != -1)))
|
||||
flags |= LM_SE;
|
||||
return lightmap_texture_index(flags);
|
||||
}
|
||||
|
||||
void dgn_calc_lightmap(struct dgn *dgn)
|
||||
{
|
||||
int nr_cells = dgn_nr_cells(dgn);
|
||||
for (int i = 0; i < nr_cells; i++) {
|
||||
struct dgn_cell *cell = &dgn->cells[i];
|
||||
struct dgn_cell *north = cell->z + 1 < dgn->size_z
|
||||
? dgn_cell_at(dgn, cell->x, cell->y, cell->z + 1) : NULL;
|
||||
struct dgn_cell *south = cell->z > 0
|
||||
? dgn_cell_at(dgn, cell->x, cell->y, cell->z - 1) : NULL;
|
||||
struct dgn_cell *west = cell->x > 0
|
||||
? dgn_cell_at(dgn, cell->x - 1, cell->y, cell->z) : NULL;
|
||||
struct dgn_cell *east = cell->x + 1 < dgn->size_x
|
||||
? dgn_cell_at(dgn, cell->x + 1, cell->y, cell->z) : NULL;
|
||||
struct dgn_cell *up = cell->y + 1 < dgn->size_y
|
||||
? dgn_cell_at(dgn, cell->x, cell->y + 1, cell->z) : NULL;
|
||||
struct dgn_cell *down = cell->y > 0
|
||||
? dgn_cell_at(dgn, cell->x, cell->y - 1, cell->z) : NULL;
|
||||
cell->lightmap_floor = calc_floor_lightmap(dgn, cell, north, south, west, east);
|
||||
cell->lightmap_ceiling = calc_ceiling_lightmap(dgn, cell, north, south, west, east);
|
||||
cell->lightmap_north = calc_north_lightmap(dgn, cell, up, down, west, east);
|
||||
cell->lightmap_south = calc_south_lightmap(dgn, cell, up, down, west, east);
|
||||
cell->lightmap_west = calc_west_lightmap(dgn, cell, up, down, north, south);
|
||||
cell->lightmap_east = calc_east_lightmap(dgn, cell, up, down, north, south);
|
||||
}
|
||||
}
|
||||
|
||||
+120
-3
@@ -14,13 +14,20 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "system4.h"
|
||||
#include "system4/buffer.h"
|
||||
#include "system4/cg.h"
|
||||
#include "system4/file.h"
|
||||
#include "system4/little_endian.h"
|
||||
|
||||
#include "xsystem4.h"
|
||||
#include "dungeon/dtx.h"
|
||||
#include "dungeon/mtrand43.h"
|
||||
|
||||
#define DTX_VERSION_DTL 0xffffffff
|
||||
|
||||
struct dtx *dtx_parse(uint8_t *data, size_t size)
|
||||
{
|
||||
@@ -68,12 +75,122 @@ void dtx_free(struct dtx *dtx)
|
||||
free(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)
|
||||
{
|
||||
if (type < 0 || type >= dtx->nr_rows || index < 0 || index >= dtx->nr_columns)
|
||||
int row;
|
||||
switch (dtx->version) {
|
||||
case 0:
|
||||
switch (type) {
|
||||
case DTX_WALL: row = 0; break;
|
||||
case DTX_FLOOR: row = 1; break;
|
||||
case DTX_CEILING: row = 2; break;
|
||||
case DTX_STAIRS: row = 3; break;
|
||||
case DTX_DOOR: row = 4; break;
|
||||
case DTX_SKYBOX: row = 5; break;
|
||||
case DTX_LIGHTMAP: row = 6; break;
|
||||
default: return NULL;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
switch (type) {
|
||||
case DTX_WALL: row = 0; break;
|
||||
case DTX_FLOOR: row = 1; break;
|
||||
case DTX_CEILING: row = 2; break;
|
||||
case DTX_STAIRS: row = 3; break;
|
||||
case DTX_DOOR: row = 4; break;
|
||||
case DTX_SKYBOX: row = 5; break;
|
||||
case DTX_LIGHTMAP: row = 7; break;
|
||||
default: return NULL;
|
||||
}
|
||||
break;
|
||||
case DTX_VERSION_DTL:
|
||||
switch (type) {
|
||||
case DTX_WALL: row = 0; break;
|
||||
case DTX_FLOOR: row = 1; break;
|
||||
case DTX_CEILING: row = 2; break;
|
||||
case DTX_LIGHTMAP: row = 3; break;
|
||||
default: return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (row >= dtx->nr_rows || index < 0 || index >= dtx->nr_columns)
|
||||
return NULL;
|
||||
struct dtx_entry *entry = &dtx->entries[type * dtx->nr_columns + index];
|
||||
struct dtx_entry *entry = &dtx->entries[row * dtx->nr_columns + index];
|
||||
if (!entry->data)
|
||||
return NULL;
|
||||
return cg_load_buffer(entry->data, entry->size);
|
||||
}
|
||||
|
||||
struct dtx *dtx_load_from_dtl(const char *path, uint32_t seed)
|
||||
{
|
||||
uint8_t *index_buf = NULL;
|
||||
struct dtx *dtx = NULL;
|
||||
FILE *fp = file_open_utf8(path, "rb");
|
||||
if (!fp) {
|
||||
WARNING("Failed to open DTL file '%s': %s", display_utf0(path), strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
char header[12];
|
||||
if (fread(header, sizeof(header), 1, fp) != 1 || strcmp(header, "DTL"))
|
||||
goto err;
|
||||
uint32_t version = LittleEndian_getDW((uint8_t*)header, 4);
|
||||
uint32_t index_size = LittleEndian_getDW((uint8_t*)header, 8);
|
||||
if (version != 0 || index_size <= 1 || index_size > 1000)
|
||||
goto err;
|
||||
index_buf = xmalloc(index_size * 4);
|
||||
if (fread(index_buf, index_size * 4, 1, fp) != 1)
|
||||
goto err;
|
||||
|
||||
// Randomly select an index based on the seed.
|
||||
struct mtrand43 mt;
|
||||
mtrand43_init(&mt, seed);
|
||||
for (;;) {
|
||||
int index = mtrand43_genrand(&mt) % (index_size - 1) + 1;
|
||||
uint32_t offset = LittleEndian_getDW(index_buf, index * 4);
|
||||
if (offset) {
|
||||
if (fseek(fp, offset, SEEK_SET))
|
||||
goto err;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dtx = xcalloc(1, sizeof(struct dtx));
|
||||
dtx->version = DTX_VERSION_DTL;
|
||||
dtx->nr_rows = 4;
|
||||
dtx->nr_columns = 46; // number of lightmap textures
|
||||
dtx->entries = xcalloc(dtx->nr_rows * dtx->nr_columns, sizeof(struct dtx_entry));
|
||||
uint8_t buf[4];
|
||||
for (int row = 0; row < dtx->nr_rows; row++) {
|
||||
if (fread(buf, sizeof(buf), 1, fp) != 1)
|
||||
goto err;
|
||||
uint32_t nr_items = LittleEndian_getDW(buf, 0);
|
||||
for (int col = 0; col < nr_items; col++) {
|
||||
if (fread(buf, sizeof(buf), 1, fp) != 1)
|
||||
goto err;
|
||||
uint32_t texture_size = LittleEndian_getDW(buf, 0);
|
||||
if (col >= dtx->nr_columns) {
|
||||
// skip the rest of the row
|
||||
if (fseek(fp, texture_size, SEEK_CUR))
|
||||
goto err;
|
||||
continue;
|
||||
}
|
||||
struct dtx_entry *entry = &dtx->entries[row * dtx->nr_columns + col];
|
||||
entry->size = texture_size;
|
||||
entry->data = xmalloc(texture_size);
|
||||
if (fread(entry->data, texture_size, 1, fp) != 1)
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
free(index_buf);
|
||||
fclose(fp);
|
||||
return dtx;
|
||||
|
||||
err:
|
||||
if (dtx)
|
||||
dtx_free(dtx);
|
||||
if (index_buf)
|
||||
free(index_buf);
|
||||
if (fp)
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+417
-77
@@ -30,69 +30,79 @@
|
||||
#include "dungeon/dgn.h"
|
||||
#include "dungeon/dtx.h"
|
||||
#include "dungeon/dungeon.h"
|
||||
#include "dungeon/generator.h"
|
||||
#include "dungeon/map.h"
|
||||
#include "dungeon/polyobj.h"
|
||||
#include "dungeon/renderer.h"
|
||||
#include "dungeon/tes.h"
|
||||
#include "gfx/gfx.h"
|
||||
#include "cJSON.h"
|
||||
#include "json.h"
|
||||
#include "sact.h"
|
||||
#include "sprite.h"
|
||||
#include "vm.h"
|
||||
#include "vm/page.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265358979323846)
|
||||
#endif
|
||||
static const char plugin_name[] = "DrawDungeon";
|
||||
|
||||
static void dungeon_render(struct sact_sprite *sp);
|
||||
static cJSON *dungeon_to_json(struct sact_sprite *sp, bool verbose);
|
||||
static void dungeon_context_free(struct draw_plugin *plugin);
|
||||
|
||||
struct dungeon_context *dungeon_context_create(enum draw_dungeon_version version, int surface)
|
||||
struct dungeon_context *dungeon_get_context(int surface)
|
||||
{
|
||||
struct dungeon_context *ctx = xcalloc(1, sizeof(struct dungeon_context));
|
||||
ctx->plugin.name = "DrawDungeon";
|
||||
struct sact_sprite *sp = sact_try_get_sprite(surface);
|
||||
if (!sp || !sp->plugin || sp->plugin->name != plugin_name)
|
||||
return NULL;
|
||||
return (struct dungeon_context *)sp->plugin;
|
||||
}
|
||||
|
||||
struct dungeon_context *dungeon_context_create(enum draw_dungeon_version version, int width, int height)
|
||||
{
|
||||
struct dungeon_context *ctx = xcalloc_aligned(1, struct dungeon_context);
|
||||
ctx->plugin.name = plugin_name;
|
||||
ctx->plugin.free = dungeon_context_free;
|
||||
ctx->plugin.update = dungeon_render;
|
||||
ctx->plugin.debug_print = NULL;
|
||||
ctx->plugin.to_json = dungeon_to_json;
|
||||
ctx->version = version;
|
||||
ctx->surface = surface;
|
||||
struct sact_sprite *sp = sact_get_sprite(ctx->surface);
|
||||
if (!sp)
|
||||
VM_ERROR("DrawDungeon.Init: invalid surface %d", surface);
|
||||
|
||||
// Dungeon scene will be rendered to this texture.
|
||||
struct texture *texture = sprite_get_texture(sp);
|
||||
sprite_bind_plugin(sp, &ctx->plugin);
|
||||
|
||||
gfx_init_texture_blank(&ctx->texture, width, height);
|
||||
glGenRenderbuffers(1, &ctx->depth_buffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, ctx->depth_buffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, texture->w, texture->h);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
glm_perspective(GLM_PIf / 3.f, (float)width / height, 0.5f, 100.f, ctx->proj_transform);
|
||||
|
||||
ctx->map = dungeon_map_create(version);
|
||||
|
||||
if (version == DRAW_FIELD) {
|
||||
ctx->characters = xcalloc(DRAWFIELD_NR_CHARACTERS, sizeof(struct drawfield_character));
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void dungeon_context_free(struct dungeon_context *ctx)
|
||||
static void dungeon_context_free(struct draw_plugin *plugin)
|
||||
{
|
||||
struct sact_sprite *sp = sact_try_get_sprite(ctx->surface);
|
||||
if (sp)
|
||||
sprite_bind_plugin(sp, NULL);
|
||||
|
||||
struct dungeon_context *ctx = (struct dungeon_context *)plugin;
|
||||
if (ctx->dgn)
|
||||
dgn_free(ctx->dgn);
|
||||
if (ctx->dtx)
|
||||
dtx_free(ctx->dtx);
|
||||
if (ctx->tes)
|
||||
tes_free(ctx->tes);
|
||||
if (ctx->characters)
|
||||
free(ctx->characters);
|
||||
|
||||
if (ctx->renderer)
|
||||
dungeon_renderer_free(ctx->renderer);
|
||||
|
||||
gfx_delete_texture(&ctx->texture);
|
||||
glDeleteRenderbuffers(1, &ctx->depth_buffer);
|
||||
if (ctx->map)
|
||||
dungeon_map_free(ctx->map);
|
||||
|
||||
free(ctx);
|
||||
xfree_aligned(ctx);
|
||||
}
|
||||
|
||||
static GLuint *load_event_textures(int *nr_textures_out)
|
||||
@@ -158,7 +168,7 @@ bool dungeon_load(struct dungeon_context *ctx, int num)
|
||||
|
||||
struct archive_data *dgn = archive_get(dlf, num * 3);
|
||||
if (dgn) {
|
||||
ctx->dgn = dgn_parse(dgn->data, dgn->size);
|
||||
ctx->dgn = dgn_parse(dgn->data, dgn->size, false);
|
||||
archive_free_data(dgn);
|
||||
}
|
||||
struct archive_data *dtx = archive_get(dlf, num * 3 + 1);
|
||||
@@ -172,6 +182,11 @@ bool dungeon_load(struct dungeon_context *ctx, int num)
|
||||
archive_free_data(tes);
|
||||
}
|
||||
archive_free(dlf);
|
||||
} else if (ctx->version == DRAW_DUNGEON_2) {
|
||||
ctx->dgn = dgn_generate_drawdungeon2(num);
|
||||
char *dtl_path = gamedir_path("Data/Texture.dtl");
|
||||
ctx->dtx = dtx_load_from_dtl(dtl_path, (num * 25) | 1);
|
||||
free(dtl_path);
|
||||
} else if (ctx->version == DRAW_DUNGEON_14) {
|
||||
char buf[100];
|
||||
sprintf(buf, "Data/map%02d.dgn", num);
|
||||
@@ -180,7 +195,7 @@ bool dungeon_load(struct dungeon_context *ctx, int num)
|
||||
size_t len;
|
||||
uint8_t *dgn = file_read(path, &len);
|
||||
if (dgn) {
|
||||
ctx->dgn = dgn_parse(dgn, len);
|
||||
ctx->dgn = dgn_parse(dgn, len, false);
|
||||
free(dgn);
|
||||
}
|
||||
|
||||
@@ -220,7 +235,7 @@ bool dungeon_load(struct dungeon_context *ctx, int num)
|
||||
if (!event_textures)
|
||||
return false;
|
||||
|
||||
ctx->renderer = dungeon_renderer_create(ctx->version, ctx->dtx, event_textures, nr_event_textures, polyobj);
|
||||
ctx->renderer = dungeon_renderer_create(ctx->version, num, ctx->dtx, event_textures, nr_event_textures, polyobj);
|
||||
|
||||
if (polyobj)
|
||||
polyobj_free(polyobj);
|
||||
@@ -231,6 +246,74 @@ bool dungeon_load(struct dungeon_context *ctx, int num)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dungeon_load_dungeon(struct dungeon_context *ctx, const char *filename, int num)
|
||||
{
|
||||
char *path = gamedir_path(filename);
|
||||
size_t len;
|
||||
uint8_t *dgn = file_read(path, &len);
|
||||
if (!dgn) {
|
||||
WARNING("Cannot load %s", path);
|
||||
free(path);
|
||||
return false;
|
||||
}
|
||||
free(path);
|
||||
if (ctx->dgn)
|
||||
dgn_free(ctx->dgn);
|
||||
ctx->dgn = dgn_parse(dgn, len, ctx->version == DRAW_FIELD);
|
||||
free(dgn);
|
||||
if (!ctx->dgn)
|
||||
return false;
|
||||
|
||||
dungeon_map_init(ctx);
|
||||
|
||||
ctx->loaded = ctx->dgn && ctx->dtx;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dungeon_load_texture(struct dungeon_context *ctx, const char *filename)
|
||||
{
|
||||
// load .dtx
|
||||
char *path = gamedir_path(filename);
|
||||
size_t dtx_len;
|
||||
uint8_t *dtx = file_read(path, &dtx_len);
|
||||
if (!dtx) {
|
||||
WARNING("Cannot load %s", path);
|
||||
free(path);
|
||||
return false;
|
||||
}
|
||||
if (ctx->dtx)
|
||||
dtx_free(ctx->dtx);
|
||||
ctx->dtx = dtx_parse(dtx, dtx_len);
|
||||
free(dtx);
|
||||
if (!ctx->dtx) {
|
||||
free(path);
|
||||
return false;
|
||||
}
|
||||
|
||||
// load .tes
|
||||
if (ctx->tes)
|
||||
tes_free(ctx->tes);
|
||||
ctx->tes = NULL;
|
||||
size_t path_len = strlen(path);
|
||||
if (path_len > 4 && path[path_len - 4] == '.') {
|
||||
strcpy(path + path_len - 4, ".tes"); // .dtx -> .tes
|
||||
size_t tes_len;
|
||||
uint8_t *tes = file_read(path, &tes_len);
|
||||
if (tes) {
|
||||
ctx->tes = tes_parse(tes, tes_len);
|
||||
free(tes);
|
||||
}
|
||||
}
|
||||
free(path);
|
||||
|
||||
if (ctx->renderer)
|
||||
dungeon_renderer_free(ctx->renderer);
|
||||
ctx->renderer = dungeon_renderer_create(ctx->version, 0, ctx->dtx, NULL, 0, NULL);
|
||||
|
||||
ctx->loaded = ctx->dgn && ctx->dtx;
|
||||
return true;
|
||||
}
|
||||
|
||||
void dungeon_set_camera(int surface, float x, float y, float z, float angle, float angle_p)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
@@ -239,23 +322,59 @@ void dungeon_set_camera(int surface, float x, float y, float z, float angle, flo
|
||||
ctx->camera.pos[0] = x;
|
||||
ctx->camera.pos[1] = y;
|
||||
ctx->camera.pos[2] = -z;
|
||||
ctx->camera.angle = -angle * M_PI / 180;
|
||||
ctx->camera.angle_p = angle_p * M_PI / 180;
|
||||
ctx->camera.angle = -glm_rad(angle);
|
||||
ctx->camera.angle_p = glm_rad(angle_p);
|
||||
|
||||
if (ctx->version != DRAW_FIELD) {
|
||||
ctx->player_pos[0] = roundf(x / 2.f);
|
||||
ctx->player_pos[1] = roundf(y / 2.f);
|
||||
ctx->player_pos[2] = roundf(z / 2.f);
|
||||
}
|
||||
}
|
||||
|
||||
static void model_view_matrix(struct camera *camera, mat4 out)
|
||||
void dungeon_set_perspective(int surface, int width, int height, float near, float far, float deg)
|
||||
{
|
||||
// The actual camera position is slightly behind camera->pos.
|
||||
float dist = 0.9;
|
||||
vec3 d = {
|
||||
dist * sin(camera->angle) * cos(camera->angle_p),
|
||||
dist * sin(camera->angle_p),
|
||||
dist * -cos(camera->angle) * cos(camera->angle_p),
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
float aspect = (float)width / height;
|
||||
// XXX: Pastel Chime Continue sets unnecessarily small near (0.1) and large
|
||||
// far (10000), which degrades depth buffer precision.
|
||||
near = max(near, 0.4f);
|
||||
far = min(far, 500.f);
|
||||
glm_perspective(glm_rad(deg), aspect, near, far, ctx->proj_transform);
|
||||
}
|
||||
|
||||
void dungeon_set_player_pos(int surface, int x, int y, int z)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx)
|
||||
return;
|
||||
ctx->player_pos[0] = x;
|
||||
ctx->player_pos[1] = y;
|
||||
ctx->player_pos[2] = z;
|
||||
}
|
||||
|
||||
static void model_view_matrix(struct dungeon_context *ctx, mat4 mv_out)
|
||||
{
|
||||
struct camera *camera = &ctx->camera;
|
||||
float dist = 0.9f;
|
||||
vec3 front = {
|
||||
dist * sinf(camera->angle) * cosf(camera->angle_p),
|
||||
dist * sinf(camera->angle_p),
|
||||
dist * -cosf(camera->angle) * cosf(camera->angle_p),
|
||||
};
|
||||
vec3 eye;
|
||||
glm_vec3_sub(camera->pos, d, eye);
|
||||
vec3 up = {0.0, 1.0, 0.0};
|
||||
glm_lookat(eye, camera->pos, up, out);
|
||||
if (ctx->version != DRAW_FIELD) {
|
||||
// The actual camera position is slightly behind camera->pos.
|
||||
vec3 eye;
|
||||
glm_vec3_sub(camera->pos, front, eye);
|
||||
glm_lookat(eye, camera->pos, GLM_YUP, mv_out);
|
||||
} else {
|
||||
vec3 target;
|
||||
glm_vec3_add(camera->pos, front, target);
|
||||
glm_lookat(camera->pos, target, GLM_YUP, mv_out);
|
||||
}
|
||||
}
|
||||
|
||||
static void dungeon_render(struct sact_sprite *sp)
|
||||
@@ -263,50 +382,64 @@ static void dungeon_render(struct sact_sprite *sp)
|
||||
struct dungeon_context *ctx = (struct dungeon_context *)sp->plugin;
|
||||
if (!ctx->loaded || !ctx->draw_enabled)
|
||||
return;
|
||||
sprite_dirty(sp);
|
||||
struct texture *texture = sprite_get_texture(sp);
|
||||
|
||||
GLuint fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, texture, 0, 0, texture->w, texture->h);
|
||||
GLuint fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, &ctx->texture, 0, 0, ctx->texture.w, ctx->texture.h);
|
||||
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, ctx->depth_buffer);
|
||||
if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
ERROR("Incomplete framebuffer");
|
||||
|
||||
glClearColor(ctx->dgn->back_color_r / 255.f, ctx->dgn->back_color_g / 255.f, ctx->dgn->back_color_b / 255.f, 1.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glClearColor(0.f, 0.f, 0.f, 1.f);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
mat4 local_transform, view_transform, proj_transform;
|
||||
glm_mat4_identity(local_transform);
|
||||
model_view_matrix(&ctx->camera, view_transform);
|
||||
glm_perspective(M_PI / 3.0, (float)texture->w / texture->h, 0.5, 100.0, proj_transform);
|
||||
mat4 view_transform;
|
||||
model_view_matrix(ctx, view_transform);
|
||||
|
||||
// Tweak the projection transform so that the rendering result is vertically
|
||||
// flipped. If we render the scene normally, the resulting image will be
|
||||
// bottom-up (the first pixel is at the bottom-left), but we want a top-down
|
||||
// image (the first pixel is at the top-left).
|
||||
proj_transform[1][1] *= -1;
|
||||
glFrontFace(GL_CW);
|
||||
|
||||
int dgn_x = round(ctx->camera.pos[0] / 2.0);
|
||||
int dgn_y = round(ctx->camera.pos[1] / 2.0);
|
||||
int dgn_z = round(ctx->camera.pos[2] / -2.0);
|
||||
int dgn_x, dgn_y, dgn_z;
|
||||
if (ctx->version == DRAW_FIELD) {
|
||||
dgn_x = 0;
|
||||
dgn_y = ctx->dgn->size_y - 1;
|
||||
dgn_z = 0;
|
||||
} else {
|
||||
dgn_x = ctx->player_pos[0];
|
||||
dgn_y = ctx->player_pos[1];
|
||||
dgn_z = ctx->player_pos[2];
|
||||
}
|
||||
int nr_cells;
|
||||
struct dgn_cell **cells = dgn_get_visible_cells(ctx->dgn, dgn_x, dgn_y, dgn_z, &nr_cells);
|
||||
dungeon_renderer_render(ctx->renderer, cells, nr_cells, view_transform, proj_transform);
|
||||
dungeon_renderer_render(ctx->renderer, cells, nr_cells, ctx->characters, view_transform, ctx->proj_transform);
|
||||
|
||||
glFrontFace(GL_CCW);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
|
||||
gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo);
|
||||
|
||||
dungeon_renderer_run_post_processing(ctx->renderer, &ctx->texture, sprite_get_texture(sp));
|
||||
sprite_dirty(sp);
|
||||
}
|
||||
|
||||
static void neighbor_reveal(struct dungeon_context *ctx, int x, int y, int z)
|
||||
{
|
||||
struct dgn_cell *cell = dgn_cell_at(ctx->dgn, x, y, z);
|
||||
if (cell->floor < 0) {
|
||||
cell->walked = 1;
|
||||
dungeon_map_reveal(ctx, x, y, z, false);
|
||||
switch (ctx->version) {
|
||||
case DRAW_DUNGEON_1:
|
||||
if (cell->floor < 0) {
|
||||
cell->walked = 1;
|
||||
dungeon_map_reveal(ctx, x, y, z, false);
|
||||
}
|
||||
break;
|
||||
case DRAW_DUNGEON_2:
|
||||
if (cell->floor >= 0) {
|
||||
cell->walked |= 2;
|
||||
dungeon_map_reveal(ctx, x, y, z, false);
|
||||
}
|
||||
break;
|
||||
case DRAW_DUNGEON_14:
|
||||
case DRAW_FIELD:
|
||||
// do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,9 +448,11 @@ void dungeon_set_walked(int surface, int x, int y, int z, int flag)
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx)
|
||||
return;
|
||||
dgn_cell_at(ctx->dgn, x, y, z)->walked = 1;
|
||||
dungeon_map_reveal(ctx, x, y, z, false);
|
||||
if (ctx->version == DRAW_DUNGEON_1) {
|
||||
if (!dgn_is_in_map(ctx->dgn, x, y, z))
|
||||
return; // avoid VM_ERROR in Pastel Chime Continue
|
||||
if (flag) {
|
||||
dgn_cell_at(ctx->dgn, x, y, z)->walked = ctx->version == DRAW_DUNGEON_2 ? 3 : 1;
|
||||
dungeon_map_reveal(ctx, x, y, z, false);
|
||||
if (x > 0)
|
||||
neighbor_reveal(ctx, x - 1, y, z);
|
||||
if (x + 1u < ctx->dgn->size_x)
|
||||
@@ -326,6 +461,28 @@ void dungeon_set_walked(int surface, int x, int y, int z, int flag)
|
||||
neighbor_reveal(ctx, x, y, z - 1);
|
||||
if (z + 1u < ctx->dgn->size_z)
|
||||
neighbor_reveal(ctx, x, y, z + 1);
|
||||
} else {
|
||||
dgn_cell_at(ctx->dgn, x, y, z)->walked = 0;
|
||||
dungeon_map_hide(ctx, x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
void dungeon_set_walked_all(int surface)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx)
|
||||
return;
|
||||
int nr_cells = dgn_nr_cells(ctx->dgn);
|
||||
for (struct dgn_cell *c = ctx->dgn->cells; c < ctx->dgn->cells + nr_cells; c++) {
|
||||
if (ctx->version == DRAW_DUNGEON_2) {
|
||||
if (c->floor >= 0) {
|
||||
c->walked = 3;
|
||||
dungeon_map_reveal(ctx, c->x, c->y, c->z, false);
|
||||
}
|
||||
} else {
|
||||
c->walked = 1;
|
||||
dungeon_map_reveal(ctx, c->x, c->y, c->z, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,7 +505,7 @@ int dungeon_calc_conquer(int surface)
|
||||
/*
|
||||
* WalkData serialization format:
|
||||
*
|
||||
* struct WalkData {
|
||||
* struct DrawDungeon1_WalkData {
|
||||
* int version; // zero
|
||||
* int nr_maps;
|
||||
* struct {
|
||||
@@ -357,17 +514,26 @@ int dungeon_calc_conquer(int surface)
|
||||
* int cells[size_x * size_y * size_z];
|
||||
* } maps[nr_maps];
|
||||
* };
|
||||
*
|
||||
* struct DrawDungeon2_WalkData {
|
||||
* int magic; // 1977
|
||||
* int reserved; // zero
|
||||
* int size_x, size_y, size_z;
|
||||
* int cells[size_x * size_y * size_z];
|
||||
* };
|
||||
*/
|
||||
|
||||
#define DD2_WALKDATA_MAGIC 1977
|
||||
|
||||
enum {
|
||||
WALK_DATA_NOT_FOUND = -1,
|
||||
WALK_DATA_BROKEN = -2,
|
||||
};
|
||||
|
||||
static int find_walk_data(struct page *array, int map_no, struct dgn *dgn)
|
||||
static int dd1_find_walk_data(struct page *array, int map_no, struct dgn *dgn)
|
||||
{
|
||||
if (!array)
|
||||
return WALK_DATA_BROKEN;
|
||||
return WALK_DATA_NOT_FOUND;
|
||||
if (array->type != ARRAY_PAGE || array->a_type != AIN_ARRAY_INT || array->array.rank != 1)
|
||||
VM_ERROR("Not a flat integer array");
|
||||
|
||||
@@ -396,16 +562,13 @@ static int find_walk_data(struct page *array, int map_no, struct dgn *dgn)
|
||||
return WALK_DATA_NOT_FOUND;
|
||||
}
|
||||
|
||||
bool dungeon_load_walk_data(int surface, int map, struct page **page)
|
||||
static bool dd1_load_walk_data(struct dungeon_context *ctx, int map, struct page **page)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx || !ctx->dgn)
|
||||
return false;
|
||||
struct page *array = *page;
|
||||
|
||||
int offset = find_walk_data(array, map, ctx->dgn);
|
||||
int offset = dd1_find_walk_data(array, map, ctx->dgn);
|
||||
if (offset < 0)
|
||||
return false;
|
||||
return offset != WALK_DATA_BROKEN;
|
||||
|
||||
offset += 4;
|
||||
int nr_cells = dgn_nr_cells(ctx->dgn);
|
||||
@@ -418,20 +581,62 @@ bool dungeon_load_walk_data(int surface, int map, struct page **page)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dungeon_save_walk_data(int surface, int map, struct page **page)
|
||||
static bool dd2_load_walk_data(struct dungeon_context *ctx, struct page **page)
|
||||
{
|
||||
int nr_cells = dgn_nr_cells(ctx->dgn);
|
||||
struct page *array = *page;
|
||||
if (!array || array->nr_vars != 5 + nr_cells) {
|
||||
WARNING("DrawDungeon2.LoadWalkData: invalid array size");
|
||||
return false;
|
||||
}
|
||||
|
||||
int ptr = 0;
|
||||
if (array->values[ptr++].i != DD2_WALKDATA_MAGIC)
|
||||
return false;
|
||||
if (array->values[ptr++].i != 0)
|
||||
return false;
|
||||
uint32_t size_x = array->values[ptr++].i;
|
||||
uint32_t size_y = array->values[ptr++].i;
|
||||
uint32_t size_z = array->values[ptr++].i;
|
||||
if (size_x != ctx->dgn->size_x || size_y != ctx->dgn->size_y || size_z != ctx->dgn->size_z)
|
||||
return false;
|
||||
|
||||
for (struct dgn_cell *c = ctx->dgn->cells; c < ctx->dgn->cells + nr_cells; c++) {
|
||||
c->walked = array->values[ptr++].i;
|
||||
if (c->walked)
|
||||
dungeon_map_reveal(ctx, c->x, c->y, c->z, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dungeon_load_walk_data(int surface, int map, struct page **page)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx || !ctx->dgn)
|
||||
return false;
|
||||
switch (ctx->version) {
|
||||
case DRAW_DUNGEON_1:
|
||||
case DRAW_DUNGEON_14:
|
||||
return dd1_load_walk_data(ctx, map, page);
|
||||
case DRAW_DUNGEON_2:
|
||||
return dd2_load_walk_data(ctx, page);
|
||||
case DRAW_FIELD:
|
||||
break; // DrawField does not export LoadWalkData
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool dd1_save_walk_data(struct dungeon_context *ctx, int map, struct page **page)
|
||||
{
|
||||
struct page *array = *page;
|
||||
int nr_cells = dgn_nr_cells(ctx->dgn);
|
||||
|
||||
int offset = find_walk_data(array, map, ctx->dgn);
|
||||
int offset = dd1_find_walk_data(array, map, ctx->dgn);
|
||||
if (offset == WALK_DATA_NOT_FOUND) {
|
||||
offset = array->nr_vars;
|
||||
array->values[1].i += 1;
|
||||
union vm_value dim = { .i = array->nr_vars + 4 + nr_cells };
|
||||
offset = array ? array->nr_vars : 0;
|
||||
union vm_value dim = { .i = offset + 4 + nr_cells };
|
||||
*page = array = realloc_array(array, 1, &dim, AIN_ARRAY_INT, 0, false);
|
||||
array->values[1].i += 1;
|
||||
} else if (offset == WALK_DATA_BROKEN) {
|
||||
if (array)
|
||||
WARNING("Discarding broken walk data");
|
||||
@@ -449,3 +654,138 @@ bool dungeon_save_walk_data(int surface, int map, struct page **page)
|
||||
array->values[offset++].i = ctx->dgn->cells[i].walked;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool dd2_save_walk_data(struct dungeon_context *ctx, struct page **page)
|
||||
{
|
||||
struct page *array = *page;
|
||||
int nr_cells = dgn_nr_cells(ctx->dgn);
|
||||
|
||||
if (!array || array->nr_vars != 5 + nr_cells) {
|
||||
WARNING("DrawDungeon2.SaveWalkData: invalid array size");
|
||||
return false;
|
||||
}
|
||||
|
||||
int ptr = 0;
|
||||
array->values[ptr++].i = DD2_WALKDATA_MAGIC;
|
||||
array->values[ptr++].i = 0;
|
||||
array->values[ptr++].i = ctx->dgn->size_x;
|
||||
array->values[ptr++].i = ctx->dgn->size_y;
|
||||
array->values[ptr++].i = ctx->dgn->size_z;
|
||||
for (int i = 0; i < nr_cells; i++)
|
||||
array->values[ptr++].i = ctx->dgn->cells[i].walked;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dungeon_save_walk_data(int surface, int map, struct page **page)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx || !ctx->dgn)
|
||||
return false;
|
||||
switch (ctx->version) {
|
||||
case DRAW_DUNGEON_1:
|
||||
case DRAW_DUNGEON_14:
|
||||
return dd1_save_walk_data(ctx, map, page);
|
||||
case DRAW_DUNGEON_2:
|
||||
return dd2_save_walk_data(ctx, page);
|
||||
case DRAW_FIELD:
|
||||
break; // DrawField does not export SaveWalkData
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void dungeon_paint_step(int surface, int x, int y, int z)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx || !ctx->dgn)
|
||||
return;
|
||||
dgn_paint_step(ctx->dgn, x, z);
|
||||
}
|
||||
|
||||
void dungeon_set_chara_sprite(int surface, int num, int sprite)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx || !ctx->characters || num < 0 || num >= DRAWFIELD_NR_CHARACTERS)
|
||||
return;
|
||||
ctx->characters[num].sprite = sprite;
|
||||
}
|
||||
|
||||
void dungeon_set_chara_pos(int surface, int num, float x, float y, float z)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx || !ctx->characters || num < 0 || num >= DRAWFIELD_NR_CHARACTERS)
|
||||
return;
|
||||
ctx->characters[num].pos[0] = x;
|
||||
ctx->characters[num].pos[1] = y;
|
||||
ctx->characters[num].pos[2] = -z;
|
||||
}
|
||||
|
||||
void dungeon_set_chara_cg(int surface, int num, int cg)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx || !ctx->characters || num < 0 || num >= DRAWFIELD_NR_CHARACTERS)
|
||||
return;
|
||||
ctx->characters[num].cg_index = cg;
|
||||
}
|
||||
|
||||
void dungeon_set_chara_cg_info(int surface, int num, int num_chara_x, int num_chara_y)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx || !ctx->characters || num < 0 || num >= DRAWFIELD_NR_CHARACTERS)
|
||||
return;
|
||||
ctx->characters[num].rows = num_chara_y;
|
||||
ctx->characters[num].cols = num_chara_x;
|
||||
}
|
||||
|
||||
void dungeon_set_chara_show(int surface, int num, bool show)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx || !ctx->characters || num < 0 || num >= DRAWFIELD_NR_CHARACTERS)
|
||||
return;
|
||||
ctx->characters[num].show = show;
|
||||
}
|
||||
|
||||
bool dungeon_project_world_to_screen(struct dungeon_context *ctx, vec3 world_pos, Point *screen_pos)
|
||||
{
|
||||
mat4 view_transform;
|
||||
model_view_matrix(ctx, view_transform);
|
||||
mat4 mvp;
|
||||
glm_mat4_mul(ctx->proj_transform, view_transform, mvp);
|
||||
|
||||
vec4 world_pos_h = {
|
||||
world_pos[0],
|
||||
world_pos[1],
|
||||
-world_pos[2], // Z is negated to match coordinate system
|
||||
1.f
|
||||
};
|
||||
|
||||
vec4 clip_coords;
|
||||
glm_mat4_mulv(mvp, world_pos_h, clip_coords);
|
||||
|
||||
if (clip_coords[3] <= 0.f) {
|
||||
return false; // point is behind the near clipping plane
|
||||
}
|
||||
|
||||
vec3 ndc;
|
||||
ndc[0] = clip_coords[0] / clip_coords[3];
|
||||
ndc[1] = clip_coords[1] / clip_coords[3];
|
||||
ndc[2] = clip_coords[2] / clip_coords[3];
|
||||
|
||||
screen_pos->x = roundf(((ndc[0] + 1.f) / 2.f) * ctx->texture.w);
|
||||
// Y is inverted from NDC to screen space (NDC is bottom-up, screen is top-down)
|
||||
screen_pos->y = roundf(((1.f - ndc[1]) / 2.f) * ctx->texture.h);
|
||||
return true;
|
||||
}
|
||||
|
||||
static cJSON *dungeon_to_json(struct sact_sprite *sp, bool verbose)
|
||||
{
|
||||
cJSON *obj, *cam;
|
||||
struct dungeon_context *ctx = (struct dungeon_context *)sp->plugin;
|
||||
obj = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(obj, "name", ctx->plugin.name);
|
||||
cJSON_AddItemToObjectCS(obj, "camera", cam = cJSON_CreateObject());
|
||||
cJSON_AddItemToObjectCS(cam, "pos", vec3_point_to_json(ctx->camera.pos, verbose));
|
||||
cJSON_AddNumberToObject(cam, "angle", ctx->camera.angle);
|
||||
cJSON_AddNumberToObject(cam, "angle_p", ctx->camera.angle_p);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+257
-79
@@ -25,38 +25,40 @@
|
||||
#include "sact.h"
|
||||
#include "vm.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265358979323846)
|
||||
#endif
|
||||
|
||||
#define CS 12 // pixel size of a map cell
|
||||
#define LMAP_WIDTH 32
|
||||
#define LMAP_HEIGHT 32
|
||||
|
||||
static const SDL_Color unenterable_cell_color = {182, 72, 0, 0};
|
||||
|
||||
enum symbol_type {
|
||||
SYMBOL_PLAYER_NORTH = 0,
|
||||
SYMBOL_PLAYER_SOUTH = 1,
|
||||
SYMBOL_PLAYER_EAST = 2,
|
||||
SYMBOL_PLAYER_WEST = 3,
|
||||
SYMBOL_CROSS = 4,
|
||||
SYMBOL_EXIT = 5,
|
||||
SYMBOL_STAIRS_UP = 6,
|
||||
SYMBOL_STAIRS_DOWN = 7,
|
||||
SYMBOL_EVENT = 8,
|
||||
SYMBOL_HEART = 9,
|
||||
SYMBOL_WARP = 10,
|
||||
SYMBOL_TREASURE = 11,
|
||||
SYMBOL_MONSTER = 12,
|
||||
SYMBOL_YELLOW_STAR = 13,
|
||||
enum dd1_symbol_type {
|
||||
DD1_SYMBOL_PLAYER_NORTH = 0,
|
||||
DD1_SYMBOL_PLAYER_SOUTH = 1,
|
||||
DD1_SYMBOL_PLAYER_EAST = 2,
|
||||
DD1_SYMBOL_PLAYER_WEST = 3,
|
||||
DD1_SYMBOL_CROSS = 4,
|
||||
DD1_SYMBOL_EXIT = 5,
|
||||
DD1_SYMBOL_STAIRS_UP = 6,
|
||||
DD1_SYMBOL_STAIRS_DOWN = 7,
|
||||
DD1_SYMBOL_EVENT = 8,
|
||||
DD1_SYMBOL_HEART = 9,
|
||||
DD1_SYMBOL_WARP = 10,
|
||||
DD1_SYMBOL_TREASURE = 11,
|
||||
DD1_SYMBOL_MONSTER = 12,
|
||||
DD1_SYMBOL_YELLOW_STAR = 13,
|
||||
NR_SYMBOLS = 14
|
||||
};
|
||||
|
||||
enum dd2_symbol_type {
|
||||
DD2_SYMBOL_PLAYER_NORTH = 0,
|
||||
DD2_SYMBOL_PLAYER_EAST = 1,
|
||||
DD2_SYMBOL_PLAYER_WEST = 2,
|
||||
DD2_SYMBOL_PLAYER_SOUTH = 3,
|
||||
DD2_SYMBOL_START = 4,
|
||||
DD2_SYMBOL_EXIT = 5,
|
||||
};
|
||||
|
||||
struct dungeon_map {
|
||||
struct texture *textures;
|
||||
int nr_textures;
|
||||
int symbol_sprites[NR_SYMBOLS];
|
||||
int grid_size;
|
||||
int small_map_floor;
|
||||
int large_map_floor;
|
||||
bool radar;
|
||||
@@ -67,8 +69,8 @@ struct dungeon_map {
|
||||
static Point texture_pos(struct dungeon_context *ctx, int x, int z)
|
||||
{
|
||||
return (Point){
|
||||
.x = (ctx->map->offset_x + x) * CS,
|
||||
.y = (ctx->map->offset_y + ctx->dgn->size_z - z - 1) * CS
|
||||
.x = (ctx->map->offset_x + x) * ctx->map->grid_size,
|
||||
.y = (ctx->map->offset_y + ctx->dgn->size_z - z - 1) * ctx->map->grid_size,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -78,6 +80,18 @@ struct dungeon_map *dungeon_map_create(enum draw_dungeon_version version)
|
||||
return NULL; // GALZOO does not use DrawDungeon's 2D map functions.
|
||||
|
||||
struct dungeon_map *map = xcalloc(1, sizeof(struct dungeon_map));
|
||||
switch (version) {
|
||||
case DRAW_DUNGEON_1:
|
||||
case DRAW_FIELD:
|
||||
map->grid_size = 12;
|
||||
break;
|
||||
case DRAW_DUNGEON_2:
|
||||
map->grid_size = 11;
|
||||
break;
|
||||
case DRAW_DUNGEON_14:
|
||||
ERROR("cannot happen");
|
||||
break;
|
||||
}
|
||||
map->small_map_floor = -1;
|
||||
map->large_map_floor = -1;
|
||||
return map;
|
||||
@@ -98,33 +112,65 @@ void dungeon_map_init(struct dungeon_context *ctx)
|
||||
if (!map)
|
||||
return;
|
||||
|
||||
if (map->textures) {
|
||||
for (int i = 0; i < map->nr_textures; i++)
|
||||
gfx_delete_texture(&map->textures[i]);
|
||||
free(map->textures);
|
||||
}
|
||||
map->textures = xcalloc(dgn->size_y, sizeof(struct texture));
|
||||
map->nr_textures = dgn->size_y;
|
||||
// Calculate the bounding box on the XZ plane so that map is drawn in the
|
||||
// center of the texture.
|
||||
uint32_t min_x = dgn->size_x;
|
||||
uint32_t min_y = dgn->size_z;
|
||||
uint32_t max_x = 0;
|
||||
uint32_t max_y = 0;
|
||||
for (uint32_t y = 0; y < dgn->size_y; y++) {
|
||||
for (uint32_t z = 0; z < dgn->size_z; z++) {
|
||||
uint32_t map_y = ctx->dgn->size_z - z - 1;
|
||||
for (uint32_t x = 0; x < dgn->size_x; x++) {
|
||||
if (dgn_cell_at(ctx->dgn, x, y, z)->floor < 0)
|
||||
continue;
|
||||
min_x = min(min_x, x);
|
||||
min_y = min(min_y, map_y);
|
||||
max_x = max(max_x, x);
|
||||
max_y = max(max_y, map_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
map->offset_x = (LMAP_WIDTH - (max_x - min_x + 1)) / 2 - min_x;
|
||||
map->offset_y = (LMAP_HEIGHT - (max_y - min_y)) / 2 - min_y;
|
||||
|
||||
int map_width;
|
||||
int map_height;
|
||||
switch (ctx->version) {
|
||||
case DRAW_DUNGEON_1: {
|
||||
// Calculate the bounding box on the XZ plane so that map is drawn in the
|
||||
// center of the texture.
|
||||
uint32_t min_x = dgn->size_x;
|
||||
uint32_t min_y = dgn->size_z;
|
||||
uint32_t max_x = 0;
|
||||
uint32_t max_y = 0;
|
||||
for (uint32_t y = 0; y < dgn->size_y; y++) {
|
||||
for (uint32_t z = 0; z < dgn->size_z; z++) {
|
||||
uint32_t map_y = ctx->dgn->size_z - z - 1;
|
||||
for (uint32_t x = 0; x < dgn->size_x; x++) {
|
||||
if (dgn_cell_at(ctx->dgn, x, y, z)->floor < 0)
|
||||
continue;
|
||||
min_x = min(min_x, x);
|
||||
min_y = min(min_y, map_y);
|
||||
max_x = max(max_x, x);
|
||||
max_y = max(max_y, map_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
map_width = 32;
|
||||
map_height = 32;
|
||||
map->offset_x = (map_width - (max_x - min_x + 1)) / 2 - min_x;
|
||||
map->offset_y = (map_height - (max_y - min_y)) / 2 - min_y;
|
||||
}
|
||||
break;
|
||||
case DRAW_DUNGEON_2:
|
||||
map_width = 19;
|
||||
map_height = 19;
|
||||
map->offset_x = 0;
|
||||
map->offset_y = 1;
|
||||
break;
|
||||
case DRAW_DUNGEON_14:
|
||||
ERROR("cannot happen");
|
||||
break;
|
||||
case DRAW_FIELD:
|
||||
map_width = dgn->size_x;
|
||||
map_height = dgn->size_z;
|
||||
map->offset_x = 0;
|
||||
map->offset_y = 0;
|
||||
break;
|
||||
}
|
||||
// Initial draw
|
||||
for (uint32_t y = 0; y < dgn->size_y; y++) {
|
||||
gfx_init_texture_rgba(&map->textures[y], LMAP_WIDTH * CS, LMAP_HEIGHT * CS, unenterable_cell_color);
|
||||
gfx_init_texture_rgba(&map->textures[y],
|
||||
map_width * map->grid_size,
|
||||
map_height * map->grid_size,
|
||||
unenterable_cell_color);
|
||||
for (uint32_t z = 0; z < dgn->size_z; z++) {
|
||||
for (uint32_t x = 0; x < dgn->size_x; x++) {
|
||||
dungeon_map_update_cell(ctx, x, y, z);
|
||||
@@ -133,13 +179,43 @@ void dungeon_map_init(struct dungeon_context *ctx)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (!ctx->map)
|
||||
return;
|
||||
struct texture *dst = &ctx->map->textures[y];
|
||||
struct dgn_cell *cell = dgn_cell_at(ctx->dgn, x, y, z);
|
||||
Point pos = texture_pos(ctx, x, z);
|
||||
int alpha;
|
||||
switch (ctx->version) {
|
||||
case DRAW_DUNGEON_1:
|
||||
alpha = from_walk_data ? 128 : 255;
|
||||
gfx_fill_amap(dst, pos.x, pos.y, ctx->map->grid_size, ctx->map->grid_size, alpha);
|
||||
break;
|
||||
case DRAW_DUNGEON_2:
|
||||
if ((x == ctx->dgn->start_x && z == ctx->dgn->start_y) || cell->floor_event == 17) {
|
||||
// Do not overwrite the start/goal symbol.
|
||||
gfx_fill_amap(dst, pos.x, pos.y, ctx->map->grid_size, ctx->map->grid_size, 255);
|
||||
} else {
|
||||
int rg = (cell->walked & 1) ? 0 : 255;
|
||||
gfx_fill_with_alpha(dst, pos.x, pos.y, ctx->map->grid_size, ctx->map->grid_size, rg, rg, 0, 255);
|
||||
}
|
||||
break;
|
||||
case DRAW_FIELD:
|
||||
alpha = cell->floor == -1 ? 160 : 255;
|
||||
gfx_fill_amap(dst, pos.x, pos.y, ctx->map->grid_size, ctx->map->grid_size, alpha);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void dungeon_map_hide(struct dungeon_context *ctx, int x, int y, int z)
|
||||
{
|
||||
if (!ctx->map)
|
||||
return;
|
||||
struct texture *dst = &ctx->map->textures[y];
|
||||
Point pos = texture_pos(ctx, x, z);
|
||||
gfx_fill_amap(dst, pos.x, pos.y, CS, CS, transparent ? 128 : 255);
|
||||
gfx_fill_amap(dst, pos.x, pos.y, ctx->map->grid_size, ctx->map->grid_size, 0);
|
||||
}
|
||||
|
||||
static void draw_hline(struct texture *dst, int x, int y, int w, SDL_Color col)
|
||||
@@ -155,13 +231,13 @@ static void draw_vline(struct texture *dst, int x, int y, int h, SDL_Color col)
|
||||
static void draw_symbol(struct dungeon_map *map, struct texture *dst, int x, int y, int type)
|
||||
{
|
||||
struct sact_sprite *sp = sact_get_sprite(map->symbol_sprites[type]);
|
||||
if (!sp || sp->rect.w != CS - 2 || sp->rect.h != CS - 2)
|
||||
if (!sp)
|
||||
VM_ERROR("Invalid map cg %d", map->symbol_sprites[type]);
|
||||
struct texture *src = sprite_get_texture(sp);
|
||||
gfx_copy(dst, x, y, src, 0, 0, src->w, src->h);
|
||||
}
|
||||
|
||||
void dungeon_map_update_cell(struct dungeon_context *ctx, int dgn_x, int dgn_y, int dgn_z)
|
||||
static void dd1_update_cell(struct dungeon_context *ctx, int dgn_x, int dgn_y, int dgn_z)
|
||||
{
|
||||
if (!ctx->map)
|
||||
return;
|
||||
@@ -170,6 +246,7 @@ void dungeon_map_update_cell(struct dungeon_context *ctx, int dgn_x, int dgn_y,
|
||||
Point pos = texture_pos(ctx, dgn_x, dgn_z);
|
||||
int x = pos.x;
|
||||
int y = pos.y;
|
||||
const int CS = ctx->map->grid_size;
|
||||
|
||||
if (cell->floor >= 0)
|
||||
gfx_fill(dst, x, y, CS, CS, 0, 0, 0);
|
||||
@@ -217,33 +294,126 @@ void dungeon_map_update_cell(struct dungeon_context *ctx, int dgn_x, int dgn_y,
|
||||
bool draw_all_symbols = ctx->map->radar || ctx->map->all_visible;
|
||||
int symbol = -1;
|
||||
switch (cell->floor_event) {
|
||||
case 11: if (draw_all_symbols) symbol = SYMBOL_TREASURE; break;
|
||||
case 12: if (draw_all_symbols) symbol = SYMBOL_MONSTER; break;
|
||||
case 14: case 140: symbol = SYMBOL_EVENT; break;
|
||||
case 15: symbol = SYMBOL_HEART; break;
|
||||
case 16: if (draw_all_symbols) symbol = SYMBOL_CROSS; break;
|
||||
case 17: symbol = SYMBOL_EXIT; break;
|
||||
case 18: symbol = SYMBOL_WARP; break;
|
||||
case 28: if (draw_all_symbols) symbol = SYMBOL_YELLOW_STAR; break;
|
||||
case 11: if (draw_all_symbols) symbol = DD1_SYMBOL_TREASURE; break;
|
||||
case 12: if (draw_all_symbols) symbol = DD1_SYMBOL_MONSTER; break;
|
||||
case 14: case 140: symbol = DD1_SYMBOL_EVENT; break;
|
||||
case 15: symbol = DD1_SYMBOL_HEART; break;
|
||||
case 16: if (draw_all_symbols) symbol = DD1_SYMBOL_CROSS; break;
|
||||
case 17: symbol = DD1_SYMBOL_EXIT; break;
|
||||
case 18: symbol = DD1_SYMBOL_WARP; break;
|
||||
case 28: if (draw_all_symbols) symbol = DD1_SYMBOL_YELLOW_STAR; break;
|
||||
}
|
||||
if (cell->stairs_texture >= 0)
|
||||
symbol = SYMBOL_STAIRS_UP;
|
||||
symbol = DD1_SYMBOL_STAIRS_UP;
|
||||
else if (dgn_y && dgn_cell_at(ctx->dgn, dgn_x, dgn_y - 1, dgn_z)->stairs_texture >= 0)
|
||||
symbol = SYMBOL_STAIRS_DOWN;
|
||||
symbol = DD1_SYMBOL_STAIRS_DOWN;
|
||||
|
||||
if (symbol >= 0)
|
||||
draw_symbol(ctx->map, dst, x + 1, y + 1, symbol);
|
||||
}
|
||||
|
||||
static void dd2_update_cell(struct dungeon_context *ctx, int dgn_x, int dgn_y, int dgn_z)
|
||||
{
|
||||
if (!ctx->map)
|
||||
return;
|
||||
struct dgn_cell *cell = dgn_cell_at(ctx->dgn, dgn_x, dgn_y, dgn_z);
|
||||
struct texture *dst = &ctx->map->textures[0];
|
||||
Point pos = texture_pos(ctx, dgn_x, dgn_z);
|
||||
|
||||
if (dgn_x == ctx->dgn->start_x && dgn_z == ctx->dgn->start_y) {
|
||||
draw_symbol(ctx->map, dst, pos.x, pos.y, DD2_SYMBOL_START);
|
||||
} else if (cell->floor_event == 17) {
|
||||
draw_symbol(ctx->map, dst, pos.x, pos.y, DD2_SYMBOL_EXIT);
|
||||
}
|
||||
}
|
||||
|
||||
static void df_update_cell(struct dungeon_context *ctx, int dgn_x, int dgn_y, int dgn_z)
|
||||
{
|
||||
if (!ctx->map)
|
||||
return;
|
||||
struct dgn_cell *cell = dgn_cell_at(ctx->dgn, dgn_x, dgn_y, dgn_z);
|
||||
struct texture *dst = &ctx->map->textures[dgn_y];
|
||||
Point pos = texture_pos(ctx, dgn_x, dgn_z);
|
||||
int x = pos.x;
|
||||
int y = pos.y;
|
||||
const int CS = ctx->map->grid_size;
|
||||
|
||||
if (cell->floor >= 0)
|
||||
gfx_fill(dst, x, y, CS, CS, 128, 64, 32);
|
||||
else
|
||||
gfx_fill(dst, x, y, CS, CS, 0, 0, 0);
|
||||
|
||||
const SDL_Color wall_color = {255, 255, 255, 255};
|
||||
if (cell->north_wall >= 0)
|
||||
draw_hline(dst, x, y, CS, wall_color);
|
||||
if (cell->south_wall >= 0)
|
||||
draw_hline(dst, x, y + CS - 1, CS, wall_color);
|
||||
if (cell->west_wall >= 0)
|
||||
draw_vline(dst, x, y, CS, wall_color);
|
||||
if (cell->east_wall >= 0)
|
||||
draw_vline(dst, x + CS - 1, y, CS, wall_color);
|
||||
|
||||
const SDL_Color door_colors[2] = {{0, 255, 0, 255}, {255, 0, 0, 255}};
|
||||
if (cell->north_door >= 0) {
|
||||
draw_hline(dst, x, y, CS / 2 - 1, door_colors[cell->north_door_lock]);
|
||||
draw_hline(dst, x + CS / 2 - 1, y, 2, COLOR(0, 0, 0, 0));
|
||||
draw_hline(dst, x + CS / 2 + 1, y, CS / 2 - 1, door_colors[cell->north_door_lock]);
|
||||
}
|
||||
if (cell->south_door >= 0) {
|
||||
draw_hline(dst, x, y + CS - 1, CS / 2 - 1, door_colors[cell->south_door_lock]);
|
||||
draw_hline(dst, x + CS / 2 - 1, y + CS - 1, 2, COLOR(0, 0, 0, 0));
|
||||
draw_hline(dst, x + CS / 2 + 1, y + CS - 1, CS / 2 - 1, door_colors[cell->south_door_lock]);
|
||||
}
|
||||
if (cell->west_door >= 0) {
|
||||
draw_vline(dst, x, y, CS / 2 - 1, door_colors[cell->west_door_lock]);
|
||||
draw_vline(dst, x, y + CS / 2 - 1, 2, COLOR(0, 0, 0, 0));
|
||||
draw_vline(dst, x, y + CS / 2 + 1, CS / 2 - 1, door_colors[cell->west_door_lock]);
|
||||
}
|
||||
if (cell->east_door >= 0) {
|
||||
draw_vline(dst, x + CS - 1, y, CS / 2 - 1, door_colors[cell->east_door_lock]);
|
||||
draw_vline(dst, x + CS - 1, y + CS / 2 - 1, 2, COLOR(0, 0, 0, 0));
|
||||
draw_vline(dst, x + CS - 1, y + CS / 2 + 1, CS / 2 - 1, door_colors[cell->east_door_lock]);
|
||||
}
|
||||
|
||||
if (cell->stairs_texture >= 0)
|
||||
gfx_fill(dst, x + 2, y + 2, CS - 4, CS - 4, 0, 255, 255);
|
||||
else if (dgn_y && dgn_cell_at(ctx->dgn, dgn_x, dgn_y - 1, dgn_z)->stairs_texture >= 0)
|
||||
gfx_fill(dst, x + 2, y + 2, CS - 4, CS - 4, 0, 0, 255);
|
||||
}
|
||||
|
||||
void dungeon_map_update_cell(struct dungeon_context *ctx, int dgn_x, int dgn_y, int dgn_z)
|
||||
{
|
||||
switch (ctx->version) {
|
||||
case DRAW_DUNGEON_1:
|
||||
dd1_update_cell(ctx, dgn_x, dgn_y, dgn_z);
|
||||
break;
|
||||
case DRAW_DUNGEON_2:
|
||||
dd2_update_cell(ctx, dgn_x, dgn_y, dgn_z);
|
||||
break;
|
||||
case DRAW_DUNGEON_14:
|
||||
// unused
|
||||
break;
|
||||
case DRAW_FIELD:
|
||||
df_update_cell(ctx, dgn_x, dgn_y, dgn_z);
|
||||
}
|
||||
}
|
||||
|
||||
static int player_symbol(struct dungeon_context *ctx)
|
||||
{
|
||||
const int symbols[] = {
|
||||
SYMBOL_PLAYER_NORTH,
|
||||
SYMBOL_PLAYER_EAST,
|
||||
SYMBOL_PLAYER_SOUTH,
|
||||
SYMBOL_PLAYER_WEST,
|
||||
const int dd1_symbols[] = {
|
||||
DD1_SYMBOL_PLAYER_NORTH,
|
||||
DD1_SYMBOL_PLAYER_EAST,
|
||||
DD1_SYMBOL_PLAYER_SOUTH,
|
||||
DD1_SYMBOL_PLAYER_WEST,
|
||||
};
|
||||
return symbols[(int)(round(ctx->camera.angle * 2 / M_PI)) & 3];
|
||||
const int dd2_symbols[] = {
|
||||
DD2_SYMBOL_PLAYER_NORTH,
|
||||
DD2_SYMBOL_PLAYER_EAST,
|
||||
DD2_SYMBOL_PLAYER_SOUTH,
|
||||
DD2_SYMBOL_PLAYER_WEST,
|
||||
};
|
||||
const int *symbols = ctx->version == DRAW_DUNGEON_1 ? dd1_symbols : dd2_symbols;
|
||||
return symbols[(int)(roundf(ctx->camera.angle * 2 / GLM_PIf)) & 3];
|
||||
}
|
||||
|
||||
void dungeon_map_draw(int surface, int sprite)
|
||||
@@ -251,22 +421,27 @@ void dungeon_map_draw(int surface, int sprite)
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
if (!ctx || !ctx->map)
|
||||
return;
|
||||
|
||||
if (ctx->version == DRAW_DUNGEON_2) {
|
||||
// Draw the entire map
|
||||
dungeon_map_draw_lmap(surface, sprite);
|
||||
return;
|
||||
}
|
||||
|
||||
struct sact_sprite *sp = sact_get_sprite(sprite);
|
||||
if (!sp)
|
||||
VM_ERROR("DrawDungeon.DrawMap: invalid sprite %d", sprite);
|
||||
sprite_dirty(sp);
|
||||
struct texture *dst = sprite_get_texture(sp);
|
||||
|
||||
int player_x = round(ctx->camera.pos[0] / 2.0);
|
||||
int player_y = round(ctx->camera.pos[1] / 2.0);
|
||||
int player_z = round(ctx->camera.pos[2] / -2.0);
|
||||
int level = ctx->map->small_map_floor;
|
||||
if (level < 0)
|
||||
level = player_y;
|
||||
level = ctx->player_pos[1];
|
||||
|
||||
struct texture *src = &ctx->map->textures[level];
|
||||
|
||||
Point pos = texture_pos(ctx, player_x, player_z);
|
||||
const int CS = ctx->map->grid_size;
|
||||
Point pos = texture_pos(ctx, ctx->player_pos[0], ctx->player_pos[2]);
|
||||
int x = pos.x - (dst->w - CS) / 2;
|
||||
int y = pos.y - (dst->h - CS) / 2;
|
||||
|
||||
@@ -292,12 +467,9 @@ void dungeon_map_draw_lmap(int surface, int sprite)
|
||||
sprite_dirty(sp);
|
||||
struct texture *dst = sprite_get_texture(sp);
|
||||
|
||||
int player_x = round(ctx->camera.pos[0] / 2.0);
|
||||
int player_y = round(ctx->camera.pos[1] / 2.0);
|
||||
int player_z = round(ctx->camera.pos[2] / -2.0);
|
||||
int level = ctx->map->large_map_floor;
|
||||
if (level < 0)
|
||||
level = player_y;
|
||||
level = ctx->player_pos[1];
|
||||
|
||||
struct texture *src = &ctx->map->textures[level];
|
||||
gfx_copy_with_alpha_map(dst, 0, 0, src, 0, 0, src->w, src->h);
|
||||
@@ -306,9 +478,15 @@ void dungeon_map_draw_lmap(int surface, int sprite)
|
||||
gfx_fill_amap(dst, 0, 0, dst->w, dst->h, 255);
|
||||
|
||||
// Draw the player symbol.
|
||||
Point pos = texture_pos(ctx, player_x, player_z);
|
||||
draw_symbol(ctx->map, dst, pos.x + 1, pos.y + 1, player_symbol(ctx));
|
||||
gfx_fill_amap(dst, pos.x + 1, pos.y + 1, CS - 2, CS - 2, level == player_y ? 255 : 128);
|
||||
Point pos = texture_pos(ctx, ctx->player_pos[0], ctx->player_pos[2]);
|
||||
if (ctx->version == DRAW_FIELD) {
|
||||
int ofs = (ctx->map->grid_size - 4) / 2;
|
||||
gfx_fill(dst, pos.x + ofs, pos.y + ofs, 4, 4, 255, 192, 255);
|
||||
} else {
|
||||
draw_symbol(ctx->map, dst, pos.x + 1, pos.y + 1, player_symbol(ctx));
|
||||
int size = ctx->map->grid_size - 2;
|
||||
gfx_fill_amap(dst, pos.x + 1, pos.y + 1, size, size, level == ctx->player_pos[1] ? 255 : 128);
|
||||
}
|
||||
}
|
||||
|
||||
static void redraw_event_symbols(struct dungeon_context *ctx)
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
// Mersenne Twister engine with N=4, M=3
|
||||
// Derived from mt19937int.c in libsys4
|
||||
|
||||
/* A C-program for MT19937: Integer version (1998/4/6) */
|
||||
/* genrand() generates one pseudorandom unsigned integer (32bit) */
|
||||
/* which is uniformly distributed among 0 to 2^32-1 for each */
|
||||
/* call. sgenrand(seed) set initial values to the working area */
|
||||
/* of 624 words. Before genrand(), sgenrand(seed) must be */
|
||||
/* called once. (seed is any 32-bit integer except for 0). */
|
||||
/* Coded by Takuji Nishimura, considering the suggestions by */
|
||||
/* Topher Cooper and Marc Rieffel in July-Aug. 1997. */
|
||||
|
||||
/* This library is free software; you can redistribute it and/or */
|
||||
/* modify it under the terms of the GNU Library General Public */
|
||||
/* License as published by the Free Software Foundation; either */
|
||||
/* version 2 of the License, or (at your option) any later */
|
||||
/* version. */
|
||||
/* This library 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 Library General Public License for more details. */
|
||||
/* You should have received a copy of the GNU Library General */
|
||||
/* Public License along with this library; if not, write to the */
|
||||
/* Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA */
|
||||
/* 02111-1307 USA */
|
||||
|
||||
/* Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura. */
|
||||
/* When you use this, send an email to: matumoto@math.keio.ac.jp */
|
||||
/* with an appropriate reference to your work. */
|
||||
|
||||
/* REFERENCE */
|
||||
/* M. Matsumoto and T. Nishimura, */
|
||||
/* "Mersenne Twister: A 623-Dimensionally Equidistributed Uniform */
|
||||
/* Pseudo-Random Number Generator", */
|
||||
/* ACM Transactions on Modeling and Computer Simulation, */
|
||||
/* Vol. 8, No. 1, January 1998, pp 3--30. */
|
||||
|
||||
#include "dungeon/mtrand43.h"
|
||||
|
||||
/* Period parameters */
|
||||
#define N MTRAND43_STATE_SIZE
|
||||
#define M 3
|
||||
#define MATRIX_A 0x9908b0df /* constant vector a */
|
||||
#define UPPER_MASK 0x80000000 /* most significant w-r bits */
|
||||
#define LOWER_MASK 0x7fffffff /* least significant r bits */
|
||||
|
||||
/* Tempering parameters */
|
||||
#define TEMPERING_MASK_B 0x9d2c5680
|
||||
#define TEMPERING_MASK_C 0xefc60000
|
||||
#define TEMPERING_SHIFT_U(y) (y >> 11)
|
||||
#define TEMPERING_SHIFT_S(y) (y << 7)
|
||||
#define TEMPERING_SHIFT_T(y) (y << 15)
|
||||
#define TEMPERING_SHIFT_L(y) (y >> 18)
|
||||
|
||||
/* initializing the array with a NONZERO seed */
|
||||
void mtrand43_init(struct mtrand43 *mt, uint32_t seed)
|
||||
{
|
||||
/* setting initial seeds to mt[N] using */
|
||||
/* the generator Line 25 of Table 1 in */
|
||||
/* [KNUTH 1981, The Art of Computer Programming */
|
||||
/* Vol. 2 (2nd Ed.), pp102] */
|
||||
mt->st[0]= seed & 0xffffffff;
|
||||
for (int i=1; i<N; i++)
|
||||
mt->st[i] = (69069 * mt->st[i-1]) & 0xffffffff;
|
||||
mt->i = N;
|
||||
}
|
||||
|
||||
uint32_t mtrand43_genrand(struct mtrand43 *mt)
|
||||
{
|
||||
uint32_t y;
|
||||
static const uint32_t mag01[2]={0x0, MATRIX_A};
|
||||
/* mag01[x] = x * MATRIX_A for x=0,1 */
|
||||
|
||||
if (mt->i >= N) { /* generate N words at one time */
|
||||
int kk;
|
||||
|
||||
for (kk=0;kk<N-M;kk++) {
|
||||
y = (mt->st[kk]&UPPER_MASK)|(mt->st[kk+1]&LOWER_MASK);
|
||||
mt->st[kk] = mt->st[kk+M] ^ (y >> 1) ^ mag01[y & 0x1];
|
||||
}
|
||||
for (;kk<N-1;kk++) {
|
||||
y = (mt->st[kk]&UPPER_MASK)|(mt->st[kk+1]&LOWER_MASK);
|
||||
mt->st[kk] = mt->st[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1];
|
||||
}
|
||||
y = (mt->st[N-1]&UPPER_MASK)|(mt->st[0]&LOWER_MASK);
|
||||
mt->st[N-1] = mt->st[M-1] ^ (y >> 1) ^ mag01[y & 0x1];
|
||||
|
||||
mt->i = 0;
|
||||
}
|
||||
|
||||
y = mt->st[mt->i++];
|
||||
y ^= TEMPERING_SHIFT_U(y);
|
||||
y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B;
|
||||
y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C;
|
||||
y ^= TEMPERING_SHIFT_L(y);
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
// Returns a random float between [0.0, 1.0], inclusive.
|
||||
float mtrand43_genfloat(struct mtrand43 *mt)
|
||||
{
|
||||
return (float)(mtrand43_genrand(mt)) / 4294967296.f;
|
||||
}
|
||||
+329
-43
@@ -30,6 +30,12 @@
|
||||
#include "dungeon/renderer.h"
|
||||
#include "dungeon/skybox.h"
|
||||
#include "gfx/gfx.h"
|
||||
#include "sact.h"
|
||||
|
||||
enum {
|
||||
COLOR_TEXTURE_UNIT,
|
||||
LIGHT_TEXTURE_UNIT,
|
||||
};
|
||||
|
||||
struct marker_info {
|
||||
int event_type;
|
||||
@@ -42,10 +48,18 @@ enum floor_marker {
|
||||
// Rance VI
|
||||
R6_MAGIC_CIRCLE = 1,
|
||||
R6_RED_OCTAGRAM = 63,
|
||||
// Dungeons & Dolls
|
||||
DD_MAGIC_CIRCLE = 40,
|
||||
DD_RED_OCTAGRAM = 41,
|
||||
DD_WHITE_OCTAGRAM = 42,
|
||||
// GALZOO Island
|
||||
GZ_MAGIC_CIRCLE = 0,
|
||||
};
|
||||
|
||||
static const struct marker_info empty_marker_info[] = {
|
||||
{0},
|
||||
};
|
||||
|
||||
static const struct marker_info rance6_marker_info[] = {
|
||||
{ 11, 41, 3, R6_MAGIC_CIRCLE}, // treasure chest
|
||||
{ 12, 51, 7, R6_RED_OCTAGRAM}, // enemy
|
||||
@@ -58,6 +72,18 @@ static const struct marker_info rance6_marker_info[] = {
|
||||
{0},
|
||||
};
|
||||
|
||||
static const struct marker_info dolls_marker_info[] = {
|
||||
{11, 43, 3, DD_WHITE_OCTAGRAM}, // treasure chest
|
||||
{17, 0, 20, DD_MAGIC_CIRCLE}, // goal
|
||||
{0},
|
||||
};
|
||||
|
||||
static const struct marker_info dolls_boss_marker_info[] = {
|
||||
{11, 43, 3, DD_WHITE_OCTAGRAM}, // treasure chest
|
||||
{17, 20, 20, DD_RED_OCTAGRAM}, // boss
|
||||
{0},
|
||||
};
|
||||
|
||||
static const struct marker_info galzoo_marker_info[] = {
|
||||
{11, 1, 0, GZ_MAGIC_CIRCLE}, // item
|
||||
{12, 2, 0, GZ_MAGIC_CIRCLE}, // enemy
|
||||
@@ -70,21 +96,33 @@ static const struct marker_info galzoo_marker_info[] = {
|
||||
|
||||
static const struct marker_info *marker_tables[] = {
|
||||
[DRAW_DUNGEON_1] = rance6_marker_info,
|
||||
[DRAW_DUNGEON_14] = galzoo_marker_info
|
||||
[DRAW_DUNGEON_2] = dolls_marker_info,
|
||||
[DRAW_DUNGEON_14] = galzoo_marker_info,
|
||||
[DRAW_FIELD] = empty_marker_info
|
||||
};
|
||||
|
||||
struct geometry;
|
||||
struct material;
|
||||
struct object3d;
|
||||
|
||||
struct raster_shader {
|
||||
struct shader s;
|
||||
GLint amp;
|
||||
GLint t;
|
||||
};
|
||||
|
||||
struct dungeon_renderer {
|
||||
enum draw_dungeon_version version;
|
||||
struct shader shader;
|
||||
// Uniform variable locations
|
||||
GLint local_transform;
|
||||
GLint proj_transform;
|
||||
GLuint alpha_mod;
|
||||
// Attribute variable locations
|
||||
GLint vertex_uv;
|
||||
GLint alpha_mod;
|
||||
GLint use_lightmap;
|
||||
GLint light_texture;
|
||||
GLint use_fog;
|
||||
GLint uv_offset;
|
||||
GLint uv_scale;
|
||||
|
||||
struct geometry *wall_geometry;
|
||||
struct geometry *door_left_geometry;
|
||||
@@ -108,6 +146,23 @@ struct dungeon_renderer {
|
||||
struct material *polyobj_materials;
|
||||
|
||||
struct skybox *skybox;
|
||||
|
||||
struct raster_shader raster_shader;
|
||||
bool raster_scroll;
|
||||
float raster_amp;
|
||||
|
||||
bool enable_lightmap;
|
||||
uint32_t draw_obj_flags;
|
||||
};
|
||||
|
||||
enum draw_obj_flag_index {
|
||||
DRAW_POLYOBJ = 0,
|
||||
DRAW_FLOOR = 1,
|
||||
DRAW_WALL = 2,
|
||||
DRAW_DOOR = 3,
|
||||
DRAW_CEILING = 4,
|
||||
DRAW_STAIRS = 5,
|
||||
NR_DRAW_OBJ_FLAGS
|
||||
};
|
||||
|
||||
static const struct marker_info *get_marker_info(struct dungeon_renderer *r, int event_type)
|
||||
@@ -211,10 +266,10 @@ static struct geometry *geometry_create(struct dungeon_renderer *r, const struct
|
||||
glGenBuffers(1, &g->attr_buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, g->attr_buffer);
|
||||
|
||||
glEnableVertexAttribArray(r->shader.vertex);
|
||||
glVertexAttribPointer(r->shader.vertex, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (const void *)0);
|
||||
glEnableVertexAttribArray(r->vertex_uv);
|
||||
glVertexAttribPointer(r->vertex_uv, 2, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (const void *)(3 * sizeof(GLfloat)));
|
||||
glEnableVertexAttribArray(r->shader.vertex_pos);
|
||||
glVertexAttribPointer(r->shader.vertex_pos, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (const void *)0);
|
||||
glEnableVertexAttribArray(r->shader.vertex_uv);
|
||||
glVertexAttribPointer(r->shader.vertex_uv, 2, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (const void *)(3 * sizeof(GLfloat)));
|
||||
glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW);
|
||||
|
||||
glBindVertexArray(0);
|
||||
@@ -332,15 +387,46 @@ static void init_polyobjs(struct dungeon_renderer *r, struct polyobj *po)
|
||||
}
|
||||
}
|
||||
|
||||
struct dungeon_renderer *dungeon_renderer_create(enum draw_dungeon_version version, struct dtx *dtx, GLuint *event_textures, int nr_event_textures, struct polyobj *po)
|
||||
static bool is_dolls_boss_floor(int num)
|
||||
{
|
||||
switch (num) {
|
||||
case 20:
|
||||
case 40:
|
||||
case 60:
|
||||
case 80:
|
||||
case 100:
|
||||
case 120:
|
||||
case 140:
|
||||
case 150:
|
||||
case 160:
|
||||
case 180:
|
||||
case 200:
|
||||
case 300:
|
||||
case 500:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct dungeon_renderer *r = xcalloc(1, sizeof(struct dungeon_renderer));
|
||||
|
||||
r->version = version;
|
||||
gfx_load_shader(&r->shader, "shaders/dungeon.v.glsl", "shaders/dungeon.f.glsl");
|
||||
r->local_transform = glGetUniformLocation(r->shader.program, "local_transform");
|
||||
r->proj_transform = glGetUniformLocation(r->shader.program, "proj_transform");
|
||||
r->alpha_mod = glGetUniformLocation(r->shader.program, "alpha_mod");
|
||||
r->vertex_uv = glGetAttribLocation(r->shader.program, "vertex_uv");
|
||||
r->use_lightmap = glGetUniformLocation(r->shader.program, "use_lightmap");
|
||||
r->light_texture = glGetUniformLocation(r->shader.program, "light_texture");
|
||||
r->use_fog = glGetUniformLocation(r->shader.program, "use_fog");
|
||||
r->uv_offset = glGetUniformLocation(r->shader.program, "uv_offset");
|
||||
r->uv_scale = glGetUniformLocation(r->shader.program, "uv_scale");
|
||||
glUseProgram(r->shader.program);
|
||||
glUniform1i(r->use_fog, version != DRAW_FIELD);
|
||||
glUniform2f(r->uv_offset, 0.0f, 0.0f);
|
||||
glUniform2f(r->uv_scale, 1.0f, 1.0f);
|
||||
glUseProgram(0);
|
||||
|
||||
r->wall_geometry = geometry_create(r, wall_vertices, sizeof(wall_vertices), GL_TRIANGLE_STRIP);
|
||||
r->door_left_geometry = geometry_create(r, door_left_vertices, sizeof(door_left_vertices), GL_TRIANGLE_STRIP);
|
||||
@@ -349,9 +435,8 @@ struct dungeon_renderer *dungeon_renderer_create(enum draw_dungeon_version versi
|
||||
r->stairs_geometry = geometry_create(r, stairs_vertices, sizeof(stairs_vertices), GL_TRIANGLE_STRIP);
|
||||
r->floating_marker_geometry = geometry_create(r, floating_marker_vertices, sizeof(floating_marker_vertices), GL_TRIANGLE_STRIP);
|
||||
|
||||
const int nr_types = DTX_DOOR + 1;
|
||||
r->materials = xcalloc(nr_types * dtx->nr_columns, sizeof(struct material));
|
||||
for (int type = 0; type < nr_types; type++) {
|
||||
r->materials = xcalloc(DTX_NR_CELL_TEXTURE_TYPES * dtx->nr_columns, sizeof(struct material));
|
||||
for (int type = 0; type < DTX_NR_CELL_TEXTURE_TYPES; type++) {
|
||||
for (int i = 0; i < dtx->nr_columns; i++) {
|
||||
struct cg *cg = dtx_create_cg(dtx, type, i);
|
||||
if (!cg)
|
||||
@@ -361,18 +446,26 @@ struct dungeon_renderer *dungeon_renderer_create(enum draw_dungeon_version versi
|
||||
}
|
||||
}
|
||||
r->nr_dtx_columns = dtx->nr_columns;
|
||||
r->nr_materials = nr_types * dtx->nr_columns;
|
||||
r->nr_materials = DTX_NR_CELL_TEXTURE_TYPES * dtx->nr_columns;
|
||||
|
||||
r->event_textures = event_textures;
|
||||
r->nr_event_textures = nr_event_textures;
|
||||
r->marker_table = marker_tables[version];
|
||||
r->draw_event_markers = true;
|
||||
|
||||
if (version == DRAW_DUNGEON_2 && is_dolls_boss_floor(num)) {
|
||||
// Use special marker table for Dungeons & Dolls boss floor
|
||||
r->marker_table = dolls_boss_marker_info;
|
||||
} else {
|
||||
r->marker_table = marker_tables[version];
|
||||
}
|
||||
|
||||
if (po)
|
||||
init_polyobjs(r, po);
|
||||
|
||||
r->skybox = skybox_create(dtx);
|
||||
r->skybox = skybox_create(version, dtx);
|
||||
|
||||
r->enable_lightmap = true;
|
||||
r->draw_obj_flags = (1 << NR_DRAW_OBJ_FLAGS) - 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -402,18 +495,32 @@ void dungeon_renderer_free(struct dungeon_renderer *r)
|
||||
free(r->polyobj_materials);
|
||||
|
||||
skybox_free(r->skybox);
|
||||
if (r->raster_shader.s.program)
|
||||
glDeleteProgram(r->raster_shader.s.program);
|
||||
|
||||
free(r);
|
||||
}
|
||||
|
||||
static void set_lightmap_texture(struct dungeon_renderer *r, int texture_index)
|
||||
{
|
||||
struct material *material = (r->enable_lightmap && texture_index >= 0)
|
||||
? get_material(r, DTX_LIGHTMAP, texture_index) : NULL;
|
||||
glUniform1i(r->use_lightmap, !!material);
|
||||
if (material) {
|
||||
glActiveTexture(GL_TEXTURE0 + LIGHT_TEXTURE_UNIT);
|
||||
glBindTexture(GL_TEXTURE_2D, material->texture);
|
||||
glUniform1i(r->light_texture, LIGHT_TEXTURE_UNIT);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw(struct dungeon_renderer *r, struct geometry *geometry, GLuint texture, mat4 transform)
|
||||
{
|
||||
glUseProgram(r->shader.program);
|
||||
glUniformMatrix4fv(r->local_transform, 1, GL_FALSE, transform[0]);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glActiveTexture(GL_TEXTURE0 + COLOR_TEXTURE_UNIT);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glUniform1i(r->shader.texture, 0);
|
||||
glUniform1i(r->shader.texture, COLOR_TEXTURE_UNIT);
|
||||
|
||||
glBindVertexArray(geometry->vao);
|
||||
glDrawArrays(geometry->mode, 0, geometry->nr_vertices);
|
||||
@@ -431,13 +538,13 @@ static void draw_door(struct dungeon_renderer *r, GLuint texture, float angle, m
|
||||
glm_mat4_copy(transform, m);
|
||||
m[3][0] += dx;
|
||||
m[3][2] += dz;
|
||||
glm_rotate_y(m, angle * (M_PI/180), m);
|
||||
glm_rotate_y(m, glm_rad(angle), m);
|
||||
draw(r, r->door_left_geometry, texture, m);
|
||||
|
||||
glm_mat4_copy(transform, m);
|
||||
m[3][0] -= dx;
|
||||
m[3][2] -= dz;
|
||||
glm_rotate_y(m, angle * -(M_PI/180), m);
|
||||
glm_rotate_y(m, -glm_rad(angle), m);
|
||||
draw(r, r->door_right_geometry, texture, m);
|
||||
}
|
||||
|
||||
@@ -478,7 +585,7 @@ static void draw_floating_marker(struct dungeon_renderer *r, const struct marker
|
||||
glm_mat4_pick3t(view_mat, r);
|
||||
glm_mat4_ins3(r, m);
|
||||
} else {
|
||||
float angle = t / -(M_PI * 100.0);
|
||||
float angle = t / -(GLM_PIf * 100.f);
|
||||
glm_rotate_y(m, angle, m);
|
||||
}
|
||||
|
||||
@@ -521,7 +628,7 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
float y = 2.0 * cell->y;
|
||||
float z = -2.0 * cell->z;
|
||||
|
||||
if (cell->floor >= 0) {
|
||||
if (cell->floor >= 0 && r->draw_obj_flags & (1 << DRAW_FLOOR)) {
|
||||
struct material *material = get_material(r, DTX_FLOOR, cell->floor);
|
||||
if (material && material->opaque == render_opaque) {
|
||||
mat4 m = MAT4(
|
||||
@@ -529,21 +636,37 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
0, 0, 1, y-1,
|
||||
0, -1, 0, z,
|
||||
0, 0, 0, 1);
|
||||
set_lightmap_texture(r, cell->lightmap_floor);
|
||||
draw(r, r->wall_geometry, material->texture, m);
|
||||
set_lightmap_texture(r, -1);
|
||||
}
|
||||
}
|
||||
if (cell->ceiling >= 0) {
|
||||
if (cell->ceiling >= 0 && r->draw_obj_flags & (1 << DRAW_CEILING)) {
|
||||
struct material *material = get_material(r, DTX_CEILING, cell->ceiling);
|
||||
if (material && material->opaque == render_opaque) {
|
||||
mat4 m = MAT4(
|
||||
-1, 0, 0, x,
|
||||
0, 0, -1, y+1,
|
||||
0, -1, 0, z,
|
||||
0, 0, 0, 1);
|
||||
draw(r, r->wall_geometry, material->texture, m);
|
||||
if (r->version == DRAW_FIELD) {
|
||||
// Draw as a billboard.
|
||||
mat4 m;
|
||||
vec3 pos = {x, y, z};
|
||||
glm_translate_make(m, pos);
|
||||
mat3 r_bill;
|
||||
glm_mat4_pick3t(view_transform, r_bill);
|
||||
glm_mat4_ins3(r_bill, m);
|
||||
glm_scale_uni(m, 1.7f);
|
||||
draw(r, r->wall_geometry, material->texture, m);
|
||||
} else {
|
||||
mat4 m = MAT4(
|
||||
-1, 0, 0, x,
|
||||
0, 0, -1, y+1,
|
||||
0, -1, 0, z,
|
||||
0, 0, 0, 1);
|
||||
set_lightmap_texture(r, cell->lightmap_ceiling);
|
||||
draw(r, r->wall_geometry, material->texture, m);
|
||||
set_lightmap_texture(r, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cell->north_wall >= 0) {
|
||||
if (cell->north_wall >= 0 && r->draw_obj_flags & (1 << DRAW_WALL)) {
|
||||
struct material *material = get_material(r, DTX_WALL, cell->north_wall);
|
||||
if (material && material->opaque == render_opaque) {
|
||||
mat4 m = MAT4(
|
||||
@@ -551,10 +674,12 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
0, 1, 0, y,
|
||||
0, 0, 1, z-1,
|
||||
0, 0, 0, 1);
|
||||
set_lightmap_texture(r, cell->lightmap_north);
|
||||
draw(r, r->wall_geometry, material->texture, m);
|
||||
set_lightmap_texture(r, -1);
|
||||
}
|
||||
}
|
||||
if (cell->south_wall >= 0) {
|
||||
if (cell->south_wall >= 0 && r->draw_obj_flags & (1 << DRAW_WALL)) {
|
||||
struct material *material = get_material(r, DTX_WALL, cell->south_wall);
|
||||
if (material && material->opaque == render_opaque) {
|
||||
mat4 m = MAT4(
|
||||
@@ -562,10 +687,12 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
0, 1, 0, y,
|
||||
0, 0, -1, z+1,
|
||||
0, 0, 0, 1);
|
||||
set_lightmap_texture(r, cell->lightmap_south);
|
||||
draw(r, r->wall_geometry, material->texture, m);
|
||||
set_lightmap_texture(r, -1);
|
||||
}
|
||||
}
|
||||
if (cell->east_wall >= 0) {
|
||||
if (cell->east_wall >= 0 && r->draw_obj_flags & (1 << DRAW_WALL)) {
|
||||
struct material *material = get_material(r, DTX_WALL, cell->east_wall);
|
||||
if (material && material->opaque == render_opaque) {
|
||||
mat4 m = MAT4(
|
||||
@@ -573,10 +700,12 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
0, 1, 0, y,
|
||||
1, 0, 0, z,
|
||||
0, 0, 0, 1);
|
||||
set_lightmap_texture(r, cell->lightmap_east);
|
||||
draw(r, r->wall_geometry, material->texture, m);
|
||||
set_lightmap_texture(r, -1);
|
||||
}
|
||||
}
|
||||
if (cell->west_wall >= 0) {
|
||||
if (cell->west_wall >= 0 && r->draw_obj_flags & (1 << DRAW_WALL)) {
|
||||
struct material *material = get_material(r, DTX_WALL, cell->west_wall);
|
||||
if (material && material->opaque == render_opaque) {
|
||||
mat4 m = MAT4(
|
||||
@@ -584,10 +713,12 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
0, 1, 0, y,
|
||||
-1, 0, 0, z,
|
||||
0, 0, 0, 1);
|
||||
set_lightmap_texture(r, cell->lightmap_west);
|
||||
draw(r, r->wall_geometry, material->texture, m);
|
||||
set_lightmap_texture(r, -1);
|
||||
}
|
||||
}
|
||||
if (cell->north_door >= 0) {
|
||||
if (cell->north_door >= 0 && r->draw_obj_flags & (1 << DRAW_DOOR)) {
|
||||
struct material *material = get_material(r, DTX_DOOR, cell->north_door);
|
||||
if (material && material->opaque == render_opaque) {
|
||||
mat4 m = MAT4(
|
||||
@@ -598,7 +729,7 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
draw_door(r, material->texture, cell->north_door_angle, m, -1, 0);
|
||||
}
|
||||
}
|
||||
if (cell->south_door >= 0) {
|
||||
if (cell->south_door >= 0 && r->draw_obj_flags & (1 << DRAW_DOOR)) {
|
||||
struct material *material = get_material(r, DTX_DOOR, cell->south_door);
|
||||
if (material && material->opaque == render_opaque) {
|
||||
mat4 m = MAT4(
|
||||
@@ -609,7 +740,7 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
draw_door(r, material->texture, cell->south_door_angle, m, 1, 0);
|
||||
}
|
||||
}
|
||||
if (cell->east_door >= 0) {
|
||||
if (cell->east_door >= 0 && r->draw_obj_flags & (1 << DRAW_DOOR)) {
|
||||
struct material *material = get_material(r, DTX_DOOR, cell->east_door);
|
||||
if (material && material->opaque == render_opaque) {
|
||||
mat4 m = MAT4(
|
||||
@@ -620,7 +751,7 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
draw_door(r, material->texture, cell->east_door_angle, m, 0, -1);
|
||||
}
|
||||
}
|
||||
if (cell->west_door >= 0) {
|
||||
if (cell->west_door >= 0 && r->draw_obj_flags & (1 << DRAW_DOOR)) {
|
||||
struct material *material = get_material(r, DTX_DOOR, cell->west_door);
|
||||
if (material && material->opaque == render_opaque) {
|
||||
mat4 m = MAT4(
|
||||
@@ -631,7 +762,7 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
draw_door(r, material->texture, cell->west_door_angle, m, 0, 1);
|
||||
}
|
||||
}
|
||||
if (cell->stairs_texture >= 0) {
|
||||
if (cell->stairs_texture >= 0 && r->draw_obj_flags & (1 << DRAW_STAIRS)) {
|
||||
struct material *material = get_material(r, DTX_STAIRS, cell->stairs_texture);
|
||||
if (material && material->opaque == render_opaque) {
|
||||
mat4 m;
|
||||
@@ -659,7 +790,7 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
glCullFace(GL_BACK);
|
||||
}
|
||||
}
|
||||
if (cell->polyobj_index >= 0) {
|
||||
if (cell->polyobj_index >= 0 && r->draw_obj_flags & (1 << DRAW_POLYOBJ)) {
|
||||
vec3 pos = {
|
||||
x + cell->polyobj_position_x,
|
||||
y + cell->polyobj_position_y - 1,
|
||||
@@ -677,12 +808,81 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re
|
||||
}
|
||||
}
|
||||
|
||||
void dungeon_renderer_render(struct dungeon_renderer *r, struct dgn_cell **cells, int nr_cells, mat4 view_transform, mat4 proj_transform)
|
||||
static void draw_character(struct dungeon_renderer *r, struct drawfield_character *chara, mat4 view_mat)
|
||||
{
|
||||
if (!chara->show || chara->sprite <= 0)
|
||||
return;
|
||||
struct sact_sprite *sp = sact_try_get_sprite(chara->sprite);
|
||||
if (!sp || !sp->texture.handle)
|
||||
return;
|
||||
|
||||
if (chara->cols == 0) chara->cols = 1;
|
||||
if (chara->rows == 0) chara->rows = 1;
|
||||
glUseProgram(r->shader.program);
|
||||
glUniform2f(r->uv_offset, (float)(chara->cg_index % chara->cols) / chara->cols, (float)(chara->cg_index / chara->cols) / chara->rows);
|
||||
glUniform2f(r->uv_scale, 1.0f / chara->cols, 1.0f / chara->rows);
|
||||
|
||||
mat4 m;
|
||||
glm_translate_make(m, chara->pos);
|
||||
|
||||
// Make it a billboard of 2.0 x 2.0
|
||||
mat3 r_bill;
|
||||
glm_mat4_pick3t(view_mat, r_bill);
|
||||
glm_mat4_ins3(r_bill, m);
|
||||
|
||||
draw(r, r->wall_geometry, sp->texture.handle, m);
|
||||
}
|
||||
|
||||
struct character_z {
|
||||
struct drawfield_character *chara;
|
||||
float z;
|
||||
};
|
||||
|
||||
static int compare_character_z(const void *a, const void *b)
|
||||
{
|
||||
const struct character_z *cha = a;
|
||||
const struct character_z *chb = b;
|
||||
if (cha->z < chb->z)
|
||||
return -1;
|
||||
if (cha->z > chb->z)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void draw_characters(struct dungeon_renderer *r, struct drawfield_character *characters, mat4 view_transform)
|
||||
{
|
||||
if (!characters)
|
||||
return;
|
||||
|
||||
struct character_z sorted_characters[DRAWFIELD_NR_CHARACTERS];
|
||||
int n = 0;
|
||||
for (int i = 0; i < DRAWFIELD_NR_CHARACTERS; i++) {
|
||||
struct drawfield_character *chara = characters + i;
|
||||
if (!chara->show || chara->sprite <= 0)
|
||||
continue;
|
||||
sorted_characters[n].chara = chara;
|
||||
vec4 pos;
|
||||
glm_mat4_mulv(view_transform, (vec4){chara->pos[0], chara->pos[1], chara->pos[2], 1}, pos);
|
||||
sorted_characters[n].z = pos[2];
|
||||
n++;
|
||||
}
|
||||
|
||||
qsort(sorted_characters, n, sizeof(struct character_z), compare_character_z);
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
draw_character(r, sorted_characters[i].chara, view_transform);
|
||||
|
||||
glUniform2f(r->uv_offset, 0.0f, 0.0f);
|
||||
glUniform2f(r->uv_scale, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
glUseProgram(r->shader.program);
|
||||
glUniformMatrix4fv(r->shader.view_transform, 1, GL_FALSE, view_transform[0]);
|
||||
glUniformMatrix4fv(r->proj_transform, 1, GL_FALSE, proj_transform[0]);
|
||||
glUniform1f(r->alpha_mod, 1.0);
|
||||
glUniform1i(r->use_lightmap, 0);
|
||||
|
||||
// Render opaque objects, from near to far.
|
||||
glDisable(GL_BLEND);
|
||||
@@ -691,11 +891,15 @@ void dungeon_renderer_render(struct dungeon_renderer *r, struct dgn_cell **cells
|
||||
|
||||
// Render the skybox.
|
||||
skybox_render(r->skybox, view_transform, proj_transform);
|
||||
|
||||
glUseProgram(r->shader.program);
|
||||
// Render transparent objects, from far to near.
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// DrawField: draw characters.
|
||||
draw_characters(r, characters, view_transform);
|
||||
|
||||
// Render transparent objects, from far to near.
|
||||
for (int i = nr_cells - 1; i >= 0; i--)
|
||||
draw_cell(r, cells[i], false, view_transform);
|
||||
|
||||
@@ -719,3 +923,85 @@ bool dungeon_renderer_is_floor_opaque(struct dungeon_renderer *r, struct dgn_cel
|
||||
struct material *material = get_material(r, DTX_FLOOR, cell->floor);
|
||||
return material && material->opaque;
|
||||
}
|
||||
|
||||
static void prepare_raster_shader(struct gfx_render_job *job, void *data)
|
||||
{
|
||||
struct dungeon_renderer *r = data;
|
||||
float width = job->world_transform[0];
|
||||
glUniform1f(r->raster_shader.amp, r->raster_amp / width);
|
||||
glUniform1f(r->raster_shader.t, SDL_GetTicks() / 1000.f);
|
||||
}
|
||||
|
||||
static void load_raster_shader(struct raster_shader *s)
|
||||
{
|
||||
gfx_load_shader(&s->s, "shaders/render.v.glsl", "shaders/dungeon_raster.f.glsl");
|
||||
s->amp = glGetUniformLocation(s->s.program, "amp");
|
||||
s->t = glGetUniformLocation(s->s.program, "t");
|
||||
s->s.prepare = prepare_raster_shader;
|
||||
}
|
||||
|
||||
void dungeon_renderer_run_post_processing(struct dungeon_renderer *r, struct texture *src, struct texture *dst)
|
||||
{
|
||||
// This function flips the image upside down, because the image in `src`
|
||||
// (rendered by dungeon_renderer_render) is bottom-up (the first pixel is at
|
||||
// the bottom-left), but sprite textures are top-down (the first pixel is at
|
||||
// the top-left).
|
||||
|
||||
if (r->raster_scroll) {
|
||||
mat4 mw_transform = MAT4(
|
||||
src->w, 0, 0, 0,
|
||||
0, -src->h, 0, src->h, // flip vertically
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1);
|
||||
mat4 wv_transform = WV_TRANSFORM(src->w, src->h);
|
||||
|
||||
struct gfx_render_job job = {
|
||||
.shader = &r->raster_shader.s,
|
||||
.shape = GFX_RECTANGLE,
|
||||
.texture = src->handle,
|
||||
.world_transform = mw_transform[0],
|
||||
.view_transform = wv_transform[0],
|
||||
.data = r
|
||||
};
|
||||
GLuint fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, dst, 0, 0, dst->w, dst->h);
|
||||
gfx_render(&job);
|
||||
gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo);
|
||||
} else {
|
||||
gfx_copy_reverse_UD(dst, 0, 0, src, 0, 0, src->w, src->h);
|
||||
}
|
||||
}
|
||||
|
||||
void dungeon_renderer_set_raster_scroll(struct dungeon_renderer *r, int type)
|
||||
{
|
||||
r->raster_scroll = !!type;
|
||||
if (r->raster_scroll && !r->raster_shader.s.program) {
|
||||
load_raster_shader(&r->raster_shader);
|
||||
}
|
||||
}
|
||||
|
||||
void dungeon_renderer_set_raster_amp(struct dungeon_renderer *r, float amp)
|
||||
{
|
||||
r->raster_amp = amp;
|
||||
}
|
||||
|
||||
void dungeon_renderer_enable_lightmap(struct dungeon_renderer *r, bool enable)
|
||||
{
|
||||
r->enable_lightmap = enable;
|
||||
}
|
||||
|
||||
bool dungeon_renderer_get_draw_obj_flag(struct dungeon_renderer *r, int type)
|
||||
{
|
||||
if (type < 0 || type >= NR_DRAW_OBJ_FLAGS)
|
||||
return false;
|
||||
return r->draw_obj_flags & (1 << type);
|
||||
}
|
||||
|
||||
void dungeon_renderer_set_draw_obj_flag(struct dungeon_renderer *r, int type, bool flag)
|
||||
{
|
||||
if (type < 0 || type >= NR_DRAW_OBJ_FLAGS)
|
||||
return;
|
||||
if (flag)
|
||||
r->draw_obj_flags |= (1 << type);
|
||||
else
|
||||
r->draw_obj_flags &= ~(1 << type);
|
||||
}
|
||||
|
||||
+13
-3
@@ -20,6 +20,7 @@
|
||||
#include "system4/cg.h"
|
||||
|
||||
#include "dungeon/dtx.h"
|
||||
#include "dungeon/dungeon.h"
|
||||
#include "dungeon/skybox.h"
|
||||
#include "gfx/gfx.h"
|
||||
|
||||
@@ -33,8 +34,13 @@ struct skybox {
|
||||
int nr_indices;
|
||||
};
|
||||
|
||||
struct skybox *skybox_create(struct dtx *dtx)
|
||||
struct skybox *skybox_create(enum draw_dungeon_version version, struct dtx *dtx)
|
||||
{
|
||||
if (version == DRAW_DUNGEON_2 || version == DRAW_FIELD) {
|
||||
// Skybox is not used in these games.
|
||||
return NULL;
|
||||
};
|
||||
|
||||
struct skybox *s = xcalloc(1, sizeof(struct skybox));
|
||||
gfx_load_shader(&s->shader, "shaders/dungeon_skybox.v.glsl", "shaders/dungeon_skybox.f.glsl");
|
||||
s->proj_transform = glGetUniformLocation(s->shader.program, "proj_transform");
|
||||
@@ -63,8 +69,8 @@ struct skybox *skybox_create(struct dtx *dtx)
|
||||
glGenBuffers(1, &s->attr_buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, s->attr_buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
glEnableVertexAttribArray(s->shader.vertex);
|
||||
glVertexAttribPointer(s->shader.vertex, 3, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
glEnableVertexAttribArray(s->shader.vertex_pos);
|
||||
glVertexAttribPointer(s->shader.vertex_pos, 3, GL_FLOAT, GL_FALSE, 0, 0);
|
||||
glGenBuffers(1, &s->index_buffer);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->index_buffer);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
|
||||
@@ -103,6 +109,8 @@ struct skybox *skybox_create(struct dtx *dtx)
|
||||
|
||||
void skybox_free(struct skybox *s)
|
||||
{
|
||||
if (!s)
|
||||
return;
|
||||
glDeleteVertexArrays(1, &s->vao);
|
||||
glDeleteBuffers(1, &s->attr_buffer);
|
||||
glDeleteBuffers(1, &s->index_buffer);
|
||||
@@ -113,6 +121,8 @@ void skybox_free(struct skybox *s)
|
||||
|
||||
void skybox_render(struct skybox *s, const mat4 view_transform, const mat4 proj_transform)
|
||||
{
|
||||
if (!s)
|
||||
return;
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glUseProgram(s->shader.program);
|
||||
|
||||
|
||||
+95
-79
@@ -19,7 +19,6 @@
|
||||
#include <SDL.h>
|
||||
#include "effect.h"
|
||||
#include "gfx/gfx.h"
|
||||
#include "sact.h"
|
||||
#include "system4.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
@@ -76,6 +75,8 @@ const char *effect_names[NR_EFFECTS] = {
|
||||
[EFFECT_BLUR_FADEOUT] = "EFFECT_BLUR_FADEOUT",
|
||||
[EFFECT_BLUR_CROSSFADE] = "EFFECT_BLUR_CROSSFADE",
|
||||
[EFFECT_2ROT_ZOOM_BLEND_BLUR] = "EFFECT_2ROT_ZOOM_BLEND_BLUR",
|
||||
[EFFECT_VWAVE_SCROLL_CROSSFADE] = "EFFECT_VWAVE_SCROLL_CROSSFADE",
|
||||
[EFFECT_VWAVE_CROSSFADE] = "EFFECT_VWAVE_CROSSFADE",
|
||||
};
|
||||
|
||||
struct effect_shader {
|
||||
@@ -92,6 +93,13 @@ struct effect_data {
|
||||
float progress;
|
||||
};
|
||||
|
||||
static struct {
|
||||
bool on;
|
||||
int type;
|
||||
Texture old;
|
||||
Texture view;
|
||||
} effect = {0};
|
||||
|
||||
static void prepare_effect_shader(struct gfx_render_job *job, void *data)
|
||||
{
|
||||
struct effect_shader *s = (struct effect_shader*)job->shader;
|
||||
@@ -115,24 +123,30 @@ static void load_effect_shader(struct effect_shader *s)
|
||||
s->s.prepare = prepare_effect_shader;
|
||||
}
|
||||
|
||||
static void render_effect_shader(struct effect_shader *shader, Texture *old, Texture *new, float progress)
|
||||
static void render_effect_shader(struct effect_shader *shader, Texture *dst, Texture *old, Texture *new, float progress)
|
||||
{
|
||||
struct effect_data data = { .old = old, .new = new, .progress = progress };
|
||||
mat4 world_transform = WORLD_TRANSFORM(new->w, new->h, 0, 0);
|
||||
mat4 wv_transform = WV_TRANSFORM(config.view_width, config.view_height);
|
||||
struct gfx_render_job job = {
|
||||
.shader = &shader->s,
|
||||
.shape = GFX_RECTANGLE,
|
||||
.texture = new->handle,
|
||||
.world_transform = new->world_transform[0],
|
||||
.world_transform = world_transform[0],
|
||||
.view_transform = wv_transform[0],
|
||||
.data = &data
|
||||
};
|
||||
GLuint fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, dst, 0, 0,
|
||||
dst->w, dst->h);
|
||||
gfx_render(&job);
|
||||
gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo);
|
||||
}
|
||||
|
||||
#define EFFECT_SHADER(path) { .s = { .prepare = prepare_effect_shader }, .f_path = path}
|
||||
static struct effect_shader crossfade_shader = EFFECT_SHADER("shaders/effects/crossfade.f.glsl");
|
||||
static struct effect_shader crossfade_lr_shader = EFFECT_SHADER("shaders/effects/crossfade_lr.f.glsl");
|
||||
static struct effect_shader crossfade_up_down_shader = EFFECT_SHADER("shaders/effects/crossfade_up_down.f.glsl");
|
||||
static struct effect_shader crossfade_down_up_shader = EFFECT_SHADER("shaders/effects/crossfade_down_up.f.glsl");
|
||||
static struct effect_shader mosaic_shader = EFFECT_SHADER("shaders/effects/mosaic.f.glsl");
|
||||
static struct effect_shader blind_down_shader = EFFECT_SHADER("shaders/effects/blind_down.f.glsl");
|
||||
static struct effect_shader blind_lr_shader = EFFECT_SHADER("shaders/effects/blind_lr.f.glsl");
|
||||
@@ -143,13 +157,14 @@ static struct effect_shader turn_page_shader = EFFECT_SHADER("shaders/effects/tu
|
||||
static struct effect_shader sepia_noise_crossfade_shader = EFFECT_SHADER("shaders/effects/sepia_noise_crossfade.f.glsl");
|
||||
static struct effect_shader blur_fadeout_shader = EFFECT_SHADER("shaders/effects/blur_fadeout.f.glsl");
|
||||
static struct effect_shader blur_crossfade_shader = EFFECT_SHADER("shaders/effects/blur_crossfade.f.glsl");
|
||||
|
||||
extern GLuint main_surface_fb;
|
||||
static struct effect_shader vwave_scroll_crossfade_shader = EFFECT_SHADER("shaders/effects/vwave_scroll_crossfade.f.glsl");
|
||||
static struct effect_shader vwave_crossfade_shader = EFFECT_SHADER("shaders/effects/vwave_crossfade.f.glsl");
|
||||
|
||||
static struct effect_shader *effect_shaders[NR_EFFECTS] = {
|
||||
[EFFECT_CROSSFADE] = &crossfade_shader,
|
||||
[EFFECT_CROSSFADE_LR] = &crossfade_lr_shader,
|
||||
[EFFECT_UP_DOWN_CROSSFADE] = &crossfade_up_down_shader,
|
||||
[EFFECT_DOWN_UP_CROSSFADE] = &crossfade_down_up_shader,
|
||||
[EFFECT_CROSSFADE_MOSAIC] = &mosaic_shader,
|
||||
[EFFECT_BLIND_DOWN] = &blind_down_shader,
|
||||
[EFFECT_BLIND_LR] = &blind_lr_shader,
|
||||
@@ -160,70 +175,64 @@ static struct effect_shader *effect_shaders[NR_EFFECTS] = {
|
||||
[EFFECT_SEPIA_NOISE_CROSSFADE] = &sepia_noise_crossfade_shader,
|
||||
[EFFECT_BLUR_FADEOUT] = &blur_fadeout_shader,
|
||||
[EFFECT_BLUR_CROSSFADE] = &blur_crossfade_shader,
|
||||
[EFFECT_VWAVE_SCROLL_CROSSFADE] = &vwave_scroll_crossfade_shader,
|
||||
[EFFECT_VWAVE_CROSSFADE] = &vwave_crossfade_shader,
|
||||
};
|
||||
|
||||
static struct {
|
||||
bool on;
|
||||
int type;
|
||||
Texture old;
|
||||
Texture new;
|
||||
} effect = {0};
|
||||
|
||||
static void effect_fadeout(float rate)
|
||||
static void effect_fadeout(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
gfx_copy_bright(gfx_main_surface(), 0, 0, &effect.old, 0, 0,
|
||||
effect.old.w, effect.old.h, (1.0f - rate) * 255);
|
||||
gfx_copy_bright(dst, 0, 0, old, 0, 0, old->w, old->h, (1.0f - rate) * 255);
|
||||
}
|
||||
|
||||
static void effect_fadein(float rate)
|
||||
static void effect_fadein(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
gfx_copy_bright(gfx_main_surface(), 0, 0, &effect.new, 0, 0,
|
||||
effect.new.w, effect.new.h, rate * 255);
|
||||
gfx_copy_bright(dst, 0, 0, new, 0, 0, new->w, new->h, rate * 255);
|
||||
}
|
||||
|
||||
static void effect_whiteout(float rate)
|
||||
static void effect_whiteout(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
Texture *dst = gfx_main_surface();
|
||||
gfx_fill(dst, 0, 0, dst->w, dst->h, 255, 255, 255);
|
||||
gfx_blend(dst, 0, 0, &effect.old, 0, 0, dst->w, dst->h, (1.0f - rate) * 255);
|
||||
gfx_blend(dst, 0, 0, old, 0, 0, dst->w, dst->h, (1.0f - rate) * 255);
|
||||
}
|
||||
|
||||
static void effect_whitein(float rate)
|
||||
static void effect_whitein(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
Texture *dst = gfx_main_surface();
|
||||
gfx_fill(dst, 0, 0, dst->w, dst->h, 255, 255, 255);
|
||||
gfx_blend(dst, 0, 0, &effect.new, 0, 0, dst->w, dst->h, rate * 255);
|
||||
gfx_blend(dst, 0, 0, new, 0, 0, dst->w, dst->h, rate * 255);
|
||||
}
|
||||
|
||||
static void effect_zoom_lr(float rate)
|
||||
static void effect_rotate_out(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
gfx_fill(dst, 0, 0, dst->w, dst->h, 0, 0, 0);
|
||||
gfx_copy_rot_zoom(dst, old, 0, 0, old->w, old->h, rate * 360, 1.f - rate);
|
||||
}
|
||||
|
||||
static void effect_zoom_lr(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
Texture *dst = gfx_main_surface();
|
||||
unsigned x_pos = roundf(dst->w * rate);
|
||||
gfx_copy_stretch(dst, x_pos, 0, dst->w-x_pos, dst->h, &effect.old, 0, 0, effect.old.w, effect.old.h);
|
||||
gfx_copy_stretch(dst, 0, 0, x_pos, dst->h, &effect.new, 0, 0, effect.new.w, effect.new.h);
|
||||
gfx_copy_stretch(dst, x_pos, 0, dst->w-x_pos, dst->h, old, 0, 0, old->w, old->h);
|
||||
gfx_copy_stretch(dst, 0, 0, x_pos, dst->h, new, 0, 0, new->w, new->h);
|
||||
}
|
||||
|
||||
static void effect_zoom_rl(float rate)
|
||||
static void effect_zoom_rl(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
Texture *dst = gfx_main_surface();
|
||||
unsigned x_pos = dst->w - roundf(dst->w * rate);
|
||||
gfx_copy_stretch(dst, x_pos, 0, dst->w-x_pos, dst->h, &effect.new, 0, 0, effect.new.w, effect.new.h);
|
||||
gfx_copy_stretch(dst, 0, 0, x_pos, dst->h, &effect.old, 0, 0, effect.old.w, effect.old.h);
|
||||
gfx_copy_stretch(dst, x_pos, 0, dst->w-x_pos, dst->h, new, 0, 0, new->w, new->h);
|
||||
gfx_copy_stretch(dst, 0, 0, x_pos, dst->h, old, 0, 0, old->w, old->h);
|
||||
}
|
||||
|
||||
static void effect_oscillate(float rate)
|
||||
static void effect_oscillate(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
Texture *dst = gfx_main_surface();
|
||||
int delta_x = (rand() % (dst->w / 10) - (dst->w / 20)) * (1.0f - rate);
|
||||
int delta_y = (rand() % (dst->h / 10) - (dst->h / 20)) * (1.0f - rate);;
|
||||
|
||||
gfx_copy(dst, 0, 0, &effect.old, 0, 0, dst->w, dst->h);
|
||||
gfx_copy(dst, delta_x, delta_y, &effect.new, 0, 0, dst->w, dst->h);
|
||||
gfx_copy(dst, 0, 0, old, 0, 0, dst->w, dst->h);
|
||||
gfx_copy(dst, delta_x, delta_y, new, 0, 0, dst->w, dst->h);
|
||||
}
|
||||
|
||||
static void effect_tv_switch(float rate, Texture *src)
|
||||
static void effect_tv_switch(Texture *dst, Texture *src, float rate)
|
||||
{
|
||||
Texture *dst = gfx_main_surface();
|
||||
gfx_fill(dst, 0, 0, dst->w, dst->h, 0, 0, 0);
|
||||
if (rate < 0.5f) {
|
||||
int h = max(1, (int)(dst->h * (1.0f - rate * 2.0f)));
|
||||
gfx_copy_stretch(dst, 0, dst->h * rate, dst->w, h,
|
||||
@@ -236,19 +245,18 @@ static void effect_tv_switch(float rate, Texture *src)
|
||||
}
|
||||
}
|
||||
|
||||
static void effect_tv_switch_off(float rate)
|
||||
static void effect_tv_switch_off(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
effect_tv_switch(rate, &effect.old);
|
||||
effect_tv_switch(dst, old, rate);
|
||||
}
|
||||
|
||||
static void effect_tv_switch_on(float rate)
|
||||
static void effect_tv_switch_on(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
effect_tv_switch(1.0f - rate, &effect.new);
|
||||
effect_tv_switch(dst, new, 1.0f - rate);
|
||||
}
|
||||
|
||||
static void effect_zoom_in_crossfade(float rate)
|
||||
static void effect_zoom_in_crossfade(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
Texture *dst = gfx_main_surface();
|
||||
float scale = 1.0f + powf(rate, 4.0f) * 16.0f;
|
||||
int w = roundf(dst->w * scale);
|
||||
int h = roundf(dst->h * scale);
|
||||
@@ -256,16 +264,17 @@ static void effect_zoom_in_crossfade(float rate)
|
||||
int y = -((h - dst->h)/2);
|
||||
int a = (1.0f - rate) * 255;
|
||||
|
||||
gfx_copy(dst, 0, 0, &effect.new, 0, 0, dst->w, dst->h);
|
||||
gfx_copy_stretch_blend(dst, x, y, w, h, &effect.old, 0, 0, dst->w, dst->h, a);
|
||||
gfx_copy(dst, 0, 0, new, 0, 0, dst->w, dst->h);
|
||||
gfx_copy_stretch_blend(dst, x, y, w, h, old, 0, 0, dst->w, dst->h, a);
|
||||
}
|
||||
|
||||
typedef void (*effect_fun)(float rate);
|
||||
typedef void (*effect_fun)(Texture *dst, Texture *old, Texture *new, float rate);
|
||||
static effect_fun effect_functions[NR_EFFECTS] = {
|
||||
[EFFECT_FADEOUT] = effect_fadeout,
|
||||
[EFFECT_FADEIN] = effect_fadein,
|
||||
[EFFECT_WHITEOUT] = effect_whiteout,
|
||||
[EFFECT_WHITEIN] = effect_whitein,
|
||||
[EFFECT_ROTATE_OUT] = effect_rotate_out,
|
||||
[EFFECT_OSCILLATE] = effect_oscillate,
|
||||
[EFFECT_TV_SWITCH_OFF] = effect_tv_switch_off,
|
||||
[EFFECT_TV_SWITCH_ON] = effect_tv_switch_on,
|
||||
@@ -274,7 +283,26 @@ static effect_fun effect_functions[NR_EFFECTS] = {
|
||||
[EFFECT_ZOOM_IN_CROSSFADE] = effect_zoom_in_crossfade,
|
||||
};
|
||||
|
||||
int sact_TRANS_Begin(int type)
|
||||
void effect_update_texture(int type, Texture *dst, Texture *old, Texture *new, float rate)
|
||||
{
|
||||
if (effect_functions[type]) {
|
||||
effect_functions[type](dst, old, new, rate);
|
||||
} else {
|
||||
struct effect_shader *s = effect_shaders[type];
|
||||
if (!s) {
|
||||
if (effect_names[type])
|
||||
WARNING("Unimplemented effect: %s", effect_names[type]);
|
||||
else
|
||||
WARNING("Unimplemented effect: %d", type);
|
||||
s = effect_shaders[EFFECT_BLUR_CROSSFADE];
|
||||
}
|
||||
if (!s->s.program)
|
||||
load_effect_shader(s);
|
||||
render_effect_shader(s, dst, old, new, rate);
|
||||
}
|
||||
}
|
||||
|
||||
int effect_init(enum effect type)
|
||||
{
|
||||
if (type <= 0 || type >= NR_EFFECTS) {
|
||||
WARNING("Invalid or unknown effect: %d", type);
|
||||
@@ -287,7 +315,10 @@ int sact_TRANS_Begin(int type)
|
||||
else {
|
||||
struct effect_shader *s = effect_shaders[type];
|
||||
if (!s) {
|
||||
WARNING("Unimplemented effect: %s", effect_names[type]);
|
||||
if (effect_names[type])
|
||||
WARNING("Unimplemented effect: %s", effect_names[type]);
|
||||
else
|
||||
WARNING("Unimplemented effect: %d", type);
|
||||
type = EFFECT_BLUR_CROSSFADE;
|
||||
s = effect_shaders[type];
|
||||
}
|
||||
@@ -299,53 +330,38 @@ int sact_TRANS_Begin(int type)
|
||||
|
||||
effect.on = true;
|
||||
effect.type = type;
|
||||
gfx_delete_texture(&effect.old);
|
||||
gfx_delete_texture(&effect.view);
|
||||
gfx_copy_main_surface(&effect.old);
|
||||
scene_render();
|
||||
gfx_copy_main_surface(&effect.new);
|
||||
gfx_copy_main_surface(&effect.view);
|
||||
gfx_set_view(&effect.view);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sact_TRANS_Update(float rate)
|
||||
int effect_update(float rate)
|
||||
{
|
||||
if (!effect.on) {
|
||||
return 0;
|
||||
}
|
||||
gfx_clear();
|
||||
|
||||
if (effect_functions[effect.type]) {
|
||||
effect_functions[effect.type](rate);
|
||||
effect_functions[effect.type](&effect.view, &effect.old, gfx_main_surface(), rate);
|
||||
} else {
|
||||
render_effect_shader(effect_shaders[effect.type], &effect.old, &effect.new, rate);
|
||||
Texture new;
|
||||
gfx_copy_main_surface(&new);
|
||||
render_effect_shader(effect_shaders[effect.type], &effect.view, &effect.old, &new, rate);
|
||||
gfx_delete_texture(&new);
|
||||
}
|
||||
|
||||
gfx_swap();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sact_TRANS_End(void)
|
||||
int effect_fini(void)
|
||||
{
|
||||
effect.on = false;
|
||||
gfx_reset_view();
|
||||
gfx_delete_texture(&effect.old);
|
||||
gfx_delete_texture(&effect.new);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sact_Effect(int type, int time, possibly_unused int key)
|
||||
{
|
||||
uint32_t start = SDL_GetTicks();
|
||||
if (!sact_TRANS_Begin(type))
|
||||
return 0;
|
||||
|
||||
uint32_t t = SDL_GetTicks() - start;
|
||||
while (t < time) {
|
||||
sact_TRANS_Update((float)t / (float)time);
|
||||
uint32_t t2 = SDL_GetTicks() - start;
|
||||
if (t2 < t + 16) {
|
||||
SDL_Delay(t + 16 - t2);
|
||||
t += 16;
|
||||
} else {
|
||||
t = t2;
|
||||
}
|
||||
}
|
||||
|
||||
sact_TRANS_End();
|
||||
gfx_delete_texture(&effect.view);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user