mirror of
https://github.com/pmret/papermario.git
synced 2024-09-08 02:29:49 +02:00
imgfx data (#1029)
This commit is contained in:
parent
44d1b29590
commit
e0cfd078da
23
include/imgfx.h
Normal file
23
include/imgfx.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef _IMGFX_H_
|
||||
#define _IMGFX_H_
|
||||
|
||||
#include "PR/gbi.h"
|
||||
|
||||
// 'compressed' vertex data for animated image fx keyframes
|
||||
typedef struct ImgFXVtx {
|
||||
/* 0x00 */ s16 ob[3];
|
||||
/* 0x06 */ u8 tc[2];
|
||||
/* 0x08 */ s8 cn[3];
|
||||
/* 0x0B */ char unk_0B;
|
||||
} ImgFXVtx; // size = 0x0C
|
||||
|
||||
typedef struct ImgFXAnimHeader {
|
||||
/* 0x00 */ ImgFXVtx* keyframesOffset;
|
||||
/* 0x04 */ Gfx* gfxOffset; // Gfx for creating mesh from vertices
|
||||
/* 0x08 */ u16 vtxCount; // conserved across keyframes
|
||||
/* 0x0A */ u16 gfxCount;
|
||||
/* 0x0C */ u16 keyframesCount;
|
||||
/* 0x0E */ u16 flags;
|
||||
} ImgFXAnimHeader; // size = 0x10
|
||||
|
||||
#endif /* _IMGFX_H_ */
|
@ -1,10 +1,11 @@
|
||||
#include "common.h"
|
||||
#include "ld_addrs.h"
|
||||
#include "sprite.h"
|
||||
#include "imgfx.h"
|
||||
|
||||
#if VERSION_IQUE
|
||||
// TODO: remove if section is split in iQue release
|
||||
extern Addr imgfx_gfx_data_ROM_START;
|
||||
extern Addr imgfx_data_ROM_START;
|
||||
#endif
|
||||
|
||||
typedef union ImgFXIntVars {
|
||||
@ -98,15 +99,6 @@ typedef struct ImgFXCacheEntry {
|
||||
/* 0x06 */ char unk_06[0x2];
|
||||
} ImgFXCacheEntry; // size = 0x8
|
||||
|
||||
typedef struct ImgFXAnimHeader {
|
||||
/* 0x00 */ s32 keyframesOffset;
|
||||
/* 0x04 */ Gfx* gfxOffset; // Gfx for creating mesh from vertices
|
||||
/* 0x08 */ u16 vtxCount; // conserved across keyframes
|
||||
/* 0x0A */ u16 gfxCount;
|
||||
/* 0x0C */ u16 keyframesCount;
|
||||
/* 0x0E */ u16 flags;
|
||||
} ImgFXAnimHeader; // size = 0x10
|
||||
|
||||
enum ImgFXAnimFlags {
|
||||
IMGFX_ANIM_FLAG_ABSOLUTE_COORDS = 1, // image-relative (in percent) when unset
|
||||
};
|
||||
@ -117,14 +109,6 @@ typedef struct ImgFXRenderMode {
|
||||
/* 0x8 */ u8 flags; // only checks TRUE so far. some kind of switch?
|
||||
} ImgFXRenderMode; // size = 0xC
|
||||
|
||||
// 'compressed' vertex data for animated image fx keyframes
|
||||
typedef struct ImgFXVtx {
|
||||
/* 0x00 */ s16 ob[3];
|
||||
/* 0x06 */ u8 tc[2];
|
||||
/* 0x08 */ s8 cn[3];
|
||||
/* 0x0B */ char unk_0B;
|
||||
} ImgFXVtx; // size = 0x0C
|
||||
|
||||
typedef ImgFXState ImgFXInstanceList[MAX_IMGFX_INSTANCES];
|
||||
|
||||
extern HeapNode heap_spriteHead;
|
||||
@ -194,28 +178,49 @@ ImgFXRenderMode ImgFXRenderModes[] = {
|
||||
[IMGFX_RENDER_UNUSED] { 0x00441208, 0x00111208, 0 },
|
||||
};
|
||||
|
||||
// all relative to imgfx_gfx_data_ROM_START
|
||||
s32 ImgFXAnimOffsets[] = {
|
||||
[IMGFX_ANIM_SHOCK] 0x14358,
|
||||
[IMGFX_ANIM_SHIVER] 0x18200,
|
||||
[IMGFX_ANIM_VERTICAL_PIPE_CURL] 0x1A858,
|
||||
[IMGFX_ANIM_HORIZONTAL_PIPE_CURL] 0x1E830,
|
||||
[IMGFX_ANIM_STARTLE] 0x29458,
|
||||
[IMGFX_ANIM_FLUTTER_DOWN] 0x314E0,
|
||||
[IMGFX_ANIM_UNFURL] 0x33498,
|
||||
[IMGFX_ANIM_GET_IN_BED] 0x38988,
|
||||
[IMGFX_ANIM_SPIRIT_CAPTURE] 0x39228,
|
||||
[IMGFX_ANIM_UNUSED_1] 0x5B7A8,
|
||||
[IMGFX_ANIM_UNUSED_2] 0x7CF10,
|
||||
[IMGFX_ANIM_UNUSED_3] 0x86490,
|
||||
[IMGFX_ANIM_TUTANKOOPA_GATHER] 0x96258,
|
||||
[IMGFX_ANIM_TUTANKOOPA_SWIRL_2] 0xA1820,
|
||||
[IMGFX_ANIM_TUTANKOOPA_SWIRL_1] 0xACDE8,
|
||||
[IMGFX_ANIM_SHUFFLE_CARDS] 0xBBF68,
|
||||
[IMGFX_ANIM_FLIP_CARD_1] 0xC0490,
|
||||
[IMGFX_ANIM_FLIP_CARD_2] 0xC49B8,
|
||||
[IMGFX_ANIM_FLIP_CARD_3] 0xC6150,
|
||||
[IMGFX_ANIM_CYMBAL_CRUSH] 0xCA380,
|
||||
extern Addr shock_header;
|
||||
extern Addr shiver_header;
|
||||
extern Addr vertical_pipe_curl_header;
|
||||
extern Addr horizontal_pipe_curl_header;
|
||||
extern Addr startle_header;
|
||||
extern Addr flutter_down_header;
|
||||
extern Addr unfurl_header;
|
||||
extern Addr get_in_bed_header;
|
||||
extern Addr spirit_capture_header;
|
||||
extern Addr unused_1_header;
|
||||
extern Addr unused_2_header;
|
||||
extern Addr unused_3_header;
|
||||
extern Addr tutankoopa_gather_header;
|
||||
extern Addr tutankoopa_swirl_2_header;
|
||||
extern Addr tutankoopa_swirl_1_header;
|
||||
extern Addr shuffle_cards_header;
|
||||
extern Addr flip_card_1_header;
|
||||
extern Addr flip_card_2_header;
|
||||
extern Addr flip_card_3_header;
|
||||
extern Addr cymbal_crush_header;
|
||||
|
||||
// all relative to imgfx_data_ROM_START
|
||||
u8* ImgFXAnimOffsets[] = {
|
||||
[IMGFX_ANIM_SHOCK] shock_header,
|
||||
[IMGFX_ANIM_SHIVER] shiver_header,
|
||||
[IMGFX_ANIM_VERTICAL_PIPE_CURL] vertical_pipe_curl_header,
|
||||
[IMGFX_ANIM_HORIZONTAL_PIPE_CURL] horizontal_pipe_curl_header,
|
||||
[IMGFX_ANIM_STARTLE] startle_header,
|
||||
[IMGFX_ANIM_FLUTTER_DOWN] flutter_down_header,
|
||||
[IMGFX_ANIM_UNFURL] unfurl_header,
|
||||
[IMGFX_ANIM_GET_IN_BED] get_in_bed_header,
|
||||
[IMGFX_ANIM_SPIRIT_CAPTURE] spirit_capture_header,
|
||||
[IMGFX_ANIM_UNUSED_1] unused_1_header,
|
||||
[IMGFX_ANIM_UNUSED_2] unused_2_header,
|
||||
[IMGFX_ANIM_UNUSED_3] unused_3_header,
|
||||
[IMGFX_ANIM_TUTANKOOPA_GATHER] tutankoopa_gather_header,
|
||||
[IMGFX_ANIM_TUTANKOOPA_SWIRL_2] tutankoopa_swirl_2_header,
|
||||
[IMGFX_ANIM_TUTANKOOPA_SWIRL_1] tutankoopa_swirl_1_header,
|
||||
[IMGFX_ANIM_SHUFFLE_CARDS] shuffle_cards_header,
|
||||
[IMGFX_ANIM_FLIP_CARD_1] flip_card_1_header,
|
||||
[IMGFX_ANIM_FLIP_CARD_2] flip_card_2_header,
|
||||
[IMGFX_ANIM_FLIP_CARD_3] flip_card_3_header,
|
||||
[IMGFX_ANIM_CYMBAL_CRUSH] cymbal_crush_header,
|
||||
};
|
||||
|
||||
extern ImgFXCacheEntry ImgFXDataCache[8];
|
||||
@ -1051,7 +1056,7 @@ void imgfx_appendGfx_mesh(ImgFXState* state, Matrix4f mtx) {
|
||||
case IMGFX_RENDER_OVERLAY_RGBA:
|
||||
// color: texture
|
||||
// alpha: texture * prim
|
||||
gDPSetCombineLERP(gMainGfxPos++,
|
||||
gDPSetCombineLERP(gMainGfxPos++,
|
||||
0, 0, 0, TEXEL0, TEXEL0, 0, PRIMITIVE, 0,
|
||||
0, 0, 0, TEXEL0, TEXEL0, 0, PRIMITIVE, 0);
|
||||
gDPSetPrimColor(gMainGfxPos++, 0, 0, 0, 0, 0, state->ints.overlay.alpha);
|
||||
@ -1215,7 +1220,7 @@ void imgfx_mesh_make_grid(ImgFXState* state) {
|
||||
}
|
||||
|
||||
ImgFXAnimHeader* imgfx_load_anim(ImgFXState* state) {
|
||||
u8* romStart = ImgFXAnimOffsets[state->ints.anim.type] + imgfx_gfx_data_ROM_START;
|
||||
u8* romStart = (s32) ImgFXAnimOffsets[state->ints.anim.type] + imgfx_data_ROM_START;
|
||||
ImgFXAnimHeader* anim = &ImgFXAnimHeaders[state->arrayIdx];
|
||||
|
||||
if (state->curAnimOffset != romStart) {
|
||||
@ -1249,7 +1254,7 @@ ImgFXAnimHeader* imgfx_load_anim(ImgFXState* state) {
|
||||
state->gfxBufs[0] = heap_malloc(anim->gfxCount * sizeof(Gfx));
|
||||
state->gfxBufs[1] = heap_malloc(anim->gfxCount * sizeof(Gfx));
|
||||
|
||||
romStart = imgfx_gfx_data_ROM_START + (s32)anim->gfxOffset;
|
||||
romStart = imgfx_data_ROM_START + (s32)anim->gfxOffset;
|
||||
romEnd = romStart + anim->gfxCount * sizeof(Gfx);
|
||||
dma_copy(romStart, romEnd, state->gfxBufs[0]);
|
||||
dma_copy(romStart, romEnd, state->gfxBufs[1]);
|
||||
@ -1271,7 +1276,7 @@ ImgFXAnimHeader* imgfx_load_anim(ImgFXState* state) {
|
||||
// ImgFXVtx structs are 0xC bytes while Vtx are 0x10, so we need a (4/3) scaling factor
|
||||
// to compute a new, equivalent Vtx[i] address for an existing ImgFXVtx[i] address.
|
||||
// Unfortunately, using sizeof here does not match.
|
||||
gfxBuffer[j-1].words.w1 = ((((s32) gfxBuffer[j-1].words.w1 - anim->keyframesOffset) / 3) * 4) +
|
||||
gfxBuffer[j-1].words.w1 = ((((s32) gfxBuffer[j-1].words.w1 - (s32) anim->keyframesOffset) / 3) * 4) +
|
||||
(s32) state->vtxBufs[i];
|
||||
}
|
||||
} while (cmd != G_ENDDL);
|
||||
@ -1331,11 +1336,11 @@ void imgfx_mesh_anim_update(ImgFXState* state) {
|
||||
|
||||
// find the current + next keyframe vertex data
|
||||
curKeyframe = heap_malloc(header->vtxCount * sizeof(ImgFXVtx));
|
||||
romStart = (u8*)((s32)imgfx_gfx_data_ROM_START + header->keyframesOffset + curKeyIdx * header->vtxCount * sizeof(ImgFXVtx));
|
||||
romStart = (u8*)((s32)imgfx_data_ROM_START + (s32) header->keyframesOffset + curKeyIdx * header->vtxCount * sizeof(ImgFXVtx));
|
||||
dma_copy(romStart, romStart + header->vtxCount * sizeof(ImgFXVtx), curKeyframe);
|
||||
if (keyframeInterval > 1) {
|
||||
nextKeyframe = heap_malloc(header->vtxCount * sizeof(*nextKeyframe));
|
||||
romStart = (u8*)((s32)imgfx_gfx_data_ROM_START + header->keyframesOffset + nextKeyIdx * header->vtxCount * sizeof(ImgFXVtx));
|
||||
romStart = (u8*)((s32)imgfx_data_ROM_START + (s32) header->keyframesOffset + nextKeyIdx * header->vtxCount * sizeof(ImgFXVtx));
|
||||
dma_copy(romStart, romStart + header->vtxCount * sizeof(ImgFXVtx), nextKeyframe);
|
||||
}
|
||||
|
@ -217,6 +217,8 @@ def write_ninja_rules(ninja: ninja_syntax.Writer, cpp: str, extra_cppflags: str,
|
||||
|
||||
ninja.rule("pm_sprite_shading_profiles", command=f"$python {BUILD_TOOLS}/sprite/sprite_shading_profiles.py $in $out $header_path")
|
||||
|
||||
ninja.rule("pm_imgfx_data", command=f"$python {BUILD_TOOLS}/imgfx/imgfx_data.py $in $out")
|
||||
|
||||
with Path("tools/permuter_settings.toml").open("w") as f:
|
||||
f.write(f"compiler_command = \"{cc} {CPPFLAGS.replace('$version', 'us')} {cflags} -DPERMUTER -fforce-addr\"\n")
|
||||
f.write(f"assembler_command = \"{cross}as -EB -march=vr4300 -mtune=vr4300 -Iinclude\"\n")
|
||||
@ -257,7 +259,7 @@ class Configure:
|
||||
if assets:
|
||||
modes.extend(["bin", "yay0", "img", "vtx", "vtx_common", "gfx", "gfx_common", "pm_map_data", "pm_msg",
|
||||
"pm_npc_sprites", "pm_charset","pm_charset_palettes", "pm_effect_loads", "pm_effect_shims",
|
||||
"pm_sprite_shading_profiles"])
|
||||
"pm_sprite_shading_profiles", "pm_imgfx_data"])
|
||||
if code:
|
||||
modes.extend(["code", "c", "data", "rodata"])
|
||||
|
||||
@ -366,12 +368,12 @@ class Configure:
|
||||
order_only.append("generated_headers_" + self.version)
|
||||
|
||||
ninja.build(
|
||||
object_strs, # $out
|
||||
task,
|
||||
self.resolve_src_paths(src_paths), # $in
|
||||
variables={ "version": self.version, **variables },
|
||||
outputs=object_strs, # $out
|
||||
rule=task,
|
||||
inputs=self.resolve_src_paths(src_paths), # $in
|
||||
implicit=implicit,
|
||||
order_only=order_only,
|
||||
variables={ "version": self.version, **variables },
|
||||
implicit_outputs=implicit_outputs
|
||||
)
|
||||
|
||||
@ -688,6 +690,15 @@ class Configure:
|
||||
"header_path": header_path,
|
||||
})
|
||||
build(entry.object_path, [entry.object_path.with_suffix("")], "bin")
|
||||
elif seg.type == "pm_imgfx_data":
|
||||
c_file_path = Path(f"assets/{self.version}") / "imgfx" / (seg.name + ".c")
|
||||
build(c_file_path, entry.src_paths, "pm_imgfx_data")
|
||||
|
||||
build(entry.object_path, [c_file_path], "cc" if not modern_gcc else "cc_modern", variables={
|
||||
"cflags": "",
|
||||
"cppflags": f"-DVERSION_{self.version.upper()}",
|
||||
"encoding": "CP932", # similar to SHIFT-JIS, but includes backslash and tilde
|
||||
})
|
||||
elif seg.type == "linker" or seg.type == "linker_offset":
|
||||
pass
|
||||
else:
|
||||
|
0
tools/build/imgfx/__init__.py
Normal file
0
tools/build/imgfx/__init__.py
Normal file
181
tools/build/imgfx/imgfx_data.py
Normal file
181
tools/build/imgfx/imgfx_data.py
Normal file
@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from dataclasses import dataclass
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any, List
|
||||
import yaml as yaml_loader
|
||||
|
||||
@dataclass
|
||||
class Vertex:
|
||||
idx: int
|
||||
x: int
|
||||
y: int
|
||||
z: int
|
||||
u: int
|
||||
v: int
|
||||
r: int
|
||||
g: int
|
||||
b: int
|
||||
a: int
|
||||
|
||||
def toJSON(self):
|
||||
return " { \"pos\": [" + f"{self.x}, {self.y}, {self.z}" + "], \"uv\": [" + f"{self.u}, {self.v}" + "], \"rgba\": [" + f"{self.r}, {self.g}, {self.b}, {self.a}" + "] }"
|
||||
|
||||
@dataclass
|
||||
class Triangle:
|
||||
i: int
|
||||
j: int
|
||||
k: int
|
||||
|
||||
def toJSON(self):
|
||||
return f" [{self.i}, {self.j}, {self.k}]"
|
||||
|
||||
@dataclass
|
||||
class Anim:
|
||||
name: str
|
||||
offset: int
|
||||
vtx_offset: int
|
||||
gfx_offset: int
|
||||
vtx_count: int
|
||||
gfx_count: int
|
||||
keyframes: int
|
||||
flags: int
|
||||
|
||||
frames: List[List[Vertex]]
|
||||
triangles: List[Triangle]
|
||||
|
||||
def toJSON(self):
|
||||
framestr = ",\n".join([" [\n" + ",\n".join([v.toJSON() for v in frame]) + "\n ]" for i, frame in enumerate(self.frames)])
|
||||
trianglestr = ",\n".join([t.toJSON() for t in self.triangles])
|
||||
|
||||
ret = "{\n"
|
||||
ret += " \"flags\": " + str(self.flags) + ",\n"
|
||||
ret += " \"frames\": [\n" + framestr + "],\n"
|
||||
ret += " \"triangles\": [\n" + trianglestr + "]\n"
|
||||
ret += "}"
|
||||
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def fromJSON(name: str, data: Any) -> "Anim":
|
||||
flags = data["flags"]
|
||||
frames = [[Vertex(idx, *vtx["pos"], *vtx["uv"], *vtx["rgba"]) for idx, vtx in enumerate(frame)] for frame in data["frames"]]
|
||||
triangles = [Triangle(*t) for t in data["triangles"]]
|
||||
|
||||
return Anim(
|
||||
name=name,
|
||||
offset=0,
|
||||
vtx_offset=0,
|
||||
gfx_offset=0,
|
||||
vtx_count=0,
|
||||
gfx_count=0,
|
||||
keyframes=len(frames),
|
||||
flags=flags,
|
||||
frames=frames,
|
||||
triangles=triangles
|
||||
)
|
||||
|
||||
def build(inputs: List[Path], output: Path):
|
||||
with open(output, "w") as f:
|
||||
f.write("/* NOTE: This file is autogenerated, do not edit */\n\n")
|
||||
f.write("#include \"PR/gbi.h\"\n")
|
||||
f.write("#include \"macros.h\"\n")
|
||||
f.write("#include \"imgfx.h\"\n\n")
|
||||
|
||||
for input in inputs:
|
||||
with open(input, "r") as fin:
|
||||
in_json = json.load(fin)
|
||||
|
||||
anim = Anim.fromJSON(input.name[:-5], in_json)
|
||||
|
||||
vtx_name = f"{anim.name}_keyframes"
|
||||
gfx_name = f"{anim.name}_gfx"
|
||||
|
||||
# this has padding before it..maybe a file split?
|
||||
if anim.name == "unused_1":
|
||||
f.write(f"s32 padding_{anim.name}[] = {{ 0, 0 }};\n\n")
|
||||
|
||||
# vtx
|
||||
f.write(f"ImgFXVtx {vtx_name}[{len(anim.frames)}][{len(anim.frames[0])}] = {{\n")
|
||||
for frame in anim.frames:
|
||||
f.write(" {\n")
|
||||
for vtx in frame:
|
||||
f.write(f" {{ {{{vtx.x}, {vtx.y}, {vtx.z}}}, {{{vtx.u}, {vtx.v}}}, {{{vtx.r}, {vtx.g}, {vtx.b}}}, {vtx.a} }},\n")
|
||||
f.write(" },\n")
|
||||
f.write("};\n\n")
|
||||
|
||||
# gfx
|
||||
f.write(f"Gfx {gfx_name}[] = {{\n")
|
||||
|
||||
# TODO hard-coded
|
||||
cur_tri = 0
|
||||
just_chunked = False
|
||||
chunk_text = ""
|
||||
max_t1 = 0
|
||||
max_t2 = 0
|
||||
min_t = 0
|
||||
sub_num = 0
|
||||
old_max_t = 0
|
||||
while cur_tri < len(anim.triangles):
|
||||
# Vertex command every 16 triangles
|
||||
t1 = anim.triangles[cur_tri]
|
||||
t2 = anim.triangles[cur_tri + 1] if cur_tri + 1 < len(anim.triangles) else None
|
||||
|
||||
t1x = t1.i - sub_num
|
||||
t1y = t1.j - sub_num
|
||||
t1z = t1.k - sub_num
|
||||
max_t1 = max(max_t1, t1x, t1y, t1z)
|
||||
t2x = 0 if t2 is None else t2.i - sub_num
|
||||
t2y = 0 if t2 is None else t2.j - sub_num
|
||||
t2z = 0 if t2 is None else t2.k - sub_num
|
||||
max_t2 = max(max_t2, t2x, t2y, t2z)
|
||||
min_t = min(min_t, t1x, t1y, t1z, t2x, t2y, t2z)
|
||||
|
||||
# We need a new chunk
|
||||
if max_t1 >= 32 and not just_chunked:
|
||||
chunk_text = f" gsSPVertex((u8*){vtx_name} + 0xC * {sub_num}, {min(32, max_t1 + 1)}, 0),\n" + chunk_text
|
||||
just_chunked = True
|
||||
f.write(chunk_text)
|
||||
chunk_text = ""
|
||||
sub_num += old_max_t - min_t + 1
|
||||
min_t = 32
|
||||
max_t1 = 0
|
||||
max_t2 = 0
|
||||
continue
|
||||
|
||||
just_chunked = False
|
||||
if max_t2 >= 32 or t2 is None:
|
||||
chunk_text += f" gsSP1Triangle({t1x}, {t1y}, {t1z}, 0),\n"
|
||||
cur_tri += 1
|
||||
old_max_t = max_t1
|
||||
else:
|
||||
chunk_text += f" gsSP2Triangles({t1x}, {t1y}, {t1z}, 0, {t2x}, {t2y}, {t2z}, 0),\n"
|
||||
cur_tri += 2
|
||||
old_max_t = max(max_t1, max_t2)
|
||||
|
||||
# Dump final chunk
|
||||
chunk_text = f" gsSPVertex((u8*){vtx_name} + 0xC * {sub_num}, {max(max_t1, max_t2) + 1}, 0),\n" + chunk_text
|
||||
f.write(chunk_text)
|
||||
f.write(" gsSPEndDisplayList(),\n")
|
||||
f.write("};\n\n")
|
||||
|
||||
# header
|
||||
f.write(f"ImgFXAnimHeader {anim.name}_header = {{\n")
|
||||
f.write(f" .keyframesOffset = {vtx_name}[0],\n")
|
||||
f.write(f" .gfxOffset = {gfx_name},\n")
|
||||
f.write(f" .vtxCount = ARRAY_COUNT({vtx_name}[0]),\n")
|
||||
f.write(f" .gfxCount = ARRAY_COUNT({gfx_name}),\n")
|
||||
f.write(f" .keyframesCount = ARRAY_COUNT({vtx_name}),\n")
|
||||
f.write(f" .flags = {anim.flags},\n")
|
||||
f.write("};\n\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="ImgFX animation c file builder")
|
||||
parser.add_argument("inputs", type=Path, help="json files to build, passed in order", nargs="+")
|
||||
parser.add_argument("output", type=Path, help="Output c file path")
|
||||
args = parser.parse_args()
|
||||
|
||||
build(args.inputs, args.output)
|
107
tools/splat_ext/pm_imgfx_data.py
Normal file
107
tools/splat_ext/pm_imgfx_data.py
Normal file
@ -0,0 +1,107 @@
|
||||
import os
|
||||
import struct
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import yaml as yaml_loader
|
||||
from typing import List, Tuple
|
||||
|
||||
TOOLS_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.append(str(Path(TOOLS_DIR) / "build/imgfx"))
|
||||
from imgfx_data import Anim, Triangle, Vertex
|
||||
|
||||
from segtypes.n64.segment import N64Segment
|
||||
from util import log, options
|
||||
|
||||
|
||||
class N64SegPm_imgfx_data(N64Segment):
|
||||
anims: List[Anim] = []
|
||||
|
||||
OUT_DIR: Path = options.opts.asset_path / "imgfx"
|
||||
|
||||
def scan(self, rom_bytes):
|
||||
data = rom_bytes[self.rom_start:self.rom_end]
|
||||
|
||||
for name, offset in self.yaml.get("animations"):
|
||||
pos = offset
|
||||
|
||||
# Animations start with vtx data which is followed by gfx data and ends with the header at `offset`
|
||||
vtx_offset, gfx_offset, vtx_count, gfx_count, keyframes, flags = struct.unpack(">IIHHHH", data[pos:pos+16])
|
||||
|
||||
frames: List[List[Vertex]] = []
|
||||
|
||||
pos = vtx_offset
|
||||
for i in range(keyframes):
|
||||
frame: List[Vertex] = []
|
||||
|
||||
for j in range(vtx_count):
|
||||
x, y, z, u, v, r, g, b, a = struct.unpack(">hhhBBbbbB", data[pos:pos+12])
|
||||
pos += 12
|
||||
frame.append(Vertex(j, x, y, z, u, v, r, g, b, a))
|
||||
|
||||
frames.append(frame)
|
||||
|
||||
triangles: List[Triangle] = []
|
||||
vtx_buf: List[int] = []
|
||||
|
||||
# Align to 8 bytes
|
||||
pos = (pos + 7) & ~(8 - 1)
|
||||
assert(pos == gfx_offset)
|
||||
|
||||
def unpack_tri(word: int, vtx_buf: List[int]) -> Tuple[int, int, int]:
|
||||
i = (word >> 16) & 0xFF
|
||||
j = (word >> 8) & 0xFF
|
||||
k = word & 0xFF
|
||||
|
||||
i = vtx_buf[int(i / 2)]
|
||||
j = vtx_buf[int(j / 2)]
|
||||
k = vtx_buf[int(k / 2)]
|
||||
|
||||
return (i, j, k)
|
||||
|
||||
for i in range(gfx_count):
|
||||
w0, w1 = struct.unpack(">II", data[pos:pos+8])
|
||||
pos += 8
|
||||
|
||||
op = (w0 >> 24) & 0xFF
|
||||
|
||||
if op == 1: # G_VTX
|
||||
num = w0 >> 12 & 0xFF
|
||||
end = int((w0 & 0xFF) / 2)
|
||||
src_idx = int((w1 - vtx_offset) / 12)
|
||||
start = end - num
|
||||
|
||||
for j in range(num):
|
||||
to_place_pos = start + j
|
||||
while len(vtx_buf) <= to_place_pos:
|
||||
vtx_buf.append(0)
|
||||
vtx_buf[to_place_pos] = src_idx + j
|
||||
elif op == 5: # G_TRI1
|
||||
triangles.append(Triangle(*unpack_tri(w0, vtx_buf)))
|
||||
elif op == 6: # G_TRI2
|
||||
triangles.append(Triangle(*unpack_tri(w0, vtx_buf)))
|
||||
triangles.append(Triangle(*unpack_tri(w1, vtx_buf)))
|
||||
elif op == 0xDF: # G_ENDDL
|
||||
pass
|
||||
else:
|
||||
log.error(f"Unknown op: {op}")
|
||||
|
||||
self.anims.append(Anim(name, offset, vtx_offset, gfx_offset, vtx_count, gfx_count, keyframes, flags, frames, triangles))
|
||||
|
||||
|
||||
def split(self, rom_bytes):
|
||||
self.OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
for anim in self.anims:
|
||||
with open(f"{self.OUT_DIR}/{anim.name}.json", "w") as f:
|
||||
f.write(anim.toJSON())
|
||||
|
||||
def get_linker_entries(self):
|
||||
from segtypes.linker_entry import LinkerEntry
|
||||
|
||||
return [
|
||||
LinkerEntry(
|
||||
self,
|
||||
[self.OUT_DIR / f"{anim.name}.json" for anim in self.anims],
|
||||
options.opts.asset_path / "imgfx" / f"{self.name}.c",
|
||||
self.get_linker_section(),
|
||||
)
|
||||
]
|
@ -409,7 +409,7 @@ segments:
|
||||
- [0xC4010, c, C50A0]
|
||||
- [0xCC0F0, c, cd180_len_38f0]
|
||||
- [0xCDDB0, c, CEE40]
|
||||
- [0xCF9E0, c, d0a70_len_4fe0]
|
||||
- [0xCF9E0, c, imgfx]
|
||||
- [0xD49C0, c, hud_element]
|
||||
- [0xDA990, c, dba20_len_350]
|
||||
- [0xDACE0, c, trigger]
|
||||
@ -438,27 +438,14 @@ segments:
|
||||
- [0xE3DE0, vtx, vtx/stencil2]
|
||||
- [0xE3F60]
|
||||
- [0xE4400, .data, CEE40]
|
||||
- [0xE4480, .data, d0a70_len_4fe0]
|
||||
- [0xE4480, .data, imgfx]
|
||||
- [0xE4630, .data, hud_element]
|
||||
- [0xE4790, .data, dc470_len_14c0]
|
||||
- [0xE47B0, .data, audio/ambience]
|
||||
- [0xE47C0, .data, windows]
|
||||
- [0xE4940, .data, audio/sfx]
|
||||
- [0xE4D60, .data, audio/e0b30_len_b80]
|
||||
- [0xE4DB0, .rodata, a5dd0_len_114e0]
|
||||
- [0xE54D0, .rodata, B4580]
|
||||
- [0xE5720, .rodata, entity_model]
|
||||
- [0xE59E0, .rodata, msg]
|
||||
- [0xE60B0, .rodata, C50A0]
|
||||
- [0xE6510, .rodata, cd180_len_38f0]
|
||||
- [0xE65F0, .rodata, CEE40]
|
||||
- [0xE6620, .rodata, d0a70_len_4fe0]
|
||||
- [0xE67B0, .rodata, hud_element]
|
||||
- [0xE6890, .rodata, dc470_len_14c0]
|
||||
- [0xE68B0, .rodata, windows]
|
||||
- [0xE68C0, .rodata, audio/sfx]
|
||||
- [0xE6900, .rodata, audio/e0b30_len_b80]
|
||||
- [0xE6920]
|
||||
- [0xE4DB0]
|
||||
- name: evt
|
||||
dir: evt
|
||||
type: code
|
||||
|
@ -1143,7 +1143,27 @@ entity_sbk_omo_ROM_START = 0x00e78190;
|
||||
entity_jan_iwa_ROM_END = 0x00e78190;
|
||||
entity_default_ROM_START = 0x00e73f70;
|
||||
entity_default_ROM_END = 0x00e76170;
|
||||
imgfx_gfx_data_ROM_START = 0x00294870;
|
||||
imgfx_data_ROM_START = 0x00294870;
|
||||
shock_header = 0x14358;
|
||||
shiver_header = 0x18200;
|
||||
vertical_pipe_curl_header = 0x1A858;
|
||||
horizontal_pipe_curl_header = 0x1E830;
|
||||
startle_header = 0x29458;
|
||||
flutter_down_header = 0x314E0;
|
||||
unfurl_header = 0x33498;
|
||||
get_in_bed_header = 0x38988;
|
||||
spirit_capture_header = 0x39228;
|
||||
unused_1_header = 0x5B7A8;
|
||||
unused_2_header = 0x7CF10;
|
||||
unused_3_header = 0x86490;
|
||||
tutankoopa_gather_header = 0x96258;
|
||||
tutankoopa_swirl_2_header = 0xA1820;
|
||||
tutankoopa_swirl_1_header = 0xACDE8;
|
||||
shuffle_cards_header = 0xBBF68;
|
||||
flip_card_1_header = 0xC0490;
|
||||
flip_card_2_header = 0xC49B8;
|
||||
flip_card_3_header = 0xC6150;
|
||||
cymbal_crush_header = 0xCA380;
|
||||
sort_items = 0x800e5aac;
|
||||
suggest_player_anim_always_forward = 0x800de358;
|
||||
add_item = 0x800e5954;
|
||||
|
@ -832,7 +832,7 @@ segments:
|
||||
- [0xC50A0, c, C50A0]
|
||||
- [0xCD180, c, cd180_len_38f0]
|
||||
- [0xCEE40, c, CEE40]
|
||||
- [0xD0A70, c, d0a70_len_4fe0]
|
||||
- [0xD0A70, c, imgfx]
|
||||
- [0xD5A50, c, hud_element]
|
||||
- [0xDBA20, c, dba20_len_350]
|
||||
- [0xDBD70, c, trigger]
|
||||
@ -861,7 +861,7 @@ segments:
|
||||
- [0xE4E70, vtx, vtx/stencil2]
|
||||
- [0xE4FF0]
|
||||
- [0xE5490, .data, CEE40]
|
||||
- [0xE5510, .data, d0a70_len_4fe0]
|
||||
- [0xE5510, .data, imgfx]
|
||||
- [0xE56C0, .data, hud_element]
|
||||
- [0xE5820, .data, dc470_len_14c0]
|
||||
- [0xE5840, .data, audio/ambience]
|
||||
@ -2596,7 +2596,31 @@ segments:
|
||||
- [0x2435F0, ia8, title/tape, 128, 128]
|
||||
- [0x2475F0, ci8, title/bowser_silhouette, 128, 128]
|
||||
- [0x24B5F0, palette, title/bowser_silhouette]
|
||||
- [0x24B7F0, bin, imgfx_gfx_data] # TODO extract into better format?
|
||||
- start: 0x24B7F0
|
||||
type: pm_imgfx_data
|
||||
name: imgfx_data
|
||||
vram: 0
|
||||
animations:
|
||||
- ["shock", 0x14358]
|
||||
- ["shiver", 0x18200]
|
||||
- ["vertical_pipe_curl", 0x1A858]
|
||||
- ["horizontal_pipe_curl", 0x1E830]
|
||||
- ["startle", 0x29458]
|
||||
- ["flutter_down", 0x314E0]
|
||||
- ["unfurl", 0x33498]
|
||||
- ["get_in_bed", 0x38988]
|
||||
- ["spirit_capture", 0x39228]
|
||||
- ["unused_1", 0x5B7A8]
|
||||
- ["unused_2", 0x7CF10]
|
||||
- ["unused_3", 0x86490]
|
||||
- ["tutankoopa_gather", 0x96258]
|
||||
- ["tutankoopa_swirl_2", 0xA1820]
|
||||
- ["tutankoopa_swirl_1", 0xACDE8]
|
||||
- ["shuffle_cards", 0xBBF68]
|
||||
- ["flip_card_1", 0xC0490]
|
||||
- ["flip_card_2", 0xC49B8]
|
||||
- ["flip_card_3", 0xC6150]
|
||||
- ["cymbal_crush", 0xCA380]
|
||||
- [0x315B80, pm_sprite_shading_profiles, sprite_shading_profiles]
|
||||
- type: code
|
||||
start: 0x3169F0
|
||||
|
Loading…
Reference in New Issue
Block a user