mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-09-22 23:18:01 +03:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3f9d93917 | ||
|
|
ac411754a0 | ||
|
|
45ebaa2c7f | ||
|
|
a923b0ce23 | ||
|
|
1be2cffd22 | ||
|
|
50af8bd3ff | ||
|
|
32ef29ee20 | ||
|
|
8620be452e | ||
|
|
cc79f9caee | ||
|
|
6a2eae7274 | ||
|
|
66002f7ceb | ||
|
|
d1b18f39dd | ||
|
|
dbd1e54c46 | ||
|
|
3c0ee51714 | ||
|
|
d4f7ab06cf | ||
|
|
66d8def3f8 | ||
|
|
8f29146c28 | ||
|
|
e53dc6ad0e | ||
|
|
90a96eb8bb | ||
|
|
3ff2c33d3c | ||
|
|
2237bb814c | ||
|
|
6b62e1db23 | ||
|
|
267bb8c99a | ||
|
|
d2ff9833de | ||
|
|
91e4478a29 | ||
|
|
842d712a31 | ||
|
|
2dbc322db8 | ||
|
|
d913ee38a3 | ||
|
|
5649236c7c | ||
|
|
a55893bf2f | ||
|
|
d8f936f1df | ||
|
|
d2539b978e | ||
|
|
92bbcb4866 | ||
|
|
96df171157 | ||
|
|
9fd0415ec6 | ||
|
|
31930c5e97 | ||
|
|
def987ad5f | ||
|
|
a4bb1a815a | ||
|
|
71f3450037 | ||
|
|
f7c9d9326d | ||
|
|
8b4df9b9a4 | ||
|
|
d4d292434c | ||
|
|
f598a7276b | ||
|
|
2f75253080 | ||
|
|
b0477deca1 | ||
|
|
de2db1af93 | ||
|
|
b6f9999a56 | ||
|
|
20b60467f2 | ||
|
|
64a92a2ffa | ||
|
|
620fecb244 | ||
|
|
ca982221f7 | ||
|
|
75d8a8b69d | ||
|
|
5a5954811f | ||
|
|
d5b1b155e9 | ||
|
|
3796521285 | ||
|
|
ec5a2c054c | ||
|
|
82407a1d59 | ||
|
|
e21f06331e | ||
|
|
d3902470c2 | ||
|
|
68eff9a122 | ||
|
|
1edf95bcde | ||
|
|
6e63b4a5e9 | ||
|
|
9465ee1c63 | ||
|
|
10710e9908 | ||
|
|
defeb0ba17 | ||
|
|
9a8e3c59ec | ||
|
|
f23d713f46 | ||
|
|
1f84f33346 | ||
|
|
8aafb271f9 | ||
|
|
c7cebb9ea9 | ||
|
|
2de5132803 | ||
|
|
70b4071350 | ||
|
|
eb13ae0cc1 | ||
|
|
06b9da800c | ||
|
|
fdd0af8dcf | ||
|
|
cfaed35f23 | ||
|
|
f2f5391ae8 | ||
|
|
1bcf026f25 | ||
|
|
acb814021c | ||
|
|
de7cbf1c1e | ||
|
|
8c72c5b5e0 | ||
|
|
9a6553f215 | ||
|
|
3ef252c68b | ||
|
|
6cc0bd7cb4 | ||
|
|
02b0c8c80c | ||
|
|
a0f4f9a4ab | ||
|
|
86bee21a9d | ||
|
|
4897dbf3c7 | ||
|
|
b79eb9ccea | ||
|
|
408f35f4ec | ||
|
|
5b9e7ac7e4 | ||
|
|
c5b0122d72 | ||
|
|
0d83bce07d | ||
|
|
f78013b70b | ||
|
|
592e7c3f9f | ||
|
|
f152f41d0a | ||
|
|
7aaa19404c | ||
|
|
6efa31454d | ||
|
|
31e91cc3ad | ||
|
|
b9637a59ff | ||
|
|
638de2057a | ||
|
|
28f95bfe38 | ||
|
|
c296ca1682 | ||
|
|
33760da0fa | ||
|
|
36d2216e66 | ||
|
|
00d1d01353 | ||
|
|
a3e3232825 | ||
|
|
eec9663145 | ||
|
|
5f17dafdf8 | ||
|
|
581593f9b5 | ||
|
|
241f631d9d | ||
|
|
814ebb7d3d | ||
|
|
1a9a2e9753 | ||
|
|
31823f5731 | ||
|
|
835679205c | ||
|
|
88268b084c | ||
|
|
76aba9cd6b | ||
|
|
2370052b54 | ||
|
|
252f1b5464 | ||
|
|
5c288f87e5 | ||
|
|
0dec3ff5eb | ||
|
|
3f1d76cd9f | ||
|
|
bc852567f2 | ||
|
|
11356db636 | ||
|
|
1ae812ff03 | ||
|
|
3615223b52 | ||
|
|
fbf74db7a4 | ||
|
|
773cce03eb | ||
|
|
8d0cf7cdcb | ||
|
|
97c65a33db | ||
|
|
909168acdb | ||
|
|
296fe887e5 | ||
|
|
803bb1a8a4 | ||
|
|
c30225d535 | ||
|
|
2ceb727a66 | ||
|
|
7bffaeffe6 | ||
|
|
57525689a2 | ||
|
|
0ff9c5a62c | ||
|
|
30b810b309 | ||
|
|
06a630ea62 | ||
|
|
3e536af09a | ||
|
|
62a2dc003c | ||
|
|
d185ae71bf | ||
|
|
be977bab46 | ||
|
|
233b2a4f20 | ||
|
|
b0e540e67b | ||
|
|
9675c8f256 | ||
|
|
2918a6310c | ||
|
|
fc81d0ec4e | ||
|
|
e7c9410df4 | ||
|
|
feb2fc17f4 | ||
|
|
3343efbcb9 | ||
|
|
b1f3e85c4d | ||
|
|
af7127580c | ||
|
|
84f55a55cd | ||
|
|
c86ca5de87 | ||
|
|
b98fff601f | ||
|
|
80c6b62074 | ||
|
|
fe02c8f401 | ||
|
|
2e23554162 | ||
|
|
6ed2417612 | ||
|
|
5e25dd4000 | ||
|
|
b39ad5fdc3 | ||
|
|
b42032c9f8 | ||
|
|
b23d05bcc2 | ||
|
|
8b6d4943b3 | ||
|
|
a78f5348f3 | ||
|
|
73b21210c2 | ||
|
|
be2b1421f1 | ||
|
|
60e9696a23 | ||
|
|
9f42ee379d | ||
|
|
3a40542aab | ||
|
|
841947d544 | ||
|
|
c14e7bd218 | ||
|
|
96f307838f | ||
|
|
fc42c42ef5 | ||
|
|
8ee136b287 | ||
|
|
37cd4ac179 | ||
|
|
57b93839be | ||
|
|
ed31e25a38 | ||
|
|
d11116f647 | ||
|
|
3851dc7c6a | ||
|
|
25695c3e86 | ||
|
|
e1872edb5a | ||
|
|
7ecbc808c6 | ||
|
|
1568d63117 | ||
|
|
790502c51f | ||
|
|
ad83a0cd39 | ||
|
|
a6913af2bc | ||
|
|
8ff4fa8666 | ||
|
|
ced0a832cc | ||
|
|
e68ff819de | ||
|
|
bbe8b4053f | ||
|
|
554d9708da | ||
|
|
7effe8920a | ||
|
|
20d5820efc | ||
|
|
e78119bf5a | ||
|
|
b60cce3e8f | ||
|
|
d79020656c | ||
|
|
49774fa4b6 | ||
|
|
db97942393 | ||
|
|
06949f59e8 | ||
|
|
315c7e6a50 | ||
|
|
442ebce079 | ||
|
|
a2bbe15b20 | ||
|
|
caec854ed8 | ||
|
|
7e04db958b | ||
|
|
1484a207c5 | ||
|
|
09f2c3e1ed | ||
|
|
7205570611 | ||
|
|
7223d4d6aa | ||
|
|
eaa0baad7f | ||
|
|
19ac7e6935 | ||
|
|
7eb620aca7 | ||
|
|
e21111bc5f | ||
|
|
9da9f53671 | ||
|
|
53b61fa69b | ||
|
|
dca30c3aab | ||
|
|
afc7665dc8 | ||
|
|
7d7bc00765 | ||
|
|
9f7cc44949 | ||
|
|
81ff379eb8 | ||
|
|
fd4cd139f8 | ||
|
|
7be684e9bf | ||
|
|
e1268262fc | ||
|
|
ce8d9aaf65 | ||
|
|
696601abcf | ||
|
|
941cb71a49 | ||
|
|
9c76a24d01 | ||
|
|
7dc4898a21 | ||
|
|
1fd475813d | ||
|
|
9b83a6d09d | ||
|
|
128edda6c0 | ||
|
|
6070f702fa | ||
|
|
8068992244 | ||
|
|
191bde690d | ||
|
|
2262f7067c | ||
|
|
c09836e551 | ||
|
|
a7ca9430e8 | ||
|
|
03f23c4339 | ||
|
|
39ce914bf9 | ||
|
|
0b98e57938 | ||
|
|
b499c432fd | ||
|
|
fa4cdc81d0 | ||
|
|
0b52a4baa0 | ||
|
|
6080af3621 | ||
|
|
5924225bc2 | ||
|
|
7fc60f6607 | ||
|
|
44891458f1 | ||
|
|
703a66106f | ||
|
|
af33413826 | ||
|
|
54f6ece82d | ||
|
|
3b75b6b545 | ||
|
|
e65560b67a | ||
|
|
2fd081234b | ||
|
|
65be47e05b | ||
|
|
9108ce19ea | ||
|
|
2f573803c4 | ||
|
|
6d745054a4 | ||
|
|
0fc5442e42 | ||
|
|
d482e165d6 | ||
|
|
d085951f68 | ||
|
|
f3969e7d7e | ||
|
|
ae56a0f4b4 | ||
|
|
6e05874ee1 | ||
|
|
ccf049b4d5 | ||
|
|
237fbebcf5 | ||
|
|
8d381dbf49 | ||
|
|
b0d1549964 | ||
|
|
a25f066a05 | ||
|
|
0d771384da | ||
|
|
f2dd4def12 | ||
|
|
f7b6da9679 | ||
|
|
9089fa1fd1 | ||
|
|
8fb0153963 | ||
|
|
3b72b88e9c | ||
|
|
547524d07b | ||
|
|
76710475fb | ||
|
|
06d51dc30e | ||
|
|
e8481d04c0 | ||
|
|
da12d6183e | ||
|
|
3d5f63a583 | ||
|
|
8639e53348 | ||
|
|
9bcd17a151 | ||
|
|
55aed4c755 | ||
|
|
c1e9099a6c | ||
|
|
5b436ed98e | ||
|
|
f58c1b43bc | ||
|
|
92ee64dcb8 | ||
|
|
0ab9a59e99 | ||
|
|
68ca748296 | ||
|
|
21cb503c06 | ||
|
|
9ede2d7af9 | ||
|
|
caf11f3991 | ||
|
|
78354cbdaf | ||
|
|
af3af870fa | ||
|
|
5e1ab7894f | ||
|
|
951834fb57 | ||
|
|
34fa732f91 | ||
|
|
3507ebc609 | ||
|
|
5bff9aa922 | ||
|
|
912a73d42a | ||
|
|
ef8ebadc7e | ||
|
|
5581417937 | ||
|
|
75bb6d1e43 | ||
|
|
eca92c4f7e | ||
|
|
322eccb371 | ||
|
|
a48704e0e2 | ||
|
|
95a7719b65 |
@@ -4,11 +4,11 @@ xsystem4
|
||||
xsystem4 is an implementation of AliceSoft's System 4 game engine for unix-like
|
||||
operating systems.
|
||||
|
||||
NOTE: This is a work in progress. I am focusing primarily on the Japanese
|
||||
version of Sengoku Rance (and by extension, the English fan translation)
|
||||
which is currently more or less playable. Some other games from the same
|
||||
era may also work, but I have not done any significant testing on them so
|
||||
breakage is likely.
|
||||
Compatiblity
|
||||
------------
|
||||
|
||||
See the [game compatibility table](game_compatibility.md) for a list of games
|
||||
that can be played on xsystem4.
|
||||
|
||||
Building
|
||||
--------
|
||||
@@ -19,6 +19,8 @@ First install the dependencies (corresponding Debian package in parentheses):
|
||||
* bison (bison)
|
||||
* cglm (libcglm-dev) [optional, fetched by meson if not available]
|
||||
* flex (flex)
|
||||
* freetype (libfreetype-dev)
|
||||
* glew (libglew-dev)
|
||||
* meson (meson)
|
||||
* libffi (libffi-dev)
|
||||
* libpng (libpng-dev)
|
||||
@@ -26,7 +28,6 @@ First install the dependencies (corresponding Debian package in parentheses):
|
||||
* libturbojpeg (libturbojpeg0-dev)
|
||||
* libwebp (libwebp-dev)
|
||||
* SDL2 (libsdl2-dev)
|
||||
* SDL2_ttf (libsdl2-ttf-dev)
|
||||
* zlib (zlib1g-dev)
|
||||
|
||||
Then fetch the git submodules,
|
||||
@@ -46,6 +47,62 @@ Finally install it to your system (optional),
|
||||
|
||||
ninja -C build install
|
||||
|
||||
### Windows
|
||||
|
||||
xsystem4 can be built on Windows using MSYS2.
|
||||
|
||||
First install MSYS2, and then open the MINGW64 shell and run the following command,
|
||||
|
||||
pacman -S flex bison \
|
||||
mingw-w64-x86_64-gcc \
|
||||
mingw-w64-x86_64-meson \
|
||||
mingw-w64-x86_64-pkg-config \
|
||||
mingw-w64-x86_64-SDL2 \
|
||||
mingw-w64-x86_64-freetype \
|
||||
mingw-w64-x86_64-libjpeg-turbo \
|
||||
mingw-w64-x86_64-libwebp \
|
||||
mingw-w64-x86_64-libsndfile \
|
||||
mingw-w64-x86_64-glew
|
||||
|
||||
Then build the xsystem4 executable with meson,
|
||||
|
||||
mkdir build
|
||||
meson build
|
||||
ninja -C build
|
||||
|
||||
To create a portable executable, it is neccessary to copy some DLLs into the same directory as xsystem4.exe.
|
||||
You can determine the required DLLs with the following command,
|
||||
|
||||
ldd build/src/xsystem4.exe | grep mingw64
|
||||
|
||||
The `fonts` and `shaders` directories must also be shipped together with xsystem4.exe.
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
If building from source, run:
|
||||
|
||||
ninja -C build install
|
||||
|
||||
to install xsystem4 on your system.
|
||||
|
||||
### Windows
|
||||
|
||||
The recommended way to install on Windows is to copy the xsystem4 directory
|
||||
into the game directory. E.g. if Sengoku Rance is installed at
|
||||
`C:\Games\AliceSoft\Sengoku Rance`, then your filesystem should look something
|
||||
like this:
|
||||
|
||||
C:\
|
||||
Games\
|
||||
AliceSoft\
|
||||
Sengoku Rance\
|
||||
System40.ini
|
||||
xsystem4\
|
||||
xsystem4.exe
|
||||
...
|
||||
...
|
||||
|
||||
Running
|
||||
-------
|
||||
|
||||
@@ -58,3 +115,8 @@ Alternatively, run xsystem4 from within the game directory,
|
||||
|
||||
cd /path/to/game_directory
|
||||
xsystem4
|
||||
|
||||
### Windows
|
||||
|
||||
If you've installed xsystem4 into the game directory as described above, simply
|
||||
double-click `xsystem4.exe` to run the game.
|
||||
|
||||
Generated
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1678137616,
|
||||
"narHash": "sha256-T+lWTRdcYaOnZQW+Ehdlg+YldC2l9cq2GXJFPq22Nxc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "7edcdf7b169c33cd3eef9aba50521ce93ee666b8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-22.11",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
{
|
||||
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";
|
||||
|
||||
outputs = { self, nixpkgs }:
|
||||
let
|
||||
|
||||
# System types to support.
|
||||
supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ];
|
||||
|
||||
# Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'.
|
||||
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||
|
||||
# Nixpkgs instantiated for supported system types.
|
||||
nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; });
|
||||
|
||||
in {
|
||||
|
||||
# Set up xsystem4 development environment:
|
||||
# nix develop # create shell environment with dependencies available
|
||||
devShell = forAllSystems (system:
|
||||
let pkgs = nixpkgsFor.${system}; in with pkgs; pkgs.mkShell {
|
||||
nativeBuildInputs = [ meson ninja pkg-config ];
|
||||
buildInputs = [
|
||||
cglm
|
||||
SDL2
|
||||
freetype
|
||||
libffi
|
||||
libjpeg
|
||||
libwebp
|
||||
libsndfile
|
||||
glew
|
||||
readline
|
||||
flex
|
||||
bison
|
||||
];
|
||||
}
|
||||
);
|
||||
|
||||
# Build xsystem4:
|
||||
# nix build .?submodules=1 # build xsystem4 (outputs to ./result/)
|
||||
# nix shell .?submodules=1 # create shell environment with xsystem4 available
|
||||
# Install xsystem4 to user profile:
|
||||
# nix profile install .?submodules=1
|
||||
packages = forAllSystems (system:
|
||||
let pkgs = nixpkgsFor.${system}; in {
|
||||
default = with pkgs; stdenv.mkDerivation rec {
|
||||
name = "xsystem4";
|
||||
src = ./.;
|
||||
mesonAutoFeatures = "auto";
|
||||
nativeBuildInputs = [ meson ninja pkg-config ];
|
||||
buildInputs = [
|
||||
cglm
|
||||
SDL2
|
||||
freetype
|
||||
libffi
|
||||
libjpeg
|
||||
libwebp
|
||||
libsndfile
|
||||
glew
|
||||
readline
|
||||
flex
|
||||
bison
|
||||
];
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
Game Compatibility
|
||||
==================
|
||||
|
||||
The table below lists xsystem4's compatibility with various games.
|
||||
The values in the "status" column have these meanings:
|
||||
|
||||
* Supported - known to be working
|
||||
* Unsupported - known to be broken
|
||||
* Unknown - insufficient/no testing to determine compatibility
|
||||
|
||||
In practice, many of the games with "unknown" status will run fine (perhaps
|
||||
with some quirks). If you manage to play an "unknown" game to the end without
|
||||
issues, please let me know or open a pull request to update this table.
|
||||
|
||||
| Game | Status | Notes |
|
||||
| ------------------------------------------------------- | ----------- | ----- |
|
||||
| DALK外伝 | Unsupported | |
|
||||
| ままにょにょ | Unsupported | |
|
||||
| 大番長 -Big Bang Age- | Supported | |
|
||||
| Daibanchou -Big Bang Age- (EN) | Supported | |
|
||||
| 魔女の贖罪 | Unknown | |
|
||||
| Rance VI -ゼス崩壊- | Supported | |
|
||||
| Rance VI -Collapse of Zeth- (EN) | Supported | |
|
||||
| ウルトラ魔法少女まなな Vol.1~Vol.5 | Unknown | |
|
||||
| 鈴宮刑務娼館 | Unknown | From ALICEの館7 |
|
||||
| しまいま | Unknown | From ALICEの館7 |
|
||||
| DUNGEONS & DOLLS | Unsupported | From ALICEの館7 |
|
||||
| こすぷれれもん | Unknown | From ALICEの館7 |
|
||||
| エルメイフラワー | Unknown | From ALICEの館7 |
|
||||
| CGは17年ゼミ | Unknown | From ALICEの館7 |
|
||||
| でーぴーえすVer_N | Unknown | From ALICEの館7 |
|
||||
| 大番長外伝 | Unknown | From ALICEの館7 |
|
||||
| ZERO | Unknown | From ALICEの館7 |
|
||||
| ぷろGRX | Unknown | From ALICEの館7 |
|
||||
| Girls | Unknown | From ALICEの館7 |
|
||||
| 神武 -カムナガラ- | Unknown | From ALICEの館7 |
|
||||
| オニむス | Unknown | From ALICEの館7 |
|
||||
| 透色 | Unknown | From ALICEの館7 |
|
||||
| DEBUT | Unknown | From ALICEの館7 |
|
||||
| ランス6その後 | Unknown | From ALICEの館7 |
|
||||
| ほいっぷハニー | Unknown | From ALICEの館7 |
|
||||
| Text Jam Session | Unknown | From ALICEの館7 |
|
||||
| 没ロゴの哀しみ | Unknown | From ALICEの館7 |
|
||||
| スタッフコーナー | Unknown | From ALICEの館7 |
|
||||
| ぱすてるチャイムContinue | Unsupported | |
|
||||
| GALZOOアイランド | Supported | |
|
||||
| ぱすちゃC++ | Unknown | |
|
||||
| よくばりサボテン | Supported | |
|
||||
| 妻しぼり | Unknown | |
|
||||
| 戦国ランス | Supported | |
|
||||
| Sengoku Rance (EN FanTL) | Supported | |
|
||||
| Sengoku Rance (MangaGamer) | Supported | |
|
||||
| だぶる先生らいふっ | Unknown | |
|
||||
| お嬢様をいいなりにするゲーム | Unknown | |
|
||||
| 超昂閃忍ハルカ | Unknown | |
|
||||
| 戦国ランス~三匹が斬ったり突いたり燃やしたり | Unknown | |
|
||||
| Sengoku Rance -Three for the Slash! Thrust! Burn!- (EN) | Unknown | |
|
||||
| AliveZ | Unknown | |
|
||||
| エドランゼ ~Come on!水戸姫様~ | Unknown | |
|
||||
| 闘神都市III | Supported | |
|
||||
| ばにしゅ!~おっぱいの消えた王国~ | Unknown | |
|
||||
| 僕だけの保健室 | Unknown | |
|
||||
| ももいろガーディアン | Unknown | |
|
||||
| 超昂閃忍ハルカ ハルカVSエスカレイヤー | Unknown | From Alice 2010 |
|
||||
| 超昂閃忍ハルカ ―疾風!?忍者大作戦― | Unknown | From Alice 2010 |
|
||||
| 眼鏡のメイと夏休み | Unknown | From Alice 2010 |
|
||||
| レイチェルの桜貝 | Unknown | From Alice 2010 |
|
||||
| 砂漠のニッカ | Unknown | From Alice 2010 |
|
||||
| ばにしゅ!~この手のひらにおっぱいを~ | Unknown | From Alice 2010 |
|
||||
| はるうられ -校内赤線区域- | Supported | From Alice 2010 |
|
||||
| わいどにょ | Supported | From Alice 2010 |
|
||||
| ランス02 -反逆の少女たち- | Supported | From Alice 2010 |
|
||||
| Rance 02 -The Rebellious Maidens- (Fan TL) | Supported | |
|
||||
| Rance 02 -The Rebellious Maidens- (MangaGamer) | Supported | |
|
||||
| All games released after Alice 2010 | Unsupported | |
|
||||
@@ -36,16 +36,15 @@ enum asset_type {
|
||||
|
||||
const char *asset_strtype(enum asset_type type);
|
||||
void asset_manager_init(void);
|
||||
bool asset_manager_load_archive(enum asset_type type, const char *archive_name);
|
||||
bool asset_exists(enum asset_type type, int no);
|
||||
bool asset_exists_by_name(enum asset_type type, const char *name, int *id_out);
|
||||
struct archive_data *asset_get(enum asset_type type, int no);
|
||||
struct archive_data *asset_get_by_name(enum asset_type type, const char *name, int *id_out);
|
||||
|
||||
// FIXME: CG code should be refactored to read from buffer only, not archive,
|
||||
// then caller can just use asset_get and cg_load
|
||||
struct cg *asset_cg_load(int no);
|
||||
struct cg *asset_cg_load_by_name(const char *name, int *no);
|
||||
int asset_cg_name_to_index(const char *name);
|
||||
bool asset_cg_exists(int no);
|
||||
struct cg *asset_cg_load_by_name(const char *name, int *id_out);
|
||||
bool asset_cg_get_metrics(int no, struct cg_metrics *metrics);
|
||||
void asset_cg_index_init(void);
|
||||
bool asset_cg_get_metrics_by_name(const char *name, struct cg_metrics *metrics);
|
||||
|
||||
#endif /* SYSTEM4_ASSET_MANAGER_H */
|
||||
|
||||
@@ -79,6 +79,8 @@ int bgm_get_time_length(int id);
|
||||
//int wav_prepare_from_file(int id, char *filename);
|
||||
//int bgm_prepare_from_file(int id, char *filename);
|
||||
|
||||
int wav_get_group_num_from_data_num(int no);
|
||||
|
||||
struct archive_data;
|
||||
|
||||
// Takes ownership of dfile.
|
||||
|
||||
@@ -210,6 +210,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
|
||||
|
||||
/* These utilities create an Array of count items.
|
||||
* The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateArray_cb(int count, cJSON *(*get_item)(int,void*), void *data);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray_cb(int count, int (*get_number)(int,void*), void *data);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
|
||||
|
||||
+27
-1
@@ -19,11 +19,15 @@
|
||||
#ifdef DEBUGGER_ENABLED
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "system4/instructions.h"
|
||||
#include "vm.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
#define DBG_ERROR(fmt, ...) sys_warning("ERROR: " fmt "\n", ##__VA_ARGS__)
|
||||
|
||||
struct ain_variable;
|
||||
|
||||
struct breakpoint {
|
||||
enum opcode restore_op;
|
||||
void *data;
|
||||
@@ -32,7 +36,19 @@ struct breakpoint {
|
||||
int count;
|
||||
};
|
||||
|
||||
struct dbg_cmd {
|
||||
const char *fullname;
|
||||
const char *shortname;
|
||||
const char *arg_description;
|
||||
const char *description;
|
||||
unsigned min_args;
|
||||
unsigned max_args;
|
||||
void (*run)(unsigned nr_args, char **args);
|
||||
};
|
||||
|
||||
extern bool dbg_enabled;
|
||||
extern bool dbg_start_in_debugger;
|
||||
extern unsigned dbg_current_frame;
|
||||
|
||||
void dbg_init(void);
|
||||
void dbg_fini(void);
|
||||
@@ -41,11 +57,21 @@ void dbg_repl(void);
|
||||
void dbg_continue(void);
|
||||
void dbg_quit(void);
|
||||
void dbg_start(void(*fun)(void*), void *data);
|
||||
void dbg_cmd_init(void);
|
||||
void dbg_cmd_repl(void);
|
||||
enum opcode dbg_handle_breakpoint(unsigned bp_no);
|
||||
void dbg_cmd_add_module(const char *name, unsigned nr_commands, struct dbg_cmd *commands);
|
||||
void dbg_handle_breakpoint(void);
|
||||
bool dbg_set_function_breakpoint(const char *_name, void(*cb)(struct breakpoint*), void *data);
|
||||
bool dbg_set_address_breakpoint(uint32_t address, void(*cb)(struct breakpoint*), void *data);
|
||||
bool dbg_set_step_over_breakpoint(void);
|
||||
bool dbg_set_step_into_breakpoint(void);
|
||||
bool dbg_set_finish_breakpoint(void);
|
||||
void dbg_print_frame(unsigned no);
|
||||
void dbg_print_stack_trace(void);
|
||||
void dbg_print_stack(void);
|
||||
void dbg_print_vm_state(void);
|
||||
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);
|
||||
|
||||
#ifdef HAVE_SCHEME
|
||||
void dbg_scm_init(void);
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "gfx/gl.h"
|
||||
#include "plugin.h"
|
||||
|
||||
struct page;
|
||||
struct dgn;
|
||||
@@ -51,6 +52,7 @@ struct camera {
|
||||
*/
|
||||
|
||||
struct dungeon_context {
|
||||
struct draw_plugin plugin;
|
||||
enum draw_dungeon_version version;
|
||||
int surface;
|
||||
bool loaded;
|
||||
@@ -68,7 +70,6 @@ struct dungeon_context *dungeon_context_create(enum draw_dungeon_version version
|
||||
void dungeon_context_free(struct dungeon_context *ctx);
|
||||
bool dungeon_load(struct dungeon_context *ctx, int num);
|
||||
void dungeon_set_camera(int surface, float x, float y, float z, float angle, float angle_p);
|
||||
void dungeon_render(struct dungeon_context *ctx);
|
||||
void dungeon_set_walked(int surface, int x, int y, int z, int flag);
|
||||
int dungeon_get_walked(int surface, int x, int y, int z);
|
||||
int dungeon_calc_conquer(int surface);
|
||||
@@ -76,6 +77,5 @@ bool dungeon_load_walk_data(int surface, int map, struct page **page);
|
||||
bool dungeon_save_walk_data(int surface, int map, struct page **page);
|
||||
|
||||
struct dungeon_context *dungeon_get_context(int surface);
|
||||
void dungeon_update(void);
|
||||
|
||||
#endif /* SYSTEM4_DUNGEON_H */
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ enum effect {
|
||||
EFFECT_VERTICAL_BAR_BLUR_HD = 41,
|
||||
EFFECT_AMAP_CROSSFADE2 = 42,
|
||||
EFFECT_ZOOM_LR = 43,
|
||||
EFFECT_ZOOR_RL = 44,
|
||||
EFFECT_ZOOM_RL = 44,
|
||||
EFFECT_CROSSFADE_LR = 45,
|
||||
EFFECT_CROSSFADE_RL = 46,
|
||||
EFFECT_PIXEL_EXPLOSION = 47,
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM4_FONT_H
|
||||
#define SYSTEM4_FONT_H
|
||||
|
||||
#include "system4.h"
|
||||
#include "gfx/gfx.h"
|
||||
|
||||
struct fnl;
|
||||
struct hash_table;
|
||||
|
||||
// standard font weight values
|
||||
enum {
|
||||
FW_NORMAL = 400,
|
||||
FW_BOLD = 700,
|
||||
FW_MEDIUM = 1400,
|
||||
FW_HEAVY = 1700
|
||||
};
|
||||
|
||||
enum font_face {
|
||||
FONT_GOTHIC = 0,
|
||||
FONT_MINCHO = 1
|
||||
};
|
||||
|
||||
enum font_weight {
|
||||
FONT_WEIGHT_NORMAL,
|
||||
FONT_WEIGHT_BOLD,
|
||||
FONT_WEIGHT_HEAVY
|
||||
};
|
||||
#define NR_FONT_WEIGHTS (FONT_WEIGHT_HEAVY+1)
|
||||
|
||||
struct glyph {
|
||||
Rectangle rect;
|
||||
float advance;
|
||||
Texture t[NR_FONT_WEIGHTS];
|
||||
};
|
||||
|
||||
struct font_size {
|
||||
float size;
|
||||
int y_offset;
|
||||
struct font *font;
|
||||
struct hash_table *glyph_table;
|
||||
};
|
||||
|
||||
enum charmap {
|
||||
CHARMAP_UNICODE,
|
||||
CHARMAP_SJIS
|
||||
};
|
||||
|
||||
struct font {
|
||||
enum charmap charmap;
|
||||
struct font_size *(*get_size)(struct font *font, float size);
|
||||
float (*get_actual_size)(struct font *font, float size);
|
||||
float (*get_actual_size_round_down)(struct font *font, float size);
|
||||
bool (*get_glyph)(struct font_size *size, struct glyph *dst, uint32_t code, enum font_weight weight);
|
||||
float (*size_char)(struct font_size *size, uint32_t code);
|
||||
};
|
||||
|
||||
struct text_style {
|
||||
unsigned face;
|
||||
float size;
|
||||
float bold_width; // SengokuRanceFont
|
||||
unsigned weight; // SACT2, etc.
|
||||
float edge_left;
|
||||
float edge_up;
|
||||
float edge_right;
|
||||
float edge_down;
|
||||
SDL_Color color;
|
||||
SDL_Color edge_color;
|
||||
float scale_x;
|
||||
float space_scale_x;
|
||||
float font_spacing;
|
||||
|
||||
// cached, for SengokuRanceFont
|
||||
struct font_size *font_size;
|
||||
};
|
||||
|
||||
static inline float text_style_edge_width(struct text_style *ts)
|
||||
{
|
||||
return max(max(ts->edge_up, ts->edge_down), max(ts->edge_left, ts->edge_right));
|
||||
}
|
||||
|
||||
static inline bool text_style_has_edge(struct text_style *ts)
|
||||
{
|
||||
return text_style_edge_width(ts) > 0.01f;
|
||||
}
|
||||
|
||||
static inline void text_style_set_edge_width(struct text_style *ts, float w)
|
||||
{
|
||||
ts->edge_left = w;
|
||||
ts->edge_up = w;
|
||||
ts->edge_right = w;
|
||||
ts->edge_down = w;
|
||||
}
|
||||
|
||||
extern bool gfx_text_advance_edges;
|
||||
|
||||
void gfx_font_init(void);
|
||||
void ft_font_init(void);
|
||||
struct font *ft_font_load(const char *path);
|
||||
struct font *fnl_font_load(struct fnl *lib, unsigned index);
|
||||
|
||||
bool gfx_set_font(enum font_face face, unsigned int size);
|
||||
bool gfx_set_font_size(unsigned int size);
|
||||
bool gfx_set_font_face(enum font_face face);
|
||||
bool gfx_set_font_weight(int weight);
|
||||
bool gfx_set_font_underline(bool on);
|
||||
bool gfx_set_font_strikeout(bool on);
|
||||
bool gfx_set_font_space(int space);
|
||||
bool gfx_set_font_color(SDL_Color color);
|
||||
|
||||
int gfx_get_font_size(void);
|
||||
enum font_face gfx_get_font_face(void);
|
||||
int gfx_get_font_weight(void);
|
||||
bool gfx_get_font_underline(void);
|
||||
bool gfx_get_font_strikeout(void);
|
||||
int gfx_get_font_space(void);
|
||||
SDL_Color gfx_get_font_color(void);
|
||||
void gfx_set_font_name(const char *name);
|
||||
|
||||
int gfx_render_text(Texture *dst, float x, int y, char *msg, struct text_style *ts);
|
||||
void gfx_draw_text_to_amap(Texture *dst, int x, int y, char *text);
|
||||
void gfx_draw_text_to_pmap(Texture *dst, int x, int y, char *text);
|
||||
float gfx_size_char(struct text_style *ts, const char *ch);
|
||||
float gfx_size_text(struct text_style *ts, const char *text);
|
||||
float gfx_get_actual_font_size(unsigned face, float size);
|
||||
float gfx_get_actual_font_size_round_down(unsigned face, float size);
|
||||
|
||||
void gfx_print_text_style(struct text_style *style, int indent);
|
||||
|
||||
#endif /* SYSTEM4_FONT_H */
|
||||
+35
-90
@@ -25,6 +25,7 @@
|
||||
|
||||
struct cg;
|
||||
struct text_metrics;
|
||||
enum cg_type;
|
||||
|
||||
enum draw_method {
|
||||
DRAW_METHOD_NORMAL,
|
||||
@@ -38,8 +39,6 @@ typedef struct texture {
|
||||
mat4 world_transform;
|
||||
int w, h;
|
||||
bool has_alpha;
|
||||
int alpha_mod;
|
||||
enum draw_method draw_method;
|
||||
} Texture;
|
||||
|
||||
struct gfx_render_job;
|
||||
@@ -51,7 +50,6 @@ typedef struct shader {
|
||||
GLuint view_transform;
|
||||
GLuint texture;
|
||||
GLuint vertex;
|
||||
GLuint alpha_mod;
|
||||
void (*prepare)(struct gfx_render_job *, void *);
|
||||
} Shader;
|
||||
|
||||
@@ -72,6 +70,7 @@ void gfx_update_screen_scale(void);
|
||||
void gfx_set_wait_vsync(bool wait);
|
||||
|
||||
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);
|
||||
|
||||
// rendering
|
||||
void gfx_set_clear_color(int r, int g, int b, int a);
|
||||
@@ -80,6 +79,7 @@ void gfx_swap(void);
|
||||
void gfx_prepare_job(struct gfx_render_job *job);
|
||||
void gfx_run_job(struct gfx_render_job *job);
|
||||
void gfx_render(struct gfx_render_job *job);
|
||||
void _gfx_render_texture(struct shader *s, struct texture *t, Rectangle *r, void *data);
|
||||
void gfx_render_texture(struct texture *t, Rectangle *r);
|
||||
|
||||
// texture management
|
||||
@@ -88,12 +88,15 @@ void gfx_init_texture_with_cg(struct texture *t, struct cg *cg);
|
||||
void gfx_init_texture_rgba(struct texture *t, int w, int h, SDL_Color color);
|
||||
void gfx_init_texture_rgb(struct texture *t, int w, int h, SDL_Color color);
|
||||
void gfx_init_texture_with_pixels(struct texture *t, int w, int h, void *pixels);
|
||||
void gfx_init_texture_amap(struct texture *t, int w, int h, uint8_t *amap, SDL_Color color);
|
||||
void gfx_init_texture_rmap(struct texture *t, int w, int h, uint8_t *rmap);
|
||||
void gfx_copy_main_surface(struct texture *dst);
|
||||
void gfx_delete_texture(struct texture *t);
|
||||
GLuint gfx_set_framebuffer(GLenum target, Texture *t, int x, int y, int w, int h);
|
||||
void gfx_reset_framebuffer(GLenum target, GLuint fbo);
|
||||
SDL_Color gfx_get_pixel(Texture *t, int x, int y);
|
||||
void *gfx_get_pixels(Texture *t);
|
||||
int gfx_save_texture(Texture *t, const char *path, enum cg_type);
|
||||
|
||||
// drawing
|
||||
void gfx_draw_init(void);
|
||||
@@ -108,28 +111,47 @@ void gfx_copy_use_amap_border(struct texture *dst, int dx, int dy, struct textur
|
||||
void gfx_copy_amap_max(struct texture *dst, int dx, int dy, struct texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_copy_amap_min(struct texture *dst, int dx, int dy, struct texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_blend(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int a);
|
||||
void gfx_blend_src_bright(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int a, int rate);
|
||||
void gfx_blend_add_satur(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_blend_amap(struct texture *dst, int dx, int dy, struct texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_blend_amap_src_only(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_blend_amap_color(struct texture *dst, int dx, int dy, struct texture *src, int sx, int sy, int w, int h, int r, int g, int b);
|
||||
void gfx_blend_amap_color_alpha(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int r, int g, int b, int a);
|
||||
void gfx_blend_amap_alpha(struct texture *dst, int dx, int dy, struct texture *src, int sx, int sy, int w, int h, int a);
|
||||
void gfx_blend_amap_bright(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int rate);
|
||||
void gfx_blend_amap_alpha_src_bright(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int alpha, int rate);
|
||||
void gfx_blend_use_amap_color(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int r, int g, int b, int rate);
|
||||
void gfx_blend_screen(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_blend_multiply(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_blend_screen_alpha(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int a);
|
||||
void gfx_fill(struct texture *dst, int x, int y, int w, int h, int r, int g, int b);
|
||||
void gfx_fill_alpha_color(struct texture *dst, int x, int y, int w, int h, int r, int g, int b, int a);
|
||||
void gfx_fill_amap(struct texture *dst, int x, int y, int w, int h, int a);
|
||||
void gfx_fill_amap_over_border(Texture *dst, int x, int y, int w, int h, int alpha, int border);
|
||||
void gfx_fill_amap_under_border(Texture *dst, int x, int y, int w, int h, int alpha, int border);
|
||||
void gfx_fill_amap_gradation_ud(Texture *dst, int x, int y, int w, int h, int up_a, int down_a);
|
||||
void gfx_fill_screen(Texture *dst, int x, int y, int w, int h, int r, int g, int b);
|
||||
void gfx_fill_multiply(Texture *dst, int x, int y, int w, int h, int r, int g, int b);
|
||||
void gfx_satur_dp_dpxsa(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_screen_da_daxsa(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_add_da_daxsa(struct texture *dst, int dx, int dy, struct texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_blend_da_daxsa(struct texture *dst, int dx, int dy, struct texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_sub_da_daxsa(struct texture *dst, int dx, int dy, struct texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_bright_dest_only(Texture *dst, int x, int y, int w, int h, int rate);
|
||||
void gfx_copy_stretch(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh);
|
||||
void gfx_copy_stretch_amap(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh);
|
||||
void gfx_copy_stretch_blend(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh, int a);
|
||||
void gfx_copy_stretch_blend_amap(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh);
|
||||
void gfx_copy_stretch_blend_amap_alpha(struct texture *dst, int dx, int dy, int dw, int dh, struct texture *src, int sx, int sy, int sw, int sh, int a);
|
||||
void gfx_copy_rot_zoom(Texture *dst, Texture *src, int sx, int sy, int w, int h, float rotate, float mag);
|
||||
void gfx_copy_rot_zoom_amap(Texture *dst, Texture *src, int sx, int sy, int w, int h, float rotate, float mag);
|
||||
void gfx_copy_rot_zoom_use_amap(Texture *dst, Texture *src, int sx, int sy, int w, int h, float rotate, float mag);
|
||||
void gfx_copy_reverse_LR(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_copy_reverse_amap_LR(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_fill_amap_over_border(Texture *dst, int x, int y, int w, int h, int alpha, int border);
|
||||
void gfx_fill_amap_under_border(Texture *dst, int x, int y, int w, int h, int alpha, int border);
|
||||
void gfx_copy_rotate_y(Texture *dst, Texture *front, Texture *back, int sx, int sy, int w, int h, float rot, float mag);
|
||||
void gfx_copy_rotate_y_use_amap(Texture *dst, Texture *front, Texture *back, int sx, int sy, int w, int h, float rot, float mag);
|
||||
void gfx_copy_rotate_x(Texture *dst, Texture *front, Texture *back, int sx, int sy, int w, int h, float rot, float mag);
|
||||
void gfx_copy_rotate_x_use_amap(Texture *dst, Texture *front, Texture *back, int sx, int sy, int w, int h, float rot, float mag);
|
||||
void gfx_copy_width_blur(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int blur);
|
||||
void gfx_copy_height_blur(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int blur);
|
||||
void gfx_copy_amap_width_blur(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int blur);
|
||||
@@ -137,91 +159,14 @@ void gfx_copy_amap_height_blur(Texture *dst, int dx, int dy, Texture *src, int s
|
||||
void gfx_copy_with_alpha_map(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h);
|
||||
void gfx_fill_with_alpha(Texture *dst, int x, int y, int w, int h, int r, int g, int b, int a);
|
||||
void gfx_copy_stretch_with_alpha_map(Texture *dst, int dx, int dy, int dw, int dh, Texture *src, int sx, int sy, int sw, int sh);
|
||||
void gfx_draw_glyph(Texture *dst, float dx, int dy, Texture *glyph, SDL_Color color, float scale_x, float bold_width);
|
||||
void gfx_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);
|
||||
|
||||
enum {
|
||||
FW_NORMAL = 400,
|
||||
FW_BOLD = 700,
|
||||
FW_NORMAL2 = 1400,
|
||||
FW_BOLD2 = 1700
|
||||
};
|
||||
|
||||
enum font_face {
|
||||
FONT_GOTHIC = 0,
|
||||
FONT_MINCHO = 1
|
||||
};
|
||||
|
||||
extern const char *font_paths[2];
|
||||
|
||||
struct text_metrics {
|
||||
SDL_Color color;
|
||||
SDL_Color outline_color;
|
||||
unsigned int size;
|
||||
int weight;
|
||||
enum font_face face;
|
||||
int outline_left;
|
||||
int outline_up;
|
||||
int outline_right;
|
||||
int outline_down;
|
||||
};
|
||||
|
||||
struct font_metrics {
|
||||
int size;
|
||||
enum font_face face;
|
||||
int weight;
|
||||
bool underline;
|
||||
bool strikeout;
|
||||
int space;
|
||||
SDL_Color color;
|
||||
};
|
||||
|
||||
void gfx_font_init(void);
|
||||
bool gfx_set_font(enum font_face face, unsigned int size);
|
||||
|
||||
bool gfx_set_font_size(unsigned int size);
|
||||
bool gfx_set_font_face(enum font_face face);
|
||||
bool gfx_set_font_weight(int weight);
|
||||
bool gfx_set_font_underline(bool on);
|
||||
bool gfx_set_font_strikeout(bool on);
|
||||
bool gfx_set_font_space(int space);
|
||||
bool gfx_set_font_color(SDL_Color color);
|
||||
|
||||
int gfx_get_font_size(void);
|
||||
enum font_face gfx_get_font_face(void);
|
||||
int gfx_get_font_weight(void);
|
||||
bool gfx_get_font_underline(void);
|
||||
bool gfx_get_font_strikeout(void);
|
||||
int gfx_get_font_space(void);
|
||||
SDL_Color gfx_get_font_color(void);
|
||||
void gfx_set_font_name(const char *name);
|
||||
|
||||
int gfx_render_text(Texture *dst, Point pos, char *msg, struct text_metrics *tm, int char_space);
|
||||
void gfx_draw_text_to_amap(Texture *dst, int x, int y, char *text);
|
||||
void gfx_draw_text_to_pmap(Texture *dst, int x, int y, char *text);
|
||||
|
||||
struct fnl_font_inst {
|
||||
struct fnl_font_face *font;
|
||||
float scale;
|
||||
SDL_Color color;
|
||||
SDL_Color outline_color;
|
||||
int outline_size;
|
||||
};
|
||||
|
||||
struct text_style {
|
||||
unsigned font_type;
|
||||
float size;
|
||||
float bold_width;
|
||||
SDL_Color color;
|
||||
float edge_width;
|
||||
SDL_Color edge_color;
|
||||
float scale_x;
|
||||
float space_scale_x;
|
||||
float font_spacing;
|
||||
|
||||
struct fnl_font_size *font_size;
|
||||
};
|
||||
|
||||
struct fnl;
|
||||
struct fnl_font_face;
|
||||
int fnl_draw_text(struct fnl *fnl, struct text_style *ts, Texture *dst, int x, int y, char *text);
|
||||
// debug printing
|
||||
void gfx_print_color(SDL_Color *c);
|
||||
void gfx_print_rectangle(Rectangle *r);
|
||||
void gfx_print_point(Point *p);
|
||||
void gfx_print_texture(struct texture *t, int indent);
|
||||
|
||||
#endif /* SYSTEM4_SDL_CORE_H */
|
||||
|
||||
@@ -142,6 +142,7 @@ bool key_is_down(enum sact_keycode code);
|
||||
void key_clear_flag(void);
|
||||
bool joy_key_is_down(uint8_t code);
|
||||
void joy_clear_flag(void);
|
||||
bool joy_get_stick_status(int controller, int type, float *deg, float *pow);
|
||||
void mouse_get_pos(int *x, int *y);
|
||||
void mouse_set_pos(int x, int y);
|
||||
void mouse_get_wheel(int *forward, int *back);
|
||||
|
||||
-627
@@ -1,627 +0,0 @@
|
||||
/* The MIT License
|
||||
|
||||
Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
An example:
|
||||
|
||||
#include "khash.h"
|
||||
KHASH_MAP_INIT_INT(32, char)
|
||||
int main() {
|
||||
int ret, is_missing;
|
||||
khiter_t k;
|
||||
khash_t(32) *h = kh_init(32);
|
||||
k = kh_put(32, h, 5, &ret);
|
||||
kh_value(h, k) = 10;
|
||||
k = kh_get(32, h, 10);
|
||||
is_missing = (k == kh_end(h));
|
||||
k = kh_get(32, h, 5);
|
||||
kh_del(32, h, k);
|
||||
for (k = kh_begin(h); k != kh_end(h); ++k)
|
||||
if (kh_exist(h, k)) kh_value(h, k) = 1;
|
||||
kh_destroy(32, h);
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
2013-05-02 (0.2.8):
|
||||
|
||||
* Use quadratic probing. When the capacity is power of 2, stepping function
|
||||
i*(i+1)/2 guarantees to traverse each bucket. It is better than double
|
||||
hashing on cache performance and is more robust than linear probing.
|
||||
|
||||
In theory, double hashing should be more robust than quadratic probing.
|
||||
However, my implementation is probably not for large hash tables, because
|
||||
the second hash function is closely tied to the first hash function,
|
||||
which reduce the effectiveness of double hashing.
|
||||
|
||||
Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php
|
||||
|
||||
2011-12-29 (0.2.7):
|
||||
|
||||
* Minor code clean up; no actual effect.
|
||||
|
||||
2011-09-16 (0.2.6):
|
||||
|
||||
* The capacity is a power of 2. This seems to dramatically improve the
|
||||
speed for simple keys. Thank Zilong Tan for the suggestion. Reference:
|
||||
|
||||
- http://code.google.com/p/ulib/
|
||||
- http://nothings.org/computer/judy/
|
||||
|
||||
* Allow to optionally use linear probing which usually has better
|
||||
performance for random input. Double hashing is still the default as it
|
||||
is more robust to certain non-random input.
|
||||
|
||||
* Added Wang's integer hash function (not used by default). This hash
|
||||
function is more robust to certain non-random input.
|
||||
|
||||
2011-02-14 (0.2.5):
|
||||
|
||||
* Allow to declare global functions.
|
||||
|
||||
2009-09-26 (0.2.4):
|
||||
|
||||
* Improve portability
|
||||
|
||||
2008-09-19 (0.2.3):
|
||||
|
||||
* Corrected the example
|
||||
* Improved interfaces
|
||||
|
||||
2008-09-11 (0.2.2):
|
||||
|
||||
* Improved speed a little in kh_put()
|
||||
|
||||
2008-09-10 (0.2.1):
|
||||
|
||||
* Added kh_clear()
|
||||
* Fixed a compiling error
|
||||
|
||||
2008-09-02 (0.2.0):
|
||||
|
||||
* Changed to token concatenation which increases flexibility.
|
||||
|
||||
2008-08-31 (0.1.2):
|
||||
|
||||
* Fixed a bug in kh_get(), which has not been tested previously.
|
||||
|
||||
2008-08-31 (0.1.1):
|
||||
|
||||
* Added destructor
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __AC_KHASH_H
|
||||
#define __AC_KHASH_H
|
||||
|
||||
/*!
|
||||
@header
|
||||
|
||||
Generic hash table library.
|
||||
*/
|
||||
|
||||
#define AC_VERSION_KHASH_H "0.2.8"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
/* compiler specific configuration */
|
||||
|
||||
#if UINT_MAX == 0xffffffffu
|
||||
typedef unsigned int khint32_t;
|
||||
#elif ULONG_MAX == 0xffffffffu
|
||||
typedef unsigned long khint32_t;
|
||||
#endif
|
||||
|
||||
#if ULONG_MAX == ULLONG_MAX
|
||||
typedef unsigned long khint64_t;
|
||||
#else
|
||||
typedef unsigned long long khint64_t;
|
||||
#endif
|
||||
|
||||
#ifndef kh_inline
|
||||
#ifdef _MSC_VER
|
||||
#define kh_inline __inline
|
||||
#else
|
||||
#define kh_inline inline
|
||||
#endif
|
||||
#endif /* kh_inline */
|
||||
|
||||
#ifndef klib_unused
|
||||
#if (defined __clang__ && __clang_major__ >= 3) || (defined __GNUC__ && __GNUC__ >= 3)
|
||||
#define klib_unused __attribute__ ((__unused__))
|
||||
#else
|
||||
#define klib_unused
|
||||
#endif
|
||||
#endif /* klib_unused */
|
||||
|
||||
typedef khint32_t khint_t;
|
||||
typedef khint_t khiter_t;
|
||||
|
||||
#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
|
||||
#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
|
||||
#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
|
||||
#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
|
||||
#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
|
||||
#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
|
||||
#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
|
||||
|
||||
#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
|
||||
|
||||
#ifndef kroundup32
|
||||
#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
|
||||
#endif
|
||||
|
||||
#ifndef kcalloc
|
||||
#define kcalloc(N,Z) calloc(N,Z)
|
||||
#endif
|
||||
#ifndef kmalloc
|
||||
#define kmalloc(Z) malloc(Z)
|
||||
#endif
|
||||
#ifndef krealloc
|
||||
#define krealloc(P,Z) realloc(P,Z)
|
||||
#endif
|
||||
#ifndef kfree
|
||||
#define kfree(P) free(P)
|
||||
#endif
|
||||
|
||||
static const double __ac_HASH_UPPER = 0.77;
|
||||
|
||||
#define __KHASH_TYPE(name, khkey_t, khval_t) \
|
||||
typedef struct kh_##name##_s { \
|
||||
khint_t n_buckets, size, n_occupied, upper_bound; \
|
||||
khint32_t *flags; \
|
||||
khkey_t *keys; \
|
||||
khval_t *vals; \
|
||||
} kh_##name##_t;
|
||||
|
||||
#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \
|
||||
extern kh_##name##_t *kh_init_##name(void); \
|
||||
extern void kh_destroy_##name(kh_##name##_t *h); \
|
||||
extern void kh_clear_##name(kh_##name##_t *h); \
|
||||
extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \
|
||||
extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
|
||||
extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
|
||||
extern void kh_del_##name(kh_##name##_t *h, khint_t x);
|
||||
|
||||
#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
|
||||
SCOPE kh_##name##_t *kh_init_##name(void) { \
|
||||
return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \
|
||||
} \
|
||||
SCOPE void kh_destroy_##name(kh_##name##_t *h) \
|
||||
{ \
|
||||
if (h) { \
|
||||
kfree((void *)h->keys); kfree(h->flags); \
|
||||
kfree((void *)h->vals); \
|
||||
kfree(h); \
|
||||
} \
|
||||
} \
|
||||
SCOPE void kh_clear_##name(kh_##name##_t *h) \
|
||||
{ \
|
||||
if (h && h->flags) { \
|
||||
memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
|
||||
h->size = h->n_occupied = 0; \
|
||||
} \
|
||||
} \
|
||||
SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
|
||||
{ \
|
||||
if (h->n_buckets) { \
|
||||
khint_t k, i, last, mask, step = 0; \
|
||||
mask = h->n_buckets - 1; \
|
||||
k = __hash_func(key); i = k & mask; \
|
||||
last = i; \
|
||||
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
|
||||
i = (i + (++step)) & mask; \
|
||||
if (i == last) return h->n_buckets; \
|
||||
} \
|
||||
return __ac_iseither(h->flags, i)? h->n_buckets : i; \
|
||||
} else return 0; \
|
||||
} \
|
||||
SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
|
||||
{ /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
|
||||
khint32_t *new_flags = 0; \
|
||||
khint_t j = 1; \
|
||||
{ \
|
||||
kroundup32(new_n_buckets); \
|
||||
if (new_n_buckets < 4) new_n_buckets = 4; \
|
||||
if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \
|
||||
else { /* hash table size to be changed (shrink or expand); rehash */ \
|
||||
new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
|
||||
if (!new_flags) return -1; \
|
||||
memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
|
||||
if (h->n_buckets < new_n_buckets) { /* expand */ \
|
||||
khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
|
||||
if (!new_keys) { kfree(new_flags); return -1; } \
|
||||
h->keys = new_keys; \
|
||||
if (kh_is_map) { \
|
||||
khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
|
||||
if (!new_vals) { kfree(new_flags); return -1; } \
|
||||
h->vals = new_vals; \
|
||||
} \
|
||||
} /* otherwise shrink */ \
|
||||
} \
|
||||
} \
|
||||
if (j) { /* rehashing is needed */ \
|
||||
for (j = 0; j != h->n_buckets; ++j) { \
|
||||
if (__ac_iseither(h->flags, j) == 0) { \
|
||||
khkey_t key = h->keys[j]; \
|
||||
khval_t val; \
|
||||
khint_t new_mask; \
|
||||
new_mask = new_n_buckets - 1; \
|
||||
if (kh_is_map) val = h->vals[j]; \
|
||||
__ac_set_isdel_true(h->flags, j); \
|
||||
while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
|
||||
khint_t k, i, step = 0; \
|
||||
k = __hash_func(key); \
|
||||
i = k & new_mask; \
|
||||
while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \
|
||||
__ac_set_isempty_false(new_flags, i); \
|
||||
if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
|
||||
{ khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
|
||||
if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
|
||||
__ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
|
||||
} else { /* write the element and jump out of the loop */ \
|
||||
h->keys[i] = key; \
|
||||
if (kh_is_map) h->vals[i] = val; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
|
||||
h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
|
||||
if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
|
||||
} \
|
||||
kfree(h->flags); /* free the working space */ \
|
||||
h->flags = new_flags; \
|
||||
h->n_buckets = new_n_buckets; \
|
||||
h->n_occupied = h->size; \
|
||||
h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
|
||||
} \
|
||||
return 0; \
|
||||
} \
|
||||
SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
|
||||
{ \
|
||||
khint_t x; \
|
||||
if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
|
||||
if (h->n_buckets > (h->size<<1)) { \
|
||||
if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \
|
||||
*ret = -1; return h->n_buckets; \
|
||||
} \
|
||||
} else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \
|
||||
*ret = -1; return h->n_buckets; \
|
||||
} \
|
||||
} /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
|
||||
{ \
|
||||
khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \
|
||||
x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
|
||||
if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \
|
||||
else { \
|
||||
last = i; \
|
||||
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
|
||||
if (__ac_isdel(h->flags, i)) site = i; \
|
||||
i = (i + (++step)) & mask; \
|
||||
if (i == last) { x = site; break; } \
|
||||
} \
|
||||
if (x == h->n_buckets) { \
|
||||
if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
|
||||
else x = i; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
if (__ac_isempty(h->flags, x)) { /* not present at all */ \
|
||||
h->keys[x] = key; \
|
||||
__ac_set_isboth_false(h->flags, x); \
|
||||
++h->size; ++h->n_occupied; \
|
||||
*ret = 1; \
|
||||
} else if (__ac_isdel(h->flags, x)) { /* deleted */ \
|
||||
h->keys[x] = key; \
|
||||
__ac_set_isboth_false(h->flags, x); \
|
||||
++h->size; \
|
||||
*ret = 2; \
|
||||
} else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
|
||||
return x; \
|
||||
} \
|
||||
SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \
|
||||
{ \
|
||||
if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \
|
||||
__ac_set_isdel_true(h->flags, x); \
|
||||
--h->size; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define KHASH_DECLARE(name, khkey_t, khval_t) \
|
||||
__KHASH_TYPE(name, khkey_t, khval_t) \
|
||||
__KHASH_PROTOTYPES(name, khkey_t, khval_t)
|
||||
|
||||
#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
|
||||
__KHASH_TYPE(name, khkey_t, khval_t) \
|
||||
__KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
|
||||
|
||||
#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
|
||||
KHASH_INIT2(name, static kh_inline klib_unused, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
|
||||
|
||||
/* --- BEGIN OF HASH FUNCTIONS --- */
|
||||
|
||||
/*! @function
|
||||
@abstract Integer hash function
|
||||
@param key The integer [khint32_t]
|
||||
@return The hash value [khint_t]
|
||||
*/
|
||||
#define kh_int_hash_func(key) (khint32_t)(key)
|
||||
/*! @function
|
||||
@abstract Integer comparison function
|
||||
*/
|
||||
#define kh_int_hash_equal(a, b) ((a) == (b))
|
||||
/*! @function
|
||||
@abstract 64-bit integer hash function
|
||||
@param key The integer [khint64_t]
|
||||
@return The hash value [khint_t]
|
||||
*/
|
||||
#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)
|
||||
/*! @function
|
||||
@abstract 64-bit integer comparison function
|
||||
*/
|
||||
#define kh_int64_hash_equal(a, b) ((a) == (b))
|
||||
/*! @function
|
||||
@abstract const char* hash function
|
||||
@param s Pointer to a null terminated string
|
||||
@return The hash value
|
||||
*/
|
||||
static kh_inline khint_t __ac_X31_hash_string(const char *s)
|
||||
{
|
||||
khint_t h = (khint_t)*s;
|
||||
if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;
|
||||
return h;
|
||||
}
|
||||
/*! @function
|
||||
@abstract Another interface to const char* hash function
|
||||
@param key Pointer to a null terminated string [const char*]
|
||||
@return The hash value [khint_t]
|
||||
*/
|
||||
#define kh_str_hash_func(key) __ac_X31_hash_string(key)
|
||||
/*! @function
|
||||
@abstract Const char* comparison function
|
||||
*/
|
||||
#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)
|
||||
|
||||
static kh_inline khint_t __ac_Wang_hash(khint_t key)
|
||||
{
|
||||
key += ~(key << 15);
|
||||
key ^= (key >> 10);
|
||||
key += (key << 3);
|
||||
key ^= (key >> 6);
|
||||
key += ~(key << 11);
|
||||
key ^= (key >> 16);
|
||||
return key;
|
||||
}
|
||||
#define kh_int_hash_func2(key) __ac_Wang_hash((khint_t)key)
|
||||
|
||||
/* --- END OF HASH FUNCTIONS --- */
|
||||
|
||||
/* Other convenient macros... */
|
||||
|
||||
/*!
|
||||
@abstract Type of the hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
*/
|
||||
#define khash_t(name) kh_##name##_t
|
||||
|
||||
/*! @function
|
||||
@abstract Initiate a hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@return Pointer to the hash table [khash_t(name)*]
|
||||
*/
|
||||
#define kh_init(name) kh_init_##name()
|
||||
|
||||
/*! @function
|
||||
@abstract Destroy a hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
*/
|
||||
#define kh_destroy(name, h) kh_destroy_##name(h)
|
||||
|
||||
/*! @function
|
||||
@abstract Reset a hash table without deallocating memory.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
*/
|
||||
#define kh_clear(name, h) kh_clear_##name(h)
|
||||
|
||||
/*! @function
|
||||
@abstract Resize a hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param s New size [khint_t]
|
||||
*/
|
||||
#define kh_resize(name, h, s) kh_resize_##name(h, s)
|
||||
|
||||
/*! @function
|
||||
@abstract Insert a key to the hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param k Key [type of keys]
|
||||
@param r Extra return code: -1 if the operation failed;
|
||||
0 if the key is present in the hash table;
|
||||
1 if the bucket is empty (never used); 2 if the element in
|
||||
the bucket has been deleted [int*]
|
||||
@return Iterator to the inserted element [khint_t]
|
||||
*/
|
||||
#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
|
||||
|
||||
/*! @function
|
||||
@abstract Retrieve a key from the hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param k Key [type of keys]
|
||||
@return Iterator to the found element, or kh_end(h) if the element is absent [khint_t]
|
||||
*/
|
||||
#define kh_get(name, h, k) kh_get_##name(h, k)
|
||||
|
||||
/*! @function
|
||||
@abstract Remove a key from the hash table.
|
||||
@param name Name of the hash table [symbol]
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param k Iterator to the element to be deleted [khint_t]
|
||||
*/
|
||||
#define kh_del(name, h, k) kh_del_##name(h, k)
|
||||
|
||||
/*! @function
|
||||
@abstract Test whether a bucket contains data.
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param x Iterator to the bucket [khint_t]
|
||||
@return 1 if containing data; 0 otherwise [int]
|
||||
*/
|
||||
#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
|
||||
|
||||
/*! @function
|
||||
@abstract Get key given an iterator
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param x Iterator to the bucket [khint_t]
|
||||
@return Key [type of keys]
|
||||
*/
|
||||
#define kh_key(h, x) ((h)->keys[x])
|
||||
|
||||
/*! @function
|
||||
@abstract Get value given an iterator
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param x Iterator to the bucket [khint_t]
|
||||
@return Value [type of values]
|
||||
@discussion For hash sets, calling this results in segfault.
|
||||
*/
|
||||
#define kh_val(h, x) ((h)->vals[x])
|
||||
|
||||
/*! @function
|
||||
@abstract Alias of kh_val()
|
||||
*/
|
||||
#define kh_value(h, x) ((h)->vals[x])
|
||||
|
||||
/*! @function
|
||||
@abstract Get the start iterator
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@return The start iterator [khint_t]
|
||||
*/
|
||||
#define kh_begin(h) (khint_t)(0)
|
||||
|
||||
/*! @function
|
||||
@abstract Get the end iterator
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@return The end iterator [khint_t]
|
||||
*/
|
||||
#define kh_end(h) ((h)->n_buckets)
|
||||
|
||||
/*! @function
|
||||
@abstract Get the number of elements in the hash table
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@return Number of elements in the hash table [khint_t]
|
||||
*/
|
||||
#define kh_size(h) ((h)->size)
|
||||
|
||||
/*! @function
|
||||
@abstract Get the number of buckets in the hash table
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@return Number of buckets in the hash table [khint_t]
|
||||
*/
|
||||
#define kh_n_buckets(h) ((h)->n_buckets)
|
||||
|
||||
/*! @function
|
||||
@abstract Iterate over the entries in the hash table
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param kvar Variable to which key will be assigned
|
||||
@param vvar Variable to which value will be assigned
|
||||
@param code Block of code to execute
|
||||
*/
|
||||
#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \
|
||||
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
|
||||
if (!kh_exist(h,__i)) continue; \
|
||||
(kvar) = kh_key(h,__i); \
|
||||
(vvar) = kh_val(h,__i); \
|
||||
code; \
|
||||
} }
|
||||
|
||||
/*! @function
|
||||
@abstract Iterate over the values in the hash table
|
||||
@param h Pointer to the hash table [khash_t(name)*]
|
||||
@param vvar Variable to which value will be assigned
|
||||
@param code Block of code to execute
|
||||
*/
|
||||
#define kh_foreach_value(h, vvar, code) { khint_t __i; \
|
||||
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
|
||||
if (!kh_exist(h,__i)) continue; \
|
||||
(vvar) = kh_val(h,__i); \
|
||||
code; \
|
||||
} }
|
||||
|
||||
/* More convenient interfaces */
|
||||
|
||||
/*! @function
|
||||
@abstract Instantiate a hash set containing integer keys
|
||||
@param name Name of the hash table [symbol]
|
||||
*/
|
||||
#define KHASH_SET_INIT_INT(name) \
|
||||
KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
|
||||
|
||||
/*! @function
|
||||
@abstract Instantiate a hash map containing integer keys
|
||||
@param name Name of the hash table [symbol]
|
||||
@param khval_t Type of values [type]
|
||||
*/
|
||||
#define KHASH_MAP_INIT_INT(name, khval_t) \
|
||||
KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
|
||||
|
||||
/*! @function
|
||||
@abstract Instantiate a hash set containing 64-bit integer keys
|
||||
@param name Name of the hash table [symbol]
|
||||
*/
|
||||
#define KHASH_SET_INIT_INT64(name) \
|
||||
KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
|
||||
|
||||
/*! @function
|
||||
@abstract Instantiate a hash map containing 64-bit integer keys
|
||||
@param name Name of the hash table [symbol]
|
||||
@param khval_t Type of values [type]
|
||||
*/
|
||||
#define KHASH_MAP_INIT_INT64(name, khval_t) \
|
||||
KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
|
||||
|
||||
typedef const char *kh_cstr_t;
|
||||
/*! @function
|
||||
@abstract Instantiate a hash map containing const char* keys
|
||||
@param name Name of the hash table [symbol]
|
||||
*/
|
||||
#define KHASH_SET_INIT_STR(name) \
|
||||
KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
|
||||
|
||||
/*! @function
|
||||
@abstract Instantiate a hash map containing const char* keys
|
||||
@param name Name of the hash table [symbol]
|
||||
@param khval_t Type of values [type]
|
||||
*/
|
||||
#define KHASH_MAP_INIT_STR(name, khval_t) \
|
||||
KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
|
||||
|
||||
#endif /* __AC_KHASH_H */
|
||||
@@ -1,94 +0,0 @@
|
||||
/* The MIT License
|
||||
|
||||
Copyright (c) 2008, by Attractive Chaos <attractor@live.co.uk>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
An example:
|
||||
|
||||
#include "kvec.h"
|
||||
int main() {
|
||||
kvec_t(int) array;
|
||||
kv_init(array);
|
||||
kv_push(int, array, 10); // append
|
||||
kv_a(int, array, 20) = 5; // dynamic
|
||||
kv_A(array, 20) = 4; // static
|
||||
kv_destroy(array);
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
2008-09-22 (0.1.0):
|
||||
|
||||
* The initial version.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef AC_KVEC_H
|
||||
#define AC_KVEC_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define kv_roundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
|
||||
|
||||
#define kvec_t(type) struct { size_t n, m; type *a; }
|
||||
#define kv_decl(name, type) typedef struct { size_t n, m; type *a; } name
|
||||
#define kv_init(v) ((v).n = (v).m = 0, (v).a = 0)
|
||||
#define kv_destroy(v) free((v).a)
|
||||
#define kv_A(v, i) ((v).a[(i)])
|
||||
#define kv_pop(v) ((v).a[--(v).n])
|
||||
#define kv_size(v) ((v).n)
|
||||
#define kv_max(v) ((v).m)
|
||||
#define kv_data(v) ((v).a)
|
||||
|
||||
#define kv_resize(type, v, s) ((v).m = (s), (v).a = (type*)realloc((v).a, sizeof(type) * (v).m))
|
||||
|
||||
#define kv_copy(type, v1, v0) do { \
|
||||
if ((v1).m < (v0).n) kv_resize(type, v1, (v0).n); \
|
||||
(v1).n = (v0).n; \
|
||||
memcpy((v1).a, (v0).a, sizeof(type) * (v0).n); \
|
||||
} while (0) \
|
||||
|
||||
#define kv_push(type, v, x) do { \
|
||||
if ((v).n == (v).m) { \
|
||||
(v).m = (v).m? (v).m<<1 : 2; \
|
||||
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m); \
|
||||
} \
|
||||
(v).a[(v).n++] = (x); \
|
||||
} while (0)
|
||||
|
||||
#define kv_pushp(type, v) ((((v).n == (v).m)? \
|
||||
((v).m = ((v).m? (v).m<<1 : 2), \
|
||||
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \
|
||||
: 0), \
|
||||
((v).a + ((v).n++)))
|
||||
|
||||
#define kv_a(type, v, i) (((v).m <= (size_t)(i)? \
|
||||
((v).m = (v).n = (i) + 1, kv_roundup32((v).m), \
|
||||
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \
|
||||
: (v).n <= (size_t)(i)? (v).n = (i) + 1 \
|
||||
: 0), \
|
||||
(v).a[(i)])
|
||||
|
||||
#endif
|
||||
+14
-8
@@ -23,6 +23,11 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// no need to decode on byte-addressable, little-endian arch
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
#define LittleEndian_getDW(b, i) (*((int32_t*)((uint8_t*)(b)+(i))))
|
||||
#define LittleEndian_getW(b, i) (*((int16_t*)((uint8_t*)(b)+(i))))
|
||||
#else
|
||||
static inline int32_t LittleEndian_getDW(const uint8_t *b, int i)
|
||||
{
|
||||
int c0, c1, c2, c3;
|
||||
@@ -36,6 +41,15 @@ static inline int32_t LittleEndian_getDW(const uint8_t *b, int i)
|
||||
return d0 + (d1 << 16);
|
||||
}
|
||||
|
||||
static inline int16_t LittleEndian_getW(const uint8_t *b, int i)
|
||||
{
|
||||
int c0, c1;
|
||||
c0 = *(b + i + 0);
|
||||
c1 = *(b + i + 1);
|
||||
return c0 + (c1 << 8);
|
||||
}
|
||||
#endif /* defined(__i386__) || defined(__x86_64__) */
|
||||
|
||||
static inline int32_t LittleEndian_get3B(const uint8_t *b, int i)
|
||||
{
|
||||
int c0, c1, c2;
|
||||
@@ -45,14 +59,6 @@ static inline int32_t LittleEndian_get3B(const uint8_t *b, int i)
|
||||
return c0 + (c1 << 8) + (c2 << 16);
|
||||
}
|
||||
|
||||
static inline int16_t LittleEndian_getW(const uint8_t *b, int i)
|
||||
{
|
||||
int c0, c1;
|
||||
c0 = *(b + i + 0);
|
||||
c1 = *(b + i + 1);
|
||||
return c0 + (c1 << 8);
|
||||
}
|
||||
|
||||
static inline void LittleEndian_putDW(uint8_t *dst, int i, uint32_t dword)
|
||||
{
|
||||
dst[i] = (uint8_t)(dword);
|
||||
|
||||
@@ -43,11 +43,17 @@ struct wai *wai_get(int no);
|
||||
void mixer_init(void);
|
||||
int mixer_get_numof(void);
|
||||
const char *mixer_get_name(int n);
|
||||
int mixer_set_name(int n, const char *name);
|
||||
int mixer_get_volume(int n, int *volume);
|
||||
int mixer_set_volume(int n, int volume);
|
||||
int mixer_get_mute(int n, int *mute);
|
||||
int mixer_set_mute(int n, int mute);
|
||||
|
||||
struct sts_mixer_stream_t;
|
||||
int mixer_stream_play(struct sts_mixer_stream_t* stream, int volume);
|
||||
bool mixer_stream_set_volume(int voice, int volume);
|
||||
void mixer_stream_stop(int voice);
|
||||
|
||||
struct archive_data;
|
||||
struct channel;
|
||||
enum asset_type;
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/* Copyright (C) 2023 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM4_MOVIE_H
|
||||
#define SYSTEM4_MOVIE_H
|
||||
|
||||
struct movie_context;
|
||||
struct sact_sprite;
|
||||
|
||||
struct movie_context *movie_load(const char *filename);
|
||||
void movie_free(struct movie_context *mc);
|
||||
bool movie_play(struct movie_context *mc);
|
||||
bool movie_draw(struct movie_context *mc, struct sact_sprite *sprite);
|
||||
bool movie_is_end(struct movie_context *mc);
|
||||
int movie_get_position(struct movie_context *mc);
|
||||
bool movie_set_volume(struct movie_context *mc, int volume /* 0-100 */);
|
||||
|
||||
#endif /* SYSTEM4_MOVIE_H */
|
||||
+60
-8
@@ -25,20 +25,41 @@ struct string;
|
||||
// parts.c
|
||||
bool PE_Init(void);
|
||||
void PE_Update(int passed_time, bool message_window_show);
|
||||
void PE_UpdateComponent(int passed_time);
|
||||
void PE_UpdateParts(int passed_time, bool is_skip, bool message_window_show);
|
||||
void PE_SetDelegateIndex(int parts_no, int delegate_index);
|
||||
int PE_GetDelegateIndex(int parts_no);
|
||||
bool PE_SetPartsCG(int parts_no, struct string *cg_name, int sprite_deform, int state);
|
||||
bool PE_SetPartsCG_by_index(int parts_no, int cg_no, int sprite_deform, int state);
|
||||
bool PE_SetPartsCG_by_string_index(int parts_no, struct string *cg_no,
|
||||
int sprite_deform, int state);
|
||||
void PE_GetPartsCGName(int parts_no, struct string **cg_name, int state);
|
||||
bool PE_SetPartsCGSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
int PE_GetPartsCGNumber(int parts_no, int state);
|
||||
bool PE_SetLoopCG(int parts_no, int cg_no, int nr_frames, int frame_time, int state);
|
||||
bool PE_SetHGaugeCG(int parts_no, int cg_no, int state);
|
||||
bool PE_SetLoopCG_by_index(int parts_no, int cg_no, int nr_frames, int frame_time, int state);
|
||||
bool PE_SetLoopCG(int parts_no, struct string *cg_name, int start_no, int nr_frames,
|
||||
int frame_time, int state);
|
||||
bool PE_SetLoopCGSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
bool PE_SetHGaugeCG(int parts_no, struct string *cg_name, int state);
|
||||
bool PE_SetHGaugeCG_by_index(int parts_no, int cg_no, int state);
|
||||
bool PE_SetHGaugeRate(int parts_no, int numerator, int denominator, int state);
|
||||
bool PE_SetVGaugeCG(int parts_no, int cg_no, int state);
|
||||
bool PE_SetHGaugeSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
bool PE_SetVGaugeCG(int parts_no, struct string *cg_name, int state);
|
||||
bool PE_SetVGaugeCG_by_index(int parts_no, int cg_no, int state);
|
||||
bool PE_SetVGaugeRate(int parts_no, int numerator, int denominator, int state);
|
||||
bool PE_SetNumeralCG(int parts_no, int cg_no, int state);
|
||||
bool PE_SetNumeralLinkedCGNumberWidthWidthList(int parts_no, int cg_no, int w0, int w1, int w2, int w3, int w4, int w5, int w6, int w7, int w8, int w9, int w_minus, int w_comma, int state);
|
||||
bool PE_SetVGaugeSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
bool PE_SetNumeralCG_by_index(int parts_no, int cg_no, int state);
|
||||
bool PE_SetNumeralLinkedCGNumberWidthWidthList_by_index(int parts_no, int cg_no,
|
||||
int w0, int w1, int w2, int w3, int w4, int w5, int w6, int w7, int w8,
|
||||
int w9, int w_minus, int w_comma, int state);
|
||||
bool PE_SetNumeralLinkedCGNumberWidthWidthList(int parts_no, struct string *cg_name,
|
||||
int w0, int w1, int w2, int w3, int w4, int w5, int w6, int w7, int w8,
|
||||
int w9, int w_minus, int w_comma, int state);
|
||||
bool PE_SetNumeralNumber(int parts_no, int n, int state);
|
||||
bool PE_SetNumeralShowComma(int parts_no, bool show_comma, int state);
|
||||
bool PE_SetNumeralSpace(int parts_no, int space, int state);
|
||||
bool PE_SetNumeralLength(int parts_no, int length, int state);
|
||||
bool PE_SetNumeralSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
bool PE_SetPartsRectangleDetectionSize(int PartsNumber, int Width, int Height, int State);
|
||||
bool PE_SetPartsFlash(int PartsNumber, struct string *pIFlashFileName, int State);
|
||||
bool PE_IsPartsFlashEnd(int PartsNumber, int State);
|
||||
@@ -52,11 +73,15 @@ void PE_SetPos(int parts_no, int x, int y);
|
||||
void PE_SetZ(int parts_no, int z);
|
||||
void PE_SetShow(int parts_no, bool show);
|
||||
void PE_SetAlpha(int parts_no, int alpha);
|
||||
void PE_SetPartsDrawFilter(int PartsNumber, int DrawFilter);;
|
||||
void PE_SetPartsDrawFilter(int PartsNumber, int DrawFilter);
|
||||
void PE_SetAddColor(int parts_no, int r, int g, int b);
|
||||
void PE_SetMultiplyColor(int parts_no, int r, int g, int b);
|
||||
int PE_GetPartsX(int parts_no);
|
||||
int PE_GetPartsY(int parts_no);
|
||||
int PE_GetPartsZ(int parts_no);
|
||||
int PE_GetPartsWidth(int parts_no, int state);
|
||||
int PE_GetPartsUpperLeftPosX(int parts_no, int state);
|
||||
int PE_GetPartsUpperLeftPosY(int parts_no, int state);
|
||||
int PE_GetPartsHeight(int parts_no, int state);
|
||||
bool PE_GetPartsShow(int parts_no);
|
||||
int PE_GetPartsAlpha(int parts_no);
|
||||
@@ -76,15 +101,35 @@ void PE_SetPartsPixelDecide(int PartsNumber, bool PixelDecide);
|
||||
bool PE_SetThumbnailReductionSize(int ReductionSize);
|
||||
bool PE_SetThumbnailMode(bool Mode);
|
||||
bool PE_Save(struct page **buffer);
|
||||
bool PE_SaveWithoutHideParts(struct page **buffer);
|
||||
bool PE_Load(struct page **buffer);
|
||||
// GUIEngine
|
||||
int PE_GetFreeNumber(void);
|
||||
bool PE_IsExist(int parts_no);
|
||||
|
||||
// construction.c
|
||||
bool PE_AddCreateToPartsConstructionProcess(int parts_no, int w, int h, int state);
|
||||
bool PE_AddCreatePixelOnlyToPartsConstructionProcess(int parts_no, int w, int h, int state);
|
||||
bool PE_AddCreateCGToProcess(int parts_no, struct string *cg_name, int state);
|
||||
bool PE_AddFillAlphaColorToPartsConstructionProcess(int parts_no, int x, int y, int w, int h, int r, int g, int b, int a, int state);
|
||||
bool PE_AddFillToPartsConstructionProcess(int parts_no, int x, int y, int w, int h,
|
||||
int r, int g, int b, int state);
|
||||
bool PE_AddFillAlphaColorToPartsConstructionProcess(int parts_no, int x, int y, int w, int h,
|
||||
int r, int g, int b, int a, int state);
|
||||
bool PE_AddFillAMapToPartsConstructionProcess(int parts_no, int x, int y, int w, int h, int a, int state);
|
||||
bool PE_AddDrawCutCGToPartsConstructionProcess(int parts_no, struct string *cg_name,
|
||||
int dx, int dy, int dw, int dh, int sx, int sy, int sw, int sh, int interp_type, int state);
|
||||
bool PE_AddCopyCutCGToPartsConstructionProcess(int parts_no, struct string *cg_name,
|
||||
int dx, int dy, int dw, int dh, int sx, int sy, int sw, int sh, int interp_type, int state);
|
||||
bool PE_AddCopyTextToPartsConstructionProcess(int parts_no, int x, int y, struct string *text,
|
||||
int type, int size, int r, int g, int b, float bold_weight,
|
||||
int edge_r, int edge_g, int edge_b, float edge_weight,
|
||||
int char_space, int line_space, int state);
|
||||
bool PE_AddDrawTextToPartsConstructionProcess(int parts_no, int x, int y, struct string *text,
|
||||
int type, int size, int r, int g, int b, float bold_weight,
|
||||
int edge_r, int edge_g, int edge_b, float edge_weight,
|
||||
int char_space, int line_space, int state);
|
||||
bool PE_BuildPartsConstructionProcess(int parts_no, int state);
|
||||
bool PE_SetPartsConstructionSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
|
||||
// input.c
|
||||
void PE_UpdateInputState(int passed_time);
|
||||
@@ -99,11 +144,16 @@ bool PE_SetClickMissSoundNumber(int sound_no);
|
||||
void PE_BeginInput(void);
|
||||
void PE_EndInput(void);
|
||||
int PE_GetClickPartsNumber(void);
|
||||
bool PE_IsCursorIn(int parts_no, int mouse_x, int mouse_y, int state);
|
||||
|
||||
// motion.c
|
||||
void PE_AddMotionPos(int parts_no, int begin_x, int begin_y, int end_x, int end_y, int begin_t, int end_t);
|
||||
void PE_AddMotionPos_curve(int parts_no, int begin_x, int begin_y, int end_x, int end_y,
|
||||
int begin_t, int end_t, struct string *curve_name);
|
||||
void PE_AddMotionAlpha(int parts_no, int begin_a, int end_a, int begin_t, int end_t);
|
||||
void PE_AddMotionCG(int parts_no, int begin_cg_no, int nr_cg, int begin_t, int end_t);
|
||||
void PE_AddMotionAlpha_curve(int parts_no, int begin_a, int end_a, int begin_t, int end_t,
|
||||
struct string *curve_name);
|
||||
void PE_AddMotionCG_by_index(int parts_no, int begin_cg_no, int nr_cg, int begin_t, int end_t);
|
||||
void PE_AddMotionHGaugeRate(int parts_no, int begin_numerator, int begin_denominator,
|
||||
int end_numerator, int end_denominator, int begin_t, int end_t);
|
||||
void PE_AddMotionVGaugeRate(int parts_no, int begin_numerator, int begin_denominator,
|
||||
@@ -119,12 +169,14 @@ void PE_AddMotionSound(int sound_no, int begin_t);
|
||||
void PE_BeginMotion(void);
|
||||
void PE_EndMotion(void);
|
||||
void PE_SetMotionTime(int t);
|
||||
void PE_UpdateMotionTime(int time, bool skip);
|
||||
bool PE_IsMotion(void);
|
||||
int PE_GetMotionEndTime(void);
|
||||
|
||||
// text.c
|
||||
bool PE_SetText(int parts_no, struct string *text, int state);
|
||||
bool PE_AddPartsText(int parts_no, struct string *text, int state);
|
||||
bool PE_SetPartsTextSurfaceArea(int parts_no, int x, int y, int w, int h, int state);
|
||||
bool PE_SetFont(int parts_no, int type, int size, int r, int g, int b, float bold_weight, int edge_r, int edge_g, int edge_b, float edge_weight, int state);
|
||||
bool PE_SetPartsFontType(int parts_no, int type, int state);
|
||||
bool PE_SetPartsFontSize(int parts_no, int size, int state);
|
||||
|
||||
+4265
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,28 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM4_PLUGIN_H
|
||||
#define SYSTEM4_PLUGIN_H
|
||||
|
||||
struct sact_sprite;
|
||||
|
||||
struct draw_plugin {
|
||||
const char *name;
|
||||
void (*update)(struct sact_sprite *);
|
||||
void (*debug_print)(struct sact_sprite *, int indent);
|
||||
};
|
||||
|
||||
#endif /* SYSTEM4_PLUGIN_H */
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM4_REIGN_H
|
||||
#define SYSTEM4_REIGN_H
|
||||
|
||||
#define RE_NR_BACK_CGS 16
|
||||
#define RE_NR_INSTANCE_TARGETS 2
|
||||
|
||||
#include <cglm/types.h>
|
||||
|
||||
#include "gfx/gfx.h"
|
||||
#include "plugin.h"
|
||||
|
||||
struct hash_table;
|
||||
|
||||
enum RE_instance_type {
|
||||
RE_ITYPE_UNINITIALIZED = 0,
|
||||
RE_ITYPE_STATIC = 1,
|
||||
RE_ITYPE_SKINNED = 2,
|
||||
RE_ITYPE_BILLBOARD = 3,
|
||||
RE_ITYPE_DIRECTIONAL_LIGHT = 4,
|
||||
RE_ITYPE_SPECULAR_LIGHT = 6,
|
||||
RE_ITYPE_PARTICLE_EFFECT = 8,
|
||||
};
|
||||
|
||||
enum RE_motion_state {
|
||||
RE_MOTION_STATE_STOP = 0,
|
||||
RE_MOTION_STATE_NOLOOP = 1,
|
||||
RE_MOTION_STATE_LOOP = 2,
|
||||
};
|
||||
|
||||
enum RE_fog_type {
|
||||
RE_FOG_NONE = 0,
|
||||
RE_FOG_LINEAR = 1,
|
||||
RE_FOG_LIGHT_SCATTERING = 2,
|
||||
};
|
||||
|
||||
enum RE_draw_type {
|
||||
RE_DRAW_TYPE_NORMAL = 0,
|
||||
RE_DRAW_TYPE_ADDITIVE = 1,
|
||||
RE_DRAW_TYPE_MAX = RE_DRAW_TYPE_ADDITIVE
|
||||
};
|
||||
|
||||
struct RE_options {
|
||||
int anti_aliasing;
|
||||
int wait_vsync;
|
||||
};
|
||||
extern struct RE_options RE_options;
|
||||
|
||||
struct RE_camera {
|
||||
vec3 pos;
|
||||
float pitch, roll, yaw; // in degrees
|
||||
float quake_pitch, quake_yaw;
|
||||
};
|
||||
|
||||
struct RE_back_cg {
|
||||
struct texture texture;
|
||||
struct string *name;
|
||||
int no;
|
||||
float blend_rate;
|
||||
float x;
|
||||
float y;
|
||||
float mag;
|
||||
bool show;
|
||||
};
|
||||
|
||||
struct RE_plugin {
|
||||
struct draw_plugin plugin;
|
||||
int sprite;
|
||||
int nr_instances;
|
||||
struct RE_instance **instances;
|
||||
struct archive *aar;
|
||||
struct hash_table *model_cache;
|
||||
struct hash_table *pae_cache;
|
||||
struct RE_renderer *renderer;
|
||||
struct RE_camera camera;
|
||||
mat4 proj_transform;
|
||||
|
||||
struct RE_back_cg back_cg[RE_NR_BACK_CGS];
|
||||
|
||||
// Settings.
|
||||
int render_mode;
|
||||
int shadow_mode;
|
||||
vec3 shadow_map_light_dir;
|
||||
float shadow_bias;
|
||||
int bump_mode;
|
||||
int bloom_mode;
|
||||
int glare_mode;
|
||||
int fog_mode;
|
||||
enum RE_fog_type fog_type;
|
||||
float fog_near;
|
||||
float fog_far;
|
||||
vec3 fog_color;
|
||||
float ls_beta_r;
|
||||
float ls_beta_m;
|
||||
float ls_g;
|
||||
float ls_distance;
|
||||
vec3 ls_light_dir;
|
||||
vec3 ls_light_color;
|
||||
vec3 ls_sun_color;
|
||||
int specular_mode;
|
||||
int light_map_mode;
|
||||
int soft_fog_edge_mode;
|
||||
int ssao_mode;
|
||||
int vertex_blend_mode;
|
||||
int shader_precision_mode;
|
||||
int texture_filter_mode;
|
||||
bool use_power2_texture;
|
||||
int shadow_map_resolution_level;
|
||||
int texture_resolution_level;
|
||||
vec3 global_ambient;
|
||||
float post_effect_filter_y;
|
||||
float post_effect_filter_cb;
|
||||
float post_effect_filter_cr;
|
||||
};
|
||||
|
||||
struct RE_instance {
|
||||
struct RE_plugin *plugin;
|
||||
struct model *model;
|
||||
struct particle_effect *effect;
|
||||
struct motion *motion;
|
||||
struct motion *next_motion;
|
||||
struct height_detector *height_detector;
|
||||
|
||||
enum RE_instance_type type;
|
||||
int target[RE_NR_INSTANCE_TARGETS];
|
||||
vec3 pos;
|
||||
float pitch, roll, yaw; // in degrees
|
||||
vec3 scale;
|
||||
float alpha;
|
||||
bool draw;
|
||||
bool draw_shadow;
|
||||
bool make_shadow;
|
||||
float shadow_volume_bone_radius;
|
||||
bool draw_bump;
|
||||
float fps;
|
||||
bool motion_blend;
|
||||
float motion_blend_rate;
|
||||
vec3 ambient;
|
||||
vec3 column_pos;
|
||||
float column_height;
|
||||
float column_radius;
|
||||
float column_angle; // around the Y axis, in degrees
|
||||
|
||||
// Billboards
|
||||
enum RE_draw_type draw_type;
|
||||
|
||||
// Lights
|
||||
vec3 vec;
|
||||
vec3 diffuse;
|
||||
vec3 globe_diffuse;
|
||||
|
||||
// Private
|
||||
bool local_transform_needs_update;
|
||||
mat4 local_transform;
|
||||
mat3 normal_transform;
|
||||
mat4 *bone_transforms; // model->nr_bones elements
|
||||
vec4 bounding_sphere;
|
||||
struct RE_instance *shadow_volume_instance;
|
||||
float z_from_camera;
|
||||
};
|
||||
|
||||
struct RE_plugin *RE_plugin_new(void);
|
||||
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_build_model(struct RE_plugin *plugin, int elapsed_ms);
|
||||
bool RE_set_projection(struct RE_plugin *plugin, float width, float height, float near, float far, float deg);
|
||||
int RE_create_instance(struct RE_plugin *plugin);
|
||||
bool RE_release_instance(struct RE_plugin *plugin, int instance);
|
||||
|
||||
bool RE_instance_set_type(struct RE_instance *instance, int type);
|
||||
bool RE_instance_load(struct RE_instance *instance, const char *name);
|
||||
bool RE_instance_load_motion(struct RE_instance *instance, const char *name);
|
||||
bool RE_instance_load_next_motion(struct RE_instance *instance, const char *name);
|
||||
bool RE_instance_free_next_motion(struct RE_instance *instance);
|
||||
bool RE_instance_set_vertex_pos(struct RE_instance *instance, int index, float x, float y, float z);
|
||||
int RE_instance_get_bone_index(struct RE_instance *instance, const char *name);
|
||||
bool RE_instance_trans_local_pos_to_world_pos_by_bone(struct RE_instance *instance, int bone, vec3 offset, vec3 out);
|
||||
float RE_instance_calc_height(struct RE_instance *instance, float x, float z);
|
||||
bool RE_instance_set_debug_draw_shadow_volume(struct RE_instance *instance, bool draw);
|
||||
|
||||
int RE_motion_get_state(struct motion *motion);
|
||||
bool RE_motion_set_state(struct motion *motion, int state);
|
||||
float RE_motion_get_frame(struct motion *motion);
|
||||
bool RE_motion_set_frame(struct motion *motion, float frame);
|
||||
bool RE_motion_set_frame_range(struct motion *motion, float begin, float end);
|
||||
bool RE_motion_set_loop_frame_range(struct motion *motion, float begin, float end);
|
||||
|
||||
int RE_effect_get_num_object(struct particle_effect *effect);
|
||||
bool RE_effect_get_camera_quake_flag(struct particle_effect *effect);
|
||||
bool RE_effect_set_camera_quake_flag(struct particle_effect *effect, bool enable);
|
||||
struct particle_object *RE_get_effect_object(struct particle_effect *effect, unsigned object);
|
||||
int RE_particle_get_type(struct particle_object *po);
|
||||
int RE_particle_get_move_type(struct particle_object *po);
|
||||
int RE_particle_get_up_vec_type(struct particle_object *po);
|
||||
float RE_particle_get_move_curve(struct particle_object *po);
|
||||
void RE_particle_get_frame(struct particle_object *po, int *begin_frame, int *end_frame);
|
||||
int RE_particle_get_stop_frame(struct particle_object *po);
|
||||
const char *RE_particle_get_name(struct particle_object *po);
|
||||
int RE_particle_get_num_pos(struct particle_object *po);
|
||||
int RE_particle_get_num_pos_unit(struct particle_object *po, int pos);
|
||||
int RE_particle_get_pos_unit_type(struct particle_object *po, int pos, int unit);
|
||||
int RE_particle_get_pos_unit_index(struct particle_object *po, int pos, int unit);
|
||||
int RE_particle_get_num_texture(struct particle_object *po);
|
||||
const char *RE_particle_get_texture(struct particle_object *po, int texture);
|
||||
bool RE_particle_get_size(struct particle_object *po, float *begin_size, float *end_size);
|
||||
bool RE_particle_get_size2(struct particle_object *po, int frame, float *size);
|
||||
bool RE_particle_get_size_x(struct particle_object *po, int frame, float *size);
|
||||
bool RE_particle_get_size_y(struct particle_object *po, int frame, float *size);
|
||||
bool RE_particle_get_size_type(struct particle_object *po, int frame, int *type);
|
||||
bool RE_particle_get_size_x_type(struct particle_object *po, int frame, int *type);
|
||||
bool RE_particle_get_size_y_type(struct particle_object *po, int frame, int *type);
|
||||
int RE_particle_get_num_size2(struct particle_object *po);
|
||||
int RE_particle_get_num_size_x(struct particle_object *po);
|
||||
int RE_particle_get_num_size_y(struct particle_object *po);
|
||||
int RE_particle_get_num_size_type(struct particle_object *po);
|
||||
int RE_particle_get_num_size_x_type(struct particle_object *po);
|
||||
int RE_particle_get_num_size_y_type(struct particle_object *po);
|
||||
int RE_particle_get_blend_type(struct particle_object *po);
|
||||
const char *RE_particle_get_polygon_name(struct particle_object *po);
|
||||
int RE_particle_get_num_particles(struct particle_object *po);
|
||||
int RE_particle_get_alpha_fadein_time(struct particle_object *po);
|
||||
int RE_particle_get_alpha_fadeout_time(struct particle_object *po);
|
||||
int RE_particle_get_texture_anime_time(struct particle_object *po);
|
||||
float RE_particle_get_alpha_fadein_frame(struct particle_object *po);
|
||||
float RE_particle_get_alpha_fadeout_frame(struct particle_object *po);
|
||||
float RE_particle_get_texture_anime_frame(struct particle_object *po);
|
||||
int RE_particle_get_frame_ref_type(struct particle_object *po);
|
||||
int RE_particle_get_frame_ref_param(struct particle_object *po);
|
||||
void RE_particle_get_x_rotation_angle(struct particle_object *po, float *begin_angle, float *end_angle);
|
||||
void RE_particle_get_y_rotation_angle(struct particle_object *po, float *begin_angle, float *end_angle);
|
||||
void RE_particle_get_z_rotation_angle(struct particle_object *po, float *begin_angle, float *end_angle);
|
||||
void RE_particle_get_x_revolution_angle(struct particle_object *po, float *begin_angle, float *end_angle);
|
||||
void RE_particle_get_x_revolution_distance(struct particle_object *po, float *begin_distance, float *end_distance);
|
||||
void RE_particle_get_y_revolution_angle(struct particle_object *po, float *begin_angle, float *end_angle);
|
||||
void RE_particle_get_y_revolution_distance(struct particle_object *po, float *begin_distance, float *end_distance);
|
||||
void RE_particle_get_z_revolution_angle(struct particle_object *po, float *begin_angle, float *end_angle);
|
||||
void RE_particle_get_z_revolution_distance(struct particle_object *po, float *begin_distance, float *end_distance);
|
||||
bool RE_particle_get_curve_length(struct particle_object *po, vec3 out);
|
||||
int RE_particle_get_child_frame(struct particle_object *po);
|
||||
float RE_particle_get_child_length(struct particle_object *po);
|
||||
float RE_particle_get_child_begin_slope(struct particle_object *po);
|
||||
float RE_particle_get_child_end_slope(struct particle_object *po);
|
||||
float RE_particle_get_child_create_begin_frame(struct particle_object *po);
|
||||
float RE_particle_get_child_create_end_frame(struct particle_object *po);
|
||||
int RE_particle_get_child_move_dir_type(struct particle_object *po);
|
||||
int RE_particle_get_dir_type(struct particle_object *po);
|
||||
int RE_particle_get_num_damage(struct particle_object *po);
|
||||
int RE_particle_get_damage(struct particle_object *po, int frame);
|
||||
float RE_particle_get_offset_x(struct particle_object *po);
|
||||
float RE_particle_get_offset_y(struct particle_object *po);
|
||||
bool RE_effect_get_frame_range(struct RE_instance *instance, int *begin_frame, int *end_frame);
|
||||
|
||||
bool RE_back_cg_set(struct RE_back_cg *bcg, int no);
|
||||
bool RE_back_cg_set_name(struct RE_back_cg *bcg, struct string *name, struct archive *aar);
|
||||
|
||||
void RE_render(struct sact_sprite *sp);
|
||||
void RE_debug_print(struct sact_sprite *sp, int indent);
|
||||
|
||||
#endif /* SYSTEM4_REIGN_H */
|
||||
+7
-4
@@ -24,7 +24,7 @@ struct sact_sprite *sact_get_sprite(int sp);
|
||||
struct sact_sprite *sact_try_get_sprite(int sp);
|
||||
struct sact_sprite *sact_create_sprite(int sp_no, int width, int height, int r, int g, int b, int a);
|
||||
void sact_ModuleFini(void);
|
||||
int sact_Init(void *_, int cg_cache_size);
|
||||
int sact_init(int cg_cache_size, bool chipmunk);
|
||||
#define sact_SetWP scene_set_wp
|
||||
#define sact_SetWP_Color scene_set_wp_color
|
||||
int sact_GetScreenWidth(void);
|
||||
@@ -37,6 +37,7 @@ int sact_SP_Count(void);
|
||||
int sact_SP_Enum(struct page **array);
|
||||
int sact_SP_GetMaxZ(void);
|
||||
int sact_SP_SetCG(int sp, int cg);
|
||||
int sact_SP_SetCG2X(int sp_no, int cg_no);
|
||||
int sact_SP_SetCGFromFile(int sp, struct string *filename);
|
||||
int sact_SP_SaveCG(int sp, struct string *filename);
|
||||
int sact_SP_Create(int sp, int width, int height, int r, int g, int b, int a);
|
||||
@@ -49,7 +50,7 @@ int sact_SP_SetY(int sp_no, int y);
|
||||
int sact_SP_SetZ(int sp, int z);
|
||||
int sact_SP_GetBlendRate(int sp_no);
|
||||
int sact_SP_SetBlendRate(int sp_no, int rate);
|
||||
int sact_SP_SetShow(int sp_no, bool show);
|
||||
int sact_SP_SetShow(int sp_no, int show);
|
||||
int sact_SP_SetDrawMethod(int sp_no, int method);
|
||||
int sact_SP_GetDrawMethod(int sp_no);
|
||||
int sact_SP_IsUsing(int sp_no);
|
||||
@@ -64,7 +65,7 @@ int sact_SP_SetTextHome(int sp_no, int x, int y);
|
||||
int sact_SP_SetTextLineSpace(int sp_no, int px);
|
||||
int sact_SP_SetTextCharSpace(int sp_no, int px);
|
||||
int sact_SP_SetTextPos(int sp_no, int x, int y);
|
||||
int _sact_SP_TextDraw(int sp_no, struct string *text, struct text_metrics *tm);
|
||||
int _sact_SP_TextDraw(int sp_no, struct string *text, struct text_style *tm);
|
||||
int sact_SP_TextDraw(int sp_no, struct string *text, struct page *tm);
|
||||
int sact_SP_TextClear(int sp_no);
|
||||
int sact_SP_TextHome(int sp_no, int size);
|
||||
@@ -99,8 +100,10 @@ void sact_CG_BlendAMapBin(int dst, int dx, int dy, int src, int sx, int sy, int
|
||||
int sact_TRANS_Begin(int type);
|
||||
int sact_TRANS_Update(float rate);
|
||||
int sact_TRANS_End(void);
|
||||
bool sact_VIEW_SetMode(int mode);
|
||||
int sact_VIEW_GetMode(void);
|
||||
|
||||
extern struct text_metrics text_sprite_tm;
|
||||
extern struct text_style text_sprite_ts;
|
||||
bool StoatSpriteEngine_SP_SetTextSprite(int sp_no, struct string *text);
|
||||
void StoatSpriteEngine_SP_SetTextSpriteType(int type);
|
||||
void StoatSpriteEngine_SP_SetTextSpriteSize(int size);
|
||||
|
||||
+1
-2
@@ -26,8 +26,7 @@ void json_load_page(struct page *page, cJSON *vars, bool call_dtors);
|
||||
union vm_value json_to_vm_value(enum ain_data_type type, enum ain_data_type struct_type, int array_rank, cJSON *json);
|
||||
|
||||
int save_json(const char *filename, cJSON *json);
|
||||
int save_globals(const char *keyname, const char *filename);
|
||||
int save_group(const char *keyname, const char *filename, const char *group_name, int *n);
|
||||
int save_globals(const char *keyname, const char *filename, const char *group_name, int *n);
|
||||
int load_globals(const char *keyname, const char *filename, const char *group_name, int *n);
|
||||
int delete_save_file(const char *filename);
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#include <stdbool.h>
|
||||
#include "queue.h"
|
||||
|
||||
struct texture;
|
||||
|
||||
struct sprite {
|
||||
TAILQ_ENTRY(sprite) entry;
|
||||
// The Z-layer of the sprite within the scene
|
||||
@@ -37,6 +39,8 @@ struct sprite {
|
||||
bool in_scene;
|
||||
// The rendering function.
|
||||
void (*render)(struct sprite*);
|
||||
// Debug printing function
|
||||
void (*debug_print)(struct sprite*);
|
||||
};
|
||||
|
||||
extern bool scene_is_dirty;
|
||||
@@ -46,9 +50,12 @@ void scene_unregister_sprite(struct sprite *sp);
|
||||
void scene_render(void);
|
||||
int scene_set_wp(int cg_no);
|
||||
int scene_set_wp_color(int r, int g, int b);
|
||||
int scene_set_wp_texture(struct texture *tex);
|
||||
void scene_set_sprite_z(struct sprite *sp, int z);
|
||||
void scene_set_sprite_z2(struct sprite *sp, int z, int z2);
|
||||
|
||||
void scene_print(void);
|
||||
|
||||
static inline void scene_sprite_dirty(struct sprite *sp)
|
||||
{
|
||||
if (!sp)
|
||||
|
||||
+37
-4
@@ -26,16 +26,25 @@
|
||||
struct string;
|
||||
struct page;
|
||||
union vm_value;
|
||||
struct text_style;
|
||||
|
||||
struct sact_sprite {
|
||||
struct sprite sp;
|
||||
TAILQ_ENTRY(sact_sprite) entry;
|
||||
LIST_ENTRY(sact_sprite) entry;
|
||||
// The sprite's texture (CG or solid color). Initialized lazily.
|
||||
struct texture texture;
|
||||
// If no CG is attached to the sprite, the solid color to fill with.
|
||||
SDL_Color color;
|
||||
// Shader params
|
||||
int blend_rate;
|
||||
SDL_Color multiply_color;
|
||||
SDL_Color add_color;
|
||||
// Draw method (blend mode)
|
||||
enum draw_method draw_method;
|
||||
// The position and dimensions of the sprite.
|
||||
Rectangle rect;
|
||||
// The visible area of the sprite
|
||||
Rectangle surface_area;
|
||||
// Arbitrary text can be attached to the sprite. This is rendered on a
|
||||
// separate texture and overlayed.
|
||||
struct {
|
||||
@@ -50,8 +59,21 @@ struct sact_sprite {
|
||||
int no;
|
||||
// The CG number attached to the sprite.
|
||||
int cg_no;
|
||||
// (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
|
||||
* * ChipmunkSpriteEngine - blend rate, add color, multiply color, surface area
|
||||
* (uses parts shader)
|
||||
*
|
||||
* The initialization function determines which renderer is used.
|
||||
*/
|
||||
void sprite_init_sact(void);
|
||||
void sprite_init_chipmunk(void);
|
||||
|
||||
static inline void sprite_dirty(struct sact_sprite *sp)
|
||||
{
|
||||
scene_sprite_dirty(&sp->sp);
|
||||
@@ -60,17 +82,24 @@ static inline void sprite_dirty(struct sact_sprite *sp)
|
||||
void sprite_free(struct sact_sprite *sp);
|
||||
struct texture *sprite_get_texture(struct sact_sprite *sp);
|
||||
void sprite_set_cg(struct sact_sprite *sp, struct cg *cg);
|
||||
void sprite_set_cg_2x(struct sact_sprite *sp, struct cg *cg);
|
||||
int sprite_set_cg_from_asset(struct sact_sprite *sp, int cg_no);
|
||||
int sprite_set_cg_2x_from_asset(struct sact_sprite *sp, int cg_no);
|
||||
int sprite_set_cg_by_name(struct sact_sprite *sp, const char *name);
|
||||
int sprite_set_cg_from_file(struct sact_sprite *sp, const char *path);
|
||||
int sprite_save_cg(struct sact_sprite *sp, const char *path);
|
||||
void sprite_init(struct sact_sprite *sp, int w, int h, int r, int g, int b, int a);
|
||||
void sprite_init(struct sact_sprite *sp);
|
||||
void sprite_init_color(struct sact_sprite *sp, int w, int h, int r, int g, int b, int a);
|
||||
void sprite_set_pos(struct sact_sprite *sp, int x, int y);
|
||||
void sprite_set_x(struct sact_sprite *sp, int x);
|
||||
void sprite_set_y(struct sact_sprite *sp, int y);
|
||||
static inline void sprite_set_z(struct sact_sprite *sp, int z) { scene_set_sprite_z(&sp->sp, z); }
|
||||
static inline void sprite_set_z2(struct sact_sprite *sp, int z, int z2) { scene_set_sprite_z2(&sp->sp, z, z2); }
|
||||
int sprite_get_blend_rate(struct sact_sprite *sp);
|
||||
static inline int sprite_get_blend_rate(struct sact_sprite *sp) { return sp->blend_rate; }
|
||||
void sprite_set_blend_rate(struct sact_sprite *sp, int rate);
|
||||
void sprite_set_multiply_color(struct sact_sprite *sp, int r, int g, int b);
|
||||
void sprite_set_add_color(struct sact_sprite *sp, int r, int g, int b);
|
||||
void sprite_set_surface_area(struct sact_sprite *sp, int x, int y, int w, int h);
|
||||
static inline void sprite_set_show(struct sact_sprite *sp, bool show) { scene_set_sprite_show(&sp->sp, show); }
|
||||
int sprite_set_draw_method(struct sact_sprite *sp, enum draw_method method);
|
||||
enum draw_method sprite_get_draw_method(struct sact_sprite *sp);
|
||||
@@ -86,7 +115,7 @@ void sprite_set_text_home(struct sact_sprite *sp, int x, int y);
|
||||
void sprite_set_text_line_space(struct sact_sprite *sp, int px);
|
||||
void sprite_set_text_char_space(struct sact_sprite *sp, int px);
|
||||
void sprite_set_text_pos(struct sact_sprite *sp, int x, int y);
|
||||
void sprite_text_draw(struct sact_sprite *sp, struct string *text, struct text_metrics *tm);
|
||||
void sprite_text_draw(struct sact_sprite *sp, struct string *text, struct text_style *ts);
|
||||
void sprite_text_clear(struct sact_sprite *sp);
|
||||
void sprite_text_home(struct sact_sprite *sp, int size);
|
||||
void sprite_text_new_line(struct sact_sprite *sp, int size);
|
||||
@@ -101,5 +130,9 @@ bool sprite_is_point_in(struct sact_sprite *sp, int x, int y);
|
||||
bool sprite_is_point_in_rect(struct sact_sprite *sp, int x, int y);
|
||||
int sprite_get_amap_value(struct sact_sprite *sp, int x, int y);
|
||||
void sprite_get_pixel_value(struct sact_sprite *sp, int x, int y, int *r, int *g, int *b);
|
||||
void sprite_bind_plugin(struct sact_sprite *sp, struct draw_plugin *plugin);
|
||||
void sprite_call_plugins(void);
|
||||
|
||||
void sprite_print(struct sact_sprite *sp);
|
||||
|
||||
#endif /* SYSTEM4_SPRITE_H */
|
||||
|
||||
+9
-9
@@ -39,6 +39,11 @@ enum {
|
||||
STS_MIXER_SAMPLE_FORMAT_FLOAT // floats
|
||||
};
|
||||
|
||||
enum {
|
||||
STS_STREAM_COMPLETE = 0,
|
||||
STS_STREAM_CONTINUE = 1
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -66,7 +71,7 @@ typedef struct {
|
||||
// The callback which will be called when the stream needs more data.
|
||||
typedef int (*sts_mixer_stream_callback)(sts_mixer_sample_t* sample, void* userdata);
|
||||
|
||||
typedef struct {
|
||||
typedef struct sts_mixer_stream_t {
|
||||
void* userdata; // a userdata pointer which will passed to the callback
|
||||
sts_mixer_stream_callback callback; // this callback will be called when the stream needs more data
|
||||
sts_mixer_sample_t sample; // the current stream "sample" which holds the current piece of audio
|
||||
@@ -160,11 +165,6 @@ enum {
|
||||
STS_MIXER_VOICE_STREAMING
|
||||
};
|
||||
|
||||
enum {
|
||||
STS_STREAM_COMPLETE = 0,
|
||||
STS_STREAM_CONTINUE = 1
|
||||
};
|
||||
|
||||
static float sts_mixer__clamp(const float value, const float min, const float max) {
|
||||
if (value < min) return min;
|
||||
else if (value > max) return max;
|
||||
@@ -329,14 +329,14 @@ void sts_mixer_mix_audio(sts_mixer_t* mixer, void* output, unsigned int samples)
|
||||
voice->position = 0.0f;
|
||||
position = 0;
|
||||
}
|
||||
left += sts_mixer__clamp_sample(sts_mixer__get_sample(&voice->stream->sample, position) * voice->gain);
|
||||
right += sts_mixer__clamp_sample(sts_mixer__get_sample(&voice->stream->sample, position + 1) * voice->gain);
|
||||
// added in xsystem4: allow stopping stream via callback return value
|
||||
if (status == STS_STREAM_COMPLETE) {
|
||||
sts_mixer_stop_voice(mixer, i);
|
||||
} else {
|
||||
left += sts_mixer__clamp_sample(sts_mixer__get_sample(&voice->stream->sample, position) * voice->gain);
|
||||
right += sts_mixer__clamp_sample(sts_mixer__get_sample(&voice->stream->sample, position + 1) * voice->gain);
|
||||
voice->position += (float)voice->stream->sample.frequency * advance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+31
-3
@@ -88,10 +88,16 @@ static inline void stack_push_value(union vm_value v)
|
||||
#define stack_set(n, v) (stack_set_value((n), vm_value_cast(v)))
|
||||
#define stack_push(v) (stack_push_value(vm_value_cast(v)))
|
||||
union vm_value stack_pop(void);
|
||||
union vm_value stack_peek(int n);
|
||||
union vm_value *stack_peek_var(void);
|
||||
|
||||
union vm_value local_get(int varno);
|
||||
union vm_value global_get(int varno);
|
||||
union vm_value member_get(int varno);
|
||||
void global_set(int varno, union vm_value val, bool call_dtors);
|
||||
struct page *local_page(void);
|
||||
struct page *get_local_page(int frame_no);
|
||||
struct page *get_struct_page(int frame_no);
|
||||
|
||||
int vm_string_ref(struct string *s);
|
||||
int vm_copy_page(struct page *page);
|
||||
@@ -100,21 +106,26 @@ union vm_value vm_copy(union vm_value v, enum ain_data_type type);
|
||||
int vm_execute_ain(struct ain *program);
|
||||
void vm_call(int fno, int struct_page);
|
||||
int vm_time(void);
|
||||
void vm_sleep(int ms);
|
||||
|
||||
void hll_call(int libno, int fno);
|
||||
void link_libraries(void);
|
||||
bool library_exists(int libno);
|
||||
bool library_function_exists(int libno, int fno);
|
||||
void init_libraries(void);
|
||||
void exit_libraries(void);
|
||||
|
||||
struct string *string_format(struct string *fmt, union vm_value arg, enum ain_data_type type);
|
||||
|
||||
void vm_stack_trace(void);
|
||||
noreturn void _vm_error(const char *fmt, ...);
|
||||
noreturn void vm_exit(int code);
|
||||
_Noreturn void _vm_error(const char *fmt, ...);
|
||||
_Noreturn void vm_exit(int code);
|
||||
|
||||
#define VM_ERROR(fmt, ...) \
|
||||
_vm_error("*ERROR*(%s:%s:%d): " fmt "\n", __FILE__, __func__, __LINE__, ##__VA_ARGS__)
|
||||
|
||||
#ifdef VM_PRIVATE
|
||||
#include "little_endian.h"
|
||||
#include "system4/ain.h"
|
||||
|
||||
struct function_call {
|
||||
int32_t fno;
|
||||
@@ -129,6 +140,23 @@ extern int32_t call_stack_ptr;
|
||||
|
||||
extern size_t instr_ptr;
|
||||
|
||||
// Read argument N for the current instruction.
|
||||
static inline int32_t get_argument(int n)
|
||||
{
|
||||
return LittleEndian_getDW(ain->code, instr_ptr + 2 + n*4);
|
||||
}
|
||||
|
||||
// XXX: not strictly portable
|
||||
static inline float get_argument_float(int n)
|
||||
{
|
||||
union vm_value v;
|
||||
v.i = LittleEndian_getDW(ain->code, instr_ptr + 2 + n*4);
|
||||
return v.f;
|
||||
}
|
||||
|
||||
uint32_t get_switch_address(int no, int val);
|
||||
uint32_t get_strswitch_address(int no, struct string *str);
|
||||
|
||||
int vm_save_image(const char *key, const char *path);
|
||||
void vm_load_image(const char *key, const char *path);
|
||||
struct page *vm_load_image_comments(const char *key, const char *path, int *success);
|
||||
|
||||
+6
-1
@@ -40,7 +40,10 @@ struct vm_pointer {
|
||||
};
|
||||
#ifdef DEBUG_HEAP
|
||||
size_t alloc_addr;
|
||||
size_t ref_addr;
|
||||
size_t ref_addr[16];
|
||||
size_t ref_nr;
|
||||
size_t deref_addr[16];
|
||||
size_t deref_nr;
|
||||
size_t free_addr;
|
||||
#endif
|
||||
};
|
||||
@@ -70,6 +73,8 @@ void heap_struct_assign(int lval, int rval);
|
||||
int32_t heap_alloc_page(struct page *page);
|
||||
int32_t heap_alloc_string(struct string *s);
|
||||
|
||||
void heap_describe_slot(int slot);
|
||||
|
||||
/*
|
||||
* Guarantee `headroom` free slots are available on the heap.
|
||||
*
|
||||
|
||||
@@ -133,6 +133,8 @@ void array_reverse(struct page *page);
|
||||
// delegates
|
||||
struct page *delegate_new_from_method(int obj, int fun);
|
||||
int delegate_numof(struct page *page);
|
||||
bool delegate_contains(struct page *dst, int obj, int fun);
|
||||
void delegate_erase(struct page *page, int obj, int fun);
|
||||
struct page *delegate_append(struct page *dst, int obj, int fun);
|
||||
struct page *delegate_plusa(struct page *dst, struct page *add);
|
||||
struct page *delegate_minusa(struct page *dst, struct page *minus);
|
||||
|
||||
@@ -36,9 +36,13 @@ struct config {
|
||||
char *bgi_path;
|
||||
char *wai_path;
|
||||
char *ex_path;
|
||||
char *fnl_path;
|
||||
char *font_paths[2];
|
||||
|
||||
bool joypad;
|
||||
bool echo;
|
||||
float text_x_scale;
|
||||
bool manual_text_x_scale;
|
||||
};
|
||||
|
||||
extern struct config config;
|
||||
@@ -52,6 +56,8 @@ const char *display_utf0(const char *utf);
|
||||
const char *display_utf1(const char *utf);
|
||||
const char *display_utf2(const char *utf);
|
||||
|
||||
void indent_printf(int indent, const char *fmt, ...);
|
||||
|
||||
char *unix_path(const char *path);
|
||||
char *gamedir_path(const char *path);
|
||||
char *savedir_path(const char *path);
|
||||
@@ -63,6 +69,14 @@ void get_time(int *hour, int *min, int *sec, int *ms);
|
||||
#define XSYS4_DATA_DIR "/usr/local/share/xsystem4"
|
||||
#endif
|
||||
|
||||
#define MMAP_IF_64BIT (sizeof(void*) >= 8 ? ARCHIVE_MMAP : 0)
|
||||
|
||||
extern bool game_daibanchou_en;
|
||||
extern bool game_rance02_mg;
|
||||
extern bool game_rance6_mg;
|
||||
extern bool game_rance7_mg;
|
||||
extern bool game_rance8;
|
||||
|
||||
extern bool id_indexed_afa;
|
||||
|
||||
#endif /* XSYSTEM4_H */
|
||||
|
||||
+19
-14
@@ -3,19 +3,24 @@ project('system4', 'c',
|
||||
add_project_arguments('-D_DEFAULT_SOURCE', language : 'c')
|
||||
add_project_arguments('-DXSYS4_DATA_DIR="' + get_option('prefix') / get_option('datadir') / 'xsystem4"', language : 'c')
|
||||
|
||||
zlib = dependency('zlib')
|
||||
libm = meson.get_compiler('c').find_library('m', required: false)
|
||||
sdl2 = dependency('sdl2')
|
||||
sdl2ttf = dependency('SDL2_ttf')
|
||||
ffi = dependency('libffi')
|
||||
tj = dependency('libturbojpeg')
|
||||
webp = dependency('libwebp')
|
||||
png = dependency('libpng')
|
||||
cglm = dependency('cglm', fallback : ['cglm', 'cglm_dep'])
|
||||
sndfile = dependency('sndfile')
|
||||
static_libs = false
|
||||
if host_machine.system() == 'windows'
|
||||
static_libs = true
|
||||
endif
|
||||
|
||||
if get_option('use_gles')
|
||||
gles = dependency('glesv2')
|
||||
zlib = dependency('zlib', static : static_libs)
|
||||
libm = meson.get_compiler('c').find_library('m', required: false)
|
||||
sdl2 = dependency('sdl2', static : static_libs)
|
||||
ft2 = dependency('freetype2', static : static_libs)
|
||||
ffi = dependency('libffi', static : static_libs)
|
||||
tj = dependency('libturbojpeg', static : static_libs)
|
||||
webp = dependency('libwebp', static : static_libs)
|
||||
png = dependency('libpng', static : static_libs)
|
||||
cglm = dependency('cglm', fallback : ['cglm', 'cglm_dep'])
|
||||
sndfile = dependency('sndfile', static : static_libs)
|
||||
|
||||
gles = dependency('glesv2', version : '>=3', required: get_option('opengles'))
|
||||
if gles.found()
|
||||
gl_deps = [gles]
|
||||
add_project_arguments('-DUSE_GLES', language : 'c')
|
||||
else
|
||||
@@ -24,8 +29,8 @@ else
|
||||
gl_deps = [gl, glew]
|
||||
endif
|
||||
|
||||
chibi = dependency('chibi-scheme', required : false)
|
||||
readline = dependency('readline', required : false)
|
||||
chibi = dependency('chibi-scheme', required : false, static : static_libs)
|
||||
readline = dependency('readline', required : false, static : static_libs)
|
||||
|
||||
flex = find_program('flex')
|
||||
bison = find_program('bison')
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
option('debugger', type : 'feature', value : 'auto')
|
||||
option('use_gles', type : 'boolean', value : false, description : 'Target OpenGL ES 3.0')
|
||||
option('opengles', type : 'feature', value : 'auto', description : 'Target OpenGL ES 3.0')
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/* 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;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
float alpha = texture(tex, tex_coord).a;
|
||||
float on = step(0.001, alpha);
|
||||
frag_color = vec4(on, on, on, alpha);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform vec4 color;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
frag_color = texture(tex, tex_coord) * color;
|
||||
}
|
||||
@@ -22,5 +22,5 @@ out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
vec4 texel = texture(tex, tex_coord);
|
||||
frag_color = vec4(color.rgb, texel.a);
|
||||
frag_color = vec4(color.rgb, texel.a * color.a);
|
||||
}
|
||||
|
||||
@@ -15,13 +15,12 @@
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float threshold;
|
||||
uniform vec4 color;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
vec4 texel = texture(tex, tex_coord);
|
||||
texel.a *= threshold;
|
||||
frag_color = texel;
|
||||
frag_color = vec4(color.rgb, texel.r * color.a);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float threshold;
|
||||
uniform vec4 color;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
// alpha values at or above `rate + 16` get an alpha of 0
|
||||
// alpha values below `rate` get an alpha of 255
|
||||
// alpha values between `rate` and `rate + 16` are blended
|
||||
float delta = texture(tex, tex_coord).a - threshold;
|
||||
float alpha = 1.0 - clamp(delta * 16.0, 0.0, 1.0);
|
||||
frag_color = vec4(color.rgb, alpha);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float threshold;
|
||||
uniform vec4 color;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
// the fractional part of threshold becomes a weight value for the
|
||||
// edge pixels
|
||||
int size = int(floor(threshold));
|
||||
int cutoff = size + 1;
|
||||
float edge_weight = fract(threshold);
|
||||
vec2 tex_size = vec2(textureSize(tex, 0).xy);
|
||||
|
||||
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)
|
||||
continue;
|
||||
float a = texture(tex, tex_coord + vec2(x, y) / tex_size).r;
|
||||
if (d > float(size))
|
||||
a *= edge_weight;
|
||||
a_out += a;
|
||||
}
|
||||
}
|
||||
|
||||
frag_color = vec4(color.rgb, min(a_out, 1.0));
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex; // the new scene
|
||||
uniform sampler2D old; // the old scene
|
||||
uniform vec2 resolution; // the screen resolution
|
||||
uniform float progress; // effect progress (0..1)
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
const float PI = 3.14159265;
|
||||
const vec2 tex_offset = vec2(1.0, 1.0);
|
||||
const float sigma = 50.0;
|
||||
|
||||
vec3 get_pixel(vec2 xy) {
|
||||
return texture(old, xy).rgb;
|
||||
}
|
||||
|
||||
// incremental gaussian blur algorithm
|
||||
void main() {
|
||||
vec2 p = tex_coord * resolution;
|
||||
float blur_size = progress * 100.0;
|
||||
float blur_half = blur_size / 2.0;
|
||||
|
||||
vec3 gauss;
|
||||
gauss.x = 1.0 / (sqrt(2.0 * PI) * sigma);
|
||||
gauss.y = exp(-0.5 / (sigma * sigma));
|
||||
gauss.z = gauss.y * gauss.y;
|
||||
|
||||
vec3 avg = vec3(0.0);
|
||||
float coefficient_sum = 0.0;
|
||||
|
||||
avg += get_pixel(p / resolution) * gauss.x;
|
||||
coefficient_sum += gauss.x;
|
||||
gauss.xy *= gauss.yz;
|
||||
|
||||
vec2 tex_offset = vec2(0.25, 0.75);
|
||||
|
||||
for (float i = 1.0; i <= blur_half; i += 1.0) {
|
||||
avg += get_pixel((p - i * tex_offset) / resolution) * gauss.x;
|
||||
avg += get_pixel((p + i * tex_offset) / resolution) * gauss.x;
|
||||
coefficient_sum += 2.0 * gauss.x;
|
||||
gauss.xy *= gauss.yz;
|
||||
}
|
||||
|
||||
tex_offset = vec2(0.75, 0.25);
|
||||
|
||||
for (float i = 1.0; i <= blur_half; i += 1.0) {
|
||||
avg += get_pixel((p - i * tex_offset) / resolution) * gauss.x;
|
||||
avg += get_pixel((p + i * tex_offset) / resolution) * gauss.x;
|
||||
coefficient_sum += 2.0 * gauss.x;
|
||||
gauss.xy *= gauss.yz;
|
||||
}
|
||||
|
||||
vec3 old_px = avg / coefficient_sum;
|
||||
vec3 new_px = texture(tex, tex_coord).rgb;
|
||||
frag_color = vec4(mix(old_px, new_px, pow(progress, 3.0)), 1.0);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex; // the new scene
|
||||
uniform sampler2D old; // the old scene
|
||||
uniform vec2 resolution; // the screen resolution
|
||||
uniform float progress; // effect progress (0..1)
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
const float PI = 3.14159265;
|
||||
|
||||
vec3 get_pixel(vec2 xy, float fade) {
|
||||
return mix(texture(old, xy).rgb, texture(tex, xy).rgb, fade);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 p = tex_coord * resolution;
|
||||
// 1 - (x - 1)^2
|
||||
float fade_progress = 1.0 - pow(progress - 1.0, 2.0); // (0..0.5..1) -> (0..1) [parabolic]
|
||||
// sin(PI*x)^4
|
||||
float blur_size = pow(sin(PI * progress), 4.0); // (0..0.5..1) -> (0..1..0) [sine wave ^ 4]
|
||||
float blur_half = (blur_size * resolution.x) / 2.0;
|
||||
|
||||
vec3 avg = vec3(0.0);
|
||||
float count = 0.0;
|
||||
|
||||
avg += get_pixel(p / resolution, fade_progress);
|
||||
count++;
|
||||
|
||||
for (float i = 1.0; i <= blur_half; i += 2.0) {
|
||||
avg += get_pixel(vec2(p.x + i, p.y) / resolution, fade_progress);
|
||||
avg += get_pixel(vec2(p.x - i, p.y) / resolution, fade_progress);
|
||||
count += 2.0;
|
||||
}
|
||||
|
||||
frag_color = vec4(avg / count, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex; // the new scene
|
||||
uniform sampler2D old; // the old scene
|
||||
uniform vec2 resolution; // the screen resolution
|
||||
uniform float progress; // effect progress (0..1)
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
float rand = fract(sin(dot(vec3(tex_coord, progress), vec3(12.9898, 78.233, 81.2463))) * 43758.5453);
|
||||
|
||||
vec3 col;
|
||||
if (progress < 0.5) {
|
||||
col = mix(texture(old, tex_coord).rgb, vec3(rand), progress * 2.0);
|
||||
} else {
|
||||
col = mix(texture(tex, tex_coord).rgb, vec3(rand), (1.0 - progress) * 2.0);
|
||||
}
|
||||
frag_color = vec4(col, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex; // the new scene
|
||||
uniform sampler2D old; // the old scene
|
||||
uniform vec2 resolution; // the screen resolution
|
||||
uniform float progress; // effect progress (0..1)
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
float rand = fract(sin(dot(vec3(tex_coord, progress), vec3(12.9898, 78.233, 81.2463))) * 43758.5453);
|
||||
vec3 sepia = rand * vec3(1.0, 0.888, 0.691);
|
||||
|
||||
vec3 col;
|
||||
if (progress < 0.5) {
|
||||
col = mix(texture(old, tex_coord).rgb, sepia, progress * 2.0);
|
||||
} else {
|
||||
col = mix(texture(tex, tex_coord).rgb, sepia, (1.0 - progress) * 2.0);
|
||||
}
|
||||
frag_color = vec4(col, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex; // the new scene
|
||||
uniform sampler2D old; // the old scene
|
||||
uniform vec2 resolution; // the screen resolution
|
||||
uniform float progress; // effect progress (0..1)
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
/*
|
||||
|
||||
+---------------------+
|
||||
| . |
|
||||
| . |
|
||||
| . |
|
||||
| . |
|
||||
| . |
|
||||
| . |
|
||||
| p2 +-------+ p4
|
||||
| | . ,'|
|
||||
| | ,' |
|
||||
| | ,' . |
|
||||
+-------------+'------+
|
||||
p3 p1
|
||||
|
||||
The triangle between p2-p3-p4 contains the old scene mirrored.
|
||||
The triangle between p1-p3-p4 contains the new scene.
|
||||
The rest contains the old scene.
|
||||
|
||||
p2 moves towards the top-left along the line between p1 and the origin.
|
||||
|
||||
*/
|
||||
|
||||
void main() {
|
||||
vec2 p = tex_coord * resolution;
|
||||
|
||||
vec2 p1 = resolution;
|
||||
vec2 p2 = vec2((1.0 - progress*2.0) * p1.x, (1.0 - progress*2.0) * p1.y);
|
||||
vec2 p3 = vec2(p2.x, resolution.y);
|
||||
vec2 p4 = vec2(resolution.x, p2.y);
|
||||
|
||||
// 1.0 if p is below the line between p3 and p4, else 0.0
|
||||
float b_below_line = step(0.0, (p4.x - p3.x)*(p.y - p3.y) - (p4.y - p3.y)*(p.x - p3.x));
|
||||
|
||||
// bool b_old = p.x < p2.x || p.y < p2.y;
|
||||
float b_old = step(0.5, step(p.x, p2.x) + step(p.y, p2.y));
|
||||
// bool b_mirror = !b_old && !b_below_line;
|
||||
float b_mirror = abs(1.0 - b_old) * abs(1.0 - b_below_line);
|
||||
// bool b_new = b_below_line;
|
||||
float b_new = b_below_line;
|
||||
|
||||
vec2 p_diff = p - p2;
|
||||
vec3 c_old = texture(old, tex_coord).rgb;
|
||||
vec3 c_mirror = texture(old, vec2(p1.x - p_diff.x, p1.y - p_diff.y)/resolution).rgb;
|
||||
vec3 c_new = texture(tex, tex_coord).rgb;
|
||||
|
||||
vec3 col = c_old * b_old + c_mirror * b_mirror + c_new * b_new;
|
||||
frag_color = vec4(col, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float threshold;
|
||||
uniform float threshold2;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
float rate = threshold + (threshold2 - threshold) * tex_coord.y;
|
||||
frag_color = vec4(texture(tex, tex_coord).rgb, rate);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform vec2 center;
|
||||
uniform float start_angle;
|
||||
uniform float angle;
|
||||
uniform vec4 color;
|
||||
|
||||
const float PI = 3.14159265358979323846;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
vec2 p = tex_coord - center;
|
||||
float a = atan(p.y, p.x);
|
||||
a = mod(a - start_angle, 2.0 * PI);
|
||||
if (a > angle)
|
||||
discard;
|
||||
frag_color = color;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/* Copyright (C) 2023 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D texture_y;
|
||||
uniform sampler2D texture_cb;
|
||||
uniform sampler2D texture_cr;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
mat4 rec601 = mat4(
|
||||
1.16438, 0.00000, 1.59603, -0.87079,
|
||||
1.16438, -0.39176, -0.81297, 0.52959,
|
||||
1.16438, 2.01723, 0.00000, -1.08139,
|
||||
0.0, 0.0, 0.0, 1.0
|
||||
);
|
||||
|
||||
void main() {
|
||||
float y = texture(texture_y, tex_coord).r;
|
||||
float cb = texture(texture_cb, tex_coord).r;
|
||||
float cr = texture(texture_cr, tex_coord).r;
|
||||
|
||||
frag_color = vec4(y, cb, cr, 1.0) * rec601;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float blend_rate;
|
||||
uniform vec2 bot_left;
|
||||
uniform vec2 top_right;
|
||||
uniform vec3 add_color;
|
||||
uniform vec3 multiply_color;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
float point_in_rect(vec2 p, vec2 bot_left, vec2 top_right) {
|
||||
vec2 s = step(bot_left, p) - step(top_right, p);
|
||||
return s.x * s.y;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 size = vec2(textureSize(tex, 0));
|
||||
vec2 bl = bot_left / size;
|
||||
vec2 tr = top_right / size;
|
||||
|
||||
vec4 tex_color = texture(tex, tex_coord);
|
||||
vec3 mod_color = (tex_color.rgb + add_color) * multiply_color;
|
||||
frag_color = vec4(mod_color, tex_color.a * blend_rate) * point_in_rect(tex_coord, bl, tr);
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define 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
|
||||
|
||||
struct dir_light {
|
||||
vec3 dir;
|
||||
vec3 diffuse;
|
||||
vec3 globe_diffuse;
|
||||
};
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform sampler2D specular_texture;
|
||||
uniform sampler2D alpha_texture;
|
||||
uniform sampler2D light_texture;
|
||||
uniform sampler2D shadow_texture;
|
||||
uniform float alpha_mod;
|
||||
|
||||
uniform bool use_normal_map;
|
||||
uniform sampler2D normal_texture;
|
||||
|
||||
uniform vec3 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 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 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;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
vec3 norm;
|
||||
if (use_normal_map) {
|
||||
// NOTE: TT3's shader doesn't normalize this vector.
|
||||
norm = texture(normal_texture, tex_coord).rgb * 2.0 - 1.0;
|
||||
} else {
|
||||
norm = normalize(normal);
|
||||
}
|
||||
|
||||
vec3 view_dir = normalize(eye - frag_pos);
|
||||
vec3 frag_rgb = ambient;
|
||||
|
||||
// Diffuse lighting
|
||||
vec4 texel;
|
||||
if (diffuse_type == DIFFUSE_ENV_MAP) {
|
||||
texel = texture(tex, (vec2(normal.x, -normal.y) + 1.0) / 2.0);
|
||||
frag_rgb = texel.rgb;
|
||||
} else {
|
||||
texel = texture(tex, tex_coord);
|
||||
if (diffuse_type == DIFFUSE_EMISSIVE) {
|
||||
frag_rgb = texel.rgb;
|
||||
} else {
|
||||
vec3 diffuse = 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 (diffuse_type == DIFFUSE_LIGHT_MAP) {
|
||||
diffuse *= texture(light_texture, light_tex_coord).rgb;
|
||||
}
|
||||
frag_rgb += texel.rgb * diffuse;
|
||||
}
|
||||
}
|
||||
|
||||
// Specular lighting
|
||||
if (specular_strength > 0.0) {
|
||||
vec3 reflect_dir = reflect(specular_dir, norm);
|
||||
float specular = pow(max(dot(view_dir, reflect_dir), 0.0), specular_shininess) * specular_strength;
|
||||
if (use_specular_map) {
|
||||
specular *= texture(specular_texture, tex_coord).r;
|
||||
}
|
||||
frag_rgb += vec3(specular);
|
||||
}
|
||||
|
||||
// Rim lighting
|
||||
if (rim_exponent > 0.0) {
|
||||
// Normal map is not used here.
|
||||
vec3 n = use_normal_map ? vec3(0.0, 0.0, 1.0) : normalize(normal);
|
||||
frag_rgb += pow(1.0 - max(dot(n, view_dir), 0.0), rim_exponent) * rim_color;
|
||||
}
|
||||
|
||||
// Fog
|
||||
if (fog_type == FOG_LINEAR) {
|
||||
float fog_factor = clamp((dist - fog_near) / (fog_far - fog_near), 0.0, 1.0);
|
||||
frag_rgb = mix(frag_rgb, fog_color, fog_factor);
|
||||
} else if (fog_type == FOG_LIGHT_SCATTERING) {
|
||||
frag_rgb = frag_rgb * ls_ex + ls_in;
|
||||
}
|
||||
|
||||
// Shadow mapping
|
||||
if (shadow_darkness > 0.0) {
|
||||
vec3 shadow_coords = shadow_frag_pos.xyz / shadow_frag_pos.w * 0.5 + 0.5;
|
||||
shadow_coords.z = min(1.0, shadow_coords.z - shadow_bias);
|
||||
// PCF with 9 samples.
|
||||
float brightness = 1.0;
|
||||
vec2 texel_size = 1.0 / vec2(textureSize(shadow_texture, 0));
|
||||
for (int x = -1; x <= 1; ++x) {
|
||||
for (int y = -1; y <= 1; ++y) {
|
||||
float depth = texture(shadow_texture, shadow_coords.xy + vec2(x, y) * texel_size).r;
|
||||
if (depth < shadow_coords.z)
|
||||
brightness -= 0.05 * shadow_darkness;
|
||||
}
|
||||
}
|
||||
frag_rgb *= brightness;
|
||||
}
|
||||
|
||||
// Alpha mapping
|
||||
float alpha = texel.a * alpha_mod;
|
||||
if (alpha_mode == ALPHA_TEST) {
|
||||
if (alpha < 0.8)
|
||||
discard;
|
||||
} else if (alpha_mode == ALPHA_MAP_BLEND) {
|
||||
alpha *= texture(alpha_texture, tex_coord).r;
|
||||
}
|
||||
|
||||
frag_color = vec4(frag_rgb, alpha);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define NR_DIR_LIGHTS 3
|
||||
#define FOG_LIGHT_SCATTERING 2
|
||||
|
||||
const float PI = 3.14159265358979323846;
|
||||
|
||||
struct dir_light {
|
||||
vec3 dir;
|
||||
vec3 diffuse;
|
||||
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;
|
||||
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;
|
||||
|
||||
in vec3 vertex_pos;
|
||||
in vec3 vertex_normal;
|
||||
in vec2 vertex_uv;
|
||||
in vec2 vertex_light_uv;
|
||||
in vec4 vertex_tangent;
|
||||
in ivec4 vertex_bone_index;
|
||||
in vec4 vertex_bone_weight;
|
||||
|
||||
out vec2 tex_coord;
|
||||
out vec2 light_tex_coord;
|
||||
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;
|
||||
|
||||
void main() {
|
||||
mat4 local_bone_transform = local_transform;
|
||||
mat3 normal_bone_transform = normal_transform;
|
||||
if (has_bones) {
|
||||
mat4 bone_transform = mat4(0.f);
|
||||
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;
|
||||
normal_bone_transform *= mat3(bone_transform);
|
||||
}
|
||||
|
||||
// World-space normal vector.
|
||||
normal = normalize(normal_bone_transform * vertex_normal);
|
||||
|
||||
mat3 TBN = mat3(1.0f);
|
||||
if (use_normal_map) {
|
||||
vec3 tangent = normalize(normal_bone_transform * vertex_tangent.xyz);
|
||||
vec3 bitangent = cross(normal, tangent) * vertex_tangent.w;
|
||||
TBN = transpose(mat3(tangent, bitangent, normal));
|
||||
}
|
||||
|
||||
vec4 world_pos = local_bone_transform * vec4(vertex_pos, 1.0);
|
||||
vec4 view_pos = view_transform * world_pos;
|
||||
gl_Position = proj_transform * view_pos;
|
||||
|
||||
tex_coord = vertex_uv;
|
||||
light_tex_coord = vertex_light_uv;
|
||||
dist = -view_pos.z;
|
||||
shadow_frag_pos = shadow_transform * world_pos;
|
||||
|
||||
// These are in tangent-space if use_normal_map is true, in world-space
|
||||
// otherwise.
|
||||
frag_pos = TBN * vec3(world_pos);
|
||||
eye = TBN * camera_pos;
|
||||
light_dir[0] = TBN * dir_lights[0].dir;
|
||||
light_dir[1] = TBN * dir_lights[1].dir;
|
||||
light_dir[2] = TBN * dir_lights[2].dir;
|
||||
specular_dir = TBN * specular_light_dir;
|
||||
|
||||
if (fog_type == FOG_LIGHT_SCATTERING) {
|
||||
float beta_r = ls_params.x;
|
||||
float beta_m = ls_params.y;
|
||||
float g = ls_params.z;
|
||||
float distance = dist / ls_params.w;
|
||||
|
||||
vec3 view_dir = normalize(camera_pos - vec3(world_pos));
|
||||
float cos_theta = dot(view_dir, normalize(ls_light_dir));
|
||||
// Note: `* PI` in the two assignments below should be `/ PI` (see the
|
||||
// definitions of Rayleigh / Mie phase functions in [1]), but this is
|
||||
// how TT3's shader works.
|
||||
// [1] http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2012/10/ATI-LightScattering.pdf
|
||||
float phase_r = 3.0 / 16.0 * PI * (1.0 + cos_theta * cos_theta);
|
||||
float phase_m = 1.0 / 4.0 * PI * (1.0 - g) * (1.0 - g) / pow(1.0 + g * g - 2.0 * g * cos_theta, 1.5);
|
||||
float f_ex = exp((beta_r + beta_m) * -distance);
|
||||
ls_in = (phase_r * beta_r + phase_m * beta_m) / (beta_r + beta_m) * (1.0 - f_ex) * ls_sun_color;
|
||||
ls_ex = ls_light_color * f_ex;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
out uvec4 frag_color;
|
||||
|
||||
void main() {
|
||||
frag_color.r = uint(gl_FragCoord.z * 65535.0);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform mat4 world_transform;
|
||||
uniform mat4 view_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];
|
||||
|
||||
in vec3 vertex_pos;
|
||||
in ivec4 vertex_bone_index;
|
||||
in vec4 vertex_bone_weight;
|
||||
|
||||
void main() {
|
||||
mat4 world_bone_transform = world_transform;
|
||||
if (has_bones) {
|
||||
mat4 bone_transform = mat4(0.f);
|
||||
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;
|
||||
}
|
||||
|
||||
gl_Position = view_transform * world_bone_transform * vec4(vertex_pos, 1.0);
|
||||
}
|
||||
@@ -15,12 +15,10 @@
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float alpha_mod;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
frag_color = texture(tex, tex_coord);
|
||||
frag_color.a *= alpha_mod;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/* Copyright (C) 2023 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform float blend_rate;
|
||||
|
||||
in vec2 tex_coord;
|
||||
out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
frag_color = texture(tex, tex_coord);
|
||||
frag_color.a *= blend_rate;
|
||||
}
|
||||
@@ -0,0 +1,509 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SYSTEM4_3D_3D_INTERNAL_H
|
||||
#define SYSTEM4_3D_3D_INTERNAL_H
|
||||
|
||||
#include <cglm/types.h>
|
||||
|
||||
#include "system4/archive.h"
|
||||
|
||||
#include "gfx/gl.h"
|
||||
#include "gfx/gfx.h"
|
||||
#include "reign.h"
|
||||
|
||||
#define NR_DIR_LIGHTS 3
|
||||
#define MAX_BONES 211 // Maximum in TT3
|
||||
|
||||
typedef struct vec3_range {
|
||||
vec3 begin;
|
||||
vec3 end;
|
||||
} vec3_range;
|
||||
|
||||
struct model {
|
||||
char *path;
|
||||
int nr_meshes;
|
||||
struct mesh *meshes;
|
||||
int nr_materials;
|
||||
struct material *materials;
|
||||
int nr_bones;
|
||||
struct bone *bones;
|
||||
struct hash_table *bone_map; // bone id in POL/MOT -> struct bone *
|
||||
struct hash_table *bone_name_map; // bone name -> (struct bone * | NULL)
|
||||
struct hash_table *mot_cache; // name -> struct mot *
|
||||
vec3 aabb[2]; // axis-aligned bounding box
|
||||
bool has_transparent_material;
|
||||
};
|
||||
|
||||
struct mesh {
|
||||
uint32_t flags;
|
||||
GLuint vao;
|
||||
GLuint attr_buffer;
|
||||
GLuint index_buffer;
|
||||
int nr_vertices;
|
||||
int nr_indices;
|
||||
int material;
|
||||
};
|
||||
|
||||
struct material {
|
||||
uint32_t flags;
|
||||
bool is_transparent;
|
||||
GLuint color_map;
|
||||
GLuint specular_map;
|
||||
GLuint alpha_map;
|
||||
GLuint light_map;
|
||||
GLuint normal_map;
|
||||
float specular_strength;
|
||||
float specular_shininess;
|
||||
float shadow_darkness;
|
||||
float rim_exponent;
|
||||
vec3 rim_color;
|
||||
};
|
||||
|
||||
struct bone {
|
||||
char *name;
|
||||
int index; // index on model->bones
|
||||
int parent; // index on model->bones, or -1
|
||||
mat4 inverse_bind_matrix;
|
||||
};
|
||||
|
||||
struct motion {
|
||||
struct RE_instance *instance;
|
||||
struct mot *mot;
|
||||
int state;
|
||||
float current_frame;
|
||||
float frame_begin, frame_end;
|
||||
float loop_frame_begin, loop_frame_end;
|
||||
};
|
||||
|
||||
// Attribute variable locations
|
||||
enum RE_attribute_location {
|
||||
VATTR_POS = 0,
|
||||
VATTR_NORMAL,
|
||||
VATTR_UV,
|
||||
VATTR_BONE_INDEX,
|
||||
VATTR_BONE_WEIGHT,
|
||||
VATTR_LIGHT_UV,
|
||||
VATTR_TANGENT,
|
||||
};
|
||||
|
||||
struct shadow_renderer {
|
||||
GLuint program;
|
||||
GLuint fbo;
|
||||
GLuint texture;
|
||||
|
||||
// Uniform variable locations
|
||||
GLint world_transform;
|
||||
GLint view_transform;
|
||||
GLint has_bones;
|
||||
GLint bone_matrices;
|
||||
};
|
||||
|
||||
struct RE_renderer {
|
||||
GLuint program;
|
||||
GLuint depth_buffer;
|
||||
struct shadow_renderer shadow;
|
||||
|
||||
// Uniform variable locations
|
||||
GLint world_transform;
|
||||
GLint view_transform;
|
||||
GLint texture;
|
||||
GLint local_transform;
|
||||
GLint proj_transform;
|
||||
GLint normal_transform;
|
||||
GLint alpha_mod;
|
||||
GLint has_bones;
|
||||
GLint bone_matrices;
|
||||
GLint ambient;
|
||||
struct {
|
||||
GLint dir;
|
||||
GLint diffuse;
|
||||
GLint globe_diffuse;
|
||||
GLint ambient;
|
||||
} dir_lights[NR_DIR_LIGHTS];
|
||||
GLint specular_light_dir;
|
||||
GLint specular_strength;
|
||||
GLint specular_shininess;
|
||||
GLint use_specular_map;
|
||||
GLint specular_texture;
|
||||
GLint rim_exponent;
|
||||
GLint rim_color;
|
||||
GLint camera_pos;
|
||||
GLint diffuse_type;
|
||||
GLint light_texture;
|
||||
GLint use_normal_map;
|
||||
GLint normal_texture;
|
||||
GLint shadow_darkness;
|
||||
GLint shadow_transform;
|
||||
GLint shadow_texture;
|
||||
GLint shadow_bias;
|
||||
GLint fog_type;
|
||||
GLint fog_near;
|
||||
GLint fog_far;
|
||||
GLint fog_color;
|
||||
GLint ls_params;
|
||||
GLint ls_light_dir;
|
||||
GLint ls_light_color;
|
||||
GLint ls_sun_color;
|
||||
GLint alpha_mode;
|
||||
GLint alpha_texture;
|
||||
|
||||
GLuint billboard_vao;
|
||||
GLuint billboard_attr_buffer;
|
||||
struct hash_table *billboard_textures; // cg_no -> struct billboard_texture*
|
||||
};
|
||||
|
||||
struct billboard_texture {
|
||||
GLuint texture;
|
||||
bool has_alpha;
|
||||
};
|
||||
|
||||
// reign.c
|
||||
|
||||
void RE_instance_update_local_transform(struct RE_instance *inst);
|
||||
|
||||
// model.c
|
||||
|
||||
struct model *model_load(struct archive *aar, const char *path);
|
||||
void model_free(struct model *model);
|
||||
struct model *model_create_sphere(int r, int g, int b, int a);
|
||||
|
||||
struct motion *motion_load(const char *name, struct RE_instance *instance, struct archive *aar);
|
||||
void motion_free(struct motion *motion);
|
||||
|
||||
struct archive_data *RE_get_aar_entry(struct archive *aar, const char *dir, const char *name, const char *ext);
|
||||
|
||||
// renderer.c
|
||||
|
||||
struct RE_renderer *RE_renderer_new(struct texture *texture);
|
||||
void RE_renderer_free(struct RE_renderer *r);
|
||||
bool RE_renderer_load_billboard_texture(struct RE_renderer *r, int cg_no);
|
||||
struct height_detector *RE_renderer_create_height_detector(struct RE_renderer *r, struct model *model);
|
||||
void RE_renderer_free_height_detector(struct height_detector *hd);
|
||||
bool RE_renderer_detect_height(struct height_detector *hd, float x, float z, float *y_out);
|
||||
void RE_calc_view_matrix(struct RE_camera *camera, vec3 up, mat4 out);
|
||||
|
||||
// particle.c
|
||||
|
||||
enum particle_type {
|
||||
PARTICLE_TYPE_BILLBOARD = 0,
|
||||
PARTICLE_TYPE_POLYGON_OBJECT = 1,
|
||||
PARTICLE_TYPE_SWORD_BLUR = 2,
|
||||
PARTICLE_TYPE_CAMERA_QUAKE = 3,
|
||||
};
|
||||
|
||||
enum particle_move_type {
|
||||
PARTICLE_MOVE_FIXED = 0,
|
||||
PARTICLE_MOVE_LINEAR = 1,
|
||||
PARTICLE_MOVE_EMITTER = 2,
|
||||
};
|
||||
|
||||
enum particle_up_vector_type {
|
||||
PARTICLE_UP_VECTOR_Y_AXIS = 0,
|
||||
PARTICLE_UP_VECTOR_OBJECT_MOVE = 1,
|
||||
PARTICLE_UP_VECTOR_INSTANCE_MOVE = 2,
|
||||
PARTICLE_UP_VECTOR_TYPE_MAX = PARTICLE_UP_VECTOR_INSTANCE_MOVE
|
||||
};
|
||||
|
||||
enum particle_blend_type {
|
||||
PARTICLE_BLEND_NORMAL = 0,
|
||||
PARTICLE_BLEND_ADDITIVE = 1,
|
||||
};
|
||||
|
||||
enum particle_frame_ref_type {
|
||||
PARTICLE_FRAME_REF_EFFECT = 0,
|
||||
PARTICLE_FRAME_REF_TARGET = 1,
|
||||
};
|
||||
|
||||
enum particle_child_move_dir_type {
|
||||
PARTICLE_CHILD_MOVE_DIR_NORMAL = 0,
|
||||
PARTICLE_CHILD_MOVE_DIR_REVERSE = 1,
|
||||
PARTICLE_CHILD_MOVE_DIR_TYPE_MAX = PARTICLE_CHILD_MOVE_DIR_REVERSE
|
||||
};
|
||||
|
||||
enum particle_dir_type {
|
||||
PARTICLE_DIR_TYPE_NORMAL = 0,
|
||||
PARTICLE_DIR_TYPE_XY_PLANE = 1,
|
||||
PARTICLE_DIR_TYPE_MAX = PARTICLE_DIR_TYPE_XY_PLANE
|
||||
};
|
||||
|
||||
struct particle_position_unit {
|
||||
enum {
|
||||
PARTICLE_POS_UNIT_NONE = 0,
|
||||
PARTICLE_POS_UNIT_TARGET = 1,
|
||||
PARTICLE_POS_UNIT_BONE = 2,
|
||||
PARTICLE_POS_UNIT_RAND = 4,
|
||||
PARTICLE_POS_UNIT_RAND_POSITIVE_Y = 5,
|
||||
PARTICLE_POS_UNIT_ABSOLUTE = 6,
|
||||
} type;
|
||||
union {
|
||||
struct {
|
||||
int index;
|
||||
vec3 offset;
|
||||
} target;
|
||||
struct {
|
||||
int target_index;
|
||||
char *name;
|
||||
vec3 offset;
|
||||
} bone;
|
||||
struct {
|
||||
float f;
|
||||
} rand;
|
||||
vec3 absolute;
|
||||
};
|
||||
};
|
||||
|
||||
#define NR_PARTICLE_POSITIONS 2
|
||||
#define NR_PARTICLE_POSITION_UNITS 3
|
||||
struct particle_position {
|
||||
struct particle_position_unit units[NR_PARTICLE_POSITION_UNITS];
|
||||
};
|
||||
|
||||
struct pae {
|
||||
char *path;
|
||||
struct hash_table *textures; // name -> struct billboard_texture*
|
||||
int nr_objects;
|
||||
struct pae_object *objects;
|
||||
};
|
||||
|
||||
struct pae_object {
|
||||
char *name;
|
||||
enum particle_type type;
|
||||
enum particle_move_type move_type;
|
||||
enum particle_up_vector_type up_vector_type;
|
||||
float move_curve;
|
||||
struct particle_position *position[NR_PARTICLE_POSITIONS];
|
||||
float size[2];
|
||||
int nr_sizes2;
|
||||
float *sizes2;
|
||||
int nr_sizes_x;
|
||||
float *sizes_x;
|
||||
int nr_sizes_y;
|
||||
float *sizes_y;
|
||||
int nr_size_types;
|
||||
int *size_types;
|
||||
int nr_size_x_types;
|
||||
int *size_x_types;
|
||||
int nr_size_y_types;
|
||||
int *size_y_types;
|
||||
int nr_textures;
|
||||
char **textures;
|
||||
enum particle_blend_type blend_type;
|
||||
float texture_anime_frame;
|
||||
int texture_anime_time;
|
||||
char *polygon_name;
|
||||
int begin_frame;
|
||||
int end_frame;
|
||||
int stop_frame;
|
||||
enum particle_frame_ref_type frame_ref_type;
|
||||
int frame_ref_param;
|
||||
int nr_particles;
|
||||
float alpha_fadein_frame;
|
||||
int alpha_fadein_time;
|
||||
float alpha_fadeout_frame;
|
||||
int alpha_fadeout_time;
|
||||
vec3_range rotation;
|
||||
vec3_range revolution_angle;
|
||||
vec3_range revolution_distance;
|
||||
vec3 curve_length;
|
||||
int child_frame;
|
||||
float child_length;
|
||||
float child_begin_slope;
|
||||
float child_end_slope;
|
||||
float child_create_begin_frame;
|
||||
float child_create_end_frame;
|
||||
enum particle_child_move_dir_type child_move_dir_type;
|
||||
enum particle_dir_type dir_type;
|
||||
int nr_damages;
|
||||
int *damages;
|
||||
float offset_x;
|
||||
float offset_y;
|
||||
|
||||
struct model *model;
|
||||
};
|
||||
|
||||
struct particle_effect {
|
||||
struct pae *pae;
|
||||
struct particle_object *objects; // pae->nr_objects elements
|
||||
bool camera_quake_enabled;
|
||||
};
|
||||
|
||||
struct particle_object {
|
||||
struct pae_object *pae_obj;
|
||||
struct particle_instance *instances; // pae_obj->nr_particles elements
|
||||
vec3_range pos;
|
||||
vec3 move_vec, move_ortho;
|
||||
};
|
||||
|
||||
struct particle_instance {
|
||||
float begin_frame;
|
||||
float end_frame;
|
||||
vec3_range random_offset;
|
||||
float roll_angle;
|
||||
float pitch_angle;
|
||||
};
|
||||
|
||||
struct pae *pae_load(struct archive *aar, const char *path);
|
||||
void pae_free(struct pae *pae);
|
||||
void pae_calc_frame_range(struct pae *pae, struct motion *motion);
|
||||
float pae_object_calc_alpha(struct pae_object *po, struct particle_instance *pi, float frame);
|
||||
|
||||
struct particle_effect *particle_effect_create(struct pae *pae);
|
||||
void particle_effect_free(struct particle_effect *effect);
|
||||
void particle_effect_update(struct RE_instance *inst);
|
||||
void particle_object_calc_local_transform(struct RE_instance *inst, struct particle_object *po, struct particle_instance *pi, float frame, mat4 dest);
|
||||
|
||||
// parser.c
|
||||
|
||||
struct pol {
|
||||
int32_t version;
|
||||
uint32_t nr_materials;
|
||||
struct pol_material_group *materials;
|
||||
uint32_t nr_meshes;
|
||||
struct pol_mesh **meshes;
|
||||
uint32_t nr_bones;
|
||||
struct pol_bone *bones;
|
||||
};
|
||||
|
||||
enum pol_texture_type {
|
||||
COLOR_MAP = 1,
|
||||
SPECULAR_MAP = 4,
|
||||
ALPHA_MAP = 6,
|
||||
LIGHT_MAP = 7,
|
||||
NORMAL_MAP = 8,
|
||||
MAX_TEXTURE_TYPE
|
||||
};
|
||||
|
||||
enum material_flags {
|
||||
MATERIAL_SPRITE = 1 << 0,
|
||||
};
|
||||
|
||||
struct pol_material {
|
||||
char *name;
|
||||
uint32_t flags;
|
||||
char *textures[MAX_TEXTURE_TYPE];
|
||||
};
|
||||
|
||||
struct pol_material_group {
|
||||
struct pol_material m;
|
||||
uint32_t nr_children;
|
||||
struct pol_material *children;
|
||||
};
|
||||
|
||||
enum mesh_flags {
|
||||
MESH_NOLIGHTING = 1 << 0,
|
||||
MESH_NOMAKESHADOW = 1 << 1,
|
||||
MESH_ENVMAP = 1 << 2,
|
||||
MESH_BOTH = 1 << 3,
|
||||
MESH_SPRITE = 1 << 4,
|
||||
};
|
||||
|
||||
struct pol_mesh {
|
||||
char *name;
|
||||
uint32_t flags;
|
||||
uint32_t material;
|
||||
uint32_t nr_vertices;
|
||||
struct pol_vertex *vertices;
|
||||
uint32_t nr_uvs;
|
||||
vec2 *uvs;
|
||||
uint32_t nr_light_uvs;
|
||||
vec2 *light_uvs;
|
||||
uint32_t nr_triangles;
|
||||
struct pol_triangle *triangles;
|
||||
};
|
||||
|
||||
struct pol_vertex {
|
||||
vec3 pos;
|
||||
uint32_t nr_weights;
|
||||
struct pol_bone_weight *weights;
|
||||
};
|
||||
|
||||
struct pol_bone_weight {
|
||||
uint32_t bone;
|
||||
float weight;
|
||||
};
|
||||
|
||||
struct pol_triangle {
|
||||
uint32_t vert_index[3];
|
||||
uint32_t uv_index[3];
|
||||
uint32_t light_uv_index[3];
|
||||
vec3 normals[3];
|
||||
uint32_t material_group_index;
|
||||
};
|
||||
|
||||
struct pol_bone {
|
||||
char *name;
|
||||
uint32_t id;
|
||||
int32_t parent;
|
||||
vec3 pos;
|
||||
versor rotq;
|
||||
};
|
||||
|
||||
struct mot {
|
||||
char *name;
|
||||
uint32_t nr_frames;
|
||||
uint32_t nr_bones;
|
||||
struct mot_bone *motions[];
|
||||
};
|
||||
|
||||
struct mot_frame {
|
||||
vec3 pos;
|
||||
versor rotq;
|
||||
};
|
||||
|
||||
struct mot_bone {
|
||||
char *name;
|
||||
uint32_t id;
|
||||
uint32_t parent;
|
||||
struct mot_frame frames[];
|
||||
};
|
||||
|
||||
struct amt {
|
||||
int version;
|
||||
int nr_materials;
|
||||
struct amt_material *materials[];
|
||||
};
|
||||
|
||||
struct amt_material {
|
||||
char *name;
|
||||
float fields[];
|
||||
};
|
||||
|
||||
enum amt_field_index {
|
||||
AMT_SPECULAR_STRENGTH = 0,
|
||||
AMT_SPECULAR_SHININESS = 2,
|
||||
// amt v4+
|
||||
AMT_SHADOW_DARKNESS = 6,
|
||||
// amt v5+
|
||||
AMT_RIM_EXPONENT = 7,
|
||||
AMT_RIM_R = 8,
|
||||
AMT_RIM_G = 9,
|
||||
AMT_RIM_B = 10,
|
||||
};
|
||||
|
||||
struct pol *pol_parse(uint8_t *data, size_t size);
|
||||
void pol_free(struct pol *pol);
|
||||
void pol_compute_aabb(struct pol *model, vec3 dest[2]);
|
||||
struct pol_bone *pol_find_bone(struct pol *pol, uint32_t id);
|
||||
|
||||
struct mot *mot_parse(uint8_t *data, size_t size, const char *name);
|
||||
void mot_free(struct mot *mot);
|
||||
|
||||
struct amt *amt_parse(uint8_t *data, size_t size);
|
||||
void amt_free(struct amt *amt);
|
||||
struct amt_material *amt_find_material(struct amt *amt, const char *name);
|
||||
|
||||
#endif /* SYSTEM4_3D_3D_INTERNAL_H */
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "system4/string.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
#include "reign.h"
|
||||
#include "sprite.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
#define SPREAD_VEC3(v) (v)[0], (v)[1], (v)[2]
|
||||
|
||||
static const char *instance_type_name(enum RE_instance_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case RE_ITYPE_UNINITIALIZED: return "UNINITIALIZED";
|
||||
case RE_ITYPE_STATIC: return "STATIC";
|
||||
case RE_ITYPE_SKINNED: return "SKINNED";
|
||||
case RE_ITYPE_BILLBOARD: return "BILLBOARD";
|
||||
case RE_ITYPE_DIRECTIONAL_LIGHT: return "DIRECTIONAL_LIGHT";
|
||||
case RE_ITYPE_SPECULAR_LIGHT: return "SPECULAR_LIGHT";
|
||||
case RE_ITYPE_PARTICLE_EFFECT: return "PARTICLE_EFFECT";
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
static const char *motion_state_name(enum RE_motion_state state)
|
||||
{
|
||||
switch (state) {
|
||||
case RE_MOTION_STATE_STOP: return "STOP";
|
||||
case RE_MOTION_STATE_NOLOOP: return "NOLOOP";
|
||||
case RE_MOTION_STATE_LOOP: return "LOOP";
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
static const char *fog_type_name(enum RE_fog_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case RE_FOG_NONE: return "NONE";
|
||||
case RE_FOG_LINEAR: return "LINEAR";
|
||||
case RE_FOG_LIGHT_SCATTERING: return "LIGHT_SCATTERING";
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
static void print_motion(const char *name, struct motion *m, int indent)
|
||||
{
|
||||
if (m->instance->type == RE_ITYPE_BILLBOARD) {
|
||||
indent_printf(indent, "%s = {state=%s, frame=%f, range=(%d,%d), loop_range=(%d,%d)},\n",
|
||||
name, motion_state_name(m->state), m->current_frame,
|
||||
(int)m->frame_begin, (int)m->frame_end, (int)m->loop_frame_begin, (int)m->loop_frame_end);
|
||||
} else if (m->mot) {
|
||||
indent_printf(indent, "%s = {name=\"%s\", state=%s, frame=%f},\n",
|
||||
name, m->mot->name, motion_state_name(m->state), m->current_frame);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_instance(struct RE_instance *inst, int index, int indent)
|
||||
{
|
||||
if (!inst)
|
||||
return;
|
||||
indent_printf(indent, "instance[%d] = {\n", index);
|
||||
indent++;
|
||||
|
||||
indent_printf(indent, "type = %s,\n", instance_type_name(inst->type));
|
||||
if (inst->type == RE_ITYPE_DIRECTIONAL_LIGHT || inst->type == RE_ITYPE_SPECULAR_LIGHT) {
|
||||
indent_printf(indent, "vec = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(inst->vec));
|
||||
indent_printf(indent, "diffuse = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(inst->diffuse));
|
||||
indent_printf(indent, "globe_diffuse = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(inst->globe_diffuse));
|
||||
} else {
|
||||
indent_printf(indent, "draw = %d,\n", inst->draw);
|
||||
indent_printf(indent, "pos = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(inst->pos));
|
||||
indent_printf(indent, "scale = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(inst->scale));
|
||||
indent_printf(indent, "ambient = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(inst->ambient));
|
||||
}
|
||||
if (inst->model) {
|
||||
indent_printf(indent, "path = \"%s\",\n", inst->model->path);
|
||||
indent_printf(indent, "aabb = {min = {x=%f, y=%f, z=%f}, max = {x=%f, y=%f, z=%f}},\n",
|
||||
SPREAD_VEC3(inst->model->aabb[0]), SPREAD_VEC3(inst->model->aabb[1]));
|
||||
}
|
||||
if (inst->motion) {
|
||||
indent_printf(indent, "fps = %f,\n", inst->fps);
|
||||
print_motion("motion", inst->motion, indent);
|
||||
}
|
||||
if (inst->next_motion)
|
||||
print_motion("next_motion", inst->next_motion, indent);
|
||||
if (inst->motion_blend)
|
||||
indent_printf(indent, "motion_blend_rate = %f,\n", inst->motion_blend_rate);
|
||||
if (inst->effect)
|
||||
indent_printf(indent, "path = \"%s\",\n", inst->effect->pae->path);
|
||||
|
||||
indent--;
|
||||
indent_printf(indent, "},\n");
|
||||
}
|
||||
|
||||
static void print_back_cg(struct RE_back_cg *bcg, int index, int indent)
|
||||
{
|
||||
if (!bcg->name && !bcg->no)
|
||||
return;
|
||||
indent_printf(indent, "backCG[%d] = {\n", index);
|
||||
indent++;
|
||||
|
||||
if (bcg->no)
|
||||
indent_printf(indent, "no = %d,\n", bcg->no);
|
||||
if (bcg->name)
|
||||
indent_printf(indent, "name = \"%s\",\n", bcg->name ? bcg->name->text : "");
|
||||
indent_printf(indent, "pos = {x=%f, y=%f},\n", bcg->x, bcg->y);
|
||||
indent_printf(indent, "blend_rate = %f,\n", bcg->blend_rate);
|
||||
indent_printf(indent, "mag = %f,\n", bcg->mag);
|
||||
indent_printf(indent, "show = %d,\n", bcg->show);
|
||||
|
||||
indent--;
|
||||
indent_printf(indent, "},\n");
|
||||
}
|
||||
|
||||
void RE_debug_print(struct sact_sprite *sp, int indent)
|
||||
{
|
||||
struct RE_plugin *p = (struct RE_plugin *)sp->plugin;
|
||||
|
||||
indent_printf(indent, "name = \"%s\",\n", p->plugin.name);
|
||||
indent_printf(indent, "camera = {x=%f, y=%f, z=%f, pitch=%f, roll=%f, yaw=%f},\n",
|
||||
SPREAD_VEC3(p->camera.pos), p->camera.pitch, p->camera.roll, p->camera.yaw);
|
||||
indent_printf(indent, "shadow_map_light_dir = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(p->shadow_map_light_dir));
|
||||
indent_printf(indent, "shadow_bias = %f,\n", p->shadow_bias);
|
||||
|
||||
indent_printf(indent, "fog_type = %s,\n", fog_type_name(p->fog_type));
|
||||
switch (p->fog_type) {
|
||||
case RE_FOG_NONE:
|
||||
break;
|
||||
case RE_FOG_LINEAR:
|
||||
indent_printf(indent, "fog_near = %f, fog_far = %f,\n", p->fog_near, p->fog_far);
|
||||
indent_printf(indent, "fog_color = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(p->fog_color));
|
||||
break;
|
||||
case RE_FOG_LIGHT_SCATTERING:
|
||||
indent_printf(indent, "ls_beta_r = %f, ls_beta_m = %f,\n", p->ls_beta_r, p->ls_beta_m);
|
||||
indent_printf(indent, "ls_g = %f,\n", p->ls_g);
|
||||
indent_printf(indent, "ls_distance = %f,\n", p->ls_distance);
|
||||
indent_printf(indent, "ls_light_dir = {x=%f, y=%f, z=%f},\n", SPREAD_VEC3(p->ls_light_dir));
|
||||
indent_printf(indent, "ls_light_color = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(p->ls_light_color));
|
||||
indent_printf(indent, "ls_sun_color = {r=%f, g=%f, b=%f},\n", SPREAD_VEC3(p->ls_sun_color));
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < p->nr_instances; i++)
|
||||
print_instance(p->instances[i], i, indent);
|
||||
|
||||
for (int i = 0; i < RE_NR_BACK_CGS; i++)
|
||||
print_back_cg(&p->back_cg[i], i, indent);
|
||||
}
|
||||
+668
@@ -0,0 +1,668 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/aar.h"
|
||||
#include "system4/cg.h"
|
||||
#include "system4/hashtable.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
#include "reign.h"
|
||||
|
||||
#define FP16_MIN 6.103516e-5f
|
||||
#define NR_WEIGHTS 4
|
||||
|
||||
struct vertex_common {
|
||||
GLfloat pos[3];
|
||||
GLfloat normal[3];
|
||||
GLfloat uv[2];
|
||||
};
|
||||
|
||||
struct vertex_light_uv {
|
||||
GLfloat uv[2];
|
||||
};
|
||||
|
||||
struct vertex_tangent {
|
||||
GLfloat tangent[4];
|
||||
};
|
||||
|
||||
struct vertex_bones {
|
||||
GLint bone_id[NR_WEIGHTS];
|
||||
GLfloat bone_weight[NR_WEIGHTS];
|
||||
};
|
||||
|
||||
struct archive_data *RE_get_aar_entry(struct archive *aar, const char *dir, const char *name, const char *ext)
|
||||
{
|
||||
char *path = xmalloc(strlen(dir) + strlen(name) + strlen(ext) + 2);
|
||||
sprintf(path, "%s\\%s%s", dir, name, ext);
|
||||
struct archive_data *dfile = archive_get_by_name(aar, path);
|
||||
free(path);
|
||||
return dfile;
|
||||
}
|
||||
|
||||
static GLuint load_texture(struct archive *aar, const char *path, const char *name, bool *has_alpha_out)
|
||||
{
|
||||
struct archive_data *dfile = RE_get_aar_entry(aar, path, name, "");
|
||||
if (!dfile) {
|
||||
WARNING("cannot load texture %s\\%s", path, name);
|
||||
return 0;
|
||||
}
|
||||
struct cg *cg = cg_load_data(dfile);
|
||||
if (!cg) {
|
||||
WARNING("cg_load_data failed: %s", dfile->name);
|
||||
archive_free_data(dfile);
|
||||
return 0;
|
||||
}
|
||||
archive_free_data(dfile);
|
||||
|
||||
GLuint texture;
|
||||
glGenTextures(1, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cg->metrics.w, cg->metrics.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, cg->pixels);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
if (has_alpha_out)
|
||||
*has_alpha_out = cg->metrics.has_alpha;
|
||||
cg_free(cg);
|
||||
return texture;
|
||||
}
|
||||
|
||||
static bool init_material(struct material *material, const struct pol_material *m, struct amt *amt, struct archive *aar, const char *path)
|
||||
{
|
||||
material->flags = m->flags;
|
||||
if (!m->textures[COLOR_MAP]) {
|
||||
WARNING("No color texture");
|
||||
return false;
|
||||
}
|
||||
bool has_alpha;
|
||||
material->color_map = load_texture(aar, path, m->textures[COLOR_MAP], &has_alpha);
|
||||
if (!material->color_map)
|
||||
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[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->shadow_darkness = 1.0f;
|
||||
|
||||
struct amt_material *amt_m = amt ? amt_find_material(amt, m->name) : NULL;
|
||||
if (amt_m) {
|
||||
material->specular_strength = amt_m->fields[AMT_SPECULAR_STRENGTH];
|
||||
material->specular_shininess = amt_m->fields[AMT_SPECULAR_SHININESS];
|
||||
if (amt->version >= 4)
|
||||
material->shadow_darkness = amt_m->fields[AMT_SHADOW_DARKNESS];
|
||||
if (amt->version >= 5) {
|
||||
material->rim_exponent = amt_m->fields[AMT_RIM_EXPONENT];
|
||||
material->rim_color[0] = amt_m->fields[AMT_RIM_R];
|
||||
material->rim_color[1] = amt_m->fields[AMT_RIM_G];
|
||||
material->rim_color[2] = amt_m->fields[AMT_RIM_B];
|
||||
// Very small rim_exponent value should not be used for rim
|
||||
// lighting. (e.g. meizi.amt in TT3)
|
||||
if (material->rim_exponent < FP16_MIN)
|
||||
material->rim_exponent = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void destroy_material(struct material *material)
|
||||
{
|
||||
if (material->color_map)
|
||||
glDeleteTextures(1, &material->color_map);
|
||||
if (material->specular_map)
|
||||
glDeleteTextures(1, &material->specular_map);
|
||||
if (material->alpha_map)
|
||||
glDeleteTextures(1, &material->alpha_map);
|
||||
if (material->light_map)
|
||||
glDeleteTextures(1, &material->light_map);
|
||||
if (material->normal_map)
|
||||
glDeleteTextures(1, &material->normal_map);
|
||||
}
|
||||
|
||||
static int cmp_by_bone_weight(const void *lhs, const void *rhs)
|
||||
{
|
||||
float l = ((struct pol_bone_weight *)lhs)->weight;
|
||||
float r = ((struct pol_bone_weight *)rhs)->weight;
|
||||
return (l < r) - (l > r); // descending order.
|
||||
}
|
||||
|
||||
static void sort_and_normalize_bone_weights(struct pol_vertex *v)
|
||||
{
|
||||
qsort(v->weights, v->nr_weights, sizeof(struct pol_bone_weight), cmp_by_bone_weight);
|
||||
float total = 0.0;
|
||||
for (uint32_t i = 0; i < v->nr_weights && i < NR_WEIGHTS; i++) {
|
||||
total += v->weights[i].weight;
|
||||
}
|
||||
for (uint32_t i = 0; i < v->nr_weights && i < NR_WEIGHTS; i++) {
|
||||
v->weights[i].weight /= total;
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_tangent(struct pol_mesh *m, struct pol_triangle *t, vec4 tangent[3])
|
||||
{
|
||||
vec3 v1, v2;
|
||||
glm_vec3_sub(m->vertices[t->vert_index[1]].pos, m->vertices[t->vert_index[0]].pos, v1);
|
||||
glm_vec3_sub(m->vertices[t->vert_index[2]].pos, m->vertices[t->vert_index[0]].pos, v2);
|
||||
|
||||
vec2 w1, w2;
|
||||
glm_vec2_sub(m->uvs[t->uv_index[1]], m->uvs[t->uv_index[0]], w1);
|
||||
glm_vec2_sub(m->uvs[t->uv_index[2]], m->uvs[t->uv_index[0]], w2);
|
||||
|
||||
float r = 1.0 / (w1[0] * w2[1] - w2[0] * w1[1]);
|
||||
if (!isfinite(r)) // degenerate uv triangle
|
||||
r = 1.0; // ??
|
||||
|
||||
vec3 sdir, tdir;
|
||||
glm_vec3_scale(v1, w2[1], sdir);
|
||||
glm_vec3_muladds(v2, -w1[1], sdir);
|
||||
glm_vec3_scale(sdir, r, sdir);
|
||||
|
||||
glm_vec3_scale(v2, w1[0], tdir);
|
||||
glm_vec3_muladds(v1, -w2[0], tdir);
|
||||
glm_vec3_scale(tdir, r, tdir);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
vec3 s;
|
||||
glm_vec3_copy(sdir, s);
|
||||
|
||||
// Gram-Schmidt orthogonalize.
|
||||
glm_vec3_muladds(t->normals[i], -glm_vec3_dot(t->normals[i], s), s);
|
||||
glm_vec3_normalize(s);
|
||||
|
||||
// Calculate handedness.
|
||||
vec3 c;
|
||||
glm_vec3_cross(t->normals[i], sdir, c);
|
||||
float w = (glm_vec3_dot(c, tdir) < 0.0) ? -1.0 : 1.0;
|
||||
|
||||
glm_vec4(s, w, tangent[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void *buf_alloc(uint8_t **ptr, int size)
|
||||
{
|
||||
void *p = *ptr;
|
||||
*ptr += size;
|
||||
return p;
|
||||
}
|
||||
|
||||
static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_group_index, int material)
|
||||
{
|
||||
bool has_light_map = m->light_uvs && model->materials[material].light_map;
|
||||
bool has_normal_map = model->materials[material].normal_map != 0;
|
||||
bool has_bones = !!model->bone_map;
|
||||
|
||||
GLsizei stride = sizeof(struct vertex_common);
|
||||
if (has_light_map)
|
||||
stride += sizeof(struct vertex_light_uv);
|
||||
if (has_normal_map)
|
||||
stride += sizeof(struct vertex_tangent);
|
||||
if (has_bones)
|
||||
stride += sizeof(struct vertex_bones);
|
||||
|
||||
void *buffer = xmalloc(m->nr_triangles * 3 * stride);
|
||||
uint8_t *ptr = buffer;
|
||||
|
||||
int nr_vertices = 0;
|
||||
for (uint32_t i = 0; i < m->nr_triangles; i++) {
|
||||
struct pol_triangle *t = &m->triangles[i];
|
||||
if (t->material_group_index != material_group_index)
|
||||
continue;
|
||||
vec4 tangent[3];
|
||||
if (has_normal_map)
|
||||
calc_tangent(m, t, tangent);
|
||||
for (int j = 0; j < 3; j++) {
|
||||
struct pol_vertex *vert = &m->vertices[t->vert_index[j]];
|
||||
struct vertex_common *v_common = buf_alloc(&ptr, sizeof(struct vertex_common));
|
||||
glm_vec3_copy(vert->pos, v_common->pos);
|
||||
glm_vec3_copy(t->normals[j], v_common->normal);
|
||||
glm_vec2_copy(m->uvs[t->uv_index[j]], v_common->uv);
|
||||
if (has_light_map) {
|
||||
struct vertex_light_uv *v_light_uv = buf_alloc(&ptr, sizeof(struct vertex_light_uv));
|
||||
glm_vec2_copy(m->light_uvs[t->light_uv_index[j]], v_light_uv->uv);
|
||||
}
|
||||
if (has_normal_map) {
|
||||
struct vertex_tangent *v_tangent = buf_alloc(&ptr, sizeof(struct vertex_tangent));
|
||||
glm_vec4_ucopy(tangent[j], v_tangent->tangent);
|
||||
}
|
||||
if (has_bones) {
|
||||
struct vertex_bones *v_bones = buf_alloc(&ptr, sizeof(struct vertex_bones));
|
||||
sort_and_normalize_bone_weights(vert);
|
||||
for (uint32_t k = 0; k < NR_WEIGHTS; k++) {
|
||||
if (k < vert->nr_weights) {
|
||||
struct bone *bone = ht_get_int(model->bone_map, vert->weights[k].bone, NULL);
|
||||
if (!bone)
|
||||
WARNING("%s: invalid bone id in vertex data", model->path);
|
||||
v_bones->bone_id[k] = bone ? bone->index : -1;
|
||||
v_bones->bone_weight[k] = vert->weights[k].weight;
|
||||
} else {
|
||||
v_bones->bone_id[k] = -1;
|
||||
v_bones->bone_weight[k] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
nr_vertices++;
|
||||
}
|
||||
}
|
||||
assert(ptr == (uint8_t *)buffer + nr_vertices * stride);
|
||||
|
||||
if (nr_vertices == 0) {
|
||||
free(buffer);
|
||||
return;
|
||||
}
|
||||
model->meshes = xrealloc_array(model->meshes, model->nr_meshes, model->nr_meshes + 1, sizeof(struct mesh));
|
||||
struct mesh *mesh = &model->meshes[model->nr_meshes++];
|
||||
mesh->flags = m->flags;
|
||||
mesh->material = material;
|
||||
mesh->nr_vertices = nr_vertices;
|
||||
|
||||
glGenVertexArrays(1, &mesh->vao);
|
||||
glBindVertexArray(mesh->vao);
|
||||
glGenBuffers(1, &mesh->attr_buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, mesh->attr_buffer);
|
||||
|
||||
const uint8_t *base = (const uint8_t *)0;
|
||||
glEnableVertexAttribArray(VATTR_POS);
|
||||
glVertexAttribPointer(VATTR_POS, 3, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_common, pos));
|
||||
glEnableVertexAttribArray(VATTR_NORMAL);
|
||||
glVertexAttribPointer(VATTR_NORMAL, 3, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_common, normal));
|
||||
glEnableVertexAttribArray(VATTR_UV);
|
||||
glVertexAttribPointer(VATTR_UV, 2, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_common, uv));
|
||||
base += sizeof(struct vertex_common);
|
||||
if (has_light_map) {
|
||||
glEnableVertexAttribArray(VATTR_LIGHT_UV);
|
||||
glVertexAttribPointer(VATTR_LIGHT_UV, 2, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_light_uv, uv));
|
||||
base += sizeof(struct vertex_light_uv);
|
||||
} else {
|
||||
glDisableVertexAttribArray(VATTR_LIGHT_UV);
|
||||
glVertexAttrib2f(VATTR_LIGHT_UV, 0.0, 0.0);
|
||||
}
|
||||
if (has_normal_map) {
|
||||
glEnableVertexAttribArray(VATTR_TANGENT);
|
||||
glVertexAttribPointer(VATTR_TANGENT, 4, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_tangent, tangent));
|
||||
base += sizeof(struct vertex_tangent);
|
||||
} else {
|
||||
glDisableVertexAttribArray(VATTR_TANGENT);
|
||||
glVertexAttrib3f(VATTR_TANGENT, 1.0, 0.0, 0.0);
|
||||
}
|
||||
if (has_bones) {
|
||||
glEnableVertexAttribArray(VATTR_BONE_INDEX);
|
||||
glVertexAttribIPointer(VATTR_BONE_INDEX, NR_WEIGHTS, GL_INT, stride, base + offsetof(struct vertex_bones, bone_id));
|
||||
glEnableVertexAttribArray(VATTR_BONE_WEIGHT);
|
||||
glVertexAttribPointer(VATTR_BONE_WEIGHT, NR_WEIGHTS, GL_FLOAT, GL_FALSE, stride, base + offsetof(struct vertex_bones, bone_weight));
|
||||
base += sizeof(struct vertex_bones);
|
||||
} else {
|
||||
glDisableVertexAttribArray(VATTR_BONE_INDEX);
|
||||
glVertexAttribI4i(VATTR_BONE_INDEX, 0, 0, 0, 0);
|
||||
glDisableVertexAttribArray(VATTR_BONE_WEIGHT);
|
||||
glVertexAttrib4f(VATTR_BONE_WEIGHT, 0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
assert((intptr_t)base == stride);
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, mesh->nr_vertices * stride, buffer, GL_STATIC_DRAW);
|
||||
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static void destroy_mesh(struct mesh *mesh)
|
||||
{
|
||||
glDeleteVertexArrays(1, &mesh->vao);
|
||||
glDeleteBuffers(1, &mesh->attr_buffer);
|
||||
if (mesh->index_buffer)
|
||||
glDeleteBuffers(1, &mesh->index_buffer);
|
||||
}
|
||||
|
||||
static struct bone *add_bone(struct model *model, struct pol *pol, struct pol_bone *pol_bone)
|
||||
{
|
||||
struct bone *bone = ht_get_int(model->bone_map, pol_bone->id, NULL);
|
||||
if (bone)
|
||||
return bone; // already added.
|
||||
|
||||
struct bone *parent = NULL;
|
||||
if (pol_bone->parent >= 0) {
|
||||
// Parent bone must appear before its children in model->bones.
|
||||
struct pol_bone *pol_parent = pol_find_bone(pol, pol_bone->parent);
|
||||
if (!pol_parent)
|
||||
ERROR("Parent bone of \"%s\" is not found", pol_bone->name);
|
||||
parent = add_bone(model, pol, pol_parent);
|
||||
}
|
||||
|
||||
bone = &model->bones[model->nr_bones];
|
||||
ht_put_int(model->bone_map, pol_bone->id, bone);
|
||||
bone->name = strdup(pol_bone->name);
|
||||
bone->index = model->nr_bones;
|
||||
bone->parent = parent ? parent->index : -1;
|
||||
|
||||
glm_quat_mat4(pol_bone->rotq, bone->inverse_bind_matrix);
|
||||
glm_translate(bone->inverse_bind_matrix, pol_bone->pos);
|
||||
|
||||
// Update bone_name_map. If the bone name is not unique in the POL, set the
|
||||
// map value to NULL so that ID matching will be used.
|
||||
struct ht_slot *slot = ht_put(model->bone_name_map, pol_bone->name, bone);
|
||||
if (slot->value != bone) {
|
||||
NOTICE("%s: non-unique bone %s", model->path, pol_bone->name);
|
||||
slot->value = NULL;
|
||||
}
|
||||
|
||||
model->nr_bones++;
|
||||
return bone;
|
||||
}
|
||||
|
||||
static void destroy_bone(struct bone *bone)
|
||||
{
|
||||
free(bone->name);
|
||||
}
|
||||
|
||||
struct model *model_load(struct archive *aar, const char *path)
|
||||
{
|
||||
const char *basename = strrchr(path, '\\');
|
||||
basename = basename ? basename + 1 : path;
|
||||
|
||||
// Load .POL file
|
||||
struct archive_data *pol_file = RE_get_aar_entry(aar, path, basename, ".POL");
|
||||
if (!pol_file) {
|
||||
WARNING("%s\\%s.POL: not found", path, basename);
|
||||
return NULL;
|
||||
}
|
||||
struct pol *pol = pol_parse(pol_file->data, pol_file->size);
|
||||
if (!pol) {
|
||||
WARNING("%s: parse error", pol_file->name);
|
||||
archive_free_data(pol_file);
|
||||
return NULL;
|
||||
}
|
||||
archive_free_data(pol_file);
|
||||
|
||||
// Load .amt file, if any
|
||||
struct amt *amt = NULL;
|
||||
struct archive_data *amt_file = RE_get_aar_entry(aar, path, basename, ".amt");
|
||||
if (amt_file) {
|
||||
amt = amt_parse(amt_file->data, amt_file->size);
|
||||
if (!amt)
|
||||
WARNING("%s: parse error", amt_file->name);
|
||||
archive_free_data(amt_file);
|
||||
}
|
||||
|
||||
struct model *model = xcalloc(1, sizeof(struct model));
|
||||
model->path = strdup(path);
|
||||
|
||||
// Bones
|
||||
if (pol->nr_bones > 0) {
|
||||
if (pol->nr_bones > MAX_BONES)
|
||||
ERROR("%s: Too many bones (%u)", model->path, pol->nr_bones);
|
||||
model->bone_map = ht_create(pol->nr_bones * 3 / 2);
|
||||
model->bone_name_map = ht_create(pol->nr_bones * 3 / 2);
|
||||
model->mot_cache = ht_create(16);
|
||||
model->bones = xcalloc(pol->nr_bones, sizeof(struct bone));
|
||||
for (uint32_t i = 0; i < pol->nr_bones; i++) {
|
||||
add_bone(model, pol, &pol->bones[i]);
|
||||
}
|
||||
if (model->nr_bones != (int)pol->nr_bones)
|
||||
ERROR("%s: Broken bone data", model->path);
|
||||
}
|
||||
|
||||
// Materials
|
||||
int *material_offsets = xmalloc(pol->nr_materials * sizeof(int));
|
||||
for (uint32_t i = 0; i < pol->nr_materials; i++) {
|
||||
material_offsets[i] = model->nr_materials;
|
||||
if (pol->materials[i].nr_children)
|
||||
model->nr_materials += pol->materials[i].nr_children;
|
||||
else
|
||||
model->nr_materials++;
|
||||
}
|
||||
model->materials = xcalloc(model->nr_materials, sizeof(struct material));
|
||||
for (uint32_t i = 0; i < pol->nr_materials; i++) {
|
||||
if (pol->materials[i].nr_children == 0) {
|
||||
init_material(&model->materials[material_offsets[i]],
|
||||
&pol->materials[i].m, amt, aar, path);
|
||||
continue;
|
||||
}
|
||||
for (uint32_t j = 0; j < pol->materials[i].nr_children; j++) {
|
||||
init_material(&model->materials[material_offsets[i] + j],
|
||||
&pol->materials[i].children[j], amt, aar, path);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < model->nr_materials; i++) {
|
||||
if (model->materials[i].is_transparent) {
|
||||
model->has_transparent_material = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Meshes
|
||||
for (uint32_t i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!pol->meshes[i])
|
||||
continue;
|
||||
struct pol_material_group *mg = &pol->materials[pol->meshes[i]->material];
|
||||
int m_off = material_offsets[pol->meshes[i]->material];
|
||||
if (mg->nr_children == 0) {
|
||||
add_mesh(model, pol->meshes[i], 0, m_off);
|
||||
continue;
|
||||
}
|
||||
for (uint32_t j = 0; j < mg->nr_children; j++) {
|
||||
add_mesh(model, pol->meshes[i], j, m_off + j);
|
||||
}
|
||||
}
|
||||
|
||||
pol_compute_aabb(pol, model->aabb);
|
||||
|
||||
free(material_offsets);
|
||||
if (amt)
|
||||
amt_free(amt);
|
||||
pol_free(pol);
|
||||
return model;
|
||||
}
|
||||
|
||||
void model_free(struct model *model)
|
||||
{
|
||||
for (int i = 0; i < model->nr_meshes; i++)
|
||||
destroy_mesh(&model->meshes[i]);
|
||||
free(model->meshes);
|
||||
|
||||
for (int i = 0; i < model->nr_materials; i++)
|
||||
destroy_material(&model->materials[i]);
|
||||
free(model->materials);
|
||||
|
||||
for (int i = 0; i < model->nr_bones; i++)
|
||||
destroy_bone(&model->bones[i]);
|
||||
free(model->bones);
|
||||
if (model->bone_map)
|
||||
ht_free_int(model->bone_map);
|
||||
if (model->bone_name_map)
|
||||
ht_free(model->bone_name_map);
|
||||
if (model->mot_cache) {
|
||||
ht_foreach_value(model->mot_cache, (void(*)(void*))mot_free);
|
||||
ht_free(model->mot_cache);
|
||||
}
|
||||
|
||||
free(model->path);
|
||||
free(model);
|
||||
}
|
||||
|
||||
static void init_sphere_mesh(struct mesh *mesh)
|
||||
{
|
||||
const int w_segments = 16;
|
||||
const int h_segments = 16;
|
||||
const int nr_vertices = (w_segments + 1) * (h_segments + 1);
|
||||
struct vertex_common *vertices = xcalloc(nr_vertices, sizeof(struct vertex_common));
|
||||
struct vertex_common *v = vertices;
|
||||
for (int y = 0; y <= h_segments; y++) {
|
||||
float theta = GLM_PIf * y / h_segments;
|
||||
for (int x = 0; x <= w_segments; x++, v++) {
|
||||
float phi = 2.0f * GLM_PIf * x / w_segments;
|
||||
v->pos[0] = -cosf(phi) * sinf(theta);
|
||||
v->pos[1] = cosf(theta);
|
||||
v->pos[2] = sinf(phi) * sinf(theta);
|
||||
glm_vec3_copy(v->pos, v->normal);
|
||||
v->uv[0] = v->uv[1] = 0.0f;
|
||||
}
|
||||
}
|
||||
assert(v == vertices + nr_vertices);
|
||||
|
||||
const int nr_indices = 3 * 2 * w_segments * (h_segments - 1);
|
||||
GLushort *indices = xcalloc(nr_indices, sizeof(GLushort));
|
||||
GLushort *pi = indices;
|
||||
for (int y = 0; y < h_segments; y++) {
|
||||
for (int x = 0; x < w_segments; x++) {
|
||||
GLushort a = y * w_segments + x + 1;
|
||||
GLushort b = y * w_segments + x;
|
||||
GLushort c = (y + 1) * w_segments + x;
|
||||
GLushort d = (y + 1) * w_segments + x + 1;
|
||||
if (y > 0) {
|
||||
*pi++ = a;
|
||||
*pi++ = b;
|
||||
*pi++ = d;
|
||||
}
|
||||
if (y < h_segments - 1) {
|
||||
*pi++ = b;
|
||||
*pi++ = c;
|
||||
*pi++ = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(pi == indices + nr_indices);
|
||||
|
||||
mesh->flags = MESH_NOLIGHTING;
|
||||
mesh->nr_vertices = nr_vertices;
|
||||
mesh->nr_indices = nr_indices;
|
||||
glGenVertexArrays(1, &mesh->vao);
|
||||
glBindVertexArray(mesh->vao);
|
||||
glGenBuffers(1, &mesh->attr_buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, mesh->attr_buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, nr_vertices * sizeof(struct vertex_common), vertices, GL_STATIC_DRAW);
|
||||
glEnableVertexAttribArray(VATTR_POS);
|
||||
glVertexAttribPointer(VATTR_POS, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex_common), (void*)offsetof(struct vertex_common, pos));
|
||||
glEnableVertexAttribArray(VATTR_NORMAL);
|
||||
glVertexAttribPointer(VATTR_NORMAL, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex_common), (void*)offsetof(struct vertex_common, normal));
|
||||
glEnableVertexAttribArray(VATTR_UV);
|
||||
glVertexAttribPointer(VATTR_UV, 2, GL_FLOAT, GL_FALSE, sizeof(struct vertex_common), (void*)offsetof(struct vertex_common, uv));
|
||||
|
||||
glGenBuffers(1, &mesh->index_buffer);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->index_buffer);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, nr_indices * sizeof(GLushort), indices, GL_STATIC_DRAW);
|
||||
glBindVertexArray(0);
|
||||
|
||||
free(vertices);
|
||||
free(indices);
|
||||
}
|
||||
|
||||
struct model *model_create_sphere(int r, int g, int b, int a)
|
||||
{
|
||||
struct model *model = xcalloc(1, sizeof(struct model));
|
||||
|
||||
model->nr_meshes = 1;
|
||||
model->meshes = xcalloc(1, sizeof(struct mesh));
|
||||
init_sphere_mesh(&model->meshes[0]);
|
||||
model->aabb[0][0] = -1.0f;
|
||||
model->aabb[0][1] = -1.0f;
|
||||
model->aabb[0][2] = -1.0f;
|
||||
model->aabb[1][0] = 1.0f;
|
||||
model->aabb[1][1] = 1.0f;
|
||||
model->aabb[1][2] = 1.0f;
|
||||
|
||||
model->nr_materials = 1;
|
||||
model->materials = xcalloc(1, sizeof(struct material));
|
||||
struct material *material = &model->materials[0];
|
||||
material->is_transparent = true;
|
||||
glGenTextures(1, &material->color_map);
|
||||
glBindTexture(GL_TEXTURE_2D, material->color_map);
|
||||
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;
|
||||
|
||||
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) {
|
||||
WARNING("%s\\%s.MOT: not found", model->path, name);
|
||||
return NULL;
|
||||
}
|
||||
struct mot *mot = mot_parse(dfile->data, dfile->size, name);
|
||||
if (!mot) {
|
||||
WARNING("%s: parse error", dfile->name);
|
||||
archive_free_data(dfile);
|
||||
return NULL;
|
||||
}
|
||||
archive_free_data(dfile);
|
||||
|
||||
if (model->nr_bones != (int)mot->nr_bones)
|
||||
ERROR("%s: wrong number of bones. Expected %d but got %d", name, model->nr_bones, mot->nr_bones);
|
||||
|
||||
// Reorder mot->motions so that motion for model->bones[i] can be
|
||||
// accessed by mot->motions[i].
|
||||
for (uint32_t i = 0; i < mot->nr_bones; i++) {
|
||||
// Match by name first, since some MOT have wrong bone IDs (e.g. maidsan_ahoge_*).
|
||||
struct bone *bone = ht_get(model->bone_name_map, mot->motions[i]->name, NULL);
|
||||
// If it is not found or is NULL (non-unique bone name), match by bone ID.
|
||||
if (!bone)
|
||||
bone = ht_get_int(model->bone_map, mot->motions[i]->id, NULL);
|
||||
if (!bone)
|
||||
ERROR("%s: invalid bone \"%s\" (%d)", name, mot->motions[i]->name, mot->motions[i]->id);
|
||||
mot->motions[i]->id = bone->index;
|
||||
}
|
||||
qsort(mot->motions, mot->nr_bones, sizeof(struct mot_bone *), cmp_motions_by_bone_id);
|
||||
|
||||
return mot;
|
||||
}
|
||||
|
||||
struct motion *motion_load(const char *name, struct RE_instance *instance, struct archive *aar)
|
||||
{
|
||||
struct model *model = instance->model;
|
||||
if (!model || !model->mot_cache)
|
||||
return NULL;
|
||||
struct mot *mot = ht_get(model->mot_cache, name, NULL);
|
||||
if (!mot) {
|
||||
mot = mot_load(name, model, aar);
|
||||
if (!mot)
|
||||
return NULL;
|
||||
ht_put(model->mot_cache, name, mot);
|
||||
}
|
||||
struct motion *motion = xcalloc(1, sizeof(struct motion));
|
||||
motion->instance = instance;
|
||||
motion->mot = mot;
|
||||
return motion;
|
||||
}
|
||||
|
||||
void motion_free(struct motion *motion)
|
||||
{
|
||||
free(motion);
|
||||
}
|
||||
+459
@@ -0,0 +1,459 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/buffer.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
|
||||
#define METERS_PER_INCH 0.0254
|
||||
|
||||
static char *read_cstring(struct buffer *r)
|
||||
{
|
||||
char *s = strdup(buffer_strdata(r));
|
||||
buffer_skip(r, strlen(s) + 1);
|
||||
return s;
|
||||
}
|
||||
|
||||
static void read_position(struct buffer *r, vec3 v)
|
||||
{
|
||||
// left->right handed system
|
||||
v[0] = buffer_read_float(r) * METERS_PER_INCH;
|
||||
v[1] = buffer_read_float(r) * METERS_PER_INCH;
|
||||
v[2] = -buffer_read_float(r) * METERS_PER_INCH;
|
||||
}
|
||||
|
||||
static void read_direction(struct buffer *r, vec3 v)
|
||||
{
|
||||
// left->right handed system
|
||||
v[0] = buffer_read_float(r);
|
||||
v[1] = buffer_read_float(r);
|
||||
v[2] = -buffer_read_float(r);
|
||||
}
|
||||
|
||||
static void read_quaternion(struct buffer *r, versor q)
|
||||
{
|
||||
// left->right handed system
|
||||
float w = buffer_read_float(r);
|
||||
float x = -buffer_read_float(r);
|
||||
float y = -buffer_read_float(r);
|
||||
float z = buffer_read_float(r);
|
||||
glm_quat_init(q, x, y, z, w);
|
||||
glm_quat_normalize(q);
|
||||
}
|
||||
|
||||
static uint32_t parse_material_attributes(const char *name)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
if (strstr(name, "(sprite)"))
|
||||
flags |= MATERIAL_SPRITE;
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void parse_material(struct buffer *r, struct pol_material *m)
|
||||
{
|
||||
m->name = read_cstring(r);
|
||||
m->flags = parse_material_attributes(m->name);
|
||||
|
||||
int nr_textures = buffer_read_int32(r);
|
||||
for (int i = 0; i < nr_textures; i++) {
|
||||
char *filename = read_cstring(r);
|
||||
int type = buffer_read_int32(r);
|
||||
if ((unsigned)type < MAX_TEXTURE_TYPE) {
|
||||
m->textures[type] = filename;
|
||||
} else {
|
||||
WARNING("invalid texture type %d", type);
|
||||
free(filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy_material(struct pol_material *m)
|
||||
{
|
||||
free(m->name);
|
||||
for (int i = 0; i < MAX_TEXTURE_TYPE; i++)
|
||||
free(m->textures[i]);
|
||||
}
|
||||
|
||||
static void parse_material_group(struct buffer *r, struct pol_material_group *mg)
|
||||
{
|
||||
parse_material(r, &mg->m);
|
||||
|
||||
mg->nr_children = buffer_read_int32(r);
|
||||
if (mg->nr_children > 0) {
|
||||
mg->children = xcalloc(mg->nr_children, sizeof(struct pol_material));
|
||||
for (uint32_t i = 0; i < mg->nr_children; i++) {
|
||||
parse_material(r, &mg->children[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy_material_group(struct pol_material_group *mg)
|
||||
{
|
||||
destroy_material(&mg->m);
|
||||
if (mg->children) {
|
||||
for (uint32_t i = 0; i < mg->nr_children; i++)
|
||||
destroy_material(&mg->children[i]);
|
||||
free(mg->children);
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_vertex(struct buffer *r, int pol_version, struct pol_vertex *v)
|
||||
{
|
||||
read_position(r, v->pos);
|
||||
v->nr_weights = pol_version == 1 ? buffer_read_int32(r) : buffer_read_u16(r);
|
||||
if (v->nr_weights) {
|
||||
v->weights = xcalloc(v->nr_weights, sizeof(struct pol_bone_weight));
|
||||
for (uint32_t i = 0; i < v->nr_weights; i++) {
|
||||
v->weights[i].bone = pol_version == 1 ? buffer_read_int32(r) : buffer_read_u16(r);
|
||||
v->weights[i].weight = buffer_read_float(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy_vertex(struct pol_vertex *v)
|
||||
{
|
||||
if (v->weights)
|
||||
free(v->weights);
|
||||
}
|
||||
|
||||
static void parse_triangle(struct buffer *r, struct pol_mesh *mesh, int triangle_index, int unknowns_length, const struct pol_material_group *mg)
|
||||
{
|
||||
struct pol_triangle *t = &mesh->triangles[triangle_index];
|
||||
t->vert_index[0] = buffer_read_int32(r);
|
||||
t->vert_index[1] = buffer_read_int32(r);
|
||||
t->vert_index[2] = buffer_read_int32(r);
|
||||
t->uv_index[0] = buffer_read_int32(r);
|
||||
t->uv_index[1] = buffer_read_int32(r);
|
||||
t->uv_index[2] = buffer_read_int32(r);
|
||||
if (mesh->light_uvs) {
|
||||
t->light_uv_index[0] = buffer_read_int32(r) - mesh->nr_uvs;
|
||||
t->light_uv_index[1] = buffer_read_int32(r) - mesh->nr_uvs;
|
||||
t->light_uv_index[2] = buffer_read_int32(r) - mesh->nr_uvs;
|
||||
}
|
||||
|
||||
buffer_skip(r, unknowns_length);
|
||||
|
||||
read_direction(r, t->normals[0]);
|
||||
read_direction(r, t->normals[1]);
|
||||
read_direction(r, t->normals[2]);
|
||||
t->material_group_index = buffer_read_int32(r);
|
||||
if (t->material_group_index >= mg->nr_children)
|
||||
t->material_group_index = 0;
|
||||
}
|
||||
|
||||
static uint32_t parse_mesh_attributes(const char *name)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
if (strstr(name, "(nolighting)"))
|
||||
flags |= MESH_NOLIGHTING;
|
||||
if (strstr(name, "(nomakeshadow)"))
|
||||
flags |= MESH_NOMAKESHADOW;
|
||||
if (strstr(name, "(env)"))
|
||||
flags |= MESH_ENVMAP;
|
||||
if (strstr(name, "(both)"))
|
||||
flags |= MESH_BOTH;
|
||||
if (strstr(name, "(sprite)"))
|
||||
flags |= MESH_SPRITE;
|
||||
return flags;
|
||||
}
|
||||
|
||||
static struct pol_mesh *parse_mesh(struct buffer *r, const struct pol *pol)
|
||||
{
|
||||
int type = buffer_read_int32(r);
|
||||
if (type != 0) {
|
||||
if (type != -1)
|
||||
WARNING("unknown mesh type: %d", type);
|
||||
return NULL;
|
||||
}
|
||||
struct pol_mesh *mesh = xcalloc(1, sizeof(struct pol_mesh));
|
||||
mesh->name = read_cstring(r);
|
||||
mesh->flags = parse_mesh_attributes(mesh->name);
|
||||
mesh->material = buffer_read_int32(r);
|
||||
if (mesh->material >= pol->nr_materials) {
|
||||
WARNING("material index out of range (%d >= %d)", mesh->material, pol->nr_materials);
|
||||
mesh->material = 0;
|
||||
}
|
||||
|
||||
mesh->nr_vertices = buffer_read_int32(r);
|
||||
mesh->vertices = xcalloc(mesh->nr_vertices, sizeof(struct pol_vertex));
|
||||
for (uint32_t i = 0; i < mesh->nr_vertices; i++) {
|
||||
parse_vertex(r, pol->version, &mesh->vertices[i]);
|
||||
}
|
||||
|
||||
mesh->nr_uvs = buffer_read_int32(r);
|
||||
mesh->uvs = xcalloc(mesh->nr_uvs, sizeof(vec2));
|
||||
for (uint32_t i = 0; i < mesh->nr_uvs; i++) {
|
||||
mesh->uvs[i][0] = buffer_read_float(r);
|
||||
mesh->uvs[i][1] = buffer_read_float(r);
|
||||
}
|
||||
|
||||
mesh->nr_light_uvs = buffer_read_int32(r);
|
||||
if (mesh->nr_light_uvs > 0) {
|
||||
mesh->light_uvs = xcalloc(mesh->nr_light_uvs, sizeof(vec2));
|
||||
for (uint32_t i = 0; i < mesh->nr_light_uvs; i++) {
|
||||
mesh->light_uvs[i][0] = buffer_read_float(r);
|
||||
mesh->light_uvs[i][1] = buffer_read_float(r);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
if (pol->version == 1) {
|
||||
if (buffer_read_int32(r) != 1)
|
||||
WARNING("unexpected mesh footer");
|
||||
if (buffer_read_int32(r) != 0)
|
||||
WARNING("unexpected mesh footer");
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
static void free_mesh(struct pol_mesh *mesh)
|
||||
{
|
||||
if (!mesh)
|
||||
return;
|
||||
free(mesh->name);
|
||||
for (uint32_t i = 0; i < mesh->nr_vertices; i++)
|
||||
destroy_vertex(&mesh->vertices[i]);
|
||||
free(mesh->vertices);
|
||||
free(mesh->uvs);
|
||||
free(mesh->light_uvs);
|
||||
free(mesh->triangles);
|
||||
free(mesh);
|
||||
}
|
||||
|
||||
static void parse_bone(struct buffer *r, struct pol_bone *bone)
|
||||
{
|
||||
bone->name = read_cstring(r);
|
||||
bone->id = buffer_read_int32(r);
|
||||
bone->parent = buffer_read_int32(r);
|
||||
read_position(r, bone->pos);
|
||||
read_quaternion(r, bone->rotq);
|
||||
}
|
||||
|
||||
static void destroy_bone(struct pol_bone *bone)
|
||||
{
|
||||
free(bone->name);
|
||||
}
|
||||
|
||||
struct pol *pol_parse(uint8_t *data, size_t size)
|
||||
{
|
||||
struct buffer r;
|
||||
buffer_init(&r, data, size);
|
||||
if (memcmp(buffer_strdata(&r), "POL\0", 4))
|
||||
return NULL;
|
||||
buffer_skip(&r, 4);
|
||||
|
||||
struct pol *pol = xcalloc(1, sizeof(struct pol));
|
||||
pol->version = buffer_read_int32(&r);
|
||||
if (pol->version != 1 && pol->version != 2) {
|
||||
WARNING("unknown POL version: %d", pol->version);
|
||||
free(pol);
|
||||
return NULL;
|
||||
}
|
||||
pol->nr_materials = buffer_read_int32(&r);
|
||||
pol->materials = xcalloc(pol->nr_materials, sizeof(struct pol_material_group));
|
||||
for (uint32_t i = 0; i < pol->nr_materials; i++) {
|
||||
parse_material_group(&r, &pol->materials[i]);
|
||||
}
|
||||
|
||||
pol->nr_meshes = buffer_read_int32(&r);
|
||||
pol->meshes = xcalloc(pol->nr_meshes, sizeof(struct pol_mesh *));
|
||||
for (uint32_t i = 0; i < pol->nr_meshes; i++) {
|
||||
pol->meshes[i] = parse_mesh(&r, pol);
|
||||
}
|
||||
|
||||
pol->nr_bones = buffer_read_int32(&r);
|
||||
if (pol->nr_bones) {
|
||||
pol->bones = xcalloc(pol->nr_bones, sizeof(struct pol_bone));
|
||||
for (uint32_t i = 0; i < pol->nr_bones; i++) {
|
||||
parse_bone(&r, &pol->bones[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer_remaining(&r) != 0) {
|
||||
WARNING("extra data at end");
|
||||
}
|
||||
return pol;
|
||||
}
|
||||
|
||||
void pol_free(struct pol *pol)
|
||||
{
|
||||
for (uint32_t i = 0; i < pol->nr_materials; i++)
|
||||
destroy_material_group(&pol->materials[i]);
|
||||
free(pol->materials);
|
||||
for (uint32_t i = 0; i < pol->nr_meshes; i++)
|
||||
free_mesh(pol->meshes[i]);
|
||||
free(pol->meshes);
|
||||
for (uint32_t i = 0; i < pol->nr_bones; i++)
|
||||
destroy_bone(&pol->bones[i]);
|
||||
free(pol->bones);
|
||||
free(pol);
|
||||
}
|
||||
|
||||
void pol_compute_aabb(struct pol *pol, vec3 dest[2])
|
||||
{
|
||||
vec3 aabb[2];
|
||||
glm_aabb_invalidate(aabb);
|
||||
for (uint32_t i = 0; i < pol->nr_meshes; i++) {
|
||||
struct pol_mesh *mesh = pol->meshes[i];
|
||||
if (!mesh)
|
||||
continue;
|
||||
for (uint32_t j = 0; j < mesh->nr_vertices; j++) {
|
||||
glm_vec3_minv(mesh->vertices[j].pos, aabb[0], aabb[0]);
|
||||
glm_vec3_maxv(mesh->vertices[j].pos, aabb[1], aabb[1]);
|
||||
}
|
||||
}
|
||||
glm_vec3_copy(aabb[0], dest[0]);
|
||||
glm_vec3_copy(aabb[1], dest[1]);
|
||||
}
|
||||
|
||||
struct pol_bone *pol_find_bone(struct pol *pol, uint32_t id)
|
||||
{
|
||||
for (uint32_t i = 0; i < pol->nr_bones; i++) {
|
||||
if (pol->bones[i].id == id)
|
||||
return &pol->bones[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mot *mot_parse(uint8_t *data, size_t size, const char *name)
|
||||
{
|
||||
struct buffer r;
|
||||
buffer_init(&r, data, size);
|
||||
if (memcmp(buffer_strdata(&r), "MOT\0", 4))
|
||||
return NULL;
|
||||
buffer_skip(&r, 4);
|
||||
uint32_t version = buffer_read_int32(&r);
|
||||
if (version != 0) {
|
||||
WARNING("unknown MOT version: %d", version);
|
||||
return NULL;
|
||||
}
|
||||
uint32_t nr_frames = buffer_read_int32(&r);
|
||||
uint32_t nr_bones = buffer_read_int32(&r);
|
||||
|
||||
struct mot *mot = xcalloc(1, sizeof(struct mot) + nr_bones * sizeof(struct mot_bone *));
|
||||
mot->name = strdup(name);
|
||||
mot->nr_frames = nr_frames;
|
||||
mot->nr_bones = nr_bones;
|
||||
for (uint32_t i = 0; i < nr_bones; i++) {
|
||||
struct mot_bone *m = xcalloc(1, sizeof(struct mot_bone) + sizeof(struct mot_frame) * nr_frames);
|
||||
m->name = read_cstring(&r);
|
||||
m->id = buffer_read_int32(&r);
|
||||
m->parent = buffer_read_int32(&r);
|
||||
for (uint32_t j = 0; j < nr_frames; j++) {
|
||||
read_position(&r, m->frames[j].pos);
|
||||
read_quaternion(&r, m->frames[j].rotq);
|
||||
buffer_skip(&r, 16); // another quaternion?
|
||||
}
|
||||
mot->motions[i] = m;
|
||||
}
|
||||
if (buffer_remaining(&r) != 0) {
|
||||
WARNING("extra data at end");
|
||||
}
|
||||
return mot;
|
||||
}
|
||||
|
||||
void mot_free(struct mot *mot)
|
||||
{
|
||||
free(mot->name);
|
||||
for (uint32_t i = 0; i < mot->nr_bones; i++) {
|
||||
free(mot->motions[i]->name);
|
||||
free(mot->motions[i]);
|
||||
}
|
||||
free(mot);
|
||||
}
|
||||
|
||||
struct amt *amt_parse(uint8_t *data, size_t size)
|
||||
{
|
||||
struct buffer r;
|
||||
buffer_init(&r, data, size);
|
||||
if (memcmp(buffer_strdata(&r), "AMT\0", 4))
|
||||
return NULL;
|
||||
buffer_skip(&r, 4);
|
||||
|
||||
uint32_t version = buffer_read_int32(&r);
|
||||
int nr_fields;
|
||||
switch (version) {
|
||||
case 1: nr_fields = 3; break;
|
||||
case 2: nr_fields = 5; break;
|
||||
case 3: nr_fields = 6; break;
|
||||
case 4: nr_fields = 7; break;
|
||||
case 5: nr_fields = 11; break;
|
||||
case 6: nr_fields = 15; break;
|
||||
default:
|
||||
WARNING("unknown MOT version: %d", version);
|
||||
return NULL;
|
||||
}
|
||||
int nr_materials = buffer_read_int32(&r);
|
||||
|
||||
struct amt *amt = xmalloc(sizeof(struct amt) + sizeof(struct amt_material *) * nr_materials);
|
||||
amt->version = version;
|
||||
amt->nr_materials = nr_materials;
|
||||
for (int i = 0; i < nr_materials; i++) {
|
||||
struct amt_material *m = amt->materials[i] =
|
||||
xmalloc(sizeof(struct amt_material) + sizeof(float) * nr_fields);
|
||||
m->name = read_cstring(&r);
|
||||
for (int j = 0; j < nr_fields; j++)
|
||||
m->fields[j] = buffer_read_float(&r);
|
||||
}
|
||||
|
||||
if (buffer_remaining(&r) != 0) {
|
||||
WARNING("extra data at end");
|
||||
}
|
||||
return amt;
|
||||
}
|
||||
|
||||
void amt_free(struct amt *amt)
|
||||
{
|
||||
for (int i = 0; i < amt->nr_materials; i++) {
|
||||
free(amt->materials[i]->name);
|
||||
free(amt->materials[i]);
|
||||
}
|
||||
free(amt);
|
||||
}
|
||||
|
||||
struct amt_material *amt_find_material(struct amt *amt, const char *name)
|
||||
{
|
||||
for (int i = 0; i < amt->nr_materials; i++) {
|
||||
if (!strcmp(amt->materials[i]->name, name))
|
||||
return amt->materials[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,995 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/aar.h"
|
||||
#include "system4/cg.h"
|
||||
#include "system4/hashtable.h"
|
||||
#include "system4/utfsjis.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
|
||||
static inline float randf(void)
|
||||
{
|
||||
return rand() / (float)RAND_MAX;
|
||||
}
|
||||
|
||||
static void parse_error(const char *msg, char *input)
|
||||
{
|
||||
char *eol = strchr(input, '\n');
|
||||
*eol = '\0';
|
||||
WARNING("Parse error: %s Near: \"%s\"", msg, input);
|
||||
}
|
||||
|
||||
static enum particle_type parse_particle_type(const char *s)
|
||||
{
|
||||
if (!strcmp(s, "ビルボード"))
|
||||
return PARTICLE_TYPE_BILLBOARD;
|
||||
else if (!strcmp(s, "ポリゴンオブジェクト"))
|
||||
return PARTICLE_TYPE_POLYGON_OBJECT;
|
||||
else if (!strcmp(s, "剣ブラー"))
|
||||
return PARTICLE_TYPE_SWORD_BLUR;
|
||||
else if (!strcmp(s, "カメラ振動"))
|
||||
return PARTICLE_TYPE_CAMERA_QUAKE;
|
||||
WARNING("unknown object type \"%s\"", s);
|
||||
return (enum particle_type)-1;
|
||||
}
|
||||
|
||||
static enum particle_move_type parse_particle_move_type(const char *s)
|
||||
{
|
||||
if (!strcmp(s, "固定"))
|
||||
return PARTICLE_MOVE_FIXED;
|
||||
else if (!strcmp(s, "直線"))
|
||||
return PARTICLE_MOVE_LINEAR;
|
||||
else if (!strcmp(s, "放射"))
|
||||
return PARTICLE_MOVE_EMITTER;
|
||||
WARNING("unknown move type \"%s\"", s);
|
||||
return (enum particle_move_type)-1;
|
||||
}
|
||||
|
||||
static enum particle_blend_type parse_particle_blend_type(const char *s)
|
||||
{
|
||||
if (!strcmp(s, "標準"))
|
||||
return PARTICLE_BLEND_NORMAL;
|
||||
else if (!strcmp(s, "加算"))
|
||||
return PARTICLE_BLEND_ADDITIVE;
|
||||
WARNING("unknown blend type \"%s\"", s);
|
||||
return (enum particle_blend_type)-1;
|
||||
}
|
||||
|
||||
static enum particle_frame_ref_type parse_particle_frame_ref_type(const char *s, int *param)
|
||||
{
|
||||
if (!strcmp(s, "エフェクト"))
|
||||
return PARTICLE_FRAME_REF_EFFECT;
|
||||
else if (sscanf(s, "ターゲット[%d]", param) == 1)
|
||||
return PARTICLE_FRAME_REF_TARGET;
|
||||
WARNING("unknown frame ref type \"%s\"", s);
|
||||
return (enum particle_frame_ref_type)-1;
|
||||
}
|
||||
|
||||
static char *parse_quoted_string(char **ptext)
|
||||
{
|
||||
char *text = *ptext;
|
||||
|
||||
if (*text++ != '"') {
|
||||
parse_error("quoted string expected.", text);
|
||||
return NULL;
|
||||
}
|
||||
char *p = strchr(text, '"');
|
||||
if (!p) {
|
||||
parse_error("unfinished string.", text);
|
||||
return NULL;
|
||||
}
|
||||
*p = '\0';
|
||||
*ptext = p + 1;
|
||||
return utf2sjis(text, 0);
|
||||
}
|
||||
|
||||
#define MATCH(fmt, ...) (len = 0, sscanf(text, fmt " %n", ##__VA_ARGS__, &len), text += len, len)
|
||||
#define STRING_BUF_SIZE 64
|
||||
#define KEYWORD "%63s"
|
||||
#define QUOTED_STRING "\"%63[^\"]\""
|
||||
|
||||
static float *parse_float_list(char **ptext, int *nr)
|
||||
{
|
||||
char *text = *ptext;
|
||||
int len;
|
||||
float f;
|
||||
|
||||
if (!MATCH("%f", &f)) {
|
||||
parse_error("float expected.", text);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int cap = 16;
|
||||
float *floats = xmalloc(cap * sizeof(float));
|
||||
floats[0] = f;
|
||||
*nr = 1;
|
||||
while (MATCH(" , %f", &f)) {
|
||||
if (*nr == cap) {
|
||||
cap *= 2;
|
||||
floats = xrealloc(floats, cap * sizeof(float));
|
||||
}
|
||||
floats[(*nr)++] = f;
|
||||
}
|
||||
*ptext = text;
|
||||
return floats;
|
||||
}
|
||||
|
||||
static int *parse_int_list(char **ptext, int *nr)
|
||||
{
|
||||
char *text = *ptext;
|
||||
int len;
|
||||
int n;
|
||||
|
||||
if (!MATCH("%d", &n)) {
|
||||
parse_error("int expected.", text);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int cap = 16;
|
||||
int *ints = xmalloc(cap * sizeof(int));
|
||||
ints[0] = n;
|
||||
*nr = 1;
|
||||
while (MATCH(" , %d", &n)) {
|
||||
if (*nr == cap) {
|
||||
cap *= 2;
|
||||
ints = xrealloc(ints, cap * sizeof(int));
|
||||
}
|
||||
ints[(*nr)++] = n;
|
||||
}
|
||||
*ptext = text;
|
||||
return ints;
|
||||
}
|
||||
|
||||
static char **parse_qstr_list(char **ptext, int *nr, bool ignore_empty)
|
||||
{
|
||||
char *text = *ptext;
|
||||
int len;
|
||||
|
||||
int cap = 16;
|
||||
char **strs = xmalloc(cap * sizeof(char *));
|
||||
*nr = 0;
|
||||
do {
|
||||
if (*nr == cap) {
|
||||
cap *= 2;
|
||||
strs = xrealloc(strs, cap * sizeof(char *));
|
||||
}
|
||||
char *s = parse_quoted_string(&text);
|
||||
if (!s) {
|
||||
for (int i = 0; i < *nr; i++)
|
||||
free(strs[i]);
|
||||
free(strs);
|
||||
return NULL;
|
||||
}
|
||||
if (s[0] == '\0' && ignore_empty)
|
||||
free(s);
|
||||
else
|
||||
strs[(*nr)++] = s;
|
||||
} while (MATCH(" , "));
|
||||
*ptext = text;
|
||||
return strs;
|
||||
}
|
||||
|
||||
static struct particle_position *parse_position(char **ptext)
|
||||
{
|
||||
char *text = *ptext;
|
||||
int len;
|
||||
|
||||
struct particle_position *epos = xcalloc(1, sizeof(struct particle_position));
|
||||
|
||||
int n;
|
||||
float x, y, z;
|
||||
char string_buf[STRING_BUF_SIZE];
|
||||
for (int i = 0; i < NR_PARTICLE_POSITION_UNITS; i++) {
|
||||
struct particle_position_unit *unit = &epos->units[i];
|
||||
if (MATCH("なし")) {
|
||||
unit->type = PARTICLE_POS_UNIT_NONE;
|
||||
} else if (MATCH("絶対位置( %f , %f , %f )", &x, &y, &z)) {
|
||||
unit->type = PARTICLE_POS_UNIT_ABSOLUTE;
|
||||
unit->absolute[0] = x;
|
||||
unit->absolute[1] = y;
|
||||
unit->absolute[2] = -z;
|
||||
} else if (MATCH("ボーン[ %d , "QUOTED_STRING" , %f , %f , %f ]", &n, string_buf, &x, &y, &z) ||
|
||||
MATCH("ボーン[ %d , \"\" , %f , %f , %f ]", &n, &x, &y, &z)) {
|
||||
unit->type = PARTICLE_POS_UNIT_BONE;
|
||||
unit->bone.target_index = n;
|
||||
unit->bone.name = utf2sjis(string_buf, 0);
|
||||
unit->bone.offset[0] = x;
|
||||
unit->bone.offset[1] = y;
|
||||
unit->bone.offset[2] = -z;
|
||||
} else if (MATCH("ターゲット[ %d , %f , %f , %f ]", &n, &x, &y, &z)) {
|
||||
unit->type = PARTICLE_POS_UNIT_TARGET;
|
||||
unit->target.index = n;
|
||||
unit->target.offset[0] = x;
|
||||
unit->target.offset[1] = y;
|
||||
unit->target.offset[2] = -z;
|
||||
} else if (MATCH("ランダム方向( %f )", &x)) {
|
||||
unit->type = PARTICLE_POS_UNIT_RAND;
|
||||
unit->rand.f = x;
|
||||
} else if (MATCH("ランダム方向Y+( %f )", &x)) {
|
||||
unit->type = PARTICLE_POS_UNIT_RAND_POSITIVE_Y;
|
||||
unit->rand.f = x;
|
||||
} else {
|
||||
parse_error("unknown position unit.", text);
|
||||
free(epos);
|
||||
return NULL;
|
||||
}
|
||||
if (!MATCH(" + "))
|
||||
break;
|
||||
}
|
||||
|
||||
*ptext = text;
|
||||
return epos;
|
||||
}
|
||||
|
||||
static struct pae *parse_pae(char *text)
|
||||
{
|
||||
struct pae *pae = xcalloc(1, sizeof(struct pae));
|
||||
struct pae_object *current_object = NULL;
|
||||
|
||||
for (;;) {
|
||||
// Skip whitespaces and comments
|
||||
for (;;) {
|
||||
while (isspace(*text))
|
||||
text++;
|
||||
if (text[0] == '/' && text[1] == '/') {
|
||||
char *newline = strchr(text, '\n');
|
||||
if (newline)
|
||||
text = newline + 1;
|
||||
else
|
||||
text += strlen(text);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int len;
|
||||
int n, n2;
|
||||
float f, f2;
|
||||
char string_buf[STRING_BUF_SIZE];
|
||||
if (!current_object) {
|
||||
// Toplevel
|
||||
if (!*text) { // EOF
|
||||
return pae;
|
||||
} else if (MATCH("オブジェクト " QUOTED_STRING " = {", string_buf)) {
|
||||
pae->objects = xrealloc_array(pae->objects, pae->nr_objects, pae->nr_objects + 1, sizeof(struct pae_object));
|
||||
current_object = &pae->objects[pae->nr_objects++];
|
||||
current_object->name = utf2sjis(string_buf, 0);
|
||||
// Default values.
|
||||
current_object->move_type = PARTICLE_MOVE_LINEAR;
|
||||
current_object->texture_anime_frame = 1.0;
|
||||
current_object->child_end_slope = 1.0;
|
||||
} else {
|
||||
parse_error("unknown toplevel.", text);
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
// Object context
|
||||
if (!*text) {
|
||||
WARNING("unexpected EOF");
|
||||
return false;
|
||||
} else if (MATCH("}")) {
|
||||
current_object = NULL;
|
||||
} else if (MATCH("種類 = " KEYWORD, string_buf)) {
|
||||
current_object->type = parse_particle_type(string_buf);
|
||||
if (current_object->type < 0)
|
||||
goto err;
|
||||
} else if (MATCH("移動タイプ = " KEYWORD, string_buf)) {
|
||||
current_object->move_type = parse_particle_move_type(string_buf);
|
||||
if (current_object->move_type < 0)
|
||||
goto err;
|
||||
} else if (MATCH("アップベクトルタイプ = %d", &n)) {
|
||||
if (n > PARTICLE_UP_VECTOR_TYPE_MAX) {
|
||||
WARNING("unknown up vector type %d", n);
|
||||
n = PARTICLE_UP_VECTOR_TYPE_MAX;
|
||||
}
|
||||
current_object->up_vector_type = n;
|
||||
} else if (MATCH("移動カーブ = %f", &f)) {
|
||||
current_object->move_curve = f;
|
||||
} else if (MATCH("位置 = ")) {
|
||||
current_object->position[0] = parse_position(&text);
|
||||
if (!current_object->position[0])
|
||||
goto err;
|
||||
if (MATCH(" , ")) {
|
||||
current_object->position[1] = parse_position(&text);
|
||||
if (!current_object->position[1])
|
||||
goto err;
|
||||
}
|
||||
} else if (MATCH("サイズ = %f", &f)) {
|
||||
current_object->size[0] = f;
|
||||
if (MATCH(" , %f", &f2))
|
||||
current_object->size[1] = f2;
|
||||
else
|
||||
current_object->size[1] = f;
|
||||
} else if (MATCH("サイズ2 = ")) {
|
||||
current_object->sizes2 = parse_float_list(&text, ¤t_object->nr_sizes2);
|
||||
if (!current_object->sizes2)
|
||||
goto err;
|
||||
} else if (MATCH("サイズX = ")) {
|
||||
current_object->sizes_x = parse_float_list(&text, ¤t_object->nr_sizes_x);
|
||||
if (!current_object->sizes_x)
|
||||
goto err;
|
||||
} else if (MATCH("サイズY = ")) {
|
||||
current_object->sizes_y = parse_float_list(&text, ¤t_object->nr_sizes_y);
|
||||
if (!current_object->sizes_y)
|
||||
goto err;
|
||||
} else if (MATCH("サイズタイプ = ")) {
|
||||
current_object->size_types = parse_int_list(&text, ¤t_object->nr_size_types);
|
||||
if (!current_object->size_types)
|
||||
goto err;
|
||||
} else if (MATCH("サイズXタイプ = ")) {
|
||||
current_object->size_x_types = parse_int_list(&text, ¤t_object->nr_size_x_types);
|
||||
if (!current_object->size_x_types)
|
||||
goto err;
|
||||
} else if (MATCH("サイズYタイプ = ")) {
|
||||
current_object->size_y_types = parse_int_list(&text, ¤t_object->nr_size_y_types);
|
||||
if (!current_object->size_y_types)
|
||||
goto err;
|
||||
} else if (MATCH("テクスチャ = ")) {
|
||||
current_object->textures = parse_qstr_list(&text, ¤t_object->nr_textures, true);
|
||||
if (!current_object->textures)
|
||||
goto err;
|
||||
} else if (MATCH("ブレンドタイプ = " KEYWORD, string_buf)) {
|
||||
current_object->blend_type = parse_particle_blend_type(string_buf);
|
||||
if (current_object->blend_type < 0)
|
||||
goto err;
|
||||
} else if (MATCH("テクスチャアニメフレーム = %f", &f)) {
|
||||
current_object->texture_anime_frame = f;
|
||||
} else if (MATCH("テクスチャアニメ時間 = %d", &n)) {
|
||||
current_object->texture_anime_time = n;
|
||||
} else if (MATCH("ポリゴン = ")) {
|
||||
current_object->polygon_name = parse_quoted_string(&text);
|
||||
if (!current_object->polygon_name)
|
||||
goto err;
|
||||
} else if (MATCH("フレーム = %d , %d", &n, &n2)) {
|
||||
current_object->begin_frame = n;
|
||||
current_object->end_frame = n2;
|
||||
} else if (MATCH("停止フレーム = %d", &n)) {
|
||||
current_object->stop_frame = n;
|
||||
} else if (MATCH("フレーム参照 = " KEYWORD, string_buf)) {
|
||||
current_object->frame_ref_type = parse_particle_frame_ref_type(string_buf, ¤t_object->frame_ref_param);
|
||||
if (current_object->frame_ref_type < 0)
|
||||
goto err;
|
||||
} else if (MATCH("個数 = %d", &n)) {
|
||||
current_object->nr_particles = n;
|
||||
} else if (MATCH("アルファフェードインフレーム = %f", &f)) {
|
||||
current_object->alpha_fadein_frame = f;
|
||||
} else if (MATCH("アルファフェードイン時間 = %d", &n)) {
|
||||
current_object->alpha_fadein_time = n;
|
||||
} else if (MATCH("アルファフェードアウトフレーム = %f", &f)) {
|
||||
current_object->alpha_fadeout_frame = f;
|
||||
} else if (MATCH("アルファフェードアウト時間 = %d", &n)) {
|
||||
current_object->alpha_fadeout_time = n;
|
||||
} else if (MATCH("X軸自転角度 = %f , %f", &f, &f2)) {
|
||||
current_object->rotation.begin[0] = f;
|
||||
current_object->rotation.end[0] = f2;
|
||||
} else if (MATCH("Y軸自転角度 = %f , %f", &f, &f2)) {
|
||||
current_object->rotation.begin[1] = f;
|
||||
current_object->rotation.end[1] = f2;
|
||||
} else if (MATCH("Z軸自転角度 = %f , %f", &f, &f2)) {
|
||||
current_object->rotation.begin[2] = f;
|
||||
current_object->rotation.end[2] = f2;
|
||||
} else if (MATCH("X軸公転角度 = %f , %f", &f, &f2)) {
|
||||
current_object->revolution_angle.begin[0] = f;
|
||||
current_object->revolution_angle.end[0] = f2;
|
||||
} else if (MATCH("Y軸公転角度 = %f , %f", &f, &f2)) {
|
||||
current_object->revolution_angle.begin[1] = f;
|
||||
current_object->revolution_angle.end[1] = f2;
|
||||
} else if (MATCH("Z軸公転角度 = %f , %f", &f, &f2)) {
|
||||
current_object->revolution_angle.begin[2] = f;
|
||||
current_object->revolution_angle.end[2] = f2;
|
||||
} else if (MATCH("X軸公転距離 = %f , %f", &f, &f2)) {
|
||||
current_object->revolution_distance.begin[0] = f;
|
||||
current_object->revolution_distance.end[0] = f2;
|
||||
} else if (MATCH("Y軸公転距離 = %f , %f", &f, &f2)) {
|
||||
current_object->revolution_distance.begin[1] = f;
|
||||
current_object->revolution_distance.end[1] = f2;
|
||||
} else if (MATCH("Z軸公転距離 = %f , %f", &f, &f2)) {
|
||||
current_object->revolution_distance.begin[2] = f;
|
||||
current_object->revolution_distance.end[2] = f2;
|
||||
} else if (MATCH("カーブX距離 = %f", &f)) {
|
||||
current_object->curve_length[0] = f;
|
||||
} else if (MATCH("カーブY距離 = %f", &f)) {
|
||||
current_object->curve_length[1] = f;
|
||||
} else if (MATCH("カーブZ距離 = %f", &f)) {
|
||||
current_object->curve_length[2] = -f;
|
||||
} else if (MATCH("子フレーム = %d", &n)) {
|
||||
current_object->child_frame = n;
|
||||
} else if (MATCH("子距離 = %f", &f)) {
|
||||
current_object->child_length = f;
|
||||
} else if (MATCH("子傾き = %f", &f)) {
|
||||
current_object->child_begin_slope = f;
|
||||
} else if (MATCH("子傾き2 = %f", &f)) {
|
||||
current_object->child_end_slope = f;
|
||||
} else if (MATCH("子生成開始フレーム = %f", &f)) {
|
||||
current_object->child_create_begin_frame = f;
|
||||
} else if (MATCH("子生成終了フレーム = %f", &f)) {
|
||||
current_object->child_create_end_frame = f;
|
||||
} else if (MATCH("子移動方向タイプ = %d", &n)) {
|
||||
if (n > PARTICLE_CHILD_MOVE_DIR_TYPE_MAX) {
|
||||
WARNING("unknown child move dir type %d", n);
|
||||
n = PARTICLE_CHILD_MOVE_DIR_TYPE_MAX;
|
||||
}
|
||||
current_object->child_move_dir_type = n;
|
||||
} else if (MATCH("方向タイプ = %d", &n)) {
|
||||
if (n > PARTICLE_DIR_TYPE_MAX) {
|
||||
WARNING("unknown dir type %d", n);
|
||||
n = PARTICLE_DIR_TYPE_MAX;
|
||||
}
|
||||
current_object->dir_type = n;
|
||||
} else if (MATCH("ダメージ = ")) {
|
||||
current_object->damages = parse_int_list(&text, ¤t_object->nr_damages);
|
||||
if (!current_object->damages)
|
||||
goto err;
|
||||
} else if (MATCH("オフセットX = %f", &f)) {
|
||||
current_object->offset_x = f;
|
||||
} else if (MATCH("オフセットY = %f", &f)) {
|
||||
current_object->offset_y = f;
|
||||
} else {
|
||||
parse_error("unknown object field.", text);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
err:
|
||||
pae_free(pae);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void load_textures(struct pae *effect, struct archive *aar)
|
||||
{
|
||||
effect->textures = ht_create(16);
|
||||
for (int i = 0; i < effect->nr_objects; i++) {
|
||||
struct pae_object *po = &effect->objects[i];
|
||||
for (int j = 0; j < po->nr_textures; j++) {
|
||||
const char *name = po->textures[j];
|
||||
if (ht_get(effect->textures, name, NULL))
|
||||
continue; // already loaded.
|
||||
|
||||
struct archive_data *dfile = RE_get_aar_entry(aar, effect->path, name, ".bmp");
|
||||
if (!dfile)
|
||||
dfile = RE_get_aar_entry(aar, effect->path, name, ".tga");
|
||||
if (!dfile) {
|
||||
WARNING("cannot load texture %s\\%s", effect->path, name);
|
||||
continue;
|
||||
}
|
||||
struct cg *cg = cg_load_data(dfile);
|
||||
if (!cg) {
|
||||
WARNING("cg_load_data failed: %s", dfile->name);
|
||||
archive_free_data(dfile);
|
||||
continue;
|
||||
}
|
||||
archive_free_data(dfile);
|
||||
|
||||
GLuint texture;
|
||||
glGenTextures(1, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cg->metrics.w, cg->metrics.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, cg->pixels);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
struct billboard_texture *bt = xcalloc(1, sizeof(struct billboard_texture));
|
||||
bt->texture = texture;
|
||||
bt->has_alpha = cg->metrics.has_alpha;
|
||||
ht_put(effect->textures, name, bt);
|
||||
cg_free(cg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct RE_instance *get_target(struct RE_instance *inst, int index)
|
||||
{
|
||||
int target_id = inst->target[index];
|
||||
if (target_id < 0 || target_id >= inst->plugin->nr_instances)
|
||||
return NULL;
|
||||
return inst->plugin->instances[target_id];
|
||||
}
|
||||
|
||||
static void eval_pos_for_object(struct RE_instance *inst, struct particle_position *pp, vec3 dest)
|
||||
{
|
||||
glm_vec3_zero(dest);
|
||||
if (!pp)
|
||||
return;
|
||||
for (int i = 0; i < NR_PARTICLE_POSITION_UNITS; i++) {
|
||||
struct particle_position_unit *unit = &pp->units[i];
|
||||
switch (unit->type) {
|
||||
case PARTICLE_POS_UNIT_NONE:
|
||||
break;
|
||||
case PARTICLE_POS_UNIT_RAND:
|
||||
case PARTICLE_POS_UNIT_RAND_POSITIVE_Y:
|
||||
// These are computed per instance, in eval_pos_for_instance().
|
||||
break;
|
||||
case PARTICLE_POS_UNIT_ABSOLUTE:
|
||||
glm_vec3_add(dest, unit->absolute, dest);
|
||||
break;
|
||||
case PARTICLE_POS_UNIT_BONE:
|
||||
{
|
||||
struct RE_instance *target = get_target(inst, unit->bone.target_index);
|
||||
int bone = RE_instance_get_bone_index(target, unit->bone.name);
|
||||
vec3 target_pos;
|
||||
if (RE_instance_trans_local_pos_to_world_pos_by_bone(target, bone, unit->bone.offset, target_pos))
|
||||
glm_vec3_add(dest, target_pos, dest);
|
||||
else
|
||||
glm_vec3_add(dest, unit->bone.offset, dest);
|
||||
}
|
||||
break;
|
||||
case PARTICLE_POS_UNIT_TARGET:
|
||||
{
|
||||
struct RE_instance *target = get_target(inst, unit->target.index);
|
||||
if (!target)
|
||||
break;
|
||||
vec3 scale = {
|
||||
target->column_radius,
|
||||
target->column_height,
|
||||
target->column_radius
|
||||
};
|
||||
mat4 transform;
|
||||
glm_scale_make(transform, scale);
|
||||
glm_rotate_y(transform, -glm_rad(target->column_angle), transform);
|
||||
vec3 target_pos;
|
||||
glm_mat4_mulv3(transform, unit->target.offset, 1.0f, target_pos);
|
||||
|
||||
glm_vec3_addadd(target->pos, target->column_pos, target_pos);
|
||||
glm_vec3_add(dest, target_pos, dest);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void eval_pos_for_instance(struct particle_position *pp, vec3 dest)
|
||||
{
|
||||
glm_vec3_zero(dest);
|
||||
if (!pp)
|
||||
return;
|
||||
for (int i = 0; i < NR_PARTICLE_POSITION_UNITS; i++) {
|
||||
struct particle_position_unit *unit = &pp->units[i];
|
||||
switch (unit->type) {
|
||||
case PARTICLE_POS_UNIT_NONE:
|
||||
break;
|
||||
case PARTICLE_POS_UNIT_ABSOLUTE:
|
||||
case PARTICLE_POS_UNIT_BONE:
|
||||
case PARTICLE_POS_UNIT_TARGET:
|
||||
// These are computed per object, in eval_pos_for_object().
|
||||
break;
|
||||
case PARTICLE_POS_UNIT_RAND:
|
||||
dest[0] += (randf() * 2.0 - 1.0) * unit->rand.f;
|
||||
dest[1] += (randf() * 2.0 - 1.0) * unit->rand.f;
|
||||
dest[2] += (randf() * 2.0 - 1.0) * unit->rand.f;
|
||||
break;
|
||||
case PARTICLE_POS_UNIT_RAND_POSITIVE_Y:
|
||||
dest[0] += (randf() * 2.0 - 1.0) * unit->rand.f;
|
||||
dest[1] += randf() * unit->rand.f;
|
||||
dest[2] += (randf() * 2.0 - 1.0) * unit->rand.f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static float eval_child_slope(struct pae_object *po)
|
||||
{
|
||||
float slope = glm_lerp(po->child_begin_slope, po->child_end_slope, randf());
|
||||
if (slope > 1.0)
|
||||
slope = 0.5 + 0.5 / slope; // ???
|
||||
return (1.0 + slope) * GLM_PI_2;
|
||||
}
|
||||
|
||||
static void interpolate_pos(vec3 begin, vec3 end, float move_curve, float t, vec3 dest)
|
||||
{
|
||||
if (move_curve == 0.0f) {
|
||||
glm_vec3_copy(end, dest);
|
||||
return;
|
||||
}
|
||||
float x = move_curve > 0.0f ? powf(t, move_curve) : 1.0f - powf(1.0f - t, -move_curve);
|
||||
glm_vec3_lerp(begin, end, x, dest);
|
||||
}
|
||||
|
||||
static void init_particle_instances(struct particle_object *po)
|
||||
{
|
||||
struct pae_object *pae_obj = po->pae_obj;
|
||||
po->instances = xcalloc(pae_obj->nr_particles, sizeof(struct particle_instance));
|
||||
|
||||
for (int i = 0; i < pae_obj->nr_particles; i++) {
|
||||
struct particle_instance *pi = &po->instances[i];
|
||||
switch (pae_obj->move_type) {
|
||||
case PARTICLE_MOVE_FIXED:
|
||||
case PARTICLE_MOVE_LINEAR:
|
||||
pi->begin_frame = pae_obj->begin_frame;
|
||||
pi->end_frame = pae_obj->end_frame;
|
||||
break;
|
||||
case PARTICLE_MOVE_EMITTER:
|
||||
pi->begin_frame = glm_lerp(pae_obj->child_create_begin_frame, pae_obj->child_create_end_frame, (float)i / pae_obj->nr_particles);
|
||||
pi->end_frame = pi->begin_frame + pae_obj->child_frame;
|
||||
pi->roll_angle = eval_child_slope(pae_obj);
|
||||
pi->pitch_angle = randf() * (2.0f * GLM_PIf);
|
||||
break;
|
||||
}
|
||||
eval_pos_for_instance(pae_obj->position[0], pi->random_offset.begin);
|
||||
eval_pos_for_instance(pae_obj->position[1], pi->random_offset.end);
|
||||
}
|
||||
}
|
||||
|
||||
struct pae *pae_load(struct archive *aar, const char *path)
|
||||
{
|
||||
const char *basename = strrchr(path, '\\');
|
||||
basename = basename ? basename + 1 : path;
|
||||
|
||||
struct archive_data *dfile = RE_get_aar_entry(aar, path, basename, ".pae");
|
||||
if (!dfile) {
|
||||
WARNING("%s\\%s.pae: not found", path, basename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *sjis_text = xmalloc(dfile->size + 1);
|
||||
memcpy(sjis_text, dfile->data, dfile->size);
|
||||
// Replace null characters (written by buggy .pae formatter) with spaces.
|
||||
for (size_t i = 0; i < dfile->size; i++) {
|
||||
if (sjis_text[i] == '\0')
|
||||
sjis_text[i] = ' ';
|
||||
}
|
||||
sjis_text[dfile->size] = '\0';
|
||||
|
||||
// We could parse the SJIS text directly, but convert it to UTF-8 for code
|
||||
// clarity.
|
||||
char *text = sjis2utf(sjis_text, dfile->size);
|
||||
free(sjis_text);
|
||||
struct pae *pae = parse_pae(text);
|
||||
free(text);
|
||||
if (!pae) {
|
||||
WARNING("could not parse %s", dfile->name);
|
||||
archive_free_data(dfile);
|
||||
return NULL;
|
||||
}
|
||||
archive_free_data(dfile);
|
||||
|
||||
pae->path = strdup(path);
|
||||
load_textures(pae, aar);
|
||||
for (int i = 0; i < pae->nr_objects; i++) {
|
||||
struct pae_object *po = &pae->objects[i];
|
||||
|
||||
if (po->polygon_name && *po->polygon_name) {
|
||||
char *pol_path = xmalloc(strlen(path) + strlen(po->polygon_name) + 2);
|
||||
sprintf(pol_path, "%s\\%s", path, po->polygon_name);
|
||||
po->model = model_load(aar, pol_path);
|
||||
free(pol_path);
|
||||
}
|
||||
}
|
||||
|
||||
return pae;
|
||||
}
|
||||
|
||||
struct particle_effect *particle_effect_create(struct pae *pae)
|
||||
{
|
||||
struct particle_effect *effect = xcalloc(1, sizeof(struct particle_effect));
|
||||
effect->pae = pae;
|
||||
effect->objects = xcalloc(pae->nr_objects, sizeof(struct particle_object));
|
||||
effect->camera_quake_enabled = true;
|
||||
for (int i = 0; i < pae->nr_objects; i++) {
|
||||
struct particle_object *po = &effect->objects[i];
|
||||
po->pae_obj = &pae->objects[i];
|
||||
init_particle_instances(po);
|
||||
}
|
||||
return effect;
|
||||
}
|
||||
|
||||
static void free_billboard_texture(void *value)
|
||||
{
|
||||
struct billboard_texture *bt = value;
|
||||
glDeleteTextures(1, &bt->texture);
|
||||
free(bt);
|
||||
}
|
||||
|
||||
static void particle_position_free(struct particle_position *pos)
|
||||
{
|
||||
if (!pos)
|
||||
return;
|
||||
for (int i = 0; i < NR_PARTICLE_POSITION_UNITS; i++) {
|
||||
if (pos->units[i].type == PARTICLE_POS_UNIT_BONE)
|
||||
free(pos->units[i].bone.name);
|
||||
}
|
||||
free(pos);
|
||||
}
|
||||
|
||||
void pae_free(struct pae *pae)
|
||||
{
|
||||
for (int i = 0; i < pae->nr_objects; i++) {
|
||||
struct pae_object *po = &pae->objects[i];
|
||||
free(po->name);
|
||||
for (int j = 0; j < NR_PARTICLE_POSITIONS; j++)
|
||||
particle_position_free(po->position[j]);
|
||||
free(po->sizes2);
|
||||
free(po->sizes_x);
|
||||
free(po->sizes_y);
|
||||
free(po->size_types);
|
||||
free(po->size_x_types);
|
||||
free(po->size_y_types);
|
||||
for (int j = 0; j < po->nr_textures; j++)
|
||||
free(po->textures[j]);
|
||||
free(po->textures);
|
||||
free(po->polygon_name);
|
||||
free(po->damages);
|
||||
if (po->model)
|
||||
model_free(po->model);
|
||||
}
|
||||
ht_foreach_value(pae->textures, free_billboard_texture);
|
||||
ht_free(pae->textures);
|
||||
free(pae->objects);
|
||||
free(pae->path);
|
||||
free(pae);
|
||||
}
|
||||
|
||||
void particle_effect_free(struct particle_effect *effect)
|
||||
{
|
||||
for (int i = 0; i < effect->pae->nr_objects; i++) {
|
||||
struct particle_object *obj = &effect->objects[i];
|
||||
free(obj->instances);
|
||||
}
|
||||
free(effect->objects);
|
||||
free(effect);
|
||||
}
|
||||
|
||||
void pae_calc_frame_range(struct pae *pae, struct motion *motion)
|
||||
{
|
||||
int begin_frame = INT_MAX;
|
||||
int end_frame = INT_MIN;
|
||||
for (int i = 0; i < pae->nr_objects; i++) {
|
||||
struct pae_object *po = &pae->objects[i];
|
||||
int obj_begin = po->begin_frame;
|
||||
int obj_end = max(po->end_frame, po->child_create_end_frame + po->child_frame);
|
||||
if (begin_frame > obj_begin)
|
||||
begin_frame = obj_begin;
|
||||
if (end_frame < obj_end)
|
||||
end_frame = obj_end;
|
||||
}
|
||||
motion->frame_begin = motion->loop_frame_begin = begin_frame;
|
||||
motion->frame_end = motion->loop_frame_end = end_frame;
|
||||
}
|
||||
|
||||
static void update_camera_quake(struct RE_instance *inst, struct pae_object *po)
|
||||
{
|
||||
float frame = inst->motion->current_frame - po->begin_frame;
|
||||
float total_frames = po->end_frame - po->begin_frame;
|
||||
if (frame < 0 || frame >= total_frames)
|
||||
return;
|
||||
|
||||
const float ease_in_duration = 4.0f;
|
||||
const float ease_out_duration = 4.0f;
|
||||
float amplitude = 1.0f;
|
||||
if (total_frames - frame < ease_out_duration)
|
||||
amplitude *= (total_frames - frame) / ease_out_duration;
|
||||
else if (frame < ease_in_duration)
|
||||
amplitude *= frame / ease_in_duration;
|
||||
|
||||
inst->plugin->camera.quake_pitch = sinf(frame * 1.1) * amplitude * -8.0f;
|
||||
inst->plugin->camera.quake_yaw = sinf(frame * 1.7) * amplitude * 12.0f;
|
||||
}
|
||||
|
||||
void particle_effect_update(struct RE_instance *inst)
|
||||
{
|
||||
struct particle_effect *effect = inst->effect;
|
||||
if (!effect)
|
||||
return;
|
||||
for (int i = 0; i < effect->pae->nr_objects; i++) {
|
||||
struct particle_object *po = &effect->objects[i];
|
||||
struct pae_object *pae_obj = po->pae_obj;
|
||||
if (pae_obj->type == PARTICLE_TYPE_CAMERA_QUAKE) {
|
||||
if (effect->camera_quake_enabled)
|
||||
update_camera_quake(inst, pae_obj);
|
||||
continue;
|
||||
}
|
||||
// These cannot be precomputed, because they may depend on the target's
|
||||
// *current* bone matrix.
|
||||
eval_pos_for_object(inst, pae_obj->position[0], po->pos.begin);
|
||||
eval_pos_for_object(inst, pae_obj->position[1], po->pos.end);
|
||||
glm_vec3_sub(po->pos.end, po->pos.begin, po->move_vec);
|
||||
switch (pae_obj->dir_type) {
|
||||
case PARTICLE_DIR_TYPE_NORMAL:
|
||||
glm_vec3_normalize(po->move_vec);
|
||||
glm_vec3_ortho(po->move_vec, po->move_ortho);
|
||||
glm_vec3_normalize(po->move_ortho);
|
||||
break;
|
||||
case PARTICLE_DIR_TYPE_XY_PLANE:
|
||||
po->move_vec[2] = 0.0f;
|
||||
glm_vec3_normalize(po->move_vec);
|
||||
glm_vec3_copy(GLM_ZUP, po->move_ortho);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_emitter_pos(struct particle_object *po, struct particle_instance *pi, float t, vec3 dest, vec3 move_vec)
|
||||
{
|
||||
struct pae_object *pae_obj = po->pae_obj;
|
||||
vec3 begin, end;
|
||||
glm_vec3_add(po->pos.begin, pi->random_offset.begin, begin);
|
||||
glm_vec3_add(po->pos.end, pi->random_offset.end, end);
|
||||
interpolate_pos(begin, end, pae_obj->move_curve, t, dest);
|
||||
if (move_vec)
|
||||
glm_vec3_sub(end, begin, move_vec);
|
||||
|
||||
// Orbital rotation.
|
||||
vec3 angle, dist;
|
||||
glm_vec3_lerp(pae_obj->revolution_angle.begin, pae_obj->revolution_angle.end, t, angle);
|
||||
glm_vec3_lerp(pae_obj->revolution_distance.begin, pae_obj->revolution_distance.end, t, dist);
|
||||
glm_vec3_scale(angle, GLM_PIf / 180.0f, angle); // deg -> rad
|
||||
if (pae_obj->revolution_angle.begin[0] != 0.0f || pae_obj->revolution_angle.end[0] != 0.0f) {
|
||||
dest[1] += dist[0] * sinf(angle[0]);
|
||||
dest[2] -= dist[0] * cosf(angle[0]);
|
||||
}
|
||||
if (pae_obj->revolution_angle.begin[1] != 0.0f || pae_obj->revolution_angle.end[1] != 0.0f) {
|
||||
dest[0] += dist[1] * sinf(angle[1]);
|
||||
dest[2] -= dist[1] * cosf(angle[1]);
|
||||
}
|
||||
if (pae_obj->revolution_angle.begin[2] != 0.0f || pae_obj->revolution_angle.end[2] != 0.0f) {
|
||||
dest[1] += dist[2] * sinf(angle[2]);
|
||||
dest[0] += dist[2] * cosf(angle[2]);
|
||||
}
|
||||
|
||||
// Apply the curve.
|
||||
glm_vec3_muladds(pae_obj->curve_length, 4.9f * t * (1.0f - t), dest);
|
||||
}
|
||||
|
||||
static void calc_pos_and_up_vector(struct particle_object *po, struct particle_instance *pi, float t, vec3 pos, vec3 up_vector)
|
||||
{
|
||||
struct pae_object *pae_obj = po->pae_obj;
|
||||
switch (pae_obj->move_type) {
|
||||
case PARTICLE_MOVE_FIXED:
|
||||
glm_vec3_add(po->pos.begin, pi->random_offset.begin, pos);
|
||||
glm_vec3_copy(GLM_YUP, up_vector);
|
||||
break;
|
||||
case PARTICLE_MOVE_LINEAR:
|
||||
calc_emitter_pos(po, pi, t, pos, up_vector);
|
||||
switch (pae_obj->up_vector_type) {
|
||||
case PARTICLE_UP_VECTOR_Y_AXIS:
|
||||
case PARTICLE_UP_VECTOR_INSTANCE_MOVE:
|
||||
glm_vec3_copy(GLM_YUP, up_vector);
|
||||
break;
|
||||
case PARTICLE_UP_VECTOR_OBJECT_MOVE:
|
||||
if (glm_vec3_norm2(up_vector) == 0.0f)
|
||||
glm_vec3_copy(GLM_XUP, up_vector);
|
||||
else
|
||||
glm_vec3_normalize(up_vector);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case PARTICLE_MOVE_EMITTER:
|
||||
{
|
||||
// Calculate the instance's initial and final position.
|
||||
float t0 = glm_clamp_zo(glm_percent(pae_obj->begin_frame, pae_obj->end_frame, pi->begin_frame));
|
||||
vec3_range inst_pos;
|
||||
calc_emitter_pos(po, pi, t0, inst_pos.begin, NULL);
|
||||
vec3 inst_move;
|
||||
glm_vec3_copy(po->move_vec, inst_move);
|
||||
switch (pae_obj->dir_type) {
|
||||
case PARTICLE_DIR_TYPE_NORMAL:
|
||||
glm_vec3_rotate(inst_move, pi->roll_angle, po->move_ortho);
|
||||
glm_vec3_rotate(inst_move, pi->pitch_angle, po->move_vec);
|
||||
break;
|
||||
case PARTICLE_DIR_TYPE_XY_PLANE:
|
||||
glm_vec3_rotate(inst_move, (pi->pitch_angle < GLM_PIf ? -1.0f : 1.0f) * pi->roll_angle, po->move_ortho);
|
||||
break;
|
||||
}
|
||||
glm_vec3_copy(inst_pos.begin, inst_pos.end);
|
||||
switch (pae_obj->child_move_dir_type) {
|
||||
case PARTICLE_CHILD_MOVE_DIR_NORMAL:
|
||||
glm_vec3_muladds(inst_move, pae_obj->child_length, inst_pos.end);
|
||||
break;
|
||||
case PARTICLE_CHILD_MOVE_DIR_REVERSE:
|
||||
glm_vec3_muladds(inst_move, pae_obj->child_length, inst_pos.begin);
|
||||
glm_vec3_negate(inst_move);
|
||||
break;
|
||||
}
|
||||
// Current position of the instance.
|
||||
glm_vec3_lerp(inst_pos.begin, inst_pos.end, t, pos);
|
||||
|
||||
// Calculate the up vector.
|
||||
switch (pae_obj->up_vector_type) {
|
||||
case PARTICLE_UP_VECTOR_Y_AXIS:
|
||||
glm_vec3_copy(GLM_YUP, up_vector);
|
||||
break;
|
||||
case PARTICLE_UP_VECTOR_OBJECT_MOVE:
|
||||
glm_vec3_copy(po->move_vec, up_vector);
|
||||
break;
|
||||
case PARTICLE_UP_VECTOR_INSTANCE_MOVE:
|
||||
if (pae_obj->child_length != 0.0f)
|
||||
glm_vec3_copy(inst_move, up_vector);
|
||||
else
|
||||
glm_vec3_copy(GLM_XUP, up_vector);
|
||||
break;
|
||||
}
|
||||
if (pae_obj->dir_type == PARTICLE_DIR_TYPE_XY_PLANE) {
|
||||
mat3 rot = {{ inst_move[1], -inst_move[0], 0.0f },
|
||||
{ inst_move[0], inst_move[1], 0.0f },
|
||||
{ 0.0f, 0.0f, 1.0f }};
|
||||
glm_mat3_mulv(rot, up_vector, up_vector);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void calc_scale(struct pae_object *po, int frame, vec3 dest)
|
||||
{
|
||||
glm_vec3_broadcast(po->sizes2[min(frame, po->nr_sizes2 - 1)], dest);
|
||||
if (po->nr_sizes_x)
|
||||
dest[0] *= po->sizes_x[min(frame, po->nr_sizes_x - 1)];
|
||||
if (po->nr_sizes_y)
|
||||
dest[1] *= po->sizes_y[min(frame, po->nr_sizes_y - 1)];
|
||||
}
|
||||
|
||||
void particle_object_calc_local_transform(struct RE_instance *inst, struct particle_object *po, struct particle_instance *pi, float frame, mat4 dest)
|
||||
{
|
||||
struct pae_object *pae_obj = po->pae_obj;
|
||||
float t = glm_percent(pi->begin_frame, pi->end_frame, frame);
|
||||
|
||||
vec3 pos, up_vector;
|
||||
calc_pos_and_up_vector(po, pi, t, pos, up_vector);
|
||||
|
||||
float rel_frame = frame - pi->begin_frame;
|
||||
int cur_step = rel_frame;
|
||||
int next_step = ceilf(rel_frame);
|
||||
float step_frac = rel_frame - cur_step;
|
||||
|
||||
glm_translate_make(dest, pos);
|
||||
if (pae_obj->type == PARTICLE_TYPE_BILLBOARD) {
|
||||
mat4 view_mat;
|
||||
RE_calc_view_matrix(&inst->plugin->camera, up_vector, view_mat);
|
||||
mat3 rot;
|
||||
glm_mat4_pick3t(view_mat, rot);
|
||||
glm_mat4_ins3(rot, dest);
|
||||
}
|
||||
|
||||
vec3 angles;
|
||||
glm_vec3_lerp(pae_obj->rotation.begin, pae_obj->rotation.end, t, angles);
|
||||
angles[0] *= -GLM_PIf / 180.0f;
|
||||
angles[1] *= -GLM_PIf / 180.0f;
|
||||
angles[2] *= GLM_PIf / 180.0f;
|
||||
mat4 rotation_mat;
|
||||
glm_euler_zxy(angles, rotation_mat);
|
||||
glm_mat4_mul(dest, rotation_mat, dest);
|
||||
|
||||
if (pae_obj->nr_sizes2 > 0) {
|
||||
vec3 scale, scale2;
|
||||
calc_scale(pae_obj, cur_step, scale);
|
||||
calc_scale(pae_obj, next_step, scale2);
|
||||
glm_vec3_lerp(scale, scale2, step_frac, scale);
|
||||
glm_scale(dest, scale);
|
||||
} else {
|
||||
glm_scale_uni(dest, glm_lerp(pae_obj->size[0], pae_obj->size[1], t));
|
||||
}
|
||||
|
||||
glm_translate_x(dest, -pae_obj->offset_x);
|
||||
glm_translate_y(dest, -pae_obj->offset_y);
|
||||
}
|
||||
|
||||
float pae_object_calc_alpha(struct pae_object *po, struct particle_instance *pi, float frame)
|
||||
{
|
||||
frame -= pi->begin_frame;
|
||||
if (frame < po->alpha_fadein_frame)
|
||||
return glm_clamp_zo(frame / po->alpha_fadein_frame);
|
||||
float total_frames = pi->end_frame - pi->begin_frame;
|
||||
if (total_frames - po->alpha_fadeout_frame < frame)
|
||||
return glm_clamp_zo((total_frames - frame) / po->alpha_fadeout_frame);
|
||||
return 1.0;
|
||||
}
|
||||
+1061
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,990 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/cg.h"
|
||||
#include "system4/hashtable.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
#include "asset_manager.h"
|
||||
#include "reign.h"
|
||||
#include "sact.h"
|
||||
|
||||
// TODO: Respect RE_plugin.shadow_map_resolution_level
|
||||
#define SHADOW_WIDTH 512
|
||||
#define SHADOW_HEIGHT 512
|
||||
|
||||
enum {
|
||||
COLOR_TEXTURE_UNIT,
|
||||
SPECULAR_TEXTURE_UNIT,
|
||||
ALPHA_TEXTURE_UNIT,
|
||||
LIGHT_TEXTURE_UNIT,
|
||||
NORMAL_TEXTURE_UNIT,
|
||||
SHADOW_TEXTURE_UNIT,
|
||||
};
|
||||
|
||||
enum {
|
||||
DIFFUSE_EMISSIVE = 0,
|
||||
DIFFUSE_NORMAL = 1,
|
||||
DIFFUSE_LIGHT_MAP = 2,
|
||||
DIFFUSE_ENV_MAP = 3,
|
||||
};
|
||||
|
||||
enum {
|
||||
ALPHA_BLEND = 0,
|
||||
ALPHA_TEST = 1,
|
||||
ALPHA_MAP_BLEND = 2,
|
||||
};
|
||||
|
||||
enum draw_phase {
|
||||
DRAW_OPAQUE,
|
||||
DRAW_TRANSPARENT,
|
||||
};
|
||||
|
||||
static GLuint load_shader(const char *vertex_shader_path, const char *fragment_shader_path)
|
||||
{
|
||||
GLuint program = glCreateProgram();
|
||||
GLuint vertex_shader = gfx_load_shader_file(vertex_shader_path, GL_VERTEX_SHADER);
|
||||
GLuint fragment_shader = gfx_load_shader_file(fragment_shader_path, GL_FRAGMENT_SHADER);
|
||||
|
||||
glAttachShader(program, vertex_shader);
|
||||
glAttachShader(program, fragment_shader);
|
||||
|
||||
// Bind vertex attributes to fixed locations, so that common VAO can be used
|
||||
// by multiple programs.
|
||||
glBindAttribLocation(program, VATTR_POS, "vertex_pos");
|
||||
glBindAttribLocation(program, VATTR_NORMAL, "vertex_normal");
|
||||
glBindAttribLocation(program, VATTR_UV, "vertex_uv");
|
||||
glBindAttribLocation(program, VATTR_BONE_INDEX, "vertex_bone_index");
|
||||
glBindAttribLocation(program, VATTR_BONE_WEIGHT, "vertex_bone_weight");
|
||||
glBindAttribLocation(program, VATTR_LIGHT_UV, "vertex_light_uv");
|
||||
glBindAttribLocation(program, VATTR_TANGENT, "vertex_tangent");
|
||||
|
||||
glLinkProgram(program);
|
||||
|
||||
GLint link_success;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &link_success);
|
||||
if (!link_success) {
|
||||
GLint len;
|
||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len);
|
||||
char *infolog = xmalloc(len + 1);
|
||||
glGetProgramInfoLog(program, len, NULL, infolog);
|
||||
ERROR("Failed to link shader %s, %s: %s", vertex_shader_path, fragment_shader_path, infolog);
|
||||
}
|
||||
return program;
|
||||
}
|
||||
|
||||
static void init_shadow_renderer(struct shadow_renderer *sr)
|
||||
{
|
||||
GLint orig_fbo;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &orig_fbo);
|
||||
|
||||
sr->program = load_shader("shaders/reign_shadow.v.glsl", "shaders/reign_shadow.f.glsl");
|
||||
sr->world_transform = glGetUniformLocation(sr->program, "world_transform");
|
||||
sr->view_transform = glGetUniformLocation(sr->program, "view_transform");
|
||||
sr->has_bones = glGetUniformLocation(sr->program, "has_bones");
|
||||
sr->bone_matrices = glGetUniformLocation(sr->program, "bone_matrices");
|
||||
|
||||
glGenFramebuffers(1, &sr->fbo);
|
||||
glGenTextures(1, &sr->texture);
|
||||
glBindTexture(GL_TEXTURE_2D, sr->texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, sr->fbo);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, sr->texture, 0);
|
||||
glDrawBuffers(0, NULL);
|
||||
glReadBuffer(GL_NONE);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, orig_fbo);
|
||||
}
|
||||
|
||||
static void destroy_shadow_renderer(struct shadow_renderer *sr)
|
||||
{
|
||||
glDeleteTextures(1, &sr->texture);
|
||||
glDeleteFramebuffers(1, &sr->fbo);
|
||||
glDeleteProgram(sr->program);
|
||||
}
|
||||
|
||||
static void init_billboard_mesh(struct RE_renderer *r)
|
||||
{
|
||||
static const GLfloat vertices[] = {
|
||||
// x, y, z, u, v
|
||||
-1.0, 1.0, 0.0, 0.0, 0.0,
|
||||
-1.0, -1.0, 0.0, 0.0, 1.0,
|
||||
1.0, 1.0, 0.0, 1.0, 0.0,
|
||||
1.0, -1.0, 0.0, 1.0, 1.0,
|
||||
};
|
||||
glGenVertexArrays(1, &r->billboard_vao);
|
||||
glBindVertexArray(r->billboard_vao);
|
||||
glGenBuffers(1, &r->billboard_attr_buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, r->billboard_attr_buffer);
|
||||
|
||||
glEnableVertexAttribArray(VATTR_POS);
|
||||
glVertexAttribPointer(VATTR_POS, 3, GL_FLOAT, GL_FALSE, 20, (const void *)0);
|
||||
glEnableVertexAttribArray(VATTR_UV);
|
||||
glVertexAttribPointer(VATTR_UV, 2, GL_FLOAT, GL_FALSE, 20, (const void *)12);
|
||||
glDisableVertexAttribArray(VATTR_LIGHT_UV);
|
||||
glVertexAttrib2f(VATTR_LIGHT_UV, 0.0, 0.0);
|
||||
glDisableVertexAttribArray(VATTR_NORMAL);
|
||||
glVertexAttrib3f(VATTR_NORMAL, 0.0, 0.0, 1.0);
|
||||
glDisableVertexAttribArray(VATTR_TANGENT);
|
||||
glVertexAttrib3f(VATTR_TANGENT, 1.0, 0.0, 0.0);
|
||||
glDisableVertexAttribArray(VATTR_BONE_INDEX);
|
||||
glVertexAttribI4i(VATTR_BONE_INDEX, 0, 0, 0, 0);
|
||||
glDisableVertexAttribArray(VATTR_BONE_WEIGHT);
|
||||
glVertexAttrib4f(VATTR_BONE_WEIGHT, 0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
static void destroy_billboard_mesh(struct RE_renderer *r)
|
||||
{
|
||||
glDeleteVertexArrays(1, &r->billboard_vao);
|
||||
glDeleteBuffers(1, &r->billboard_attr_buffer);
|
||||
}
|
||||
|
||||
struct RE_renderer *RE_renderer_new(struct texture *texture)
|
||||
{
|
||||
struct RE_renderer *r = xcalloc(1, sizeof(struct RE_renderer));
|
||||
|
||||
r->program = load_shader("shaders/reign.v.glsl", "shaders/reign.f.glsl");
|
||||
r->world_transform = glGetUniformLocation(r->program, "world_transform");
|
||||
r->view_transform = glGetUniformLocation(r->program, "view_transform");
|
||||
r->texture = glGetUniformLocation(r->program, "tex");
|
||||
r->local_transform = glGetUniformLocation(r->program, "local_transform");
|
||||
r->proj_transform = glGetUniformLocation(r->program, "proj_transform");
|
||||
r->normal_transform = glGetUniformLocation(r->program, "normal_transform");
|
||||
r->alpha_mod = glGetUniformLocation(r->program, "alpha_mod");
|
||||
r->has_bones = glGetUniformLocation(r->program, "has_bones");
|
||||
r->bone_matrices = glGetUniformLocation(r->program, "bone_matrices");
|
||||
r->ambient = glGetUniformLocation(r->program, "ambient");
|
||||
for (int i = 0; i < NR_DIR_LIGHTS; i++) {
|
||||
char buf[64];
|
||||
sprintf(buf, "dir_lights[%d].dir", i);
|
||||
r->dir_lights[i].dir = glGetUniformLocation(r->program, buf);
|
||||
sprintf(buf, "dir_lights[%d].diffuse", i);
|
||||
r->dir_lights[i].diffuse = glGetUniformLocation(r->program, buf);
|
||||
sprintf(buf, "dir_lights[%d].globe_diffuse", i);
|
||||
r->dir_lights[i].globe_diffuse = glGetUniformLocation(r->program, buf);
|
||||
}
|
||||
r->specular_light_dir = glGetUniformLocation(r->program, "specular_light_dir");
|
||||
r->specular_strength = glGetUniformLocation(r->program, "specular_strength");
|
||||
r->specular_shininess = glGetUniformLocation(r->program, "specular_shininess");
|
||||
r->use_specular_map = glGetUniformLocation(r->program, "use_specular_map");
|
||||
r->specular_texture = glGetUniformLocation(r->program, "specular_texture");
|
||||
r->rim_exponent = glGetUniformLocation(r->program, "rim_exponent");
|
||||
r->rim_color = glGetUniformLocation(r->program, "rim_color");
|
||||
r->camera_pos = glGetUniformLocation(r->program, "camera_pos");
|
||||
r->diffuse_type = glGetUniformLocation(r->program, "diffuse_type");
|
||||
r->light_texture = glGetUniformLocation(r->program, "light_texture");
|
||||
r->use_normal_map = glGetUniformLocation(r->program, "use_normal_map");
|
||||
r->normal_texture = glGetUniformLocation(r->program, "normal_texture");
|
||||
r->shadow_darkness = glGetUniformLocation(r->program, "shadow_darkness");
|
||||
r->shadow_transform = glGetUniformLocation(r->program, "shadow_transform");
|
||||
r->shadow_texture = glGetUniformLocation(r->program, "shadow_texture");
|
||||
r->shadow_bias = glGetUniformLocation(r->program, "shadow_bias");
|
||||
r->fog_type = glGetUniformLocation(r->program, "fog_type");
|
||||
r->fog_near = glGetUniformLocation(r->program, "fog_near");
|
||||
r->fog_far = glGetUniformLocation(r->program, "fog_far");
|
||||
r->fog_color = glGetUniformLocation(r->program, "fog_color");
|
||||
r->ls_params = glGetUniformLocation(r->program, "ls_params");
|
||||
r->ls_light_dir = glGetUniformLocation(r->program, "ls_light_dir");
|
||||
r->ls_light_color = glGetUniformLocation(r->program, "ls_light_color");
|
||||
r->ls_sun_color = glGetUniformLocation(r->program, "ls_sun_color");
|
||||
r->alpha_mode = glGetUniformLocation(r->program, "alpha_mode");
|
||||
r->alpha_texture = glGetUniformLocation(r->program, "alpha_texture");
|
||||
|
||||
glGenRenderbuffers(1, &r->depth_buffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, r->depth_buffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, texture->w, texture->h);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
|
||||
init_shadow_renderer(&r->shadow);
|
||||
init_billboard_mesh(r);
|
||||
r->billboard_textures = ht_create(256);
|
||||
return r;
|
||||
}
|
||||
|
||||
bool RE_renderer_load_billboard_texture(struct RE_renderer *r, int cg_no)
|
||||
{
|
||||
if (ht_get_int(r->billboard_textures, cg_no, NULL))
|
||||
return true;
|
||||
|
||||
struct cg *cg = asset_cg_load(cg_no);
|
||||
if (!cg)
|
||||
return false;
|
||||
|
||||
struct billboard_texture *bt = xcalloc(1, sizeof(struct billboard_texture));
|
||||
glGenTextures(1, &bt->texture);
|
||||
glBindTexture(GL_TEXTURE_2D, bt->texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cg->metrics.w, cg->metrics.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, cg->pixels);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
bt->has_alpha = cg->metrics.has_alpha;
|
||||
|
||||
cg_free(cg);
|
||||
ht_put_int(r->billboard_textures, cg_no, bt);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void free_billboard_texture(void *value)
|
||||
{
|
||||
struct billboard_texture *bt = value;
|
||||
glDeleteTextures(1, &bt->texture);
|
||||
free(bt);
|
||||
}
|
||||
|
||||
void RE_renderer_free(struct RE_renderer *r)
|
||||
{
|
||||
glDeleteProgram(r->program);
|
||||
glDeleteRenderbuffers(1, &r->depth_buffer);
|
||||
ht_foreach_value(r->billboard_textures, free_billboard_texture);
|
||||
ht_free_int(r->billboard_textures);
|
||||
destroy_billboard_mesh(r);
|
||||
destroy_shadow_renderer(&r->shadow);
|
||||
free(r);
|
||||
}
|
||||
|
||||
void RE_calc_view_matrix(struct RE_camera *camera, vec3 up, mat4 out)
|
||||
{
|
||||
vec3 front = { 0.0, 0.0, -1.0 };
|
||||
vec3 euler = {
|
||||
glm_rad(camera->pitch + camera->quake_pitch),
|
||||
glm_rad(camera->yaw + camera->quake_yaw),
|
||||
glm_rad(camera->roll)
|
||||
};
|
||||
mat4 rot;
|
||||
glm_euler(euler, rot);
|
||||
glm_mat4_mulv3(rot, front, 0.0, front);
|
||||
glm_look(camera->pos, front, up, out);
|
||||
}
|
||||
|
||||
static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum draw_phase phase)
|
||||
{
|
||||
struct model *model = inst->model;
|
||||
if (!inst->model)
|
||||
return;
|
||||
bool all_transparent = inst->alpha < 1.0f;
|
||||
bool all_opaque = !model->has_transparent_material && inst->alpha >= 1.0f;
|
||||
if ((phase == DRAW_OPAQUE && all_transparent) || (phase == DRAW_TRANSPARENT && all_opaque))
|
||||
return;
|
||||
|
||||
if (inst->local_transform_needs_update)
|
||||
RE_instance_update_local_transform(inst);
|
||||
|
||||
glUniformMatrix4fv(r->local_transform, 1, GL_FALSE, inst->local_transform[0]);
|
||||
glUniformMatrix3fv(r->normal_transform, 1, GL_FALSE, inst->normal_transform[0]);
|
||||
glUniform1f(r->alpha_mod, inst->alpha);
|
||||
vec3 ambient;
|
||||
glm_vec3_add(inst->plugin->global_ambient, inst->ambient, ambient);
|
||||
glUniform3fv(r->ambient, 1, ambient);
|
||||
|
||||
bool draw_shadow = inst->draw_shadow && inst->plugin->shadow_mode;
|
||||
if (draw_shadow) {
|
||||
glActiveTexture(GL_TEXTURE0 + SHADOW_TEXTURE_UNIT);
|
||||
glBindTexture(GL_TEXTURE_2D, r->shadow.texture);
|
||||
glUniform1i(r->shadow_texture, SHADOW_TEXTURE_UNIT);
|
||||
}
|
||||
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
|
||||
|
||||
for (int i = 0; i < model->nr_meshes; i++) {
|
||||
struct mesh *mesh = &model->meshes[i];
|
||||
struct material *material = &model->materials[mesh->material];
|
||||
bool is_transparent = (material->is_transparent && !(mesh->flags & MESH_SPRITE)) || inst->alpha < 1.0f;
|
||||
if (phase != (is_transparent ? DRAW_TRANSPARENT : DRAW_OPAQUE))
|
||||
continue;
|
||||
|
||||
glUniform1i(r->fog_type, (inst->plugin->fog_mode && !(mesh->flags & MESH_NOLIGHTING))
|
||||
? inst->plugin->fog_type : 0);
|
||||
|
||||
GLboolean use_specular_map = GL_FALSE;
|
||||
if (inst->plugin->specular_mode && !(mesh->flags & MESH_NOLIGHTING)) {
|
||||
glUniform1f(r->specular_strength, material->specular_strength);
|
||||
glUniform1f(r->specular_shininess, material->specular_shininess);
|
||||
if (material->specular_map) {
|
||||
glActiveTexture(GL_TEXTURE0 + SPECULAR_TEXTURE_UNIT);
|
||||
glBindTexture(GL_TEXTURE_2D, material->specular_map);
|
||||
glUniform1i(r->specular_texture, SPECULAR_TEXTURE_UNIT);
|
||||
use_specular_map = GL_TRUE;
|
||||
}
|
||||
} else {
|
||||
glUniform1f(r->specular_strength, 0.0);
|
||||
glUniform1f(r->specular_shininess, 0.0);
|
||||
}
|
||||
glUniform1i(r->use_specular_map, use_specular_map);
|
||||
|
||||
glUniform1f(r->rim_exponent, material->rim_exponent);
|
||||
glUniform3fv(r->rim_color, 1, material->rim_color);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + COLOR_TEXTURE_UNIT);
|
||||
glBindTexture(GL_TEXTURE_2D, material->color_map);
|
||||
glUniform1i(r->texture, COLOR_TEXTURE_UNIT);
|
||||
|
||||
if (draw_shadow && !(mesh->flags & MESH_BOTH))
|
||||
glUniform1f(r->shadow_darkness, material->shadow_darkness);
|
||||
else
|
||||
glUniform1f(r->shadow_darkness, 0.0f);
|
||||
|
||||
if (mesh->flags & MESH_ENVMAP) {
|
||||
glUniform1i(r->diffuse_type, DIFFUSE_ENV_MAP);
|
||||
} else if (mesh->flags & MESH_NOLIGHTING) {
|
||||
glUniform1i(r->diffuse_type, DIFFUSE_EMISSIVE);
|
||||
} else if (material->light_map && inst->plugin->light_map_mode) {
|
||||
glUniform1i(r->diffuse_type, DIFFUSE_LIGHT_MAP);
|
||||
glActiveTexture(GL_TEXTURE0 + LIGHT_TEXTURE_UNIT);
|
||||
glBindTexture(GL_TEXTURE_2D, material->light_map);
|
||||
glUniform1i(r->light_texture, LIGHT_TEXTURE_UNIT);
|
||||
} else {
|
||||
glUniform1i(r->diffuse_type, DIFFUSE_NORMAL);
|
||||
}
|
||||
|
||||
if (material->normal_map && inst->draw_bump && inst->plugin->bump_mode) {
|
||||
glUniform1i(r->use_normal_map, GL_TRUE);
|
||||
glActiveTexture(GL_TEXTURE0 + NORMAL_TEXTURE_UNIT);
|
||||
glBindTexture(GL_TEXTURE_2D, material->normal_map);
|
||||
glUniform1i(r->normal_texture, NORMAL_TEXTURE_UNIT);
|
||||
} else {
|
||||
glUniform1i(r->use_normal_map, GL_FALSE);
|
||||
}
|
||||
|
||||
if (material->alpha_map) {
|
||||
glUniform1i(r->alpha_mode, ALPHA_MAP_BLEND);
|
||||
glActiveTexture(GL_TEXTURE0 + ALPHA_TEXTURE_UNIT);
|
||||
glBindTexture(GL_TEXTURE_2D, material->alpha_map);
|
||||
glUniform1i(r->alpha_texture, ALPHA_TEXTURE_UNIT);
|
||||
} else if (material->flags & MATERIAL_SPRITE || mesh->flags & MESH_SPRITE) {
|
||||
glUniform1i(r->alpha_mode, ALPHA_TEST);
|
||||
} else {
|
||||
glUniform1i(r->alpha_mode, ALPHA_BLEND);
|
||||
}
|
||||
|
||||
glBindVertexArray(mesh->vao);
|
||||
|
||||
if (mesh->nr_indices)
|
||||
glDrawElements(GL_TRIANGLES, mesh->nr_indices, GL_UNSIGNED_SHORT, NULL);
|
||||
else
|
||||
glDrawArrays(GL_TRIANGLES, 0, mesh->nr_vertices);
|
||||
|
||||
glBindVertexArray(0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void render_static_model(struct RE_instance *inst, struct RE_renderer *r, enum draw_phase phase)
|
||||
{
|
||||
if (!inst->draw)
|
||||
return;
|
||||
glUniform1i(r->has_bones, GL_FALSE);
|
||||
render_model(inst, r, phase);
|
||||
}
|
||||
|
||||
static void render_skinned_model(struct RE_instance *inst, struct RE_renderer *r, enum draw_phase phase)
|
||||
{
|
||||
if (!inst->draw)
|
||||
return;
|
||||
struct model *model = inst->model;
|
||||
if (!model)
|
||||
return;
|
||||
|
||||
if (model->nr_bones > 0 && inst->motion) {
|
||||
glUniform1i(r->has_bones, GL_TRUE);
|
||||
glUniformMatrix4fv(r->bone_matrices, model->nr_bones, GL_FALSE, inst->bone_transforms[0][0]);
|
||||
} else {
|
||||
glUniform1i(r->has_bones, GL_FALSE);
|
||||
}
|
||||
render_model(inst, r, phase);
|
||||
|
||||
if (inst->shadow_volume_instance && phase == DRAW_TRANSPARENT)
|
||||
render_static_model(inst->shadow_volume_instance, r, phase);
|
||||
}
|
||||
|
||||
static void render_billboard(struct RE_instance *inst, struct RE_renderer *r, mat4 view_mat, enum draw_phase phase)
|
||||
{
|
||||
if (!inst->draw)
|
||||
return;
|
||||
int cg_no = inst->motion->current_frame;
|
||||
struct billboard_texture *bt = ht_get_int(r->billboard_textures, cg_no, NULL);
|
||||
if (!bt)
|
||||
return;
|
||||
bool is_transparent = bt->has_alpha || inst->alpha < 1.0f;
|
||||
if (phase != (is_transparent ? DRAW_TRANSPARENT : DRAW_OPAQUE))
|
||||
return;
|
||||
|
||||
mat4 local_transform;
|
||||
glm_translate_make(local_transform, inst->pos);
|
||||
mat3 rot;
|
||||
glm_mat4_pick3t(view_mat, rot);
|
||||
glm_mat4_ins3(rot, local_transform);
|
||||
glm_scale(local_transform, inst->scale);
|
||||
// Billboard instances are bottomed at y=0.
|
||||
glm_translate_y(local_transform, 1.0);
|
||||
mat3 normal_transform;
|
||||
// This should be safe because billboards do not have non-uniform scaling.
|
||||
glm_mat4_pick3(local_transform, normal_transform);
|
||||
|
||||
vec3 ambient;
|
||||
glm_vec3_add(inst->plugin->global_ambient, inst->ambient, ambient);
|
||||
glUniform3fv(r->ambient, 1, ambient);
|
||||
|
||||
glUniformMatrix4fv(r->local_transform, 1, GL_FALSE, local_transform[0]);
|
||||
glUniformMatrix3fv(r->normal_transform, 1, GL_FALSE, normal_transform[0]);
|
||||
glUniform1i(r->has_bones, GL_FALSE);
|
||||
glUniform1f(r->alpha_mod, inst->alpha);
|
||||
glUniform1f(r->specular_strength, 0.0);
|
||||
glUniform1f(r->specular_shininess, 0.0);
|
||||
glUniform1i(r->use_specular_map, GL_FALSE);
|
||||
glUniform1f(r->rim_exponent, 0.0);
|
||||
glUniform1i(r->diffuse_type, DIFFUSE_NORMAL);
|
||||
glUniform1i(r->use_normal_map, GL_FALSE);
|
||||
glUniform1f(r->shadow_darkness, 0.0f);
|
||||
glUniform1i(r->alpha_mode, ALPHA_BLEND);
|
||||
glUniform1i(r->fog_type, inst->plugin->fog_mode ? inst->plugin->fog_type : 0);
|
||||
switch (inst->draw_type) {
|
||||
case RE_DRAW_TYPE_NORMAL:
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
|
||||
break;
|
||||
case RE_DRAW_TYPE_ADDITIVE:
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE);
|
||||
break;
|
||||
}
|
||||
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, bt->texture);
|
||||
glUniform1i(r->texture, 0);
|
||||
glBindVertexArray(r->billboard_vao);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
glBindVertexArray(0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glDepthFunc(GL_LESS);
|
||||
}
|
||||
|
||||
static void render_billboard_particles(struct RE_renderer *r, struct RE_instance *inst, struct particle_object *po, float frame)
|
||||
{
|
||||
struct pae_object *pae_obj = po->pae_obj;
|
||||
if (pae_obj->nr_textures == 0)
|
||||
return;
|
||||
|
||||
glUniform1i(r->diffuse_type, DIFFUSE_EMISSIVE);
|
||||
glUniform1i(r->fog_type, 0);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glUniform1i(r->texture, 0);
|
||||
glBindVertexArray(r->billboard_vao);
|
||||
|
||||
for (int index = 0; index < pae_obj->nr_particles; index++) {
|
||||
struct particle_instance *pi = &po->instances[index];
|
||||
if (frame < pi->begin_frame || pi->end_frame <= frame)
|
||||
continue;
|
||||
|
||||
int step = (int)((frame - pi->begin_frame) / pae_obj->texture_anime_frame);
|
||||
struct billboard_texture *bt = ht_get(inst->effect->pae->textures, pae_obj->textures[step % pae_obj->nr_textures], NULL);
|
||||
if (!bt)
|
||||
continue;
|
||||
glBindTexture(GL_TEXTURE_2D, bt->texture);
|
||||
|
||||
float alpha = pae_object_calc_alpha(pae_obj, pi, frame);
|
||||
if (pae_obj->blend_type == PARTICLE_BLEND_ADDITIVE && !bt->has_alpha)
|
||||
alpha *= alpha; // why...
|
||||
glUniform1f(r->alpha_mod, alpha);
|
||||
|
||||
mat4 local_transform;
|
||||
particle_object_calc_local_transform(inst, po, pi, frame, local_transform);
|
||||
glUniformMatrix4fv(r->local_transform, 1, GL_FALSE, local_transform[0]);
|
||||
glUniformMatrix3fv(r->normal_transform, 1, GL_FALSE, local_transform[0]);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
glBindVertexArray(0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
static void render_polygon_particles(struct RE_renderer *r, struct RE_instance *inst, struct particle_object *po, float frame)
|
||||
{
|
||||
struct pae_object *pae_obj = po->pae_obj;
|
||||
struct model *model = pae_obj->model;
|
||||
if (!model)
|
||||
return;
|
||||
|
||||
glUniform1i(r->diffuse_type, DIFFUSE_NORMAL);
|
||||
glUniform1i(r->fog_type, inst->plugin->fog_mode ? inst->plugin->fog_type : 0);
|
||||
|
||||
for (int index = 0; index < pae_obj->nr_particles; index++) {
|
||||
struct particle_instance *pi = &po->instances[index];
|
||||
if (frame < pi->begin_frame || pi->end_frame <= frame)
|
||||
continue;
|
||||
|
||||
float alpha = pae_object_calc_alpha(pae_obj, pi, frame);
|
||||
glUniform1f(r->alpha_mod, alpha);
|
||||
|
||||
mat4 local_transform;
|
||||
particle_object_calc_local_transform(inst, po, pi, frame, local_transform);
|
||||
glUniformMatrix4fv(r->local_transform, 1, GL_FALSE, local_transform[0]);
|
||||
glUniformMatrix3fv(r->normal_transform, 1, GL_FALSE, local_transform[0]);
|
||||
|
||||
for (int i = 0; i < model->nr_meshes; i++) {
|
||||
struct mesh *mesh = &model->meshes[i];
|
||||
struct material *material = &model->materials[mesh->material];
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, material->color_map);
|
||||
glUniform1i(r->texture, 0);
|
||||
|
||||
glBindVertexArray(mesh->vao);
|
||||
glDrawArrays(GL_TRIANGLES, 0, mesh->nr_vertices);
|
||||
|
||||
glBindVertexArray(0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void render_particle_effect(struct RE_instance *inst, struct RE_renderer *r, enum draw_phase phase)
|
||||
{
|
||||
// NOTE: inst->draw flag has no effect for particle effects.
|
||||
if (!inst->effect || phase == DRAW_OPAQUE)
|
||||
return;
|
||||
|
||||
vec3 ambient;
|
||||
glm_vec3_add(inst->plugin->global_ambient, inst->ambient, ambient);
|
||||
glUniform3fv(r->ambient, 1, ambient);
|
||||
|
||||
glUniform1i(r->has_bones, GL_FALSE);
|
||||
glUniform1f(r->specular_strength, 0.0);
|
||||
glUniform1f(r->specular_shininess, 0.0);
|
||||
glUniform1i(r->use_specular_map, GL_FALSE);
|
||||
glUniform1f(r->rim_exponent, 0.0);
|
||||
glUniform1i(r->use_normal_map, GL_FALSE);
|
||||
glUniform1f(r->shadow_darkness, 0.0f);
|
||||
glUniform1i(r->alpha_mode, ALPHA_BLEND);
|
||||
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
for (int i = 0; i < inst->effect->pae->nr_objects; i++) {
|
||||
struct particle_object *po = &inst->effect->objects[i];
|
||||
|
||||
switch (po->pae_obj->blend_type) {
|
||||
case PARTICLE_BLEND_NORMAL:
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
|
||||
break;
|
||||
case PARTICLE_BLEND_ADDITIVE:
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (po->pae_obj->type) {
|
||||
case PARTICLE_TYPE_BILLBOARD:
|
||||
render_billboard_particles(r, inst, po, inst->motion->current_frame);
|
||||
break;
|
||||
case PARTICLE_TYPE_POLYGON_OBJECT:
|
||||
render_polygon_particles(r, inst, po, inst->motion->current_frame);
|
||||
break;
|
||||
case PARTICLE_TYPE_SWORD_BLUR: // unused
|
||||
case PARTICLE_TYPE_CAMERA_QUAKE: // handled in particle_effect_update()
|
||||
break;
|
||||
}
|
||||
}
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
|
||||
static void render_instance(struct RE_instance *inst, struct RE_renderer *r, mat4 view_mat, enum draw_phase phase)
|
||||
{
|
||||
switch (inst->type) {
|
||||
case RE_ITYPE_STATIC:
|
||||
render_static_model(inst, r, phase);
|
||||
break;
|
||||
case RE_ITYPE_SKINNED:
|
||||
render_skinned_model(inst, r, phase);
|
||||
break;
|
||||
case RE_ITYPE_BILLBOARD:
|
||||
render_billboard(inst, r, view_mat, phase);
|
||||
break;
|
||||
case RE_ITYPE_PARTICLE_EFFECT:
|
||||
render_particle_effect(inst, r, phase);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool calc_shadow_light_transform(struct RE_plugin *plugin, mat4 dest)
|
||||
{
|
||||
// Compute a bounding sphere of shadow casters.
|
||||
vec4 bounding_sphere = {};
|
||||
for (int i = 0; i < plugin->nr_instances; i++) {
|
||||
struct RE_instance *inst = plugin->instances[i];
|
||||
if (!inst || !inst->draw || !inst->make_shadow || !inst->model)
|
||||
continue;
|
||||
if (bounding_sphere[3] > 0.0f)
|
||||
glm_sphere_merge(inst->bounding_sphere, bounding_sphere, inst->bounding_sphere);
|
||||
else
|
||||
glm_vec4_copy(inst->bounding_sphere, bounding_sphere);
|
||||
}
|
||||
|
||||
float radius = bounding_sphere[3] * 1.2f; // Add some padding.
|
||||
if (radius <= 0.0f)
|
||||
return false;
|
||||
|
||||
// Create a orthographic frustum that contains the bounding sphere.
|
||||
mat4 view_matrix;
|
||||
glm_look(bounding_sphere, plugin->shadow_map_light_dir, GLM_YUP, view_matrix);
|
||||
mat4 proj_matrix;
|
||||
glm_ortho(-radius, radius, -radius, radius, -radius, radius, proj_matrix);
|
||||
glm_mat4_mul(proj_matrix, view_matrix, dest);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void render_shadow_map(struct RE_plugin *plugin, mat4 light_space_transform)
|
||||
{
|
||||
if (!calc_shadow_light_transform(plugin, light_space_transform)) {
|
||||
// No shadow casters, but proceed anyway to clear the shadow texture.
|
||||
glm_mat4_identity(light_space_transform);
|
||||
}
|
||||
|
||||
struct RE_renderer *r = plugin->renderer;
|
||||
GLint orig_fbo, orig_viewport[4];
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &orig_fbo);
|
||||
glGetIntegerv(GL_VIEWPORT, orig_viewport);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, r->shadow.fbo);
|
||||
glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
glUseProgram(r->shadow.program);
|
||||
|
||||
glUniformMatrix4fv(r->shadow.view_transform, 1, GL_FALSE, light_space_transform[0]);
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
// Render the shadow casters.
|
||||
for (int i = 0; i < plugin->nr_instances; i++) {
|
||||
struct RE_instance *inst = plugin->instances[i];
|
||||
if (!inst || !inst->draw || !inst->make_shadow || !inst->model)
|
||||
continue;
|
||||
struct model *model = inst->model;
|
||||
if (model->nr_bones > 0) {
|
||||
glUniform1i(r->shadow.has_bones, GL_TRUE);
|
||||
glUniformMatrix4fv(r->shadow.bone_matrices, model->nr_bones, GL_FALSE, inst->bone_transforms[0][0]);
|
||||
} else {
|
||||
glUniform1i(r->shadow.has_bones, GL_FALSE);
|
||||
}
|
||||
glUniformMatrix4fv(r->shadow.world_transform, 1, GL_FALSE, inst->local_transform[0]);
|
||||
for (int j = 0; j < model->nr_meshes; j++) {
|
||||
struct mesh *mesh = &model->meshes[j];
|
||||
if (mesh->flags & MESH_NOMAKESHADOW)
|
||||
continue;
|
||||
glBindVertexArray(mesh->vao);
|
||||
glDrawArrays(GL_TRIANGLES, 0, mesh->nr_vertices);
|
||||
}
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, orig_fbo);
|
||||
glViewport(orig_viewport[0], orig_viewport[1], orig_viewport[2], orig_viewport[3]);
|
||||
}
|
||||
|
||||
static void render_back_cg(struct texture *dst, struct RE_back_cg *bcg, struct RE_renderer *r)
|
||||
{
|
||||
if (!bcg->show || !bcg->texture.handle)
|
||||
return;
|
||||
int sw = bcg->texture.w;
|
||||
int sh = bcg->texture.h;
|
||||
gfx_copy_stretch_blend_amap_alpha(dst, bcg->x, bcg->y, sw * bcg->mag, sh * bcg->mag, &bcg->texture, 0, 0, sw, sh, bcg->blend_rate * 255);
|
||||
}
|
||||
|
||||
static void setup_lights(struct RE_plugin *plugin)
|
||||
{
|
||||
struct RE_renderer *r = plugin->renderer;
|
||||
glUniform3fv(r->camera_pos, 1, plugin->camera.pos);
|
||||
int light_index = 0;
|
||||
for (int i = 0; i < plugin->nr_instances; i++) {
|
||||
struct RE_instance *inst = plugin->instances[i];
|
||||
if (!inst)
|
||||
continue;
|
||||
switch (inst->type) {
|
||||
case RE_ITYPE_DIRECTIONAL_LIGHT:
|
||||
if (light_index >= NR_DIR_LIGHTS) {
|
||||
WARNING("too many directional lights");
|
||||
break;
|
||||
}
|
||||
glUniform3fv(r->dir_lights[light_index].dir, 1, inst->vec);
|
||||
glUniform3fv(r->dir_lights[light_index].diffuse, 1, inst->diffuse);
|
||||
glUniform3fv(r->dir_lights[light_index].globe_diffuse, 1, inst->globe_diffuse);
|
||||
light_index++;
|
||||
break;
|
||||
case RE_ITYPE_SPECULAR_LIGHT:
|
||||
glUniform3fv(r->specular_light_dir, 1, inst->vec);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (; light_index < NR_DIR_LIGHTS; light_index++) {
|
||||
const vec3 zero = {0.0, 0.0, 0.0};
|
||||
glUniform3fv(r->dir_lights[light_index].dir, 1, zero);
|
||||
glUniform3fv(r->dir_lights[light_index].diffuse, 1, zero);
|
||||
glUniform3fv(r->dir_lights[light_index].globe_diffuse, 1, zero);
|
||||
}
|
||||
}
|
||||
|
||||
static int cmp_instances_by_z(const void *lhs, const void *rhs)
|
||||
{
|
||||
struct RE_instance *l = *(struct RE_instance **)lhs;
|
||||
struct RE_instance *r = *(struct RE_instance **)rhs;
|
||||
float lz = l ? l->z_from_camera : FLT_MAX;
|
||||
float rz = r ? r->z_from_camera : FLT_MAX;
|
||||
return (lz > rz) - (lz < rz); // ascending order.
|
||||
}
|
||||
|
||||
static struct RE_instance **sort_instances(struct RE_plugin *plugin, mat4 view_transform)
|
||||
{
|
||||
struct RE_instance **instances = xmalloc(plugin->nr_instances * sizeof(struct RE_instance *));
|
||||
memcpy(instances, plugin->instances, plugin->nr_instances * sizeof(struct RE_instance *));
|
||||
|
||||
for (int i = 0; i < plugin->nr_instances; i++) {
|
||||
struct RE_instance *inst = instances[i];
|
||||
if (!inst)
|
||||
continue;
|
||||
vec3 center;
|
||||
if (inst->model) {
|
||||
glm_aabb_center(inst->model->aabb, center);
|
||||
if (inst->local_transform_needs_update)
|
||||
RE_instance_update_local_transform(inst);
|
||||
glm_mat4_mulv3(inst->local_transform, center, 1.0, center);
|
||||
} else {
|
||||
glm_vec3_copy(inst->pos, center);
|
||||
}
|
||||
glm_mat4_mulv3(view_transform, center, 1.0f, center);
|
||||
inst->z_from_camera = center[2];
|
||||
}
|
||||
|
||||
qsort(instances, plugin->nr_instances, sizeof(struct RE_instance *), cmp_instances_by_z);
|
||||
return instances;
|
||||
}
|
||||
|
||||
void RE_render(struct sact_sprite *sp)
|
||||
{
|
||||
struct RE_plugin *plugin = (struct RE_plugin *)sp->plugin;
|
||||
struct RE_renderer *r = plugin->renderer;
|
||||
if (!r)
|
||||
return;
|
||||
sprite_dirty(sp);
|
||||
struct texture *texture = sprite_get_texture(sp);
|
||||
|
||||
mat4 shadow_transform;
|
||||
if (plugin->shadow_mode)
|
||||
render_shadow_map(plugin, shadow_transform);
|
||||
else
|
||||
glm_mat4_identity(shadow_transform);
|
||||
|
||||
GLuint fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, texture, 0, 0, texture->w, texture->h);
|
||||
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, r->depth_buffer);
|
||||
if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
ERROR("Incomplete framebuffer");
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// Draw background CGs.
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
for (int i = 0; i < RE_NR_BACK_CGS; i++)
|
||||
render_back_cg(texture, &plugin->back_cg[i], r);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
mat4 view_transform;
|
||||
RE_calc_view_matrix(&plugin->camera, GLM_YUP, view_transform);
|
||||
|
||||
// Tweak the projection transform so that the rendering result is vertically
|
||||
// flipped. If we render the scene normally, the resulting image will be
|
||||
// bottom-up (the first pixel is at the bottom-left), but we want a top-down
|
||||
// image (the first pixel is at the top-left). This changes the coordinate
|
||||
// system from right-handed to left-handed, we also need to reverse the
|
||||
// winding order.
|
||||
plugin->proj_transform[1][1] *= -1;
|
||||
glFrontFace(GL_CW);
|
||||
|
||||
glUseProgram(r->program);
|
||||
glUniformMatrix4fv(r->view_transform, 1, GL_FALSE, view_transform[0]);
|
||||
glUniformMatrix4fv(r->proj_transform, 1, GL_FALSE, plugin->proj_transform[0]);
|
||||
glUniformMatrix4fv(r->shadow_transform, 1, GL_FALSE, shadow_transform[0]);
|
||||
glUniform1f(r->shadow_bias, plugin->shadow_bias);
|
||||
if (plugin->fog_mode) {
|
||||
switch (plugin->fog_type) {
|
||||
case RE_FOG_NONE:
|
||||
break;
|
||||
case RE_FOG_LINEAR:
|
||||
glUniform1f(r->fog_near, plugin->fog_near);
|
||||
glUniform1f(r->fog_far, plugin->fog_far);
|
||||
glUniform3fv(r->fog_color, 1, plugin->fog_color);
|
||||
break;
|
||||
case RE_FOG_LIGHT_SCATTERING:
|
||||
glUniform4f(r->ls_params, plugin->ls_beta_r, plugin->ls_beta_m, plugin->ls_g, plugin->ls_distance);
|
||||
glUniform3fv(r->ls_light_dir, 1, plugin->ls_light_dir);
|
||||
glUniform3fv(r->ls_light_color, 1, plugin->ls_light_color);
|
||||
glUniform3fv(r->ls_sun_color, 1, plugin->ls_sun_color);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
setup_lights(plugin);
|
||||
|
||||
// Sort instances by z-order.
|
||||
struct RE_instance **sorted_instances = sort_instances(plugin, view_transform);
|
||||
|
||||
// Render opaque instances, from nearest to farthest.
|
||||
for (int i = plugin->nr_instances - 1; i >= 0; i--) {
|
||||
struct RE_instance *inst = sorted_instances[i];
|
||||
if (!inst)
|
||||
continue;
|
||||
render_instance(inst, r, view_transform, DRAW_OPAQUE);
|
||||
}
|
||||
// Render transparent instances, from farthest to nearest.
|
||||
for (int i = 0; i < plugin->nr_instances; i++) {
|
||||
struct RE_instance *inst = sorted_instances[i];
|
||||
if (!inst)
|
||||
continue;
|
||||
render_instance(inst, r, view_transform, DRAW_TRANSPARENT);
|
||||
}
|
||||
|
||||
free(sorted_instances);
|
||||
|
||||
plugin->proj_transform[1][1] *= -1;
|
||||
glFrontFace(GL_CCW);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glUseProgram(0);
|
||||
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
|
||||
gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
struct height_detector {
|
||||
GLushort *buf;
|
||||
vec3 aabb[2];
|
||||
};
|
||||
|
||||
struct height_detector *RE_renderer_create_height_detector(struct RE_renderer *r, struct model *model)
|
||||
{
|
||||
struct height_detector *hd = xcalloc(1, sizeof(struct height_detector));
|
||||
vec3 aabb[2];
|
||||
memcpy(aabb, model->aabb, sizeof(aabb));
|
||||
// Add some Y padding.
|
||||
aabb[0][1] -= 1.0;
|
||||
aabb[1][1] += 1.0;
|
||||
memcpy(hd->aabb, aabb, sizeof(aabb));
|
||||
|
||||
// Set up an orthographic projection matrix looking down the instance from
|
||||
// right above.
|
||||
mat4 view_matrix, proj_matrix;
|
||||
glm_mat4_identity(view_matrix);
|
||||
glm_rotate_x(view_matrix, glm_rad(90.0), view_matrix);
|
||||
glm_aabb_transform(aabb, view_matrix, aabb);
|
||||
glm_ortho_aabb(aabb, proj_matrix);
|
||||
|
||||
GLint orig_fbo, orig_viewport[4];
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &orig_fbo);
|
||||
glGetIntegerv(GL_VIEWPORT, orig_viewport);
|
||||
|
||||
// Since OpenGL ES does not allow reading from depth textures, create a
|
||||
// color texture and render depth values into it.
|
||||
GLuint color_texture;
|
||||
glGenTextures(1, &color_texture);
|
||||
glBindTexture(GL_TEXTURE_2D, color_texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_R16UI, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_RED_INTEGER, GL_UNSIGNED_SHORT, NULL);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, r->shadow.fbo);
|
||||
GLenum draw_buffers[1] = { GL_COLOR_ATTACHMENT0 };
|
||||
glDrawBuffers(1, draw_buffers);
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_texture, 0);
|
||||
|
||||
glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
glUseProgram(r->shadow.program);
|
||||
glUniformMatrix4fv(r->shadow.world_transform, 1, GL_FALSE, view_matrix[0]);
|
||||
glUniformMatrix4fv(r->shadow.view_transform, 1, GL_FALSE, proj_matrix[0]);
|
||||
glUniform1i(r->shadow.has_bones, GL_FALSE);
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
// Render the model.
|
||||
for (int i = 0; i < model->nr_meshes; i++) {
|
||||
struct mesh *mesh = &model->meshes[i];
|
||||
glBindVertexArray(mesh->vao);
|
||||
glDrawArrays(GL_TRIANGLES, 0, mesh->nr_vertices);
|
||||
}
|
||||
glBindVertexArray(0);
|
||||
glFinish();
|
||||
|
||||
hd->buf = xmalloc(SHADOW_WIDTH * SHADOW_HEIGHT * sizeof(GLushort));
|
||||
glReadPixels(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT, GL_RED_INTEGER, GL_UNSIGNED_SHORT, hd->buf);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDrawBuffers(0, NULL);
|
||||
glReadBuffer(GL_NONE);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, orig_fbo);
|
||||
glViewport(orig_viewport[0], orig_viewport[1], orig_viewport[2], orig_viewport[3]);
|
||||
|
||||
glDeleteTextures(1, &color_texture);
|
||||
return hd;
|
||||
}
|
||||
|
||||
void RE_renderer_free_height_detector(struct height_detector *hd)
|
||||
{
|
||||
free(hd->buf);
|
||||
free(hd);
|
||||
}
|
||||
|
||||
bool RE_renderer_detect_height(struct height_detector *hd, float x, float z, float *y_out)
|
||||
{
|
||||
float minx = hd->aabb[0][0];
|
||||
float maxx = hd->aabb[1][0];
|
||||
float miny = hd->aabb[0][1];
|
||||
float maxy = hd->aabb[1][1];
|
||||
float minz = hd->aabb[0][2];
|
||||
float maxz = hd->aabb[1][2];
|
||||
if (x < minx || x > maxx || z < minz || z > maxz)
|
||||
return false;
|
||||
int px = glm_percent(minx, maxx, x) * SHADOW_WIDTH;
|
||||
int pz = glm_percent(maxz, minz, z) * SHADOW_HEIGHT;
|
||||
GLushort val = hd->buf[pz * SHADOW_WIDTH + px];
|
||||
if (!val)
|
||||
return false;
|
||||
*y_out = glm_lerp(maxy, miny, val / 65535.0f);
|
||||
return true;
|
||||
}
|
||||
+314
-146
@@ -18,7 +18,6 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <libgen.h>
|
||||
#include <dirent.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
@@ -29,10 +28,12 @@
|
||||
#include "system4/cg.h"
|
||||
#include "system4/file.h"
|
||||
#include "system4/hashtable.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/utfsjis.h"
|
||||
|
||||
#include "xsystem4.h"
|
||||
#include "asset_manager.h"
|
||||
#include "gfx/font.h"
|
||||
|
||||
enum archive_type {
|
||||
AR_TYPE_ALD,
|
||||
@@ -54,18 +55,301 @@ const char *asset_strtype(enum asset_type type)
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
static struct archive *archives[ASSET_TYPE_MAX] = {0};
|
||||
#define MAX_ARCHIVES 8
|
||||
|
||||
struct asset_manager {
|
||||
bool (*load_archive)(struct asset_manager *manager, const char *name);
|
||||
bool (*exists_by_id)(struct asset_manager *manager, int id);
|
||||
bool (*exists_by_name)(struct asset_manager *manager, const char *name, int *id_out);
|
||||
struct archive_data *(*get_by_id)(struct asset_manager *manager, int id);
|
||||
struct archive_data *(*get_by_name)(struct asset_manager *manager, const char *name, int *id_out);
|
||||
};
|
||||
|
||||
// XXX: also used for AFAv1 in Daiteikoku, Shaman's Sanctuary
|
||||
struct asset_manager_ald {
|
||||
struct asset_manager manager;
|
||||
struct archive *archive;
|
||||
};
|
||||
|
||||
struct asset_manager_afa {
|
||||
struct asset_manager manager;
|
||||
struct afa_archive *archives[MAX_ARCHIVES];
|
||||
struct hash_table *index;
|
||||
};
|
||||
|
||||
static struct asset_manager *assets[ASSET_TYPE_MAX] = {0};
|
||||
|
||||
bool asset_manager_load_archive(enum asset_type type, const char *archive_name)
|
||||
{
|
||||
if (!assets[type])
|
||||
return false;
|
||||
if (!assets[type]->load_archive)
|
||||
ERROR("load_archive not supported on this archive type");
|
||||
return assets[type]->load_archive(assets[type], archive_name);
|
||||
}
|
||||
|
||||
bool asset_exists(enum asset_type type, int id)
|
||||
{
|
||||
if (!assets[type])
|
||||
return false;
|
||||
return assets[type]->exists_by_id(assets[type], id);
|
||||
}
|
||||
|
||||
bool asset_exists_by_name(enum asset_type type, const char *name, int *id_out)
|
||||
{
|
||||
if (!assets[type])
|
||||
return false;
|
||||
if (!assets[type]->exists_by_name)
|
||||
ERROR("exists_by_name not supported on this archive type");
|
||||
return assets[type]->exists_by_name(assets[type], name, id_out);
|
||||
}
|
||||
|
||||
struct archive_data *asset_get(enum asset_type type, int id)
|
||||
{
|
||||
if (!assets[type])
|
||||
return NULL;
|
||||
return assets[type]->get_by_id(assets[type], id);
|
||||
}
|
||||
|
||||
struct archive_data *asset_get_by_name(enum asset_type type, const char *name, int *id_out)
|
||||
{
|
||||
if (!assets[type])
|
||||
return NULL;
|
||||
if (!assets[type]->get_by_name)
|
||||
ERROR("get_by_name not supported on this archive type");
|
||||
return assets[type]->get_by_name(assets[type], name, id_out);
|
||||
}
|
||||
|
||||
struct cg *asset_cg_load(int id)
|
||||
{
|
||||
struct archive_data *data = asset_get(ASSET_CG, id);
|
||||
if (!data)
|
||||
return NULL;
|
||||
struct cg *cg = cg_load_data(data);
|
||||
archive_free_data(data);
|
||||
return cg;
|
||||
}
|
||||
|
||||
struct cg *asset_cg_load_by_name(const char *name, int *id_out)
|
||||
{
|
||||
struct archive_data *data = asset_get_by_name(ASSET_CG, name, id_out);
|
||||
if (!data)
|
||||
return NULL;
|
||||
struct cg *cg = cg_load_data(data);
|
||||
archive_free_data(data);
|
||||
return cg;
|
||||
}
|
||||
|
||||
bool asset_cg_get_metrics(int id, struct cg_metrics *metrics)
|
||||
{
|
||||
struct archive_data *data = asset_get(ASSET_CG, id);
|
||||
if (!data)
|
||||
return false;
|
||||
bool r = cg_get_metrics_data(data, metrics);
|
||||
archive_free_data(data);
|
||||
return r;
|
||||
}
|
||||
|
||||
bool asset_cg_get_metrics_by_name(const char *name, struct cg_metrics *metrics)
|
||||
{
|
||||
struct archive_data *data = asset_get_by_name(ASSET_CG, name, NULL);
|
||||
if (!data)
|
||||
return false;
|
||||
bool r = cg_get_metrics_data(data, metrics);
|
||||
archive_free_data(data);
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool ald_exists_by_id(struct asset_manager *_manager, int id)
|
||||
{
|
||||
struct asset_manager_ald *manager = (struct asset_manager_ald*)_manager;
|
||||
return archive_exists(manager->archive, id - 1);
|
||||
}
|
||||
|
||||
static struct archive_data *ald_get_by_id(struct asset_manager *_manager, int id)
|
||||
{
|
||||
struct asset_manager_ald *manager = (struct asset_manager_ald*)_manager;
|
||||
return archive_get(manager->archive, id - 1);
|
||||
}
|
||||
|
||||
static bool afa_load_archive(struct asset_manager *_manager, const char *name)
|
||||
{
|
||||
struct asset_manager_afa *manager = (struct asset_manager_afa*)_manager;
|
||||
if (manager->archives[MAX_ARCHIVES-1])
|
||||
ERROR("Archive limit exceeded");
|
||||
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, PATH_MAX, "%s.afa", name);
|
||||
|
||||
int error;
|
||||
struct afa_archive *ar = afa_open(path, MMAP_IF_64BIT, &error);
|
||||
if (!ar) {
|
||||
snprintf(path, PATH_MAX, "%s.AFA", name);
|
||||
ar = afa_open(path, MMAP_IF_64BIT, &error);
|
||||
if (!ar) {
|
||||
WARNING("Failed to open archive: %s", display_utf0(path));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = MAX_ARCHIVES - 1; i > 0; i--) {
|
||||
manager->archives[i] = manager->archives[i-1];
|
||||
}
|
||||
manager->archives[0] = ar;
|
||||
|
||||
// index needs to be rebuilt
|
||||
if (manager->index) {
|
||||
ht_free(manager->index);
|
||||
manager->index = NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _afa_get_by_index(struct asset_manager *_manager, int id,
|
||||
struct afa_archive **ar_out, int *no_out)
|
||||
{
|
||||
struct asset_manager_afa *manager = (struct asset_manager_afa*)_manager;
|
||||
uint32_t no = id - 1;
|
||||
for (int i = 0; i < MAX_ARCHIVES && manager->archives[i]; i++) {
|
||||
if (no < manager->archives[i]->nr_files) {
|
||||
*ar_out = manager->archives[i];
|
||||
*no_out = no;
|
||||
return true;
|
||||
}
|
||||
no -= manager->archives[i]->nr_files;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// XXX: id-indexed afa
|
||||
static bool afa_exists_by_id(struct asset_manager *_manager, int id)
|
||||
{
|
||||
struct asset_manager_afa *manager = (struct asset_manager_afa*)_manager;
|
||||
for (int i = 0; i < MAX_ARCHIVES && manager->archives[i]; i++) {
|
||||
if (archive_exists(&manager->archives[i]->ar, id - 1))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// XXX: string-indexed afa
|
||||
static bool afa_exists_by_index(struct asset_manager *manager, int index)
|
||||
{
|
||||
int no;
|
||||
struct afa_archive *ar;
|
||||
return _afa_get_by_index(manager, index, &ar, &no);
|
||||
}
|
||||
|
||||
// XXX: id-indexed afa
|
||||
static struct archive_data *afa_get_by_id(struct asset_manager *_manager, int id)
|
||||
{
|
||||
struct asset_manager_afa *manager = (struct asset_manager_afa*)_manager;
|
||||
for (int i = 0; i < MAX_ARCHIVES && manager->archives[i]; i++) {
|
||||
struct archive_data *data = archive_get(&manager->archives[i]->ar, id - 1);
|
||||
if (data)
|
||||
return data;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// XXX: string-indexed afa
|
||||
static struct archive_data *afa_get_by_index(struct asset_manager *manager, int index)
|
||||
{
|
||||
int no;
|
||||
struct afa_archive *ar;
|
||||
if (!_afa_get_by_index(manager, index, &ar, &no))
|
||||
return NULL;
|
||||
return archive_get(&ar->ar, no);
|
||||
}
|
||||
|
||||
static void afa_index_archive(struct hash_table *index, struct afa_archive *ar, unsigned base)
|
||||
{
|
||||
for (unsigned i = 0; i < ar->nr_files; i++) {
|
||||
// normalize case and remove file extension from file name
|
||||
char *name = strdup(ar->files[i].name->text);
|
||||
char *dot = strrchr(name, '.');
|
||||
if (dot)
|
||||
*dot = '\0';
|
||||
sjis_toupper(name);
|
||||
// add file to index
|
||||
ht_put(index, name, (void*)((uintptr_t)base + i + 1));
|
||||
free(name);
|
||||
}
|
||||
}
|
||||
|
||||
static bool _afa_get_by_name(struct asset_manager *_manager, const char *_name,
|
||||
struct afa_archive **ar_out, int *no_out, int *id_out)
|
||||
{
|
||||
struct asset_manager_afa *manager = (struct asset_manager_afa*)_manager;
|
||||
// initialize index lazily
|
||||
if (!manager->index) {
|
||||
manager->index = ht_create(4096);
|
||||
unsigned base = 0;
|
||||
for (unsigned i = 0; i < MAX_ARCHIVES && manager->archives[i]; i++) {
|
||||
afa_index_archive(manager->index, manager->archives[i], base);
|
||||
base += manager->archives[i]->nr_files;
|
||||
}
|
||||
}
|
||||
|
||||
// normalize name and get id from index
|
||||
char *name = strdup(_name);
|
||||
sjis_toupper(name);
|
||||
char *dot = strrchr(name, '.');
|
||||
if (dot)
|
||||
*dot = '\0';
|
||||
uintptr_t id = (uintptr_t)ht_get(manager->index, name, NULL);
|
||||
free(name);
|
||||
|
||||
if (!id)
|
||||
return false;
|
||||
|
||||
if (id_out)
|
||||
*id_out = id;
|
||||
return _afa_get_by_index(_manager, id, ar_out, no_out);
|
||||
}
|
||||
|
||||
static bool afa_exists_by_name(struct asset_manager *manager, const char *name, int *id_out)
|
||||
{
|
||||
int no;
|
||||
struct afa_archive *ar;
|
||||
return _afa_get_by_name(manager, name, &ar, &no, id_out);
|
||||
}
|
||||
|
||||
static struct archive_data *afa_get_by_name(struct asset_manager *manager, const char *name, int *id_out)
|
||||
{
|
||||
int no;
|
||||
struct afa_archive *ar;
|
||||
if (!_afa_get_by_name(manager, name, &ar, &no, id_out))
|
||||
return NULL;
|
||||
return archive_get(&ar->ar, no);
|
||||
}
|
||||
|
||||
static void _ald_init(enum asset_type type, struct archive *ar)
|
||||
{
|
||||
struct asset_manager_ald *manager = xcalloc(1, sizeof(struct asset_manager_ald));
|
||||
manager->manager.exists_by_id = ald_exists_by_id;
|
||||
manager->manager.get_by_id = ald_get_by_id;
|
||||
manager->manager.load_archive = NULL;
|
||||
manager->manager.exists_by_name = NULL;
|
||||
manager->manager.get_by_name = NULL;
|
||||
manager->archive = ar;
|
||||
assets[type] = &manager->manager;
|
||||
}
|
||||
|
||||
static void ald_init(enum asset_type type, char **files, int count)
|
||||
{
|
||||
if (count < 1)
|
||||
return;
|
||||
if (archives[type])
|
||||
if (assets[type])
|
||||
WARNING("Multiple asset archives for type %s", asset_strtype(type));
|
||||
int error = ARCHIVE_SUCCESS;
|
||||
archives[type] = ald_open(files, count, ARCHIVE_MMAP, &error);
|
||||
if (error)
|
||||
|
||||
int error;
|
||||
struct archive *ar = ald_open(files, count, MMAP_IF_64BIT, &error);
|
||||
if (!ar)
|
||||
ERROR("Failed to open ALD file: %s", archive_strerror(error));
|
||||
|
||||
_ald_init(type, ar);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
free(files[i]);
|
||||
}
|
||||
@@ -75,13 +359,30 @@ static void afa_init(enum asset_type type, char *file)
|
||||
{
|
||||
if (!file)
|
||||
return;
|
||||
if (archives[type])
|
||||
if (assets[type])
|
||||
WARNING("Multiple asset archives for type %s", asset_strtype(type));
|
||||
int error = ARCHIVE_SUCCESS;
|
||||
archives[type] = (struct archive*)afa_open(file, ARCHIVE_MMAP, &error);
|
||||
if (error != ARCHIVE_SUCCESS) {
|
||||
|
||||
int error;
|
||||
struct afa_archive *ar = afa_open(file, MMAP_IF_64BIT, &error);
|
||||
if (!ar)
|
||||
ERROR("Failed to open AFA file: %s", archive_strerror(error));
|
||||
|
||||
struct asset_manager_afa *manager = xcalloc(1, sizeof(struct asset_manager_afa));
|
||||
manager->manager.load_archive = afa_load_archive;
|
||||
if (id_indexed_afa) {
|
||||
manager->manager.exists_by_id = afa_exists_by_id;
|
||||
manager->manager.get_by_id = afa_get_by_id;
|
||||
manager->manager.exists_by_name = NULL;
|
||||
manager->manager.get_by_name = NULL;
|
||||
} else {
|
||||
manager->manager.exists_by_name = afa_exists_by_name;
|
||||
manager->manager.get_by_name = afa_get_by_name;
|
||||
manager->manager.exists_by_id = afa_exists_by_index;
|
||||
manager->manager.get_by_id = afa_get_by_index;
|
||||
}
|
||||
manager->archives[0] = ar;
|
||||
assets[type] = &manager->manager;
|
||||
|
||||
free(file);
|
||||
}
|
||||
|
||||
@@ -183,6 +484,9 @@ void asset_manager_init(void)
|
||||
} else if (!strcmp(type, "Pact.afa")) {
|
||||
afa_filenames[ASSET_PACT] = path_join(config.game_dir, d_name);
|
||||
}
|
||||
} else if (!strcasecmp(ext, "fnl")) {
|
||||
if (!config.fnl_path)
|
||||
config.fnl_path = path_join(config.game_dir, d_name);
|
||||
}
|
||||
loop_next:
|
||||
free(d_name);
|
||||
@@ -208,139 +512,3 @@ void asset_manager_init(void)
|
||||
afa_init(ASSET_PACT, afa_filenames[ASSET_PACT]);
|
||||
afa_init(ASSET_DATA, afa_filenames[ASSET_DATA]);
|
||||
}
|
||||
|
||||
bool asset_exists(enum asset_type type, int no)
|
||||
{
|
||||
return archives[type] && archive_exists(archives[type], no);
|
||||
}
|
||||
|
||||
struct archive_data *asset_get(enum asset_type type, int no)
|
||||
{
|
||||
if (!archives[type])
|
||||
return NULL;
|
||||
return archive_get(archives[type], no);
|
||||
}
|
||||
|
||||
static struct hash_table *cg_name_index = NULL;
|
||||
static struct hash_table *cg_id_index = NULL;
|
||||
|
||||
// Convert the last sequence of digits in a string to an integer.
|
||||
static int cg_name_to_int(const char *name)
|
||||
{
|
||||
size_t len = strlen(name);
|
||||
int i;
|
||||
for (i = len - 1; i >= 0 && !isdigit(name[i]); i--);
|
||||
if (i < 0)
|
||||
return -1;
|
||||
assert(isdigit(name[i]));
|
||||
for (; i > 0 && isdigit(name[i-1]); i--);
|
||||
assert(isdigit(name[i]));
|
||||
|
||||
if (!(i = atoi(name+i)))
|
||||
return -1;
|
||||
return i;
|
||||
}
|
||||
|
||||
static void add_cg_to_index(struct archive_data *data, possibly_unused void *_)
|
||||
{
|
||||
int logical_no = cg_name_to_int(data->name);
|
||||
if (logical_no < 0) {
|
||||
WARNING("Can't determine logical index for CG: %s", display_sjis0(data->name));
|
||||
return;
|
||||
}
|
||||
|
||||
struct ht_slot *slot = ht_put_int(cg_id_index, logical_no, NULL);
|
||||
if (slot->value)
|
||||
ERROR("Duplicate CG numbers");
|
||||
slot->value = (void*)(uintptr_t)(data->no + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: Starting in Shaman's Sanctuary, .afa files are used for CGs but the
|
||||
* library APIs still use integers rather than names to reference them.
|
||||
* Thus it is necessary to parse file names and create an index.
|
||||
*/
|
||||
void asset_cg_index_init(void)
|
||||
{
|
||||
if (!archives[ASSET_CG])
|
||||
return;
|
||||
|
||||
cg_id_index = ht_create(4096);
|
||||
archive_for_each(archives[ASSET_CG], add_cg_to_index, NULL);
|
||||
}
|
||||
|
||||
static void add_cg_name_to_index(struct archive_data *data, possibly_unused void *_)
|
||||
{
|
||||
// copy name and strip file extension
|
||||
char *name = strdup(data->name);
|
||||
char *dot = strrchr(name, '.');
|
||||
if (dot)
|
||||
*dot = '\0';
|
||||
|
||||
struct ht_slot *slot = ht_put(cg_name_index, name, NULL);
|
||||
if (slot->value)
|
||||
ERROR("Duplicate CG names");
|
||||
slot->value = (void*)(uintptr_t)(data->no + 1);
|
||||
|
||||
free(name);
|
||||
}
|
||||
|
||||
static void init_cg_name_index(void)
|
||||
{
|
||||
if (!archives[ASSET_CG])
|
||||
return;
|
||||
|
||||
cg_name_index = ht_create(4096);
|
||||
archive_for_each(archives[ASSET_CG], add_cg_name_to_index, NULL);
|
||||
}
|
||||
|
||||
int asset_cg_name_to_index(const char *name)
|
||||
{
|
||||
if (!cg_name_index)
|
||||
init_cg_name_index();
|
||||
return (uintptr_t)ht_get(cg_name_index, name, NULL);
|
||||
}
|
||||
|
||||
static int cg_translate_index(int no)
|
||||
{
|
||||
if (!cg_id_index)
|
||||
return no;
|
||||
return (uintptr_t)ht_get_int(cg_id_index, no, NULL);
|
||||
}
|
||||
|
||||
struct cg *asset_cg_load(int no)
|
||||
{
|
||||
if (!archives[ASSET_CG])
|
||||
return NULL;
|
||||
if (!(no = cg_translate_index(no)))
|
||||
return NULL;
|
||||
return cg_load(archives[ASSET_CG], no - 1);
|
||||
}
|
||||
|
||||
struct cg *asset_cg_load_by_name(const char *name, int *no)
|
||||
{
|
||||
if (!archives[ASSET_CG])
|
||||
return NULL;
|
||||
if (!(*no = asset_cg_name_to_index(name)))
|
||||
return NULL;
|
||||
return cg_load(archives[ASSET_CG], *no - 1);
|
||||
}
|
||||
|
||||
bool asset_cg_exists(int no)
|
||||
{
|
||||
if (!archives[ASSET_CG])
|
||||
return false;
|
||||
if (!(no = cg_translate_index(no)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asset_cg_get_metrics(int no, struct cg_metrics *metrics)
|
||||
{
|
||||
if (!archives[ASSET_CG])
|
||||
return false;
|
||||
if (!(no = cg_translate_index(no)))
|
||||
return NULL;
|
||||
return cg_get_metrics(archives[ASSET_CG], no - 1, metrics);
|
||||
}
|
||||
|
||||
|
||||
+13
-2
@@ -39,6 +39,10 @@ static int anonymous_channels[NR_ANONYMOUS_CHANNELS];
|
||||
|
||||
void audio_init(void)
|
||||
{
|
||||
static bool audio_initialized = false;
|
||||
if (audio_initialized)
|
||||
return;
|
||||
|
||||
id_pool_init(&wav);
|
||||
id_pool_init(&bgm);
|
||||
|
||||
@@ -47,6 +51,7 @@ void audio_init(void)
|
||||
}
|
||||
|
||||
mixer_init();
|
||||
audio_initialized = true;
|
||||
}
|
||||
|
||||
void audio_update(void)
|
||||
@@ -80,8 +85,8 @@ bool audio_play_sound(int sound_no)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool wav_exists(int no) { return asset_exists(ASSET_SOUND, no-1); }
|
||||
bool bgm_exists(int no) { return asset_exists(ASSET_BGM, no-1); }
|
||||
bool wav_exists(int no) { return asset_exists(ASSET_SOUND, no); }
|
||||
bool bgm_exists(int no) { return asset_exists(ASSET_BGM, no); }
|
||||
|
||||
static int audio_prepare(struct id_pool *pool, int id, enum asset_type type, int no)
|
||||
{
|
||||
@@ -344,3 +349,9 @@ static int audio_prepare_from_archive_data(struct id_pool *pool, int id, struct
|
||||
int wav_prepare_from_archive_data(int id, struct archive_data *dfile) {
|
||||
return audio_prepare_from_archive_data(&wav, id, dfile);
|
||||
}
|
||||
|
||||
int wav_get_group_num_from_data_num(int no)
|
||||
{
|
||||
struct wai *wai = wai_get(no);
|
||||
return wai ? wai->channel : 0;
|
||||
}
|
||||
|
||||
+99
-38
@@ -29,6 +29,7 @@
|
||||
#include "xsystem4.h"
|
||||
|
||||
#define clamp(min_value, max_value, value) min(max_value, max(min_value, value))
|
||||
#define muldiv(x, y, denom) ((int64_t)(x) * (int64_t)(y) / (int64_t)(denom))
|
||||
|
||||
/*
|
||||
* The actual mixer implementation is contained in this header.
|
||||
@@ -44,7 +45,8 @@ struct fade {
|
||||
atomic_bool fading;
|
||||
bool stop;
|
||||
uint_least32_t start_pos;
|
||||
uint_least32_t end_pos;
|
||||
uint_least32_t frames;
|
||||
uint_least32_t elapsed;
|
||||
float start_volume;
|
||||
float end_volume;
|
||||
};
|
||||
@@ -67,8 +69,8 @@ struct channel {
|
||||
|
||||
// main thread read-only
|
||||
atomic_uint_least32_t frame;
|
||||
atomic_uint volume;
|
||||
|
||||
atomic_uint volume;
|
||||
atomic_bool swapped;
|
||||
uint_least32_t loop_start;
|
||||
uint_least32_t loop_end;
|
||||
@@ -143,19 +145,19 @@ static bool cb_loop(struct channel *ch)
|
||||
* Callback to refill the stream's audio data.
|
||||
* Called from sys_mixer_mix_audio.
|
||||
*/
|
||||
static int cb_read_frames(struct channel *ch, float *out, sf_count_t frame_count)
|
||||
static int cb_read_frames(struct channel *ch, float *out, sf_count_t frame_count, uint_least32_t *num_read)
|
||||
{
|
||||
sf_count_t num_read = 0;
|
||||
*num_read = 0;
|
||||
|
||||
// handle case where chunk crosses loop point (seamless)
|
||||
// NOTE: it's assumed that the length of the loop is greater than the chunk length
|
||||
if (ch->frame + frame_count >= ch->loop_end) {
|
||||
// read frames up to loop_end
|
||||
num_read = sf_readf_float(ch->file, out, ch->loop_end - ch->frame);
|
||||
*num_read = sf_readf_float(ch->file, out, ch->loop_end - ch->frame);
|
||||
// adjust parameters for later
|
||||
ch->frame += num_read;
|
||||
out += num_read;
|
||||
frame_count -= num_read;
|
||||
ch->frame += *num_read;
|
||||
out += *num_read;
|
||||
frame_count -= *num_read;
|
||||
// seek to loop_start
|
||||
if (!cb_loop(ch))
|
||||
return STS_STREAM_COMPLETE;
|
||||
@@ -167,10 +169,10 @@ static int cb_read_frames(struct channel *ch, float *out, sf_count_t frame_count
|
||||
|
||||
// read remaining data
|
||||
sf_count_t n = sf_readf_float(ch->file, out, frame_count);
|
||||
num_read += n;
|
||||
*num_read += n;
|
||||
ch->frame += n;
|
||||
out += num_read;
|
||||
frame_count -= num_read;
|
||||
out += *num_read;
|
||||
frame_count -= *num_read;
|
||||
|
||||
// XXX: This *shouldn't* be necessary, but sometimes libsndfile seems to stop reading
|
||||
// just before the end of file (i.e. ch->frame + frame_count is < ch->info.frames,
|
||||
@@ -179,7 +181,7 @@ static int cb_read_frames(struct channel *ch, float *out, sf_count_t frame_count
|
||||
if (frame_count > 0) {
|
||||
if (!cb_loop(ch))
|
||||
return STS_STREAM_COMPLETE;
|
||||
num_read += sf_readf_float(ch->file, out, frame_count);
|
||||
*num_read += sf_readf_float(ch->file, out, frame_count);
|
||||
}
|
||||
|
||||
return STS_STREAM_CONTINUE;
|
||||
@@ -188,13 +190,12 @@ static int cb_read_frames(struct channel *ch, float *out, sf_count_t frame_count
|
||||
/*
|
||||
* Calculate the gain for a fade at a given sample.
|
||||
*/
|
||||
static float cb_calc_fade(struct fade *fade, uint_least32_t sample)
|
||||
static float cb_calc_fade(struct fade *fade)
|
||||
{
|
||||
if (sample >= fade->end_pos) {
|
||||
if (fade->elapsed >= fade->frames)
|
||||
return fade->stop ? 0.0 : fade->end_volume;
|
||||
}
|
||||
|
||||
float progress = (float)(sample - fade->start_pos) / (float)(fade->end_pos - fade->start_pos);
|
||||
float progress = (float)fade->elapsed / (float)fade->frames;
|
||||
float delta_v = fade->end_volume - fade->start_volume;
|
||||
float gain = fade->start_volume + delta_v * progress;
|
||||
if (gain < 0.0) return 0.0;
|
||||
@@ -205,10 +206,11 @@ static float cb_calc_fade(struct fade *fade, uint_least32_t sample)
|
||||
static int refill_stream(sts_mixer_sample_t *sample, void *data)
|
||||
{
|
||||
struct channel *ch = data;
|
||||
uint_least32_t frames_read;
|
||||
memset(ch->data, 0, sizeof(float) * sample->length);
|
||||
|
||||
// read audio data from file
|
||||
int r = cb_read_frames(ch, ch->data, CHUNK_SIZE);
|
||||
int r = cb_read_frames(ch, ch->data, CHUNK_SIZE, &frames_read);
|
||||
|
||||
// convert mono to stereo
|
||||
if (ch->info.channels == 1) {
|
||||
@@ -229,16 +231,21 @@ static int refill_stream(sts_mixer_sample_t *sample, void *data)
|
||||
|
||||
// set gain for fade
|
||||
if (ch->fade.fading) {
|
||||
float gain = cb_calc_fade(&ch->fade, ch->frame);
|
||||
float gain = cb_calc_fade(&ch->fade);
|
||||
mixers[ch->mixer_no].mixer.voices[ch->voice].gain = gain;
|
||||
ch->volume = gain * 100.0;
|
||||
|
||||
if (ch->frame >= ch->fade.end_pos) {
|
||||
ch->fade.elapsed += frames_read;
|
||||
if (ch->fade.elapsed >= ch->fade.frames) {
|
||||
ch->fade.fading = false;
|
||||
if (ch->fade.stop) {
|
||||
cb_seek(ch, 0);
|
||||
r = STS_STREAM_COMPLETE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
float gain = ch->volume / 100.0;
|
||||
mixers[ch->mixer_no].mixer.voices[ch->voice].gain = gain;
|
||||
}
|
||||
|
||||
if (r == STS_STREAM_COMPLETE) {
|
||||
@@ -264,8 +271,9 @@ int channel_play(struct channel *ch)
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
if (ch->voice >= 0) {
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
memset(ch->data, 0, sizeof(ch->data));
|
||||
ch->voice = sts_mixer_play_stream(&mixers[ch->mixer_no].mixer, &ch->stream, 1.0f);
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return 1;
|
||||
@@ -276,8 +284,9 @@ int channel_stop(struct channel *ch)
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
if (ch->voice < 0) {
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
cb_seek(ch, 0);
|
||||
sts_mixer_stop_voice(&mixers[ch->mixer_no].mixer, ch->voice);
|
||||
ch->voice = -1;
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
@@ -320,13 +329,30 @@ int channel_set_loop_end_pos(struct channel *ch, int pos)
|
||||
|
||||
int channel_fade(struct channel *ch, int time, int volume, bool stop)
|
||||
{
|
||||
if (!time && stop)
|
||||
return channel_stop(ch);
|
||||
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
ch->fade.fading = true;
|
||||
ch->fade.stop = stop;
|
||||
ch->fade.start_pos = ch->frame;
|
||||
ch->fade.start_volume = (float)ch->volume / 100.0;
|
||||
ch->fade.end_pos = ch->fade.start_pos + ((time * ch->info.samplerate) / 1000);
|
||||
ch->fade.end_volume = clamp(0.0f, 1.0f, (float)volume / 100.0f);
|
||||
if (!time) {
|
||||
// XXX: Fade with time=0 is used to set volume. This needs to
|
||||
// take effect immediately, not via the audio callback
|
||||
// because the stream isn't necessarily playing yet.
|
||||
// E.g. the below call sequence should fade from 0 -> 33:
|
||||
//
|
||||
// SACT2.Music_Fade(ch, 0, 0, 0);
|
||||
// SACT2.Music_Fade(ch, 33, 4000, 0);
|
||||
// SACT2.Music_Play(ch);
|
||||
ch->fade.fading = false;
|
||||
ch->volume = max(0, min(100, volume));
|
||||
} else {
|
||||
ch->fade.fading = true;
|
||||
ch->fade.stop = stop;
|
||||
ch->fade.start_pos = ch->frame;
|
||||
ch->fade.start_volume = (float)ch->volume / 100.0;
|
||||
ch->fade.frames = muldiv(time, ch->info.samplerate, 1000);
|
||||
ch->fade.elapsed = 0;
|
||||
ch->fade.end_volume = clamp(0.0f, 1.0f, (float)volume / 100.0f);
|
||||
}
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return 1;
|
||||
}
|
||||
@@ -336,7 +362,7 @@ int channel_stop_fade(struct channel *ch)
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
// XXX: we need to set the volume to end_volume and potentially stop the
|
||||
// stream here; better to let the callback do it
|
||||
ch->fade.end_pos = ch->frame;
|
||||
ch->fade.elapsed = ch->fade.frames;
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return 1;
|
||||
}
|
||||
@@ -367,13 +393,13 @@ int channel_is_paused(possibly_unused struct channel *ch)
|
||||
|
||||
int channel_get_pos(struct channel *ch)
|
||||
{
|
||||
return (ch->frame * 1000) / ch->info.samplerate;
|
||||
return muldiv(ch->frame, 1000, ch->info.samplerate);
|
||||
}
|
||||
|
||||
int channel_get_length(struct channel *ch)
|
||||
{
|
||||
// FIXME: how is this different than channel_get_time_length?
|
||||
return (ch->info.frames * 1000) / ch->info.samplerate;
|
||||
return muldiv(ch->info.frames, 1000, ch->info.samplerate);
|
||||
}
|
||||
|
||||
int channel_get_sample_pos(struct channel *ch)
|
||||
@@ -390,7 +416,7 @@ int channel_seek(struct channel *ch, int pos)
|
||||
{
|
||||
// NOTE: SACT2.Music_Seek doesn't seem to do anything in Sengoku Rance...
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
int r = cb_seek(ch, (pos * ch->info.samplerate) / 1000);
|
||||
int r = cb_seek(ch, muldiv(pos, ch->info.samplerate, 1000));
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return r;
|
||||
}
|
||||
@@ -408,7 +434,7 @@ int channel_get_volume(struct channel *ch)
|
||||
|
||||
int channel_get_time_length(struct channel *ch)
|
||||
{
|
||||
return (ch->info.frames * 1000) / ch->info.samplerate;
|
||||
return muldiv(ch->info.frames, 1000, ch->info.samplerate);
|
||||
}
|
||||
|
||||
static sf_count_t channel_vio_get_filelen(void *data)
|
||||
@@ -466,7 +492,7 @@ static SF_VIRTUAL_IO channel_vio = {
|
||||
struct channel *channel_open(enum asset_type type, int no)
|
||||
{
|
||||
// get file from archive
|
||||
struct archive_data *dfile = asset_get(type, no-1);
|
||||
struct archive_data *dfile = asset_get(type, no);
|
||||
if (!dfile) {
|
||||
WARNING("Failed to load %s %d", type == ASSET_SOUND ? "WAV" : "BGM", no);
|
||||
return NULL;
|
||||
@@ -557,9 +583,9 @@ void mixer_init(void)
|
||||
if (!config.mixer_nr_channels) {
|
||||
nr_mixers = 3;
|
||||
mixers = xcalloc(nr_mixers, sizeof(struct mixer));
|
||||
mixers[0].name = "Music";
|
||||
mixers[1].name = "Sound";
|
||||
mixers[2].name = "Master";
|
||||
mixers[0].name = strdup("Music");
|
||||
mixers[1].name = strdup("Sound");
|
||||
mixers[2].name = strdup("Master");
|
||||
} else {
|
||||
// NOTE: Older games don't have an explicit master channel.
|
||||
// We add an extra mixer in this case for consistency.
|
||||
@@ -570,10 +596,10 @@ void mixer_init(void)
|
||||
nr_mixers = config.mixer_nr_channels + need_master;
|
||||
mixers = xcalloc(nr_mixers, sizeof(struct mixer));
|
||||
for (unsigned i = 0; i < config.mixer_nr_channels; i++) {
|
||||
mixers[i].name = config.mixer_channels[i];
|
||||
mixers[i].name = strdup(config.mixer_channels[i]);
|
||||
}
|
||||
if (need_master) {
|
||||
mixers[nr_mixers-1].name = "Master";
|
||||
mixers[nr_mixers-1].name = strdup("Master");
|
||||
master = &mixers[nr_mixers-1];
|
||||
} else {
|
||||
master = &mixers[0];
|
||||
@@ -647,6 +673,15 @@ const char *mixer_get_name(int n)
|
||||
return mixers[n].name;
|
||||
}
|
||||
|
||||
int mixer_set_name(int n, const char *name)
|
||||
{
|
||||
if (n < 0 || n >= nr_mixers)
|
||||
return 0;
|
||||
free(mixers[n].name);
|
||||
mixers[n].name = strdup(name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mixer_get_volume(int n, int *volume)
|
||||
{
|
||||
if (n < 0 || n >= nr_mixers) {
|
||||
@@ -682,3 +717,29 @@ int mixer_set_mute(int n, int mute)
|
||||
mixers[n].muted = !!mute;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mixer_stream_play(sts_mixer_stream_t* stream, int volume)
|
||||
{
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
float gain = clamp(0.0f, 1.0f, (float)volume / 100.0f);
|
||||
int voice = sts_mixer_play_stream(&master->mixer, stream, gain);
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return voice;
|
||||
}
|
||||
|
||||
bool mixer_stream_set_volume(int voice, int volume)
|
||||
{
|
||||
if (voice < 0 || voice >= STS_MIXER_VOICES)
|
||||
return false;
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
master->mixer.voices[voice].gain = clamp(0.0f, 1.0f, (float)volume / 100.0f);
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
return true;
|
||||
}
|
||||
|
||||
void mixer_stream_stop(int voice)
|
||||
{
|
||||
SDL_LockAudioDevice(audio_device);
|
||||
sts_mixer_stop_voice(&master->mixer, voice);
|
||||
SDL_UnlockAudioDevice(audio_device);
|
||||
}
|
||||
|
||||
+36
@@ -2488,6 +2488,42 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray_cb(int count, int (*get_number)(int,v
|
||||
return a;
|
||||
}
|
||||
|
||||
/* XXX: xsystem4 addition: callback-based CreateArray. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateArray_cb(int count, cJSON *(*get_item)(int,void*), void *data)
|
||||
{
|
||||
size_t i = 0;
|
||||
cJSON *n = NULL;
|
||||
cJSON *p = NULL;
|
||||
cJSON *a = NULL;
|
||||
|
||||
if (count < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
a = cJSON_CreateArray();
|
||||
for(i = 0; a && (i < (size_t)count); i++)
|
||||
{
|
||||
n = get_item(i, data);;
|
||||
if (!n)
|
||||
{
|
||||
// XXX: NULL return is ignored
|
||||
continue;
|
||||
}
|
||||
if(!i)
|
||||
{
|
||||
a->child = n;
|
||||
}
|
||||
else
|
||||
{
|
||||
suffix_object(p, n);
|
||||
}
|
||||
p = n;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
+922
-32
File diff suppressed because it is too large
Load Diff
+323
-158
@@ -21,8 +21,11 @@
|
||||
#include <string.h>
|
||||
#include <setjmp.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/file.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/utfsjis.h"
|
||||
|
||||
@@ -30,17 +33,28 @@
|
||||
#include "vm/heap.h"
|
||||
#include "vm/page.h"
|
||||
|
||||
#include "scene.h"
|
||||
#include "debugger.h"
|
||||
#include "little_endian.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
struct dbg_cmd {
|
||||
const char *fullname;
|
||||
const char *shortname;
|
||||
const char *description;
|
||||
unsigned min_args;
|
||||
unsigned max_args;
|
||||
void (*run)(unsigned nr_args, char **args);
|
||||
struct dbg_cmd_node;
|
||||
static bool stepping = false;
|
||||
|
||||
struct dbg_cmd_list {
|
||||
unsigned nr_commands;
|
||||
struct dbg_cmd_node *commands;
|
||||
};
|
||||
|
||||
struct dbg_cmd_node {
|
||||
enum { CMD_NODE_CMD, CMD_NODE_MOD } type;
|
||||
union {
|
||||
struct dbg_cmd cmd;
|
||||
struct {
|
||||
const char *name;
|
||||
struct dbg_cmd_list list;
|
||||
} mod;
|
||||
};
|
||||
};
|
||||
|
||||
static void dbg_cmd_help(unsigned nr_args, char **args);
|
||||
@@ -52,7 +66,7 @@ static void dbg_cmd_backtrace(unsigned nr_args, char **args)
|
||||
|
||||
static void dbg_cmd_breakpoint(unsigned nr_args, char **args)
|
||||
{
|
||||
int addr = atoi(args[0]);
|
||||
int addr = strtol(args[0], NULL, 0);
|
||||
if (addr > 0) {
|
||||
dbg_set_address_breakpoint(addr, NULL, NULL);
|
||||
} else {
|
||||
@@ -62,98 +76,33 @@ static void dbg_cmd_breakpoint(unsigned nr_args, char **args)
|
||||
|
||||
static void dbg_cmd_continue(unsigned nr_args, char **args)
|
||||
{
|
||||
stepping = false;
|
||||
dbg_continue();
|
||||
}
|
||||
|
||||
static struct string *value_to_string(struct ain_type *type, union vm_value value, int recursive)
|
||||
static void dbg_cmd_frame(unsigned nr_args, char **args)
|
||||
{
|
||||
switch (type->data) {
|
||||
case AIN_INT:
|
||||
return integer_to_string(value.i);
|
||||
case AIN_FLOAT:
|
||||
return float_to_string(value.f, 6);
|
||||
break;
|
||||
case AIN_STRING: {
|
||||
struct string *out = cstr_to_string("\"");
|
||||
string_append(&out, heap_get_string(value.i));
|
||||
string_push_back(&out, '"');
|
||||
return out;
|
||||
int frame_no = atoi(args[0]);
|
||||
if (frame_no < 0 || frame_no >= call_stack_ptr) {
|
||||
DBG_ERROR("Invalid frame number: %d", frame_no);
|
||||
return;
|
||||
}
|
||||
case AIN_STRUCT:
|
||||
case AIN_REF_STRUCT: {
|
||||
struct page *page = heap_get_page(value.i);
|
||||
if (page->nr_vars == 0) {
|
||||
return cstr_to_string("{}");
|
||||
}
|
||||
if (!recursive) {
|
||||
return cstr_to_string("{ <...> }");
|
||||
}
|
||||
struct string *out = cstr_to_string("{ ");
|
||||
for (int i = 0; i < page->nr_vars; i++) {
|
||||
struct ain_variable *m = &ain->structures[type->struc].members[i];
|
||||
if (i) {
|
||||
string_append_cstr(&out, "; ", 2);
|
||||
}
|
||||
string_append_cstr(&out, m->name, strlen(m->name));
|
||||
string_append_cstr(&out, " = ", 3);
|
||||
struct string *tmp = value_to_string(&m->type, page->values[i], recursive-1);
|
||||
string_append(&out, tmp);
|
||||
free_string(tmp);
|
||||
}
|
||||
string_append_cstr(&out, " }", 2);
|
||||
return out;
|
||||
}
|
||||
case AIN_ARRAY_TYPE:
|
||||
case AIN_REF_ARRAY_TYPE: {
|
||||
struct page *page = heap_get_page(value.i);
|
||||
if (!page || page->nr_vars == 0) {
|
||||
return cstr_to_string("[]");
|
||||
}
|
||||
// get member type
|
||||
struct ain_type t;
|
||||
t.data = variable_type(page, 0, &t.struc, &t.rank);
|
||||
t.array_type = NULL;
|
||||
if (!recursive) {
|
||||
switch (t.data) {
|
||||
case AIN_STRUCT:
|
||||
case AIN_REF_STRUCT:
|
||||
case AIN_ARRAY_TYPE:
|
||||
case AIN_REF_ARRAY_TYPE:
|
||||
return cstr_to_string("[ <...> ]");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
struct string *out = cstr_to_string("[ ");
|
||||
for (int i = 0; i < page->nr_vars; i++) {
|
||||
if (i) {
|
||||
string_append_cstr(&out, "; ", 2);
|
||||
}
|
||||
struct string *tmp = value_to_string(&t, page->values[i], recursive-1);
|
||||
string_append(&out, tmp);
|
||||
free_string(tmp);
|
||||
}
|
||||
string_append_cstr(&out, " ]", 2);
|
||||
return out;
|
||||
}
|
||||
default:
|
||||
return cstr_to_string("<unsupported-data-type>");
|
||||
}
|
||||
return string_ref(&EMPTY_STRING);
|
||||
dbg_current_frame = frame_no;
|
||||
dbg_print_frame(frame_no);
|
||||
}
|
||||
|
||||
static void dbg_cmd_locals(unsigned nr_args, char **args)
|
||||
{
|
||||
unsigned frame_no = nr_args > 0 ? atoi(args[0]) : 0;
|
||||
int frame_no = nr_args > 0 ? atoi(args[0]) : dbg_current_frame;
|
||||
if (frame_no < 0 || frame_no >= call_stack_ptr) {
|
||||
DBG_ERROR("Invalid frame number: %u", frame_no);
|
||||
DBG_ERROR("Invalid frame number: %d", frame_no);
|
||||
return;
|
||||
}
|
||||
|
||||
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 = value_to_string(&f->vars[i].type, page->values[i], 1);
|
||||
struct string *value = dbg_value_to_string(&f->vars[i].type, page->values[i], 1);
|
||||
printf("[%d] %s: %s\n", i, display_sjis0(f->vars[i].name), display_sjis1(value->text));
|
||||
free_string(value);
|
||||
}
|
||||
@@ -165,7 +114,7 @@ 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 = value_to_string(&f->vars[i].type, locals->values[i], 1);
|
||||
struct string *value = dbg_value_to_string(&f->vars[i].type, locals->values[i], 1);
|
||||
printf(i > 0 ? ", %s" : "%s", display_sjis0(value->text));
|
||||
free_string(value);
|
||||
}
|
||||
@@ -182,37 +131,44 @@ static void dbg_cmd_log(unsigned nr_args, char **args)
|
||||
dbg_set_function_breakpoint(args[0], log_handler, &ain->functions[fno]);
|
||||
}
|
||||
|
||||
struct ain_variable *dbg_get_variable(const char *name, union vm_value *val_out)
|
||||
static void dbg_cmd_members(unsigned nr_args, char **args)
|
||||
{
|
||||
struct page *page = local_page();
|
||||
struct ain_function *f = &ain->functions[page->index];
|
||||
for (int i = 0; i < f->nr_vars; i++) {
|
||||
if (!strcmp(f->vars[i].name, name)) {
|
||||
*val_out = page->values[i];
|
||||
return &f->vars[i];
|
||||
}
|
||||
int frame_no = nr_args > 0 ? atoi(args[0]) : dbg_current_frame;
|
||||
if (frame_no < 0 || frame_no >= call_stack_ptr) {
|
||||
DBG_ERROR("Invalid frame number: %d", frame_no);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < ain->nr_globals; i++) {
|
||||
if (!strcmp(ain->globals[i].name, name)) {
|
||||
*val_out = global_get(i);
|
||||
return &ain->globals[i];
|
||||
}
|
||||
|
||||
struct page *page = get_struct_page(frame_no);
|
||||
if (!page) {
|
||||
DBG_ERROR("Frame #%d is not a method", frame_no);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(page->type == STRUCT_PAGE);
|
||||
assert(page->index >= 0 && page->index < ain->nr_structures);
|
||||
struct ain_struct *s = &ain->structures[page->index];
|
||||
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);
|
||||
printf("[%d] %s: %s\n", i, display_sjis0(s->members[i].name), display_sjis1(value->text));
|
||||
free_string(value);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void dbg_cmd_print(unsigned nr_args, char **args)
|
||||
{
|
||||
union vm_value value;
|
||||
struct ain_variable *var = dbg_get_variable(args[0], &value);
|
||||
if (var) {
|
||||
struct string *v = value_to_string(&var->type, value, 1);
|
||||
printf("%s\n", display_sjis0(v->text));
|
||||
free_string(v);
|
||||
int recursive = nr_args == 2 ? atoi(args[1]) : 2;
|
||||
|
||||
struct ain_type type;
|
||||
union vm_value value = dbg_eval_string(args[0], &type);
|
||||
if (type.data == AIN_VOID)
|
||||
return;
|
||||
}
|
||||
// TODO: print functions, etc.
|
||||
DBG_ERROR("Undefined variable: %s", args[0]);
|
||||
|
||||
struct string *str = dbg_value_to_string(&type, value, recursive);
|
||||
printf("%s\n", display_sjis0(str->text));
|
||||
free_string(str);
|
||||
}
|
||||
|
||||
static void dbg_cmd_quit(unsigned nr_args, char **args)
|
||||
@@ -220,6 +176,41 @@ static void dbg_cmd_quit(unsigned nr_args, char **args)
|
||||
dbg_quit();
|
||||
}
|
||||
|
||||
static void dbg_cmd_scene(unsigned nr_args, char **args)
|
||||
{
|
||||
scene_print();
|
||||
}
|
||||
|
||||
static void dbg_cmd_next(unsigned nr_args, char **args)
|
||||
{
|
||||
if (!dbg_set_step_over_breakpoint()) {
|
||||
DBG_ERROR("Can't step over this instruction");
|
||||
return;
|
||||
}
|
||||
stepping = true;
|
||||
dbg_continue();
|
||||
}
|
||||
|
||||
static void dbg_cmd_step(unsigned nr_args, char **args)
|
||||
{
|
||||
if (!dbg_set_step_into_breakpoint()) {
|
||||
DBG_ERROR("Can't step into this instruction");
|
||||
return;
|
||||
}
|
||||
stepping = true;
|
||||
dbg_continue();
|
||||
}
|
||||
|
||||
static void dbg_cmd_finish(unsigned nr_args, char **args)
|
||||
{
|
||||
if (!dbg_set_finish_breakpoint()) {
|
||||
DBG_ERROR("Can't set finish breakpoint from this location");
|
||||
return;
|
||||
}
|
||||
stepping = true;
|
||||
dbg_continue();
|
||||
}
|
||||
|
||||
#ifdef HAVE_SCHEME
|
||||
static void dbg_cmd_scheme(unsigned nr_args, char **args)
|
||||
{
|
||||
@@ -227,48 +218,159 @@ static void dbg_cmd_scheme(unsigned nr_args, char **args)
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct dbg_cmd dbg_commands[] = {
|
||||
{ "help", "h", "[command-name] - Display this message", 0, 1, dbg_cmd_help },
|
||||
{ "backtrace", "bt", "- Display stack trace", 0, 0, dbg_cmd_backtrace },
|
||||
{ "breakpoint", "bp", "<function-or-address> - Set breakpoint", 1, 1, dbg_cmd_breakpoint },
|
||||
{ "continue", "c", "- Resume execution", 0, 0, dbg_cmd_continue },
|
||||
{ "locals", "l", "[frame-number] - Print local variables", 0, 1, dbg_cmd_locals },
|
||||
{ "log", NULL, "<function-name> - Log function calls", 1, 1, dbg_cmd_log },
|
||||
{ "print", "p", "<variable-name> - Print a variable", 1, 1, dbg_cmd_print },
|
||||
{ "quit", "q", "- Quit xsystem4", 0, 0, dbg_cmd_quit },
|
||||
static void dbg_cmd_vm_state(unsigned nr_args, char **args)
|
||||
{
|
||||
dbg_print_vm_state();
|
||||
}
|
||||
|
||||
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 },
|
||||
{ "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 },
|
||||
{ "help", "h", "[command-name]", "Get help about a command", 0, 2, dbg_cmd_help },
|
||||
{ "locals", "l", "[frame-number]", "Print local variables", 0, 1, dbg_cmd_locals },
|
||||
{ "log", NULL, "<function-name>", "Log function calls", 1, 1, dbg_cmd_log },
|
||||
{ "members", "m", "[frame-number]", "Print struct members", 0, 1, dbg_cmd_members },
|
||||
{ "next", "n", NULL, "Step to the next instruction within the current function", 0, 0, dbg_cmd_next },
|
||||
{ "print", "p", "<variable-name> [recursion-depth]", "Print a variable", 1, 2, dbg_cmd_print },
|
||||
{ "quit", "q", NULL, "Quit xsystem4", 0, 0, dbg_cmd_quit },
|
||||
{ "scene", NULL, NULL, "Display scene graph", 0, 0, dbg_cmd_scene },
|
||||
{ "step", "s", NULL, "Step to the next instruction", 0, 0, dbg_cmd_step },
|
||||
#ifdef HAVE_SCHEME
|
||||
{ "scheme", "scm", "- Drop into Scheme REPL", 0, 0, dbg_cmd_scheme },
|
||||
{ "scheme", "scm", NULL, "Drop into the Scheme REPL", 0, 0, dbg_cmd_scheme },
|
||||
#endif
|
||||
{ "vm-state", "vm", NULL, "Display current VM state", 0, 0, dbg_cmd_vm_state },
|
||||
};
|
||||
|
||||
static void dbg_cmd_help(unsigned nr_args, char **args)
|
||||
{
|
||||
NOTICE("Available Commands");
|
||||
NOTICE("------------------");
|
||||
for (unsigned i = 0; i < sizeof(dbg_commands)/sizeof(*dbg_commands); i++) {
|
||||
struct dbg_cmd *cmd = &dbg_commands[i];
|
||||
if (!nr_args || !strcmp(args[0], cmd->fullname) || !strcmp(args[0], cmd->shortname)) {
|
||||
if (cmd->shortname)
|
||||
NOTICE("%s (%s) %s", cmd->fullname, cmd->shortname, cmd->description);
|
||||
else
|
||||
NOTICE("%s %s", cmd->fullname, cmd->description);
|
||||
}
|
||||
}
|
||||
}
|
||||
static struct dbg_cmd_list dbg_commands = {0};
|
||||
|
||||
/*
|
||||
* Get a command by name.
|
||||
*/
|
||||
static struct dbg_cmd *dbg_get_command(const char *name)
|
||||
static struct dbg_cmd_node *dbg_get_node(struct dbg_cmd_list *list, const char *name)
|
||||
{
|
||||
for (unsigned i = 0; i < sizeof(dbg_commands)/sizeof(*dbg_commands); i++) {
|
||||
struct dbg_cmd *cmd = &dbg_commands[i];
|
||||
if (!strcmp(name, cmd->fullname) || (cmd->shortname && !strcmp(name, cmd->shortname)))
|
||||
return cmd;
|
||||
for (unsigned i = 0; i < list->nr_commands; i++) {
|
||||
switch (list->commands[i].type) {
|
||||
case CMD_NODE_CMD: {
|
||||
struct dbg_cmd *cmd = &list->commands[i].cmd;
|
||||
if (!strcmp(name, cmd->fullname) || (cmd->shortname && !strcmp(name, cmd->shortname)))
|
||||
return &list->commands[i];
|
||||
break;
|
||||
}
|
||||
case CMD_NODE_MOD:
|
||||
if (!strcmp(name, list->commands[i].mod.name))
|
||||
return &list->commands[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void dbg_help_cmd(struct dbg_cmd *cmd)
|
||||
{
|
||||
printf("%s", cmd->fullname);
|
||||
if (cmd->shortname)
|
||||
printf(", %s", cmd->shortname);
|
||||
|
||||
if (cmd->arg_description)
|
||||
printf(" %s", cmd->arg_description);
|
||||
|
||||
printf("\n\t%s\n", cmd->description);
|
||||
}
|
||||
|
||||
static int cmd_name_max(struct dbg_cmd_list *list)
|
||||
{
|
||||
int m = 0;
|
||||
for (unsigned i = 0; i < list->nr_commands; i++) {
|
||||
if (list->commands[i].type == CMD_NODE_MOD) {
|
||||
m = max(m, strlen(list->commands[i].mod.name));
|
||||
} else if (list->commands[i].type == CMD_NODE_CMD) {
|
||||
m = max(m, strlen(list->commands[i].cmd.fullname));
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
static void dbg_help_cmd_short(struct dbg_cmd *cmd, int name_len)
|
||||
{
|
||||
printf("%*s", name_len, cmd->fullname);
|
||||
if (cmd->shortname)
|
||||
printf(" (%s)%*s", cmd->shortname, 3 - (int)strlen(cmd->shortname), "");
|
||||
else
|
||||
printf(" ");
|
||||
printf(" -- %s\n", cmd->description);
|
||||
}
|
||||
|
||||
static void dbg_help_list(struct dbg_cmd_list *list)
|
||||
{
|
||||
int name_len = cmd_name_max(list);
|
||||
|
||||
puts("");
|
||||
puts("Available Commands");
|
||||
puts("==================");
|
||||
for (unsigned i = 0; i < list->nr_commands; i++) {
|
||||
if (list->commands[i].type == CMD_NODE_MOD) {
|
||||
printf("%s - command module; run to see additional commands\n",
|
||||
list->commands[i].mod.name);
|
||||
} else if (list->commands[i].type == CMD_NODE_CMD) {
|
||||
dbg_help_cmd_short(&list->commands[i].cmd, name_len);
|
||||
}
|
||||
}
|
||||
puts("");
|
||||
puts("Type 'help <command>' to get help about a specific command.");
|
||||
}
|
||||
|
||||
static void dbg_cmd_help(unsigned nr_args, char **args)
|
||||
{
|
||||
if (!nr_args) {
|
||||
dbg_help_list(&dbg_commands);
|
||||
return;
|
||||
}
|
||||
|
||||
struct dbg_cmd_node *node = dbg_get_node(&dbg_commands, args[0]);
|
||||
if (!node) {
|
||||
DBG_ERROR("'%s' is not a command or module", args[0]);
|
||||
return;
|
||||
}
|
||||
if (nr_args > 1) {
|
||||
if (node->type != CMD_NODE_MOD) {
|
||||
DBG_ERROR("'%s' is not a command module", args[0]);
|
||||
return;
|
||||
}
|
||||
node = dbg_get_node(&node->mod.list, args[1]);
|
||||
if (!node) {
|
||||
DBG_ERROR("'%s %s' is not a command or module", args[0], args[1]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (node->type) {
|
||||
case CMD_NODE_CMD:
|
||||
dbg_help_cmd(&node->cmd);
|
||||
break;
|
||||
case CMD_NODE_MOD:
|
||||
dbg_help_list(&node->mod.list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void dbg_cmd_add_module(const char *name, unsigned nr_commands, struct dbg_cmd *commands)
|
||||
{
|
||||
dbg_commands.commands = xrealloc_array(dbg_commands.commands, dbg_commands.nr_commands,
|
||||
dbg_commands.nr_commands+1, sizeof(struct dbg_cmd_node));
|
||||
struct dbg_cmd_node *node = &dbg_commands.commands[dbg_commands.nr_commands++];
|
||||
node->type = CMD_NODE_MOD;
|
||||
node->mod.name = name;
|
||||
node->mod.list.nr_commands = nr_commands;
|
||||
node->mod.list.commands = xcalloc(nr_commands, sizeof(struct dbg_cmd_node));
|
||||
for (unsigned i = 0; i < nr_commands; i++) {
|
||||
node->mod.list.commands[i].type = CMD_NODE_CMD;
|
||||
node->mod.list.commands[i].cmd = commands[i];
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_READLINE
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
@@ -322,30 +424,93 @@ static char **cmd_parse(char *line, unsigned *nr_words)
|
||||
return words;
|
||||
}
|
||||
|
||||
void dbg_cmd_repl(void)
|
||||
static void execute_command(struct dbg_cmd *cmd, unsigned nr_args, char **args)
|
||||
{
|
||||
NOTICE("Entering the debugger REPL. Type 'help' for a list of commands.");
|
||||
while (1) {
|
||||
char *line = cmd_gets();
|
||||
if (!line)
|
||||
continue;
|
||||
if (nr_args < cmd->min_args || nr_args > cmd->max_args) {
|
||||
printf("Wrong number of arguments to '%s' command\n", cmd->fullname);
|
||||
return;
|
||||
}
|
||||
cmd->run(nr_args, args);
|
||||
}
|
||||
|
||||
unsigned nr_words;
|
||||
char **words = cmd_parse(line, &nr_words);
|
||||
if (!nr_words)
|
||||
continue;
|
||||
static void execute_words(struct dbg_cmd_list *list, unsigned nr_words, char **words)
|
||||
{
|
||||
struct dbg_cmd_node *node = dbg_get_node(list, words[0]);
|
||||
if (!node) {
|
||||
printf("Invalid command: %s (type 'help' for a list of commands)\n", words[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
struct dbg_cmd *cmd = dbg_get_command(words[0]);
|
||||
if (!cmd) {
|
||||
NOTICE("Invalid command: %s (type 'help' for a list of commands)", words[0]);
|
||||
continue;
|
||||
switch (node->type) {
|
||||
case CMD_NODE_CMD:
|
||||
execute_command(&node->cmd, nr_words-1, words+1);
|
||||
break;
|
||||
case CMD_NODE_MOD:
|
||||
if (nr_words == 1) {
|
||||
printf("TODO: module help");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((nr_words-1) < cmd->min_args || (nr_words-1) > cmd->max_args) {
|
||||
NOTICE("Wrong number of arguments to '%s' command", cmd->fullname);
|
||||
continue;
|
||||
}
|
||||
|
||||
cmd->run(nr_words-1, words+1);
|
||||
execute_words(&node->mod.list, nr_words-1, words+1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void execute_line(char *line)
|
||||
{
|
||||
unsigned nr_words;
|
||||
char **words = cmd_parse(line, &nr_words);
|
||||
if (!nr_words)
|
||||
return;
|
||||
|
||||
execute_words(&dbg_commands, nr_words, words);
|
||||
}
|
||||
|
||||
void dbg_cmd_repl(void)
|
||||
{
|
||||
if (stepping)
|
||||
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)
|
||||
execute_line(line);
|
||||
}
|
||||
}
|
||||
|
||||
static void execute_config(void *_f)
|
||||
{
|
||||
FILE *f = _f;
|
||||
char line[1024];
|
||||
while (fgets(line, 1024, f)) {
|
||||
execute_line(line);
|
||||
}
|
||||
}
|
||||
|
||||
static void read_config(const char *path)
|
||||
{
|
||||
FILE *f = file_open_utf8(path, "rb");
|
||||
if (!f)
|
||||
return;
|
||||
dbg_start(execute_config, f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void dbg_cmd_init(void)
|
||||
{
|
||||
dbg_commands.nr_commands = sizeof(dbg_default_commands)/sizeof(*dbg_default_commands);
|
||||
dbg_commands.commands = xcalloc(dbg_commands.nr_commands, sizeof(struct dbg_cmd_node));
|
||||
for (unsigned i = 0; i < dbg_commands.nr_commands; i++) {
|
||||
dbg_commands.commands[i].type = CMD_NODE_CMD;
|
||||
dbg_commands.commands[i].cmd = dbg_default_commands[i];
|
||||
}
|
||||
|
||||
char *path = xmalloc(PATH_MAX);
|
||||
snprintf(path, PATH_MAX, "%s/.xsys4-debugrc", config.home_dir);
|
||||
read_config(path);
|
||||
free(path);
|
||||
|
||||
path = gamedir_path(".xsys4-debugrc");
|
||||
read_config(path);
|
||||
free(path);
|
||||
}
|
||||
|
||||
+345
-5
@@ -34,6 +34,7 @@ struct copy_shader {
|
||||
// optional
|
||||
GLint color;
|
||||
GLint threshold;
|
||||
GLint threshold2;
|
||||
};
|
||||
|
||||
struct copy_data {
|
||||
@@ -43,6 +44,7 @@ struct copy_data {
|
||||
// optional
|
||||
float r, g, b, a;
|
||||
float threshold;
|
||||
float threshold2;
|
||||
};
|
||||
|
||||
#define ROTATE_DATA(dst, _sx, _sy, _w, _h) \
|
||||
@@ -63,12 +65,17 @@ static struct copy_shader copy_alpha_key_shader;
|
||||
static struct copy_shader copy_use_amap_under_shader;
|
||||
static struct copy_shader copy_use_amap_border_shader;
|
||||
static struct copy_shader blend_amap_color_shader;
|
||||
static struct copy_shader blend_amap_alpha_shader;
|
||||
static struct copy_shader blend_amap_alpha_bright_shader;
|
||||
static struct copy_shader blend_use_amap_color_shader;
|
||||
static struct copy_shader fill_shader;
|
||||
static struct copy_shader fill_amap_over_border_shader;
|
||||
static struct copy_shader fill_amap_under_border_shader;
|
||||
static struct copy_shader fill_amap_gradation_ud_shader;
|
||||
static struct copy_shader hitbox_shader;
|
||||
static struct copy_shader hitbox_noblend_shader;
|
||||
static struct copy_shader amap_saturate_shader;
|
||||
static struct copy_shader blend_rmap_color_shader;
|
||||
static struct copy_shader dilate_shader;
|
||||
|
||||
static void prepare_copy_shader(struct gfx_render_job *job, void *data)
|
||||
{
|
||||
@@ -79,6 +86,7 @@ static void prepare_copy_shader(struct gfx_render_job *job, void *data)
|
||||
|
||||
glUniform4f(s->color, d->r, d->g, d->b, d->a);
|
||||
glUniform1f(s->threshold, d->threshold);
|
||||
glUniform1f(s->threshold2, d->threshold2);
|
||||
}
|
||||
|
||||
static void load_copy_shader(struct copy_shader *s, const char *v_path, const char *f_path)
|
||||
@@ -88,6 +96,7 @@ static void load_copy_shader(struct copy_shader *s, const char *v_path, const ch
|
||||
s->top_right = glGetUniformLocation(s->s.program, "top_right");
|
||||
s->color = glGetUniformLocation(s->s.program, "color");
|
||||
s->threshold = glGetUniformLocation(s->s.program, "threshold");
|
||||
s->threshold2 = glGetUniformLocation(s->s.program, "threshold2");
|
||||
s->s.prepare = prepare_copy_shader;
|
||||
}
|
||||
|
||||
@@ -115,8 +124,10 @@ void gfx_draw_init(void)
|
||||
// copy shader that sets source RGB to a constant
|
||||
load_copy_shader(&blend_amap_color_shader, "shaders/render.v.glsl", "shaders/blend_amap_color.f.glsl");
|
||||
|
||||
// copy shader that multiples the source alpha by a constant
|
||||
load_copy_shader(&blend_amap_alpha_shader, "shaders/render.v.glsl", "shaders/blend_amap_alpha.f.glsl");
|
||||
// copy shader that multiples the source color and alpha by a constant
|
||||
load_copy_shader(&blend_amap_alpha_bright_shader, "shaders/render.v.glsl", "shaders/blend_amap_alpha_bright.f.glsl");
|
||||
|
||||
load_copy_shader(&blend_use_amap_color_shader, "shaders/render.v.glsl", "shaders/blend_use_amap_color.f.glsl");
|
||||
|
||||
// basic fill shader
|
||||
load_copy_shader(&fill_shader, "shaders/render.v.glsl", "shaders/fill.f.glsl");
|
||||
@@ -127,11 +138,23 @@ void gfx_draw_init(void)
|
||||
// fill shader that only fills fragments below a certain alpha threshold
|
||||
load_copy_shader(&fill_amap_under_border_shader, "shaders/render.v.glsl", "shaders/fill_amap_under_border.f.glsl");
|
||||
|
||||
// fill shader that fills the alpha map with a gradient
|
||||
load_copy_shader(&fill_amap_gradation_ud_shader, "shaders/render.v.glsl", "shaders/fill_amap_gradation_ud.f.glsl");
|
||||
|
||||
// shader that discards texels that fail a hitbox test
|
||||
load_copy_shader(&hitbox_shader, "shaders/render.v.glsl", "shaders/hitbox.f.glsl");
|
||||
|
||||
// hitbox shader which ignores alpha component of texture (a=1)
|
||||
load_copy_shader(&hitbox_noblend_shader, "shaders/render.v.glsl", "shaders/hitbox_noblend.f.glsl");
|
||||
|
||||
// shader that sets the color to black or white depending on alpha value
|
||||
load_copy_shader(&amap_saturate_shader, "shaders/render.v.glsl", "shaders/amap_saturate.f.glsl");
|
||||
|
||||
// shader that sets source RGB to a constant, using source red channel as alpha
|
||||
load_copy_shader(&blend_rmap_color_shader, "shaders/render.v.glsl", "shaders/blend_rmap_color.f.glsl");
|
||||
|
||||
// shader that dilates every pixel (for bold/outline text rendering)
|
||||
load_copy_shader(&dilate_shader, "shaders/render.v.glsl", "shaders/dilate.f.glsl");
|
||||
}
|
||||
|
||||
static void run_draw_shader(Shader *s, Texture *dst, Texture *src, mat4 mw_transform, mat4 wv_transform, struct copy_data *data)
|
||||
@@ -306,6 +329,29 @@ void gfx_blend(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_blend_src_bright(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int a, int rate)
|
||||
{
|
||||
GLfloat f_rate = rate / 255.0;
|
||||
f_rate *= (a / 255.0);
|
||||
glBlendFuncSeparate(GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_ALPHA, GL_ZERO, GL_ONE);
|
||||
glBlendColor(f_rate, f_rate, f_rate, a / 255.0);
|
||||
|
||||
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
|
||||
run_copy_shader(©_shader.s, dst, src, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_blend_add_satur(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h)
|
||||
{
|
||||
glBlendFuncSeparate(GL_DST_ALPHA, GL_ONE, GL_ZERO, GL_ONE);
|
||||
|
||||
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
|
||||
run_copy_shader(©_shader.s, dst, src, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_blend_amap(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h)
|
||||
{
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
@@ -316,6 +362,16 @@ void gfx_blend_amap(Texture *dst, int dx, int dy, Texture *src, int sx, int sy,
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_blend_amap_src_only(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h)
|
||||
{
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE);
|
||||
|
||||
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
|
||||
run_copy_shader(©_shader.s, dst, src, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_blend_amap_color(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int r, int g, int b)
|
||||
{
|
||||
// color = (r,g,b) * src_alpha + dst_color * (1 - src_alpha)
|
||||
@@ -326,6 +382,21 @@ void gfx_blend_amap_color(Texture *dst, int dx, int dy, Texture *src, int sx, in
|
||||
data.r = r / 255.0;
|
||||
data.g = g / 255.0;
|
||||
data.b = b / 255.0;
|
||||
data.a = 1.0;
|
||||
run_copy_shader(&blend_amap_color_shader.s, dst, src, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_blend_amap_color_alpha(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int r, int g, int b, int a)
|
||||
{
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
|
||||
data.r = r / 255.0;
|
||||
data.g = g / 255.0;
|
||||
data.b = b / 255.0;
|
||||
data.a = a / 255.0;
|
||||
run_copy_shader(&blend_amap_color_shader.s, dst, src, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
@@ -336,8 +407,89 @@ void gfx_blend_amap_alpha(Texture *dst, int dx, int dy, Texture *src, int sx, in
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
|
||||
data.threshold = a / 255.0;
|
||||
run_copy_shader(&blend_amap_alpha_shader.s, dst, src, &data);
|
||||
data.r = 1.0;
|
||||
data.g = 1.0;
|
||||
data.b = 1.0;
|
||||
data.a = a / 255.0;
|
||||
run_copy_shader(&blend_amap_alpha_bright_shader.s, dst, src, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_blend_amap_bright(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int rate)
|
||||
{
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
|
||||
data.r = rate / 255.0;
|
||||
data.g = rate / 255.0;
|
||||
data.b = rate / 255.0;
|
||||
data.a = 1.0;
|
||||
run_copy_shader(&blend_amap_alpha_bright_shader.s, dst, src, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_blend_amap_alpha_src_bright(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int alpha, int rate)
|
||||
{
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
|
||||
data.r = rate / 255.0;
|
||||
data.g = rate / 255.0;
|
||||
data.b = rate / 255.0;
|
||||
data.a = alpha / 255.0;
|
||||
run_copy_shader(&blend_amap_alpha_bright_shader.s, dst, src, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_blend_use_amap_color(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int r, int g, int b, int rate)
|
||||
{
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
|
||||
|
||||
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
|
||||
data.r = r / 255.0;
|
||||
data.g = g / 255.0;
|
||||
data.b = b / 255.0;
|
||||
data.a = 1.0;
|
||||
data.threshold = rate / 255.0;
|
||||
run_copy_shader(&blend_use_amap_color_shader.s, dst, src, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
|
||||
}
|
||||
|
||||
void gfx_blend_screen(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h)
|
||||
{
|
||||
glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_COLOR, GL_ZERO, GL_ONE);
|
||||
|
||||
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
|
||||
run_copy_shader(©_shader.s, dst, src, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_blend_multiply(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h)
|
||||
{
|
||||
glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE);
|
||||
|
||||
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
|
||||
run_copy_shader(©_shader.s, dst, src, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_blend_screen_alpha(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h, int a)
|
||||
{
|
||||
glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_COLOR, GL_ZERO, GL_ONE);
|
||||
|
||||
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
|
||||
data.r = a / 255.0;
|
||||
data.g = a / 255.0;
|
||||
data.b = a / 255.0;
|
||||
data.a = 1.0;
|
||||
run_copy_shader(&blend_amap_alpha_bright_shader.s, dst, src, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
@@ -405,6 +557,66 @@ void gfx_fill_amap_under_border(Texture *dst, int x, int y, int w, int h, int al
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_fill_amap_gradation_ud(Texture *dst, int x, int y, int w, int h, int up_a, int down_a)
|
||||
{
|
||||
glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_ONE, GL_ZERO);
|
||||
|
||||
struct copy_data data = COPY_DATA(x, y, 0, 0, w, h);
|
||||
data.threshold = up_a / 255.0;
|
||||
data.threshold2 = down_a / 255.0;
|
||||
run_fill_shader(&fill_amap_gradation_ud_shader.s, dst, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_fill_screen(Texture *dst, int x, int y, int w, int h, int r, int g, int b)
|
||||
{
|
||||
glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_COLOR, GL_ZERO, GL_ONE);
|
||||
|
||||
struct copy_data data = COPY_DATA(x, y, 0, 0, w, h);
|
||||
data.r = r / 255.0;
|
||||
data.g = g / 255.0;
|
||||
data.b = b / 255.0;
|
||||
data.a = 1.0;
|
||||
run_copy_shader(&fill_shader.s, dst, NULL, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_fill_multiply(Texture *dst, int x, int y, int w, int h, int r, int g, int b)
|
||||
{
|
||||
glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE);
|
||||
|
||||
struct copy_data data = COPY_DATA(x, y, 0, 0, w, h);
|
||||
data.r = r / 255.0;
|
||||
data.g = g / 255.0;
|
||||
data.b = b / 255.0;
|
||||
data.a = 1.0;
|
||||
run_copy_shader(&fill_shader.s, dst, NULL, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_satur_dp_dpxsa(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h)
|
||||
{
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE);
|
||||
|
||||
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
|
||||
run_copy_shader(&amap_saturate_shader.s, dst, src, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_screen_da_daxsa(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h)
|
||||
{
|
||||
glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_ONE, GL_ONE);
|
||||
|
||||
struct copy_data data = COPY_DATA(dx, dy, sx, sy, w, h);
|
||||
run_copy_shader(©_shader.s, dst, src, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_add_da_daxsa(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h)
|
||||
{
|
||||
// color = dst_color
|
||||
@@ -442,6 +654,18 @@ void gfx_sub_da_daxsa(struct texture *dst, int dx, int dy, struct texture *src,
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_bright_dest_only(Texture *dst, int x, int y, int w, int h, int rate)
|
||||
{
|
||||
GLfloat f_rate = rate / 255.0;
|
||||
glBlendFuncSeparate(GL_ZERO, GL_CONSTANT_COLOR, GL_ZERO, GL_ONE);
|
||||
glBlendColor(f_rate, f_rate, f_rate, 1.0);
|
||||
|
||||
struct copy_data data = COPY_DATA(x, y, 0, 0, w, h);
|
||||
run_copy_shader(©_shader.s, dst, NULL, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
// FIXME: DrawGraph.dll does some really strange clipping on the src/dst rectangles
|
||||
// before drawing. This implementation does what you would normally expect
|
||||
// a stretch function to do when passed negative (x,y) coordinates.
|
||||
@@ -488,6 +712,20 @@ void gfx_copy_stretch_blend_amap(struct texture *dst, int dx, int dy, int dw, in
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
struct copy_data data = STRETCH_DATA(dx, dy, dw, dh, sx, sy, sw, sh);
|
||||
data.r = 1.0;
|
||||
data.g = 1.0;
|
||||
data.b = 1.0;
|
||||
data.a = a / 255.0;
|
||||
run_copy_shader(&blend_amap_alpha_bright_shader.s, dst, src, &data);
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
// FIXME: this doesn't work correctly when the src rectangle crosses the edge of the CG.
|
||||
static void copy_rot_zoom(Texture *dst, Texture *src, int sx, int sy, int w, int h, float rotate, float mag, Shader *shader)
|
||||
{
|
||||
@@ -565,6 +803,45 @@ void gfx_copy_rotate_y_use_amap(Texture *dst, Texture *front, Texture *back, int
|
||||
copy_rotate_y(dst, front, back, sx, sy, w, h, rot, mag, &hitbox_shader.s);
|
||||
}
|
||||
|
||||
static void copy_rotate_x(Texture *dst, Texture *front, Texture *back, int sx, int sy, int w, int h, float rot, float mag, Shader *shader)
|
||||
{
|
||||
Texture *src = front;
|
||||
float flip_y = 1.0;
|
||||
rot = 360.0 - rot;
|
||||
if (rot > 90.0 && rot <= 270.0) {
|
||||
src = back;
|
||||
flip_y = -1.0;
|
||||
}
|
||||
|
||||
|
||||
gfx_fill_amap(dst, 0, 0, dst->w, dst->h, 0);
|
||||
|
||||
mat4 mw_transform = GLM_MAT4_IDENTITY_INIT;
|
||||
glm_rotate_x(mw_transform, rot * (M_PI/180.0), mw_transform);
|
||||
glm_scale(mw_transform, (vec3){ src->w * mag, src->h * mag * flip_y, 0 });
|
||||
glm_translate(mw_transform, (vec3){ -0.5, -0.5, 0});
|
||||
|
||||
mat4 proj_transform;
|
||||
float fov = 22.5 * (M_PI / 180.0);
|
||||
// calculate the camera distance such that the height at the pivot is unchanged
|
||||
float cam_off = ((float)dst->h * 0.5) / tanf(fov * 0.5);
|
||||
glm_perspective(fov, (float)dst->w / dst->h, 0.1, cam_off + w * mag, proj_transform);
|
||||
glm_translate(proj_transform, (vec3){ 0, 0, -cam_off });
|
||||
|
||||
struct copy_data data = ROTATE_DATA(dst, sx, sy, w, h);
|
||||
run_draw_shader(shader, dst, src, mw_transform, proj_transform, &data);
|
||||
}
|
||||
|
||||
void gfx_copy_rotate_x(Texture *dst, Texture *front, Texture *back, int sx, int sy, int w, int h, float rot, float mag)
|
||||
{
|
||||
copy_rotate_x(dst, front, back, sx, sy, w, h, rot, mag, &hitbox_noblend_shader.s);
|
||||
}
|
||||
|
||||
void gfx_copy_rotate_x_use_amap(Texture *dst, Texture *front, Texture *back, int sx, int sy, int w, int h, float rot, float mag)
|
||||
{
|
||||
copy_rotate_x(dst, front, back, sx, sy, w, h, rot, mag, &hitbox_shader.s);
|
||||
}
|
||||
|
||||
void gfx_copy_reverse_LR(Texture *dst, int dx, int dy, Texture *src, int sx, int sy, int w, int h)
|
||||
{
|
||||
mat4 mw_transform = MAT4(
|
||||
@@ -728,3 +1005,66 @@ void gfx_copy_stretch_with_alpha_map(Texture *dst, int dx, int dy, int dw, int d
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
// XXX: Not an actual DrawGraph function; used for rendering text
|
||||
void gfx_draw_glyph(Texture *dst, float dx, int dy, Texture *glyph, SDL_Color color, float scale_x, float bold_width)
|
||||
{
|
||||
dx = roundf(dx);
|
||||
|
||||
glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ZERO, GL_ONE);
|
||||
struct copy_data fill_data = COPY_DATA(dx, dy, dx, dy, glyph->w * scale_x, glyph->h);
|
||||
fill_data.r = color.r / 255.0;
|
||||
fill_data.g = color.g / 255.0;
|
||||
fill_data.b = color.b / 255.0;
|
||||
fill_data.a = 0.0;
|
||||
fill_data.threshold = 0.001;
|
||||
run_copy_shader(&fill_amap_under_border_shader.s, dst, dst, &fill_data);
|
||||
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glBlendEquationSeparate(GL_FUNC_ADD, GL_MAX);
|
||||
|
||||
struct copy_data data = STRETCH_DATA(
|
||||
dx, dy, glyph->w * scale_x, glyph->h,
|
||||
0, 0, glyph->w, glyph->h);
|
||||
data.r = color.r / 255.0;
|
||||
data.g = color.g / 255.0;
|
||||
data.b = color.b / 255.0;
|
||||
data.a = 1.0;
|
||||
|
||||
if (bold_width < 0.01) {
|
||||
run_copy_shader(&blend_rmap_color_shader.s, dst, glyph, &data);
|
||||
} else {
|
||||
data.threshold = bold_width;
|
||||
run_copy_shader(&dilate_shader.s, dst, glyph, &data);
|
||||
}
|
||||
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_draw_glyph_to_pmap(Texture *dst, float dx, int dy, Texture *glyph, Rectangle glyph_pos, SDL_Color color, float scale_x)
|
||||
{
|
||||
struct copy_data data = STRETCH_DATA(
|
||||
dx, dy, glyph_pos.w * scale_x, glyph_pos.h,
|
||||
glyph_pos.x, glyph_pos.y, glyph_pos.w, glyph_pos.h);
|
||||
data.r = color.r / 255.0;
|
||||
data.g = color.g / 255.0;
|
||||
data.b = color.b / 255.0;
|
||||
data.a = 1.0;
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
|
||||
run_copy_shader(&blend_rmap_color_shader.s, dst, glyph, &data);
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
void gfx_draw_glyph_to_amap(Texture *dst, float dx, int dy, Texture *glyph, Rectangle glyph_pos, float scale_x)
|
||||
{
|
||||
struct copy_data data = STRETCH_DATA(
|
||||
dx, dy, glyph_pos.w * scale_x, glyph_pos.h,
|
||||
glyph_pos.x, glyph_pos.y, glyph_pos.w, glyph_pos.h);
|
||||
data.r = 1.0;
|
||||
data.g = 1.0;
|
||||
data.b = 1.0;
|
||||
data.a = 1.0;
|
||||
glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_ONE, GL_ZERO);
|
||||
run_copy_shader(&blend_rmap_color_shader.s, dst, glyph, &data);
|
||||
restore_blend_mode();
|
||||
}
|
||||
|
||||
+16
-4
@@ -45,9 +45,14 @@
|
||||
#define M_PI (3.14159265358979323846)
|
||||
#endif
|
||||
|
||||
static void dungeon_render(struct sact_sprite *sp);
|
||||
|
||||
struct dungeon_context *dungeon_context_create(enum draw_dungeon_version version, int surface)
|
||||
{
|
||||
struct dungeon_context *ctx = xcalloc(1, sizeof(struct dungeon_context));
|
||||
ctx->plugin.name = "DrawDungeon";
|
||||
ctx->plugin.update = dungeon_render;
|
||||
ctx->plugin.debug_print = NULL;
|
||||
ctx->version = version;
|
||||
ctx->surface = surface;
|
||||
struct sact_sprite *sp = sact_get_sprite(ctx->surface);
|
||||
@@ -56,6 +61,7 @@ struct dungeon_context *dungeon_context_create(enum draw_dungeon_version version
|
||||
|
||||
// Dungeon scene will be rendered to this texture.
|
||||
struct texture *texture = sprite_get_texture(sp);
|
||||
sprite_bind_plugin(sp, &ctx->plugin);
|
||||
|
||||
glGenRenderbuffers(1, &ctx->depth_buffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, ctx->depth_buffer);
|
||||
@@ -68,6 +74,10 @@ struct dungeon_context *dungeon_context_create(enum draw_dungeon_version version
|
||||
|
||||
void dungeon_context_free(struct dungeon_context *ctx)
|
||||
{
|
||||
struct sact_sprite *sp = sact_try_get_sprite(ctx->surface);
|
||||
if (sp)
|
||||
sprite_bind_plugin(sp, NULL);
|
||||
|
||||
if (ctx->dgn)
|
||||
dgn_free(ctx->dgn);
|
||||
if (ctx->dtx)
|
||||
@@ -89,7 +99,7 @@ static GLuint *load_event_textures(int *nr_textures_out)
|
||||
{
|
||||
char *path = gamedir_path("Data/Event.alk");
|
||||
int error = ARCHIVE_SUCCESS;
|
||||
struct alk_archive *alk = alk_open(path, ARCHIVE_MMAP, &error);
|
||||
struct alk_archive *alk = alk_open(path, MMAP_IF_64BIT, &error);
|
||||
if (error == ARCHIVE_FILE_ERROR) {
|
||||
WARNING("alk_open(\"%s\"): %s", display_utf0(path), strerror(errno));
|
||||
} else if (error == ARCHIVE_BAD_ARCHIVE_ERROR) {
|
||||
@@ -136,7 +146,7 @@ bool dungeon_load(struct dungeon_context *ctx, int num)
|
||||
if (ctx->version == DRAW_DUNGEON_1) {
|
||||
char *path = gamedir_path("Data/DungeonData.dlf");
|
||||
int error = ARCHIVE_SUCCESS;
|
||||
struct archive *dlf = (struct archive *)dlf_open(path, ARCHIVE_MMAP, &error);
|
||||
struct archive *dlf = (struct archive *)dlf_open(path, MMAP_IF_64BIT, &error);
|
||||
if (error == ARCHIVE_FILE_ERROR) {
|
||||
WARNING("dlf_open(\"%s\"): %s", display_utf0(path), strerror(errno));
|
||||
} else if (error == ARCHIVE_BAD_ARCHIVE_ERROR) {
|
||||
@@ -248,9 +258,11 @@ static void model_view_matrix(struct camera *camera, mat4 out)
|
||||
glm_lookat(eye, camera->pos, up, out);
|
||||
}
|
||||
|
||||
void dungeon_render(struct dungeon_context *ctx)
|
||||
static void dungeon_render(struct sact_sprite *sp)
|
||||
{
|
||||
struct sact_sprite *sp = sact_get_sprite(ctx->surface);
|
||||
struct dungeon_context *ctx = (struct dungeon_context *)sp->plugin;
|
||||
if (!ctx->loaded || !ctx->draw_enabled)
|
||||
return;
|
||||
sprite_dirty(sp);
|
||||
struct texture *texture = sprite_get_texture(sp);
|
||||
|
||||
|
||||
+168
-54
@@ -14,6 +14,8 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <SDL.h>
|
||||
#include "effect.h"
|
||||
#include "gfx/gfx.h"
|
||||
@@ -65,7 +67,7 @@ const char *effect_names[NR_EFFECTS] = {
|
||||
[EFFECT_VERTICAL_BAR_BLUR_HD] = "EFFECT_VERTICAL_BAR_BLUR_HD",
|
||||
[EFFECT_AMAP_CROSSFADE2] = "EFFECT_AMAP_CROSSFADE2",
|
||||
[EFFECT_ZOOM_LR] = "EFFECT_ZOOM_LR",
|
||||
[EFFECT_ZOOR_RL] = "EFFECT_ZOOR_RL",
|
||||
[EFFECT_ZOOM_RL] = "EFFECT_ZOOM_RL",
|
||||
[EFFECT_CROSSFADE_LR] = "EFFECT_CROSSFADE_LR",
|
||||
[EFFECT_CROSSFADE_RL] = "EFFECT_CROSSFADE_RL",
|
||||
[EFFECT_PIXEL_EXPLOSION] = "EFFECT_PIXEL_EXPLOSION",
|
||||
@@ -134,7 +136,12 @@ static struct effect_shader crossfade_up_down_shader = EFFECT_SHADER("shaders/ef
|
||||
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");
|
||||
static struct effect_shader linear_blur_shader = EFFECT_SHADER("shaders/effects/linear_blur.f.glsl");
|
||||
static struct effect_shader zigzag_crossfade_shader = EFFECT_SHADER("shaders/effects/zigzag_crossfade.f.glsl");
|
||||
static struct effect_shader noise_crossfade_shader = EFFECT_SHADER("shaders/effects/noise_crossfade.f.glsl");
|
||||
static struct effect_shader turn_page_shader = EFFECT_SHADER("shaders/effects/turn_page.f.glsl");
|
||||
static struct effect_shader sepia_noise_crossfade_shader = EFFECT_SHADER("shaders/effects/sepia_noise_crossfade.f.glsl");
|
||||
static struct effect_shader blur_fadeout_shader = EFFECT_SHADER("shaders/effects/blur_fadeout.f.glsl");
|
||||
static struct effect_shader blur_crossfade_shader = EFFECT_SHADER("shaders/effects/blur_crossfade.f.glsl");
|
||||
|
||||
extern GLuint main_surface_fb;
|
||||
@@ -146,52 +153,126 @@ static struct effect_shader *effect_shaders[NR_EFFECTS] = {
|
||||
[EFFECT_CROSSFADE_MOSAIC] = &mosaic_shader,
|
||||
[EFFECT_BLIND_DOWN] = &blind_down_shader,
|
||||
[EFFECT_BLIND_LR] = &blind_lr_shader,
|
||||
[EFFECT_LINEAR_BLUR] = &linear_blur_shader,
|
||||
[EFFECT_ZIGZAG_CROSSFADE] = &zigzag_crossfade_shader,
|
||||
[EFFECT_NOISE_CROSSFADE] = &noise_crossfade_shader,
|
||||
[EFFECT_TURN_PAGE] = &turn_page_shader,
|
||||
[EFFECT_SEPIA_NOISE_CROSSFADE] = &sepia_noise_crossfade_shader,
|
||||
[EFFECT_BLUR_FADEOUT] = &blur_fadeout_shader,
|
||||
[EFFECT_BLUR_CROSSFADE] = &blur_crossfade_shader,
|
||||
};
|
||||
|
||||
int sact_Effect(int type, possibly_unused int time, possibly_unused int key)
|
||||
{
|
||||
if (type <= 0 || type >= NR_EFFECTS) {
|
||||
WARNING("Invalid or unknown effect: %d", type);
|
||||
return 0;
|
||||
}
|
||||
struct effect_shader *s = effect_shaders[type];
|
||||
if (!s) {
|
||||
WARNING("Unimplemented effect: %s", effect_names[type]);
|
||||
return 0;
|
||||
}
|
||||
// load shader lazily
|
||||
if (!s->s.program) {
|
||||
load_effect_shader(s);
|
||||
}
|
||||
|
||||
// get old & new scene textures
|
||||
Texture old, new;
|
||||
gfx_copy_main_surface(&old);
|
||||
scene_render();
|
||||
gfx_copy_main_surface(&new);
|
||||
|
||||
//effect_callback effect = effects[type];
|
||||
for (int i = 0; i < time; i += 16) {
|
||||
gfx_clear();
|
||||
render_effect_shader(s, &old, &new, (float)i / (float)time);
|
||||
gfx_swap();
|
||||
SDL_Delay(16);
|
||||
}
|
||||
|
||||
gfx_delete_texture(&old);
|
||||
gfx_delete_texture(&new);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct {
|
||||
bool on;
|
||||
int type;
|
||||
Texture old;
|
||||
Texture new;
|
||||
} trans = {0};
|
||||
} effect = {0};
|
||||
|
||||
static void effect_fadeout(float rate)
|
||||
{
|
||||
gfx_copy_bright(gfx_main_surface(), 0, 0, &effect.old, 0, 0,
|
||||
effect.old.w, effect.old.h, (1.0f - rate) * 255);
|
||||
}
|
||||
|
||||
static void effect_fadein(float rate)
|
||||
{
|
||||
gfx_copy_bright(gfx_main_surface(), 0, 0, &effect.new, 0, 0,
|
||||
effect.new.w, effect.new.h, rate * 255);
|
||||
}
|
||||
|
||||
static void effect_whiteout(float rate)
|
||||
{
|
||||
Texture *dst = gfx_main_surface();
|
||||
gfx_fill(dst, 0, 0, dst->w, dst->h, 255, 255, 255);
|
||||
gfx_blend(dst, 0, 0, &effect.old, 0, 0, dst->w, dst->h, (1.0f - rate) * 255);
|
||||
}
|
||||
|
||||
static void effect_whitein(float rate)
|
||||
{
|
||||
Texture *dst = gfx_main_surface();
|
||||
gfx_fill(dst, 0, 0, dst->w, dst->h, 255, 255, 255);
|
||||
gfx_blend(dst, 0, 0, &effect.new, 0, 0, dst->w, dst->h, rate * 255);
|
||||
}
|
||||
|
||||
static void effect_zoom_lr(float rate)
|
||||
{
|
||||
Texture *dst = gfx_main_surface();
|
||||
unsigned x_pos = roundf(dst->w * rate);
|
||||
gfx_copy_stretch(dst, x_pos, 0, dst->w-x_pos, dst->h, &effect.old, 0, 0, effect.old.w, effect.old.h);
|
||||
gfx_copy_stretch(dst, 0, 0, x_pos, dst->h, &effect.new, 0, 0, effect.new.w, effect.new.h);
|
||||
}
|
||||
|
||||
static void effect_zoom_rl(float rate)
|
||||
{
|
||||
Texture *dst = gfx_main_surface();
|
||||
unsigned x_pos = dst->w - roundf(dst->w * rate);
|
||||
gfx_copy_stretch(dst, x_pos, 0, dst->w-x_pos, dst->h, &effect.new, 0, 0, effect.new.w, effect.new.h);
|
||||
gfx_copy_stretch(dst, 0, 0, x_pos, dst->h, &effect.old, 0, 0, effect.old.w, effect.old.h);
|
||||
}
|
||||
|
||||
static void effect_oscillate(float rate)
|
||||
{
|
||||
Texture *dst = gfx_main_surface();
|
||||
int delta_x = (rand() % (dst->w / 10) - (dst->w / 20)) * (1.0f - rate);
|
||||
int delta_y = (rand() % (dst->h / 10) - (dst->h / 20)) * (1.0f - rate);;
|
||||
|
||||
gfx_copy(dst, 0, 0, &effect.old, 0, 0, dst->w, dst->h);
|
||||
gfx_copy(dst, delta_x, delta_y, &effect.new, 0, 0, dst->w, dst->h);
|
||||
}
|
||||
|
||||
static void effect_tv_switch(float rate, Texture *src)
|
||||
{
|
||||
Texture *dst = gfx_main_surface();
|
||||
if (rate < 0.5f) {
|
||||
int h = max(1, (int)(dst->h * (1.0f - rate * 2.0f)));
|
||||
gfx_copy_stretch(dst, 0, dst->h * rate, dst->w, h,
|
||||
src, 0, 0, src->w, src->h);
|
||||
} else {
|
||||
rate -= 0.5f;
|
||||
int w = dst->w * (1.0f - rate * 2.0f);
|
||||
gfx_copy_stretch(dst, dst->w * rate, dst->h / 2, w, 1,
|
||||
src, 0, 0, src->w, src->h);
|
||||
}
|
||||
}
|
||||
|
||||
static void effect_tv_switch_off(float rate)
|
||||
{
|
||||
effect_tv_switch(rate, &effect.old);
|
||||
}
|
||||
|
||||
static void effect_tv_switch_on(float rate)
|
||||
{
|
||||
effect_tv_switch(1.0f - rate, &effect.new);
|
||||
}
|
||||
|
||||
static void effect_zoom_in_crossfade(float rate)
|
||||
{
|
||||
Texture *dst = gfx_main_surface();
|
||||
float scale = 1.0f + powf(rate, 4.0f) * 16.0f;
|
||||
int w = roundf(dst->w * scale);
|
||||
int h = roundf(dst->h * scale);
|
||||
int x = -((w - dst->w)/2);
|
||||
int y = -((h - dst->h)/2);
|
||||
int a = (1.0f - rate) * 255;
|
||||
|
||||
gfx_copy(dst, 0, 0, &effect.new, 0, 0, dst->w, dst->h);
|
||||
gfx_copy_stretch_blend(dst, x, y, w, h, &effect.old, 0, 0, dst->w, dst->h, a);
|
||||
}
|
||||
|
||||
typedef void (*effect_fun)(float rate);
|
||||
static effect_fun effect_functions[NR_EFFECTS] = {
|
||||
[EFFECT_FADEOUT] = effect_fadeout,
|
||||
[EFFECT_FADEIN] = effect_fadein,
|
||||
[EFFECT_WHITEOUT] = effect_whiteout,
|
||||
[EFFECT_WHITEIN] = effect_whitein,
|
||||
[EFFECT_OSCILLATE] = effect_oscillate,
|
||||
[EFFECT_TV_SWITCH_OFF] = effect_tv_switch_off,
|
||||
[EFFECT_TV_SWITCH_ON] = effect_tv_switch_on,
|
||||
[EFFECT_ZOOM_LR] = effect_zoom_lr,
|
||||
[EFFECT_ZOOM_RL] = effect_zoom_rl,
|
||||
[EFFECT_ZOOM_IN_CROSSFADE] = effect_zoom_in_crossfade,
|
||||
};
|
||||
|
||||
int sact_TRANS_Begin(int type)
|
||||
{
|
||||
@@ -199,39 +280,72 @@ int sact_TRANS_Begin(int type)
|
||||
WARNING("Invalid or unknown effect: %d", type);
|
||||
return 0;
|
||||
}
|
||||
struct effect_shader *s = effect_shaders[type];
|
||||
if (!s) {
|
||||
WARNING("Unimplemented effect: %s", effect_names[type]);
|
||||
return 0;
|
||||
|
||||
if (effect_functions[type]) {
|
||||
// nothing to do
|
||||
}
|
||||
// load shader lazily
|
||||
if (!s->s.program) {
|
||||
load_effect_shader(s);
|
||||
else {
|
||||
struct effect_shader *s = effect_shaders[type];
|
||||
if (!s) {
|
||||
WARNING("Unimplemented effect: %s", effect_names[type]);
|
||||
type = EFFECT_BLUR_CROSSFADE;
|
||||
s = effect_shaders[type];
|
||||
}
|
||||
// load shader lazily
|
||||
if (!s->s.program) {
|
||||
load_effect_shader(s);
|
||||
}
|
||||
}
|
||||
|
||||
trans.on = true;
|
||||
trans.type = type;
|
||||
gfx_copy_main_surface(&trans.old);
|
||||
effect.on = true;
|
||||
effect.type = type;
|
||||
gfx_copy_main_surface(&effect.old);
|
||||
scene_render();
|
||||
gfx_copy_main_surface(&trans.new);
|
||||
gfx_copy_main_surface(&effect.new);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sact_TRANS_Update(float rate)
|
||||
{
|
||||
if (!trans.on) {
|
||||
if (!effect.on) {
|
||||
return 0;
|
||||
}
|
||||
gfx_clear();
|
||||
render_effect_shader(effect_shaders[trans.type], &trans.old, &trans.new, rate);
|
||||
if (effect_functions[effect.type]) {
|
||||
effect_functions[effect.type](rate);
|
||||
} else {
|
||||
render_effect_shader(effect_shaders[effect.type], &effect.old, &effect.new, rate);
|
||||
}
|
||||
gfx_swap();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sact_TRANS_End(void)
|
||||
{
|
||||
trans.on = false;
|
||||
gfx_delete_texture(&trans.old);
|
||||
gfx_delete_texture(&trans.new);
|
||||
effect.on = false;
|
||||
gfx_delete_texture(&effect.old);
|
||||
gfx_delete_texture(&effect.new);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sact_Effect(int type, int time, possibly_unused int key)
|
||||
{
|
||||
uint32_t start = SDL_GetTicks();
|
||||
if (!sact_TRANS_Begin(type))
|
||||
return 0;
|
||||
|
||||
uint32_t t = SDL_GetTicks() - start;
|
||||
while (t < time) {
|
||||
sact_TRANS_Update((float)t / (float)time);
|
||||
uint32_t t2 = SDL_GetTicks() - start;
|
||||
if (t2 < t + 16) {
|
||||
SDL_Delay(t + 16 - t2);
|
||||
t += 16;
|
||||
} else {
|
||||
t = t2;
|
||||
}
|
||||
}
|
||||
|
||||
sact_TRANS_End();
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "vm.h"
|
||||
#include "vm/heap.h"
|
||||
#include "vm/page.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
#define HLL_MAX_ARGS 64
|
||||
|
||||
@@ -57,6 +58,7 @@ bool library_function_exists(int libno, int fno)
|
||||
static void trace_hll_call(struct ain_library *lib, struct ain_hll_function *f,
|
||||
struct hll_function *fun, union vm_value *r, void *_args)
|
||||
{
|
||||
/*
|
||||
// list of traced libraries
|
||||
if (!strcmp(lib->name, "StoatSpriteEngine") || !strcmp(lib->name, "ChipmunkSpriteEngine")) {
|
||||
// list of excluded functions
|
||||
@@ -86,16 +88,22 @@ static void trace_hll_call(struct ain_library *lib, struct ain_hll_function *f,
|
||||
}
|
||||
//else if (!strcmp(lib->name, "DrawGraph"));
|
||||
else if (!strcmp(lib->name, "SACT2")) {
|
||||
if (strncmp(f->name, "SP_", 3)) goto notrace;
|
||||
if (!strcmp(f->name, "Key_IsDown")) goto notrace;
|
||||
if (!strcmp(f->name, "Mouse_GetPos")) goto notrace;
|
||||
if (!strcmp(f->name, "SP_ExistAlpha")) goto notrace;
|
||||
if (!strcmp(f->name, "SP_IsPtInRect")) goto notrace;
|
||||
if (!strcmp(f->name, "SP_IsPtIn")) goto notrace;
|
||||
if (!strcmp(f->name, "SP_IsUsing")) goto notrace;
|
||||
if (!strcmp(f->name, "SP_SetShow")) goto notrace;
|
||||
if (!strcmp(f->name, "SP_SetPos")) goto notrace;
|
||||
if (!strcmp(f->name, "Timer_Get")) goto notrace;
|
||||
if (!strcmp(f->name, "Update")) goto notrace;
|
||||
}
|
||||
else if (!strcmp(lib->name, "SengokuRanceFont"));
|
||||
else*/ if (!strcmp(lib->name, "SengokuRanceFont")) {
|
||||
if (!strcmp(f->name, "SP_SetReduceDescender")) goto notrace;
|
||||
if (!strcmp(f->name, "SP_ClearState")) goto notrace;
|
||||
}
|
||||
else goto notrace;
|
||||
|
||||
sys_message("(%s) ", display_sjis0(ain->functions[call_stack[call_stack_ptr-1].fno].name));
|
||||
@@ -290,21 +298,28 @@ extern struct static_library lib_AliceLogo5;
|
||||
extern struct static_library lib_AnteaterADVEngine;
|
||||
extern struct static_library lib_BanMisc;
|
||||
extern struct static_library lib_Bitarray;
|
||||
extern struct static_library lib_CGManager;
|
||||
extern struct static_library lib_ChipmunkSpriteEngine;
|
||||
extern struct static_library lib_ChrLoader;
|
||||
extern struct static_library lib_CommonSystemData;
|
||||
extern struct static_library lib_Confirm;
|
||||
extern struct static_library lib_Confirm2;
|
||||
extern struct static_library lib_Confirm3;
|
||||
extern struct static_library lib_CrayfishLogViewer;
|
||||
extern struct static_library lib_Cursor;
|
||||
extern struct static_library lib_Data;
|
||||
extern struct static_library lib_DataFile;
|
||||
extern struct static_library lib_DrawDungeon;
|
||||
extern struct static_library lib_DrawDungeon14;
|
||||
extern struct static_library lib_DrawGraph;
|
||||
extern struct static_library lib_DrawMovie;
|
||||
extern struct static_library lib_DrawMovie2;
|
||||
extern struct static_library lib_DrawPluginManager;
|
||||
extern struct static_library lib_DrawSimpleText;
|
||||
extern struct static_library lib_File;
|
||||
extern struct static_library lib_File2;
|
||||
extern struct static_library lib_FileOperation;
|
||||
extern struct static_library lib_FillAngle;
|
||||
extern struct static_library lib_GoatGUIEngine;
|
||||
extern struct static_library lib_Gpx2Plus;
|
||||
extern struct static_library lib_GUIEngine;
|
||||
@@ -312,11 +327,14 @@ 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_MainEXFile;
|
||||
extern struct static_library lib_MainSurface;
|
||||
extern struct static_library lib_MarmotModelEngine;
|
||||
extern struct static_library lib_Math;
|
||||
extern struct static_library lib_MapLoader;
|
||||
extern struct static_library lib_MenuMsg;
|
||||
extern struct static_library lib_MonsterInfo;
|
||||
extern struct static_library lib_MsgLogManager;
|
||||
extern struct static_library lib_MsgLogViewer;
|
||||
extern struct static_library lib_MsgSkip;
|
||||
@@ -324,14 +342,17 @@ extern struct static_library lib_OutputLog;
|
||||
extern struct static_library lib_PassRegister;
|
||||
extern struct static_library lib_PlayDemo;
|
||||
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_SengokuRanceFont;
|
||||
extern struct static_library lib_SoundFilePlayer;
|
||||
extern struct static_library lib_StoatSpriteEngine;
|
||||
extern struct static_library lib_StretchHelper;
|
||||
extern struct static_library lib_SystemService;
|
||||
extern struct static_library lib_SystemServiceEx;
|
||||
extern struct static_library lib_Timer;
|
||||
extern struct static_library lib_Toushin3Loader;
|
||||
extern struct static_library lib_VSFile;
|
||||
|
||||
static struct static_library *static_libraries[] = {
|
||||
@@ -345,21 +366,28 @@ static struct static_library *static_libraries[] = {
|
||||
&lib_AnteaterADVEngine,
|
||||
&lib_BanMisc,
|
||||
&lib_Bitarray,
|
||||
&lib_CGManager,
|
||||
&lib_ChipmunkSpriteEngine,
|
||||
&lib_ChrLoader,
|
||||
&lib_CommonSystemData,
|
||||
&lib_Confirm,
|
||||
&lib_Confirm2,
|
||||
&lib_Confirm3,
|
||||
&lib_CrayfishLogViewer,
|
||||
&lib_Cursor,
|
||||
&lib_Data,
|
||||
&lib_DataFile,
|
||||
&lib_DrawDungeon,
|
||||
&lib_DrawDungeon14,
|
||||
&lib_DrawGraph,
|
||||
&lib_DrawMovie,
|
||||
&lib_DrawMovie2,
|
||||
&lib_DrawPluginManager,
|
||||
&lib_DrawSimpleText,
|
||||
&lib_File,
|
||||
&lib_File2,
|
||||
&lib_FileOperation,
|
||||
&lib_FillAngle,
|
||||
&lib_GoatGUIEngine,
|
||||
&lib_Gpx2Plus,
|
||||
&lib_GUIEngine,
|
||||
@@ -367,11 +395,14 @@ static struct static_library *static_libraries[] = {
|
||||
&lib_InputDevice,
|
||||
&lib_InputString,
|
||||
&lib_KiwiSoundEngine,
|
||||
&lib_LoadCG,
|
||||
&lib_MainEXFile,
|
||||
&lib_MainSurface,
|
||||
&lib_MarmotModelEngine,
|
||||
&lib_Math,
|
||||
&lib_MapLoader,
|
||||
&lib_MenuMsg,
|
||||
&lib_MonsterInfo,
|
||||
&lib_MsgLogManager,
|
||||
&lib_MsgLogViewer,
|
||||
&lib_MsgSkip,
|
||||
@@ -379,14 +410,17 @@ static struct static_library *static_libraries[] = {
|
||||
&lib_PassRegister,
|
||||
&lib_PlayDemo,
|
||||
&lib_PlayMovie,
|
||||
&lib_ReignEngine,
|
||||
&lib_SACT2,
|
||||
&lib_SACTDX,
|
||||
&lib_SengokuRanceFont,
|
||||
&lib_SoundFilePlayer,
|
||||
&lib_StoatSpriteEngine,
|
||||
&lib_StretchHelper,
|
||||
&lib_SystemService,
|
||||
&lib_SystemServiceEx,
|
||||
&lib_Timer,
|
||||
&lib_Toushin3Loader,
|
||||
&lib_VSFile,
|
||||
NULL
|
||||
};
|
||||
@@ -451,16 +485,32 @@ static struct hll_function *link_static_library(struct ain_library *ainlib, stru
|
||||
ERROR("Too many arguments to library function: %s", ainlib->functions[i].name);
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
static void library_run(struct static_library *lib, const char *name)
|
||||
{
|
||||
for (int i = 0; lib->functions[i].name; i++) {
|
||||
if (!strcmp(lib->functions[i].name, "_ModuleInit")) {
|
||||
if (!strcmp(lib->functions[i].name, name)) {
|
||||
((void(*)(void))lib->functions[i].fun)();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
void link_libraries(void)
|
||||
static void library_run_all(const char *name)
|
||||
{
|
||||
for (int i = 0; i < ain->nr_libraries; i++) {
|
||||
for (int j = 0; static_libraries[j]; j++) {
|
||||
if (!strcmp(ain->libraries[i].name, static_libraries[j]->name)) {
|
||||
library_run(static_libraries[j], name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void link_libraries(void)
|
||||
{
|
||||
if (libraries)
|
||||
return;
|
||||
@@ -479,24 +529,25 @@ void link_libraries(void)
|
||||
}
|
||||
}
|
||||
|
||||
void library_fini(struct static_library *lib)
|
||||
void init_libraries(void)
|
||||
{
|
||||
for (int i = 0; lib->functions[i].name; i++) {
|
||||
if (!strcmp(lib->functions[i].name, "_ModuleFini")) {
|
||||
((void(*)(void))lib->functions[i].fun)();
|
||||
break;
|
||||
}
|
||||
}
|
||||
library_run_all("_PreLink");
|
||||
link_libraries();
|
||||
library_run_all("_ModuleInit");
|
||||
}
|
||||
|
||||
void exit_libraries(void)
|
||||
{
|
||||
for (int i = 0; i < ain->nr_libraries; i++) {
|
||||
for (int j = 0; static_libraries[j]; j++) {
|
||||
if (!strcmp(ain->libraries[i].name, static_libraries[j]->name)) {
|
||||
library_fini(static_libraries[j]);
|
||||
break;
|
||||
}
|
||||
library_run_all("_ModuleFini");
|
||||
}
|
||||
|
||||
void static_library_replace(struct static_library *lib, const char *name, void *fun)
|
||||
{
|
||||
for (int i = 0; lib->functions[i].name; i++) {
|
||||
if (!strcmp(lib->functions[i].name, name)) {
|
||||
lib->functions[i].fun = fun;
|
||||
return;
|
||||
}
|
||||
}
|
||||
ERROR("No library function '%s.%s'", lib->name, name);
|
||||
}
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
#include "gfx/gfx.h"
|
||||
#include "gfx/types.h"
|
||||
#include "system4.h"
|
||||
#include "system4/fnl.h"
|
||||
#include "system4/utfsjis.h"
|
||||
#include "gfx/gfx.h"
|
||||
|
||||
static void set_pixel(uint8_t *p, int w, int h, int x, int y, SDL_Color c)
|
||||
{
|
||||
if (x >= w || y >= h)
|
||||
return;
|
||||
p[y*w*4 + x*4 + 0] = c.r;
|
||||
p[y*w*4 + x*4 + 1] = c.g;
|
||||
p[y*w*4 + x*4 + 2] = c.b;
|
||||
p[y*w*4 + x*4 + 3] = 255;
|
||||
}
|
||||
|
||||
static void render_outline(struct text_style *ts, uint8_t *surf, uint8_t *data, unsigned long data_size, int x, int w, int h)
|
||||
{
|
||||
const int span = w * 4;
|
||||
const int glyph_w = (data_size*8) / h;
|
||||
const int size = ts->font_size->denominator * ts->edge_width;
|
||||
|
||||
|
||||
uint32_t p = 0;
|
||||
for (int row = h - 1; row >= 0; row--) {
|
||||
uint8_t *pos = surf + row*span + x*4;
|
||||
for (int col = 0; col < glyph_w; col++, p++, pos += 4) {
|
||||
bool on = data[p/8] & (1 << (7 - p % 8));
|
||||
if (!on)
|
||||
continue;
|
||||
int start_x = x + col - size;
|
||||
int start_y = row - size;
|
||||
for (int ol_row = 0; ol_row < size*2; ol_row++) {
|
||||
for (int ol_col = 0; ol_col < size*2; ol_col++) {
|
||||
set_pixel(surf, w, h, start_x + ol_col, start_y + ol_row, ts->edge_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: cache glyph textures, render directly to dst texture
|
||||
static void render_glyph(struct fnl_font_face *font, struct text_style *ts, uint8_t *surf, struct fnl_glyph *glyph, int x, int w, int h)
|
||||
{
|
||||
unsigned long data_size;
|
||||
uint8_t *data = fnl_glyph_data(font->font->fnl, glyph, &data_size);
|
||||
|
||||
const int span = w * 4;
|
||||
const int glyph_w = (data_size*8) / h;
|
||||
|
||||
if (ts->edge_width)
|
||||
render_outline(ts, surf, data, data_size, x, w, h);
|
||||
|
||||
uint32_t p = 0;
|
||||
for (int row = h - 1; row >= 0; row--) {
|
||||
uint8_t *pos = surf + row*span + x*4;
|
||||
for (int col = 0; col < glyph_w; col++, p++) {
|
||||
bool on = data[p/8] & (1 << (7 - p % 8));
|
||||
if (on) {
|
||||
set_pixel(surf, w, h, x+col, row, ts->color);
|
||||
}
|
||||
pos += 4;
|
||||
}
|
||||
}
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void render_text(struct fnl_font_face *font, struct text_style *ts, Texture *dst, char *text)
|
||||
{
|
||||
// create array of fnl_glyph
|
||||
const int height = font->height;
|
||||
int len = sjis_count_char(text);
|
||||
struct fnl_glyph **glyphs = xcalloc(len, sizeof(struct fnl_glyph*));
|
||||
for (int i = 0; i < len; i++) {
|
||||
glyphs[i] = fnl_get_glyph(font, sjis_code(text));
|
||||
text = sjis_skip_char(text);
|
||||
}
|
||||
|
||||
// calculate width/height of text surface
|
||||
int width = 0;
|
||||
for (int i = 0; i < len; i++) {
|
||||
width += glyphs[i]->real_width;
|
||||
}
|
||||
|
||||
// allocate memory for surface
|
||||
uint8_t *surf = xcalloc(4, width * height);
|
||||
|
||||
// render glyphs to surface
|
||||
int x = 0;
|
||||
for (int i = 0; i < len; i++) {
|
||||
render_glyph(font, ts, surf, glyphs[i], x, width, height);
|
||||
x += glyphs[i]->real_width;
|
||||
}
|
||||
|
||||
// create texture from surface
|
||||
gfx_init_texture_with_pixels(dst, width, height, surf);
|
||||
free(surf);
|
||||
free(glyphs);
|
||||
}
|
||||
|
||||
static void draw_text(Texture *dst, Texture *glyph, int x, int y, float scale)
|
||||
{
|
||||
GLuint fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, dst, x, y, glyph->w, glyph->h);
|
||||
|
||||
mat4 mw_transform = MAT4(
|
||||
glyph->w * scale, 0, 0, 0,
|
||||
0, glyph->h * scale, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1);
|
||||
mat4 wv_transform = WV_TRANSFORM(glyph->w, glyph->h);
|
||||
struct gfx_render_job job = {
|
||||
.texture = glyph->handle,
|
||||
.world_transform = mw_transform[0],
|
||||
.view_transform = wv_transform[0],
|
||||
.data = glyph
|
||||
};
|
||||
gfx_render(&job);
|
||||
|
||||
gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo);
|
||||
}
|
||||
|
||||
int fnl_draw_text(struct fnl *fnl, struct text_style *ts, Texture *dst, int x, int y, char *text)
|
||||
{
|
||||
Texture rendered;
|
||||
struct fnl_font_face *font = &fnl->fonts[ts->font_type].faces[ts->font_size->face];
|
||||
render_text(font, ts, &rendered, text);
|
||||
|
||||
//glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
|
||||
draw_text(dst, &rendered, x, y, 1.0 / ts->font_size->denominator);
|
||||
//glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
||||
|
||||
int w = rendered.w / (float)ts->font_size->denominator;
|
||||
gfx_delete_texture(&rendered);
|
||||
return w;
|
||||
}
|
||||
+235
@@ -0,0 +1,235 @@
|
||||
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/fnl.h"
|
||||
#include "system4/hashtable.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/utfsjis.h"
|
||||
|
||||
#include "gfx/gfx.h"
|
||||
#include "gfx/font.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
struct fnl_bitmap_glyph {
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned advance;
|
||||
uint8_t *pixels;
|
||||
};
|
||||
|
||||
struct fnl_bitmap_size {
|
||||
struct fnl_font_face *face;
|
||||
unsigned nr_glyphs;
|
||||
struct fnl_bitmap_glyph **glyphs;
|
||||
};
|
||||
|
||||
struct fnl_font_size {
|
||||
struct font_size super;
|
||||
struct fnl_bitmap_size *bitmap_size;
|
||||
unsigned denominator;
|
||||
};
|
||||
|
||||
struct font_fnl {
|
||||
struct font super;
|
||||
struct fnl_font *font;
|
||||
unsigned nr_bitmap_sizes;
|
||||
struct fnl_bitmap_size *bitmap_sizes;
|
||||
unsigned nr_sizes;
|
||||
struct fnl_font_size *sizes;
|
||||
};
|
||||
|
||||
static struct font_size *fnl_font_get_size(struct font *_font, float size)
|
||||
{
|
||||
struct font_fnl *font = (struct font_fnl*)_font;
|
||||
|
||||
float min_diff = 9999;
|
||||
struct fnl_font_size *closest = &font->sizes[0];
|
||||
for (unsigned i = 0; i < font->nr_sizes; i++) {
|
||||
float diff = fabsf(font->sizes[i].super.size - size);
|
||||
if (diff < min_diff) {
|
||||
min_diff = diff;
|
||||
closest = &font->sizes[i];
|
||||
}
|
||||
}
|
||||
return &closest->super;
|
||||
}
|
||||
|
||||
static struct font_size *fnl_font_get_size_round_down(struct font *_font, float size)
|
||||
{
|
||||
struct font_fnl *font = (struct font_fnl*)_font;
|
||||
|
||||
float min_diff = 9999;
|
||||
struct fnl_font_size *closest = &font->sizes[0];
|
||||
for (unsigned i = 0; i < font->nr_sizes; i++) {
|
||||
if (font->sizes[i].super.size > size)
|
||||
continue;
|
||||
float diff = fabsf(font->sizes[i].super.size - size);
|
||||
if (diff < min_diff) {
|
||||
min_diff = diff;
|
||||
closest = &font->sizes[i];
|
||||
}
|
||||
}
|
||||
return &closest->super;
|
||||
}
|
||||
|
||||
static float fnl_font_get_actual_size(struct font *font, float size)
|
||||
{
|
||||
return fnl_font_get_size(font, size)->size;
|
||||
}
|
||||
|
||||
static float fnl_font_get_actual_size_round_down(struct font *font, float size)
|
||||
{
|
||||
return fnl_font_get_size_round_down(font, size)->size;
|
||||
}
|
||||
|
||||
static struct fnl_bitmap_glyph *fnl_get_bitmap_glyph(struct fnl_bitmap_size *bitmap, unsigned index)
|
||||
{
|
||||
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);
|
||||
|
||||
const int glyph_w = (data_size*8) / bitmap->face->height;
|
||||
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);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
free(data);
|
||||
bitmap->glyphs[index] = out;
|
||||
return out;
|
||||
}
|
||||
|
||||
#define GLYPH_BORDER_SIZE 4
|
||||
|
||||
static bool fnl_font_get_glyph(struct font_size *_size, struct glyph *glyph, uint32_t code, enum font_weight weight)
|
||||
{
|
||||
struct fnl_font_size *size = (struct fnl_font_size*)_size;
|
||||
unsigned index = fnl_char_to_index(code);
|
||||
if (index > size->bitmap_size->nr_glyphs)
|
||||
return false;
|
||||
if (!size->bitmap_size->face->glyphs[index].data_pos)
|
||||
return false;
|
||||
|
||||
struct fnl_bitmap_glyph *fullsize = fnl_get_bitmap_glyph(size->bitmap_size, index);
|
||||
const unsigned block_width = fullsize->width / size->denominator;
|
||||
const unsigned block_height = fullsize->height / size->denominator;
|
||||
const unsigned width = block_width + GLYPH_BORDER_SIZE*2;
|
||||
const unsigned height = block_height + GLYPH_BORDER_SIZE*2;
|
||||
const unsigned off_x = GLYPH_BORDER_SIZE;
|
||||
const unsigned off_y = GLYPH_BORDER_SIZE;
|
||||
|
||||
// Sample each pixel in size->denominator`-sized blocks and compute the average.
|
||||
// TODO: no need to sample every pixel; 4 should be fine?
|
||||
uint8_t *pixels = xcalloc(1, width * height);
|
||||
unsigned *acc = xcalloc(block_width * block_height, sizeof(unsigned));
|
||||
for (unsigned i = 0; i < block_width * block_height; i++) {
|
||||
unsigned dst_row = i / block_width;
|
||||
unsigned dst_col = i % block_width;
|
||||
for (unsigned r = 0; r < size->denominator; r++) {
|
||||
unsigned src_row = dst_row * size->denominator + r;
|
||||
for (unsigned c = 0; c < size->denominator; c++) {
|
||||
unsigned src_col = dst_col * size->denominator + c;
|
||||
acc[i] += fullsize->pixels[src_row*fullsize->width + src_col];
|
||||
}
|
||||
}
|
||||
uint8_t p = acc[i] / (size->denominator * size->denominator);
|
||||
pixels[(dst_row+off_y)*width + (dst_col+off_x)] = p;
|
||||
}
|
||||
|
||||
gfx_init_texture_rmap(&glyph->t[weight], width, height, pixels);
|
||||
glyph->rect.x = off_x;
|
||||
glyph->rect.y = off_y;
|
||||
glyph->rect.w = block_width;
|
||||
glyph->rect.h = block_height;
|
||||
glyph->advance = fullsize->advance / (float)size->denominator;
|
||||
|
||||
free(pixels);
|
||||
free(acc);
|
||||
return true;
|
||||
}
|
||||
|
||||
static float fnl_font_size_char(struct font_size *_size, uint32_t code)
|
||||
{
|
||||
struct fnl_font_size *size = (struct fnl_font_size*)_size;
|
||||
unsigned index = fnl_char_to_index(code);
|
||||
if (index > size->bitmap_size->nr_glyphs)
|
||||
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;
|
||||
if (code == '\t')
|
||||
r *= 2.88;
|
||||
return r;
|
||||
}
|
||||
|
||||
struct font *fnl_font_load(struct fnl *lib, unsigned index)
|
||||
{
|
||||
if (index >= lib->nr_fonts)
|
||||
return NULL;
|
||||
|
||||
struct fnl_font *lib_font = &lib->fonts[index];
|
||||
struct font_fnl *font = xcalloc(1, sizeof(struct font_fnl));
|
||||
|
||||
// initialize bitmap sizes
|
||||
font->nr_bitmap_sizes = lib_font->nr_faces;
|
||||
font->bitmap_sizes = xcalloc(font->nr_bitmap_sizes, sizeof(struct fnl_bitmap_size));
|
||||
for (unsigned i = 0; i < font->nr_bitmap_sizes; i++) {
|
||||
struct fnl_bitmap_size *bitsize = &font->bitmap_sizes[i];
|
||||
bitsize->face = &lib_font->faces[i];
|
||||
bitsize->nr_glyphs = bitsize->face->nr_glyphs;
|
||||
bitsize->glyphs = xcalloc(bitsize->nr_glyphs, sizeof(struct fnl_bitmap_glyph*));
|
||||
}
|
||||
|
||||
// initialize render sizes (downscaled)
|
||||
font->nr_sizes = font->nr_bitmap_sizes * 12;
|
||||
font->sizes = xcalloc(font->nr_sizes, sizeof(struct fnl_font_size));
|
||||
for (unsigned i = 0; i < font->nr_bitmap_sizes; i++) {
|
||||
struct fnl_bitmap_size *bitsize = &font->bitmap_sizes[i];
|
||||
struct fnl_font_size *sizes = &font->sizes[i*12];
|
||||
for (unsigned i = 0; i < 12; i++) {
|
||||
sizes[i].bitmap_size = bitsize;
|
||||
sizes[i].super.size = bitsize->face->height / (float)(i+1);
|
||||
sizes[i].super.font = &font->super;
|
||||
sizes[i].denominator = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
font->super.charmap = CHARMAP_SJIS;
|
||||
font->super.get_size = fnl_font_get_size;
|
||||
font->super.get_actual_size = fnl_font_get_actual_size;
|
||||
font->super.get_actual_size_round_down = fnl_font_get_actual_size_round_down;
|
||||
font->super.get_glyph = fnl_font_get_glyph;
|
||||
font->super.size_char = fnl_font_size_char;
|
||||
|
||||
if (!game_rance7_mg)
|
||||
gfx_text_advance_edges = true;
|
||||
|
||||
return &font->super;
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_BITMAP_H
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/hashtable.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/utfsjis.h"
|
||||
|
||||
#include "gfx/gfx.h"
|
||||
#include "gfx/font.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
#define MAX_FONT_SIZES 64
|
||||
|
||||
struct font_ft {
|
||||
struct font super;
|
||||
FT_Face font;
|
||||
unsigned current_size;
|
||||
unsigned nr_sizes;
|
||||
struct font_size sizes[MAX_FONT_SIZES];
|
||||
};
|
||||
|
||||
static FT_Library ft_lib;
|
||||
|
||||
static bool is_half_width(uint32_t code)
|
||||
{
|
||||
// ASCII characters
|
||||
if (code < 128)
|
||||
return true;
|
||||
// Latin letters
|
||||
if (0xbf <= code && code <= 0xff && code != 0xd7 && code != 0xf7)
|
||||
return true;
|
||||
// Halfwidth punctuation / katakana
|
||||
if (0xff61 <= code && code <= 0xff9f)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void ft_font_init(void)
|
||||
{
|
||||
if (FT_Init_FreeType(&ft_lib)) {
|
||||
ERROR("Failed to initialize FreeType");
|
||||
}
|
||||
}
|
||||
|
||||
static struct font_size *ft_font_get_size(struct font *_font, float size)
|
||||
{
|
||||
struct font_ft *font = (struct font_ft*)_font;
|
||||
size = roundf(size);
|
||||
for (int i = 0; i < font->nr_sizes; i++) {
|
||||
if (fabsf(font->sizes[i].size - size) < 0.01) {
|
||||
return &font->sizes[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (font->nr_sizes + 1 >= MAX_FONT_SIZES)
|
||||
ERROR("Exceeded maximum number of font sizes");
|
||||
struct font_size *fs = &font->sizes[font->nr_sizes++];
|
||||
fs->size = size;
|
||||
fs->y_offset = -roundf(size * 0.15f);
|
||||
fs->font = _font;
|
||||
fs->glyph_table = ht_create(4096);
|
||||
return fs;
|
||||
}
|
||||
|
||||
static float ft_font_get_actual_size(struct font *_, float size)
|
||||
{
|
||||
return roundf(size);
|
||||
}
|
||||
|
||||
// NOTE: we create glyph textures slightly larger than necessary so that outlines
|
||||
// aren't cut off at the texture boundary.
|
||||
// FIXME: the outline rendering code should be fixed so this isn't necessary.
|
||||
#define GLYPH_BORDER_SIZE 4
|
||||
|
||||
// Convert a glyph rendered by FreeType to a block-sized texture.
|
||||
// Block size is size x 1.5*size (full-width) or size/2 x 1.5*size (half-width)
|
||||
static Rectangle init_glyph_texture(Texture *dst, FT_Bitmap *glyph, int bitmap_left, int bitmap_top, int size, bool half_width)
|
||||
{
|
||||
// calculate block size and offsets
|
||||
int block_width = (half_width ? size/2 : size);
|
||||
int block_height = size + size/2;
|
||||
int width = block_width + GLYPH_BORDER_SIZE*2;
|
||||
int height = block_height + GLYPH_BORDER_SIZE*2;
|
||||
int off_x = max(0, bitmap_left) + GLYPH_BORDER_SIZE;
|
||||
int off_y = max(0, size - bitmap_top) + GLYPH_BORDER_SIZE;
|
||||
uint8_t *bitmap = xcalloc(1, width * height);
|
||||
|
||||
// Expand the glyph bitmap to block size
|
||||
for (int row = 0; row < glyph->rows && row + off_y < height; row++) {
|
||||
for (int col = 0; col < glyph->width && col + off_x < width; col++) {
|
||||
uint8_t p = glyph->buffer[row*glyph->width + col];
|
||||
bitmap[(row+off_y)*width + (col+off_x)] = p;
|
||||
}
|
||||
}
|
||||
|
||||
// create texture from block-size bitmap
|
||||
gfx_init_texture_rmap(dst, width, height, bitmap);
|
||||
free(bitmap);
|
||||
|
||||
return (Rectangle) {
|
||||
.x = GLYPH_BORDER_SIZE,
|
||||
.y = GLYPH_BORDER_SIZE,
|
||||
.w = block_width,
|
||||
.h = block_height
|
||||
};
|
||||
}
|
||||
|
||||
static void ft_font_set_size(struct font_ft *font, unsigned size)
|
||||
{
|
||||
if (font->current_size != size && FT_Set_Pixel_Sizes(font->font, 0, size)) {
|
||||
WARNING("Failed setting font size to %u", size);
|
||||
}
|
||||
font->current_size = size;
|
||||
}
|
||||
|
||||
static bool ft_font_get_glyph(struct font_size *size, struct glyph *glyph, uint32_t code, enum font_weight weight)
|
||||
{
|
||||
Texture *t = &glyph->t[weight];
|
||||
|
||||
// render bitmap
|
||||
bool half_width = is_half_width(code);
|
||||
struct font_ft *font = (struct font_ft*)size->font;
|
||||
ft_font_set_size(font, size->size);
|
||||
if (FT_Load_Char(font->font, code, FT_LOAD_RENDER)) {
|
||||
WARNING("Failed to load glyph for codepoint 0x%x", code);
|
||||
return false;
|
||||
}
|
||||
if (weight != FONT_WEIGHT_NORMAL) {
|
||||
int bold_weight = weight == FONT_WEIGHT_HEAVY ? 128 : 64;
|
||||
FT_GlyphSlot_Own_Bitmap(font->font->glyph);
|
||||
FT_Bitmap_Embolden(ft_lib, &font->font->glyph->bitmap, bold_weight, 0);
|
||||
}
|
||||
|
||||
// create texture from bitmap
|
||||
FT_Bitmap *bitmap = &font->font->glyph->bitmap;
|
||||
if (bitmap->pixel_mode == FT_PIXEL_MODE_GRAY) {
|
||||
glyph->rect = init_glyph_texture(t, bitmap, font->font->glyph->bitmap_left,
|
||||
font->font->glyph->bitmap_top, size->size, half_width);
|
||||
} else if (bitmap->pixel_mode == FT_PIXEL_MODE_MONO) {
|
||||
FT_Bitmap tmp;
|
||||
FT_Bitmap_New(&tmp);
|
||||
if (FT_Bitmap_Convert(ft_lib, &font->font->glyph->bitmap, &tmp, 1)) {
|
||||
WARNING("Failed to convert monochrome glyph to grayscale");
|
||||
FT_Bitmap_Done(ft_lib, &tmp);
|
||||
return false;
|
||||
}
|
||||
// round non-zero values up to 255 because FreeType doesn't
|
||||
for (int i = 0; i < tmp.rows * tmp.width; i++) {
|
||||
if (tmp.buffer[i])
|
||||
tmp.buffer[i] = 255;
|
||||
}
|
||||
glyph->rect = init_glyph_texture(t, &tmp, font->font->glyph->bitmap_left,
|
||||
font->font->glyph->bitmap_top, size->size, half_width);
|
||||
FT_Bitmap_Done(ft_lib, &tmp);
|
||||
} else {
|
||||
WARNING("Font returned glyph with unsupported pixel mode");
|
||||
return false;
|
||||
}
|
||||
|
||||
glyph->advance = half_width ? size->size / 2 : size->size;
|
||||
return true;
|
||||
}
|
||||
|
||||
static float ft_font_size_char(struct font_size *size, uint32_t code)
|
||||
{
|
||||
return is_half_width(code) ? size->size / 2 : size->size;
|
||||
}
|
||||
|
||||
struct font *ft_font_load(const char *path)
|
||||
{
|
||||
struct font_ft *font = xcalloc(1, sizeof(struct font_ft));
|
||||
if (FT_New_Face(ft_lib, path, 0, &font->font)) {
|
||||
free(font);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!font->font->charmap) {
|
||||
WARNING("Font '%s' doesn't contain unicode charmap", path);
|
||||
free(font);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
font->super.get_size = ft_font_get_size;
|
||||
font->super.get_actual_size = ft_font_get_actual_size;
|
||||
font->super.get_actual_size_round_down = ft_font_get_actual_size;
|
||||
font->super.get_glyph = ft_font_get_glyph;
|
||||
font->super.size_char = ft_font_size_char;
|
||||
return &font->super;
|
||||
}
|
||||
|
||||
|
||||
+23
-10
@@ -17,6 +17,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "system4.h"
|
||||
#include "system4/ain.h"
|
||||
#include "system4/string.h"
|
||||
#include "vm.h"
|
||||
#include "vm/heap.h"
|
||||
@@ -53,35 +54,43 @@ static int read_number(const char **_fmt)
|
||||
return n;
|
||||
}
|
||||
|
||||
static void parse_fmt_spec(const char **_fmt, struct fmt_spec *spec)
|
||||
static bool parse_fmt_spec(const char **_fmt, struct fmt_spec *spec, enum ain_data_type target)
|
||||
{
|
||||
const char *fmt = *_fmt;
|
||||
bool r = false;
|
||||
const char *fmt = (*_fmt) + 1;
|
||||
memset(spec, 0, sizeof(struct fmt_spec));
|
||||
spec->precision = 6;
|
||||
while (*fmt) {
|
||||
switch (*fmt) {
|
||||
case 'd':
|
||||
spec->type = FMT_INT;
|
||||
r = target == AIN_INT;
|
||||
goto end;
|
||||
case 'D':
|
||||
spec->type = FMT_INT;
|
||||
spec->zenkaku = true;
|
||||
r = target == AIN_INT;
|
||||
goto end;
|
||||
case 'f':
|
||||
spec->type = FMT_FLOAT;
|
||||
r = target == AIN_FLOAT;
|
||||
goto end;
|
||||
case 'F':
|
||||
spec->type = FMT_FLOAT;
|
||||
spec->zenkaku = true;
|
||||
r = target == AIN_FLOAT;
|
||||
goto end;
|
||||
case 's':
|
||||
spec->type = FMT_STRING;
|
||||
r = target == AIN_STRING;
|
||||
goto end;
|
||||
case 'c':
|
||||
spec->type = FMT_CHAR;
|
||||
r = target == AIN_INT;
|
||||
goto end;
|
||||
case 'b':
|
||||
spec->type = FMT_BOOL;
|
||||
r = target == AIN_INT;
|
||||
goto end;
|
||||
case '0':
|
||||
spec->zero_pad = true;
|
||||
@@ -109,7 +118,9 @@ static void parse_fmt_spec(const char **_fmt, struct fmt_spec *spec)
|
||||
warn:
|
||||
WARNING("Invalid format specifier: %s", *_fmt);
|
||||
end:
|
||||
*_fmt = ++fmt;
|
||||
if (r)
|
||||
*_fmt = ++fmt;
|
||||
return r;
|
||||
}
|
||||
|
||||
static void append_fmt(struct string **s, struct fmt_spec *spec, union vm_value arg)
|
||||
@@ -128,7 +139,7 @@ static void append_fmt(struct string **s, struct fmt_spec *spec, union vm_value
|
||||
string_append_cstr(s, buf, len);
|
||||
break;
|
||||
case FMT_STRING:
|
||||
string_append(s, heap[arg.i].s);
|
||||
string_append(s, heap_get_string(arg.i));
|
||||
heap_unref(arg.i);
|
||||
break;
|
||||
case FMT_CHAR:
|
||||
@@ -143,15 +154,17 @@ static void append_fmt(struct string **s, struct fmt_spec *spec, union vm_value
|
||||
}
|
||||
}
|
||||
|
||||
struct string *string_format(struct string *fmt, union vm_value arg)
|
||||
struct string *string_format(struct string *fmt, union vm_value arg, enum ain_data_type type)
|
||||
{
|
||||
struct string *out = string_ref(&EMPTY_STRING);
|
||||
for (const char *s = fmt->text; *s; s++) {
|
||||
if (*s == '%') {
|
||||
struct fmt_spec spec;
|
||||
string_append_cstr(&out, fmt->text, s - fmt->text);
|
||||
s++;
|
||||
parse_fmt_spec(&s, &spec);
|
||||
if (*s != '%')
|
||||
continue;
|
||||
|
||||
size_t size = s - fmt->text;
|
||||
struct fmt_spec spec;
|
||||
if (parse_fmt_spec(&s, &spec, type)) {
|
||||
string_append_cstr(&out, fmt->text, size);
|
||||
append_fmt(&out, &spec, arg);
|
||||
string_append_cstr(&out, s, strlen(s));
|
||||
break;
|
||||
|
||||
+75
-8
@@ -17,19 +17,27 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/ain.h"
|
||||
#include "system4/buffer.h"
|
||||
#include "system4/instructions.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/utfsjis.h"
|
||||
|
||||
#include "gfx/gfx.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
#include "hll/hll.h"
|
||||
|
||||
bool game_daibanchou_en = false;
|
||||
bool game_rance02_mg = false;
|
||||
bool game_rance6_mg = false;
|
||||
bool game_rance7_mg = false;
|
||||
bool game_rance8 = false;
|
||||
|
||||
bool id_indexed_afa = false;
|
||||
|
||||
static void write_instruction0(struct buffer *out, enum opcode op)
|
||||
{
|
||||
@@ -48,9 +56,8 @@ static void write_instruction1(struct buffer *out, enum opcode op, int32_t arg)
|
||||
// about the font. We replace the _CalculateWidth function with a font-generic
|
||||
// implementation.
|
||||
//
|
||||
// This causes quite a bit of text overflow, but there's not much that can be
|
||||
// done about it. Using TTF_SizeUTF8 to calculate the width actually makes it
|
||||
// worse.
|
||||
// '^' character is mapped to 'é' (Rosé)
|
||||
// '}' character is mapped to double-width dash
|
||||
static void apply_rance02_hacks(struct ain *ain)
|
||||
{
|
||||
int fno = ain_get_function(ain, "_CalculateWidth");
|
||||
@@ -60,6 +67,11 @@ static void apply_rance02_hacks(struct ain *ain)
|
||||
}
|
||||
struct ain_function *f = &ain->functions[fno];
|
||||
|
||||
// Reduce the text scale if it wasn't set by the user
|
||||
if (!config.manual_text_x_scale) {
|
||||
config.text_x_scale = 0.9375;
|
||||
}
|
||||
|
||||
// rewrite function
|
||||
struct buffer out;
|
||||
buffer_init(&out, ain->code, ain->code_size);
|
||||
@@ -89,7 +101,8 @@ static void apply_rance02_hacks(struct ain *ain)
|
||||
write_instruction1(&out, SH_LOCALREF, 1);
|
||||
write_instruction1(&out, SH_LOCALREF, 2);
|
||||
write_instruction0(&out, ADD);
|
||||
write_instruction0(&out, RETURN);
|
||||
int jump_addr = out.index;
|
||||
write_instruction1(&out, JUMP, 0);
|
||||
|
||||
// return nFontSize / 2 + nWeightSize;
|
||||
buffer_write_int32_at(&out, ifz_addr + 2, out.index);
|
||||
@@ -98,16 +111,70 @@ static void apply_rance02_hacks(struct ain *ain)
|
||||
write_instruction0(&out, DIV);
|
||||
write_instruction1(&out, SH_LOCALREF, 2);
|
||||
write_instruction0(&out, ADD);
|
||||
write_instruction0(&out, RETURN);
|
||||
|
||||
// multiply by config.text_x_scale (if not 1.0)
|
||||
buffer_write_int32_at(&out, jump_addr + 2, out.index);
|
||||
if (fabsf(1.0f - config.text_x_scale) > 0.01) {
|
||||
union { int32_t i; float f; } cast = { .f = config.text_x_scale };
|
||||
write_instruction0(&out, ITOF);
|
||||
write_instruction1(&out, F_PUSH, cast.i);
|
||||
write_instruction0(&out, F_MUL);
|
||||
write_instruction0(&out, FTOI);
|
||||
}
|
||||
|
||||
write_instruction0(&out, RETURN);
|
||||
write_instruction1(&out, ENDFUNC, fno);
|
||||
|
||||
game_rance02_mg = true;
|
||||
}
|
||||
|
||||
void apply_game_specific_hacks(struct ain *ain)
|
||||
// Rance VI (MangaGamer version)
|
||||
// -----------------------------
|
||||
// 'ノ' character is mapped to 'é' (Rosé)
|
||||
static void apply_rance6_hacks(struct ain *ain)
|
||||
{
|
||||
if (!strcmp(config.game_name, "Rance 02")) {
|
||||
apply_rance02_hacks(ain);
|
||||
if (ain_get_function(ain, "GetTextWidth") < 0)
|
||||
return;
|
||||
game_rance6_mg = true;
|
||||
}
|
||||
|
||||
static void apply_rance7_hacks(struct ain *ain)
|
||||
{
|
||||
if (ain_get_library(ain, "SengokuRanceFont") < 0)
|
||||
return;
|
||||
game_rance7_mg = true;
|
||||
}
|
||||
|
||||
// Daibanchou (English fan translation)
|
||||
// ------------------------------------
|
||||
// Gpx2Plus.CopyStretchReduceAMap behaves differently in order to work around
|
||||
// text rendering issues caused by how the game calculates text width in
|
||||
// StructRegionalSelect::DrawMenuItem.
|
||||
static void apply_daibanchou_hacks(struct ain *ain)
|
||||
{
|
||||
if (ain->nr_strings > 2 && !strcmp(ain->strings[2]->text, "Seijou Academy")) {
|
||||
game_daibanchou_en = true;
|
||||
}
|
||||
}
|
||||
|
||||
void apply_game_specific_hacks(struct ain *ain)
|
||||
{
|
||||
char *game_name = sjis2utf(config.game_name, 0);
|
||||
if (!strcmp(game_name, "大番長")) {
|
||||
apply_daibanchou_hacks(ain);
|
||||
} else if (!strcmp(game_name, "Rance 02")) {
|
||||
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, "しゃーまんず・さんくちゅあり 巫女の聖域")) {
|
||||
id_indexed_afa = true;
|
||||
} else if (!strcmp(game_name, "大帝国")) {
|
||||
id_indexed_afa = true;
|
||||
} else if (!strcmp(game_name, "ランス・クエスト") || !strcmp(game_name, "Rance Quest")) {
|
||||
id_indexed_afa = true;
|
||||
game_rance8 = true;
|
||||
}
|
||||
free(game_name);
|
||||
}
|
||||
|
||||
+104
-20
@@ -23,6 +23,7 @@
|
||||
#include "vm.h"
|
||||
#include "vm/heap.h"
|
||||
#include "vm/page.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
#define INITIAL_HEAP_SIZE 4096
|
||||
#define HEAP_ALLOC_STEP 4096
|
||||
@@ -98,7 +99,10 @@ int32_t heap_alloc_slot(enum vm_pointer_type type)
|
||||
heap[slot].type = type;
|
||||
#ifdef DEBUG_HEAP
|
||||
heap[slot].alloc_addr = instr_ptr;
|
||||
heap[slot].ref_addr = 0;
|
||||
memset(heap[slot].ref_addr, 0, sizeof(heap[slot].ref_addr));
|
||||
heap[slot].ref_nr = 0;
|
||||
memset(heap[slot].deref_addr, 0, sizeof(heap[slot].deref_addr));
|
||||
heap[slot].deref_nr = 0;
|
||||
heap[slot].free_addr = 0;
|
||||
#endif
|
||||
return slot;
|
||||
@@ -109,26 +113,37 @@ static void heap_free_slot(int32_t slot)
|
||||
heap_free_stack[--heap_free_ptr] = slot;
|
||||
}
|
||||
|
||||
static void heap_double_free(int32_t slot)
|
||||
{
|
||||
#ifdef DEBUG_HEAP
|
||||
WARNING("double free of slot %d (%s)\nOriginally allocated at %X\nOriginally freed at %X",
|
||||
slot, vm_ptrtype_string(heap[slot].type),
|
||||
heap[slot].alloc_addr, heap[slot].free_addr);
|
||||
#else
|
||||
WARNING("double free of slot %d (%s)", slot, vm_ptrtype_string(heap[slot].type));
|
||||
#endif
|
||||
}
|
||||
|
||||
void heap_ref(int32_t slot)
|
||||
{
|
||||
if (slot == -1)
|
||||
return;
|
||||
heap[slot].ref++;
|
||||
#ifdef DEBUG_HEAP
|
||||
heap[slot].ref_addr = instr_ptr;
|
||||
heap[slot].ref_addr[heap[slot].ref_nr++ % 16] = instr_ptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void heap_unref(int slot)
|
||||
{
|
||||
if (heap[slot].ref <= 0) {
|
||||
#ifdef DEBUG_HEAP
|
||||
VM_ERROR("double free of slot %d (%s)\nOriginally allocated at %X\nOriginally freed at %X",
|
||||
slot, vm_ptrtype_string(heap[slot].type), heap[slot].alloc_addr, heap[slot].free_addr);
|
||||
#endif
|
||||
VM_ERROR("double free of slot %d (%s)", slot, vm_ptrtype_string(heap[slot].type));
|
||||
if (unlikely(heap[slot].ref <= 0)) {
|
||||
heap_double_free(slot);
|
||||
VM_ERROR("double free");
|
||||
}
|
||||
if (heap[slot].ref > 1) {
|
||||
#ifdef DEBUG_HEAP
|
||||
heap[slot].deref_addr[heap[slot].deref_nr++ % 16] = instr_ptr;
|
||||
#endif
|
||||
heap[slot].ref--;
|
||||
return;
|
||||
}
|
||||
@@ -157,10 +172,13 @@ void exit_unref(int slot)
|
||||
return;
|
||||
}
|
||||
if (heap[slot].ref <= 0) {
|
||||
WARNING("double free of slot %d", slot);
|
||||
heap_double_free(slot);
|
||||
return;
|
||||
}
|
||||
if (heap[slot].ref > 1) {
|
||||
#ifdef DEBUG_HEAP
|
||||
heap[slot].deref_addr[heap[slot].deref_nr++ % 16] = 0xDEADC0DE;
|
||||
#endif
|
||||
heap[slot].ref--;
|
||||
return;
|
||||
}
|
||||
@@ -172,6 +190,7 @@ void exit_unref(int slot)
|
||||
switch (variable_type(page, i, NULL, NULL)) {
|
||||
case AIN_STRING:
|
||||
case AIN_STRUCT:
|
||||
case AIN_DELEGATE:
|
||||
case AIN_ARRAY_TYPE:
|
||||
case AIN_REF_TYPE:
|
||||
if (page->values[i].i == -1)
|
||||
@@ -210,14 +229,14 @@ bool string_index_valid(int index)
|
||||
|
||||
struct page *heap_get_page(int index)
|
||||
{
|
||||
if (!page_index_valid(index))
|
||||
if (unlikely(!page_index_valid(index)))
|
||||
VM_ERROR("Invalid page index: %d", index);
|
||||
return heap[index].page;
|
||||
}
|
||||
|
||||
struct string *heap_get_string(int index)
|
||||
{
|
||||
if (!string_index_valid(index))
|
||||
if (unlikely(!string_index_valid(index)))
|
||||
VM_ERROR("Invalid string index: %d", index);
|
||||
return heap[index].s;
|
||||
}
|
||||
@@ -225,7 +244,7 @@ struct string *heap_get_string(int index)
|
||||
struct page *heap_get_delegate_page(int index)
|
||||
{
|
||||
struct page *page = heap_get_page(index);
|
||||
if (page && page->type != DELEGATE_PAGE)
|
||||
if (unlikely(page && page->type != DELEGATE_PAGE))
|
||||
VM_ERROR("Not a delegate page: %d", index);
|
||||
return page;
|
||||
}
|
||||
@@ -233,7 +252,7 @@ struct page *heap_get_delegate_page(int index)
|
||||
void heap_set_page(int slot, struct page *page)
|
||||
{
|
||||
#ifdef DEBUG_HEAP
|
||||
if (!page_index_valid(slot))
|
||||
if (unlikely(!page_index_valid(slot)))
|
||||
VM_ERROR("Invalid page index: %d", index);
|
||||
#endif
|
||||
heap[slot].page = page;
|
||||
@@ -242,7 +261,7 @@ void heap_set_page(int slot, struct page *page)
|
||||
void heap_string_assign(int slot, struct string *string)
|
||||
{
|
||||
#ifdef DEBUG_HEAP
|
||||
if (!string_index_valid(slot))
|
||||
if (unlikely(!string_index_valid(slot)))
|
||||
VM_ERROR("Tried to assign string to non-string slot");
|
||||
#endif
|
||||
if (heap[slot].s) {
|
||||
@@ -253,20 +272,20 @@ void heap_string_assign(int slot, struct string *string)
|
||||
|
||||
void heap_struct_assign(int lval, int rval)
|
||||
{
|
||||
if (lval == -1)
|
||||
if (unlikely(lval == -1))
|
||||
VM_ERROR("Assignment to null-pointer");
|
||||
if (lval == rval)
|
||||
return;
|
||||
#ifdef DEBUG_HEAP
|
||||
if (!page_index_valid(lval))
|
||||
if (unlikely(!page_index_valid(lval)))
|
||||
VM_ERROR("Invalid page index: %d", lval);
|
||||
if (!page_index_valid(rval))
|
||||
if (unlikely(!page_index_valid(rval)))
|
||||
VM_ERROR("Invalid page index: %d", rval);
|
||||
if (heap[lval].page && heap[lval].page->type != STRUCT_PAGE)
|
||||
if (unlikely(heap[lval].page && heap[lval].page->type != STRUCT_PAGE))
|
||||
VM_ERROR("SR_ASSIGN to non-struct page");
|
||||
if (heap[rval].page && heap[rval].page->type != STRUCT_PAGE)
|
||||
if (unlikely(heap[rval].page && heap[rval].page->type != STRUCT_PAGE))
|
||||
VM_ERROR("SR_ASSIGN from non-struct page");
|
||||
if (heap[lval].page && heap[rval].page && heap[lval].page->index != heap[rval].page->index)
|
||||
if (unlikely(heap[lval].page && heap[rval].page && heap[lval].page->index != heap[rval].page->index))
|
||||
VM_ERROR("SR_ASSIGN with different struct types");
|
||||
#endif
|
||||
if (heap[lval].page) {
|
||||
@@ -288,3 +307,68 @@ int32_t heap_alloc_page(struct page *page)
|
||||
heap[slot].page = page;
|
||||
return slot;
|
||||
}
|
||||
|
||||
static void describe_page(struct page *page)
|
||||
{
|
||||
if (!page) {
|
||||
sys_message("NULL_PAGE\n");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (page->type) {
|
||||
case GLOBAL_PAGE:
|
||||
sys_message("GLOBAL_PAGE\n");
|
||||
break;
|
||||
case LOCAL_PAGE:
|
||||
sys_message("LOCAL_PAGE: %s\n", display_sjis0(ain->functions[page->index].name));
|
||||
break;
|
||||
case STRUCT_PAGE:
|
||||
sys_message("STRUCT_PAGE: %s\n", display_sjis0(ain->structures[page->index].name));
|
||||
break;
|
||||
case ARRAY_PAGE:
|
||||
sys_message("ARRAY_PAGE: %s\n", display_sjis0(ain_strtype(ain, page->a_type, page->array.struct_type)));
|
||||
break;
|
||||
case DELEGATE_PAGE:
|
||||
// TODO: list function names
|
||||
sys_message("DELEGATE_PAGE\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void heap_describe_slot(int slot)
|
||||
{
|
||||
if (heap[slot].type == VM_STRING && heap[slot].s == &EMPTY_STRING)
|
||||
return;
|
||||
#ifdef DEBUG_HEAP
|
||||
sys_message("[%d](%d)(%08X)[", slot, heap[slot].ref, heap[slot].alloc_addr);
|
||||
for (int i = 0; i < heap[slot].ref_nr && i < 16; i++) {
|
||||
if (i > 0)
|
||||
sys_message(",");
|
||||
sys_message("%08X", heap[slot].ref_addr[i]);
|
||||
}
|
||||
sys_message("][");
|
||||
for (int i = 0; i < heap[slot].deref_nr && i < 16; i++) {
|
||||
if (i > 0)
|
||||
sys_message(",");
|
||||
sys_message("%08X", heap[slot].deref_addr[i]);
|
||||
}
|
||||
sys_message("] = ");
|
||||
#else
|
||||
sys_message("[%d](%d) = ", slot, heap[slot].ref);
|
||||
#endif
|
||||
switch (heap[slot].type) {
|
||||
case VM_PAGE:
|
||||
describe_page(heap[slot].page);
|
||||
break;
|
||||
case VM_STRING:
|
||||
if (heap[slot].s) {
|
||||
sys_message("STRING: %s\n", display_sjis0(heap[slot].s->text));
|
||||
} else {
|
||||
sys_message("STRING: NULL\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sys_message("???\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -102,7 +102,7 @@ HLL_WARN_UNIMPLEMENTED( , void, BanMisc, Init, void *imainsystem);
|
||||
|
||||
static void BanMisc_FadePlay(int ch, int num, int time, int volume, int loop)
|
||||
{
|
||||
bgm_prepare(ch, num - 1);
|
||||
bgm_prepare(ch, num);
|
||||
bgm_play(ch);
|
||||
// TODO: fading
|
||||
}
|
||||
@@ -126,7 +126,7 @@ HLL_WARN_UNIMPLEMENTED(0, int, BanMisc, IsFade, int ch);
|
||||
|
||||
static int BanMisc_SoundLoad(int ch, int num)
|
||||
{
|
||||
return wav_prepare(ch, num - 1);
|
||||
return wav_prepare(ch, num);
|
||||
}
|
||||
|
||||
static void BanMisc_SoundPlay(int ch, int loop)
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
#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)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
//static int CGManager_GetCountOfDataFromArchive(void);
|
||||
//static void CGManager_GetTitleByIndexFromArchive(int index, struct string **cg_name);
|
||||
//static int CGManager_SearchTitleFromArchive(struct string *cg_name);
|
||||
//static int CGManager_PrefixSearchTitleFromArchive(struct string *cg_name);
|
||||
//static int CGManager_SuffixSearchTitleFromArchive(struct string *cg_name);
|
||||
//static int CGManager_GetCountOfSearchDataFromArchive(void);
|
||||
//static void CGManager_GetSearchTitleByIndexFromArchive(int index, struct string **cg_name);
|
||||
|
||||
HLL_LIBRARY(CGManager,
|
||||
HLL_EXPORT(Init, CGManager_Init),
|
||||
HLL_EXPORT(LoadArchive, CGManager_LoadArchive),
|
||||
HLL_TODO_EXPORT(GetCountOfDataFromArchive, CGManager_GetCountOfDataFromArchive),
|
||||
HLL_TODO_EXPORT(GetTitleByIndexFromArchive, CGManager_GetTitleByIndexFromArchive),
|
||||
HLL_TODO_EXPORT(SearchTitleFromArchive, CGManager_SearchTitleFromArchive),
|
||||
HLL_TODO_EXPORT(PrefixSearchTitleFromArchive, CGManager_PrefixSearchTitleFromArchive),
|
||||
HLL_TODO_EXPORT(SuffixSearchTitleFromArchive, CGManager_SuffixSearchTitleFromArchive),
|
||||
HLL_TODO_EXPORT(GetCountOfSearchDataFromArchive, CGManager_GetCountOfSearchDataFromArchive),
|
||||
HLL_TODO_EXPORT(GetSearchTitleByIndexFromArchive, CGManager_GetSearchTitleByIndexFromArchive));
|
||||
|
||||
+45
-45
@@ -22,6 +22,7 @@
|
||||
#include "system4/utfsjis.h"
|
||||
#include "vm/heap.h"
|
||||
#include "vm/page.h"
|
||||
#include "gfx/font.h"
|
||||
#include "sprite.h"
|
||||
#include "xsystem4.h"
|
||||
#include "CharSpriteManager.h"
|
||||
@@ -37,7 +38,7 @@ struct charsprite {
|
||||
// NOTE: even though we only render a single character, we need to store an
|
||||
// arbitrary string because CharSprite_GetChar returns it.
|
||||
struct string *ch;
|
||||
struct text_metrics tm;
|
||||
struct text_style ts;
|
||||
// NOTE: this is needed because we have to hide all sprites after loading
|
||||
// regardless of their original visibility. When Rebuild is called,
|
||||
// this value is used to correctly set the visibility.
|
||||
@@ -105,6 +106,7 @@ int CharSpriteManager_CreateHandle(void)
|
||||
int handle = get_handle();
|
||||
assert(!chars.sprites[handle]);
|
||||
chars.sprites[handle] = xcalloc(1, sizeof(struct charsprite));
|
||||
sprite_init(&chars.sprites[handle]->sp);
|
||||
return handle;
|
||||
}
|
||||
|
||||
@@ -172,16 +174,15 @@ static void charsprite_render(struct charsprite *cs)
|
||||
ch[2] = 0;
|
||||
}
|
||||
|
||||
int w = cs->tm.size;
|
||||
int h = cs->tm.size + cs->tm.size/2;
|
||||
int w = cs->ts.size;
|
||||
int h = cs->ts.size + cs->ts.size/2;
|
||||
if (isascii(ch[0]))
|
||||
w /= 2;
|
||||
|
||||
sprite_init(&cs->sp, w, h, 0, 0, 0, 0);
|
||||
sprite_init_color(&cs->sp, w, h, 0, 0, 0, 0);
|
||||
sprite_get_texture(&cs->sp); // XXX: force initialization of texture
|
||||
gfx_render_text(&cs->sp.texture, (Point) { .x=0, .y = 0 }, ch, &cs->tm, 0);
|
||||
gfx_render_text(&cs->sp.texture, 0.0f, 0, ch, &cs->ts);
|
||||
sprite_dirty(&cs->sp);
|
||||
|
||||
}
|
||||
|
||||
void CharSpriteManager_Rebuild(void)
|
||||
@@ -237,19 +238,19 @@ bool CharSpriteManager_Save(struct page **iarray)
|
||||
struct charsprite *s = chars.sprites[i];
|
||||
iarray_write(&w, 1); // ???
|
||||
iarray_write_string(&w, s->ch);
|
||||
iarray_write(&w, s->tm.face);
|
||||
iarray_write(&w, s->tm.size);
|
||||
iarray_write(&w, s->tm.color.r);
|
||||
iarray_write(&w, s->tm.color.g);
|
||||
iarray_write(&w, s->tm.color.b);
|
||||
iarray_write(&w, s->tm.color.a);
|
||||
//iarray_write_float(&w, s->tm.weight);
|
||||
iarray_write(&w, s->ts.face);
|
||||
iarray_write(&w, s->ts.size);
|
||||
iarray_write(&w, s->ts.color.r);
|
||||
iarray_write(&w, s->ts.color.g);
|
||||
iarray_write(&w, s->ts.color.b);
|
||||
iarray_write(&w, s->ts.color.a);
|
||||
//iarray_write_float(&w, s->ts.weight);
|
||||
iarray_write_float(&w, 1.0);
|
||||
iarray_write_float(&w, s->tm.outline_left);
|
||||
iarray_write(&w, s->tm.outline_color.r);
|
||||
iarray_write(&w, s->tm.outline_color.g);
|
||||
iarray_write(&w, s->tm.outline_color.b);
|
||||
iarray_write(&w, s->tm.outline_color.a);
|
||||
iarray_write_float(&w, s->ts.edge_left);
|
||||
iarray_write(&w, s->ts.edge_color.r);
|
||||
iarray_write(&w, s->ts.edge_color.g);
|
||||
iarray_write(&w, s->ts.edge_color.b);
|
||||
iarray_write(&w, s->ts.edge_color.a);
|
||||
iarray_write(&w, sprite_get_pos_x(&s->sp));
|
||||
iarray_write(&w, sprite_get_pos_y(&s->sp));
|
||||
iarray_write(&w, sprite_get_z(&s->sp));
|
||||
@@ -299,21 +300,23 @@ bool CharSpriteManager_Load(struct page **data)
|
||||
struct charsprite *cs = xcalloc(1, sizeof(struct charsprite));
|
||||
iarray_read(&r); // ???
|
||||
cs->ch = iarray_read_string(&r);
|
||||
cs->tm.face = iarray_read(&r);
|
||||
cs->tm.size = iarray_read(&r);
|
||||
cs->tm.color.r = iarray_read(&r);
|
||||
cs->tm.color.g = iarray_read(&r);
|
||||
cs->tm.color.b = iarray_read(&r);
|
||||
cs->tm.color.a = iarray_read(&r);
|
||||
cs->tm.weight = FW_NORMAL; iarray_read_float(&r); // FIXME
|
||||
cs->tm.outline_left = iarray_read_float(&r);
|
||||
cs->tm.outline_up = cs->tm.outline_left;
|
||||
cs->tm.outline_right = cs->tm.outline_left;
|
||||
cs->tm.outline_down = cs->tm.outline_left;
|
||||
cs->tm.outline_color.r = iarray_read(&r);
|
||||
cs->tm.outline_color.g = iarray_read(&r);
|
||||
cs->tm.outline_color.b = iarray_read(&r);
|
||||
cs->tm.outline_color.a = iarray_read(&r);
|
||||
cs->ts.face = iarray_read(&r);
|
||||
cs->ts.size = iarray_read(&r);
|
||||
cs->ts.color.r = iarray_read(&r);
|
||||
cs->ts.color.g = iarray_read(&r);
|
||||
cs->ts.color.b = iarray_read(&r);
|
||||
cs->ts.color.a = iarray_read(&r);
|
||||
cs->ts.weight = FW_NORMAL; iarray_read_float(&r); // FIXME
|
||||
text_style_set_edge_width(&cs->ts, iarray_read_float(&r));
|
||||
cs->ts.edge_color.r = iarray_read(&r);
|
||||
cs->ts.edge_color.g = iarray_read(&r);
|
||||
cs->ts.edge_color.b = iarray_read(&r);
|
||||
cs->ts.edge_color.a = iarray_read(&r);
|
||||
cs->ts.bold_width = 0.0f;
|
||||
cs->ts.scale_x = 1.0f;
|
||||
cs->ts.space_scale_x = 1.0f;
|
||||
cs->ts.font_spacing = 0.0f;
|
||||
cs->ts.font_size = NULL;
|
||||
|
||||
// sprite data
|
||||
int x = iarray_read(&r);
|
||||
@@ -373,27 +376,24 @@ static void CASColor_to_SDL_Color(SDL_Color *color, int page_no)
|
||||
color->a = page->values[3].i;
|
||||
}
|
||||
|
||||
static void CASCharSpriteProperty_to_text_metrics(struct text_metrics *tm, struct page *page)
|
||||
static void CASCharSpriteProperty_to_text_style(struct text_style *ts, struct page *page)
|
||||
{
|
||||
assert(page);
|
||||
assert(page->nr_vars == 6);
|
||||
|
||||
tm->face = page->values[0].i;
|
||||
tm->size = page->values[1].i;
|
||||
CASColor_to_SDL_Color(&tm->color, page->values[2].i);
|
||||
//tm->weight = page->values[3].f;
|
||||
tm->weight = FW_NORMAL;
|
||||
tm->outline_left = page->values[4].f;
|
||||
tm->outline_up = tm->outline_left;
|
||||
tm->outline_right = tm->outline_left;
|
||||
tm->outline_down = tm->outline_left;
|
||||
CASColor_to_SDL_Color(&tm->outline_color, page->values[5].i);
|
||||
ts->face = page->values[0].i;
|
||||
ts->size = page->values[1].i;
|
||||
CASColor_to_SDL_Color(&ts->color, page->values[2].i);
|
||||
//ts->weight = page->values[3].f;
|
||||
ts->weight = FW_NORMAL;
|
||||
text_style_set_edge_width(ts, page->values[4].f);
|
||||
CASColor_to_SDL_Color(&ts->edge_color, page->values[5].i);
|
||||
}
|
||||
|
||||
void CharSprite_SetChar(int handle, struct string **s, struct page **property)
|
||||
{
|
||||
struct charsprite *sp = charsprite_get(handle);
|
||||
CASCharSpriteProperty_to_text_metrics(&sp->tm, *property);
|
||||
CASCharSpriteProperty_to_text_style(&sp->ts, *property);
|
||||
|
||||
if (charsprite_allocated(sp)) {
|
||||
sprite_free(&sp->sp);
|
||||
|
||||
+187
-20
@@ -14,25 +14,94 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "system4/cg.h"
|
||||
#include "system4/string.h"
|
||||
#include "system4/utfsjis.h"
|
||||
|
||||
#include "asset_manager.h"
|
||||
#include "gfx/gfx.h"
|
||||
#include "gfx/font.h"
|
||||
#include "vm/page.h"
|
||||
#include "input.h"
|
||||
#include "sact.h"
|
||||
#include "CharSpriteManager.h"
|
||||
#include "xsystem4.h"
|
||||
#include "hll.h"
|
||||
|
||||
static void ChipmunkSpriteEngine_PreLink(void);
|
||||
|
||||
static void ChipmunkSpriteEngine_ModuleFini(void)
|
||||
{
|
||||
sact_ModuleFini();
|
||||
}
|
||||
|
||||
static int ChipmunkSpriteEngine_Init(void *imain_system, int cg_cache_size)
|
||||
static int ChipmunkSpriteEngine_Init(possibly_unused void *imain_system)
|
||||
{
|
||||
asset_cg_index_init();
|
||||
return sact_Init(imain_system, cg_cache_size);
|
||||
return sact_init(16, true);
|
||||
}
|
||||
|
||||
static int ChipmunkSpriteEngine_Init_with_size(possibly_unused void *imain_system, int cg_cache_size)
|
||||
{
|
||||
return sact_init(cg_cache_size, true);
|
||||
}
|
||||
|
||||
static int ChipmunkSpriteEngine_SP_SetCG(int sp_no, struct string *cg_name)
|
||||
{
|
||||
struct sact_sprite *sp = sact_get_sprite(sp_no);
|
||||
if (!sp) return 0;
|
||||
return sprite_set_cg_by_name(sp, cg_name->text);
|
||||
}
|
||||
|
||||
// XXX: Rance Quest
|
||||
static int ChipmunkSpriteEngine_SP_SetCG_by_string_id(int sp_no, struct string *cg_no)
|
||||
{
|
||||
struct sact_sprite *sp = sact_get_sprite(sp_no);
|
||||
if (!sp) return 0;
|
||||
return sprite_set_cg_from_asset(sp, atoi(cg_no->text));
|
||||
}
|
||||
|
||||
static int ChipmunkSpriteEngine_CG_IsExist(struct string *cg_name)
|
||||
{
|
||||
return asset_exists_by_name(ASSET_CG, cg_name->text, NULL);
|
||||
}
|
||||
|
||||
// XXX: Rance Quest
|
||||
static int ChipmunkSpriteEngine_CG_IsExist_by_string_id(struct string *cg_no)
|
||||
{
|
||||
return asset_exists(ASSET_CG, atoi(cg_no->text));
|
||||
}
|
||||
|
||||
static void set_cg_metrics(struct cg_metrics *metrics, struct page **page)
|
||||
{
|
||||
union vm_value *cgm = (*page)->values;
|
||||
cgm[0].i = metrics->w;
|
||||
cgm[1].i = metrics->h;
|
||||
cgm[2].i = metrics->bpp;
|
||||
cgm[3].i = metrics->has_pixel;
|
||||
cgm[4].i = metrics->has_alpha;
|
||||
cgm[5].i = metrics->pixel_pitch;
|
||||
cgm[6].i = metrics->alpha_pitch;
|
||||
}
|
||||
|
||||
static int ChipmunkSpriteEngine_CG_GetMetrics(struct string *cg_name, struct page **page)
|
||||
{
|
||||
struct cg_metrics metrics;
|
||||
if (!asset_cg_get_metrics_by_name(cg_name->text, &metrics))
|
||||
return 0;
|
||||
set_cg_metrics(&metrics, page);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// XXX: Rance Quest
|
||||
static int ChipmunkSpriteEngine_CG_GetMetrics_by_string_id(struct string *cg_no, struct page **page)
|
||||
{
|
||||
struct cg_metrics metrics;
|
||||
if (!asset_cg_get_metrics(atoi(cg_no->text), &metrics))
|
||||
return 0;
|
||||
set_cg_metrics(&metrics, page);
|
||||
return 1;
|
||||
}
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED(0, int, ChipmunkSpriteEngine, SP_SetBrightness, int sp_no, int brightness);
|
||||
@@ -45,13 +114,49 @@ 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(false, bool, ChipmunkSpriteEngine, KeepPreviousView);
|
||||
HLL_QUIET_UNIMPLEMENTED(, void, ChipmunkSpriteEngine, Sleep);
|
||||
|
||||
static void ChipmunkSpriteEngine_Sleep(void)
|
||||
{
|
||||
SDL_Delay(16);
|
||||
}
|
||||
|
||||
//static bool ChipmunkSpriteEngine_SP_SetCutCG(int sp_no, int cg_no, int cut_x, int cut_y, int cut_w, int cut_h);
|
||||
//static bool ChipmunkSpriteEngine_SP_SetMultipleColor(int sp_no, int r, int g, int b);
|
||||
//static bool ChipmunkSpriteEngine_SP_SetAddColor(int sp_no, int r, int g, int b, int a);
|
||||
//static bool ChipmunkSpriteEngine_SP_SetSurfaceArea(int sp_no, int x, int y, int w, int h);
|
||||
//static bool ChipmunkSpriteEngine_SP_GetSurfaceArea(int sp_no, int *x, int *y, int *w, int *h);
|
||||
|
||||
static bool ChipmunkSpriteEngine_SP_SetMultipleColor(int sp_no, int r, int g, int b)
|
||||
{
|
||||
struct sact_sprite *sp = sact_get_sprite(sp_no);
|
||||
if (!sp) return false;
|
||||
sprite_set_multiply_color(sp, r, g, b);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ChipmunkSpriteEngine_SP_SetAddColor(int sp_no, int r, int g, int b, int a)
|
||||
{
|
||||
struct sact_sprite *sp = sact_get_sprite(sp_no);
|
||||
if (!sp) return false;
|
||||
sprite_set_add_color(sp, r, g, b);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ChipmunkSpriteEngine_SP_SetSurfaceArea(int sp_no, int x, int y, int w, int h)
|
||||
{
|
||||
struct sact_sprite *sp = sact_get_sprite(sp_no);
|
||||
if (!sp) return false;
|
||||
sprite_set_surface_area(sp, x, y, w, h);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ChipmunkSpriteEngine_SP_GetSurfaceArea(int sp_no, int *x, int *y, int *w, int *h)
|
||||
{
|
||||
struct sact_sprite *sp = sact_get_sprite(sp_no);
|
||||
if (!sp) return false;
|
||||
*x = sp->surface_area.x;
|
||||
*y = sp->surface_area.y;
|
||||
*w = sp->surface_area.w;
|
||||
*h = sp->surface_area.h;
|
||||
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 bool ChipmunkSpriteEngine_Sprite_SetTextureFilerType(int sp_no, int texture_filter_type);
|
||||
@@ -66,15 +171,24 @@ static bool ChipmunkSpriteEngine_SP_GetFontWidth(struct string *text, int *width
|
||||
return true;
|
||||
}
|
||||
|
||||
*width = SJIS_2BYTE(text->text[0]) ? text_sprite_tm.size : text_sprite_tm.size / 2;
|
||||
*width = gfx_size_text(&text_sprite_ts, text->text);
|
||||
return true;
|
||||
}
|
||||
|
||||
//static bool ChipmunkSpriteEngine_SP_SetAlphaClipperSpriteNumber(int sp_no, int ac_sp_no);
|
||||
//static int ChipmunkSpriteEngine_SP_GetAlphaClipperSpriteNumber(int sp_no);
|
||||
//static int CG_GetFormat(int cg_no);
|
||||
//static int CG_GetFormat_by_index(int cg_no);
|
||||
//static int CG_GetFormat(struct string *cg_name);
|
||||
|
||||
//static 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_Debug_GetVideoMemoryInfo(int *max_size, int *use_size, int *use_max_size);
|
||||
//static void ChipmunkSpriteEngine_CombineTexture(void);
|
||||
//static void ChipmunkSpriteEngine_ReleaseCombinedTexture(void);
|
||||
|
||||
HLL_LIBRARY(ChipmunkSpriteEngine,
|
||||
HLL_EXPORT(_PreLink, ChipmunkSpriteEngine_PreLink),
|
||||
HLL_EXPORT(_ModuleFini, ChipmunkSpriteEngine_ModuleFini),
|
||||
HLL_EXPORT(Init, ChipmunkSpriteEngine_Init),
|
||||
HLL_TODO_EXPORT(Error, SACT2_Error),
|
||||
@@ -84,7 +198,7 @@ HLL_LIBRARY(ChipmunkSpriteEngine,
|
||||
HLL_EXPORT(SP_GetUnuseNum, sact_SP_GetUnuseNum),
|
||||
HLL_EXPORT(SP_Count, sact_SP_Count),
|
||||
HLL_EXPORT(SP_Enum, sact_SP_Enum),
|
||||
HLL_EXPORT(SP_SetCG, sact_SP_SetCG),
|
||||
HLL_EXPORT(SP_SetCG, ChipmunkSpriteEngine_SP_SetCG),
|
||||
HLL_TODO_EXPORT(SP_SetCutCG, ChipmunkSpriteEngine_SP_SetCutCG),
|
||||
HLL_EXPORT(SP_SetCGFromFile, sact_SP_SetCGFromFile),
|
||||
HLL_EXPORT(SP_SaveCG, sact_SP_SaveCG),
|
||||
@@ -98,12 +212,12 @@ HLL_LIBRARY(ChipmunkSpriteEngine,
|
||||
HLL_EXPORT(SP_SetY, sact_SP_SetY),
|
||||
HLL_EXPORT(SP_SetZ, sact_SP_SetZ),
|
||||
HLL_EXPORT(SP_SetBlendRate, sact_SP_SetBlendRate),
|
||||
HLL_TODO_EXPORT(SP_SetMultipleColor, ChipmunkSpriteEngine_SP_SetMultipleColor),
|
||||
HLL_TODO_EXPORT(SP_SetAddColor, ChipmunkSpriteEngine_SP_SetAddColor),
|
||||
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_TODO_EXPORT(SP_SetSurfaceArea, ChipmunkSpriteEngine_SP_SetSurfaceArea),
|
||||
HLL_TODO_EXPORT(SP_GetSurfaceArea, ChipmunkSpriteEngine_SP_GetSurfaceArea),
|
||||
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_TODO_EXPORT(Sprite_SetTextureFilerType, ChipmunkSpriteEngine_Sprite_SetTextureFilerType),
|
||||
@@ -155,8 +269,9 @@ HLL_LIBRARY(ChipmunkSpriteEngine,
|
||||
HLL_EXPORT(Key_IsDown, sact_Key_IsDown),
|
||||
HLL_EXPORT(KEY_GetState, key_is_down),
|
||||
HLL_EXPORT(Timer_Get, vm_time),
|
||||
HLL_EXPORT(CG_IsExist, sact_CG_IsExist),
|
||||
HLL_EXPORT(CG_GetMetrics, sact_CG_GetMetrics),
|
||||
HLL_EXPORT(CG_IsExist, ChipmunkSpriteEngine_CG_IsExist),
|
||||
HLL_EXPORT(CG_GetMetrics, ChipmunkSpriteEngine_CG_GetMetrics),
|
||||
HLL_TODO_EXPORT(CG_GetFormat, ChipmunkSpriteEngine_CG_GetFormat),
|
||||
HLL_EXPORT(System_GetDate, get_date),
|
||||
HLL_EXPORT(System_GetTime, get_time),
|
||||
HLL_TODO_EXPORT(CG_RotateRGB, SACT2_CG_RotateRGB),
|
||||
@@ -173,8 +288,8 @@ HLL_LIBRARY(ChipmunkSpriteEngine,
|
||||
HLL_EXPORT(TRANS_Begin, sact_TRANS_Begin),
|
||||
HLL_EXPORT(TRANS_Update, sact_TRANS_Update),
|
||||
HLL_EXPORT(TRANS_End, sact_TRANS_End),
|
||||
HLL_TODO_EXPORT(VIEW_SetMode, SACTDX_VIEW_SetMode),
|
||||
HLL_TODO_EXPORT(VIEW_GetMode, SACTDX_VIEW_GetMode),
|
||||
HLL_EXPORT(VIEW_SetMode, sact_VIEW_SetMode),
|
||||
HLL_EXPORT(VIEW_GetMode, sact_VIEW_GetMode),
|
||||
HLL_EXPORT(VIEW_SetOffsetPos, ChipmunkSpriteEngine_VIEW_SetOffsetPos),
|
||||
HLL_TODO_EXPORT(DX_GetUsePower2Texture, SACTDX_DX_GetUsePower2Texture),
|
||||
HLL_TODO_EXPORT(DX_SetUsePower2Texture, SACTDX_DX_SetUsePower2Texture),
|
||||
@@ -210,5 +325,57 @@ HLL_LIBRARY(ChipmunkSpriteEngine,
|
||||
HLL_TODO_EXPORT(Debug_GetCurrentAllocatedMemorySize, StoatSpriteEngine_Debug_GetCurrentAllocatedMemorySize),
|
||||
HLL_TODO_EXPORT(Debug_GetMaxAllocatedMemorySize, StoatSpriteEngine_Debug_GetMaxAllocatedMemorySize),
|
||||
HLL_TODO_EXPORT(Debug_GetFillRate, StoatSpriteEngine_Debug_GetFillRate),
|
||||
HLL_TODO_EXPORT(MUSIC_ReloadParam, StoatSpriteEngine_MUSIC_ReloadParam));
|
||||
HLL_TODO_EXPORT(MUSIC_ReloadParam, StoatSpriteEngine_MUSIC_ReloadParam),
|
||||
HLL_TODO_EXPORT(SP_Suspend, ChipmunkSpriteEngine_SP_Suspend),
|
||||
HLL_TODO_EXPORT(SP_Resume, ChipmunkSpriteEngine_SP_Resume),
|
||||
HLL_TODO_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));
|
||||
|
||||
static struct ain_hll_function *get_fun(int libno, const char *name)
|
||||
{
|
||||
int fno = ain_get_library_function(ain, libno, name);
|
||||
return fno >= 0 ? &ain->libraries[libno].functions[fno] : NULL;
|
||||
}
|
||||
|
||||
// Daiteikoku and Shaman's Sanctuary have different interface than later versions
|
||||
static void ChipmunkSpriteEngine_PreLink(void)
|
||||
{
|
||||
struct ain_hll_function *fun;
|
||||
int libno = ain_get_library(ain, "ChipmunkSpriteEngine");
|
||||
assert(libno >= 0);
|
||||
|
||||
fun = get_fun(libno, "Init");
|
||||
if (fun && fun->nr_arguments == 2) {
|
||||
static_library_replace(&lib_ChipmunkSpriteEngine, "Init", ChipmunkSpriteEngine_Init_with_size);
|
||||
}
|
||||
|
||||
fun = get_fun(libno, "SP_SetCG");
|
||||
if (fun && game_rance8) {
|
||||
static_library_replace(&lib_ChipmunkSpriteEngine, "SP_SetCG",
|
||||
ChipmunkSpriteEngine_SP_SetCG_by_string_id);
|
||||
} else if (fun && fun->arguments[1].type.data == AIN_INT) {
|
||||
static_library_replace(&lib_ChipmunkSpriteEngine, "SP_SetCG", sact_SP_SetCG);
|
||||
}
|
||||
|
||||
// TODO: SP_SetCutCG
|
||||
|
||||
fun = get_fun(libno, "CG_IsExist");
|
||||
if (fun && game_rance8) {
|
||||
static_library_replace(&lib_ChipmunkSpriteEngine, "CG_IsExist",
|
||||
ChipmunkSpriteEngine_CG_IsExist_by_string_id);
|
||||
} else if (fun && fun->arguments[0].type.data == AIN_INT) {
|
||||
static_library_replace(&lib_ChipmunkSpriteEngine, "CG_IsExist", sact_CG_IsExist);
|
||||
}
|
||||
|
||||
fun = get_fun(libno, "CG_GetMetrics");
|
||||
if (fun && game_rance8) {
|
||||
static_library_replace(&lib_ChipmunkSpriteEngine, "CG_GetMetrics",
|
||||
ChipmunkSpriteEngine_CG_GetMetrics_by_string_id);
|
||||
} else if (fun && fun->arguments[0].type.data == AIN_INT) {
|
||||
static_library_replace(&lib_ChipmunkSpriteEngine, "CG_GetMetrics", sact_CG_GetMetrics);
|
||||
}
|
||||
|
||||
// TODO: CG_GetFormat
|
||||
}
|
||||
|
||||
+114
-16
@@ -14,34 +14,132 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/hashtable.h"
|
||||
#include "system4/string.h"
|
||||
|
||||
#include "hll.h"
|
||||
|
||||
static struct string *save_path = NULL;
|
||||
|
||||
union csd_datum {
|
||||
void *p;
|
||||
intptr_t i;
|
||||
float f;
|
||||
struct string *s;
|
||||
};
|
||||
|
||||
static struct hash_table *csd_table(void)
|
||||
{
|
||||
static struct hash_table *table = NULL;
|
||||
if (!table) {
|
||||
table = ht_create(64);
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
static void csd_put_datum(struct string *name, union csd_datum datum)
|
||||
{
|
||||
struct ht_slot *slot = ht_put(csd_table(), name->text, NULL);
|
||||
slot->value = datum.p;
|
||||
}
|
||||
|
||||
static bool csd_get_datum(struct string *name, union csd_datum *out)
|
||||
{
|
||||
return _ht_get(csd_table(), name->text, &out->p);
|
||||
}
|
||||
|
||||
static void CommonSystemData_SetFullPathSaveFileName(struct string *filename)
|
||||
{
|
||||
if (save_path)
|
||||
free_string(save_path);
|
||||
save_path = string_dup(filename);
|
||||
NOTICE("SetFullPathSaveFileName(%s)", filename->text);
|
||||
}
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED(false, bool, CommonSystemData, LoadAtStartup);
|
||||
//static bool CommonSystemData_LoadAtStartup(void);
|
||||
//static bool CommonSystemData_SetDataInt(struct string *name, int value);
|
||||
//static bool CommonSystemData_SetDataFloat(struct string *name, float value);
|
||||
//static bool CommonSystemData_SetDataString(struct string *name, struct string *value);
|
||||
//static bool CommonSystemData_SetDataBool(struct string *name, bool value);
|
||||
//static bool CommonSystemData_GetDataInt(struct string *name, int *value);
|
||||
//static bool CommonSystemData_GetDataFloat(struct string *name, float *value);
|
||||
//static bool CommonSystemData_GetDataString(struct string *name, struct string **value);
|
||||
//static bool CommonSystemData_GetDataBool(struct string *name, bool *value);
|
||||
|
||||
static bool CommonSystemData_SetDataInt(struct string *name, int value)
|
||||
{
|
||||
csd_put_datum(name, (union csd_datum) { .i = value });
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CommonSystemData_SetDataFloat(struct string *name, float value)
|
||||
{
|
||||
csd_put_datum(name, (union csd_datum) { .f = value });
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CommonSystemData_SetDataString(struct string *name, struct string *value)
|
||||
{
|
||||
union csd_datum datum = { .s = string_dup(value) };
|
||||
struct ht_slot *slot = ht_put(csd_table(), name->text, NULL);
|
||||
if (slot->value)
|
||||
free_string(slot->value);
|
||||
slot->value = datum.p;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CommonSystemData_SetDataBool(struct string *name, bool value)
|
||||
{
|
||||
csd_put_datum(name, (union csd_datum) { .i = value });
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CommonSystemData_GetDataInt(struct string *name, int *value)
|
||||
{
|
||||
union csd_datum datum;
|
||||
if (csd_get_datum(name, &datum)) {
|
||||
*value = datum.i;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool CommonSystemData_GetDataFloat(struct string *name, float *value)
|
||||
{
|
||||
union csd_datum datum;
|
||||
if (csd_get_datum(name, &datum)) {
|
||||
*value = datum.f;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool CommonSystemData_GetDataString(struct string *name, struct string **value)
|
||||
{
|
||||
union csd_datum datum;
|
||||
if (csd_get_datum(name, &datum)) {
|
||||
if (*value)
|
||||
free_string(*value);
|
||||
*value = string_dup(datum.s);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool CommonSystemData_GetDataBool(struct string *name, bool *value)
|
||||
{
|
||||
union csd_datum datum;
|
||||
if (csd_get_datum(name, &datum)) {
|
||||
*value = datum.i;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
HLL_LIBRARY(CommonSystemData,
|
||||
HLL_EXPORT(SetFullPathSaveFileName, CommonSystemData_SetFullPathSaveFileName),
|
||||
HLL_EXPORT(LoadAtStartup, CommonSystemData_LoadAtStartup),
|
||||
HLL_TODO_EXPORT(SetDataInt, CommonSystemData_SetDataInt),
|
||||
HLL_TODO_EXPORT(SetDataFloat, CommonSystemData_SetDataFloat),
|
||||
HLL_TODO_EXPORT(SetDataString, CommonSystemData_SetDataString),
|
||||
HLL_TODO_EXPORT(SetDataBool, CommonSystemData_SetDataBool),
|
||||
HLL_TODO_EXPORT(GetDataInt, CommonSystemData_GetDataInt),
|
||||
HLL_TODO_EXPORT(GetDataFloat, CommonSystemData_GetDataFloat),
|
||||
HLL_TODO_EXPORT(GetDataString, CommonSystemData_GetDataString),
|
||||
HLL_TODO_EXPORT(GetDataBool, CommonSystemData_GetDataBool));
|
||||
HLL_EXPORT(SetDataInt, CommonSystemData_SetDataInt),
|
||||
HLL_EXPORT(SetDataFloat, CommonSystemData_SetDataFloat),
|
||||
HLL_EXPORT(SetDataString, CommonSystemData_SetDataString),
|
||||
HLL_EXPORT(SetDataBool, CommonSystemData_SetDataBool),
|
||||
HLL_EXPORT(GetDataInt, CommonSystemData_GetDataInt),
|
||||
HLL_EXPORT(GetDataFloat, CommonSystemData_GetDataFloat),
|
||||
HLL_EXPORT(GetDataString, CommonSystemData_GetDataString),
|
||||
HLL_EXPORT(GetDataBool, CommonSystemData_GetDataBool));
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hll.h"
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED( , void, Confirm3, Init, void *iMainsystem);
|
||||
HLL_WARN_UNIMPLEMENTED(0, int, Confirm3, GetHDDVolumeNumber, void);
|
||||
HLL_WARN_UNIMPLEMENTED(5, int, Confirm3, GetDrive, struct string *filename);
|
||||
HLL_WARN_UNIMPLEMENTED(0, int, Confirm3, GetRandom, int seed);
|
||||
HLL_WARN_UNIMPLEMENTED(true, bool, Confirm3, File_Open, struct string *filename);
|
||||
HLL_WARN_UNIMPLEMENTED(true, bool, Confirm3, File_Create, struct string *filename);
|
||||
HLL_WARN_UNIMPLEMENTED(true, bool, Confirm3, File_SetPos, int pos);
|
||||
|
||||
static bool Confirm3_File_ReadByte(int *data)
|
||||
{
|
||||
*data = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool Confirm3_File_ReadInt(int *data)
|
||||
{
|
||||
*data = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED(true, bool, Confirm3, File_WriteInt, int data);
|
||||
HLL_WARN_UNIMPLEMENTED(true, bool, Confirm3, File_Close, void);
|
||||
|
||||
HLL_LIBRARY(Confirm3,
|
||||
HLL_EXPORT(Init, Confirm3_Init),
|
||||
HLL_EXPORT(GetHDDVolumeNumber, Confirm3_GetHDDVolumeNumber),
|
||||
HLL_EXPORT(GetDrive, Confirm3_GetDrive),
|
||||
HLL_EXPORT(GetRandom, Confirm3_GetRandom),
|
||||
HLL_EXPORT(File_Open, Confirm3_File_Open),
|
||||
HLL_EXPORT(File_Create, Confirm3_File_Create),
|
||||
HLL_EXPORT(File_SetPos, Confirm3_File_SetPos),
|
||||
HLL_EXPORT(File_ReadByte, Confirm3_File_ReadByte),
|
||||
HLL_EXPORT(File_ReadInt, Confirm3_File_ReadInt),
|
||||
HLL_EXPORT(File_WriteInt, Confirm3_File_WriteInt),
|
||||
HLL_EXPORT(File_Close, Confirm3_File_Close)
|
||||
);
|
||||
@@ -0,0 +1,42 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "input.h"
|
||||
#include "hll.h"
|
||||
|
||||
#define TT3_CURSOR_TRANSPARENT 11
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED(1, int, Cursor, Load, int nNum, int nLinkNum);
|
||||
|
||||
static void Cursor_Set(int num)
|
||||
{
|
||||
switch (num) {
|
||||
case 0:
|
||||
mouse_show_cursor(true);
|
||||
break;
|
||||
case TT3_CURSOR_TRANSPARENT:
|
||||
mouse_show_cursor(false);
|
||||
break;
|
||||
default:
|
||||
WARNING("Cursor.Set: unexpected argument %d", num);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
HLL_LIBRARY(Cursor,
|
||||
HLL_EXPORT(Load, Cursor_Load),
|
||||
HLL_EXPORT(Set, Cursor_Set)
|
||||
);
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "system4/archive.h"
|
||||
#include "system4/buffer.h"
|
||||
#include "system4/string.h"
|
||||
|
||||
#include "hll.h"
|
||||
#include "asset_manager.h"
|
||||
#include "little_endian.h"
|
||||
#include "vm/heap.h"
|
||||
#include "vm/page.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
static struct buffer reader;
|
||||
|
||||
static int Data_Exist(int num)
|
||||
{
|
||||
return asset_exists(ASSET_DATA, num);
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
archive_free_data(dfile);
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
archive_free_data(dfile);
|
||||
if (reader.buf)
|
||||
free(reader.buf);
|
||||
buffer_init(&reader, buf, uncompressed_size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int Data_Close(void)
|
||||
{
|
||||
if (!reader.buf)
|
||||
return 0;
|
||||
free(reader.buf);
|
||||
reader.buf = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void read_struct(struct page *page)
|
||||
{
|
||||
if (page->type != STRUCT_PAGE) {
|
||||
VM_ERROR("Data.Read of non-struct");
|
||||
}
|
||||
|
||||
for (int i = 0; i < page->nr_vars; i++) {
|
||||
int struct_type;
|
||||
enum ain_data_type type = variable_type(page, i, &struct_type, NULL);
|
||||
union vm_value val;
|
||||
switch (type) {
|
||||
case AIN_INT:
|
||||
val = vm_int(buffer_read_int32(&reader));
|
||||
break;
|
||||
case AIN_FLOAT:
|
||||
val = vm_float(buffer_read_float(&reader));
|
||||
break;
|
||||
case AIN_STRING:
|
||||
{
|
||||
struct string *s = cstr_to_string(buffer_strdata(&reader));
|
||||
buffer_skip(&reader, s->size + 1);
|
||||
val = vm_int(heap_alloc_string(s));
|
||||
}
|
||||
break;
|
||||
case AIN_STRUCT:
|
||||
{
|
||||
int slot = alloc_struct(struct_type);
|
||||
read_struct(heap[slot].page);
|
||||
val = vm_int(slot);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
VM_ERROR("Data.Read: unsupported data type");
|
||||
}
|
||||
variable_set(page, i, type, val);
|
||||
}
|
||||
}
|
||||
|
||||
static int Data_Read(struct page **page)
|
||||
{
|
||||
if (!reader.buf || buffer_remaining(&reader) == 0) {
|
||||
return 0;
|
||||
}
|
||||
read_struct(*page);
|
||||
return 1;
|
||||
}
|
||||
|
||||
HLL_LIBRARY(Data,
|
||||
HLL_EXPORT(Exist, Data_Exist),
|
||||
HLL_EXPORT(Open, Data_Open),
|
||||
HLL_EXPORT(Close, Data_Close),
|
||||
HLL_EXPORT(Read, Data_Read)
|
||||
);
|
||||
+1
-1
@@ -251,7 +251,7 @@ possibly_unused static void gdat_dump_as_json(struct gdat *dat, FILE *fp)
|
||||
|
||||
static int DataFile_Open(int link)
|
||||
{
|
||||
struct archive_data *dfile = asset_get(ASSET_DATA, link - 1);
|
||||
struct archive_data *dfile = asset_get(ASSET_DATA, link);
|
||||
if (!dfile) {
|
||||
WARNING("Failed to load data file %d", link);
|
||||
return 0;
|
||||
|
||||
@@ -46,12 +46,6 @@ struct dungeon_context *dungeon_get_context(int surface)
|
||||
return current_context;
|
||||
}
|
||||
|
||||
void dungeon_update(void)
|
||||
{
|
||||
if (current_context && current_context->loaded && current_context->draw_enabled)
|
||||
dungeon_render(current_context);
|
||||
}
|
||||
|
||||
static struct dgn_cell *dungeon_get_cell(int surface, int x, int y, int z)
|
||||
{
|
||||
struct dungeon_context *ctx = dungeon_get_context(surface);
|
||||
@@ -414,6 +408,7 @@ HLL_LIBRARY(DrawDungeon,
|
||||
DRAW_DUNGEON_EXPORTS
|
||||
);
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon14, SetRasterScroll, int surface, int type);
|
||||
HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon14, SetRasterAmp, int nSurface, float fAmp);
|
||||
HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon14, SetLapFilter, int surface, int type, int r, int g, int b);
|
||||
|
||||
@@ -437,7 +432,7 @@ HLL_LIBRARY(DrawDungeon14,
|
||||
HLL_EXPORT(Init, DrawDungeon14_Init),
|
||||
DRAW_DUNGEON_EXPORTS,
|
||||
HLL_EXPORT(GetWalked, dungeon_get_walked),
|
||||
HLL_TODO_EXPORT(SetRasterScroll, DrawDungeon14_SetRasterScroll),
|
||||
HLL_EXPORT(SetRasterScroll, DrawDungeon14_SetRasterScroll),
|
||||
HLL_EXPORT(SetRasterAmp, DrawDungeon14_SetRasterAmp),
|
||||
HLL_EXPORT(SetLapFilter, DrawDungeon14_SetLapFilter),
|
||||
HLL_EXPORT(GetDrawMapObjectFlag, DrawDungeon14_GetDrawMapObjectFlag),
|
||||
|
||||
+176
-97
@@ -16,7 +16,8 @@
|
||||
|
||||
#include "hll.h"
|
||||
#include "sact.h"
|
||||
#include "gfx/types.h"
|
||||
#include "gfx/gfx.h"
|
||||
#include "gfx/font.h"
|
||||
#include "system4/string.h"
|
||||
|
||||
// Get texture for source sprite
|
||||
@@ -86,21 +87,71 @@ static void DrawGraph_Blend(int dst, int dx, int dy, int src, int sx, int sy, in
|
||||
gfx_blend(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h, alpha);
|
||||
}
|
||||
|
||||
static void DrawGraph_BlendSrcBright(int dst, int dx, int dy, int src, int sx, int sy, int w, int h, int alpha, int rate)
|
||||
{
|
||||
gfx_blend_src_bright(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h, alpha, rate);
|
||||
}
|
||||
|
||||
static void DrawGraph_BlendAddSatur(int dst, int dx, int dy, int src, int sx, int sy, int w, int h)
|
||||
{
|
||||
gfx_blend_add_satur(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h);
|
||||
}
|
||||
|
||||
static void DrawGraph_BlendAMap(int dst, int dx, int dy, int src, int sx, int sy, int w, int h)
|
||||
{
|
||||
gfx_blend_amap(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h);
|
||||
}
|
||||
|
||||
static void DrawGraph_BlendAMapSrcOnly(int dst, int dx, int dy, int src, int sx, int sy, int w, int h)
|
||||
{
|
||||
gfx_blend_amap_src_only(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h);
|
||||
}
|
||||
|
||||
static void DrawGraph_BlendAMapColor(int dst, int dx, int dy, int src, int sx, int sy, int w, int h, int r, int g, int b)
|
||||
{
|
||||
gfx_blend_amap_color(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h, r, g, b);
|
||||
}
|
||||
|
||||
static void DrawGraph_BlendAMapColorAlpha(int dst, int dx, int dy, int src, int sx, int sy, int w, int h, int r, int g, int b, int a)
|
||||
{
|
||||
gfx_blend_amap_color_alpha(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h, r, g, b, a);
|
||||
}
|
||||
|
||||
static void DrawGraph_BlendAMapAlpha(int dst, int dx, int dy, int src, int sx, int sy, int w, int h, int alpha)
|
||||
{
|
||||
gfx_blend_amap_alpha(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h, alpha);
|
||||
}
|
||||
|
||||
static void DrawGraph_BlendAMapBright(int dst, int dx, int dy, int src, int sx, int sy, int w, int h, int rate)
|
||||
{
|
||||
gfx_blend_amap_bright(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h, rate);
|
||||
}
|
||||
|
||||
static void DrawGraph_BlendAMapAlphaSrcBright(int dst, int dx, int dy, int src, int sx, int sy, int w, int h, int alpha, int rate)
|
||||
{
|
||||
gfx_blend_amap_alpha_src_bright(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h, alpha, rate);
|
||||
}
|
||||
|
||||
static void DrawGraph_BlendUseAMapColor(int dst, int dx, int dy, int alpha, int ax, int ay, int w, int h, int r, int g, int b, int rate)
|
||||
{
|
||||
gfx_blend_use_amap_color(DTEX(dst), dx, dy, STEX(alpha), ax, ay, w, h, r, g, b, rate);
|
||||
}
|
||||
|
||||
static void DrawGraph_BlendScreen(int dst, int dx, int dy, int src, int sx, int sy, int w, int h)
|
||||
{
|
||||
gfx_blend_screen(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h);
|
||||
}
|
||||
|
||||
static void DrawGraph_BlendMultiply(int dst, int dx, int dy, int src, int sx, int sy, int w, int h)
|
||||
{
|
||||
gfx_blend_multiply(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h);
|
||||
}
|
||||
|
||||
static void DrawGraph_BlendScreenAlpha(int dst, int dx, int dy, int src, int sx, int sy, int w, int h, int alpha)
|
||||
{
|
||||
gfx_blend_screen_alpha(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h, alpha);
|
||||
}
|
||||
|
||||
static void DrawGraph_Fill(int dst, int x, int y, int w, int h, int r, int g, int b)
|
||||
{
|
||||
gfx_fill(DTEX(dst), x, y, w, h, r, g, b);
|
||||
@@ -116,11 +167,51 @@ static void DrawGraph_FillAMap(int dst, int x, int y, int w, int h, int alpha)
|
||||
gfx_fill_amap(DTEX(dst), x, y, w, h, alpha);
|
||||
}
|
||||
|
||||
static void DrawGraph_FillAMapOverBorder(int dst, int x, int y, int w, int h, int alpha, int border)
|
||||
{
|
||||
gfx_fill_amap_over_border(DTEX(dst), x, y, w, h, alpha, border);
|
||||
}
|
||||
|
||||
static void DrawGraph_FillAMapUnderBorder(int dst, int x, int y, int w, int h, int alpha, int border)
|
||||
{
|
||||
gfx_fill_amap_under_border(DTEX(dst), x, y, w, h, alpha, border);
|
||||
}
|
||||
|
||||
static void DrawGraph_FillAMapGradationUD(int dst, int x, int y, int w, int h, int up_a, int down_a)
|
||||
{
|
||||
gfx_fill_amap_gradation_ud(DTEX(dst), x, y, w, h, up_a, down_a);
|
||||
}
|
||||
|
||||
static void DrawGraph_FillScreen(int dst, int x, int y, int w, int h, int r, int g, int b)
|
||||
{
|
||||
gfx_fill_screen(DTEX(dst), x, y, w, h, r, g, b);
|
||||
}
|
||||
|
||||
static void DrawGraph_FillMultiply(int dst, int x, int y, int w, int h, int r, int g, int b)
|
||||
{
|
||||
gfx_fill_multiply(DTEX(dst), x, y, w, h, r, g, b);
|
||||
}
|
||||
|
||||
static void DrawGraph_SaturDP_DPxSA(int dst, int dx, int dy, int src, int sx, int sy, int w, int h)
|
||||
{
|
||||
gfx_satur_dp_dpxsa(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h);
|
||||
}
|
||||
|
||||
static void DrawGraph_ScreenDA_DAxSA(int dst, int dx, int dy, int src, int sx, int sy, int w, int h)
|
||||
{
|
||||
gfx_screen_da_daxsa(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h);
|
||||
}
|
||||
|
||||
static void DrawGraph_AddDA_DAxSA(int dst, int dx, int dy, int src, int sx, int sy, int w, int h)
|
||||
{
|
||||
gfx_add_da_daxsa(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h);
|
||||
}
|
||||
|
||||
static void DrawGraph_SpriteCopyAMap(int dst, int dx, int dy, int src, int sx, int sy, int w, int h, int key)
|
||||
{
|
||||
gfx_sprite_copy_amap(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h, key);
|
||||
}
|
||||
|
||||
static void DrawGraph_BlendDA_DAxSA(int dst, int dx, int dy, int src, int sx, int sy, int w, int h)
|
||||
{
|
||||
gfx_blend_da_daxsa(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h);
|
||||
@@ -131,16 +222,37 @@ static void DrawGraph_SubDA_DAxSA(int dst, int dx, int dy, int src, int sx, int
|
||||
gfx_sub_da_daxsa(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h);
|
||||
}
|
||||
|
||||
static void DrawGraph_BrightDestOnly(int dst, int x, int y, int w, int h, int rate)
|
||||
{
|
||||
gfx_bright_dest_only(DTEX(dst), x, y, w, h, rate);
|
||||
}
|
||||
|
||||
//void DrawGraph_CopyTextureWrap(int dst, int dx, int dy, int dw, int dh, int src, int sx, int sy, int sw, int sh, int u, int v);
|
||||
//void DrawGraph_CopyTextureWrapAlpha(int dst, int dx, int dy, int dw, int dh, int src, int sx, int sy, int sw, int sh, int u, int v, int alpha);
|
||||
|
||||
static void DrawGraph_CopyStretch(int dst, int dx, int dy, int dw, int dh, int src, int sx, int sy, int sw, int sh)
|
||||
{
|
||||
gfx_copy_stretch(DTEX(dst), dx, dy, dw, dh, STEX(src), sx, sy, sw, sh);
|
||||
}
|
||||
|
||||
static void DrawGraph_CopyStretchBlend(int dst, int dx, int dy, int dw, int dh, int src, int sx, int sy, int sw, int sh, int alpha)
|
||||
{
|
||||
gfx_copy_stretch_blend(DTEX(dst), dx, dy, dw, dh, STEX(src), sx, sy, sw, sh, alpha);
|
||||
}
|
||||
|
||||
static void DrawGraph_CopyStretchBlendAMap(int dst, int dx, int dy, int dw, int dh, int src, int sx, int sy, int sw, int sh)
|
||||
{
|
||||
gfx_copy_stretch_blend_amap(DTEX(dst), dx, dy, dw, dh, STEX(src), sx, sy, sw, sh);
|
||||
}
|
||||
|
||||
static void DrawGraph_CopyStretchAMap(int dst, int dx, int dy, int dw, int dh, int src, int sx, int sy, int sw, int sh)
|
||||
{
|
||||
gfx_copy_stretch_amap(DTEX(dst), dx, dy, dw, dh, STEX(src), sx, sy, sw, sh);
|
||||
}
|
||||
|
||||
//void DrawGraph_CopyStretchInterp(int dst, int dx, int dy, int dw, int dh, int src, int sx, int sy, int sw, int sh);
|
||||
//void DrawGraph_CopyStretchAMapInterp(int dst, int dx, int dy, int dw, int dh, int src, int sx, int sy, int sw, int sh);
|
||||
|
||||
static void DrawGraph_DrawTextToPMap(int dst, int x, int y, struct string *s)
|
||||
{
|
||||
gfx_draw_text_to_pmap(DTEX(dst), x, y, s->text);
|
||||
@@ -151,6 +263,13 @@ static void DrawGraph_DrawTextToAMap(int dst, int x, int y, struct string *s)
|
||||
gfx_draw_text_to_amap(DTEX(dst), x, y, s->text);
|
||||
}
|
||||
|
||||
static void DrawGraph_SetFontName(struct string *text)
|
||||
{
|
||||
gfx_set_font_name(text->text);
|
||||
}
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED(string_ref(&EMPTY_STRING), struct string*, DrawGraph, GetFontName, void);
|
||||
|
||||
static void DrawGraph_SetFontColor(int r, int g, int b)
|
||||
{
|
||||
gfx_set_font_color(COLOR(r, g, b, 255));
|
||||
@@ -179,82 +298,6 @@ static void DrawGraph_CopyRotZoomUseAMap(int dst, int src, int sx, int sy, int w
|
||||
gfx_copy_rot_zoom_use_amap(DTEX(dst), STEX(src), sx, sy, w, h, rotate, mag);
|
||||
}
|
||||
|
||||
static void DrawGraph_CopyReverseLR(int dst, int dx, int dy, int src, int sx, int sy, int w, int h)
|
||||
{
|
||||
gfx_copy_reverse_LR(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h);
|
||||
}
|
||||
|
||||
static void DrawGraph_CopyReverseAMapLR(int dst, int dx, int dy, int src, int sx, int sy, int w, int h)
|
||||
{
|
||||
gfx_copy_reverse_amap_LR(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h);
|
||||
}
|
||||
|
||||
static bool DrawGraph_GetAlphaColor(int surface, int x, int y, int *a)
|
||||
{
|
||||
struct sact_sprite *sp = sact_get_sprite(surface);
|
||||
// NOTE: System40.exe errors here
|
||||
if (!sp)
|
||||
VM_ERROR("Invalid sprite index: %d", surface);
|
||||
if (x < 0 || x >= sp->rect.w || y < 0 || y >= sp->rect.h)
|
||||
return false;
|
||||
*a = sact_SP_GetAMapValue(surface, x, y);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void DrawGraph_SetFontName(struct string *text)
|
||||
{
|
||||
gfx_set_font_name(text->text);
|
||||
}
|
||||
|
||||
//void DrawGraph_BlendSrcBright(int dst, int dx, int dy, int src, int sx, int sy, int w, int h, int alpha, int rate);
|
||||
//void DrawGraph_BlendAddSatur(int dst, int dx, int dy, int src, int sx, int sy, int w, int h);
|
||||
//void DrawGraph_BlendAMapSrcOnly(int dst, int dx, int dy, int src, int sx, int sy, int w, int h);
|
||||
//void DrawGraph_BlendAMapColorAlpha(int dst, int dx, int dy, int src, int sx, int sy, int w, int h, int r, int g, int b, int a);
|
||||
//void DrawGraph_BlendAMapBright(int dst, int dx, int dy, int src, int sx, int sy, int w, int h, int rate);
|
||||
//void DrawGraph_BlendAMapAlphaSrcBright(int dst, int dx, int dy, int src, int sx, int sy, int w, int h, int alpha, int rate);
|
||||
//void DrawGraph_BlendUseAMapColor(int dst, int dx, int dy, int alpha, int ax, int ay, int w, int h, int r, int g, int b, int rate);
|
||||
//void DrawGraph_BlendScreen(int dst, int dx, int dy, int src, int sx, int sy, int w, int h);
|
||||
//void DrawGraph_BlendMultiply(int dst, int dx, int dy, int src, int sx, int sy, int w, int h);
|
||||
//void DrawGraph_BlendScreenAlpha(int dst, int dx, int dy, int src, int sx, int sy, int w, int h, int alpha);
|
||||
|
||||
static void DrawGraph_FillAMapOverBorder(int dst, int x, int y, int w, int h, int alpha, int border)
|
||||
{
|
||||
gfx_fill_amap_over_border(DTEX(dst), x, y, w, h, alpha, border);
|
||||
}
|
||||
|
||||
static void DrawGraph_FillAMapUnderBorder(int dst, int x, int y, int w, int h, int alpha, int border)
|
||||
{
|
||||
gfx_fill_amap_under_border(DTEX(dst), x, y, w, h, alpha, border);
|
||||
}
|
||||
|
||||
//void DrawGraph_FillAMapGradationUD(int dst, int x, int y, int w, int h, int up_a, int down_a);
|
||||
//void DrawGraph_FillScreen(int dst, int x, int y, int w, int h, int r, int g, int b);
|
||||
//void DrawGraph_FillMultiply(int dst, int x, int y, int w, int h, int r, int g, int b);
|
||||
//void DrawGraph_SaturDP_DPxSA(int dst, int dx, int dy, int src, int sx, int sy, int w, int h);
|
||||
//void DrawGraph_ScreenDA_DAxSA(int dst, int dx, int dy, int src, int sx, int sy, int w, int h);
|
||||
|
||||
static void DrawGraph_SpriteCopyAMap(int dst, int dx, int dy, int src, int sx, int sy, int w, int h, int key)
|
||||
{
|
||||
gfx_sprite_copy_amap(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h, key);
|
||||
}
|
||||
|
||||
//void DrawGraph_BrightDestOnly(int dst, int x, int y, int w, int h, int rate);
|
||||
//void DrawGraph_CopyTextureWrap(int dst, int dx, int dy, int dw, int dh, int src, int sx, int sy, int sw, int sh, int u, int v);
|
||||
//void DrawGraph_CopyTextureWrapAlpha(int dst, int dx, int dy, int dw, int dh, int src, int sx, int sy, int sw, int sh, int u, int v, int alpha);
|
||||
|
||||
static void DrawGraph_CopyStretchBlend(int dst, int dx, int dy, int dw, int dh, int src, int sx, int sy, int sw, int sh, int alpha)
|
||||
{
|
||||
gfx_copy_stretch_blend(DTEX(dst), dx, dy, dw, dh, STEX(src), sx, sy, sw, sh, alpha);
|
||||
}
|
||||
|
||||
static void DrawGraph_CopyStretchBlendAMap(int dst, int dx, int dy, int dw, int dh, int src, int sx, int sy, int sw, int sh)
|
||||
{
|
||||
gfx_copy_stretch_blend_amap(DTEX(dst), dx, dy, dw, dh, STEX(src), sx, sy, sw, sh);
|
||||
}
|
||||
|
||||
//void DrawGraph_CopyStretchInterp(int dst, int dx, int dy, int dw, int dh, int src, int sx, int sy, int sw, int sh);
|
||||
//void DrawGraph_CopyStretchAMapInterp(int dst, int dx, int dy, int dw, int dh, int src, int sx, int sy, int sw, int sh);
|
||||
HLL_WARN_UNIMPLEMENTED(string_ref(&EMPTY_STRING), struct string*, DrawGraph, GetFontName, void);
|
||||
//void DrawGraph_CopyRotZoom2Bilinear(int dst, float cx, float cy, int src, float scx, float scy, float rot, float mag);
|
||||
|
||||
static void DrawGraph_CopyRotateY(int write, int dst, int src, int sx, int sy, int w, int h, float rot, float mag)
|
||||
@@ -271,13 +314,34 @@ static void DrawGraph_CopyRotateYUseAMap(int write, int dst, int src, int sx, in
|
||||
//void DrawGraph_CopyRotateYFixR(int write, int dst, int src, int sx, int sy, int w, int h, float rot, float mag);
|
||||
//void DrawGraph_CopyRotateYFixLUseAMap(int write, int dst, int src, int sx, int sy, int w, int h, float rot, float mag);
|
||||
//void DrawGraph_CopyRotateYFixRUseAMap(int write, int dst, int src, int sx, int sy, int w, int h, float rot, float mag);
|
||||
//void DrawGraph_CopyRotateX(int write, int dst, int src, int sx, int sy, int w, int h, float rot, float mag);
|
||||
//void DrawGraph_CopyRotateXUseAMap(int write, int dst, int src, int sx, int sy, int w, int h, float rot, float mag);
|
||||
|
||||
static void DrawGraph_CopyRotateX(int write, int dst, int src, int sx, int sy, int w, int h, float rot, float mag)
|
||||
{
|
||||
gfx_copy_rotate_x(DTEX(write), STEX(dst), STEX(src), sx, sy, w, h, rot, mag);
|
||||
}
|
||||
|
||||
static void DrawGraph_CopyRotateXUseAMap(int write, int dst, int src, int sx, int sy, int w, int h, float rot, float mag)
|
||||
{
|
||||
gfx_copy_rotate_x_use_amap(DTEX(write), STEX(dst), STEX(src), sx, sy, w, h, rot, mag);
|
||||
}
|
||||
|
||||
//void DrawGraph_CopyRotateXFixU(int write, int dst, int src, int sx, int sy, int w, int h, float rot, float mag);
|
||||
//void DrawGraph_CopyRotateXFixD(int write, int dst, int src, int sx, int sy, int w, int h, float rot, float mag);
|
||||
//void DrawGraph_CopyRotateXFixUUseAMap(int write, int dst, int src, int sx, int sy, int w, int h, float rot, float mag);
|
||||
//void DrawGraph_CopyRotateXFixDUseAMap(int write, int dst, int src, int sx, int sy, int w, int h, float rot, float mag);
|
||||
|
||||
static void DrawGraph_CopyReverseLR(int dst, int dx, int dy, int src, int sx, int sy, int w, int h)
|
||||
{
|
||||
gfx_copy_reverse_LR(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h);
|
||||
}
|
||||
|
||||
//void DrawGraph_CopyReverseUD(int dst, int dx, int dy, int src, int sx, int sy, int w, int h);
|
||||
|
||||
static void DrawGraph_CopyReverseAMapLR(int dst, int dx, int dy, int src, int sx, int sy, int w, int h)
|
||||
{
|
||||
gfx_copy_reverse_amap_LR(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h);
|
||||
}
|
||||
|
||||
//void DrawGraph_CopyReverseAMapUD(int dst, int dx, int dy, int src, int sx, int sy, int w, int h);
|
||||
|
||||
static void DrawGraph_CopyWidthBlur(int dst, int dx, int dy, int src, int sx, int sy, int w, int h, int blur)
|
||||
@@ -301,9 +365,24 @@ static void DrawGraph_CopyAMapHeightBlur(int dst, int dx, int dy, int src, int s
|
||||
}
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED( , void, DrawGraph, DrawLine, int dst, int x0, int y0, int x1, int y1, int r, int g, int b);
|
||||
|
||||
//void DrawGraph_DrawLineToAMap(int dst, int x0, int y0, int x1, int y1, int alpha);
|
||||
|
||||
static bool DrawGraph_GetAlphaColor(int surface, int x, int y, int *a)
|
||||
{
|
||||
struct sact_sprite *sp = sact_get_sprite(surface);
|
||||
// NOTE: System40.exe errors here
|
||||
if (!sp)
|
||||
VM_ERROR("Invalid sprite index: %d", surface);
|
||||
if (x < 0 || x >= sp->rect.w || y < 0 || y >= sp->rect.h)
|
||||
return false;
|
||||
*a = sact_SP_GetAMapValue(surface, x, y);
|
||||
return true;
|
||||
}
|
||||
|
||||
//void DrawGraph_DrawPolygon(int dst, int tex, float x0, float y0, float z0, float u0, float v0, float x1, float y1, float z1, float u1, float v1, float x2, float y2, float z2, float u2, float v2);
|
||||
//void DrawGraph_DrawColorPolygon(int dst, int tex, float x0, float y0, float z0, int r0, int g0, int b0, int a0, float x1, float y1, float z1, int r1, int g1, int b1, int a1, float x2, float y2, float z2, int r2, int g2, int b2, int a2);
|
||||
|
||||
static void DrawGraph_CopyWithAlphaMap(int dst, int dx, int dy, int src, int sx, int sy, int w, int h)
|
||||
{
|
||||
gfx_copy_with_alpha_map(DTEX(dst), dx, dy, STEX(src), sx, sy, w, h);
|
||||
@@ -332,34 +411,34 @@ HLL_LIBRARY(DrawGraph,
|
||||
HLL_EXPORT(CopyAMapMax, DrawGraph_CopyAMapMax),
|
||||
HLL_EXPORT(CopyAMapMin, DrawGraph_CopyAMapMin),
|
||||
HLL_EXPORT(Blend, DrawGraph_Blend),
|
||||
//HLL_EXPORT(BlendSrcBright, DrawGraph_BlendSrcBright),
|
||||
//HLL_EXPORT(BlendAddSatur, DrawGraph_BlendAddSatur),
|
||||
HLL_EXPORT(BlendSrcBright, DrawGraph_BlendSrcBright),
|
||||
HLL_EXPORT(BlendAddSatur, DrawGraph_BlendAddSatur),
|
||||
HLL_EXPORT(BlendAMap, DrawGraph_BlendAMap),
|
||||
//HLL_EXPORT(BlendAMapSrcOnly, DrawGraph_BlendAMapSrcOnly),
|
||||
HLL_EXPORT(BlendAMapSrcOnly, DrawGraph_BlendAMapSrcOnly),
|
||||
HLL_EXPORT(BlendAMapColor, DrawGraph_BlendAMapColor),
|
||||
//HLL_EXPORT(BlendAMapColorAlpha, DrawGraph_BlendAMapColorAlpha),
|
||||
HLL_EXPORT(BlendAMapColorAlpha, DrawGraph_BlendAMapColorAlpha),
|
||||
HLL_EXPORT(BlendAMapAlpha, DrawGraph_BlendAMapAlpha),
|
||||
//HLL_EXPORT(BlendAMapBright, DrawGraph_BlendAMapBright),
|
||||
//HLL_EXPORT(BlendAMapAlphaSrcBright, DrawGraph_BlendAMapAlphaSrcBright),
|
||||
//HLL_EXPORT(BlendUseAMapColor, DrawGraph_BlendUseAMapColor),
|
||||
//HLL_EXPORT(BlendScreen, DrawGraph_BlendScreen),
|
||||
//HLL_EXPORT(BlendMultiply, DrawGraph_BlendMultiply),
|
||||
//HLL_EXPORT(BlendScreenAlpha, DrawGraph_BlendScreenAlpha),
|
||||
HLL_EXPORT(BlendAMapBright, DrawGraph_BlendAMapBright),
|
||||
HLL_EXPORT(BlendAMapAlphaSrcBright, DrawGraph_BlendAMapAlphaSrcBright),
|
||||
HLL_EXPORT(BlendUseAMapColor, DrawGraph_BlendUseAMapColor),
|
||||
HLL_EXPORT(BlendScreen, DrawGraph_BlendScreen),
|
||||
HLL_EXPORT(BlendMultiply, DrawGraph_BlendMultiply),
|
||||
HLL_EXPORT(BlendScreenAlpha, DrawGraph_BlendScreenAlpha),
|
||||
HLL_EXPORT(Fill, DrawGraph_Fill),
|
||||
HLL_EXPORT(FillAlphaColor, DrawGraph_FillAlphaColor),
|
||||
HLL_EXPORT(FillAMap, DrawGraph_FillAMap),
|
||||
HLL_EXPORT(FillAMapOverBorder, DrawGraph_FillAMapOverBorder),
|
||||
HLL_EXPORT(FillAMapUnderBorder, DrawGraph_FillAMapUnderBorder),
|
||||
//HLL_EXPORT(FillAMapGradationUD, DrawGraph_FillAMapGradationUD),
|
||||
//HLL_EXPORT(FillScreen, DrawGraph_FillScreen),
|
||||
//HLL_EXPORT(FillMultiply, DrawGraph_FillMultiply),
|
||||
//HLL_EXPORT(SaturDP_DPxSA, DrawGraph_SaturDP_DPxSA),
|
||||
//HLL_EXPORT(ScreenDA_DAxSA, DrawGraph_ScreenDA_DAxSA),
|
||||
HLL_EXPORT(FillAMapGradationUD, DrawGraph_FillAMapGradationUD),
|
||||
HLL_EXPORT(FillScreen, DrawGraph_FillScreen),
|
||||
HLL_EXPORT(FillMultiply, DrawGraph_FillMultiply),
|
||||
HLL_EXPORT(SaturDP_DPxSA, DrawGraph_SaturDP_DPxSA),
|
||||
HLL_EXPORT(ScreenDA_DAxSA, DrawGraph_ScreenDA_DAxSA),
|
||||
HLL_EXPORT(AddDA_DAxSA, DrawGraph_AddDA_DAxSA),
|
||||
HLL_EXPORT(SpriteCopyAMap, DrawGraph_SpriteCopyAMap),
|
||||
HLL_EXPORT(BlendDA_DAxSA, DrawGraph_BlendDA_DAxSA),
|
||||
HLL_EXPORT(SubDA_DAxSA, DrawGraph_SubDA_DAxSA),
|
||||
//HLL_EXPORT(BrightDestOnly, DrawGraph_BrightDestOnly),
|
||||
HLL_EXPORT(BrightDestOnly, DrawGraph_BrightDestOnly),
|
||||
//HLL_EXPORT(CopyTextureWrap, DrawGraph_CopyTextureWrap),
|
||||
//HLL_EXPORT(CopyTextureWrapAlpha, DrawGraph_CopyTextureWrapAlpha),
|
||||
HLL_EXPORT(CopyStretch, DrawGraph_CopyStretch),
|
||||
@@ -397,8 +476,8 @@ HLL_LIBRARY(DrawGraph,
|
||||
//HLL_EXPORT(CopyRotateYFixR, DrawGraph_CopyRotateYFixR),
|
||||
//HLL_EXPORT(CopyRotateYFixLUseAMap, DrawGraph_CopyRotateYFixLUseAMap),
|
||||
//HLL_EXPORT(CopyRotateYFixRUseAMap, DrawGraph_CopyRotateYFixRUseAMap),
|
||||
//HLL_EXPORT(CopyRotateX, DrawGraph_CopyRotateX),
|
||||
//HLL_EXPORT(CopyRotateXUseAMap, DrawGraph_CopyRotateXUseAMap),
|
||||
HLL_EXPORT(CopyRotateX, DrawGraph_CopyRotateX),
|
||||
HLL_EXPORT(CopyRotateXUseAMap, DrawGraph_CopyRotateXUseAMap),
|
||||
//HLL_EXPORT(CopyRotateXFixU, DrawGraph_CopyRotateXFixU),
|
||||
//HLL_EXPORT(CopyRotateXFixD, DrawGraph_CopyRotateXFixD),
|
||||
//HLL_EXPORT(CopyRotateXFixUUseAMap, DrawGraph_CopyRotateXFixUUseAMap),
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "system4/string.h"
|
||||
|
||||
#include "hll.h"
|
||||
#include "movie.h"
|
||||
#include "sact.h"
|
||||
|
||||
static struct movie_context *mc;
|
||||
|
||||
static void DrawMovie_Release(void)
|
||||
{
|
||||
if (mc)
|
||||
movie_free(mc);
|
||||
mc = NULL;
|
||||
}
|
||||
|
||||
static bool DrawMovie_Load(struct string *filename)
|
||||
{
|
||||
if (mc)
|
||||
movie_free(mc);
|
||||
mc = movie_load(filename->text);
|
||||
return !!mc;
|
||||
}
|
||||
|
||||
static bool DrawMovie_Run(void)
|
||||
{
|
||||
if (!mc)
|
||||
return false;
|
||||
return movie_play(mc);
|
||||
}
|
||||
|
||||
static bool DrawMovie_Draw(int sprite)
|
||||
{
|
||||
if (!mc)
|
||||
return false;
|
||||
|
||||
struct sact_sprite *sp = sact_try_get_sprite(sprite);
|
||||
if (!sp)
|
||||
return false;
|
||||
|
||||
return movie_draw(mc, sp);
|
||||
}
|
||||
|
||||
static bool DrawMovie_SetVolume(int volume)
|
||||
{
|
||||
if (!mc)
|
||||
return false;
|
||||
return movie_set_volume(mc, volume);
|
||||
}
|
||||
|
||||
static bool DrawMovie_IsEnd(void)
|
||||
{
|
||||
if (!mc)
|
||||
return true;
|
||||
return movie_is_end(mc);
|
||||
}
|
||||
|
||||
static int DrawMovie_GetCount(void)
|
||||
{
|
||||
if (!mc)
|
||||
return 0;
|
||||
return movie_get_position(mc);
|
||||
}
|
||||
|
||||
static void DrawMovie2_UpdateVolume(void)
|
||||
{
|
||||
// Do nothing, as SetInnerVolume immediately takes effect.
|
||||
}
|
||||
|
||||
HLL_LIBRARY(DrawMovie,
|
||||
HLL_EXPORT(Release, DrawMovie_Release),
|
||||
HLL_EXPORT(Load, DrawMovie_Load),
|
||||
HLL_EXPORT(Run, DrawMovie_Run),
|
||||
HLL_EXPORT(Draw, DrawMovie_Draw),
|
||||
HLL_EXPORT(SetVolume, DrawMovie_SetVolume),
|
||||
HLL_EXPORT(IsEnd, DrawMovie_IsEnd),
|
||||
HLL_EXPORT(GetCount, DrawMovie_GetCount)
|
||||
);
|
||||
|
||||
HLL_LIBRARY(DrawMovie2,
|
||||
HLL_EXPORT(Release, DrawMovie_Release),
|
||||
HLL_EXPORT(Load, DrawMovie_Load),
|
||||
HLL_EXPORT(Run, DrawMovie_Run),
|
||||
HLL_EXPORT(Draw, DrawMovie_Draw),
|
||||
HLL_EXPORT(SetVolume, DrawMovie_SetVolume),
|
||||
HLL_EXPORT(SetInnerVolume, DrawMovie_SetVolume),
|
||||
HLL_EXPORT(UpdateVolume, DrawMovie2_UpdateVolume),
|
||||
HLL_EXPORT(IsEnd, DrawMovie_IsEnd),
|
||||
HLL_EXPORT(GetCount, DrawMovie_GetCount)
|
||||
);
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "hll.h"
|
||||
#include "gfx/gfx.h"
|
||||
#include "gfx/font.h"
|
||||
#include "sact.h"
|
||||
|
||||
static int size = 20;
|
||||
|
||||
+1
-1
@@ -175,7 +175,7 @@ static int File_GetTime(struct string *filename, struct page **page)
|
||||
}
|
||||
|
||||
date[0].i = tm->tm_year + 1900;
|
||||
date[1].i = tm->tm_mon;
|
||||
date[1].i = tm->tm_mon + 1;
|
||||
date[2].i = tm->tm_mday;
|
||||
date[3].i = tm->tm_hour;
|
||||
date[4].i = tm->tm_min;
|
||||
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/file.h"
|
||||
#include "system4/string.h"
|
||||
|
||||
#include "little_endian.h"
|
||||
#include "hll.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
static FILE *current_file = NULL;
|
||||
|
||||
static bool file2_open(struct string *filename, const char *mode)
|
||||
{
|
||||
if (current_file) {
|
||||
WARNING("Previously opened file wasn't closed");
|
||||
fclose(current_file);
|
||||
}
|
||||
|
||||
char *path = unix_path(filename->text);
|
||||
current_file = file_open_utf8(path, mode);
|
||||
if (!current_file) {
|
||||
WARNING("Failed to open file '%s': %s", display_utf0(path), strerror(errno));
|
||||
}
|
||||
|
||||
free(path);
|
||||
return !!current_file;
|
||||
}
|
||||
|
||||
static bool file2_write(const void *ptr, size_t size)
|
||||
{
|
||||
if (!current_file) {
|
||||
WARNING("File2.Write* called, but no open file");
|
||||
return false;
|
||||
}
|
||||
if (fwrite(ptr, size, 1, current_file) != 1) {
|
||||
WARNING("fwrite failed: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool file2_read(void *dst, size_t size)
|
||||
{
|
||||
if (!current_file) {
|
||||
WARNING("File2.Read* called, but no open file");
|
||||
return false;
|
||||
}
|
||||
if (fread(dst, size, 1, current_file) != 1) {
|
||||
WARNING("fread failed: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool File2_OpenForWrite(struct string *filename)
|
||||
{
|
||||
return file2_open(filename, "wb");
|
||||
}
|
||||
|
||||
static bool File2_OpenForRead(struct string *filename)
|
||||
{
|
||||
return file2_open(filename, "rb");
|
||||
}
|
||||
|
||||
static bool File2_Close(void)
|
||||
{
|
||||
if (!current_file) {
|
||||
VM_ERROR("File2.Close called, but no open file");
|
||||
}
|
||||
int r = !fclose(current_file);
|
||||
current_file = NULL;
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool File2_WriteByte(int data)
|
||||
{
|
||||
uint8_t b = data;
|
||||
return file2_write(&b, 1);
|
||||
}
|
||||
|
||||
static bool File2_WriteInt(int data)
|
||||
{
|
||||
uint8_t b[4];
|
||||
LittleEndian_putDW(b, 0, data);
|
||||
return file2_write(b, 4);
|
||||
}
|
||||
|
||||
static bool File2_WriteFloat(float data)
|
||||
{
|
||||
uint8_t b[4];
|
||||
union {
|
||||
uint32_t i;
|
||||
float f;
|
||||
} cast;
|
||||
cast.f = data;
|
||||
LittleEndian_putDW(b, 0, cast.i);
|
||||
return file2_write(b, 4);
|
||||
}
|
||||
|
||||
static bool File2_WriteString(struct string *str)
|
||||
{
|
||||
return file2_write(str->text, str->size+1);
|
||||
}
|
||||
|
||||
static bool File2_ReadByte(int *data)
|
||||
{
|
||||
uint8_t b;
|
||||
if (!file2_read(&b, 1))
|
||||
return false;
|
||||
*data = b;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool File2_ReadInt(int *data)
|
||||
{
|
||||
uint8_t b[4];
|
||||
if (!file2_read(b, 4))
|
||||
return false;
|
||||
*data = LittleEndian_getDW(b, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool File2_ReadFloat(float *data)
|
||||
{
|
||||
uint8_t b[4];
|
||||
union {
|
||||
uint32_t i;
|
||||
float f;
|
||||
} cast;
|
||||
if (!file2_read(b, 4))
|
||||
return false;
|
||||
cast.i = LittleEndian_getDW(b, 0);
|
||||
*data = cast.f;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool File2_ReadString(struct string **str)
|
||||
{
|
||||
char b;
|
||||
struct string *s = make_string("", 0);
|
||||
while (true) {
|
||||
if (!file2_read((uint8_t*)&b, 1)) {
|
||||
free_string(s);
|
||||
return false;
|
||||
}
|
||||
if (!b)
|
||||
break;
|
||||
string_append_cstr(&s, &b, 1);
|
||||
}
|
||||
|
||||
if (*str)
|
||||
free_string(*str);
|
||||
*str = s;
|
||||
return true;
|
||||
}
|
||||
|
||||
HLL_LIBRARY(File2,
|
||||
HLL_EXPORT(OpenForWrite, File2_OpenForWrite),
|
||||
HLL_EXPORT(OpenForRead, File2_OpenForRead),
|
||||
HLL_EXPORT(Close, File2_Close),
|
||||
HLL_EXPORT(WriteByte, File2_WriteByte),
|
||||
HLL_EXPORT(WriteInt, File2_WriteInt),
|
||||
HLL_EXPORT(WriteFloat, File2_WriteFloat),
|
||||
HLL_EXPORT(WriteString, File2_WriteString),
|
||||
HLL_EXPORT(ReadByte, File2_ReadByte),
|
||||
HLL_EXPORT(ReadInt, File2_ReadInt),
|
||||
HLL_EXPORT(ReadFloat, File2_ReadFloat),
|
||||
HLL_EXPORT(ReadString, File2_ReadString)
|
||||
);
|
||||
@@ -0,0 +1,84 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#include "gfx/gfx.h"
|
||||
#include "hll.h"
|
||||
#include "sact.h"
|
||||
|
||||
struct fill_angele_shader {
|
||||
struct shader s;
|
||||
GLint center;
|
||||
GLint start_angle;
|
||||
GLint angle;
|
||||
GLint color;
|
||||
};
|
||||
static struct fill_angele_shader shader;
|
||||
|
||||
static void load_shader(void)
|
||||
{
|
||||
gfx_load_shader(&shader.s, "shaders/render.v.glsl", "shaders/fill_angle.f.glsl");
|
||||
shader.center = glGetUniformLocation(shader.s.program, "center");
|
||||
shader.start_angle = glGetUniformLocation(shader.s.program, "start_angle");
|
||||
shader.angle = glGetUniformLocation(shader.s.program, "angle");
|
||||
shader.color = glGetUniformLocation(shader.s.program, "color");
|
||||
}
|
||||
|
||||
static bool FillAngle_FillAngleAMAP(int sprite, int center_x, int center_y, int start_angle, int angle, int alpha)
|
||||
{
|
||||
struct sact_sprite *sp = sact_try_get_sprite(sprite);
|
||||
if (!sp)
|
||||
return false;
|
||||
struct texture *dst = sprite_get_texture(sp);
|
||||
float w = dst->w;
|
||||
float h = dst->h;
|
||||
|
||||
if (!shader.s.program)
|
||||
load_shader();
|
||||
glUseProgram(shader.s.program);
|
||||
glUniform2f(shader.center, center_x / w, center_y / h);
|
||||
glUniform1f(shader.start_angle, glm_rad(start_angle));
|
||||
glUniform1f(shader.angle, glm_rad(angle));
|
||||
glUniform4f(shader.color, 0, 0, 0, alpha / 255.0);
|
||||
glUseProgram(0);
|
||||
|
||||
mat4 mw_transform = MAT4(
|
||||
w, 0, 0, 0,
|
||||
0, h, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1);
|
||||
mat4 wv_transform = WV_TRANSFORM(dst->w, dst->h);
|
||||
GLuint fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, dst, 0, 0, dst->w, dst->h);
|
||||
|
||||
struct gfx_render_job job = {
|
||||
.shader = &shader.s,
|
||||
.texture = 0,
|
||||
.world_transform = mw_transform[0],
|
||||
.view_transform = wv_transform[0],
|
||||
.data = NULL
|
||||
};
|
||||
glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_ONE, GL_ZERO);
|
||||
gfx_render(&job);
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
||||
|
||||
gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo);
|
||||
return true;
|
||||
}
|
||||
|
||||
HLL_LIBRARY(FillAngle,
|
||||
HLL_EXPORT(FillAngleAMAP, FillAngle_FillAngleAMAP)
|
||||
);
|
||||
+60
-62
@@ -39,7 +39,6 @@ static void GUIEngine_ModuleFini(void)
|
||||
//static void GUIEngine_PushController(void);
|
||||
//static void GUIEngine_PopController(ref array<int> EraseNumberList);
|
||||
//static void GUIEngine_UpdateComponent(possibly_unused int passed_time)
|
||||
HLL_QUIET_UNIMPLEMENTED(, void, GUIEngine, UpdateComponent, int passed_time);
|
||||
//static void GUIEngine_BeginInput(void);
|
||||
//static void GUIEngine_EndInput(void);
|
||||
//static void GUIEngine_UpdateInputState(int PassedTime);
|
||||
@@ -47,6 +46,7 @@ HLL_QUIET_UNIMPLEMENTED(, void, GUIEngine, UpdateComponent, int passed_time);
|
||||
//static void GUIEngine_SetFocus(int PartsNumber);
|
||||
//static bool GUIEngine_IsFocus(int PartsNumber);
|
||||
//static void GUIEngine_StopSoundWithoutSystemSound(void);
|
||||
HLL_WARN_UNIMPLEMENTED(, void, GUIEngine, StopSoundWithoutSystemSound);
|
||||
//static void GUIEngine_ReleaseMessage(void);
|
||||
HLL_QUIET_UNIMPLEMENTED(, void, GUIEngine, ReleaseMessage);
|
||||
//static void GUIEngine_PopMessage(void);
|
||||
@@ -340,7 +340,6 @@ HLL_QUIET_UNIMPLEMENTED(0, int, GUIEngine, GetMessageType);
|
||||
//static bool GUIEngine_Parts_SetPartsCG(int PartsNumber, string pCGName, int SpriteDeform, int State);
|
||||
//static void GUIEngine_Parts_GetPartsCGName(int PartsNumber, ref string pCGName, int State);
|
||||
//static bool GUIEngine_Parts_SetPartsCGSurfaceArea(int PartsNumber, int X, int Y, int Width, int Height, int State);
|
||||
HLL_QUIET_UNIMPLEMENTED(0, bool, GUIEngine, Parts_SetPartsCGSurfaceArea, int parts_number, int x, int y, int w, int h, int state);
|
||||
//static bool GUIEngine_Parts_SetLoopCG(int PartsNumber, string pCGName, int StartNo, int NumofCG, int TimePerCG, int State);
|
||||
//static bool GUIEngine_Parts_SetLoopCGSurfaceArea(int PartsNumber, int X, int Y, int Width, int Height, int State);
|
||||
//static bool GUIEngine_Parts_SetText(int PartsNumber, string pIText, int State);
|
||||
@@ -424,7 +423,6 @@ HLL_QUIET_UNIMPLEMENTED(0, bool, GUIEngine, Parts_SetPartsCGSurfaceArea, int par
|
||||
//static void GUIEngine_Parts_SetSpeedupRateByMessageSkip(int PartsNumber, int Rate);
|
||||
//static void GUIEngine_Parts_SetResetTimerByChangeInputStatus(int PartsNumber, bool Reset, int State);
|
||||
//static void GUIEngine_Parts_SetAddColor(int PartsNumber, int nR, int nG, int nB);
|
||||
HLL_QUIET_UNIMPLEMENTED(, void, GUIEngine, Parts_SetAddColor, int parts_number, int r, int g, int b);
|
||||
//static void GUIEngine_Parts_SetMultiplyColor(int PartsNumber, int nR, int nG, int nB);
|
||||
//static void GUIEngine_Parts_SetDrag(int PartsNumber, bool Drag);
|
||||
//static void GUIEngine_Parts_SetPartsOriginPosMode(int PartsNumber, int OriginPosMode);
|
||||
@@ -506,24 +504,24 @@ HLL_LIBRARY(GUIEngine,
|
||||
HLL_EXPORT(Release, PE_ReleaseParts),
|
||||
HLL_EXPORT(ReleaseAll, PE_ReleaseAllParts),
|
||||
HLL_EXPORT(ReleaseAllWithoutSystem, PE_ReleaseAllPartsWithoutSystem),
|
||||
HLL_TODO_EXPORT(SetDelegateIndex, GUIEngine_SetDelegateIndex),
|
||||
HLL_EXPORT(SetDelegateIndex, PE_SetDelegateIndex),
|
||||
HLL_EXPORT(GetFreeNumber, PE_GetFreeNumber),
|
||||
HLL_EXPORT(IsExist, PE_IsExist),
|
||||
HLL_TODO_EXPORT(PushController, GUIEngine_PushController),
|
||||
HLL_TODO_EXPORT(PopController, GUIEngine_PopController),
|
||||
HLL_EXPORT(UpdateComponent, GUIEngine_UpdateComponent),
|
||||
HLL_EXPORT(UpdateComponent, PE_UpdateComponent),
|
||||
HLL_EXPORT(BeginInput, PE_BeginInput),
|
||||
HLL_EXPORT(EndInput, PE_EndInput),
|
||||
HLL_EXPORT(UpdateInputState, PE_UpdateInputState),
|
||||
HLL_EXPORT(UpdateParts, PE_UpdateParts),
|
||||
HLL_TODO_EXPORT(SetFocus, GUIEngine_SetFocus),
|
||||
HLL_TODO_EXPORT(IsFocus, GUIEngine_IsFocus),
|
||||
HLL_TODO_EXPORT(StopSoundWithoutSystemSound, GUIEngine_StopSoundWithoutSystemSound),
|
||||
HLL_EXPORT(StopSoundWithoutSystemSound, GUIEngine_StopSoundWithoutSystemSound),
|
||||
HLL_EXPORT(ReleaseMessage, GUIEngine_ReleaseMessage),
|
||||
HLL_TODO_EXPORT(PopMessage, GUIEngine_PopMessage),
|
||||
HLL_TODO_EXPORT(GetMessagePartsNumber, GUIEngine_GetMessagePartsNumber),
|
||||
HLL_TODO_EXPORT(GetMessageDelegateIndex, GUIEngine_GetMessageDelegateIndex),
|
||||
HLL_TODO_EXPORT(GetDelegateIndex, GUIEngine_GetDelegateIndex),
|
||||
HLL_EXPORT(GetDelegateIndex, PE_GetDelegateIndex),
|
||||
HLL_EXPORT(GetMessageType, GUIEngine_GetMessageType),
|
||||
HLL_TODO_EXPORT(GetMessageVariableCount, GUIEngine_GetMessageVariableCount),
|
||||
HLL_TODO_EXPORT(GetMessageVariableType, GUIEngine_GetMessageVariableType),
|
||||
@@ -808,42 +806,42 @@ HLL_LIBRARY(GUIEngine,
|
||||
HLL_TODO_EXPORT(SetMultiTextBoxCGName, GUIEngine_SetMultiTextBoxCGName),
|
||||
HLL_TODO_EXPORT(GetMultiTextBoxCGName, GUIEngine_GetMultiTextBoxCGName),
|
||||
HLL_EXPORT(Parts_SetPartsCG, PE_SetPartsCG),
|
||||
HLL_TODO_EXPORT(Parts_GetPartsCGName, GUIEngine_Parts_GetPartsCGName),
|
||||
HLL_EXPORT(Parts_SetPartsCGSurfaceArea, GUIEngine_Parts_SetPartsCGSurfaceArea),
|
||||
HLL_TODO_EXPORT(Parts_SetLoopCG, GUIEngine_Parts_SetLoopCG),
|
||||
HLL_TODO_EXPORT(Parts_SetLoopCGSurfaceArea, GUIEngine_Parts_SetLoopCGSurfaceArea),
|
||||
HLL_TODO_EXPORT(Parts_SetText, GUIEngine_Parts_SetText),
|
||||
HLL_TODO_EXPORT(Parts_AddPartsText, GUIEngine_Parts_AddPartsText),
|
||||
HLL_EXPORT(Parts_GetPartsCGName, PE_GetPartsCGName),
|
||||
HLL_EXPORT(Parts_SetPartsCGSurfaceArea, PE_SetPartsCGSurfaceArea),
|
||||
HLL_EXPORT(Parts_SetLoopCG, PE_SetLoopCG),
|
||||
HLL_EXPORT(Parts_SetLoopCGSurfaceArea, PE_SetLoopCGSurfaceArea),
|
||||
HLL_EXPORT(Parts_SetText, PE_SetText),
|
||||
HLL_EXPORT(Parts_AddPartsText, PE_AddPartsText),
|
||||
HLL_TODO_EXPORT(Parts_DeletePartsTopTextLine, GUIEngine_Parts_DeletePartsTopTextLine),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsTextSurfaceArea, GUIEngine_Parts_SetPartsTextSurfaceArea),
|
||||
HLL_EXPORT(Parts_SetPartsTextSurfaceArea, PE_SetPartsTextSurfaceArea),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsTextHighlight, GUIEngine_Parts_SetPartsTextHighlight),
|
||||
HLL_TODO_EXPORT(Parts_AddPartsTextHighlight, GUIEngine_Parts_AddPartsTextHighlight),
|
||||
HLL_TODO_EXPORT(Parts_ClearPartsTextHighlight, GUIEngine_Parts_ClearPartsTextHighlight),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsTextCountReturn, GUIEngine_Parts_SetPartsTextCountReturn),
|
||||
HLL_TODO_EXPORT(Parts_GetPartsTextCountReturn, GUIEngine_Parts_GetPartsTextCountReturn),
|
||||
HLL_TODO_EXPORT(Parts_SetFont, GUIEngine_Parts_SetFont),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsFontType, GUIEngine_Parts_SetPartsFontType),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsFontSize, GUIEngine_Parts_SetPartsFontSize),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsFontColor, GUIEngine_Parts_SetPartsFontColor),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsFontBoldWeight, GUIEngine_Parts_SetPartsFontBoldWeight),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsFontEdgeColor, GUIEngine_Parts_SetPartsFontEdgeColor),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsFontEdgeWeight, GUIEngine_Parts_SetPartsFontEdgeWeight),
|
||||
HLL_TODO_EXPORT(Parts_SetTextCharSpace, GUIEngine_Parts_SetTextCharSpace),
|
||||
HLL_TODO_EXPORT(Parts_SetTextLineSpace, GUIEngine_Parts_SetTextLineSpace),
|
||||
HLL_EXPORT(Parts_SetFont, PE_SetFont),
|
||||
HLL_EXPORT(Parts_SetPartsFontType, PE_SetPartsFontType),
|
||||
HLL_EXPORT(Parts_SetPartsFontSize, PE_SetPartsFontSize),
|
||||
HLL_EXPORT(Parts_SetPartsFontColor, PE_SetPartsFontColor),
|
||||
HLL_EXPORT(Parts_SetPartsFontBoldWeight, PE_SetPartsFontBoldWeight),
|
||||
HLL_EXPORT(Parts_SetPartsFontEdgeColor, PE_SetPartsFontEdgeColor),
|
||||
HLL_EXPORT(Parts_SetPartsFontEdgeWeight, PE_SetPartsFontEdgeWeight),
|
||||
HLL_EXPORT(Parts_SetTextCharSpace, PE_SetTextCharSpace),
|
||||
HLL_EXPORT(Parts_SetTextLineSpace, PE_SetTextLineSpace),
|
||||
HLL_TODO_EXPORT(Parts_SetHGaugeCG, GUIEngine_Parts_SetHGaugeCG),
|
||||
HLL_TODO_EXPORT(Parts_SetHGaugeRate, GUIEngine_Parts_SetHGaugeRate),
|
||||
HLL_TODO_EXPORT(Parts_SetVGaugeCG, GUIEngine_Parts_SetVGaugeCG),
|
||||
HLL_TODO_EXPORT(Parts_SetVGaugeRate, GUIEngine_Parts_SetVGaugeRate),
|
||||
HLL_TODO_EXPORT(Parts_SetHGaugeSurfaceArea, GUIEngine_Parts_SetHGaugeSurfaceArea),
|
||||
HLL_TODO_EXPORT(Parts_SetVGaugeSurfaceArea, GUIEngine_Parts_SetVGaugeSurfaceArea),
|
||||
HLL_EXPORT(Parts_SetHGaugeSurfaceArea, PE_SetHGaugeSurfaceArea),
|
||||
HLL_EXPORT(Parts_SetVGaugeSurfaceArea, PE_SetVGaugeSurfaceArea),
|
||||
HLL_TODO_EXPORT(Parts_SetNumeralCG, GUIEngine_Parts_SetNumeralCG),
|
||||
HLL_TODO_EXPORT(Parts_SetNumeralLinkedCGNumberWidthWidthList, GUIEngine_Parts_SetNumeralLinkedCGNumberWidthWidthList),
|
||||
HLL_EXPORT(Parts_SetNumeralLinkedCGNumberWidthWidthList, PE_SetNumeralLinkedCGNumberWidthWidthList),
|
||||
HLL_TODO_EXPORT(Parts_SetNumeralFont, GUIEngine_Parts_SetNumeralFont),
|
||||
HLL_TODO_EXPORT(Parts_SetNumeralNumber, GUIEngine_Parts_SetNumeralNumber),
|
||||
HLL_TODO_EXPORT(Parts_SetNumeralShowComma, GUIEngine_Parts_SetNumeralShowComma),
|
||||
HLL_TODO_EXPORT(Parts_SetNumeralSpace, GUIEngine_Parts_SetNumeralSpace),
|
||||
HLL_TODO_EXPORT(Parts_SetNumeralLength, GUIEngine_Parts_SetNumeralLength),
|
||||
HLL_TODO_EXPORT(Parts_SetNumeralSurfaceArea, GUIEngine_Parts_SetNumeralSurfaceArea),
|
||||
HLL_EXPORT(Parts_SetNumeralNumber, PE_SetNumeralNumber),
|
||||
HLL_EXPORT(Parts_SetNumeralShowComma, PE_SetNumeralShowComma),
|
||||
HLL_EXPORT(Parts_SetNumeralSpace, PE_SetNumeralSpace),
|
||||
HLL_EXPORT(Parts_SetNumeralLength, PE_SetNumeralLength),
|
||||
HLL_EXPORT(Parts_SetNumeralSurfaceArea, PE_SetNumeralSurfaceArea),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsRectangleDetectionSize, GUIEngine_Parts_SetPartsRectangleDetectionSize),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsRectangleDetectionSurfaceArea, GUIEngine_Parts_SetPartsRectangleDetectionSurfaceArea),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsCGDetectionSize, GUIEngine_Parts_SetPartsCGDetectionSize),
|
||||
@@ -861,25 +859,25 @@ HLL_LIBRARY(GUIEngine,
|
||||
HLL_TODO_EXPORT(Parts_GetPartsFlatEndFrame, GUIEngine_Parts_GetPartsFlatEndFrame),
|
||||
HLL_TODO_EXPORT(Parts_ExistsFlatFile, GUIEngine_Parts_ExistsFlatFile),
|
||||
HLL_TODO_EXPORT(Parts_ClearPartsConstructionProcess, GUIEngine_Parts_ClearPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddCreateToPartsConstructionProcess, GUIEngine_Parts_AddCreateToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddCreatePixelOnlyToPartsConstructionProcess, GUIEngine_Parts_AddCreatePixelOnlyToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddCreateToPartsConstructionProcess, PE_AddCreateToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddCreatePixelOnlyToPartsConstructionProcess, PE_AddCreatePixelOnlyToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddCreateCGToProcess, PE_AddCreateCGToProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddFillToPartsConstructionProcess, GUIEngine_Parts_AddFillToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddFillToPartsConstructionProcess, PE_AddFillToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddFillAlphaColorToPartsConstructionProcess, PE_AddFillAlphaColorToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddFillAMapToPartsConstructionProcess, GUIEngine_Parts_AddFillAMapToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddFillAMapToPartsConstructionProcess, PE_AddFillAMapToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddFillWithAlphaToPartsConstructionProcess, GUIEngine_Parts_AddFillWithAlphaToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddFillGradationHorizonToPartsConstructionProcess, GUIEngine_Parts_AddFillGradationHorizonToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddDrawRectToPartsConstructionProcess, GUIEngine_Parts_AddDrawRectToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddDrawCutCGToPartsConstructionProcess, GUIEngine_Parts_AddDrawCutCGToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddCopyCutCGToPartsConstructionProcess, GUIEngine_Parts_AddCopyCutCGToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddDrawCutCGToPartsConstructionProcess, PE_AddDrawCutCGToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddCopyCutCGToPartsConstructionProcess, PE_AddCopyCutCGToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddGrayFilterToPartsConstructionProcess, GUIEngine_Parts_AddGrayFilterToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddAddFilterToPartsConstructionProcess, GUIEngine_Parts_AddAddFilterToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddMulFilterToPartsConstructionProcess, GUIEngine_Parts_AddMulFilterToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddDrawLineToPartsConstructionProcess, GUIEngine_Parts_AddDrawLineToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_BuildPartsConstructionProcess, PE_BuildPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddDrawTextToPartsConstructionProcess, GUIEngine_Parts_AddDrawTextToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_AddCopyTextToPartsConstructionProcess, GUIEngine_Parts_AddCopyTextToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsConstructionSurfaceArea, GUIEngine_Parts_SetPartsConstructionSurfaceArea),
|
||||
HLL_EXPORT(Parts_AddDrawTextToPartsConstructionProcess, PE_AddDrawTextToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_AddCopyTextToPartsConstructionProcess, PE_AddCopyTextToPartsConstructionProcess),
|
||||
HLL_EXPORT(Parts_SetPartsConstructionSurfaceArea, PE_SetPartsConstructionSurfaceArea),
|
||||
HLL_TODO_EXPORT(Parts_CreateParts3DLayerPluginID, GUIEngine_Parts_CreateParts3DLayerPluginID),
|
||||
HLL_TODO_EXPORT(Parts_GetParts3DLayerPluginID, GUIEngine_Parts_GetParts3DLayerPluginID),
|
||||
HLL_TODO_EXPORT(Parts_ReleaseParts3DLayerPluginID, GUIEngine_Parts_ReleaseParts3DLayerPluginID),
|
||||
@@ -887,28 +885,28 @@ HLL_LIBRARY(GUIEngine,
|
||||
HLL_EXPORT(Parts_SetZ, PE_SetZ),
|
||||
HLL_EXPORT(Parts_SetShow, PE_SetShow),
|
||||
HLL_EXPORT(Parts_SetAlpha, PE_SetAlpha),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsDrawFilter, GUIEngine_Parts_SetPartsDrawFilter),
|
||||
HLL_EXPORT(Parts_SetPartsDrawFilter, PE_SetPartsDrawFilter),
|
||||
HLL_TODO_EXPORT(Parts_SetPassCursor, GUIEngine_Parts_SetPassCursor),
|
||||
HLL_TODO_EXPORT(Parts_SetClickable, GUIEngine_Parts_SetClickable),
|
||||
HLL_TODO_EXPORT(Parts_SetSpeedupRateByMessageSkip, GUIEngine_Parts_SetSpeedupRateByMessageSkip),
|
||||
HLL_TODO_EXPORT(Parts_SetResetTimerByChangeInputStatus, GUIEngine_Parts_SetResetTimerByChangeInputStatus),
|
||||
HLL_EXPORT(Parts_SetAddColor, GUIEngine_Parts_SetAddColor),
|
||||
HLL_TODO_EXPORT(Parts_SetMultiplyColor, GUIEngine_Parts_SetMultiplyColor),
|
||||
HLL_EXPORT(Parts_SetAddColor, PE_SetAddColor),
|
||||
HLL_EXPORT(Parts_SetMultiplyColor, PE_SetMultiplyColor),
|
||||
HLL_TODO_EXPORT(Parts_SetDrag, GUIEngine_Parts_SetDrag),
|
||||
HLL_EXPORT(Parts_SetPartsOriginPosMode, PE_SetPartsOriginPosMode),
|
||||
HLL_EXPORT(Parts_SetParentPartsNumber, PE_SetParentPartsNumber),
|
||||
HLL_TODO_EXPORT(Parts_SetInputState, GUIEngine_Parts_SetInputState),
|
||||
HLL_TODO_EXPORT(Parts_SetOnCursorShowLinkPartsNumber, GUIEngine_Parts_SetOnCursorShowLinkPartsNumber),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsMessageWindowShowLink, GUIEngine_Parts_SetPartsMessageWindowShowLink),
|
||||
HLL_EXPORT(Parts_SetPartsMessageWindowShowLink, PE_SetPartsMessageWindowShowLink),
|
||||
HLL_TODO_EXPORT(Parts_SetSoundNumber, GUIEngine_Parts_SetSoundNumber),
|
||||
HLL_TODO_EXPORT(Parts_SetClickMissSoundNumber, GUIEngine_Parts_SetClickMissSoundNumber),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsMagX, GUIEngine_Parts_SetPartsMagX),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsMagY, GUIEngine_Parts_SetPartsMagY),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsRotateX, GUIEngine_Parts_SetPartsRotateX),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsRotateY, GUIEngine_Parts_SetPartsRotateY),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsRotateZ, GUIEngine_Parts_SetPartsRotateZ),
|
||||
HLL_EXPORT(Parts_SetPartsMagX, PE_SetPartsMagX),
|
||||
HLL_EXPORT(Parts_SetPartsMagY, PE_SetPartsMagY),
|
||||
HLL_EXPORT(Parts_SetPartsRotateX, PE_SetPartsRotateX),
|
||||
HLL_EXPORT(Parts_SetPartsRotateY, PE_SetPartsRotateY),
|
||||
HLL_EXPORT(Parts_SetPartsRotateZ, PE_SetPartsRotateZ),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsAlphaClipperPartsNumber, GUIEngine_Parts_SetPartsAlphaClipperPartsNumber),
|
||||
HLL_TODO_EXPORT(Parts_SetPartsPixelDecide, GUIEngine_Parts_SetPartsPixelDecide),
|
||||
HLL_EXPORT(Parts_SetPartsPixelDecide, PE_SetPartsPixelDecide),
|
||||
HLL_EXPORT(Parts_GetPartsX, PE_GetPartsX),
|
||||
HLL_EXPORT(Parts_GetPartsY, PE_GetPartsY),
|
||||
HLL_EXPORT(Parts_GetPartsZ, PE_GetPartsZ),
|
||||
@@ -936,12 +934,12 @@ HLL_LIBRARY(GUIEngine,
|
||||
HLL_TODO_EXPORT(Parts_GetPartsRotateZ, GUIEngine_Parts_GetPartsRotateZ),
|
||||
HLL_TODO_EXPORT(Parts_GetPartsAlphaClipperPartsNumber, GUIEngine_Parts_GetPartsAlphaClipperPartsNumber),
|
||||
HLL_TODO_EXPORT(Parts_IsPartsPixelDecide, GUIEngine_Parts_IsPartsPixelDecide),
|
||||
HLL_TODO_EXPORT(Parts_GetPartsUpperLeftPosX, GUIEngine_Parts_GetPartsUpperLeftPosX),
|
||||
HLL_TODO_EXPORT(Parts_GetPartsUpperLeftPosY, GUIEngine_Parts_GetPartsUpperLeftPosY),
|
||||
HLL_EXPORT(Parts_GetPartsUpperLeftPosX, PE_GetPartsUpperLeftPosX),
|
||||
HLL_EXPORT(Parts_GetPartsUpperLeftPosY, PE_GetPartsUpperLeftPosY),
|
||||
HLL_EXPORT(Parts_GetPartsWidth, PE_GetPartsWidth),
|
||||
HLL_EXPORT(Parts_GetPartsHeight, PE_GetPartsHeight),
|
||||
HLL_TODO_EXPORT(Parts_AddMotionPos, GUIEngine_Parts_AddMotionPos),
|
||||
HLL_TODO_EXPORT(Parts_AddMotionAlpha, GUIEngine_Parts_AddMotionAlpha),
|
||||
HLL_EXPORT(Parts_AddMotionPos, PE_AddMotionPos_curve),
|
||||
HLL_EXPORT(Parts_AddMotionAlpha, PE_AddMotionAlpha_curve),
|
||||
HLL_TODO_EXPORT(Parts_AddMotionCG, GUIEngine_Parts_AddMotionCG),
|
||||
HLL_TODO_EXPORT(Parts_AddMotionCGTermination, GUIEngine_Parts_AddMotionCGTermination),
|
||||
HLL_TODO_EXPORT(Parts_AddMotionHGaugeRate, GUIEngine_Parts_AddMotionHGaugeRate),
|
||||
@@ -952,18 +950,18 @@ HLL_LIBRARY(GUIEngine,
|
||||
HLL_TODO_EXPORT(Parts_AddMotionRotateX, GUIEngine_Parts_AddMotionRotateX),
|
||||
HLL_TODO_EXPORT(Parts_AddMotionRotateY, GUIEngine_Parts_AddMotionRotateY),
|
||||
HLL_TODO_EXPORT(Parts_AddMotionRotateZ, GUIEngine_Parts_AddMotionRotateZ),
|
||||
HLL_TODO_EXPORT(Parts_AddMotionVibrationSize, GUIEngine_Parts_AddMotionVibrationSize),
|
||||
HLL_EXPORT(Parts_AddMotionVibrationSize, PE_AddMotionVibrationSize),
|
||||
HLL_TODO_EXPORT(Parts_AddWholeMotionVibrationSize, GUIEngine_Parts_AddWholeMotionVibrationSize),
|
||||
HLL_TODO_EXPORT(Parts_AddMotionSound, GUIEngine_Parts_AddMotionSound),
|
||||
HLL_TODO_EXPORT(Parts_IsCursorIn, GUIEngine_Parts_IsCursorIn),
|
||||
HLL_EXPORT(Parts_IsCursorIn, PE_IsCursorIn),
|
||||
HLL_TODO_EXPORT(Parts_SetThumbnailReductionSize, GUIEngine_Parts_SetThumbnailReductionSize),
|
||||
HLL_TODO_EXPORT(Parts_SetThumbnailMode, GUIEngine_Parts_SetThumbnailMode),
|
||||
HLL_TODO_EXPORT(GetClickNumber, GUIEngine_GetClickNumber),
|
||||
HLL_TODO_EXPORT(Parts_BeginMotion, GUIEngine_Parts_BeginMotion),
|
||||
HLL_TODO_EXPORT(Parts_EndMotion, GUIEngine_Parts_EndMotion),
|
||||
HLL_TODO_EXPORT(Parts_IsMotion, GUIEngine_Parts_IsMotion),
|
||||
HLL_EXPORT(Parts_BeginMotion, PE_BeginMotion),
|
||||
HLL_EXPORT(Parts_EndMotion, PE_EndMotion),
|
||||
HLL_EXPORT(Parts_IsMotion, PE_IsMotion),
|
||||
HLL_TODO_EXPORT(Parts_SeekEndMotion, GUIEngine_Parts_SeekEndMotion),
|
||||
HLL_TODO_EXPORT(Parts_UpdateMotionTime, GUIEngine_Parts_UpdateMotionTime),
|
||||
HLL_TODO_EXPORT(Save, GUIEngine_Save),
|
||||
HLL_TODO_EXPORT(SaveWithoutHideParts, GUIEngine_SaveWithoutHideParts),
|
||||
HLL_EXPORT(Parts_UpdateMotionTime, PE_UpdateMotionTime),
|
||||
HLL_EXPORT(Save, PE_Save),
|
||||
HLL_EXPORT(SaveWithoutHideParts, PE_SaveWithoutHideParts),
|
||||
HLL_TODO_EXPORT(Load, GUIEngine_Load));
|
||||
|
||||
+91
-5
@@ -14,15 +14,25 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "system4/ain.h"
|
||||
|
||||
#include "asset_manager.h"
|
||||
#include "parts.h"
|
||||
#include "xsystem4.h"
|
||||
#include "hll.h"
|
||||
|
||||
static void GoatGUIEngine_PreLink(void);
|
||||
|
||||
HLL_LIBRARY(GoatGUIEngine,
|
||||
HLL_EXPORT(_PreLink, GoatGUIEngine_PreLink),
|
||||
HLL_EXPORT(Init, PE_Init),
|
||||
HLL_EXPORT(Update, PE_Update),
|
||||
HLL_EXPORT(SetPartsCG, PE_SetPartsCG_by_index),
|
||||
HLL_EXPORT(SetPartsCG, PE_SetPartsCG),
|
||||
HLL_EXPORT(GetPartsCGNumber, PE_GetPartsCGNumber),
|
||||
HLL_EXPORT(SetLoopCG, PE_SetLoopCG),
|
||||
HLL_EXPORT(GetPartsCGName, PE_GetPartsCGName),
|
||||
HLL_TODO_EXPORT(SetLoopCG, PE_SetLoopCG),
|
||||
HLL_EXPORT(SetText, PE_SetText),
|
||||
HLL_EXPORT(AddPartsText, PE_AddPartsText),
|
||||
HLL_TODO_EXPORT(DeletePartsTopTextLine, PE_DeletePartsTopTextLine),
|
||||
@@ -39,7 +49,7 @@ HLL_LIBRARY(GoatGUIEngine,
|
||||
HLL_EXPORT(SetHGaugeRate, PE_SetHGaugeRate),
|
||||
HLL_EXPORT(SetVGaugeCG, PE_SetVGaugeCG),
|
||||
HLL_EXPORT(SetVGaugeRate, PE_SetVGaugeRate),
|
||||
HLL_EXPORT(SetNumeralCG, PE_SetNumeralCG),
|
||||
HLL_TODO_EXPORT(SetNumeralCG, PE_SetNumeralCG),
|
||||
HLL_EXPORT(SetNumeralLinkedCGNumberWidthWidthList, PE_SetNumeralLinkedCGNumberWidthWidthList),
|
||||
HLL_EXPORT(SetNumeralNumber, PE_SetNumeralNumber),
|
||||
HLL_EXPORT(SetNumeralShowComma, PE_SetNumeralShowComma),
|
||||
@@ -58,12 +68,16 @@ HLL_LIBRARY(GoatGUIEngine,
|
||||
HLL_EXPORT(SetShow, PE_SetShow),
|
||||
HLL_EXPORT(SetAlpha, PE_SetAlpha),
|
||||
HLL_TODO_EXPORT(SetPartsDrawFilter, PE_SetPartsDrawFilter),
|
||||
HLL_EXPORT(SetAddColor, PE_SetAddColor),
|
||||
HLL_EXPORT(SetMultiplyColor, PE_SetMultiplyColor),
|
||||
HLL_EXPORT(SetClickable, PE_SetClickable),
|
||||
HLL_EXPORT(GetPartsX, PE_GetPartsX),
|
||||
HLL_EXPORT(GetPartsY, PE_GetPartsY),
|
||||
HLL_EXPORT(GetPartsZ, PE_GetPartsZ),
|
||||
HLL_EXPORT(GetPartsShow, PE_GetPartsShow),
|
||||
HLL_EXPORT(GetPartsAlpha, PE_GetPartsAlpha),
|
||||
HLL_TODO_EXPORT(GetAddColor, PE_GetAddColor),
|
||||
HLL_TODO_EXPORT(GetMultiplyColor, PE_GetMultiplyColor),
|
||||
HLL_EXPORT(GetPartsClickable, PE_GetPartsClickable),
|
||||
HLL_EXPORT(SetPartsOriginPosMode, PE_SetPartsOriginPosMode),
|
||||
HLL_EXPORT(GetPartsOriginPosMode, PE_GetPartsOriginPosMode),
|
||||
@@ -82,7 +96,7 @@ HLL_LIBRARY(GoatGUIEngine,
|
||||
HLL_EXPORT(GetClickPartsNumber, PE_GetClickPartsNumber),
|
||||
HLL_EXPORT(AddMotionPos, PE_AddMotionPos),
|
||||
HLL_EXPORT(AddMotionAlpha, PE_AddMotionAlpha),
|
||||
HLL_EXPORT(AddMotionCG, PE_AddMotionCG),
|
||||
HLL_TODO_EXPORT(AddMotionCG, PE_AddMotionCG),
|
||||
HLL_EXPORT(AddMotionHGaugeRate, PE_AddMotionHGaugeRate),
|
||||
HLL_EXPORT(AddMotionVGaugeRate, PE_AddMotionVGaugeRate),
|
||||
HLL_EXPORT(AddMotionNumeralNumber, PE_AddMotionNumeralNumber),
|
||||
@@ -92,6 +106,7 @@ HLL_LIBRARY(GoatGUIEngine,
|
||||
HLL_EXPORT(AddMotionRotateY, PE_AddMotionRotateY),
|
||||
HLL_EXPORT(AddMotionRotateZ, PE_AddMotionRotateZ),
|
||||
HLL_EXPORT(AddMotionVibrationSize, PE_AddMotionVibrationSize),
|
||||
HLL_TODO_EXPORT(AddWholeMotionVibrationSize, PE_AddWholeMotionVibrationSize),
|
||||
HLL_EXPORT(AddMotionSound, PE_AddMotionSound),
|
||||
HLL_EXPORT(BeginMotion, PE_BeginMotion),
|
||||
HLL_EXPORT(EndMotion, PE_EndMotion),
|
||||
@@ -108,4 +123,75 @@ HLL_LIBRARY(GoatGUIEngine,
|
||||
HLL_TODO_EXPORT(SetThumbnailReductionSize, PE_SetThumbnailReductionSize),
|
||||
HLL_TODO_EXPORT(SetThumbnailMode, PE_SetThumbnailMode),
|
||||
HLL_EXPORT(Save, PE_Save),
|
||||
HLL_EXPORT(Load, PE_Load));
|
||||
HLL_EXPORT(Load, PE_Load),
|
||||
|
||||
HLL_TODO_EXPORT(SetPartsFlashAndStop, PE_SetPartsFlashAndStop),
|
||||
HLL_TODO_EXPORT(StopPartsFlash, PE_StopPartsFlash),
|
||||
HLL_TODO_EXPORT(StartPartsFlash, PE_StartPartsFlash),
|
||||
HLL_TODO_EXPORT(GoFramePartsFlash, PE_GoFramePartsFlash),
|
||||
HLL_TODO_EXPORT(GetPartsFlashEndFrame, PE_GetPartsFlashEndFrame),
|
||||
HLL_TODO_EXPORT(ClearPartsConstructionProcess, PE_ClearPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(AddCreateToPartsConstructionProcess, PE_AddCreateToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(AddCreatePixelOnlyToPartsConstructionProcess, PE_AddCreatePixelOnlyToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(AddFillToPartsConstructionProcess, PE_AddFillToPartsConstructionProcess),
|
||||
HLL_EXPORT(AddFillAlphaColorToPartsConstructionProcess, PE_AddFillAlphaColorToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(AddFillAMapToPartsConstructionProcess, PE_AddFillAMapToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(AddFillWithAlphaToPartsConstructionProcess, PE_AddFillWithAlphaToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(AddFillGradationHorizonToPartsConstructionProcess, PE_AddFillGradationHorizonToPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(AddDrawRectToPartsConstructionProcess, PE_AddDrawRectToPartsConstructionProcess),
|
||||
HLL_EXPORT(BuildPartsConstructionProcess, PE_BuildPartsConstructionProcess),
|
||||
HLL_TODO_EXPORT(AddDrawTextToPartsConstructionProcess, PE_AddDrawTextToPartsConstructionProcess));
|
||||
|
||||
static struct ain_hll_function *get_fun(int libno, const char *name)
|
||||
{
|
||||
int fno = ain_get_library_function(ain, libno, name);
|
||||
return fno >= 0 ? &ain->libraries[libno].functions[fno] : NULL;
|
||||
}
|
||||
|
||||
// Daiteikoku and Shaman's Sanctuary have different interface than later versions
|
||||
static void GoatGUIEngine_PreLink(void)
|
||||
{
|
||||
struct ain_hll_function *fun;
|
||||
int libno = ain_get_library(ain, "GoatGUIEngine");
|
||||
assert(libno >= 0);
|
||||
|
||||
fun = get_fun(libno, "SetPartsCG");
|
||||
if (fun && game_rance8) {
|
||||
static_library_replace(&lib_GoatGUIEngine, "SetPartsCG", PE_SetPartsCG_by_string_index);
|
||||
}
|
||||
else if (fun && fun->arguments[1].type.data == AIN_INT) {
|
||||
static_library_replace(&lib_GoatGUIEngine, "SetPartsCG", PE_SetPartsCG_by_index);
|
||||
}
|
||||
|
||||
fun = get_fun(libno, "SetLoopCG");
|
||||
if (fun && fun->arguments[1].type.data == AIN_INT) {
|
||||
static_library_replace(&lib_GoatGUIEngine, "SetLoopCG", PE_SetLoopCG_by_index);
|
||||
}
|
||||
|
||||
fun = get_fun(libno, "SetHGaugeCG");
|
||||
if (fun && fun->arguments[1].type.data == AIN_INT) {
|
||||
static_library_replace(&lib_GoatGUIEngine, "SetHGaugeCG", PE_SetHGaugeCG_by_index);
|
||||
}
|
||||
|
||||
fun = get_fun(libno, "SetVGaugeCG");
|
||||
if (fun && fun->arguments[1].type.data == AIN_INT) {
|
||||
static_library_replace(&lib_GoatGUIEngine, "SetVGaugeCG", PE_SetVGaugeCG_by_index);
|
||||
}
|
||||
|
||||
fun = get_fun(libno, "SetNumeralCG");
|
||||
if (fun && fun->arguments[1].type.data == AIN_INT) {
|
||||
static_library_replace(&lib_GoatGUIEngine, "SetNumeralCG", PE_SetNumeralCG_by_index);
|
||||
}
|
||||
|
||||
fun = get_fun(libno, "SetNumeralLinkedCGNumberWidthWidthList");
|
||||
if (fun && fun->arguments[1].type.data == AIN_INT) {
|
||||
static_library_replace(&lib_GoatGUIEngine, "SetNumeralLinkedCGNumberWidthWidthList",
|
||||
PE_SetNumeralLinkedCGNumberWidthWidthList_by_index);
|
||||
}
|
||||
|
||||
fun = get_fun(libno, "AddMotionCG");
|
||||
if (fun && fun->arguments[1].type.data == AIN_INT) {
|
||||
static_library_replace(&lib_GoatGUIEngine, "AddMotionCG", PE_AddMotionCG_by_index);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+35
-18
@@ -26,6 +26,7 @@
|
||||
#include "audio.h"
|
||||
#include "effect.h"
|
||||
#include "gfx/gfx.h"
|
||||
#include "gfx/font.h"
|
||||
#include "input.h"
|
||||
#include "vm.h"
|
||||
#include "xsystem4.h"
|
||||
@@ -353,6 +354,8 @@ static void Gpx2Plus_CopyStretchReduce(int destSurface, int dx, int dy, int dWid
|
||||
|
||||
static void Gpx2Plus_CopyStretchReduceAMap(int destSurface, int dx, int dy, int dWidth, int dHeight, int srcSurface, int sx, int sy, int sWidth, int sHeight)
|
||||
{
|
||||
if (game_daibanchou_en)
|
||||
sWidth /= 2;
|
||||
struct texture *dst = get_texture(destSurface);
|
||||
struct texture *src = get_texture(srcSurface);
|
||||
if (!dst || !src)
|
||||
@@ -485,15 +488,29 @@ static void Gpx2Plus_MsgDraw(int surface, int x, int y)
|
||||
if (!dst)
|
||||
return;
|
||||
|
||||
struct text_metrics tm = { .size = 16, .weight = FW_NORMAL };
|
||||
int char_space = 0;
|
||||
struct text_style ts = {
|
||||
.face = FONT_GOTHIC,
|
||||
.size = 16.0f,
|
||||
.bold_width = 0.0f,
|
||||
.weight = FW_NORMAL,
|
||||
.edge_left = 0.0f,
|
||||
.edge_up = 0.0f,
|
||||
.edge_right = 0.0f,
|
||||
.edge_down = 0.0f,
|
||||
.color = {0,0,0,0},
|
||||
.edge_color = {0,0,0,0},
|
||||
.scale_x = 1.0,
|
||||
.space_scale_x = 1.0f,
|
||||
.font_spacing = 0.0f,
|
||||
.font_size = NULL
|
||||
};
|
||||
int line_space = 0;
|
||||
int old_size = 16;
|
||||
enum font_face old_face = FONT_GOTHIC;
|
||||
|
||||
Point pos = POINT(x, y);
|
||||
char *s = msgbuf.text;
|
||||
int line_height = calculate_line_height(tm.size, s);
|
||||
int line_height = calculate_line_height(ts.size, s);
|
||||
while (*s) {
|
||||
int skiplen = 0, a1, a2, a3;
|
||||
if (*s != '<') {
|
||||
@@ -504,40 +521,40 @@ static void Gpx2Plus_MsgDraw(int surface, int x, int y)
|
||||
} else {
|
||||
skiplen = strlen(s);
|
||||
}
|
||||
pos.y += line_height - tm.size; // bottom align
|
||||
pos.x += gfx_render_text(dst, pos, s, &tm, char_space);
|
||||
pos.y -= line_height - tm.size;
|
||||
pos.y += line_height - ts.size; // bottom align
|
||||
pos.x += gfx_render_text(dst, pos.x, pos.y, s, &ts);
|
||||
pos.y -= line_height - ts.size;
|
||||
if (lb)
|
||||
*lb = '<';
|
||||
} else if (sscanf(s, "<F%d>%n", &a1, &skiplen) == 1) {
|
||||
old_face = tm.face;
|
||||
tm.face = a1;
|
||||
old_face = ts.face;
|
||||
ts.face = a1;
|
||||
} else if (!strncmp(s, "</F>", 4)) {
|
||||
skiplen = 4;
|
||||
tm.face = old_face;
|
||||
ts.face = old_face;
|
||||
} else if (sscanf(s, "<S%d>%n", &a1, &skiplen) == 1) {
|
||||
old_size = tm.size;
|
||||
tm.size = a1;
|
||||
old_size = ts.size;
|
||||
ts.size = a1;
|
||||
} else if (!strncmp(s, "</S>", 4)) {
|
||||
skiplen = 4;
|
||||
tm.size = old_size;
|
||||
ts.size = old_size;
|
||||
} else if (sscanf(s, "<C%d,%d,%d>%n", &a1, &a2, &a3, &skiplen) == 3) {
|
||||
tm.color = (SDL_Color) { .r = a1, .g = a2, .b = a3, .a = 255 };
|
||||
ts.color = (SDL_Color) { .r = a1, .g = a2, .b = a3, .a = 255 };
|
||||
} else if (sscanf(s, "<KC%d,%d,%d>%n", &a1, &a2, &a3, &skiplen) == 3) {
|
||||
tm.outline_color = (SDL_Color) { .r = a1, .g = a2, .b = a3, .a = 255 };
|
||||
ts.edge_color = (SDL_Color) { .r = a1, .g = a2, .b = a3, .a = 255 };
|
||||
} else if (!strncmp(s, "<K>", 3)) {
|
||||
skiplen = 3;
|
||||
tm.outline_right = tm.outline_down = 2;
|
||||
ts.edge_right = ts.edge_down = 2;
|
||||
} else if (!strncmp(s, "</K>", 4)) {
|
||||
skiplen = 4;
|
||||
tm.outline_right = tm.outline_down = 0;
|
||||
ts.edge_right = ts.edge_down = 0;
|
||||
} else if (!strncmp(s, "<R>", 3)) {
|
||||
skiplen = 3;
|
||||
pos.x = x;
|
||||
pos.y += line_height + line_space;
|
||||
line_height = calculate_line_height(tm.size, s + skiplen);
|
||||
line_height = calculate_line_height(ts.size, s + skiplen);
|
||||
} else if (sscanf(s, "<SPW%d>%n", &a1, &skiplen) == 1) {
|
||||
char_space = a1;
|
||||
ts.font_spacing = a1;
|
||||
} else if (sscanf(s, "<SPH%d>%n", &a1, &skiplen) == 1) {
|
||||
line_space = a1;
|
||||
} else {
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
#include "hll.h"
|
||||
#include "sact.h"
|
||||
|
||||
//bool Mouse_HideCursorByGame(bool Hide);
|
||||
//bool Mouse_MovePosImmediately(int x, int y);
|
||||
//bool Mouse_HideCursorByGame(bool hide);
|
||||
//bool Mouse_IsHideCursorByGame(void);
|
||||
//void Mouse_HideByStepMessage(void);
|
||||
HLL_QUIET_UNIMPLEMENTED(, void, IbisInputEngine, Mouse_HideByStepMessage);
|
||||
//void MouseWheel_ClearCount(void);
|
||||
HLL_QUIET_UNIMPLEMENTED(, void, IbisInputEngine, MouseWheel_ClearCount);
|
||||
|
||||
@@ -49,6 +51,10 @@ HLL_WARN_UNIMPLEMENTED(0.0, float, IbisInputEngine, Joystick_GetAxis, int dev, i
|
||||
|
||||
HLL_LIBRARY(IbisInputEngine,
|
||||
HLL_EXPORT(Mouse_GetPos, sact_Mouse_GetPos),
|
||||
HLL_TODO_EXPORT(Mouse_MovePosImmediately, IbisInputEngine_Mouse_MovePosImmediately),
|
||||
HLL_TODO_EXPORT(Mouse_HideCursorByGame, IbisInputEngine_Mouse_HideCursorByGame),
|
||||
HLL_TODO_EXPORT(Mouse_IsHideCursorByGame, IbisInputEngine_Mouse_IsHideCursorByGame),
|
||||
HLL_EXPORT(Mouse_HideByStepMessage, IbisInputEngine_Mouse_HideByStepMessage),
|
||||
HLL_EXPORT(MouseWheel_ClearCount, IbisInputEngine_MouseWheel_ClearCount),
|
||||
HLL_EXPORT(MouseWheel_GetCount, IbisInputEngine_MouseWheel_GetCount),
|
||||
HLL_EXPORT(Key_IsDown, sact_Key_IsDown),
|
||||
|
||||
@@ -14,12 +14,11 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "system4/string.h"
|
||||
|
||||
#include "hll.h"
|
||||
#include "input.h"
|
||||
#include "vm.h"
|
||||
|
||||
static void handle_events_throttled(void)
|
||||
{
|
||||
@@ -28,11 +27,7 @@ static void handle_events_throttled(void)
|
||||
static int count;
|
||||
if (++count == 30) {
|
||||
count = 0;
|
||||
struct timespec ts = {
|
||||
.tv_sec = 0,
|
||||
.tv_nsec = 10000000L
|
||||
};
|
||||
nanosleep(&ts, NULL);
|
||||
vm_sleep(10);
|
||||
}
|
||||
handle_events();
|
||||
}
|
||||
|
||||
@@ -22,11 +22,6 @@
|
||||
HLL_WARN_UNIMPLEMENTED( , void, KiwiSoundEngine, SetGlobalFocus, possibly_unused int nNum);
|
||||
|
||||
//int KiwiSoundEngine_Sound_GetGroupNum(int nCh);
|
||||
static int KiwiSoundEngine_Sound_GetGroupNumFromDataNum(possibly_unused int no)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//static bool KiwiSoundEngine_Sound_PrepareFromFile(int ch, struct string *filename);
|
||||
//static int KiwiSoundEngine_Sound_GetDataLength(int data_number);
|
||||
|
||||
@@ -96,7 +91,7 @@ HLL_LIBRARY(KiwiSoundEngine,
|
||||
HLL_EXPORT(Sound_IsFade, wav_is_fading),
|
||||
HLL_EXPORT(Sound_GetTimeLength, wav_get_time_length),
|
||||
HLL_TODO_EXPORT(Sound_GetGroupNum, KiwiSoundEngine_Sound_GetGroupNum),
|
||||
HLL_EXPORT(Sound_GetGroupNumFromDataNum, KiwiSoundEngine_Sound_GetGroupNumFromDataNum),
|
||||
HLL_EXPORT(Sound_GetGroupNumFromDataNum, wav_get_group_num_from_data_num),
|
||||
HLL_TODO_EXPORT(Sound_PrepareFromFile, KiwiSoundEngine_Sound_PrepareFromFile),
|
||||
HLL_TODO_EXPORT(Sound_GetDataLength, KiwiSoundEngine_Sound_GetDataLength),
|
||||
HLL_EXPORT(GetMasterGroup, KiwiSoundEngine_GetMasterGroup),
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
#include "hll.h"
|
||||
#include "sact.h"
|
||||
|
||||
HLL_LIBRARY(LoadCG,
|
||||
HLL_EXPORT(SP_SetCG, sact_SP_SetCG),
|
||||
HLL_EXPORT(SP_SetCG2X, sact_SP_SetCG2X),
|
||||
HLL_EXPORT(CG_GetMetrics, sact_CG_GetMetrics),
|
||||
HLL_EXPORT(SP_SetCG2X_Nearest, sact_SP_SetCG2X));
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user