mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-09-22 23:18:01 +03:00
Compare commits
514
Commits
alpha-5
...
04333ad689
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04333ad689 | ||
|
|
960c97ecc9 | ||
|
|
e61f4b3c42 | ||
|
|
03f9e7e87e | ||
|
|
87e4bebb12 | ||
|
|
3a0dc49340 | ||
|
|
e93cddad76 | ||
|
|
461eca1a85 | ||
|
|
6ea8af95ba | ||
|
|
167d75d092 | ||
|
|
275d71712e | ||
|
|
d68baa74a0 | ||
|
|
267f7bb762 | ||
|
|
addc26ca45 | ||
|
|
bc745035e1 | ||
|
|
11ad7a583a | ||
|
|
9654950ab4 | ||
|
|
021187752b | ||
|
|
5cc163ab16 | ||
|
|
e14b4bca79 | ||
|
|
02f29b6c6d | ||
|
|
fa09b0059f | ||
|
|
4233c7d294 | ||
|
|
b30a107fda | ||
|
|
bf3430dbad | ||
|
|
135cae216f | ||
|
|
05820dc536 | ||
|
|
e76762d11d | ||
|
|
5fb3c49f75 | ||
|
|
5d032d739e | ||
|
|
e3bf653165 | ||
|
|
a19c200870 | ||
|
|
5cbc697f23 | ||
|
|
4e20a499d4 | ||
|
|
0d4eb2a2a7 | ||
|
|
d800e12f59 | ||
|
|
b1f8948b74 | ||
|
|
ebc319994e | ||
|
|
9bb871d9fd | ||
|
|
62d38aaf59 | ||
|
|
5bb36c7524 | ||
|
|
4dc9792632 | ||
|
|
a7e973c0a9 | ||
|
|
33f1295237 | ||
|
|
e699fb0aa2 | ||
|
|
4e0dd82776 | ||
|
|
77d9e77053 | ||
|
|
d65a1e6d9b | ||
|
|
68e8257c27 | ||
|
|
8e339d1537 | ||
|
|
a2524bafd5 | ||
|
|
f146320e36 | ||
|
|
09a0e3329a | ||
|
|
b68e1622fd | ||
|
|
433e3be3e6 | ||
|
|
d5b27a0a47 | ||
|
|
a38c55602d | ||
|
|
b3bcd79bae | ||
|
|
f0c4db718d | ||
|
|
d17fc30731 | ||
|
|
ca2254360c | ||
|
|
81f9113906 | ||
|
|
d63e71e85d | ||
|
|
6d05546c2c | ||
|
|
e260b82828 | ||
|
|
c0cdf4f070 | ||
|
|
74461f5b9c | ||
|
|
d8479ad80a | ||
|
|
e8bd5ab036 | ||
|
|
05fee4ba83 | ||
|
|
bf3b3416fe | ||
|
|
fb3d0bbd3e | ||
|
|
507b65fc9c | ||
|
|
24df15b9e9 | ||
|
|
fab789df65 | ||
|
|
3012faa6ff | ||
|
|
6dbb5baf19 | ||
|
|
db8279ebc3 | ||
|
|
250bc82818 | ||
|
|
4843e4e87a | ||
|
|
1051294247 | ||
|
|
fa98a76c42 | ||
|
|
d073875d94 | ||
|
|
2b67a9e000 | ||
|
|
b62edb7188 | ||
|
|
85547ef4f4 | ||
|
|
f108b7f436 | ||
|
|
90c3ab5008 | ||
|
|
7b9565fe25 | ||
|
|
4dcc50d6ca | ||
|
|
5c47ad5aae | ||
|
|
062713f70e | ||
|
|
0e8bd36407 | ||
|
|
28554137cc | ||
|
|
32024bc805 | ||
|
|
ec9bd95dc9 | ||
|
|
8dee7d8658 | ||
|
|
0f73ded483 | ||
|
|
847987ec1b | ||
|
|
ec4b608877 | ||
|
|
12735d5c8c | ||
|
|
22d58d977a | ||
|
|
20e0d00661 | ||
|
|
a706a41dcb | ||
|
|
bf0b2bb44f | ||
|
|
916daec6b2 | ||
|
|
1fcccb766c | ||
|
|
aa112849de | ||
|
|
8ca1a31ccc | ||
|
|
2c622bb331 | ||
|
|
c01a27583c | ||
|
|
ced4daa331 | ||
|
|
9eee82c13b | ||
|
|
f53ec98d53 | ||
|
|
32f4253948 | ||
|
|
cb0b969492 | ||
|
|
0030e28471 | ||
|
|
224db2f9cc | ||
|
|
33d07c190e | ||
|
|
a4dd6a42df | ||
|
|
da268288c3 | ||
|
|
f535223988 | ||
|
|
f9087eb7ac | ||
|
|
636743478c | ||
|
|
a2f087b7f9 | ||
|
|
07bd9c7438 | ||
|
|
028c907a12 | ||
|
|
c45bb3a736 | ||
|
|
484f383826 | ||
|
|
af12e19859 | ||
|
|
e83cb1c91e | ||
|
|
4c4a62e1ed | ||
|
|
f01e41be12 | ||
|
|
6bb00a268c | ||
|
|
9ed1f52627 | ||
|
|
59789c0a8a | ||
|
|
13d0c51351 | ||
|
|
46bdbb05ef | ||
|
|
234b04b460 | ||
|
|
ae9544a1ac | ||
|
|
6509dca693 | ||
|
|
bf455de35d | ||
|
|
511f6a4bfa | ||
|
|
4239345289 | ||
|
|
955950198e | ||
|
|
63ab438391 | ||
|
|
1dd37559bb | ||
|
|
f52f4bd1f0 | ||
|
|
1b87d3f9a5 | ||
|
|
15f70f29a0 | ||
|
|
df6998c0f9 | ||
|
|
2f436ce5bf | ||
|
|
c4f89d040a | ||
|
|
93c790dcc6 | ||
|
|
88f199a8f2 | ||
|
|
28c8c277c9 | ||
|
|
e5087f0757 | ||
|
|
6dc62bffc8 | ||
|
|
0ea5d90797 | ||
|
|
d20f52cf79 | ||
|
|
dcbd149409 | ||
|
|
1655c8a8e0 | ||
|
|
2a99c75e4d | ||
|
|
70336546ed | ||
|
|
cb24db29c2 | ||
|
|
03ef51e76a | ||
|
|
a0d78a024c | ||
|
|
dc105aeb16 | ||
|
|
c877e261c5 | ||
|
|
39c0094ce8 | ||
|
|
93ca13a50b | ||
|
|
c540eb9796 | ||
|
|
753e202cea | ||
|
|
f884a58a77 | ||
|
|
2bc9a3d2cb | ||
|
|
1e29930f4c | ||
|
|
887eebf31d | ||
|
|
96d46069ce | ||
|
|
9192bc80c5 | ||
|
|
003ab16c72 | ||
|
|
03062d5d61 | ||
|
|
0bf5aaa8c8 | ||
|
|
fe952354c5 | ||
|
|
f43ca3a31a | ||
|
|
52f2948e19 | ||
|
|
92b2dd1990 | ||
|
|
f8ce39a324 | ||
|
|
9562e0d1c3 | ||
|
|
623b3d3407 | ||
|
|
39886c30bd | ||
|
|
1ad496c28d | ||
|
|
039f1c9a2e | ||
|
|
caa593d00b | ||
|
|
38610d93bb | ||
|
|
65fa898d9f | ||
|
|
547e8158a2 | ||
|
|
197b89620c | ||
|
|
ecc04bc9bb | ||
|
|
65ff73ca06 | ||
|
|
2ad0de6663 | ||
|
|
5f318e859e | ||
|
|
efddc4f128 | ||
|
|
d93bb91d72 | ||
|
|
49ee259d2c | ||
|
|
3c362e9db6 | ||
|
|
1ad7cbb2c5 | ||
|
|
a476b1d892 | ||
|
|
d4121032db | ||
|
|
9474e321e1 | ||
|
|
d130f38e74 | ||
|
|
e66fe928e5 | ||
|
|
134168d864 | ||
|
|
6e755b21ef | ||
|
|
a5d488564a | ||
|
|
205e0a3ed0 | ||
|
|
b758a04760 | ||
|
|
9c962e3467 | ||
|
|
02129edc10 | ||
|
|
5ac79311b9 | ||
|
|
75cfc9f18d | ||
|
|
eaa7f3d8d0 | ||
|
|
0d7ef9b577 | ||
|
|
4a7fc4d12a | ||
|
|
e662955a1d | ||
|
|
a3c23387f4 | ||
|
|
f23125cb77 | ||
|
|
30ac11e74b | ||
|
|
f40132497c | ||
|
|
7540712de1 | ||
|
|
6a75bc5a6b | ||
|
|
a1a809faef | ||
|
|
3929de81f1 | ||
|
|
65ccb29423 | ||
|
|
9f3af7b256 | ||
|
|
249bc06f53 | ||
|
|
621eb49be8 | ||
|
|
5f13b3e9d0 | ||
|
|
15cfc5463b | ||
|
|
ed2239fb1b | ||
|
|
5980e49a96 | ||
|
|
a6871623f2 | ||
|
|
477bbdb949 | ||
|
|
0baa493f79 | ||
|
|
2c51bec001 | ||
|
|
d6b5bde80c | ||
|
|
2510fa6b9b | ||
|
|
8fecf72281 | ||
|
|
93ce40bcdd | ||
|
|
4f2d367b19 | ||
|
|
74bdbd4f37 | ||
|
|
2c8fc425db | ||
|
|
f59158648f | ||
|
|
484b643f02 | ||
|
|
d1fdab4cde | ||
|
|
43c6a2804c | ||
|
|
180c81e5f5 | ||
|
|
e29739f0be | ||
|
|
1e682bad09 | ||
|
|
0a4981533b | ||
|
|
998ab23dab | ||
|
|
dc8342cbab | ||
|
|
8d9eb931f6 | ||
|
|
1621378ba1 | ||
|
|
d3cb6b4001 | ||
|
|
2198bf5970 | ||
|
|
0a152f0e34 | ||
|
|
2a3b636bb0 | ||
|
|
1506d085db | ||
|
|
729957b842 | ||
|
|
6f661fb370 | ||
|
|
57a73cd924 | ||
|
|
6424efa6ab | ||
|
|
dc4a6f1dfa | ||
|
|
b53c511b4a | ||
|
|
0c75f21985 | ||
|
|
b4c3818d6b | ||
|
|
fe61704b90 | ||
|
|
199c91dd6c | ||
|
|
828971ce19 | ||
|
|
e1b1208438 | ||
|
|
b42e59c494 | ||
|
|
0d689ac1c2 | ||
|
|
aba2a53afe | ||
|
|
30d599f161 | ||
|
|
7817592728 | ||
|
|
85244a304a | ||
|
|
f2d26c2a46 | ||
|
|
d48f637744 | ||
|
|
008d43be1b | ||
|
|
05b78e6dc6 | ||
|
|
b28624203a | ||
|
|
e943ef8ea9 | ||
|
|
d547919713 | ||
|
|
ddf6ac5088 | ||
|
|
037616f38d | ||
|
|
af70ee09de | ||
|
|
8d67c87f6f | ||
|
|
94c8329d7a | ||
|
|
2c771acfe6 | ||
|
|
eef99c0910 | ||
|
|
44c73f644f | ||
|
|
6cada80d34 | ||
|
|
96a560f6a2 | ||
|
|
e13583a75e | ||
|
|
f0e25e53d7 | ||
|
|
eb0c642ab1 | ||
|
|
acb4d92705 | ||
|
|
b6c523f4f3 | ||
|
|
6b9edcdd69 | ||
|
|
538480b348 | ||
|
|
d9be9a7ad3 | ||
|
|
554f742a00 | ||
|
|
eda8ad35f3 | ||
|
|
59a66a8a5a | ||
|
|
02eab7149c | ||
|
|
1042b328a1 | ||
|
|
f3484643a6 | ||
|
|
46469e8732 | ||
|
|
62ec0be41a | ||
|
|
0972c60558 | ||
|
|
ba2805b09f | ||
|
|
20711ffa57 | ||
|
|
7adbf9d6e6 | ||
|
|
2dcc9b2aa9 | ||
|
|
441419d0f9 | ||
|
|
a32e3b9401 | ||
|
|
d579374a9c | ||
|
|
f0a54b337d | ||
|
|
80f4246095 | ||
|
|
b71b61c1d6 | ||
|
|
8c791db5d4 | ||
|
|
d404324e7b | ||
|
|
aeb5dbb08c | ||
|
|
9db75ab870 | ||
|
|
67387e88d0 | ||
|
|
d57c8cc9f4 | ||
|
|
677f20628c | ||
|
|
e68c41a0b9 | ||
|
|
fbd105b697 | ||
|
|
3c7634522a | ||
|
|
5ed05ba685 | ||
|
|
11fe6f6dda | ||
|
|
3057c2a96b | ||
|
|
5041d817d1 | ||
|
|
cb4e44a551 | ||
|
|
74ae7223c3 | ||
|
|
7dc6760928 | ||
|
|
e1ca00a27b | ||
|
|
7989ffc542 | ||
|
|
6e14285038 | ||
|
|
55a214553a | ||
|
|
bf9900d8aa | ||
|
|
e00ba69546 | ||
|
|
b88abe375a | ||
|
|
c86977ad1b | ||
|
|
ffb9fbae31 | ||
|
|
3ef7ce926b | ||
|
|
bf6dcef2a8 | ||
|
|
8a62ec6dc5 | ||
|
|
152af8685f | ||
|
|
c5d6afdd08 | ||
|
|
9d6861b10b | ||
|
|
c7ba379173 | ||
|
|
f4d4e8f90a | ||
|
|
379bbfeb07 | ||
|
|
117c72cf09 | ||
|
|
3577079a04 | ||
|
|
8d120b8a4d | ||
|
|
e2440a5a44 | ||
|
|
bba3f96a1b | ||
|
|
f4f7adb618 | ||
|
|
8730475f1c | ||
|
|
a84d78ac5d | ||
|
|
92322c27f0 | ||
|
|
50cb0f8063 | ||
|
|
13e76f67b3 | ||
|
|
37f1bc2723 | ||
|
|
0d7830f24c | ||
|
|
c70e5e028e | ||
|
|
a4b4e7f5bb | ||
|
|
9ccc4ab0c7 | ||
|
|
13a00c131d | ||
|
|
929a2dd3e7 | ||
|
|
b5b92bc367 | ||
|
|
e5fadf2b73 | ||
|
|
2713290ac9 | ||
|
|
3bfb92e1fb | ||
|
|
378450c625 | ||
|
|
919b86916c | ||
|
|
6fbd147c54 | ||
|
|
bcae91003a | ||
|
|
7e584c5fd2 | ||
|
|
b0c7aad6c3 | ||
|
|
5d0819a115 | ||
|
|
724bbcdfdd | ||
|
|
dc0daf3329 | ||
|
|
5d271deec0 | ||
|
|
2ede7e6bb1 | ||
|
|
e6d069b757 | ||
|
|
d6abcc2f92 | ||
|
|
de0646e430 | ||
|
|
c04fd2280d | ||
|
|
71c58e85ae | ||
|
|
61650308b7 | ||
|
|
bb26326929 | ||
|
|
f50c9f6d77 | ||
|
|
ea081e0eb7 | ||
|
|
e8ecad598a | ||
|
|
cb551e8f06 | ||
|
|
002bca4e24 | ||
|
|
0bbd9171b5 | ||
|
|
7dfc9926be | ||
|
|
1dd10ecc45 | ||
|
|
ba11900350 | ||
|
|
508839ed4c | ||
|
|
55f826db36 | ||
|
|
882354c692 | ||
|
|
071d7d2137 | ||
|
|
eddac54fa0 | ||
|
|
2e1059cbac | ||
|
|
f9177b89b1 | ||
|
|
7bae2cdf8a | ||
|
|
769bdca725 | ||
|
|
6517303e2a | ||
|
|
0e388c74c0 | ||
|
|
7ff5ec89e7 | ||
|
|
33403cbd3d | ||
|
|
40fe2068e7 | ||
|
|
40914a7cef | ||
|
|
0946900e02 | ||
|
|
56d9b607b2 | ||
|
|
155c801804 | ||
|
|
4942e41fb8 | ||
|
|
ac8a37f67b | ||
|
|
331563a165 | ||
|
|
c30510e688 | ||
|
|
81c0c494b0 | ||
|
|
e9bec3c2fe | ||
|
|
298e9bddf5 | ||
|
|
bf3c4feb9d | ||
|
|
6a36fc8f53 | ||
|
|
45876a9921 | ||
|
|
2461c1e52d | ||
|
|
41c7830558 | ||
|
|
009bafae40 | ||
|
|
e891e74bcf | ||
|
|
5576f14a0f | ||
|
|
90075f2e25 | ||
|
|
c0cc3f729b | ||
|
|
4d303b291d | ||
|
|
e73abc3cd6 | ||
|
|
579c158477 | ||
|
|
842bac92f3 | ||
|
|
dc8a9379d0 | ||
|
|
f8f02a1c99 | ||
|
|
d7d9bb70f2 | ||
|
|
dcdb82e2a8 | ||
|
|
f1a2904262 | ||
|
|
ecd71896b3 | ||
|
|
d7b7879747 | ||
|
|
1cbf6125ce | ||
|
|
9b03741288 | ||
|
|
4e04db0045 | ||
|
|
abd3a276a3 | ||
|
|
9ca3d3e9c6 | ||
|
|
d8ad22b638 | ||
|
|
3f02a22a8d | ||
|
|
bff491bbe9 | ||
|
|
f566539e4f | ||
|
|
c04aa8a844 | ||
|
|
87b1e2abe2 | ||
|
|
a0a68a022c | ||
|
|
3fb2dc3650 | ||
|
|
c1ce2c88c6 | ||
|
|
222d2700a9 | ||
|
|
dd18d68be5 | ||
|
|
db7c3a608c | ||
|
|
899c6c02a1 | ||
|
|
8ff5ffe30b | ||
|
|
ff9175c1fd | ||
|
|
74666c9b90 | ||
|
|
68bd33e099 | ||
|
|
84ab9a41da | ||
|
|
22a06b1da9 | ||
|
|
128b2fd6d5 | ||
|
|
09dc9470ae | ||
|
|
0f5a39c292 | ||
|
|
73f7acde40 | ||
|
|
3afe4e4453 | ||
|
|
8f69f1911a | ||
|
|
e466b3f06b | ||
|
|
a2ae28c1c1 | ||
|
|
41626b5018 | ||
|
|
9a76d472db | ||
|
|
964bba1823 | ||
|
|
49b85f885d | ||
|
|
93e9357f84 | ||
|
|
2a7f58c187 | ||
|
|
08e47b8d04 | ||
|
|
217c4d5d65 | ||
|
|
ea762de937 | ||
|
|
e6105ca654 | ||
|
|
7a87ffc20a | ||
|
|
f53ad7d557 | ||
|
|
ffe4d0d4ae | ||
|
|
9e2f03d8be | ||
|
|
489cfe5a96 | ||
|
|
71debfbb65 | ||
|
|
34258511b9 | ||
|
|
0f121860fe | ||
|
|
70a6f547cf | ||
|
|
886f15aa9f | ||
|
|
d895301116 | ||
|
|
fdaa2d1dba |
@@ -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
|
||||
+33
-13
@@ -11,24 +11,17 @@ FetchContent_Declare(
|
||||
URL https://github.com/recp/cglm/archive/refs/tags/v0.9.2.tar.gz
|
||||
URL_HASH SHA1=cb8472aa8c2ab67b66378dbaf10c2c7368d4e4c3
|
||||
)
|
||||
FetchContent_Declare(
|
||||
SDL
|
||||
URL https://github.com/libsdl-org/SDL/releases/download/release-2.30.1/SDL2-2.30.1.tar.gz
|
||||
URL_HASH SHA1=9d502c495f3aa2d15446376e835a5e561ac32897
|
||||
)
|
||||
FetchContent_Declare(
|
||||
freetype
|
||||
GIT_REPOSITORY https://github.com/freetype/freetype.git
|
||||
GIT_TAG VER-2-13-2
|
||||
)
|
||||
FetchContent_MakeAvailable(cglm SDL freetype)
|
||||
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)
|
||||
|
||||
@@ -64,6 +57,7 @@ target_sources(xsystem4 PRIVATE
|
||||
src/format.c
|
||||
src/hacks.c
|
||||
src/heap.c
|
||||
src/icon.c
|
||||
src/id_pool.c
|
||||
src/input.c
|
||||
src/json.c
|
||||
@@ -73,6 +67,7 @@ target_sources(xsystem4 PRIVATE
|
||||
src/resume.c
|
||||
src/savedata.c
|
||||
src/scene.c
|
||||
src/screenshot.c
|
||||
src/sprite.c
|
||||
src/swf.c
|
||||
src/system4.c
|
||||
@@ -81,17 +76,23 @@ target_sources(xsystem4 PRIVATE
|
||||
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
|
||||
@@ -100,7 +101,10 @@ target_sources(xsystem4 PRIVATE
|
||||
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
|
||||
@@ -109,14 +113,17 @@ target_sources(xsystem4 PRIVATE
|
||||
|
||||
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
|
||||
@@ -131,11 +138,17 @@ target_sources(xsystem4 PRIVATE
|
||||
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
|
||||
@@ -143,12 +156,14 @@ target_sources(xsystem4 PRIVATE
|
||||
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
|
||||
@@ -161,9 +176,13 @@ target_sources(xsystem4 PRIVATE
|
||||
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
|
||||
@@ -203,12 +222,13 @@ target_sources(xsystem4 PRIVATE
|
||||
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 z SDL2 freetype ffi GLESv3 cglm SndFile::sndfile sys4)
|
||||
m ZLIB::ZLIB SDL2::SDL2 Freetype::Freetype ffi GLESv3 cglm SndFile::sndfile sys4)
|
||||
|
||||
target_compile_options(xsystem4 PRIVATE -Wno-unused-parameter)
|
||||
|
||||
install(TARGETS xsystem4 SDL2 freetype)
|
||||
install(TARGETS xsystem4)
|
||||
|
||||
@@ -53,7 +53,7 @@ xsystem4 can be built on Windows using MSYS2.
|
||||
|
||||
First install MSYS2, and then open the MINGW64 shell and run the following command,
|
||||
|
||||
pacman -S flex bison \
|
||||
pacman -S make flex bison \
|
||||
mingw-w64-x86_64-gcc \
|
||||
mingw-w64-x86_64-meson \
|
||||
mingw-w64-x86_64-pkg-config \
|
||||
@@ -62,13 +62,15 @@ First install MSYS2, and then open the MINGW64 shell and run the following comma
|
||||
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-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 n6.0
|
||||
git checkout n7.1.2
|
||||
./configure --disable-everything \
|
||||
--enable-decoder=mpegvideo \
|
||||
--enable-decoder=mpeg1video \
|
||||
|
||||
Binary file not shown.
Generated
+4
-4
@@ -2,16 +2,16 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1678137616,
|
||||
"narHash": "sha256-T+lWTRdcYaOnZQW+Ehdlg+YldC2l9cq2GXJFPq22Nxc=",
|
||||
"lastModified": 1757244434,
|
||||
"narHash": "sha256-AeqTqY0Y95K1Fgs6wuT1LafBNcmKxcOkWnm4alD9pqM=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "7edcdf7b169c33cd3eef9aba50521ce93ee666b8",
|
||||
"rev": "092c565d333be1e17b4779ac22104338941d913f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-22.11",
|
||||
"ref": "nixos-25.05",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
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-22.11";
|
||||
inputs.nixpkgs.url = "nixpkgs/nixos-25.05";
|
||||
|
||||
outputs = { self, nixpkgs }:
|
||||
let
|
||||
@@ -32,7 +32,7 @@
|
||||
libwebp
|
||||
libsndfile
|
||||
glew
|
||||
readline81
|
||||
readline
|
||||
chibi
|
||||
flex
|
||||
bison
|
||||
@@ -62,7 +62,7 @@
|
||||
libwebp
|
||||
libsndfile
|
||||
glew
|
||||
readline81
|
||||
readline
|
||||
chibi
|
||||
flex
|
||||
bison
|
||||
|
||||
+24
-10
@@ -24,7 +24,7 @@ issues, please let me know or open a pull request to update this table.
|
||||
| ウルトラ魔法少女まなな Vol.1~Vol.5 | Supported | |
|
||||
| 鈴宮刑務娼館 | Supported | From ALICEの館7 |
|
||||
| しまいま | Supported | From ALICEの館7 |
|
||||
| DUNGEONS & DOLLS | Unsupported | From ALICEの館7 |
|
||||
| DUNGEONS & DOLLS | Supported | From ALICEの館7 |
|
||||
| こすぷれれもん | Supported | From ALICEの館7 |
|
||||
| エルメイフラワー | Supported | From ALICEの館7 |
|
||||
| CGは17年ゼミ | Supported | From ALICEの館7 |
|
||||
@@ -42,24 +42,24 @@ issues, please let me know or open a pull request to update this table.
|
||||
| Text Jam Session | Supported | From ALICEの館7 |
|
||||
| 没ロゴの哀しみ | Supported | From ALICEの館7 |
|
||||
| スタッフコーナー | Supported | From ALICEの館7 |
|
||||
| ぱすてるチャイムContinue | Unsupported | |
|
||||
| ぱすてるチャイムContinue | Supported | |
|
||||
| GALZOOアイランド | Supported | |
|
||||
| ぱすちゃC++ | Unknown | |
|
||||
| ぱすちゃC++ | Supported | |
|
||||
| よくばりサボテン | Supported | |
|
||||
| 妻しぼり | Unknown | |
|
||||
| 戦国ランス | Supported | |
|
||||
| Sengoku Rance (EN FanTL) | Supported | |
|
||||
| Sengoku Rance (MangaGamer) | Supported | |
|
||||
| だぶる先生らいふっ | Unknown | |
|
||||
| お嬢様をいいなりにするゲーム | Unknown | |
|
||||
| お嬢様をいいなりにするゲーム | Supported | |
|
||||
| 超昂閃忍ハルカ | 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 |
|
||||
@@ -73,11 +73,25 @@ issues, please let me know or open a pull request to update this table.
|
||||
| Rance 02 -The Rebellious Maidens- (Fan TL) | Supported | |
|
||||
| Rance 02 -The Rebellious Maidens- (MangaGamer) | Supported | |
|
||||
| しゃーまんず・さんくちゅあり-巫女の聖域- | Unknown | |
|
||||
| 大帝国 | Unknown | |
|
||||
| ランス・クエスト | Unsupported | |
|
||||
| 大帝国 | Supported | |
|
||||
| ランス・クエスト | Supported | |
|
||||
| ランス・クエスト マグナム | Supported | |
|
||||
| 母娘乱館 | Unknown | |
|
||||
| パステルチャイム3 バインドシーカー | Unsupported | |
|
||||
| パステルチャイム3 バインドシーカー | Supported | |
|
||||
| パスチャ3 プラスコンテンツ | Supported | |
|
||||
| どらぺこ! ~おねだりドラゴンとおっぱい勇者~ | Unsupported | |
|
||||
| ランス01 光をもとめて | Supported | |
|
||||
| Rance 01 - Quest for Hikari (EN) | Supported | |
|
||||
| All games released after Rance 01 | Unsupported | |
|
||||
| ランス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 | |
|
||||
|
||||
+6
-1
@@ -19,6 +19,8 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#define AUDIO_NO_METADATA -1
|
||||
|
||||
struct archive_data;
|
||||
|
||||
void audio_init(void);
|
||||
@@ -78,6 +80,8 @@ 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_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);
|
||||
@@ -86,6 +90,7 @@ int bgm_prepare_from_file(int id, char *filename);
|
||||
int wav_get_group_num_from_data_num(int no);
|
||||
|
||||
// Takes ownership of dfile.
|
||||
int wav_prepare_from_archive_data(int id, struct archive_data *dfile);
|
||||
int wav_prepare_from_archive_data(int id, int metadata_no, struct archive_data *dfile);
|
||||
int bgm_prepare_from_archive_data(int id, int metadata_no, struct archive_data *dfile);
|
||||
|
||||
#endif /* SYSTEM4_AUDIO_H */
|
||||
|
||||
+16
-2
@@ -59,12 +59,15 @@ 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(enum dbg_stop_type, const char *msg);
|
||||
|
||||
@@ -78,6 +81,7 @@ 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);
|
||||
@@ -87,8 +91,10 @@ 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);
|
||||
@@ -96,13 +102,21 @@ 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()
|
||||
#define dbg_init(debug_info_path)
|
||||
#define dbg_repl(type, msg)
|
||||
#define dbg_dap 0
|
||||
#define dbg_dap_handle_messages()
|
||||
|
||||
+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 <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,20 +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 */
|
||||
@@ -26,14 +26,26 @@ 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);
|
||||
|
||||
|
||||
@@ -72,6 +72,8 @@ 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
|
||||
};
|
||||
|
||||
|
||||
+3
-8
@@ -160,23 +160,18 @@ 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_char_kerning(struct text_style *ts, uint32_t code, uint32_t code_next);
|
||||
float gfx_size_text(struct text_style *ts, const char *text);
|
||||
float gfx_get_actual_font_size(unsigned face, float size);
|
||||
float gfx_get_actual_font_size_round_down(unsigned face, float size);
|
||||
|
||||
static inline float text_style_width(struct text_style *ts, const char *ch)
|
||||
{
|
||||
return (gfx_size_char(ts, ch) + (ts->bold_width * 2) + ts->edge_left + ts->edge_right)
|
||||
* ts->scale_x;
|
||||
}
|
||||
|
||||
static inline float text_style_height(struct text_style *ts)
|
||||
{
|
||||
return ts->size + (ts->bold_width * 2) + ts->edge_up + ts->edge_down;
|
||||
return ts->size + ceilf(ts->bold_width)*2 + ts->edge_up + ts->edge_down;
|
||||
}
|
||||
|
||||
#endif /* SYSTEM4_FONT_H */
|
||||
|
||||
+19
-3
@@ -36,7 +36,6 @@ enum draw_method {
|
||||
|
||||
typedef struct texture {
|
||||
GLuint handle;
|
||||
mat4 world_transform;
|
||||
int w, h;
|
||||
} Texture;
|
||||
|
||||
@@ -48,13 +47,15 @@ typedef struct shader {
|
||||
GLuint world_transform;
|
||||
GLuint view_transform;
|
||||
GLuint texture;
|
||||
GLuint vertex;
|
||||
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 {
|
||||
@@ -66,16 +67,25 @@ struct gfx_render_job {
|
||||
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);
|
||||
@@ -89,6 +99,7 @@ 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);
|
||||
@@ -150,6 +161,7 @@ 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);
|
||||
@@ -159,6 +171,7 @@ void gfx_copy_rot_zoom2(Texture *dst, float cx, float cy, Texture *src, float sc
|
||||
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);
|
||||
@@ -171,10 +184,13 @@ void gfx_copy_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx,
|
||||
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_copy_grayscale(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_copy_grayscale_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_copy_grayscale_reverse_LR_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_draw_line(Texture *dst, int x0, int y0, int x1, int y1, int r, int g, int b);
|
||||
void gfx_draw_line_to_amap(Texture *dst, int x0, int y0, int x1, int y1, int a);
|
||||
void gfx_draw_glyph(Texture *dst, float dx, int dy, Texture *glyph, SDL_Color color, float scale_x, float bold_width, bool blend);
|
||||
void gfx_draw_glyph_to_pmap(Texture *dst, float dx, int dy, Texture *glyph, Rectangle glyph_pos, SDL_Color color, float scale_x);
|
||||
void gfx_draw_glyph_to_amap(Texture *dst, float dx, int dy, Texture *glyph, Rectangle glyph_pos, float scale_x);
|
||||
void gfx_draw_quadrilateral(Texture *dst, Texture *src, struct gfx_vertex vertices[4]);
|
||||
|
||||
#endif /* SYSTEM4_SDL_CORE_H */
|
||||
|
||||
@@ -31,6 +31,7 @@ struct sdl_private {
|
||||
SDL_GLContext context;
|
||||
GLuint vao;
|
||||
GLuint vbo;
|
||||
GLuint quad_vbo;
|
||||
GLuint rect_ibo;
|
||||
GLuint line_ibo;
|
||||
} gl;
|
||||
|
||||
@@ -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 */
|
||||
+3
-1
@@ -140,7 +140,7 @@ 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);
|
||||
@@ -153,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 */
|
||||
|
||||
+3
-2
@@ -59,8 +59,9 @@ 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);
|
||||
|
||||
@@ -23,6 +23,7 @@ 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);
|
||||
@@ -36,6 +37,7 @@ 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,
|
||||
@@ -67,6 +69,7 @@ bool PE_SetNumeralSurfaceArea(int parts_no, int x, int y, int w, int h, int stat
|
||||
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);
|
||||
int PE_GetPartsX(int parts_no);
|
||||
int PE_GetPartsY(int parts_no);
|
||||
@@ -94,6 +97,7 @@ bool PE_SetPartsGroupNumber(int parts_no, int group_no);
|
||||
void PE_SetPartsMessageWindowShowLink(int parts_no, bool message_window_show_link);
|
||||
bool PE_GetPartsMessageWindowShowLink(int parts_no);
|
||||
void PE_SetSpeedupRateByMessageSkip(int parts_no, int rate);
|
||||
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);
|
||||
@@ -102,9 +106,12 @@ void PE_SetPartsRotateX(int parts_no, float rot_x);
|
||||
void PE_SetPartsRotateY(int parts_no, float rot_y);
|
||||
void PE_SetPartsRotateZ(int parts_no, float rot_z);
|
||||
float PE_GetPartsRotateZ(int parts_no);
|
||||
void PE_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);
|
||||
@@ -112,9 +119,27 @@ 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);
|
||||
@@ -125,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,
|
||||
@@ -143,8 +174,11 @@ bool PE_SetPartsConstructionSurfaceArea(int parts_no, int x, int y, int w, int h
|
||||
|
||||
// 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_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);
|
||||
@@ -157,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,
|
||||
@@ -203,9 +250,11 @@ 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);
|
||||
@@ -219,6 +268,7 @@ 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);
|
||||
@@ -230,4 +280,37 @@ 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
|
||||
|
||||
@@ -24,7 +24,9 @@ struct sact_sprite;
|
||||
|
||||
struct draw_plugin {
|
||||
const char *name;
|
||||
void (*free)(struct draw_plugin *);
|
||||
void (*update)(struct sact_sprite *);
|
||||
void (*render)(struct sact_sprite *);
|
||||
cJSON *(*to_json)(struct sact_sprite *, bool);
|
||||
};
|
||||
|
||||
|
||||
+95
-4
@@ -19,6 +19,7 @@
|
||||
|
||||
#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>
|
||||
|
||||
@@ -27,10 +28,14 @@
|
||||
|
||||
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 {
|
||||
@@ -56,6 +61,29 @@ 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,
|
||||
@@ -63,7 +91,8 @@ enum RE_draw_type {
|
||||
};
|
||||
|
||||
enum RE_draw_options {
|
||||
RE_DRAW_OPTION_UNKNOWN,
|
||||
RE_DRAW_OPTION_EDGE = 0,
|
||||
RE_DRAW_OPTION_LIGHTING = 1,
|
||||
RE_DRAW_OPTION_MAX
|
||||
};
|
||||
|
||||
@@ -77,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 {
|
||||
@@ -98,10 +133,11 @@ struct RE_plugin {
|
||||
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];
|
||||
|
||||
@@ -143,30 +179,43 @@ struct RE_plugin {
|
||||
|
||||
// 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;
|
||||
@@ -175,42 +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;
|
||||
};
|
||||
|
||||
#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);
|
||||
@@ -290,4 +366,19 @@ bool RE_back_cg_set_name(struct RE_back_cg *bcg, struct string *name, struct arc
|
||||
void RE_render(struct sact_sprite *sp);
|
||||
cJSON *RE_to_json(struct sact_sprite *sp, bool verbose);
|
||||
|
||||
// save.c
|
||||
void RE_plugin_serialize(struct RE_plugin *plugin, struct iarray_writer *w);
|
||||
void RE_plugin_deserialize(struct RE_plugin *plugin, struct iarray_reader *r, int version);
|
||||
|
||||
// Exposed for PartsEngine 3DLayer
|
||||
int ReignEngine_create_plugin(enum RE_plugin_version version);
|
||||
bool ReignEngine_ReleasePlugin(int handle);
|
||||
bool ReignEngine_BindPlugin(int handle, int sprite);
|
||||
|
||||
// Exposed for MarmotModelEngine
|
||||
void ReignEngine_update_models(void);
|
||||
|
||||
// debug.c
|
||||
void re_debug_init(void);
|
||||
|
||||
#endif /* SYSTEM4_REIGN_H */
|
||||
|
||||
+18
-1
@@ -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(int cg_cache_size, bool chipmunk);
|
||||
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);
|
||||
@@ -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);
|
||||
@@ -106,6 +118,8 @@ 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);
|
||||
@@ -114,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);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
void json_load_page(struct page *page, cJSON *vars, bool call_dtors);
|
||||
|
||||
int save_json(const char *filename, cJSON *json);
|
||||
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);
|
||||
|
||||
+4
-1
@@ -60,13 +60,15 @@ struct sact_sprite {
|
||||
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 - only shader param is blend rate
|
||||
* * SACT2 - blend rate and brightness (stored in multiply_color field)
|
||||
* * ChipmunkSpriteEngine - blend rate, add color, multiply color, surface area
|
||||
* (uses parts shader)
|
||||
*
|
||||
@@ -91,6 +93,7 @@ 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);
|
||||
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);
|
||||
|
||||
@@ -78,15 +78,6 @@ 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;
|
||||
|
||||
+1
-1
@@ -103,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);
|
||||
|
||||
|
||||
+13
-1
@@ -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,7 +50,7 @@ struct config {
|
||||
bool echo;
|
||||
float text_x_scale;
|
||||
bool manual_text_x_scale;
|
||||
bool rsm_save;
|
||||
enum resume_save_format save_format;
|
||||
int msgskip_delay;
|
||||
};
|
||||
|
||||
@@ -65,6 +72,7 @@ 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);
|
||||
@@ -73,6 +81,8 @@ 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
|
||||
@@ -84,5 +94,7 @@ extern bool game_rance02_mg;
|
||||
extern bool game_rance6_mg;
|
||||
extern bool game_rance7_mg;
|
||||
extern bool game_rance8;
|
||||
extern bool game_rance8_mg;
|
||||
extern bool game_dungeons_and_dolls;
|
||||
|
||||
#endif /* XSYSTEM4_H */
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ 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.8.3', fallback : ['cglm', 'cglm_dep'])
|
||||
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)
|
||||
|
||||
@@ -20,6 +20,7 @@ in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
float gray = dot(texture(tex, tex_coord).rgb, vec3(0.299, 0.587, 0.114));
|
||||
frag_color = vec4(vec3(gray), 1);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
+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;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ const float PI = 3.14159265;
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
vec4 sample(float offset_x) {
|
||||
vec4 sample_texture(float offset_x) {
|
||||
return texture(tex, vec2(tex_coord.x + offset_x, tex_coord.y));
|
||||
}
|
||||
|
||||
@@ -35,5 +35,5 @@ void main() {
|
||||
float offset1 = cos(p1 * 2.0 * PI) * a;
|
||||
float offset2 = cos(p2 * 2.0 * PI) * a;
|
||||
|
||||
frag_color = mix(sample(offset1), sample(offset2), 0.5);
|
||||
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);
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
+32
-7
@@ -20,21 +20,46 @@ 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 * blend_rate) * 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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
|
||||
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 = texture(tex, tex_coord);
|
||||
frag_color.rgb *= multiply_color;
|
||||
frag_color.a *= blend_rate;
|
||||
}
|
||||
|
||||
+387
-22
@@ -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,27 @@ 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;
|
||||
};
|
||||
@@ -175,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
|
||||
@@ -193,7 +243,8 @@ struct archive_data *RE_get_aar_entry(struct archive *aar, const char *dir, cons
|
||||
struct RE_renderer *RE_renderer_new(void);
|
||||
void RE_renderer_free(struct RE_renderer *r);
|
||||
void RE_renderer_set_viewport_size(struct RE_renderer *r, int width, int height);
|
||||
bool RE_renderer_load_billboard_texture(struct RE_renderer *r, int cg_no);
|
||||
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);
|
||||
@@ -369,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 {
|
||||
@@ -392,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 {
|
||||
@@ -424,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 {
|
||||
@@ -443,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;
|
||||
};
|
||||
@@ -459,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[];
|
||||
};
|
||||
|
||||
@@ -509,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;
|
||||
}
|
||||
+249
-2
@@ -18,8 +18,12 @@
|
||||
|
||||
#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"
|
||||
|
||||
@@ -115,6 +119,9 @@ static cJSON *instance_to_json(struct RE_instance *inst, int index, bool verbose
|
||||
if (inst->effect) {
|
||||
cJSON_AddStringToObject(obj, "path", inst->effect->pae->path);
|
||||
}
|
||||
if (inst->s3de_effect && inst->s3de_effect->s3de) {
|
||||
cJSON_AddStringToObject(obj, "path", inst->s3de_effect->s3de->path);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
@@ -143,9 +150,8 @@ static cJSON *back_cg_to_json(struct RE_back_cg *bcg, int index, bool verbose)
|
||||
return obj;
|
||||
}
|
||||
|
||||
cJSON *RE_to_json(struct sact_sprite *sp, bool verbose)
|
||||
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;
|
||||
|
||||
obj = cJSON_CreateObject();
|
||||
@@ -198,3 +204,244 @@ cJSON *RE_to_json(struct sact_sprite *sp, bool 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;
|
||||
|
||||
+455
-59
@@ -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;
|
||||
@@ -201,7 +244,10 @@ static void update_bones(struct RE_instance *inst)
|
||||
|
||||
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,7 +259,7 @@ struct RE_plugin *RE_plugin_new(enum RE_plugin_version version)
|
||||
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;
|
||||
@@ -222,18 +268,25 @@ struct RE_plugin *RE_plugin_new(enum RE_plugin_version version)
|
||||
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]);
|
||||
@@ -242,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)
|
||||
@@ -277,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];
|
||||
@@ -290,18 +365,25 @@ 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;
|
||||
@@ -319,6 +401,10 @@ bool RE_set_viewport(struct RE_plugin *plugin, int x, int y, int width, int heig
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -342,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)
|
||||
@@ -381,6 +479,10 @@ 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;
|
||||
@@ -395,15 +497,71 @@ bool RE_instance_set_type(struct RE_instance *instance, int 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:
|
||||
@@ -413,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)
|
||||
@@ -466,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)
|
||||
@@ -500,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;
|
||||
}
|
||||
@@ -527,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)
|
||||
@@ -540,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 = {
|
||||
@@ -594,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;
|
||||
}
|
||||
}
|
||||
@@ -609,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;
|
||||
}
|
||||
}
|
||||
|
||||
+681
-120
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));
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -85,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)
|
||||
@@ -334,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)
|
||||
|
||||
+27
-5
@@ -103,7 +103,7 @@ bool audio_play_archive_data(struct archive_data *dfile)
|
||||
for (int i = 0; i < NR_ANONYMOUS_CHANNELS; i++) {
|
||||
if (anonymous_channels[i] == -1) {
|
||||
int ch = wav_get_unused_channel();
|
||||
if (!wav_prepare_from_archive_data(ch, dfile))
|
||||
if (!wav_prepare_from_archive_data(ch, AUDIO_NO_METADATA, dfile))
|
||||
return false;
|
||||
if (!wav_play(ch)) {
|
||||
wav_unprepare(ch);
|
||||
@@ -361,6 +361,18 @@ 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); }
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -392,19 +404,29 @@ static int audio_prepare_from_file(struct id_pool *pool, int id, char *filename)
|
||||
int wav_prepare_from_file(int id, char *filename) { return audio_prepare_from_file(&wav, id, filename); }
|
||||
int bgm_prepare_from_file(int id, char *filename) { return audio_prepare_from_file(&bgm, id, filename); }
|
||||
|
||||
static int audio_prepare_from_archive_data(struct id_pool *pool, int id, struct archive_data *dfile)
|
||||
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);
|
||||
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, struct archive_data *dfile) {
|
||||
return audio_prepare_from_archive_data(&wav, id, dfile);
|
||||
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)
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+21
-23
@@ -505,33 +505,12 @@ 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);
|
||||
} else {
|
||||
ch->loop_count = 0;
|
||||
}
|
||||
}
|
||||
ch->no = no;
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
@@ -565,7 +544,8 @@ static bool init_channel(struct channel *ch)
|
||||
return true;
|
||||
}
|
||||
|
||||
struct channel *channel_open_archive_data(struct archive_data *dfile)
|
||||
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;
|
||||
@@ -580,6 +560,24 @@ struct channel *channel_open_archive_data(struct archive_data *dfile)
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
+101
-3
@@ -24,6 +24,7 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include "system4/dasm.h"
|
||||
#include "system4/file.h"
|
||||
#include "system4/hashtable.h"
|
||||
#include "system4/little_endian.h"
|
||||
#include "system4/string.h"
|
||||
@@ -39,6 +40,7 @@
|
||||
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;
|
||||
|
||||
@@ -94,8 +96,19 @@ void dbg_repl(enum dbg_stop_type type, const char *msg)
|
||||
dbg_start(_dbg_repl, &stop);
|
||||
}
|
||||
|
||||
void dbg_init(void)
|
||||
void dbg_init(const char *debug_info_path)
|
||||
{
|
||||
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
|
||||
@@ -162,6 +175,27 @@ bool dbg_clear_breakpoint(uint32_t addr, void(*free_data)(void*))
|
||||
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);
|
||||
@@ -437,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;
|
||||
sys_message("%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)
|
||||
@@ -724,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)
|
||||
{
|
||||
@@ -748,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);
|
||||
@@ -831,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
|
||||
@@ -1094,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;
|
||||
}
|
||||
+69
-15
@@ -40,7 +40,14 @@
|
||||
#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;
|
||||
@@ -67,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();
|
||||
}
|
||||
|
||||
@@ -103,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);
|
||||
}
|
||||
@@ -115,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);
|
||||
}
|
||||
@@ -152,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);
|
||||
}
|
||||
@@ -184,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();
|
||||
}
|
||||
|
||||
@@ -208,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();
|
||||
}
|
||||
|
||||
@@ -226,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 },
|
||||
@@ -477,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)
|
||||
|
||||
+194
-22
@@ -56,6 +56,18 @@ static enum {
|
||||
DAP_STOPPED
|
||||
} dap_state = DAP_UNINITIALIZED;
|
||||
|
||||
static bool dap_launch_req_received = false;
|
||||
static bool dap_config_done_req_received = 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;
|
||||
|
||||
static struct msgq *queue;
|
||||
|
||||
static void json_add_sjis_to_object(cJSON *obj, const char *name, const char *sjis)
|
||||
@@ -127,6 +139,7 @@ static void emit_stopped_event(struct dbg_stop *stop)
|
||||
}
|
||||
if (stop->message)
|
||||
cJSON_AddStringToObject(body, "text", stop->message);
|
||||
cJSON_AddBoolToObject(body, "allThreadsStopped", true);
|
||||
emit_event("stopped", body);
|
||||
}
|
||||
|
||||
@@ -152,6 +165,7 @@ static void cmd_initialize(cJSON *args, cJSON *resp)
|
||||
//cJSON_AddBoolToObject(body, "supportsSetExpression", true);
|
||||
//cJSON_AddBoolToObject(body, "supportsTerminateRequest", true);
|
||||
cJSON_AddBoolToObject(body, "supportsInstructionBreakpoints", true);
|
||||
cJSON_AddBoolToObject(body, "supportsSteppingGranularity", true);
|
||||
send_response(resp, true);
|
||||
|
||||
emit_initialized_event();
|
||||
@@ -167,12 +181,17 @@ static void cmd_launch(cJSON *args, cJSON *resp)
|
||||
|
||||
// TODO: handle noDebug option
|
||||
send_response(resp, true);
|
||||
dap_state = DAP_RUNNING;
|
||||
dap_launch_req_received = true;
|
||||
if (dap_config_done_req_received)
|
||||
dap_state = DAP_RUNNING;
|
||||
}
|
||||
|
||||
static void cmd_configurationDone(cJSON *args, cJSON *resp)
|
||||
{
|
||||
send_response(resp, true);
|
||||
dap_config_done_req_received = true;
|
||||
if (dap_launch_req_received)
|
||||
dap_state = DAP_RUNNING;
|
||||
}
|
||||
|
||||
static void cmd_stackTrace(cJSON *args, cJSON *resp)
|
||||
@@ -182,7 +201,7 @@ static void cmd_stackTrace(cJSON *args, cJSON *resp)
|
||||
cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject());
|
||||
cJSON_AddNumberToObject(body, "totalFrames", call_stack_ptr);
|
||||
cJSON_AddItemToObjectCS(body, "stackFrames", stack_frames = cJSON_CreateArray());
|
||||
for (int i = 0; i < call_stack_ptr; i++) {
|
||||
for (int i = call_stack_ptr - 1; i >= 0; i--) {
|
||||
cJSON_AddItemToArray(stack_frames, frame = cJSON_CreateObject());
|
||||
cJSON_AddNumberToObject(frame, "id", i);
|
||||
// this *shouldn't* happen, but since we're in the debugger...
|
||||
@@ -193,13 +212,24 @@ static void cmd_stackTrace(cJSON *args, cJSON *resp)
|
||||
json_add_sjis_to_object(frame, "name", ain->functions[fno].name);
|
||||
}
|
||||
char ip[9];
|
||||
if (i == call_stack_ptr - 1) {
|
||||
snprintf(ip, 9, "%x", (unsigned)instr_ptr);
|
||||
} else {
|
||||
snprintf(ip, 9, "%x", call_stack[i].call_address);
|
||||
}
|
||||
int addr = (i == call_stack_ptr - 1) ? instr_ptr : call_stack[i+1].call_address;
|
||||
snprintf(ip, 9, "%x", (unsigned)addr);
|
||||
cJSON_AddStringToObject(frame, "instructionPointerReference", ip);
|
||||
cJSON_AddNumberToObject(frame, "line", 0);
|
||||
int file, line;
|
||||
if (dbg_info && dbg_info_addr2line(dbg_info, addr, &file, &line)) {
|
||||
cJSON_AddNumberToObject(frame, "line", line);
|
||||
|
||||
const char *fname = dbg_info_source_name(dbg_info, file);
|
||||
cJSON *source = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(source, "name", fname);
|
||||
char *path = dbg_info_source_path(dbg_info, file);
|
||||
cJSON_AddStringToObject(source, "path", path);
|
||||
free(path);
|
||||
cJSON_AddNumberToObject(source, "sourceReference", 0);
|
||||
cJSON_AddItemToObjectCS(frame, "source", source);
|
||||
} else {
|
||||
cJSON_AddNumberToObject(frame, "line", 0);
|
||||
}
|
||||
cJSON_AddNumberToObject(frame, "column", 0);
|
||||
}
|
||||
send_response(resp, true);
|
||||
@@ -303,32 +333,34 @@ static int make_var_ref(struct ain_type *type, union vm_value value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static cJSON *value_to_json(const char *name, struct ain_type *type, union vm_value value)
|
||||
static cJSON *value_to_json(const char *name, struct ain_type *type, struct page *page, int slot)
|
||||
{
|
||||
char *s_type = ain_strtype_d(ain, type);
|
||||
struct string *s_val = dbg_value_to_string(type, value, 0);
|
||||
struct string *s_val = dbg_variable_to_string(type, page, slot, 0);
|
||||
|
||||
cJSON *json = cJSON_CreateObject();
|
||||
json_add_sjis_to_object(json, "name", name);
|
||||
json_add_sjis_to_object(json, "value", s_val->text);
|
||||
json_add_sjis_to_object(json, "type", s_type);
|
||||
cJSON_AddNumberToObject(json, "variablesReference", make_var_ref(type, value));
|
||||
cJSON_AddNumberToObject(json, "variablesReference", make_var_ref(type, page->values[slot]));
|
||||
|
||||
free_string(s_val);
|
||||
free(s_type);
|
||||
return json;
|
||||
}
|
||||
|
||||
static cJSON *var_to_json(struct ain_variable *var, union vm_value value)
|
||||
static void add_var_to_json_array(cJSON *array, struct ain_variable *var, struct page *page, int slot)
|
||||
{
|
||||
return value_to_json(var->name, &var->type, value);
|
||||
if (var->type.data == AIN_VOID)
|
||||
return;
|
||||
cJSON_AddItemToArray(array, value_to_json(var->name, &var->type, page, slot));
|
||||
}
|
||||
|
||||
static void add_arguments_to_array(cJSON *array, struct page *page)
|
||||
{
|
||||
struct ain_function *f = &ain->functions[page->index];
|
||||
for (int i = 0; i < f->nr_args; i++) {
|
||||
cJSON_AddItemToArray(array, var_to_json(&f->vars[i], page->values[i]));
|
||||
add_var_to_json_array(array, &f->vars[i], page, i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,7 +368,7 @@ static void add_locals_to_array(cJSON *array, struct page *page)
|
||||
{
|
||||
struct ain_function *f = &ain->functions[page->index];
|
||||
for (int i = f->nr_args; i < f->nr_vars; i++) {
|
||||
cJSON_AddItemToArray(array, var_to_json(&f->vars[i], page->values[i]));
|
||||
add_var_to_json_array(array, &f->vars[i], page, i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +376,7 @@ static void add_variables_to_array(cJSON *array, struct page *page)
|
||||
{
|
||||
struct ain_function *f = &ain->functions[page->index];
|
||||
for (int i = 0; i < f->nr_vars; i++) {
|
||||
cJSON_AddItemToArray(array, var_to_json(&f->vars[i], page->values[i]));
|
||||
add_var_to_json_array(array, &f->vars[i], page, i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,14 +384,14 @@ static void add_members_to_array(cJSON *array, struct page *page)
|
||||
{
|
||||
struct ain_struct *s = &ain->structures[page->index];
|
||||
for (int i = 0; i < s->nr_members; i++) {
|
||||
cJSON_AddItemToArray(array, var_to_json(&s->members[i], page->values[i]));
|
||||
add_var_to_json_array(array, &s->members[i], page, i);
|
||||
}
|
||||
}
|
||||
|
||||
static void add_globals_to_array(cJSON *array, struct page *page)
|
||||
{
|
||||
for (int i = 0; i < ain->nr_globals; i++) {
|
||||
cJSON_AddItemToArray(array, var_to_json(&ain->globals[i], page->values[i]));
|
||||
add_var_to_json_array(array, &ain->globals[i], page, i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,12 +405,14 @@ static void add_array_elements_to_array(cJSON *array, struct page *page)
|
||||
snprintf(name, 512, "[%d]", i);
|
||||
struct ain_type type;
|
||||
type.data = variable_type(page, i, &type.struc, &type.rank);
|
||||
cJSON_AddItemToArray(array, value_to_json(name, &type, page->values[i]));
|
||||
cJSON_AddItemToArray(array, value_to_json(name, &type, page, i));
|
||||
}
|
||||
}
|
||||
|
||||
static void add_page_to_array(cJSON *array, struct page *page)
|
||||
{
|
||||
if (!page)
|
||||
return;
|
||||
switch (page->type) {
|
||||
case GLOBAL_PAGE:
|
||||
add_globals_to_array(array, page);
|
||||
@@ -451,6 +485,76 @@ static void cmd_variables(cJSON *args, cJSON *resp)
|
||||
send_response(resp, true);
|
||||
}
|
||||
|
||||
void delete_breakpoints_in_file(int addr, struct breakpoint *bp, void *file_)
|
||||
{
|
||||
int file = *(int *)file_;
|
||||
int f;
|
||||
if (dbg_info_addr2line(dbg_info, addr, &f, NULL) && f == file) {
|
||||
dbg_clear_breakpoint(addr, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_setBreakpoints(cJSON *args, cJSON *resp)
|
||||
{
|
||||
if (!dbg_info) {
|
||||
cJSON_AddStringToObject(resp, "message", "no debug info available");
|
||||
send_response(resp, false);
|
||||
return;
|
||||
}
|
||||
cJSON *source = cJSON_GetObjectItemCaseSensitive(args, "source");
|
||||
cJSON *source_path = cJSON_GetObjectItemCaseSensitive(source, "path");
|
||||
cJSON *req_bps = cJSON_GetObjectItemCaseSensitive(args, "breakpoints");
|
||||
if (!cJSON_IsString(source_path) || !cJSON_IsArray(req_bps)) {
|
||||
cJSON_AddStringToObject(resp, "message", "invalid arguments");
|
||||
send_response(resp, false);
|
||||
return;
|
||||
}
|
||||
const char *path = source_path->valuestring;
|
||||
int file = dbg_info_find_file(dbg_info, path);
|
||||
if (file < 0) {
|
||||
cJSON_AddStringToObject(resp, "message", "no such file");
|
||||
send_response(resp, false);
|
||||
return;
|
||||
}
|
||||
|
||||
dbg_foreach_breakpoint(delete_breakpoints_in_file, &file);
|
||||
|
||||
cJSON *body, *resp_bps;
|
||||
cJSON_AddItemToObjectCS(resp, "body", body = cJSON_CreateObject());
|
||||
cJSON_AddItemToObjectCS(body, "breakpoints", resp_bps = cJSON_CreateArray());
|
||||
|
||||
char message[256];
|
||||
cJSON *req_bp;
|
||||
cJSON_ArrayForEach(req_bp, req_bps) {
|
||||
cJSON *resp_bp = cJSON_CreateObject();
|
||||
cJSON_AddItemToArray(resp_bps, resp_bp);
|
||||
|
||||
cJSON *line = cJSON_GetObjectItemCaseSensitive(req_bp, "line");
|
||||
int line_no = line->valueint;
|
||||
int addr = dbg_info_line2addr(dbg_info, file, line_no);
|
||||
if (addr < 0) {
|
||||
snprintf(message, sizeof(message), "no line %d in %s", line_no, path);
|
||||
cJSON_AddBoolToObject(resp_bp, "verified", false);
|
||||
cJSON_AddStringToObject(resp_bp, "message", message);
|
||||
continue;
|
||||
}
|
||||
bool verified = dbg_set_address_breakpoint(addr, NULL, NULL);
|
||||
if (!verified) {
|
||||
snprintf(message, sizeof(message), "failed to set breakpoint at 0x%x", addr);
|
||||
cJSON_AddBoolToObject(resp_bp, "verified", false);
|
||||
cJSON_AddStringToObject(resp_bp, "message", message);
|
||||
continue;
|
||||
}
|
||||
|
||||
dbg_info_addr2line(dbg_info, addr, NULL, &line_no);
|
||||
cJSON_AddNumberToObject(resp_bp, "id", addr);
|
||||
cJSON_AddBoolToObject(resp_bp, "verified", true);
|
||||
cJSON_AddNumberToObject(resp_bp, "line", line_no);
|
||||
}
|
||||
|
||||
send_response(resp, true);
|
||||
}
|
||||
|
||||
static void cmd_setInstructionBreakpoints(cJSON *args, cJSON *resp)
|
||||
{
|
||||
static uint32_t *old_breakpoints = NULL;
|
||||
@@ -532,10 +636,22 @@ static void cmd_evaluate(cJSON *args, cJSON *resp)
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: frameId
|
||||
unsigned old_frame = dbg_current_frame;
|
||||
cJSON *j_id = cJSON_GetObjectItemCaseSensitive(args, "frameId");
|
||||
if (j_id && cJSON_IsNumber(j_id)) {
|
||||
int frame_id = j_id->valueint;
|
||||
if (frame_id >= 0 && frame_id < call_stack_ptr) {
|
||||
dbg_current_frame = call_stack_ptr - 1 - frame_id;
|
||||
}
|
||||
}
|
||||
|
||||
struct ain_type type;
|
||||
union vm_value val = dbg_eval_string(j_expr->valuestring, &type);
|
||||
char *sjis_expr = utf2sjis(j_expr->valuestring, 0);
|
||||
union vm_value val = dbg_eval_string(sjis_expr, &type);
|
||||
free(sjis_expr);
|
||||
|
||||
dbg_current_frame = old_frame;
|
||||
|
||||
struct string *s_val = dbg_value_to_string(&type, val, 0);
|
||||
char *s_type = ain_strtype_d(ain, &type);
|
||||
|
||||
@@ -553,6 +669,8 @@ static void cmd_continue(cJSON *args, cJSON *resp)
|
||||
{
|
||||
send_response(resp, true);
|
||||
|
||||
stepping = NOT_STEPPING;
|
||||
stepping_file = stepping_line = 0;
|
||||
dap_state = DAP_RUNNING;
|
||||
dbg_continue();
|
||||
}
|
||||
@@ -572,12 +690,35 @@ static void cmd_pause(cJSON *args, cJSON *resp)
|
||||
}
|
||||
}
|
||||
|
||||
enum stepping_granularity {
|
||||
STEPPING_GRANULARITY_STATEMENT,
|
||||
STEPPING_GRANULARITY_LINE,
|
||||
STEPPING_GRANULARITY_INSTRUCTION,
|
||||
};
|
||||
|
||||
static enum stepping_granularity get_granularity(cJSON *args)
|
||||
{
|
||||
cJSON *g = cJSON_GetObjectItemCaseSensitive(args, "granularity");
|
||||
if (cJSON_IsString(g)) {
|
||||
if (!strcmp(g->valuestring, "line"))
|
||||
return STEPPING_GRANULARITY_LINE;
|
||||
if (!strcmp(g->valuestring, "instruction"))
|
||||
return STEPPING_GRANULARITY_INSTRUCTION;
|
||||
}
|
||||
return STEPPING_GRANULARITY_STATEMENT;
|
||||
}
|
||||
|
||||
static void cmd_stepIn(cJSON *args, cJSON *resp)
|
||||
{
|
||||
stepping_file = stepping_line = 0;
|
||||
if (dbg_info && get_granularity(args) != STEPPING_GRANULARITY_INSTRUCTION)
|
||||
dbg_info_addr2line(dbg_info, instr_ptr, &stepping_file, &stepping_line);
|
||||
|
||||
dbg_set_step_into_breakpoint();
|
||||
send_response(resp, true);
|
||||
|
||||
dap_state = DAP_RUNNING;
|
||||
stepping = STEPPING_INTO;
|
||||
dbg_continue();
|
||||
}
|
||||
|
||||
@@ -587,15 +728,21 @@ static void cmd_stepOut(cJSON *args, cJSON *resp)
|
||||
send_response(resp, true);
|
||||
|
||||
dap_state = DAP_RUNNING;
|
||||
stepping = STEPPING_FINISH;
|
||||
dbg_continue();
|
||||
}
|
||||
|
||||
static void cmd_next(cJSON *args, cJSON *resp)
|
||||
{
|
||||
stepping_file = stepping_line = 0;
|
||||
if (dbg_info && get_granularity(args) != STEPPING_GRANULARITY_INSTRUCTION)
|
||||
dbg_info_addr2line(dbg_info, instr_ptr, &stepping_file, &stepping_line);
|
||||
|
||||
dbg_set_step_over_breakpoint();
|
||||
send_response(resp, true);
|
||||
|
||||
dap_state = DAP_RUNNING;
|
||||
stepping = STEPPING_OVER;
|
||||
dbg_continue();
|
||||
}
|
||||
|
||||
@@ -797,6 +944,7 @@ static struct {
|
||||
{ "next", cmd_next, false },
|
||||
{ "pause", cmd_pause, true },
|
||||
{ "evaluate", cmd_evaluate, true },
|
||||
{ "setBreakpoints", cmd_setBreakpoints, true },
|
||||
{ "setInstructionBreakpoints", cmd_setInstructionBreakpoints, true },
|
||||
{ "threads", cmd_threads, true },
|
||||
{ "scopes", cmd_scopes, true },
|
||||
@@ -862,12 +1010,13 @@ static int read_command_thread(void *data)
|
||||
WARNING("Debug Adapter Protocol error: no Content-Length header");
|
||||
continue;
|
||||
}
|
||||
char *buf = malloc(content_length);
|
||||
char *buf = malloc(content_length+1);
|
||||
if (fread(buf, content_length, 1, stdin) != 1) {
|
||||
WARNING("fread(stdin): %s", strerror(errno));
|
||||
free(buf);
|
||||
continue;
|
||||
}
|
||||
buf[content_length] = '\0';
|
||||
msgq_enqueue(queue, buf);
|
||||
content_length = -1;
|
||||
} else {
|
||||
@@ -905,6 +1054,22 @@ void dbg_dap_quit(void)
|
||||
|
||||
void dbg_dap_repl(struct dbg_stop *stop)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
emit_stopped_event(stop);
|
||||
dap_state = DAP_STOPPED;
|
||||
|
||||
@@ -933,6 +1098,13 @@ void dbg_dap_log(const char *log, const char *fmt, va_list ap)
|
||||
size_t len = max(1024, strlen(fmt) * 2);
|
||||
char *buf = xmalloc(len);
|
||||
vsnprintf(buf, len, fmt, ap);
|
||||
#ifdef _WIN32
|
||||
// Since the callers use display_sjis*(), buf is in Shift_JIS on Windows.
|
||||
// Convert to UTF-8 for DAP.
|
||||
char *utf8 = sjis2utf(buf, 0);
|
||||
free(buf);
|
||||
buf = utf8;
|
||||
#endif
|
||||
emit_output_event(log, buf);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
+66
-1
@@ -707,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);
|
||||
@@ -778,6 +788,7 @@ void gfx_copy_rot_zoom_use_amap(Texture *dst, Texture *src, int sx, int sy, int
|
||||
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
|
||||
@@ -795,7 +806,8 @@ void gfx_copy_rot_zoom2(Texture *dst, float cx, float cy, Texture *src, float sc
|
||||
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_noblend_shader.s, dst, src, mw_transform, wv_transform, &data);
|
||||
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)
|
||||
@@ -925,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);
|
||||
@@ -1065,6 +1094,33 @@ void gfx_copy_grayscale(Texture *dst, int dx, int dy, Texture *src, int sx, int
|
||||
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;
|
||||
@@ -1117,6 +1173,15 @@ void gfx_draw_line_to_amap(Texture *dst, int x0, int y0, int x1, int y1, int a)
|
||||
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)
|
||||
{
|
||||
|
||||
+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;
|
||||
}
|
||||
|
||||
+394
-65
@@ -30,6 +30,7 @@
|
||||
#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"
|
||||
@@ -43,49 +44,55 @@
|
||||
#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.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);
|
||||
|
||||
sprite_bind_plugin(sp, &ctx->plugin);
|
||||
|
||||
gfx_init_texture_blank(&ctx->texture, sp->rect.w, sp->rect.h);
|
||||
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, sp->rect.w, sp->rect.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);
|
||||
@@ -95,7 +102,7 @@ void dungeon_context_free(struct dungeon_context *ctx)
|
||||
if (ctx->map)
|
||||
dungeon_map_free(ctx->map);
|
||||
|
||||
free(ctx);
|
||||
xfree_aligned(ctx);
|
||||
}
|
||||
|
||||
static GLuint *load_event_textures(int *nr_textures_out)
|
||||
@@ -161,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);
|
||||
@@ -175,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);
|
||||
@@ -183,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);
|
||||
}
|
||||
|
||||
@@ -223,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);
|
||||
@@ -234,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);
|
||||
@@ -242,22 +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, vec3 eye_out, mat4 mv_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),
|
||||
};
|
||||
glm_vec3_sub(camera->pos, d, eye_out);
|
||||
vec3 up = {0.0, 1.0, 0.0};
|
||||
glm_lookat(eye_out, camera->pos, up, mv_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)
|
||||
@@ -271,23 +388,28 @@ static void dungeon_render(struct sact_sprite *sp)
|
||||
if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
ERROR("Incomplete framebuffer");
|
||||
|
||||
glClearColor(0.f, 0.f, 0.f, 1.f);
|
||||
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);
|
||||
|
||||
vec3 eye;
|
||||
mat4 local_transform, view_transform, proj_transform;
|
||||
glm_mat4_identity(local_transform);
|
||||
model_view_matrix(&ctx->camera, eye, view_transform);
|
||||
glm_perspective(M_PI / 3.0, (float)ctx->texture.w / ctx->texture.h, 0.5, 100.0, proj_transform);
|
||||
mat4 view_transform;
|
||||
model_view_matrix(ctx, view_transform);
|
||||
|
||||
int dgn_x = round(eye[0] / 2.0);
|
||||
int dgn_y = round(eye[1] / 2.0);
|
||||
int dgn_z = round(eye[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);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
@@ -301,9 +423,23 @@ static void dungeon_render(struct sact_sprite *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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,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)
|
||||
@@ -323,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,7 +505,7 @@ int dungeon_calc_conquer(int surface)
|
||||
/*
|
||||
* WalkData serialization format:
|
||||
*
|
||||
* struct WalkData {
|
||||
* struct DrawDungeon1_WalkData {
|
||||
* int version; // zero
|
||||
* int nr_maps;
|
||||
* struct {
|
||||
@@ -354,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");
|
||||
|
||||
@@ -393,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);
|
||||
@@ -415,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");
|
||||
@@ -447,6 +655,127 @@ bool dungeon_save_walk_data(int surface, int map, struct page **page)
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
+257
-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,7 +96,9 @@ 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;
|
||||
@@ -84,13 +112,17 @@ struct raster_shader {
|
||||
};
|
||||
|
||||
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;
|
||||
@@ -118,6 +150,19 @@ struct dungeon_renderer {
|
||||
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)
|
||||
@@ -221,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);
|
||||
@@ -342,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);
|
||||
@@ -359,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)
|
||||
@@ -371,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;
|
||||
}
|
||||
|
||||
@@ -418,14 +501,26 @@ void dungeon_renderer_free(struct dungeon_renderer *r)
|
||||
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);
|
||||
@@ -443,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);
|
||||
}
|
||||
|
||||
@@ -490,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);
|
||||
}
|
||||
|
||||
@@ -533,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(
|
||||
@@ -541,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(
|
||||
@@ -563,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(
|
||||
@@ -574,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(
|
||||
@@ -585,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(
|
||||
@@ -596,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(
|
||||
@@ -610,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(
|
||||
@@ -621,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(
|
||||
@@ -632,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(
|
||||
@@ -643,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;
|
||||
@@ -671,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,
|
||||
@@ -689,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);
|
||||
@@ -703,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);
|
||||
|
||||
@@ -791,3 +983,25 @@ 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);
|
||||
|
||||
|
||||
+25
-3
@@ -75,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 {
|
||||
@@ -124,12 +126,13 @@ static void load_effect_shader(struct effect_shader *s)
|
||||
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
|
||||
};
|
||||
@@ -143,6 +146,7 @@ static void render_effect_shader(struct effect_shader *shader, Texture *dst, Tex
|
||||
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");
|
||||
@@ -153,11 +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");
|
||||
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,
|
||||
@@ -168,6 +175,8 @@ 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 void effect_fadeout(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
@@ -192,6 +201,12 @@ static void effect_whitein(Texture *dst, Texture *old, Texture *new, float rate)
|
||||
gfx_blend(dst, 0, 0, new, 0, 0, dst->w, dst->h, rate * 255);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
unsigned x_pos = roundf(dst->w * rate);
|
||||
@@ -259,6 +274,7 @@ static effect_fun effect_functions[NR_EFFECTS] = {
|
||||
[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 +290,10 @@ void effect_update_texture(int type, Texture *dst, Texture *old, Texture *new, f
|
||||
} 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);
|
||||
s = effect_shaders[EFFECT_BLUR_CROSSFADE];
|
||||
}
|
||||
if (!s->s.program)
|
||||
@@ -296,7 +315,10 @@ int effect_init(enum effect 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];
|
||||
}
|
||||
|
||||
@@ -303,13 +303,13 @@ void hll_call(int libno, int fno)
|
||||
if (!fun->fun)
|
||||
VM_ERROR("Unimplemented HLL function: %s.%s", ain->libraries[libno].name, f->name);
|
||||
|
||||
// XXX: Try to prevent the heap from being reallocated mid-call.
|
||||
// This only works to the extent that HLL functions can guarantee
|
||||
// no more than 64 heap allocations occur within the call...
|
||||
heap_guarantee(64);
|
||||
|
||||
void *args[HLL_MAX_ARGS];
|
||||
void *ptrs[HLL_MAX_ARGS];
|
||||
// Copy reference arguments to the stack to protect against heap
|
||||
// reallocation during HLL calls.
|
||||
void *heap_ptrs[HLL_MAX_ARGS];
|
||||
int heap_slots[HLL_MAX_ARGS];
|
||||
|
||||
for (int i = f->nr_arguments - 1; i >= 0; i--) {
|
||||
switch (f->arguments[i].type.data) {
|
||||
case AIN_REF_INT:
|
||||
@@ -330,7 +330,9 @@ void hll_call(int libno, int fno)
|
||||
break;
|
||||
case AIN_REF_STRING:
|
||||
stack_ptr--;
|
||||
ptrs[i] = &heap[stack[stack_ptr].i].s;
|
||||
heap_slots[i] = stack[stack_ptr].i;
|
||||
heap_ptrs[i] = heap[stack[stack_ptr].i].s;
|
||||
ptrs[i] = &heap_ptrs[i];
|
||||
args[i] = &ptrs[i];
|
||||
break;
|
||||
case AIN_STRUCT:
|
||||
@@ -341,7 +343,9 @@ void hll_call(int libno, int fno)
|
||||
case AIN_REF_STRUCT:
|
||||
case AIN_REF_ARRAY_TYPE:
|
||||
stack_ptr--;
|
||||
ptrs[i] = &heap[stack[stack_ptr].i].page;
|
||||
heap_slots[i] = stack[stack_ptr].i;
|
||||
heap_ptrs[i] = heap[stack[stack_ptr].i].page;
|
||||
ptrs[i] = &heap_ptrs[i];
|
||||
args[i] = &ptrs[i];
|
||||
break;
|
||||
default:
|
||||
@@ -370,9 +374,13 @@ void hll_call(int libno, int fno)
|
||||
j++;
|
||||
break;
|
||||
case AIN_REF_STRING:
|
||||
heap[heap_slots[i]].s = heap_ptrs[i];
|
||||
break;
|
||||
case AIN_REF_STRUCT:
|
||||
case AIN_REF_FUNC_TYPE:
|
||||
case AIN_REF_ARRAY_TYPE:
|
||||
heap[heap_slots[i]].page = heap_ptrs[i];
|
||||
break;
|
||||
case AIN_REF_FUNC_TYPE:
|
||||
break;
|
||||
case AIN_ARRAY_TYPE:
|
||||
// Sys41VM doesn't make a copy when passing an array by value.
|
||||
@@ -380,32 +388,46 @@ void hll_call(int libno, int fno)
|
||||
break;
|
||||
// fallthrough
|
||||
default:
|
||||
variable_fini(stack[stack_ptr+j], f->arguments[i].type.data);
|
||||
variable_fini(stack[stack_ptr+j], f->arguments[i].type.data, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (f->return_type.data != AIN_VOID) {
|
||||
if (f->return_type.data == AIN_STRING) {
|
||||
int slot = heap_alloc_slot(VM_STRING);
|
||||
heap[slot].s = r.ref;
|
||||
stack_push(slot);
|
||||
} else {
|
||||
stack_push(r);
|
||||
}
|
||||
int slot;
|
||||
switch (f->return_type.data) {
|
||||
case AIN_VOID:
|
||||
break;
|
||||
case AIN_STRING:
|
||||
slot = heap_alloc_slot(VM_STRING);
|
||||
heap[slot].s = r.ref;
|
||||
stack_push(slot);
|
||||
break;
|
||||
case AIN_BOOL:
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
stack_push(*(bool*)&r);
|
||||
#pragma GCC diagnostic pop
|
||||
break;
|
||||
default:
|
||||
stack_push(r);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
extern struct static_library lib_ACXLoader;
|
||||
extern struct static_library lib_ACXLoaderP2;
|
||||
extern struct static_library lib_ADVSYS;
|
||||
extern struct static_library lib_AFAFactory;
|
||||
extern struct static_library lib_AliceLogo;
|
||||
extern struct static_library lib_AliceLogo2;
|
||||
extern struct static_library lib_AliceLogo3;
|
||||
extern struct static_library lib_AliceLogo4;
|
||||
extern struct static_library lib_AliceLogo5;
|
||||
extern struct static_library lib_AnteaterADVEngine;
|
||||
extern struct static_library lib_Array;
|
||||
extern struct static_library lib_BanMisc;
|
||||
extern struct static_library lib_Bitarray;
|
||||
extern struct static_library lib_CalcTable;
|
||||
extern struct static_library lib_CGManager;
|
||||
extern struct static_library lib_ChipmunkSpriteEngine;
|
||||
extern struct static_library lib_ChrLoader;
|
||||
@@ -419,14 +441,22 @@ extern struct static_library lib_DALKDemo;
|
||||
extern struct static_library lib_DALKEDemo;
|
||||
extern struct static_library lib_Data;
|
||||
extern struct static_library lib_DataFile;
|
||||
extern struct static_library lib_Discord;
|
||||
extern struct static_library lib_DrawDungeon;
|
||||
extern struct static_library lib_DrawDungeon2;
|
||||
extern struct static_library lib_DrawDungeon14;
|
||||
extern struct static_library lib_DrawEffect;
|
||||
extern struct static_library lib_DrawField;
|
||||
extern struct static_library lib_DrawGraph;
|
||||
extern struct static_library lib_DrawMovie;
|
||||
extern struct static_library lib_DrawMovie2;
|
||||
extern struct static_library lib_DrawMovie3;
|
||||
extern struct static_library lib_DrawNumeral;
|
||||
extern struct static_library lib_DrawPluginManager;
|
||||
extern struct static_library lib_DrawRain;
|
||||
extern struct static_library lib_DrawRipple;
|
||||
extern struct static_library lib_DrawSimpleText;
|
||||
extern struct static_library lib_DrawSnow;
|
||||
extern struct static_library lib_File;
|
||||
extern struct static_library lib_File2;
|
||||
extern struct static_library lib_FileOperation;
|
||||
@@ -434,11 +464,13 @@ extern struct static_library lib_FillAngle;
|
||||
extern struct static_library lib_GoatGUIEngine;
|
||||
extern struct static_library lib_Gpx2Plus;
|
||||
extern struct static_library lib_GUIEngine;
|
||||
extern struct static_library lib_HTTPDownloader;
|
||||
extern struct static_library lib_IbisInputEngine;
|
||||
extern struct static_library lib_InputDevice;
|
||||
extern struct static_library lib_InputString;
|
||||
extern struct static_library lib_KiwiSoundEngine;
|
||||
extern struct static_library lib_LoadCG;
|
||||
extern struct static_library lib_MADLoader;
|
||||
extern struct static_library lib_MainEXFile;
|
||||
extern struct static_library lib_MainSurface;
|
||||
extern struct static_library lib_MamanyoDemo;
|
||||
@@ -451,9 +483,13 @@ extern struct static_library lib_MonsterInfo;
|
||||
extern struct static_library lib_MsgLogManager;
|
||||
extern struct static_library lib_MsgLogViewer;
|
||||
extern struct static_library lib_MsgSkip;
|
||||
extern struct static_library lib_MusicSystem;
|
||||
extern struct static_library lib_NewFont;
|
||||
extern struct static_library lib_OutputLog;
|
||||
extern struct static_library lib_P3MapSprite;
|
||||
extern struct static_library lib_P3SquareSprite;
|
||||
extern struct static_library lib_PassRegister;
|
||||
extern struct static_library lib_PastelChime2;
|
||||
extern struct static_library lib_PartsEngine;
|
||||
extern struct static_library lib_PixelRestore;
|
||||
extern struct static_library lib_PlayDemo;
|
||||
@@ -461,6 +497,7 @@ extern struct static_library lib_PlayMovie;
|
||||
extern struct static_library lib_ReignEngine;
|
||||
extern struct static_library lib_SACT2;
|
||||
extern struct static_library lib_SACTDX;
|
||||
extern struct static_library lib_SealEngine;
|
||||
extern struct static_library lib_SengokuRanceFont;
|
||||
extern struct static_library lib_Sound2ex;
|
||||
extern struct static_library lib_SoundFilePlayer;
|
||||
@@ -496,19 +533,24 @@ extern struct static_library lib_vmString;
|
||||
extern struct static_library lib_vmSurface;
|
||||
extern struct static_library lib_vmSystem;
|
||||
extern struct static_library lib_vmTimer;
|
||||
extern struct static_library lib_ValueEncryption;
|
||||
extern struct static_library lib_VSFile;
|
||||
|
||||
static struct static_library *static_libraries[] = {
|
||||
&lib_ACXLoader,
|
||||
&lib_ACXLoaderP2,
|
||||
&lib_ADVSYS,
|
||||
&lib_AFAFactory,
|
||||
&lib_AliceLogo,
|
||||
&lib_AliceLogo2,
|
||||
&lib_AliceLogo3,
|
||||
&lib_AliceLogo4,
|
||||
&lib_AliceLogo5,
|
||||
&lib_AnteaterADVEngine,
|
||||
&lib_Array,
|
||||
&lib_BanMisc,
|
||||
&lib_Bitarray,
|
||||
&lib_CalcTable,
|
||||
&lib_CGManager,
|
||||
&lib_ChipmunkSpriteEngine,
|
||||
&lib_ChrLoader,
|
||||
@@ -522,14 +564,22 @@ static struct static_library *static_libraries[] = {
|
||||
&lib_DALKEDemo,
|
||||
&lib_Data,
|
||||
&lib_DataFile,
|
||||
&lib_Discord,
|
||||
&lib_DrawDungeon,
|
||||
&lib_DrawDungeon2,
|
||||
&lib_DrawDungeon14,
|
||||
&lib_DrawEffect,
|
||||
&lib_DrawField,
|
||||
&lib_DrawGraph,
|
||||
&lib_DrawMovie,
|
||||
&lib_DrawMovie2,
|
||||
&lib_DrawMovie3,
|
||||
&lib_DrawNumeral,
|
||||
&lib_DrawPluginManager,
|
||||
&lib_DrawRain,
|
||||
&lib_DrawRipple,
|
||||
&lib_DrawSimpleText,
|
||||
&lib_DrawSnow,
|
||||
&lib_File,
|
||||
&lib_File2,
|
||||
&lib_FileOperation,
|
||||
@@ -537,11 +587,13 @@ static struct static_library *static_libraries[] = {
|
||||
&lib_GoatGUIEngine,
|
||||
&lib_Gpx2Plus,
|
||||
&lib_GUIEngine,
|
||||
&lib_HTTPDownloader,
|
||||
&lib_IbisInputEngine,
|
||||
&lib_InputDevice,
|
||||
&lib_InputString,
|
||||
&lib_KiwiSoundEngine,
|
||||
&lib_LoadCG,
|
||||
&lib_MADLoader,
|
||||
&lib_MainEXFile,
|
||||
&lib_MainSurface,
|
||||
&lib_MamanyoDemo,
|
||||
@@ -554,9 +606,13 @@ static struct static_library *static_libraries[] = {
|
||||
&lib_MsgLogManager,
|
||||
&lib_MsgLogViewer,
|
||||
&lib_MsgSkip,
|
||||
&lib_MusicSystem,
|
||||
&lib_NewFont,
|
||||
&lib_OutputLog,
|
||||
&lib_P3MapSprite,
|
||||
&lib_P3SquareSprite,
|
||||
&lib_PassRegister,
|
||||
&lib_PastelChime2,
|
||||
&lib_PartsEngine,
|
||||
&lib_PixelRestore,
|
||||
&lib_PlayDemo,
|
||||
@@ -564,6 +620,7 @@ static struct static_library *static_libraries[] = {
|
||||
&lib_ReignEngine,
|
||||
&lib_SACT2,
|
||||
&lib_SACTDX,
|
||||
&lib_SealEngine,
|
||||
&lib_SengokuRanceFont,
|
||||
&lib_Sound2ex,
|
||||
&lib_SoundFilePlayer,
|
||||
@@ -599,6 +656,7 @@ static struct static_library *static_libraries[] = {
|
||||
&lib_vmSurface,
|
||||
&lib_vmSystem,
|
||||
&lib_vmTimer,
|
||||
&lib_ValueEncryption,
|
||||
&lib_VSFile,
|
||||
NULL
|
||||
};
|
||||
|
||||
+13
-11
@@ -101,24 +101,26 @@ static struct fnl_bitmap_glyph *fnl_get_bitmap_glyph(struct fnl_bitmap_size *bit
|
||||
if (bitmap->glyphs[index])
|
||||
return bitmap->glyphs[index];
|
||||
|
||||
unsigned long data_size;
|
||||
uint8_t *data = fnl_glyph_data(bitmap->face->font->fnl, &bitmap->face->glyphs[index], &data_size);
|
||||
struct fnl_glyph *glyph = &bitmap->face->glyphs[index];
|
||||
uint8_t *data = fnl_glyph_data(bitmap->face->font->fnl, glyph);
|
||||
|
||||
const int glyph_w = (data_size*8) / bitmap->face->height;
|
||||
const int glyph_w = fnl_glyph_stride(glyph) * 8;
|
||||
const int glyph_h = bitmap->face->height;
|
||||
|
||||
struct fnl_bitmap_glyph *out = xmalloc(sizeof(struct fnl_bitmap_glyph));
|
||||
out->width = glyph_w;
|
||||
out->height = glyph_h;
|
||||
out->advance = bitmap->face->glyphs[index].real_width;
|
||||
out->pixels = xmalloc(glyph_w * glyph_h);
|
||||
out->advance = glyph->width;
|
||||
out->pixels = xcalloc(glyph_w, glyph_h);
|
||||
|
||||
// expand 1-bit bitmap to 8-bit
|
||||
for (unsigned i = 0; i < glyph_w * glyph_h; i++) {
|
||||
unsigned row = (glyph_h - 1) - i / glyph_w;
|
||||
unsigned col = i % glyph_w;
|
||||
bool on = data[i/8] & (1 << (7 - i % 8));
|
||||
out->pixels[row*glyph_w + col] = on ? 255 : 0;
|
||||
for (unsigned row = 0; row < glyph_h; row++) {
|
||||
uint8_t *dst = out->pixels + ((glyph_h - 1) - row) * glyph_w;
|
||||
for (unsigned col = 0; col < glyph->width; col++) {
|
||||
unsigned i = row * glyph_w + col;
|
||||
bool on = data[i/8] & (1 << (7 - i % 8));
|
||||
dst[col] = on ? 255 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
free(data);
|
||||
@@ -183,7 +185,7 @@ static float fnl_font_size_char(struct font_size *_size, uint32_t code)
|
||||
return 0.0;
|
||||
if (!size->bitmap_size->face->glyphs[index].data_pos)
|
||||
return 0.0;
|
||||
float r = (float)size->bitmap_size->face->glyphs[index].real_width / (float)size->denominator;
|
||||
float r = (float)size->bitmap_size->face->glyphs[index].width / (float)size->denominator;
|
||||
if (code == '\t')
|
||||
r *= 2.88;
|
||||
return r;
|
||||
|
||||
+10
-2
@@ -145,10 +145,18 @@ static void append_fmt(struct string **s, struct fmt_spec *spec, union vm_value
|
||||
len = float_to_cstr(buf, DIGIT_MAX, arg.f, spec->padding, spec->zero_pad, spec->precision, spec->zenkaku);
|
||||
string_append_cstr(s, buf, len);
|
||||
break;
|
||||
case FMT_STRING:
|
||||
string_append(s, heap_get_string(arg.i));
|
||||
case FMT_STRING: {
|
||||
struct string *val = heap_get_string(arg.i);
|
||||
int pad_len = spec->padding - val->size;
|
||||
if (pad_len > 0) {
|
||||
for (int i = 0; i < pad_len; i++) {
|
||||
string_push_back(s, ' ');
|
||||
}
|
||||
}
|
||||
string_append(s, val);
|
||||
heap_unref(arg.i);
|
||||
break;
|
||||
}
|
||||
case FMT_CHAR:
|
||||
string_push_back(s, arg.i);
|
||||
break;
|
||||
|
||||
+114
-6
@@ -37,6 +37,8 @@ bool game_rance02_mg = false;
|
||||
bool game_rance6_mg = false;
|
||||
bool game_rance7_mg = false;
|
||||
bool game_rance8 = false;
|
||||
bool game_rance8_mg = false;
|
||||
bool game_dungeons_and_dolls = false;
|
||||
|
||||
static void write_instruction0(struct buffer *out, enum opcode op)
|
||||
{
|
||||
@@ -49,6 +51,15 @@ static void write_instruction1(struct buffer *out, enum opcode op, int32_t arg)
|
||||
buffer_write_int32(out, arg);
|
||||
}
|
||||
|
||||
static void write_instruction3(struct buffer *out, enum opcode op, int32_t arg0, int32_t arg1,
|
||||
int32_t arg2)
|
||||
{
|
||||
buffer_write_int16(out, op);
|
||||
buffer_write_int32(out, arg0);
|
||||
buffer_write_int32(out, arg1);
|
||||
buffer_write_int32(out, arg2);
|
||||
}
|
||||
|
||||
void set_msgskip_delay(struct ain *ain, unsigned ms)
|
||||
{
|
||||
int orig_fno = ain_get_function(ain, "A");
|
||||
@@ -173,11 +184,98 @@ static void apply_rance6_hacks(struct ain *ain)
|
||||
game_rance6_mg = true;
|
||||
}
|
||||
|
||||
static void apply_rance7_hacks(struct ain *ain)
|
||||
// Rance IX
|
||||
// --------
|
||||
// T_PartsNo class is implemented in an extremely inefficient way, which
|
||||
// causes noticable performance issues when using the UI (e.g. when loading
|
||||
// the items screen).
|
||||
// We rewrite this class to improve performance.
|
||||
static void apply_rance9_hacks(struct ain *ain)
|
||||
{
|
||||
if (ain_get_library(ain, "SengokuRanceFont") < 0)
|
||||
int add_fno = ain_get_function(ain, "T_PartsNo@add");
|
||||
int erase_fno = ain_get_function(ain, "T_PartsNo@erase");
|
||||
if (add_fno < 0 || erase_fno < 0) {
|
||||
WARNING("Couldn't location T_PartsNo@add or T_PartsNo@erase");
|
||||
return;
|
||||
game_rance7_mg = true;
|
||||
}
|
||||
|
||||
struct buffer out;
|
||||
buffer_init(&out, ain->code, ain->code_size);
|
||||
|
||||
// bool T_PartsNo::add(int nNo)
|
||||
// {
|
||||
// if (nNo < 10000 || nNo > 999999)
|
||||
// return false;
|
||||
// int idx = nNo - 10000;
|
||||
// if (this.m_abUsed[idx])
|
||||
// return false;
|
||||
// return (this.m_abUsed[idx] = true);
|
||||
// }
|
||||
int32_t return_false_addr[3];
|
||||
struct ain_function *f = &ain->functions[add_fno];
|
||||
buffer_seek(&out, f->address);
|
||||
// if (nNo < 10000 || nNo > 999999) return false;
|
||||
write_instruction3(&out, SH_IF_LOC_LT_IMM, 0, 10000, 0);
|
||||
return_false_addr[0] = out.index - 4;
|
||||
write_instruction3(&out, SH_IF_LOC_GT_IMM, 0, 999999, 0);
|
||||
return_false_addr[1] = out.index - 4;
|
||||
// int idx = nNo - 10000;
|
||||
write_instruction0(&out, PUSHLOCALPAGE);
|
||||
write_instruction1(&out, PUSH, 1);
|
||||
write_instruction1(&out, SH_LOCALREF, 0);
|
||||
write_instruction1(&out, PUSH, 10000);
|
||||
write_instruction0(&out, SUB);
|
||||
write_instruction0(&out, ASSIGN);
|
||||
write_instruction0(&out, POP);
|
||||
// if (this.m_abUsed[idx]) return false;
|
||||
write_instruction1(&out, SH_STRUCTREF, 1);
|
||||
write_instruction1(&out, SH_LOCALREF, 1);
|
||||
write_instruction0(&out, REF);
|
||||
write_instruction1(&out, IFNZ, 0);
|
||||
return_false_addr[2] = out.index - 4;
|
||||
// return (this.m_abUsed[idx] = true);
|
||||
write_instruction1(&out, SH_STRUCTREF, 1);
|
||||
write_instruction1(&out, SH_LOCALREF, 1);
|
||||
write_instruction1(&out, PUSH, 1);
|
||||
write_instruction0(&out, ASSIGN);
|
||||
write_instruction0(&out, RETURN);
|
||||
// [return false]
|
||||
buffer_write_int32_at(&out, return_false_addr[0], out.index);
|
||||
buffer_write_int32_at(&out, return_false_addr[1], out.index);
|
||||
buffer_write_int32_at(&out, return_false_addr[2], out.index);
|
||||
write_instruction1(&out, PUSH, 0);
|
||||
write_instruction0(&out, RETURN);
|
||||
|
||||
// bool T_PartsNo::erase(int nNo)
|
||||
// {
|
||||
// if (nNo < 10000 || nNo > 999999)
|
||||
// return false;
|
||||
// this.m_abUsed[nNo - 10000] = false;
|
||||
// return true;
|
||||
// }
|
||||
f = &ain->functions[erase_fno];
|
||||
buffer_seek(&out, f->address);
|
||||
// if (nNo < 10000 || nNo > 999999) return false;
|
||||
write_instruction3(&out, SH_IF_LOC_LT_IMM, 0, 10000, 0);
|
||||
return_false_addr[0] = out.index - 4;
|
||||
write_instruction3(&out, SH_IF_LOC_GT_IMM, 0, 999999, 0);
|
||||
return_false_addr[1] = out.index - 4;
|
||||
// this.m_abUsed[nNo - 10000] = false;
|
||||
write_instruction1(&out, SH_STRUCTREF, 1);
|
||||
write_instruction1(&out, SH_LOCALREF, 0);
|
||||
write_instruction1(&out, PUSH, 10000);
|
||||
write_instruction0(&out, SUB);
|
||||
write_instruction1(&out, PUSH, 0);
|
||||
write_instruction0(&out, ASSIGN);
|
||||
write_instruction0(&out, POP);
|
||||
// return true;
|
||||
write_instruction1(&out, PUSH, 1);
|
||||
write_instruction0(&out, RETURN);
|
||||
// [return false]
|
||||
buffer_write_int32_at(&out, return_false_addr[0], out.index);
|
||||
buffer_write_int32_at(&out, return_false_addr[1], out.index);
|
||||
write_instruction1(&out, PUSH, 0);
|
||||
write_instruction0(&out, RETURN);
|
||||
}
|
||||
|
||||
// Daibanchou (English fan translation)
|
||||
@@ -201,10 +299,20 @@ void apply_game_specific_hacks(struct ain *ain)
|
||||
apply_rance02_hacks(ain);
|
||||
} else if (!strcmp(game_name, "Rance6")) {
|
||||
apply_rance6_hacks(ain);
|
||||
} else if (!strcmp(game_name, "Sengoku Rance")) {
|
||||
apply_rance7_hacks(ain);
|
||||
} else if (!strcmp(game_name, "ランス・クエスト") || !strcmp(game_name, "Rance Quest")) {
|
||||
} else if (!strcmp(game_name, "ランス・クエスト")) {
|
||||
game_rance8 = true;
|
||||
} else if (!strcmp(game_name, "Rance Quest")) {
|
||||
game_rance8 = true;
|
||||
game_rance8_mg = true;
|
||||
} else if (!strcmp(game_name, "DUNGEONS&DOLLS")) {
|
||||
game_dungeons_and_dolls = true;
|
||||
} else if (!strcmp(game_name, "ランス9")
|
||||
|| !strcmp(game_name, "Rance 9 - The Helmanian Revolution")) {
|
||||
apply_rance9_hacks(ain);
|
||||
}
|
||||
free(game_name);
|
||||
|
||||
// XXX: we don't rely on game_name because mods change it
|
||||
if (ain_get_library(ain, "SengokuRanceFont") >= 0)
|
||||
game_rance7_mg = true;
|
||||
}
|
||||
|
||||
-13
@@ -60,19 +60,6 @@ void heap_grow(size_t new_size)
|
||||
heap_size = new_size;
|
||||
}
|
||||
|
||||
void heap_guarantee(unsigned headroom)
|
||||
{
|
||||
if (heap_size - heap_free_ptr >= headroom)
|
||||
return;
|
||||
|
||||
size_t new_size = heap_size;
|
||||
while (new_size - heap_free_ptr < headroom) {
|
||||
new_size += HEAP_ALLOC_STEP;
|
||||
}
|
||||
|
||||
heap_grow(new_size);
|
||||
}
|
||||
|
||||
void heap_init(void)
|
||||
{
|
||||
if (!heap) {
|
||||
|
||||
+48
-3
@@ -17,8 +17,6 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/acx.h"
|
||||
@@ -30,7 +28,7 @@
|
||||
#include "vm/page.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
struct acx *acx = NULL;
|
||||
static struct acx *acx = NULL;
|
||||
|
||||
static bool ACXLoader_Load(struct string *filename)
|
||||
{
|
||||
@@ -131,3 +129,50 @@ HLL_LIBRARY(ACXLoader,
|
||||
HLL_EXPORT(GetDataInt, ACXLoader_GetDataInt),
|
||||
HLL_EXPORT(GetDataString, ACXLoader_GetDataString),
|
||||
HLL_EXPORT(GetDataStruct, ACXLoader_GetDataStruct));
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED(, void, ACXLoaderP2, GetError, int *nError, struct string **szErrorString);
|
||||
|
||||
static bool ACXLoaderP2_GetDataIntByKey(int key, int col, int *ptr)
|
||||
{
|
||||
if (!acx)
|
||||
return false;
|
||||
if (col < 0 || col >= acx->nr_columns)
|
||||
return false;
|
||||
for (int line = 0; line < acx->nr_lines; line++) {
|
||||
if (key == acx_get_int(acx, line, 0)) {
|
||||
*ptr = acx_get_int(acx, line, col);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ACXLoaderP2_GetDataStringByKey(int key, int col, struct string **ptr)
|
||||
{
|
||||
if (!acx)
|
||||
return false;
|
||||
if (col < 0 || col >= acx->nr_columns)
|
||||
return false;
|
||||
for (int line = 0; line < acx->nr_lines; line++) {
|
||||
if (key == acx_get_int(acx, line, 0)) {
|
||||
if (*ptr)
|
||||
free_string(*ptr);
|
||||
*ptr = string_ref(acx_get_string(acx, line, col));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
HLL_LIBRARY(ACXLoaderP2,
|
||||
HLL_EXPORT(Load, ACXLoader_Load),
|
||||
HLL_EXPORT(Release, ACXLoader_Release),
|
||||
HLL_EXPORT(GetNumofLine, ACXLoader_GetNumofLine),
|
||||
HLL_EXPORT(GetNumofColumn, ACXLoader_GetNumofColumn),
|
||||
HLL_EXPORT(GetDataInt, ACXLoader_GetDataInt),
|
||||
HLL_EXPORT(GetDataString, ACXLoader_GetDataString),
|
||||
HLL_EXPORT(GetDataStruct, ACXLoader_GetDataStruct),
|
||||
HLL_EXPORT(GetError, ACXLoaderP2_GetError),
|
||||
HLL_EXPORT(GetDataIntByKey, ACXLoaderP2_GetDataIntByKey),
|
||||
HLL_EXPORT(GetDataStringByKey, ACXLoaderP2_GetDataStringByKey)
|
||||
);
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/* 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 "system4/string.h"
|
||||
#include "asset_manager.h"
|
||||
#include "hll.h"
|
||||
|
||||
static enum asset_type get_asset_type(int type)
|
||||
{
|
||||
switch (type) {
|
||||
case 2: return ASSET_CG;
|
||||
case 3: return ASSET_FLAT;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED(false, bool, AFAFactory, IsExistArchive, struct string *ArchiveName);
|
||||
|
||||
bool AFAFactory_LoadArchive(int type, struct string *archive_name)
|
||||
{
|
||||
enum asset_type t = get_asset_type(type);
|
||||
if (t < 0) {
|
||||
WARNING("Unknown asset type: %d", type);
|
||||
return false;
|
||||
}
|
||||
return asset_manager_load_archive(t, archive_name->text);
|
||||
}
|
||||
|
||||
//int AFAFactory_GetCountOfData(int Type);
|
||||
//void AFAFactory_GetTitleByIndex(int Type, int Index, struct string **Name);
|
||||
//int AFAFactory_SearchTitle(int Type, struct string *Name);
|
||||
//int AFAFactory_PrefixSearchTitle(int Type, struct string *Name);
|
||||
//int AFAFactory_SuffixSearchTitle(int Type, struct string *Name);
|
||||
//int AFAFactory_GetCountOfSearchData(int Type);
|
||||
//void AFAFactory_GetSearchTitleByIndex(int Type, int Index, struct string **Name);
|
||||
|
||||
HLL_LIBRARY(AFAFactory,
|
||||
HLL_EXPORT(IsExistArchive, AFAFactory_IsExistArchive),
|
||||
HLL_EXPORT(LoadArchive, AFAFactory_LoadArchive),
|
||||
HLL_TODO_EXPORT(GetCountOfData, AFAFactory_GetCountOfData),
|
||||
HLL_TODO_EXPORT(GetTitleByIndex, AFAFactory_GetTitleByIndex),
|
||||
HLL_TODO_EXPORT(SearchTitle, AFAFactory_SearchTitle),
|
||||
HLL_TODO_EXPORT(PrefixSearchTitle, AFAFactory_PrefixSearchTitle),
|
||||
HLL_TODO_EXPORT(SuffixSearchTitle, AFAFactory_SuffixSearchTitle),
|
||||
HLL_TODO_EXPORT(GetCountOfSearchData, AFAFactory_GetCountOfSearchData),
|
||||
HLL_TODO_EXPORT(GetSearchTitleByIndex, AFAFactory_GetSearchTitleByIndex)
|
||||
);
|
||||
+122
-59
@@ -40,8 +40,12 @@ struct adv_log {
|
||||
};
|
||||
|
||||
static bool enabled = true;
|
||||
static unsigned nr_logs = 0;
|
||||
struct adv_log *logs = NULL;
|
||||
|
||||
// A circular buffer of logs.
|
||||
#define LOG_BUFFER_SIZE 1000
|
||||
static struct adv_log *logs = NULL;
|
||||
static unsigned log_first;
|
||||
static unsigned log_last;
|
||||
|
||||
static void AnteaterADVEngine_PreLink(void);
|
||||
|
||||
@@ -52,20 +56,33 @@ static void init_log(struct adv_log *log)
|
||||
log->nr_lines = 1;
|
||||
}
|
||||
|
||||
static void free_log(struct adv_log *log)
|
||||
{
|
||||
for (unsigned i = 0; i < log->nr_lines; i++) {
|
||||
free_string(log->lines[i]);
|
||||
}
|
||||
free(log->lines);
|
||||
free(log->voices);
|
||||
log->nr_lines = 0;
|
||||
log->lines = NULL;
|
||||
log->nr_voices = 0;
|
||||
log->voices = NULL;
|
||||
}
|
||||
|
||||
// Ensure there is at least one log allocated
|
||||
static void ensure_log(void)
|
||||
{
|
||||
if (!nr_logs) {
|
||||
logs = xcalloc(1, sizeof(struct adv_log));
|
||||
if (!logs) {
|
||||
logs = xcalloc(LOG_BUFFER_SIZE, sizeof(struct adv_log));
|
||||
init_log(&logs[0]);
|
||||
nr_logs = 1;
|
||||
log_first = log_last = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static struct adv_log *current_log()
|
||||
{
|
||||
ensure_log();
|
||||
return &logs[nr_logs-1];
|
||||
return &logs[log_last];
|
||||
}
|
||||
|
||||
static struct string **current_line(struct adv_log *log)
|
||||
@@ -77,17 +94,14 @@ static struct string **current_line(struct adv_log *log)
|
||||
void ADVLogList_Clear(void)
|
||||
{
|
||||
// XXX: this function works even if logging is disabled
|
||||
for (unsigned i = 0; i < nr_logs; i++) {
|
||||
struct adv_log *log = &logs[i];
|
||||
for (unsigned i = 0; i < log->nr_lines; i++) {
|
||||
free_string(log->lines[i]);
|
||||
}
|
||||
free(log->lines);
|
||||
free(log->voices);
|
||||
if (!logs) return;
|
||||
for (unsigned i = log_first;; i = (i + 1) % LOG_BUFFER_SIZE) {
|
||||
free_log(&logs[i]);
|
||||
if (i == log_last)
|
||||
break;
|
||||
}
|
||||
free(logs);
|
||||
logs = NULL;
|
||||
nr_logs = 0;
|
||||
}
|
||||
|
||||
void ADVLogList_AddText(struct string **text)
|
||||
@@ -108,8 +122,12 @@ void ADVLogList_AddNewLine(void)
|
||||
void ADVLogList_AddNewPage(void)
|
||||
{
|
||||
if (!enabled) return;
|
||||
logs = xrealloc_array(logs, nr_logs, nr_logs + 1, sizeof(struct adv_log));
|
||||
init_log(&logs[nr_logs++]);
|
||||
log_last = (log_last + 1) % LOG_BUFFER_SIZE;
|
||||
if (log_last == log_first) {
|
||||
free_log(&logs[log_first]);
|
||||
log_first = (log_first + 1) % LOG_BUFFER_SIZE;
|
||||
}
|
||||
init_log(&logs[log_last]);
|
||||
}
|
||||
|
||||
void ADVLogList_AddVoice(int voice_no)
|
||||
@@ -132,55 +150,55 @@ bool ADVLogList_IsEnable(void)
|
||||
|
||||
int ADVLogList_GetNumofADVLog(void)
|
||||
{
|
||||
return nr_logs ? nr_logs : 1;
|
||||
if (!logs) return 1;
|
||||
return (log_last - log_first + LOG_BUFFER_SIZE) % LOG_BUFFER_SIZE + 1;
|
||||
}
|
||||
|
||||
static void check_log_nr(int log_no)
|
||||
static struct adv_log *log_entry(int log_no)
|
||||
{
|
||||
// NOTE: AnteaterADVEngine.dll segfaults on invalid index
|
||||
ensure_log();
|
||||
if (log_no < 0 || (unsigned)log_no >= nr_logs)
|
||||
if (log_no < 0 || (unsigned)log_no >= ADVLogList_GetNumofADVLog())
|
||||
VM_ERROR("Invalid log number: %d", log_no);
|
||||
return &logs[(log_first + log_no) % LOG_BUFFER_SIZE];
|
||||
}
|
||||
|
||||
int ADVLogList_GetNumofADVLogText(int log_no)
|
||||
{
|
||||
check_log_nr(log_no);
|
||||
return logs[log_no].nr_lines;
|
||||
return log_entry(log_no)->nr_lines;
|
||||
}
|
||||
|
||||
void ADVLogList_GetADVLogText(int log_no, int line_no, struct string **text)
|
||||
{
|
||||
check_log_nr(log_no);
|
||||
struct adv_log *log = log_entry(log_no);
|
||||
// NOTE: AnteaterADVEngine.dll segfaults on invalid index
|
||||
if (line_no < 0 || (unsigned)line_no >= logs[log_no].nr_lines)
|
||||
if (line_no < 0 || (unsigned)line_no >= log->nr_lines)
|
||||
VM_ERROR("Invalid line number: %d", line_no);
|
||||
|
||||
*text = string_ref(logs[log_no].lines[line_no]);
|
||||
*text = string_ref(log->lines[line_no]);
|
||||
}
|
||||
|
||||
int ADVLogList_GetNumofADVLogVoice(int log_no)
|
||||
{
|
||||
check_log_nr(log_no);
|
||||
return logs[log_no].nr_voices;
|
||||
return log_entry(log_no)->nr_voices;
|
||||
}
|
||||
|
||||
int ADVLogList_GetADVLogVoice(int log_no, int voice_no)
|
||||
{
|
||||
check_log_nr(log_no);
|
||||
if (voice_no < 0 || (unsigned)voice_no >= logs[log_no].nr_voices)
|
||||
struct adv_log *log = log_entry(log_no);
|
||||
if (voice_no < 0 || (unsigned)voice_no >= log->nr_voices)
|
||||
VM_ERROR("Invalid voice number: %d", voice_no);
|
||||
|
||||
return logs[log_no].voices[voice_no];
|
||||
return log->voices[voice_no];
|
||||
}
|
||||
|
||||
int ADVLogList_GetADVLogVoiceLast(int log_no)
|
||||
{
|
||||
check_log_nr(log_no);
|
||||
unsigned nr_voices = logs[log_no].nr_voices;
|
||||
struct adv_log *log = log_entry(log_no);
|
||||
unsigned nr_voices = log->nr_voices;
|
||||
if (!nr_voices)
|
||||
return 0;
|
||||
return logs[log_no].voices[nr_voices - 1];
|
||||
return log->voices[nr_voices - 1];
|
||||
}
|
||||
|
||||
bool ADVLogList_Save(struct page **iarray)
|
||||
@@ -202,9 +220,10 @@ bool ADVLogList_Save(struct page **iarray)
|
||||
struct iarray_writer b;
|
||||
iarray_init_writer(&b, "ADL");
|
||||
iarray_write(&b, 0);
|
||||
int nr_logs = ADVLogList_GetNumofADVLog();
|
||||
iarray_write(&b, nr_logs);
|
||||
for (unsigned i = 0; i < nr_logs; i++) {
|
||||
struct adv_log *log = &logs[i];
|
||||
struct adv_log *log = &logs[(log_first + i) % LOG_BUFFER_SIZE];
|
||||
iarray_write(&b, log->nr_lines);
|
||||
for (unsigned i = 0; i < log->nr_lines; i++) {
|
||||
iarray_write_string(&b, log->lines[i]);
|
||||
@@ -243,20 +262,21 @@ bool ADVLogList_Load(struct page **data)
|
||||
goto error;
|
||||
}
|
||||
|
||||
logs = xcalloc(nr_pages + 1, sizeof(struct adv_log));
|
||||
// XXX: For whatever reason this function always creates a blank page 0,
|
||||
// and then loads the input pages after that. So a consecutive save
|
||||
// and load will increase all page numbers by 1.
|
||||
init_log(&logs[0]);
|
||||
ensure_log();
|
||||
|
||||
for (int i = 1; i < nr_pages + 1; i++) {
|
||||
struct adv_log *log = &logs[i];
|
||||
ADVLogList_AddNewPage();
|
||||
struct adv_log *log = current_log();
|
||||
int nr_lines = iarray_read(&r);
|
||||
if (nr_lines <= 0 || nr_lines > 10000) {
|
||||
WARNING("Invalid value for nr_lines[%d]: %d", i, nr_lines);
|
||||
goto error;
|
||||
}
|
||||
|
||||
free_log(log);
|
||||
log->lines = xcalloc(nr_lines, sizeof(struct string*));
|
||||
for (int i = 0; i < nr_lines; i++) {
|
||||
log->lines[i] = iarray_read_string(&r);
|
||||
@@ -275,7 +295,6 @@ bool ADVLogList_Load(struct page **data)
|
||||
}
|
||||
log->nr_voices = nr_voices;
|
||||
}
|
||||
nr_logs = nr_pages + 1;
|
||||
|
||||
if (iarray_read(&r) != 1 || r.error)
|
||||
goto error;
|
||||
@@ -285,10 +304,11 @@ error:
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct {
|
||||
unsigned n;
|
||||
struct page **data;
|
||||
} scenes = {0};
|
||||
#define SCENE_BUFFER_SIZE 500 // Maximum in Rance 02
|
||||
static struct page **scenes = NULL;
|
||||
static unsigned scene_first = 0;
|
||||
static unsigned scene_last = 0;
|
||||
|
||||
|
||||
static int scene_struct_no(void)
|
||||
{
|
||||
@@ -311,13 +331,25 @@ bool ADVSceneKeeper_AddADVScene(struct page **page)
|
||||
WARNING("Invalid struct type: %d", (*page)->index);
|
||||
return false;
|
||||
}
|
||||
scenes.data = xrealloc_array(scenes.data, scenes.n, scenes.n+1, sizeof(struct page*));
|
||||
|
||||
if (!scenes) {
|
||||
scenes = xcalloc(SCENE_BUFFER_SIZE, sizeof(struct page*));
|
||||
scene_first = 0;
|
||||
scene_last = 0;
|
||||
} else {
|
||||
scene_last = (scene_last + 1) % SCENE_BUFFER_SIZE;
|
||||
if (scene_last == scene_first) {
|
||||
free_page(scenes[scene_first]);
|
||||
scenes[scene_first] = NULL;
|
||||
scene_first = (scene_first + 1) % SCENE_BUFFER_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
// serialize
|
||||
struct iarray_writer out;
|
||||
iarray_init_writer(&out, "SCN");
|
||||
iarray_write_struct(&out, *page, false);
|
||||
scenes.data[scenes.n++] = iarray_to_page(&out);
|
||||
scenes[scene_last] = iarray_to_page(&out);
|
||||
iarray_free_writer(&out);
|
||||
|
||||
return true;
|
||||
@@ -325,17 +357,21 @@ bool ADVSceneKeeper_AddADVScene(struct page **page)
|
||||
|
||||
int ADVSceneKeeper_GetNumofADVScene(void)
|
||||
{
|
||||
return scenes.n;
|
||||
if (!scenes)
|
||||
return 0;
|
||||
return (scene_last - scene_first + SCENE_BUFFER_SIZE) % SCENE_BUFFER_SIZE + 1;
|
||||
}
|
||||
|
||||
bool ADVSceneKeeper_GetADVScene(int index, struct page **page)
|
||||
{
|
||||
if (index < 0 || (unsigned)index >= scenes.n)
|
||||
if (!scenes || index < 0 || (unsigned)index >= ADVSceneKeeper_GetNumofADVScene())
|
||||
return false;
|
||||
|
||||
unsigned i = (scene_first + index) % SCENE_BUFFER_SIZE;
|
||||
|
||||
// deserialize
|
||||
struct iarray_reader in;
|
||||
if (!iarray_init_reader(&in, scenes.data[index], "SCN"))
|
||||
if (!iarray_init_reader(&in, scenes[i], "SCN"))
|
||||
return false;
|
||||
|
||||
if (*page) {
|
||||
@@ -349,12 +385,16 @@ bool ADVSceneKeeper_GetADVScene(int index, struct page **page)
|
||||
|
||||
void ADVSceneKeeper_Clear(void)
|
||||
{
|
||||
for (unsigned i = 0; i < scenes.n; i++) {
|
||||
free_page(scenes.data[i]);
|
||||
if (!scenes)
|
||||
return;
|
||||
for (unsigned i = scene_first;; i = (i + 1) % SCENE_BUFFER_SIZE) {
|
||||
if (scenes[i])
|
||||
free_page(scenes[i]);
|
||||
if (i == scene_last)
|
||||
break;
|
||||
}
|
||||
free(scenes.data);
|
||||
scenes.data = NULL;
|
||||
scenes.n = 0;
|
||||
free(scenes);
|
||||
scenes = NULL;
|
||||
}
|
||||
|
||||
bool ADVSceneKeeper_Save(struct page **iarray)
|
||||
@@ -369,9 +409,11 @@ bool ADVSceneKeeper_Save(struct page **iarray)
|
||||
struct iarray_writer w;
|
||||
iarray_init_writer(&w, "SCS");
|
||||
iarray_write(&w, 0); // version
|
||||
iarray_write(&w, scenes.n);
|
||||
for (unsigned i = 0; i < scenes.n; i++) {
|
||||
iarray_write_array(&w, scenes.data[i]);
|
||||
int nr_scenes = ADVSceneKeeper_GetNumofADVScene();
|
||||
iarray_write(&w, nr_scenes);
|
||||
for (int i = 0; i < nr_scenes; i++) {
|
||||
unsigned idx = (scene_first + i) % SCENE_BUFFER_SIZE;
|
||||
iarray_write_array(&w, scenes[idx]);
|
||||
}
|
||||
*iarray = iarray_to_page(&w);
|
||||
iarray_free_writer(&w);
|
||||
@@ -397,15 +439,35 @@ bool ADVSceneKeeper_Load(struct page **iarray)
|
||||
if (nr_scenes < 0 || nr_scenes > 10000)
|
||||
goto error;
|
||||
|
||||
scenes.n = 0;
|
||||
scenes.data = xcalloc(nr_scenes, sizeof(struct page*));
|
||||
if (nr_scenes == 0)
|
||||
return true;
|
||||
|
||||
for (int i = 0; i < nr_scenes; i++) {
|
||||
struct ain_type type = { .data = AIN_ARRAY_INT, .struc = 0, .rank = 0 };
|
||||
scenes.data[scenes.n++] = iarray_read_array(&r, &type);
|
||||
int start_offset = 0;
|
||||
if (nr_scenes > SCENE_BUFFER_SIZE) {
|
||||
start_offset = nr_scenes - SCENE_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
struct ain_type type = { .data = AIN_ARRAY_INT, .struc = 0, .rank = 0 };
|
||||
for (int i = 0; i < start_offset; i++) {
|
||||
struct page *p = iarray_read_array(&r, &type);
|
||||
if (r.error)
|
||||
goto error;
|
||||
free_page(p);
|
||||
}
|
||||
|
||||
int scenes_to_load = nr_scenes - start_offset;
|
||||
if (scenes_to_load > 0) {
|
||||
scenes = xcalloc(SCENE_BUFFER_SIZE, sizeof(struct page*));
|
||||
scene_first = 0;
|
||||
scene_last = scenes_to_load - 1;
|
||||
|
||||
for (int i = 0; i < scenes_to_load; i++) {
|
||||
scenes[i] = iarray_read_array(&r, &type);
|
||||
if (r.error)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
error:
|
||||
ADVSceneKeeper_Clear();
|
||||
@@ -414,6 +476,7 @@ error:
|
||||
|
||||
static void AnteaterADVEngine_ModuleFini(void)
|
||||
{
|
||||
ADVLogList_Clear();
|
||||
ADVSceneKeeper_Clear();
|
||||
}
|
||||
|
||||
|
||||
+498
@@ -0,0 +1,498 @@
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
#include "hll.h"
|
||||
#include "vm/page.h"
|
||||
|
||||
static void check_array(struct page *array)
|
||||
{
|
||||
if (array->type != ARRAY_PAGE || array->a_type != AIN_ARRAY_INT || array->array.rank != 1)
|
||||
VM_ERROR("Not a flat integer array");
|
||||
}
|
||||
|
||||
//void Array_NV_copy(struct page **nArray, int nNum);
|
||||
//void Array_NV_add(struct page **nArray, int nNum);
|
||||
//void Array_NV_sub(struct page **nArray, int nNum);
|
||||
//void Array_NV_mul(struct page **nArray, int nNum);
|
||||
//void Array_NV_div(struct page **nArray, int nNum);
|
||||
//void Array_NV_and(struct page **nArray, int nNum);
|
||||
//void Array_NV_or(struct page **nArray, int nNum);
|
||||
//void Array_NV_xor(struct page **nArray, int nNum);
|
||||
//void Array_NV_min(struct page **nArray, int nNum);
|
||||
//void Array_NV_max(struct page **nArray, int nNum);
|
||||
//void Array_NN_copy(struct page **nArray, struct page *nArrayS);
|
||||
//void Array_NN_add(struct page **nArray, struct page *nArrayS);
|
||||
//void Array_NN_sub(struct page **nArray, struct page *nArrayS);
|
||||
//void Array_NN_mul(struct page **nArray, struct page *nArrayS);
|
||||
//void Array_NN_div(struct page **nArray, struct page *nArrayS);
|
||||
//void Array_NN_and(struct page **nArray, struct page *nArrayS);
|
||||
//void Array_NN_or(struct page **nArray, struct page *nArrayS);
|
||||
//void Array_NN_xor(struct page **nArray, struct page *nArrayS);
|
||||
//void Array_NN_min(struct page **nArray, struct page *nArrayS);
|
||||
//void Array_NN_max(struct page **nArray, struct page *nArrayS);
|
||||
//void Array_NS_copy(struct page **nArray, struct page *sArray, int nMember);
|
||||
//void Array_NS_add(struct page **nArray, struct page *sArray, int nMember);
|
||||
//void Array_NS_sub(struct page **nArray, struct page *sArray, int nMember);
|
||||
//void Array_NS_mul(struct page **nArray, struct page *sArray, int nMember);
|
||||
//void Array_NS_div(struct page **nArray, struct page *sArray, int nMember);
|
||||
//void Array_NS_and(struct page **nArray, struct page *sArray, int nMember);
|
||||
//void Array_NS_or(struct page **nArray, struct page *sArray, int nMember);
|
||||
//void Array_NS_xor(struct page **nArray, struct page *sArray, int nMember);
|
||||
//void Array_NS_min(struct page **nArray, struct page *sArray, int nMember);
|
||||
//void Array_NS_max(struct page **nArray, struct page *sArray, int nMember);
|
||||
//void Array_SV_copy(struct page **sArray, int nMember, int nNum);
|
||||
//void Array_SV_add(struct page **sArray, int nMember, int nNum);
|
||||
//void Array_SV_sub(struct page **sArray, int nMember, int nNum);
|
||||
//void Array_SV_mul(struct page **sArray, int nMember, int nNum);
|
||||
//void Array_SV_div(struct page **sArray, int nMember, int nNum);
|
||||
//void Array_SV_and(struct page **sArray, int nMember, int nNum);
|
||||
//void Array_SV_or(struct page **sArray, int nMember, int nNum);
|
||||
//void Array_SV_xor(struct page **sArray, int nMember, int nNum);
|
||||
//void Array_SV_min(struct page **sArray, int nMember, int nNum);
|
||||
//void Array_SV_max(struct page **sArray, int nMember, int nNum);
|
||||
//void Array_SN_copy(struct page **sArray, int nMember, struct page *nArray);
|
||||
//void Array_SN_add(struct page **sArray, int nMember, struct page *nArray);
|
||||
//void Array_SN_sub(struct page **sArray, int nMember, struct page *nArray);
|
||||
//void Array_SN_mul(struct page **sArray, int nMember, struct page *nArray);
|
||||
//void Array_SN_div(struct page **sArray, int nMember, struct page *nArray);
|
||||
//void Array_SN_and(struct page **sArray, int nMember, struct page *nArray);
|
||||
//void Array_SN_or(struct page **sArray, int nMember, struct page *nArray);
|
||||
//void Array_SN_xor(struct page **sArray, int nMember, struct page *nArray);
|
||||
//void Array_SN_min(struct page **sArray, int nMember, struct page *nArray);
|
||||
//void Array_SN_max(struct page **sArray, int nMember, struct page *nArray);
|
||||
//void Array_SS_copy(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//void Array_SS_add(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//void Array_SS_sub(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//void Array_SS_mul(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//void Array_SS_div(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//void Array_SS_and(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//void Array_SS_or(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//void Array_SS_xor(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//void Array_SS_min(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//void Array_SS_max(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
|
||||
static int Array_NV_eneq(struct page *array, int num)
|
||||
{
|
||||
if (!array)
|
||||
return 0;
|
||||
check_array(array);
|
||||
int count = 0;
|
||||
for (int i = 0; i < array->nr_vars; i++) {
|
||||
if (array->values[i].i == num)
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
//int Array_NV_enne(struct page *nArray, int nNum);
|
||||
//int Array_NV_enlo(struct page *nArray, int nNum);
|
||||
//int Array_NV_enhi(struct page *nArray, int nNum);
|
||||
//int Array_NV_enra(struct page *nArray, int nMin, int nMax);
|
||||
//int Array_NN_eneq(struct page *nArray, struct page *nArrayS);
|
||||
//int Array_NN_enne(struct page *nArray, struct page *nArrayS);
|
||||
//int Array_NN_enlo(struct page *nArray, struct page *nArrayS);
|
||||
//int Array_NN_enhi(struct page *nArray, struct page *nArrayS);
|
||||
//int Array_NS_eneq(struct page *nArray, struct page *sArray, int nMember);
|
||||
//int Array_NS_enne(struct page *nArray, struct page *sArray, int nMember);
|
||||
//int Array_NS_enlo(struct page *nArray, struct page *sArray, int nMember);
|
||||
//int Array_NS_enhi(struct page *nArray, struct page *sArray, int nMember);
|
||||
//int Array_SV_eneq(struct page *sArray, int nMember, int nNum);
|
||||
//int Array_SV_enne(struct page *sArray, int nMember, int nNum);
|
||||
//int Array_SV_enlo(struct page *sArray, int nMember, int nNum);
|
||||
//int Array_SV_enhi(struct page *sArray, int nMember, int nNum);
|
||||
//int Array_SV_enra(struct page *sArray, int nMember, int nMin, int nMax);
|
||||
//int Array_SN_eneq(struct page *sArray, int nMember, struct page *nArray);
|
||||
//int Array_SN_enne(struct page *sArray, int nMember, struct page *nArray);
|
||||
//int Array_SN_enlo(struct page *sArray, int nMember, struct page *nArray);
|
||||
//int Array_SN_enhi(struct page *sArray, int nMember, struct page *nArray);
|
||||
//int Array_SS_eneq(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//int Array_SS_enne(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//int Array_SS_enlo(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//int Array_SS_enhi(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS);
|
||||
//void Array_NV_cheq(struct page **nArray, int nNum, int nChg);
|
||||
//void Array_NV_chne(struct page **nArray, int nNum, int nChg);
|
||||
//void Array_NV_chlo(struct page **nArray, int nNum, int nChg);
|
||||
//void Array_NV_chhi(struct page **nArray, int nNum, int nChg);
|
||||
//void Array_NV_chra(struct page **nArray, int nMin, int nMax, int nChg);
|
||||
//void Array_NN_cheq(struct page **nArray, struct page *nArrayS, int nChg);
|
||||
//void Array_NN_chne(struct page **nArray, struct page *nArrayS, int nChg);
|
||||
//void Array_NN_chlo(struct page **nArray, struct page *nArrayS, int nChg);
|
||||
//void Array_NN_chhi(struct page **nArray, struct page *nArrayS, int nChg);
|
||||
//void Array_NS_cheq(struct page **nArray, struct page *sArray, int nMember, int nChg);
|
||||
//void Array_NS_chne(struct page **nArray, struct page *sArray, int nMember, int nChg);
|
||||
//void Array_NS_chlo(struct page **nArray, struct page *sArray, int nMember, int nChg);
|
||||
//void Array_NS_chhi(struct page **nArray, struct page *sArray, int nMember, int nChg);
|
||||
//void Array_SV_cheq(struct page **sArray, int nMember, int nNum, int nChg);
|
||||
//void Array_SV_chne(struct page **sArray, int nMember, int nNum, int nChg);
|
||||
//void Array_SV_chlo(struct page **sArray, int nMember, int nNum, int nChg);
|
||||
//void Array_SV_chhi(struct page **sArray, int nMember, int nNum, int nChg);
|
||||
//void Array_SV_chra(struct page **sArray, int nMember, int nMin, int nMax, int nChg);
|
||||
//void Array_SN_cheq(struct page **sArray, int nMember, struct page *nArray, int nChg);
|
||||
//void Array_SN_chne(struct page **sArray, int nMember, struct page *nArray, int nChg);
|
||||
//void Array_SN_chlo(struct page **sArray, int nMember, struct page *nArray, int nChg);
|
||||
//void Array_SN_chhi(struct page **sArray, int nMember, struct page *nArray, int nChg);
|
||||
//void Array_SS_cheq(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS, int nChg);
|
||||
//void Array_SS_chne(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS, int nChg);
|
||||
//void Array_SS_chlo(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS, int nChg);
|
||||
//void Array_SS_chhi(struct page **sArray, int nMember, struct page *sArrayS, int nMemberS, int nChg);
|
||||
//void Array_NV_fweq(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_fwne(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_fwlo(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_fwhi(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_fwra(struct page *nArray, int nMin, int nMax, struct page **nArrayD);
|
||||
//void Array_NV_faeq(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_fane(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_falo(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_fahi(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_fara(struct page *nArray, int nMin, int nMax, struct page **nArrayD);
|
||||
//void Array_NV_foeq(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_fone(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_folo(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_fohi(struct page *nArray, int nNum, struct page **nArrayD);
|
||||
//void Array_NV_fora(struct page *nArray, int nMin, int nMax, struct page **nArrayD);
|
||||
//void Array_NN_fweq(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NN_fwne(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NN_fwlo(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NN_fwhi(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NN_faeq(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NN_fane(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NN_falo(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NN_fahi(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NN_foeq(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NN_fone(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NN_folo(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NN_fohi(struct page *nArray, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_NS_fweq(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_NS_fwne(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_NS_fwlo(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_NS_fwhi(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_NS_faeq(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_NS_fane(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_NS_falo(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_NS_fahi(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_NS_foeq(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_NS_fone(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_NS_folo(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_NS_fohi(struct page *nArray, struct page *sArray, int nMember, struct page **nArrayD);
|
||||
//void Array_SV_fweq(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_fwne(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_fwlo(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_fwhi(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_fwra(struct page *sArray, int nMember, int nMin, int nMax, struct page **nArrayD);
|
||||
//void Array_SV_faeq(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_fane(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_falo(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_fahi(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_fara(struct page *sArray, int nMember, int nMin, int nMax, struct page **nArrayD);
|
||||
//void Array_SV_foeq(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_fone(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_folo(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_fohi(struct page *sArray, int nMember, int nNum, struct page **nArrayD);
|
||||
//void Array_SV_fora(struct page *sArray, int nMember, int nMin, int nMax, struct page **nArrayD);
|
||||
//void Array_SN_fweq(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SN_fwne(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SN_fwlo(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SN_fwhi(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SN_faeq(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SN_fane(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SN_falo(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SN_fahi(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SN_foeq(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SN_fone(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SN_folo(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SN_fohi(struct page *sArray, int nMember, struct page *nArrayS, struct page **nArrayD);
|
||||
//void Array_SS_fweq(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
//void Array_SS_fwne(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
//void Array_SS_fwlo(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
//void Array_SS_fwhi(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
//void Array_SS_faeq(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
//void Array_SS_fane(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
//void Array_SS_falo(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
//void Array_SS_fahi(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
//void Array_SS_foeq(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
//void Array_SS_fone(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
//void Array_SS_folo(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
//void Array_SS_fohi(struct page *sArray, int nMember, struct page *sArrayS, int nMemberS, struct page **nArrayD);
|
||||
|
||||
static int Array_NV_sceq(struct page *array, int index, int num, int *out_index)
|
||||
{
|
||||
if (!array)
|
||||
return 0;
|
||||
check_array(array);
|
||||
|
||||
if (index >= 0) {
|
||||
for (int i = index; i < array->nr_vars; i++) {
|
||||
if (array->values[i].i == num) {
|
||||
*out_index = i;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Negative index means backwards search
|
||||
index &= 0x7fffffff;
|
||||
if (index >= array->nr_vars)
|
||||
return 0;
|
||||
for (int i = index; i >= 0; --i) {
|
||||
if (array->values[i].i == num) {
|
||||
*out_index = i;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
//int Array_NV_scne(struct page *nArray, int nIndex, int nNum, int *pnIndex);
|
||||
//int Array_NV_sclo(struct page *nArray, int nIndex, int nNum, int *pnIndex);
|
||||
//int Array_NV_schi(struct page *nArray, int nIndex, int nNum, int *pnIndex);
|
||||
//int Array_NV_scra(struct page *nArray, int nIndex, int nMin, int nMax, int *pnIndex);
|
||||
//int Array_SV_sceq(struct page *sArray, int nMember, int nIndex, int nNum, int *pnIndex);
|
||||
//int Array_SV_scne(struct page *sArray, int nMember, int nIndex, int nNum, int *pnIndex);
|
||||
//int Array_SV_sclo(struct page *sArray, int nMember, int nIndex, int nNum, int *pnIndex);
|
||||
//int Array_SV_schi(struct page *sArray, int nMember, int nIndex, int nNum, int *pnIndex);
|
||||
//int Array_SV_scra(struct page *sArray, int nMember, int nIndex, int nMin, int nMax, int *pnIndex);
|
||||
//int Array_NN_sclowest(struct page *nArray, struct page **nArrayD, int *pnIndex);
|
||||
//int Array_NN_schighest(struct page *nArray, struct page **nArrayD, int *pnIndex);
|
||||
//int Array_NS_sclowest(struct page *nArray, struct page **sArray, int nMember, int *pnIndex);
|
||||
//int Array_NS_schighest(struct page *nArray, struct page **sArray, int nMember, int *pnIndex);
|
||||
//int Array_SN_sclowest(struct page *sArray, int nMember, struct page **nArray, int *pnIndex);
|
||||
//int Array_SN_schighest(struct page *sArray, int nMember, struct page **nArray, int *pnIndex);
|
||||
//int Array_SS_sclowest(struct page *sArray, int nMember, struct page **sArrayD, int nMemberS, int *pnIndex);
|
||||
//int Array_SS_schighest(struct page *sArray, int nMember, struct page **sArrayD, int nMemberS, int *pnIndex);
|
||||
//int Array_VN_add(struct page *nArray);
|
||||
//int Array_VN_and(struct page *nArray);
|
||||
//int Array_VN_or(struct page *nArray);
|
||||
//int Array_VS_add(struct page *sArray, int nMember);
|
||||
//int Array_VS_and(struct page *sArray, int nMember);
|
||||
//int Array_VS_or(struct page *sArray, int nMember);
|
||||
|
||||
HLL_LIBRARY(Array,
|
||||
HLL_TODO_EXPORT(NV_copy, Array_NV_copy),
|
||||
HLL_TODO_EXPORT(NV_add, Array_NV_add),
|
||||
HLL_TODO_EXPORT(NV_sub, Array_NV_sub),
|
||||
HLL_TODO_EXPORT(NV_mul, Array_NV_mul),
|
||||
HLL_TODO_EXPORT(NV_div, Array_NV_div),
|
||||
HLL_TODO_EXPORT(NV_and, Array_NV_and),
|
||||
HLL_TODO_EXPORT(NV_or, Array_NV_or),
|
||||
HLL_TODO_EXPORT(NV_xor, Array_NV_xor),
|
||||
HLL_TODO_EXPORT(NV_min, Array_NV_min),
|
||||
HLL_TODO_EXPORT(NV_max, Array_NV_max),
|
||||
HLL_TODO_EXPORT(NN_copy, Array_NN_copy),
|
||||
HLL_TODO_EXPORT(NN_add, Array_NN_add),
|
||||
HLL_TODO_EXPORT(NN_sub, Array_NN_sub),
|
||||
HLL_TODO_EXPORT(NN_mul, Array_NN_mul),
|
||||
HLL_TODO_EXPORT(NN_div, Array_NN_div),
|
||||
HLL_TODO_EXPORT(NN_and, Array_NN_and),
|
||||
HLL_TODO_EXPORT(NN_or, Array_NN_or),
|
||||
HLL_TODO_EXPORT(NN_xor, Array_NN_xor),
|
||||
HLL_TODO_EXPORT(NN_min, Array_NN_min),
|
||||
HLL_TODO_EXPORT(NN_max, Array_NN_max),
|
||||
HLL_TODO_EXPORT(NS_copy, Array_NS_copy),
|
||||
HLL_TODO_EXPORT(NS_add, Array_NS_add),
|
||||
HLL_TODO_EXPORT(NS_sub, Array_NS_sub),
|
||||
HLL_TODO_EXPORT(NS_mul, Array_NS_mul),
|
||||
HLL_TODO_EXPORT(NS_div, Array_NS_div),
|
||||
HLL_TODO_EXPORT(NS_and, Array_NS_and),
|
||||
HLL_TODO_EXPORT(NS_or, Array_NS_or),
|
||||
HLL_TODO_EXPORT(NS_xor, Array_NS_xor),
|
||||
HLL_TODO_EXPORT(NS_min, Array_NS_min),
|
||||
HLL_TODO_EXPORT(NS_max, Array_NS_max),
|
||||
HLL_TODO_EXPORT(SV_copy, Array_SV_copy),
|
||||
HLL_TODO_EXPORT(SV_add, Array_SV_add),
|
||||
HLL_TODO_EXPORT(SV_sub, Array_SV_sub),
|
||||
HLL_TODO_EXPORT(SV_mul, Array_SV_mul),
|
||||
HLL_TODO_EXPORT(SV_div, Array_SV_div),
|
||||
HLL_TODO_EXPORT(SV_and, Array_SV_and),
|
||||
HLL_TODO_EXPORT(SV_or, Array_SV_or),
|
||||
HLL_TODO_EXPORT(SV_xor, Array_SV_xor),
|
||||
HLL_TODO_EXPORT(SV_min, Array_SV_min),
|
||||
HLL_TODO_EXPORT(SV_max, Array_SV_max),
|
||||
HLL_TODO_EXPORT(SN_copy, Array_SN_copy),
|
||||
HLL_TODO_EXPORT(SN_add, Array_SN_add),
|
||||
HLL_TODO_EXPORT(SN_sub, Array_SN_sub),
|
||||
HLL_TODO_EXPORT(SN_mul, Array_SN_mul),
|
||||
HLL_TODO_EXPORT(SN_div, Array_SN_div),
|
||||
HLL_TODO_EXPORT(SN_and, Array_SN_and),
|
||||
HLL_TODO_EXPORT(SN_or, Array_SN_or),
|
||||
HLL_TODO_EXPORT(SN_xor, Array_SN_xor),
|
||||
HLL_TODO_EXPORT(SN_min, Array_SN_min),
|
||||
HLL_TODO_EXPORT(SN_max, Array_SN_max),
|
||||
HLL_TODO_EXPORT(SS_copy, Array_SS_copy),
|
||||
HLL_TODO_EXPORT(SS_add, Array_SS_add),
|
||||
HLL_TODO_EXPORT(SS_sub, Array_SS_sub),
|
||||
HLL_TODO_EXPORT(SS_mul, Array_SS_mul),
|
||||
HLL_TODO_EXPORT(SS_div, Array_SS_div),
|
||||
HLL_TODO_EXPORT(SS_and, Array_SS_and),
|
||||
HLL_TODO_EXPORT(SS_or, Array_SS_or),
|
||||
HLL_TODO_EXPORT(SS_xor, Array_SS_xor),
|
||||
HLL_TODO_EXPORT(SS_min, Array_SS_min),
|
||||
HLL_TODO_EXPORT(SS_max, Array_SS_max),
|
||||
HLL_EXPORT(NV_eneq, Array_NV_eneq),
|
||||
HLL_TODO_EXPORT(NV_enne, Array_NV_enne),
|
||||
HLL_TODO_EXPORT(NV_enlo, Array_NV_enlo),
|
||||
HLL_TODO_EXPORT(NV_enhi, Array_NV_enhi),
|
||||
HLL_TODO_EXPORT(NV_enra, Array_NV_enra),
|
||||
HLL_TODO_EXPORT(NN_eneq, Array_NN_eneq),
|
||||
HLL_TODO_EXPORT(NN_enne, Array_NN_enne),
|
||||
HLL_TODO_EXPORT(NN_enlo, Array_NN_enlo),
|
||||
HLL_TODO_EXPORT(NN_enhi, Array_NN_enhi),
|
||||
HLL_TODO_EXPORT(NS_eneq, Array_NS_eneq),
|
||||
HLL_TODO_EXPORT(NS_enne, Array_NS_enne),
|
||||
HLL_TODO_EXPORT(NS_enlo, Array_NS_enlo),
|
||||
HLL_TODO_EXPORT(NS_enhi, Array_NS_enhi),
|
||||
HLL_TODO_EXPORT(SV_eneq, Array_SV_eneq),
|
||||
HLL_TODO_EXPORT(SV_enne, Array_SV_enne),
|
||||
HLL_TODO_EXPORT(SV_enlo, Array_SV_enlo),
|
||||
HLL_TODO_EXPORT(SV_enhi, Array_SV_enhi),
|
||||
HLL_TODO_EXPORT(SV_enra, Array_SV_enra),
|
||||
HLL_TODO_EXPORT(SN_eneq, Array_SN_eneq),
|
||||
HLL_TODO_EXPORT(SN_enne, Array_SN_enne),
|
||||
HLL_TODO_EXPORT(SN_enlo, Array_SN_enlo),
|
||||
HLL_TODO_EXPORT(SN_enhi, Array_SN_enhi),
|
||||
HLL_TODO_EXPORT(SS_eneq, Array_SS_eneq),
|
||||
HLL_TODO_EXPORT(SS_enne, Array_SS_enne),
|
||||
HLL_TODO_EXPORT(SS_enlo, Array_SS_enlo),
|
||||
HLL_TODO_EXPORT(SS_enhi, Array_SS_enhi),
|
||||
HLL_TODO_EXPORT(NV_cheq, Array_NV_cheq),
|
||||
HLL_TODO_EXPORT(NV_chne, Array_NV_chne),
|
||||
HLL_TODO_EXPORT(NV_chlo, Array_NV_chlo),
|
||||
HLL_TODO_EXPORT(NV_chhi, Array_NV_chhi),
|
||||
HLL_TODO_EXPORT(NV_chra, Array_NV_chra),
|
||||
HLL_TODO_EXPORT(NN_cheq, Array_NN_cheq),
|
||||
HLL_TODO_EXPORT(NN_chne, Array_NN_chne),
|
||||
HLL_TODO_EXPORT(NN_chlo, Array_NN_chlo),
|
||||
HLL_TODO_EXPORT(NN_chhi, Array_NN_chhi),
|
||||
HLL_TODO_EXPORT(NS_cheq, Array_NS_cheq),
|
||||
HLL_TODO_EXPORT(NS_chne, Array_NS_chne),
|
||||
HLL_TODO_EXPORT(NS_chlo, Array_NS_chlo),
|
||||
HLL_TODO_EXPORT(NS_chhi, Array_NS_chhi),
|
||||
HLL_TODO_EXPORT(SV_cheq, Array_SV_cheq),
|
||||
HLL_TODO_EXPORT(SV_chne, Array_SV_chne),
|
||||
HLL_TODO_EXPORT(SV_chlo, Array_SV_chlo),
|
||||
HLL_TODO_EXPORT(SV_chhi, Array_SV_chhi),
|
||||
HLL_TODO_EXPORT(SV_chra, Array_SV_chra),
|
||||
HLL_TODO_EXPORT(SN_cheq, Array_SN_cheq),
|
||||
HLL_TODO_EXPORT(SN_chne, Array_SN_chne),
|
||||
HLL_TODO_EXPORT(SN_chlo, Array_SN_chlo),
|
||||
HLL_TODO_EXPORT(SN_chhi, Array_SN_chhi),
|
||||
HLL_TODO_EXPORT(SS_cheq, Array_SS_cheq),
|
||||
HLL_TODO_EXPORT(SS_chne, Array_SS_chne),
|
||||
HLL_TODO_EXPORT(SS_chlo, Array_SS_chlo),
|
||||
HLL_TODO_EXPORT(SS_chhi, Array_SS_chhi),
|
||||
HLL_TODO_EXPORT(NV_fweq, Array_NV_fweq),
|
||||
HLL_TODO_EXPORT(NV_fwne, Array_NV_fwne),
|
||||
HLL_TODO_EXPORT(NV_fwlo, Array_NV_fwlo),
|
||||
HLL_TODO_EXPORT(NV_fwhi, Array_NV_fwhi),
|
||||
HLL_TODO_EXPORT(NV_fwra, Array_NV_fwra),
|
||||
HLL_TODO_EXPORT(NV_faeq, Array_NV_faeq),
|
||||
HLL_TODO_EXPORT(NV_fane, Array_NV_fane),
|
||||
HLL_TODO_EXPORT(NV_falo, Array_NV_falo),
|
||||
HLL_TODO_EXPORT(NV_fahi, Array_NV_fahi),
|
||||
HLL_TODO_EXPORT(NV_fara, Array_NV_fara),
|
||||
HLL_TODO_EXPORT(NV_foeq, Array_NV_foeq),
|
||||
HLL_TODO_EXPORT(NV_fone, Array_NV_fone),
|
||||
HLL_TODO_EXPORT(NV_folo, Array_NV_folo),
|
||||
HLL_TODO_EXPORT(NV_fohi, Array_NV_fohi),
|
||||
HLL_TODO_EXPORT(NV_fora, Array_NV_fora),
|
||||
HLL_TODO_EXPORT(NN_fweq, Array_NN_fweq),
|
||||
HLL_TODO_EXPORT(NN_fwne, Array_NN_fwne),
|
||||
HLL_TODO_EXPORT(NN_fwlo, Array_NN_fwlo),
|
||||
HLL_TODO_EXPORT(NN_fwhi, Array_NN_fwhi),
|
||||
HLL_TODO_EXPORT(NN_faeq, Array_NN_faeq),
|
||||
HLL_TODO_EXPORT(NN_fane, Array_NN_fane),
|
||||
HLL_TODO_EXPORT(NN_falo, Array_NN_falo),
|
||||
HLL_TODO_EXPORT(NN_fahi, Array_NN_fahi),
|
||||
HLL_TODO_EXPORT(NN_foeq, Array_NN_foeq),
|
||||
HLL_TODO_EXPORT(NN_fone, Array_NN_fone),
|
||||
HLL_TODO_EXPORT(NN_folo, Array_NN_folo),
|
||||
HLL_TODO_EXPORT(NN_fohi, Array_NN_fohi),
|
||||
HLL_TODO_EXPORT(NS_fweq, Array_NS_fweq),
|
||||
HLL_TODO_EXPORT(NS_fwne, Array_NS_fwne),
|
||||
HLL_TODO_EXPORT(NS_fwlo, Array_NS_fwlo),
|
||||
HLL_TODO_EXPORT(NS_fwhi, Array_NS_fwhi),
|
||||
HLL_TODO_EXPORT(NS_faeq, Array_NS_faeq),
|
||||
HLL_TODO_EXPORT(NS_fane, Array_NS_fane),
|
||||
HLL_TODO_EXPORT(NS_falo, Array_NS_falo),
|
||||
HLL_TODO_EXPORT(NS_fahi, Array_NS_fahi),
|
||||
HLL_TODO_EXPORT(NS_foeq, Array_NS_foeq),
|
||||
HLL_TODO_EXPORT(NS_fone, Array_NS_fone),
|
||||
HLL_TODO_EXPORT(NS_folo, Array_NS_folo),
|
||||
HLL_TODO_EXPORT(NS_fohi, Array_NS_fohi),
|
||||
HLL_TODO_EXPORT(SV_fweq, Array_SV_fweq),
|
||||
HLL_TODO_EXPORT(SV_fwne, Array_SV_fwne),
|
||||
HLL_TODO_EXPORT(SV_fwlo, Array_SV_fwlo),
|
||||
HLL_TODO_EXPORT(SV_fwhi, Array_SV_fwhi),
|
||||
HLL_TODO_EXPORT(SV_fwra, Array_SV_fwra),
|
||||
HLL_TODO_EXPORT(SV_faeq, Array_SV_faeq),
|
||||
HLL_TODO_EXPORT(SV_fane, Array_SV_fane),
|
||||
HLL_TODO_EXPORT(SV_falo, Array_SV_falo),
|
||||
HLL_TODO_EXPORT(SV_fahi, Array_SV_fahi),
|
||||
HLL_TODO_EXPORT(SV_fara, Array_SV_fara),
|
||||
HLL_TODO_EXPORT(SV_foeq, Array_SV_foeq),
|
||||
HLL_TODO_EXPORT(SV_fone, Array_SV_fone),
|
||||
HLL_TODO_EXPORT(SV_folo, Array_SV_folo),
|
||||
HLL_TODO_EXPORT(SV_fohi, Array_SV_fohi),
|
||||
HLL_TODO_EXPORT(SV_fora, Array_SV_fora),
|
||||
HLL_TODO_EXPORT(SN_fweq, Array_SN_fweq),
|
||||
HLL_TODO_EXPORT(SN_fwne, Array_SN_fwne),
|
||||
HLL_TODO_EXPORT(SN_fwlo, Array_SN_fwlo),
|
||||
HLL_TODO_EXPORT(SN_fwhi, Array_SN_fwhi),
|
||||
HLL_TODO_EXPORT(SN_faeq, Array_SN_faeq),
|
||||
HLL_TODO_EXPORT(SN_fane, Array_SN_fane),
|
||||
HLL_TODO_EXPORT(SN_falo, Array_SN_falo),
|
||||
HLL_TODO_EXPORT(SN_fahi, Array_SN_fahi),
|
||||
HLL_TODO_EXPORT(SN_foeq, Array_SN_foeq),
|
||||
HLL_TODO_EXPORT(SN_fone, Array_SN_fone),
|
||||
HLL_TODO_EXPORT(SN_folo, Array_SN_folo),
|
||||
HLL_TODO_EXPORT(SN_fohi, Array_SN_fohi),
|
||||
HLL_TODO_EXPORT(SS_fweq, Array_SS_fweq),
|
||||
HLL_TODO_EXPORT(SS_fwne, Array_SS_fwne),
|
||||
HLL_TODO_EXPORT(SS_fwlo, Array_SS_fwlo),
|
||||
HLL_TODO_EXPORT(SS_fwhi, Array_SS_fwhi),
|
||||
HLL_TODO_EXPORT(SS_faeq, Array_SS_faeq),
|
||||
HLL_TODO_EXPORT(SS_fane, Array_SS_fane),
|
||||
HLL_TODO_EXPORT(SS_falo, Array_SS_falo),
|
||||
HLL_TODO_EXPORT(SS_fahi, Array_SS_fahi),
|
||||
HLL_TODO_EXPORT(SS_foeq, Array_SS_foeq),
|
||||
HLL_TODO_EXPORT(SS_fone, Array_SS_fone),
|
||||
HLL_TODO_EXPORT(SS_folo, Array_SS_folo),
|
||||
HLL_TODO_EXPORT(SS_fohi, Array_SS_fohi),
|
||||
HLL_EXPORT(NV_sceq, Array_NV_sceq),
|
||||
HLL_TODO_EXPORT(NV_scne, Array_NV_scne),
|
||||
HLL_TODO_EXPORT(NV_sclo, Array_NV_sclo),
|
||||
HLL_TODO_EXPORT(NV_schi, Array_NV_schi),
|
||||
HLL_TODO_EXPORT(NV_scra, Array_NV_scra),
|
||||
HLL_TODO_EXPORT(SV_sceq, Array_SV_sceq),
|
||||
HLL_TODO_EXPORT(SV_scne, Array_SV_scne),
|
||||
HLL_TODO_EXPORT(SV_sclo, Array_SV_sclo),
|
||||
HLL_TODO_EXPORT(SV_schi, Array_SV_schi),
|
||||
HLL_TODO_EXPORT(SV_scra, Array_SV_scra),
|
||||
HLL_TODO_EXPORT(NN_sclowest, Array_NN_sclowest),
|
||||
HLL_TODO_EXPORT(NN_schighest, Array_NN_schighest),
|
||||
HLL_TODO_EXPORT(NS_sclowest, Array_NS_sclowest),
|
||||
HLL_TODO_EXPORT(NS_schighest, Array_NS_schighest),
|
||||
HLL_TODO_EXPORT(SN_sclowest, Array_SN_sclowest),
|
||||
HLL_TODO_EXPORT(SN_schighest, Array_SN_schighest),
|
||||
HLL_TODO_EXPORT(SS_sclowest, Array_SS_sclowest),
|
||||
HLL_TODO_EXPORT(SS_schighest, Array_SS_schighest),
|
||||
HLL_TODO_EXPORT(VN_add, Array_VN_add),
|
||||
HLL_TODO_EXPORT(VN_and, Array_VN_and),
|
||||
HLL_TODO_EXPORT(VN_or, Array_VN_or),
|
||||
HLL_TODO_EXPORT(VS_add, Array_VS_add),
|
||||
HLL_TODO_EXPORT(VS_and, Array_VS_and),
|
||||
HLL_TODO_EXPORT(VS_or, Array_VS_or)
|
||||
);
|
||||
+8
-9
@@ -17,12 +17,11 @@
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "system4/file.h"
|
||||
#include "system4/little_endian.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/mt19937int.h"
|
||||
#include "system4/zlib.h"
|
||||
|
||||
#include "hll.h"
|
||||
#include "audio.h"
|
||||
@@ -47,14 +46,15 @@ static int BanMisc_SaveStruct(struct page *page, struct string *file_name)
|
||||
iarray_free_writer(&out);
|
||||
|
||||
// compress
|
||||
unsigned long compressed_size = compressBound(raw_size);
|
||||
size_t compressed_size = zlib_compress_bound(raw_size);
|
||||
uint8_t *buf = xmalloc(MD11_HEADER_SIZE + compressed_size);
|
||||
memcpy(buf, md11_signature, sizeof(md11_signature));
|
||||
LittleEndian_putDW(buf, 4, raw_size);
|
||||
uint8_t *compressed = buf + MD11_HEADER_SIZE;
|
||||
int r = compress2(compressed, &compressed_size, raw_buf, raw_size, Z_DEFAULT_COMPRESSION);
|
||||
compressed_size = zlib_compress(compressed, compressed_size, raw_buf,
|
||||
raw_size, ZLIB_DEFAULT_COMPRESSION);
|
||||
free(raw_buf);
|
||||
if (r != Z_OK) {
|
||||
if (!compressed_size) {
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
@@ -79,7 +79,7 @@ static struct page *load_md11(int struct_type, uint8_t *buf, size_t len)
|
||||
WARNING("BanMisc.LoadStruct file too short");
|
||||
return NULL;
|
||||
}
|
||||
unsigned long raw_size = LittleEndian_getDW(buf, 4);
|
||||
size_t raw_size = LittleEndian_getDW(buf, 4);
|
||||
if (raw_size % 4 != 0) {
|
||||
WARNING("BanMisc.LoadStruct invalid file format");
|
||||
return NULL;
|
||||
@@ -92,9 +92,8 @@ static struct page *load_md11(int struct_type, uint8_t *buf, size_t len)
|
||||
|
||||
// decompress
|
||||
uint8_t *raw_buf = xmalloc(raw_size);
|
||||
int r = uncompress(raw_buf, &raw_size, buf, len);
|
||||
if (r != Z_OK) {
|
||||
WARNING("BanMisc.LoadStruct uncompress failed: %d", r);
|
||||
if (!zlib_decompress_exact(raw_buf, raw_size, buf, len)) {
|
||||
WARNING("BanMisc.LoadStruct uncompress failed");
|
||||
free(raw_buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+24
-6
@@ -14,10 +14,9 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "system4/cg.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/utfsjis.h"
|
||||
#include "asset_manager.h"
|
||||
#include "xsystem4.h"
|
||||
#include "hll.h"
|
||||
|
||||
static bool CGManager_Init(void *imain_system, int cg_cache_size)
|
||||
@@ -27,10 +26,27 @@ static bool CGManager_Init(void *imain_system, int cg_cache_size)
|
||||
|
||||
static bool CGManager_LoadArchive(struct string *archive_name)
|
||||
{
|
||||
char *name = sjis2utf(archive_name->text, 0);
|
||||
bool r = asset_manager_load_archive(ASSET_CG, name);
|
||||
free(name);
|
||||
return r;
|
||||
return asset_manager_load_archive(ASSET_CG, archive_name->text);
|
||||
}
|
||||
|
||||
static bool CGManager_IsExist(struct string *cg_name)
|
||||
{
|
||||
if (!cg_name)
|
||||
return false;
|
||||
return asset_exists_by_name(ASSET_CG, cg_name->text, NULL);
|
||||
}
|
||||
|
||||
static bool CGManager_GetInfo(struct string *cg_name, int *width, int *height, int *bpp, bool *exist_pixel, bool *exist_alpha)
|
||||
{
|
||||
struct cg_metrics metrics;
|
||||
if (!cg_name || !asset_cg_get_metrics_by_name(cg_name->text, &metrics))
|
||||
return false;
|
||||
*width = metrics.w;
|
||||
*height = metrics.h;
|
||||
*bpp = metrics.bpp;
|
||||
*exist_pixel = metrics.has_pixel;
|
||||
*exist_alpha = metrics.has_alpha;
|
||||
return true;
|
||||
}
|
||||
|
||||
//static int CGManager_GetCountOfDataFromArchive(void);
|
||||
@@ -44,6 +60,8 @@ static bool CGManager_LoadArchive(struct string *archive_name)
|
||||
HLL_LIBRARY(CGManager,
|
||||
HLL_EXPORT(Init, CGManager_Init),
|
||||
HLL_EXPORT(LoadArchive, CGManager_LoadArchive),
|
||||
HLL_EXPORT(IsExist, CGManager_IsExist),
|
||||
HLL_EXPORT(GetInfo, CGManager_GetInfo),
|
||||
HLL_TODO_EXPORT(GetCountOfDataFromArchive, CGManager_GetCountOfDataFromArchive),
|
||||
HLL_TODO_EXPORT(GetTitleByIndexFromArchive, CGManager_GetTitleByIndexFromArchive),
|
||||
HLL_TODO_EXPORT(SearchTitleFromArchive, CGManager_SearchTitleFromArchive),
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "hll.h"
|
||||
|
||||
#define REG_COUNT 32
|
||||
|
||||
static int64_t regs[REG_COUNT];
|
||||
|
||||
#define REG_CHECK(reg) ((reg) >= 0 && (reg) < REG_COUNT)
|
||||
|
||||
static int CalcTable_Get(int reg)
|
||||
{
|
||||
if (reg < 0 || reg >= REG_COUNT)
|
||||
return 0;
|
||||
if (regs[reg] > INT32_MAX)
|
||||
return INT32_MAX;
|
||||
if (regs[reg] < INT32_MIN)
|
||||
return INT32_MIN;
|
||||
return (int)regs[reg];
|
||||
}
|
||||
|
||||
static void CalcTable_Init(bool clear)
|
||||
{
|
||||
if (clear) {
|
||||
memset(regs, 0, sizeof(regs));
|
||||
}
|
||||
}
|
||||
|
||||
static bool CalcTable_Set(int regR, int num)
|
||||
{
|
||||
if (!REG_CHECK(regR))
|
||||
return false;
|
||||
regs[regR] = num;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CalcTable_Copy(int regR, int regA)
|
||||
{
|
||||
if (!REG_CHECK(regR) || !REG_CHECK(regA))
|
||||
return false;
|
||||
regs[regR] = regs[regA];
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CalcTable_Add(int regR, int regA, int regB)
|
||||
{
|
||||
if (!REG_CHECK(regR) || !REG_CHECK(regA) || !REG_CHECK(regB))
|
||||
return false;
|
||||
regs[regR] = regs[regA] + regs[regB];
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CalcTable_Sub(int regR, int regA, int regB)
|
||||
{
|
||||
if (!REG_CHECK(regR) || !REG_CHECK(regA) || !REG_CHECK(regB))
|
||||
return false;
|
||||
regs[regR] = regs[regA] - regs[regB];
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CalcTable_Mul(int regR, int regA, int regB)
|
||||
{
|
||||
if (!REG_CHECK(regR) || !REG_CHECK(regA) || !REG_CHECK(regB))
|
||||
return false;
|
||||
regs[regR] = regs[regA] * regs[regB];
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CalcTable_Div(int regR, int regA, int regB)
|
||||
{
|
||||
if (!REG_CHECK(regR) || !REG_CHECK(regA) || !REG_CHECK(regB))
|
||||
return false;
|
||||
regs[regR] = regs[regA] / regs[regB]; // NOTE: No check for division by zero
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CalcTable_And(int regR, int regA, int regB)
|
||||
{
|
||||
if (!REG_CHECK(regR) || !REG_CHECK(regA) || !REG_CHECK(regB))
|
||||
return false;
|
||||
regs[regR] = regs[regA] & regs[regB];
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CalcTable_Or(int regR, int regA, int regB)
|
||||
{
|
||||
if (!REG_CHECK(regR) || !REG_CHECK(regA) || !REG_CHECK(regB))
|
||||
return false;
|
||||
regs[regR] = regs[regA] | regs[regB];
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CalcTable_Xor(int regR, int regA, int regB)
|
||||
{
|
||||
if (!REG_CHECK(regR) || !REG_CHECK(regA) || !REG_CHECK(regB))
|
||||
return false;
|
||||
regs[regR] = regs[regA] ^ regs[regB];
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CalcTable_ShiftL(int regR, int regA, int regB)
|
||||
{
|
||||
if (!REG_CHECK(regR) || !REG_CHECK(regA) || !REG_CHECK(regB))
|
||||
return false;
|
||||
regs[regR] = regs[regA] << regs[regB];
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CalcTable_ShiftR(int regR, int regA, int regB)
|
||||
{
|
||||
if (!REG_CHECK(regR) || !REG_CHECK(regA) || !REG_CHECK(regB))
|
||||
return false;
|
||||
regs[regR] = regs[regA] >> regs[regB];
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CalcTable_Percent(int regR, int regA, int regB, bool rounding)
|
||||
{
|
||||
if (!REG_CHECK(regR) || !REG_CHECK(regA) || !REG_CHECK(regB))
|
||||
return false;
|
||||
int64_t v = regs[regA] * regs[regB];
|
||||
int64_t q = v / 100;
|
||||
int64_t r = v % 100;
|
||||
regs[regR] = q + (rounding && r != 0 ? 1 : 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void CalcTable_Run(int loop)
|
||||
{
|
||||
if (loop != 1) {
|
||||
VM_ERROR("Unsupported loop count: %d", loop);
|
||||
}
|
||||
// This function is a no-op in this implementation.
|
||||
}
|
||||
|
||||
HLL_LIBRARY(CalcTable,
|
||||
HLL_EXPORT(Get, CalcTable_Get),
|
||||
HLL_EXPORT(Init, CalcTable_Init),
|
||||
HLL_EXPORT(Set, CalcTable_Set),
|
||||
HLL_EXPORT(Copy, CalcTable_Copy),
|
||||
HLL_EXPORT(Add, CalcTable_Add),
|
||||
HLL_EXPORT(Sub, CalcTable_Sub),
|
||||
HLL_EXPORT(Mul, CalcTable_Mul),
|
||||
HLL_EXPORT(Div, CalcTable_Div),
|
||||
HLL_EXPORT(And, CalcTable_And),
|
||||
HLL_EXPORT(Or, CalcTable_Or),
|
||||
HLL_EXPORT(Xor, CalcTable_Xor),
|
||||
HLL_EXPORT(ShiftL, CalcTable_ShiftL),
|
||||
HLL_EXPORT(ShiftR, CalcTable_ShiftR),
|
||||
HLL_EXPORT(Percent, CalcTable_Percent),
|
||||
HLL_EXPORT(Run, CalcTable_Run)
|
||||
);
|
||||
@@ -108,6 +108,7 @@ int CharSpriteManager_CreateHandle(void)
|
||||
assert(!chars.sprites[handle]);
|
||||
chars.sprites[handle] = xcalloc(1, sizeof(struct charsprite));
|
||||
sprite_init(&chars.sprites[handle]->sp);
|
||||
chars.sprites[handle]->ts.scale_x = 1.0f;
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "CharSpriteManager.h"
|
||||
#include "xsystem4.h"
|
||||
#include "hll.h"
|
||||
#include "plugin.h"
|
||||
|
||||
static void ChipmunkSpriteEngine_PreLink(void);
|
||||
|
||||
@@ -40,12 +41,12 @@ static void ChipmunkSpriteEngine_ModuleFini(void)
|
||||
|
||||
static int ChipmunkSpriteEngine_Init(possibly_unused void *imain_system)
|
||||
{
|
||||
return sact_init(16, true);
|
||||
return sact_init(16, CHIPMUNK_SPRITE_ENGINE);
|
||||
}
|
||||
|
||||
static int ChipmunkSpriteEngine_Init_with_size(possibly_unused void *imain_system, int cg_cache_size)
|
||||
{
|
||||
return sact_init(cg_cache_size, true);
|
||||
return sact_init(cg_cache_size, CHIPMUNK_SPRITE_ENGINE);
|
||||
}
|
||||
|
||||
static int ChipmunkSpriteEngine_SP_SetCG(int sp_no, struct string *cg_name)
|
||||
@@ -110,8 +111,8 @@ HLL_WARN_UNIMPLEMENTED(0, int, ChipmunkSpriteEngine, SP_SetBrightness, int sp_n
|
||||
HLL_WARN_UNIMPLEMENTED(0, int, ChipmunkSpriteEngine, SP_GetBrightness, int sp_no);
|
||||
|
||||
static bool keep_previous_view = true;
|
||||
HLL_WARN_UNIMPLEMENTED((keep_previous_view = on, true), bool, ChipmunkSpriteEngine, KeepPreviousView_SetMode, bool on);
|
||||
HLL_WARN_UNIMPLEMENTED(keep_previous_view, bool, ChipmunkSpriteEngine, KeepPreviousView_GetMode);
|
||||
HLL_QUIET_UNIMPLEMENTED((keep_previous_view = on, true), bool, ChipmunkSpriteEngine, KeepPreviousView_SetMode, bool on);
|
||||
HLL_QUIET_UNIMPLEMENTED(keep_previous_view, bool, ChipmunkSpriteEngine, KeepPreviousView_GetMode);
|
||||
HLL_QUIET_UNIMPLEMENTED(false, bool, ChipmunkSpriteEngine, KeepPreviousView);
|
||||
|
||||
static void ChipmunkSpriteEngine_Sleep(void)
|
||||
@@ -163,8 +164,68 @@ static bool ChipmunkSpriteEngine_SP_GetSurfaceArea(int sp_no, int *x, int *y, in
|
||||
return true;
|
||||
}
|
||||
|
||||
//static bool ChipmunkSpriteEngine_SP_SetSpriteTransformMode(int sp_no, int transform_mode);
|
||||
//static bool ChipmunkSpriteEngine_SP_SetSpriteTransformPos(int sp_no, float x0, float y0, float z0, float w0, float u0, float v0, float x1, float y1, float z1, float w1, float u1, float v1, float x2, float y2, float z2, float w2, float u2, float v2, float x3, float y3, float z3, float w3, float u3, float v3);
|
||||
static const char transform_plugin_name[] = "ChipmunkSpriteEngine_Transform";
|
||||
|
||||
struct transform_plugin {
|
||||
struct draw_plugin p;
|
||||
struct gfx_vertex vertices[4];
|
||||
};
|
||||
|
||||
static struct transform_plugin *get_transform_plugin(int sp_no)
|
||||
{
|
||||
struct sact_sprite *sp = sact_try_get_sprite(sp_no);
|
||||
if (!sp || !sp->plugin || sp->plugin->name != transform_plugin_name)
|
||||
return NULL;
|
||||
return (struct transform_plugin *)sp->plugin;
|
||||
}
|
||||
|
||||
static void transform_plugin_free(struct draw_plugin *plugin)
|
||||
{
|
||||
free(plugin);
|
||||
}
|
||||
|
||||
static void transform_plugin_render(struct sact_sprite *sp)
|
||||
{
|
||||
struct transform_plugin *plugin = (struct transform_plugin *)sp->plugin;
|
||||
gfx_render_quadrilateral(&sp->texture, plugin->vertices);
|
||||
}
|
||||
|
||||
static bool ChipmunkSpriteEngine_SP_SetSpriteTransformMode(int sp_no, int transform_mode)
|
||||
{
|
||||
struct sact_sprite *sp = sact_get_sprite(sp_no);
|
||||
if (!sp)
|
||||
return false;
|
||||
bool current_mode = sp->plugin && sp->plugin->name == transform_plugin_name;
|
||||
if (current_mode && !transform_mode) {
|
||||
sprite_bind_plugin(sp, NULL);
|
||||
} else if (!current_mode && transform_mode) {
|
||||
struct transform_plugin *plugin = xcalloc(1, sizeof(struct transform_plugin));
|
||||
plugin->p.name = transform_plugin_name;
|
||||
plugin->p.free = transform_plugin_free;
|
||||
plugin->p.render = transform_plugin_render;
|
||||
sprite_bind_plugin(sp, &plugin->p);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ChipmunkSpriteEngine_SP_SetSpriteTransformPos(
|
||||
int sp_no,
|
||||
float x0, float y0, float z0, float w0, float u0, float v0,
|
||||
float x1, float y1, float z1, float w1, float u1, float v1,
|
||||
float x2, float y2, float z2, float w2, float u2, float v2,
|
||||
float x3, float y3, float z3, float w3, float u3, float v3)
|
||||
{
|
||||
struct transform_plugin *plugin = get_transform_plugin(sp_no);
|
||||
if (!plugin)
|
||||
return false;
|
||||
plugin->vertices[0] = (struct gfx_vertex){x0, y0, z0, w0, u0, v0};
|
||||
plugin->vertices[1] = (struct gfx_vertex){x1, y1, z1, w1, u1, v1};
|
||||
plugin->vertices[2] = (struct gfx_vertex){x2, y2, z2, w2, u2, v2};
|
||||
plugin->vertices[3] = (struct gfx_vertex){x3, y3, z3, w3, u3, v3};
|
||||
sprite_dirty(sact_try_get_sprite(sp_no));
|
||||
return true;
|
||||
}
|
||||
|
||||
//static bool ChipmunkSpriteEngine_Sprite_SetTextureFilerType(int sp_no, int texture_filter_type);
|
||||
//static int ChipmunkSpriteEngine_Sprite_GetTextureFilterType(int sp_no);
|
||||
|
||||
@@ -189,7 +250,7 @@ static bool ChipmunkSpriteEngine_SP_GetFontWidth(struct string *text, int *width
|
||||
return true;
|
||||
}
|
||||
|
||||
*width = gfx_size_text(&text_sprite_ts, text->text);
|
||||
*width = ceilf(gfx_size_text(&text_sprite_ts, text->text));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -198,15 +259,32 @@ static bool ChipmunkSpriteEngine_SP_GetFontWidth(struct string *text, int *width
|
||||
//static int CG_GetFormat_by_index(int cg_no);
|
||||
//static int CG_GetFormat(struct string *cg_name);
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED(false, bool, ChipmunkSpriteEngine, SP_Suspend, int sp_no);
|
||||
//static bool ChipmunkSpriteEngine_SP_Resume(int sp_no);
|
||||
//static bool ChipmunkSpriteEngine_SP_IsSuspend(int sp_no);
|
||||
static bool ChipmunkSpriteEngine_SP_Suspend(int sp_no)
|
||||
{
|
||||
struct sact_sprite *sp = sact_try_get_sprite(sp_no);
|
||||
if (!sp) return false;
|
||||
sp->suspended = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ChipmunkSpriteEngine_SP_Resume(int sp_no)
|
||||
{
|
||||
struct sact_sprite *sp = sact_try_get_sprite(sp_no);
|
||||
if (!sp) return false;
|
||||
sp->suspended = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ChipmunkSpriteEngine_SP_IsSuspend(int sp_no)
|
||||
{
|
||||
struct sact_sprite *sp = sact_try_get_sprite(sp_no);
|
||||
return sp && sp->suspended;
|
||||
}
|
||||
|
||||
//static bool ChipmunkSpriteEngine_Debug_GetVideoMemoryInfo(int *max_size, int *use_size, int *use_max_size);
|
||||
//static void ChipmunkSpriteEngine_CombineTexture(void);
|
||||
//static void ChipmunkSpriteEngine_ReleaseCombinedTexture(void);
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED( , void, StoatSpriteEngine, FPS_SetShow, bool bShow);
|
||||
HLL_WARN_UNIMPLEMENTED(false, bool, StoatSpriteEngine, FPS_GetShow, void);
|
||||
|
||||
HLL_LIBRARY(ChipmunkSpriteEngine,
|
||||
HLL_EXPORT(_PreLink, ChipmunkSpriteEngine_PreLink),
|
||||
@@ -225,7 +303,7 @@ HLL_LIBRARY(ChipmunkSpriteEngine,
|
||||
HLL_EXPORT(SP_SaveCG, sact_SP_SaveCG),
|
||||
HLL_EXPORT(SP_Create, sact_SP_Create),
|
||||
HLL_EXPORT(SP_CreatePixelOnly, sact_SP_CreatePixelOnly),
|
||||
HLL_TODO_EXPORT(SP_CreateCustom, SACT2_SP_CreateCustom),
|
||||
HLL_EXPORT(SP_CreateCustom, sact_SP_CreateCustom),
|
||||
HLL_EXPORT(SP_Delete, sact_SP_Delete),
|
||||
HLL_EXPORT(SP_DeleteAll, sact_SP_DeleteAll),
|
||||
HLL_EXPORT(SP_SetPos, sact_SP_SetPos),
|
||||
@@ -236,11 +314,11 @@ HLL_LIBRARY(ChipmunkSpriteEngine,
|
||||
HLL_EXPORT(SP_SetMultipleColor, ChipmunkSpriteEngine_SP_SetMultipleColor),
|
||||
HLL_EXPORT(SP_SetAddColor, ChipmunkSpriteEngine_SP_SetAddColor),
|
||||
HLL_EXPORT(SP_SetShow, sact_SP_SetShow),
|
||||
HLL_EXPORT(SP_SetDrawMethod, sact_SP_SetDrawMethod),
|
||||
HLL_EXPORT(SP_SetDrawMethod, StoatSpriteEngine_SP_SetDrawMethod),
|
||||
HLL_EXPORT(SP_SetSurfaceArea, ChipmunkSpriteEngine_SP_SetSurfaceArea),
|
||||
HLL_EXPORT(SP_GetSurfaceArea, ChipmunkSpriteEngine_SP_GetSurfaceArea),
|
||||
HLL_TODO_EXPORT(SP_SetSpriteTransformMode, ChipmunkSpriteEngine_SP_SetSpriteTransformMode),
|
||||
HLL_TODO_EXPORT(SP_SetSpriteTransformPos, ChipmunkSpriteEngine_SP_SetSpriteTransformPos),
|
||||
HLL_EXPORT(SP_SetSpriteTransformMode, ChipmunkSpriteEngine_SP_SetSpriteTransformMode),
|
||||
HLL_EXPORT(SP_SetSpriteTransformPos, ChipmunkSpriteEngine_SP_SetSpriteTransformPos),
|
||||
HLL_TODO_EXPORT(Sprite_SetTextureFilerType, ChipmunkSpriteEngine_Sprite_SetTextureFilerType),
|
||||
HLL_TODO_EXPORT(Sprite_GetTextureFilterType, ChipmunkSpriteEngine_Sprite_GetTextureFilterType),
|
||||
HLL_EXPORT(SP_IsUsing, sact_SP_IsUsing),
|
||||
@@ -252,7 +330,7 @@ HLL_LIBRARY(ChipmunkSpriteEngine,
|
||||
HLL_EXPORT(SP_GetZ, sact_SP_GetZ),
|
||||
HLL_EXPORT(SP_GetBlendRate, sact_SP_GetBlendRate),
|
||||
HLL_EXPORT(SP_GetShow, sact_SP_GetShow),
|
||||
HLL_EXPORT(SP_GetDrawMethod, sact_SP_GetDrawMethod),
|
||||
HLL_EXPORT(SP_GetDrawMethod, StoatSpriteEngine_SP_GetDrawMethod),
|
||||
HLL_EXPORT(SP_RenderView, ChipmunkSpriteEngine_SP_RenderView),
|
||||
HLL_EXPORT(SP_IsPtIn, sact_SP_IsPtIn),
|
||||
HLL_EXPORT(SP_IsPtInRect, sact_SP_IsPtInRect),
|
||||
@@ -263,13 +341,6 @@ HLL_LIBRARY(ChipmunkSpriteEngine,
|
||||
HLL_EXPORT(SP_SetTextSpriteBoldWeight, StoatSpriteEngine_SP_SetTextSpriteBoldWeight),
|
||||
HLL_EXPORT(SP_SetTextSpriteEdgeWeight, StoatSpriteEngine_SP_SetTextSpriteEdgeWeight),
|
||||
HLL_EXPORT(SP_SetTextSpriteEdgeColor, StoatSpriteEngine_SP_SetTextSpriteEdgeColor),
|
||||
HLL_EXPORT(SP_SetTextSprite, StoatSpriteEngine_SP_SetTextSprite),
|
||||
HLL_EXPORT(SP_SetTextSpriteType, StoatSpriteEngine_SP_SetTextSpriteType),
|
||||
HLL_EXPORT(SP_SetTextSpriteSize, StoatSpriteEngine_SP_SetTextSpriteSize),
|
||||
HLL_EXPORT(SP_SetTextSpriteColor, StoatSpriteEngine_SP_SetTextSpriteColor),
|
||||
HLL_EXPORT(SP_SetTextSpriteBoldWeight, StoatSpriteEngine_SP_SetTextSpriteBoldWeight),
|
||||
HLL_EXPORT(SP_SetTextSpriteEdgeWeight, StoatSpriteEngine_SP_SetTextSpriteEdgeWeight),
|
||||
HLL_EXPORT(SP_SetTextSpriteEdgeColor, StoatSpriteEngine_SP_SetTextSpriteEdgeColor),
|
||||
HLL_TODO_EXPORT(SP_GetFontDescent, ChipmunkSpriteEngine_SP_GetFontDescent),
|
||||
HLL_EXPORT(SP_GetFontWidth, ChipmunkSpriteEngine_SP_GetFontWidth),
|
||||
HLL_EXPORT(SP_SetDashTextSprite, StoatSpriteEngine_SP_SetDashTextSprite),
|
||||
@@ -277,7 +348,7 @@ HLL_LIBRARY(ChipmunkSpriteEngine,
|
||||
HLL_TODO_EXPORT(SP_GetAlphaClipperSpriteNumber, ChipmunkSpriteEngine_SP_GetAlphaClipperSpriteNumber),
|
||||
HLL_EXPORT(FPS_SetShow, StoatSpriteEngine_FPS_SetShow),
|
||||
HLL_EXPORT(FPS_GetShow, StoatSpriteEngine_FPS_GetShow),
|
||||
HLL_TODO_EXPORT(FPS_Get, StoatSpriteEngine_FPS_Get),
|
||||
HLL_EXPORT(FPS_Get, StoatSpriteEngine_FPS_Get),
|
||||
HLL_EXPORT(GAME_MSG_GetNumof, sact_GAME_MSG_GetNumOf),
|
||||
HLL_TODO_EXPORT(GAME_MSG_Get, SACT2_GAME_MSG_Get),
|
||||
HLL_EXPORT(IntToZenkaku, sact_IntToZenkaku),
|
||||
@@ -304,7 +375,7 @@ HLL_LIBRARY(ChipmunkSpriteEngine,
|
||||
HLL_EXPORT(SP_SetBrightness, ChipmunkSpriteEngine_SP_SetBrightness),
|
||||
HLL_EXPORT(SP_GetBrightness, ChipmunkSpriteEngine_SP_GetBrightness),
|
||||
HLL_TODO_EXPORT(SP_CreateCopy, SACTDX_SP_CreateCopy),
|
||||
HLL_TODO_EXPORT(Key_ClearFlagNoCtrl, SACTDX_Key_ClearFlagNoCtrl),
|
||||
HLL_EXPORT(Key_ClearFlagNoCtrl, sact_Key_ClearFlagNoCtrl),
|
||||
HLL_TODO_EXPORT(Key_ClearFlagOne, SACTDX_Key_ClearFlagOne),
|
||||
HLL_EXPORT(TRANS_Begin, sact_TRANS_Begin),
|
||||
HLL_EXPORT(TRANS_Update, sact_TRANS_Update),
|
||||
@@ -348,8 +419,8 @@ HLL_LIBRARY(ChipmunkSpriteEngine,
|
||||
HLL_TODO_EXPORT(Debug_GetFillRate, StoatSpriteEngine_Debug_GetFillRate),
|
||||
HLL_TODO_EXPORT(MUSIC_ReloadParam, StoatSpriteEngine_MUSIC_ReloadParam),
|
||||
HLL_EXPORT(SP_Suspend, ChipmunkSpriteEngine_SP_Suspend),
|
||||
HLL_TODO_EXPORT(SP_Resume, ChipmunkSpriteEngine_SP_Resume),
|
||||
HLL_TODO_EXPORT(SP_IsSuspend, ChipmunkSpriteEngine_SP_IsSuspend),
|
||||
HLL_EXPORT(SP_Resume, ChipmunkSpriteEngine_SP_Resume),
|
||||
HLL_EXPORT(SP_IsSuspend, ChipmunkSpriteEngine_SP_IsSuspend),
|
||||
HLL_TODO_EXPORT(Debug_GetVideoMemoryInfo, ChipmunkSpriteEngine_Debug_GetVideoMemoryInfo),
|
||||
HLL_TODO_EXPORT(CombineTexture, ChipmunkSpriteEngine_CombineTexture),
|
||||
HLL_TODO_EXPORT(ReleaseCombinedTexture, ChipmunkSpriteEngine_ReleaseCombinedTexture));
|
||||
|
||||
+1
-1
@@ -288,7 +288,7 @@ static bool ChrLoader_LoadWavFile(int sound_channel, int id)
|
||||
{
|
||||
int blob = chr_loader_get_blob_handle(id, 4);
|
||||
struct archive_data *dfile = chr_loader_get_blob_as_archive_data(blob);
|
||||
if (!dfile || !wav_prepare_from_archive_data(sound_channel, dfile)) {
|
||||
if (!dfile || !wav_prepare_from_archive_data(sound_channel, AUDIO_NO_METADATA, dfile)) {
|
||||
WARNING("ChrLoader.LoadWavFile %d failed", id);
|
||||
return false;
|
||||
}
|
||||
|
||||
+5
-6
@@ -15,12 +15,11 @@
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "system4/archive.h"
|
||||
#include "system4/buffer.h"
|
||||
#include "system4/little_endian.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/zlib.h"
|
||||
|
||||
#include "hll.h"
|
||||
#include "asset_manager.h"
|
||||
@@ -40,11 +39,11 @@ static int Data_Open(int num)
|
||||
struct archive_data *dfile = asset_get(ASSET_DATA, num);
|
||||
if (!dfile)
|
||||
return 0;
|
||||
unsigned long uncompressed_size = LittleEndian_getDW(dfile->data, 0);
|
||||
size_t uncompressed_size = LittleEndian_getDW(dfile->data, 0);
|
||||
uint8_t *buf = xmalloc(uncompressed_size);
|
||||
int rv = uncompress(buf, &uncompressed_size, (uint8_t *)dfile->data + 4, dfile->size - 4);
|
||||
if (rv != Z_OK) {
|
||||
WARNING("%s: uncompress failed (%d)", dfile->name, rv);
|
||||
if (!zlib_decompress_exact(buf, uncompressed_size,
|
||||
(uint8_t *)dfile->data + 4, dfile->size - 4)) {
|
||||
WARNING("%s: uncompress failed", dfile->name);
|
||||
archive_free_data(dfile);
|
||||
free(buf);
|
||||
return 0;
|
||||
|
||||
+5
-6
@@ -16,12 +16,11 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "system4/archive.h"
|
||||
#include "system4/buffer.h"
|
||||
#include "system4/mt19937int.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/zlib.h"
|
||||
|
||||
#include "hll.h"
|
||||
#include "asset_manager.h"
|
||||
@@ -267,7 +266,7 @@ static int DataFile_Open(int link)
|
||||
}
|
||||
buffer_skip(&r, 4);
|
||||
|
||||
unsigned long uncompressed_size = buffer_read_int32(&r);
|
||||
size_t uncompressed_size = buffer_read_int32(&r);
|
||||
uint32_t compressed_size = buffer_remaining(&r);
|
||||
|
||||
// decode compressed data
|
||||
@@ -277,10 +276,10 @@ static int DataFile_Open(int link)
|
||||
mt19937_xorcode(compressed_data, compressed_size, 4588163);
|
||||
|
||||
uint8_t *raw = xmalloc(uncompressed_size);
|
||||
int rv = uncompress(raw, &uncompressed_size, compressed_data, compressed_size);
|
||||
bool ok = zlib_decompress_exact(raw, uncompressed_size, compressed_data, compressed_size);
|
||||
free(compressed_data);
|
||||
if (rv != Z_OK) {
|
||||
WARNING("Uncompress failed: %d", rv);
|
||||
if (!ok) {
|
||||
WARNING("Uncompress failed");
|
||||
free(raw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user