mirror of
https://github.com/Xeeynamo/sotn-decomp.git
synced 2024-11-26 22:40:33 +00:00
Add display_animations.py tool for viewing entity animation frames (#1746)
This extends upon my work in display_texture.py. I have used this for identifying a few entities already. display_texture is suited for viewing prims, while this one is better for entities that are rendered by RenderEntities. I think there's enough documentation at the top of the function for others to use it. Hopefully it can be helpful, especially as it evolves over time. At some point this could be converted to an editor in addition to a viewer, but for now the viewing capability is the core focus. Also rewrote some of DRA's sprite data (this should probably be in sotn-assets though) to fit the standard format so that this tool could parse the data.
This commit is contained in:
parent
50b8135531
commit
02e4c62f6a
@ -737,7 +737,7 @@ s16 D_800D0EB4[];
|
||||
s16 D_800D0EE4[];
|
||||
s16 D_800D0F14[];
|
||||
s16 D_800D0F30[];
|
||||
u16 D_800D0F4C[];
|
||||
s16 D_800D0F4C[];
|
||||
|
||||
u16* D_800CFF10[] = {
|
||||
NULL, D_800D0020, D_800D003C, D_800D0058, D_800D0074, D_800D0090,
|
||||
@ -1027,9 +1027,8 @@ s16 D_800D0F30[] = {
|
||||
1, 0, -14, 15, 32, 16, 368, 104, 32, 48, 64, 64, 0, 0,
|
||||
};
|
||||
|
||||
u16 D_800D0F4C[] = {
|
||||
0x0001, 0x0024, 0xFFC1, 0xFFC1, 0x0080, 0x0080, 0x01A0,
|
||||
0x0068, 0x0080, 0x0000, 0x0100, 0x0080, 0x0000,
|
||||
s16 D_800D0F4C[] = {
|
||||
1, 36, -63, -63, 128, 128, 416, 104, 128, 0, 256, 128, 0,
|
||||
#ifdef VERSION_US // dirty data
|
||||
0x5F65,
|
||||
#endif
|
||||
@ -2240,177 +2239,110 @@ u16 D_800D2CDC[] = {
|
||||
#endif
|
||||
};
|
||||
|
||||
u32 D_800D2D5C[];
|
||||
u16 D_800D2D78[];
|
||||
u32 D_800D2D94[];
|
||||
u32 D_800D2DB0[];
|
||||
u32 D_800D2DCC[];
|
||||
u32 D_800D2DE8[];
|
||||
u32 D_800D2E04[];
|
||||
u32 D_800D2E20[];
|
||||
u32 D_800D2E3C[];
|
||||
u32 D_800D2E58[];
|
||||
u32 D_800D2E74[];
|
||||
u32 D_800D2E90[];
|
||||
u32 D_800D2EAC[];
|
||||
u32 D_800D2EC8[];
|
||||
u32 D_800D2EE4[];
|
||||
u32 D_800D2F00[];
|
||||
u32 D_800D2F1C[];
|
||||
u32 D_800D2F38[];
|
||||
u32 D_800D2F54[];
|
||||
u32 D_800D2F70[];
|
||||
u32 D_800D2F8C[];
|
||||
u32 D_800D2FA8[];
|
||||
u32 D_800D2FC4[];
|
||||
u16 D_800D2FE0[];
|
||||
s16 D_800D2D5C[];
|
||||
s16 D_800D2D78[];
|
||||
s16 D_800D2D94[];
|
||||
s16 D_800D2DB0[];
|
||||
s16 D_800D2DCC[];
|
||||
s16 D_800D2DE8[];
|
||||
s16 D_800D2E04[];
|
||||
s16 D_800D2E20[];
|
||||
s16 D_800D2E3C[];
|
||||
s16 D_800D2E58[];
|
||||
s16 D_800D2E74[];
|
||||
s16 D_800D2E90[];
|
||||
s16 D_800D2EAC[];
|
||||
s16 D_800D2EC8[];
|
||||
s16 D_800D2EE4[];
|
||||
s16 D_800D2F00[];
|
||||
s16 D_800D2F1C[];
|
||||
s16 D_800D2F38[];
|
||||
s16 D_800D2F54[];
|
||||
s16 D_800D2F70[];
|
||||
s16 D_800D2F8C[];
|
||||
s16 D_800D2FA8[];
|
||||
s16 D_800D2FC4[];
|
||||
s16 D_800D2FE0[];
|
||||
|
||||
s16* D_800D2CF8[] = {
|
||||
NULL,
|
||||
(s16*)D_800D2D5C,
|
||||
(s16*)D_800D2D78,
|
||||
(s16*)D_800D2D94,
|
||||
(s16*)D_800D2DB0,
|
||||
(s16*)D_800D2DCC,
|
||||
(s16*)D_800D2DE8,
|
||||
(s16*)D_800D2E04,
|
||||
(s16*)D_800D2E20,
|
||||
(s16*)D_800D2E3C,
|
||||
(s16*)D_800D2E58,
|
||||
(s16*)D_800D2E74,
|
||||
(s16*)D_800D2E90,
|
||||
(s16*)D_800D2EAC,
|
||||
(s16*)D_800D2EC8,
|
||||
(s16*)D_800D2EE4,
|
||||
(s16*)D_800D2F00,
|
||||
(s16*)D_800D2F1C,
|
||||
(s16*)D_800D2F38,
|
||||
(s16*)D_800D2F54,
|
||||
(s16*)D_800D2F70,
|
||||
(s16*)D_800D2F8C,
|
||||
(s16*)D_800D2FA8,
|
||||
(s16*)D_800D2FC4,
|
||||
(s16*)D_800D2FE0,
|
||||
NULL, D_800D2D5C, D_800D2D78, D_800D2D94, D_800D2DB0,
|
||||
D_800D2DCC, D_800D2DE8, D_800D2E04, D_800D2E20, D_800D2E3C,
|
||||
D_800D2E58, D_800D2E74, D_800D2E90, D_800D2EAC, D_800D2EC8,
|
||||
D_800D2EE4, D_800D2F00, D_800D2F1C, D_800D2F38, D_800D2F54,
|
||||
D_800D2F70, D_800D2F8C, D_800D2FA8, D_800D2FC4, D_800D2FE0,
|
||||
};
|
||||
|
||||
u32 D_800D2D5C[] = {
|
||||
0x00200001, 0xFFF3FFF9, 0x00100010, 0x00680195,
|
||||
0x00000080, 0x00100090, 0x00000000,
|
||||
s16 D_800D2D5C[] = {
|
||||
1, 32, -7, -13, 16, 16, 405, 104, 128, 0, 144, 16, 0, 0,
|
||||
};
|
||||
|
||||
u16 D_800D2D78[] = {
|
||||
0x0001, 0x0020, 0xFFF8, 0xFFF4, 0x0010, 0x0010, 0x0195,
|
||||
0x0068, 0x0090, 0x0000, 0x00A0, 0x0010, 0x0000, 0x0000,
|
||||
s16 D_800D2D78[] = {
|
||||
1, 32, -8, -12, 16, 16, 405, 104, 144, 0, 160, 16, 0, 0,
|
||||
};
|
||||
|
||||
u32 D_800D2D94[] = {
|
||||
0x00200001, 0xFFF4FFF8, 0x00100010, 0x00680195,
|
||||
0x000000A0, 0x001000B0, 0x00000000,
|
||||
s16 D_800D2D94[] = {
|
||||
1, 32, -8, -12, 16, 16, 405, 104, 160, 0, 176, 16, 0, 0,
|
||||
};
|
||||
|
||||
u32 D_800D2DB0[] = {
|
||||
0x00200001, 0xFFF4FFF8, 0x00100010, 0x00680195,
|
||||
0x000000B0, 0x001000C0, 0x00000000,
|
||||
s16 D_800D2DB0[] = {
|
||||
1, 32, -8, -12, 16, 16, 405, 104, 176, 0, 192, 16, 0, 0,
|
||||
};
|
||||
|
||||
u32 D_800D2DCC[] = {
|
||||
0x00000001, 0xFFF2FFF7, 0x00180018, 0x00680195,
|
||||
0x00100080, 0x00280098, 0x00000000,
|
||||
s16 D_800D2DCC[] = {
|
||||
1, 0, -9, -14, 24, 24, 405, 104, 128, 16, 152, 40, 0, 0,
|
||||
};
|
||||
|
||||
u32 D_800D2DE8[] = {
|
||||
0x00000001, 0xFFF6FFF5, 0x00180018, 0x00680195,
|
||||
0x00100098, 0x002800B0, 0x00000000,
|
||||
s16 D_800D2DE8[] = {
|
||||
1, 0, -11, -10, 24, 24, 405, 104, 152, 16, 176, 40, 0, 0,
|
||||
};
|
||||
|
||||
u32 D_800D2E04[] = {
|
||||
0x00000001, 0xFFF6FFF5, 0x00180018, 0x00680195,
|
||||
0x001000B0, 0x002800C8, 0x00000000,
|
||||
s16 D_800D2E04[] = {
|
||||
1, 0, -11, -10, 24, 24, 405, 104, 176, 16, 200, 40, 0, 0,
|
||||
};
|
||||
|
||||
u32 D_800D2E20[] = {
|
||||
0x00000001, 0xFFF6FFF5, 0x00180018, 0x00680195,
|
||||
0x001000C8, 0x002800E0, 0x00000000,
|
||||
s16 D_800D2E20[] = {
|
||||
1, 0, -11, -10, 24, 24, 405, 104, 200, 16, 224, 40, 0, 0,
|
||||
};
|
||||
|
||||
u32 D_800D2E3C[] = {
|
||||
0x00000001, 0xFFF5FFF5, 0x00180018, 0x00680195,
|
||||
0x001000E0, 0x002800F8, 0x00000000,
|
||||
s16 D_800D2E3C[] = {
|
||||
1, 0, -11, -11, 24, 24, 405, 104, 224, 16, 248, 40, 0, 0,
|
||||
};
|
||||
|
||||
u32 D_800D2E58[] = {
|
||||
0x00000001, 0xFFF5FFF5, 0x00180018, 0x00680195,
|
||||
0x00280080, 0x00400098, 0x00000000,
|
||||
s16 D_800D2E58[] = {
|
||||
1, 0, -11, -11, 24, 24, 405, 104, 128, 40, 152, 64, 0, 0,
|
||||
};
|
||||
|
||||
u32 D_800D2E74[] = {
|
||||
0x00000001, 0xFFF5FFF5, 0x00180018, 0x00680195,
|
||||
0x00280098, 0x004000B0, 0x00000000,
|
||||
s16 D_800D2E74[] = {
|
||||
1, 0, -11, -11, 24, 24, 405, 104, 152, 40, 176, 64, 0, 0,
|
||||
};
|
||||
|
||||
u32 D_800D2E90[] = {
|
||||
0x00000001, 0xFFF5FFF5, 0x00180018, 0x00680195,
|
||||
0x002800B0, 0x004000C8, 0x00000000,
|
||||
s16 D_800D2E90[] = {
|
||||
1, 0, -11, -11, 24, 24, 405, 104, 176, 40, 200, 64, 0, 0,
|
||||
};
|
||||
|
||||
u32 D_800D2EAC[] = {
|
||||
0x00000001, 0xFFF6FFF5, 0x00180018, 0x00680195,
|
||||
0x002800C8, 0x004000E0, 0x00000000,
|
||||
s16 D_800D2EAC[] = {
|
||||
1, 0, -11, -10, 24, 24, 405, 104, 200, 40, 224, 64, 0, 0,
|
||||
};
|
||||
|
||||
u32 D_800D2EC8[] = {
|
||||
0x00000001, 0xFFF6FFF8, 0x00180010, 0x00680195,
|
||||
0x002800E0, 0x004000F0, 0x00000000,
|
||||
s16 D_800D2EC8[] = {
|
||||
1, 0, -8, -10, 16, 24, 405, 104, 224, 40, 240, 64, 0, 0,
|
||||
};
|
||||
|
||||
u32 D_800D2EE4[] = {
|
||||
0x00000001, 0xFFFBFFF8, 0x00180010, 0x00680195,
|
||||
0x00400080, 0x00580090, 0x00000000,
|
||||
s16 D_800D2EE4[] = {
|
||||
1, 0, -8, -5, 16, 24, 405, 104, 128, 64, 144, 88, 0, 0,
|
||||
};
|
||||
|
||||
u32 D_800D2F00[] = {
|
||||
0x00000001, 0xFFFAFFF9, 0x00180010, 0x00680195,
|
||||
0x00400090, 0x005800A0, 0x00000000,
|
||||
s16 D_800D2F00[] = {
|
||||
1, 0, -7, -6, 16, 24, 405, 104, 144, 64, 160, 88, 0, 0,
|
||||
};
|
||||
|
||||
u32 D_800D2F1C[] = {
|
||||
0x00000001, 0xFFFCFFF7, 0x00180010, 0x00680195,
|
||||
0x004000A0, 0x005800B0, 0x00000000,
|
||||
s16 D_800D2F1C[] = {
|
||||
1, 0, -9, -4, 16, 24, 405, 104, 160, 64, 176, 88, 0, 0,
|
||||
};
|
||||
|
||||
u32 D_800D2F38[] = {
|
||||
0x00000001, 0xFFFCFFF8, 0x00180010, 0x00680195,
|
||||
0x004000B0, 0x005800C0, 0x00000000,
|
||||
s16 D_800D2F38[] = {
|
||||
1, 0, -8, -4, 16, 24, 405, 104, 176, 64, 192, 88, 0, 0,
|
||||
};
|
||||
|
||||
u32 D_800D2F54[] = {
|
||||
0x00000001, 0xFFFCFFF7, 0x00180010, 0x00680195,
|
||||
0x004000C0, 0x005800D0, 0x00000000,
|
||||
s16 D_800D2F54[] = {
|
||||
1, 0, -9, -4, 16, 24, 405, 104, 192, 64, 208, 88, 0, 0,
|
||||
};
|
||||
|
||||
u32 D_800D2F70[] = {
|
||||
0x00000001, 0xFFFCFFF8, 0x00180010, 0x00680195,
|
||||
0x004000D0, 0x005800E0, 0x00000000,
|
||||
s16 D_800D2F70[] = {
|
||||
1, 0, -8, -4, 16, 24, 405, 104, 208, 64, 224, 88, 0, 0,
|
||||
};
|
||||
|
||||
u32 D_800D2F8C[] = {
|
||||
0x00000001, 0x0001FFF8, 0x00100010, 0x00680195,
|
||||
0x000000C0, 0x001000D0, 0x00000000,
|
||||
s16 D_800D2F8C[] = {
|
||||
1, 0, -8, 1, 16, 16, 405, 104, 192, 0, 208, 16, 0, 0,
|
||||
};
|
||||
|
||||
u32 D_800D2FA8[] = {
|
||||
0x00000001, 0x0001FFF8, 0x00100010, 0x00680195,
|
||||
0x000000D0, 0x001000E0, 0x00000000,
|
||||
s16 D_800D2FA8[] = {
|
||||
1, 0, -8, 1, 16, 16, 405, 104, 208, 0, 224, 16, 0, 0,
|
||||
};
|
||||
|
||||
u32 D_800D2FC4[] = {
|
||||
0x00000001, 0x0002FFF9, 0x00080010, 0x00680195,
|
||||
0x000000E0, 0x000800F0, 0x00000000,
|
||||
s16 D_800D2FC4[] = {
|
||||
1, 0, -7, 2, 16, 8, 405, 104, 224, 0, 240, 8, 0, 0,
|
||||
};
|
||||
|
||||
u16 D_800D2FE0[] = {
|
||||
0x0001, 0x0000, 0xFFFD, 0x0000, 0x0008, 0x0008, 0x0195,
|
||||
0x0068, 0x00E8, 0x0008, 0x00F0, 0x0010, 0x0000,
|
||||
s16 D_800D2FE0[] = {
|
||||
1, 0, -3, 0, 8, 8, 405, 104, 232, 8, 240, 16, 0,
|
||||
#ifdef VERSION_US // dirty data
|
||||
0x6B61,
|
||||
#endif
|
||||
|
@ -59,10 +59,10 @@ s32 unk14_yVel[] = {
|
||||
};
|
||||
|
||||
u8 unk14_startFrame[] = {
|
||||
/* 1038 */ 0x01,
|
||||
/* 1039 */ 0x09,
|
||||
/* 103A */ 0x15,
|
||||
/* 103B */ 0x2B,
|
||||
/* 1038 */ 1,
|
||||
/* 1039 */ 9,
|
||||
/* 103A */ 21,
|
||||
/* 103B */ 43,
|
||||
};
|
||||
|
||||
u16 unk14_lifetime[] = {
|
||||
|
224
tools/display_animation.py
Normal file
224
tools/display_animation.py
Normal file
@ -0,0 +1,224 @@
|
||||
# Author: bismurphy. Please feel free to contact with any questions!
|
||||
# This tool may be non-intuitive and/or buggy to use; I am happy to make
|
||||
# fixes if anyone requests them.
|
||||
|
||||
# Displays animations for entities.
|
||||
# Uses display_texture.py as dependency, so make sure that is available to it.
|
||||
|
||||
# Example use case: We wanted to figure out what entity EntityLightningCloud is
|
||||
# in NO3/NP3. We can see in its initialization that its Animset is 0x8001
|
||||
# and its palette is 0. Then, we can do:
|
||||
# python3 tools/display_animation.py LIVE no3 0x8001 0 150 100
|
||||
# To review all the frames in that animation.
|
||||
# LIVE will pull a live vram dump from pcsx. no3 is the overlay to grab sprite
|
||||
# data from. 0x8001 is that animset, 0 is the clut, and then 150x100 will be our
|
||||
# viewport size as we click through the entities.
|
||||
|
||||
import display_texture as dt
|
||||
import argparse
|
||||
import re
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.widgets import Button
|
||||
import numpy as np
|
||||
|
||||
from PIL import Image
|
||||
|
||||
# holds the list of animsets
|
||||
DRA_ANIM_ARRAY_FILE = "src/dra/63ED4.c"
|
||||
# some day this may have a symbol name
|
||||
DRA_ANIM_ARRAY = "D_800A3B70"
|
||||
# holds the individual animsets
|
||||
DRA_ANIMSET_FILE = "src/dra/d_2F324.c"
|
||||
|
||||
|
||||
def load_array_from_file(filelines, arrayname):
|
||||
print(f"Trying to load {arrayname}")
|
||||
arraydata = ""
|
||||
inarray = False
|
||||
for line in filelines:
|
||||
if arrayname in line and "{" in line:
|
||||
inarray = True
|
||||
if inarray:
|
||||
arraydata += line
|
||||
if "}" in line:
|
||||
inarray = False
|
||||
# find the data between the curly braces
|
||||
pattern = r"\{([^}]*)\}"
|
||||
match = re.search(pattern, arraydata)
|
||||
if match:
|
||||
array_contents = match.group(1)
|
||||
array_contents = array_contents.rstrip(",") # remove trailing comma if exists
|
||||
# strip whitespace and turn into list of members
|
||||
array_members = array_contents.replace(" ", "").split(",")
|
||||
return array_members
|
||||
print("Error loading animation array. Hmm.")
|
||||
exit()
|
||||
|
||||
|
||||
def show_animset(ovl_name, anim_num, arg_palette, view_w, view_h, unk5A):
|
||||
|
||||
# Now we have an array that tells us the name of all the frames.
|
||||
# Start GUI code.
|
||||
class AnimationShower:
|
||||
def __init__(self):
|
||||
self.anim_index = 1
|
||||
self.textureDisplayer = dt.textureDisplayer(texture_data)
|
||||
# Need to load the animation's frames now.
|
||||
# Depends on if we're an ANIMSET_DRA or ANIMSET_OVL.
|
||||
if anim_num & 0x8000:
|
||||
print("Overlay animation")
|
||||
assert ovl_name != "dra"
|
||||
spritebank = anim_num & 0x7FFF
|
||||
main_array_file = f"src/st/{ovl_name}/sprite_banks.h"
|
||||
main_array = "spriteBanks"
|
||||
animset_file = f"src/st/{ovl_name}/sprites.c"
|
||||
|
||||
else:
|
||||
print("DRA animation")
|
||||
assert ovl_name == "dra"
|
||||
main_array_file = DRA_ANIM_ARRAY_FILE
|
||||
main_array = DRA_ANIM_ARRAY
|
||||
animset_file = DRA_ANIMSET_FILE
|
||||
spritebank = anim_num
|
||||
with open(main_array_file) as f:
|
||||
animdata = f.read().splitlines()
|
||||
animarray = load_array_from_file(animdata, main_array)
|
||||
anim_set_name = animarray[spritebank]
|
||||
print(f"Animation set {anim_num} is {anim_set_name}. Loading.")
|
||||
with open(animset_file) as f:
|
||||
self.framesdata = f.read().splitlines()
|
||||
print("Loading framearray")
|
||||
print(animset_file)
|
||||
self.framearray = load_array_from_file(self.framesdata, anim_set_name)
|
||||
print("Loaded framearray.")
|
||||
|
||||
def prev(self, event):
|
||||
self.anim_index -= 1
|
||||
if self.anim_index < 1:
|
||||
self.anim_index = len(self.framearray) - 1
|
||||
self.render_frame()
|
||||
|
||||
def next(self, event):
|
||||
self.anim_index += 1
|
||||
if self.anim_index >= len(self.framearray):
|
||||
self.anim_index = 1
|
||||
self.render_frame()
|
||||
|
||||
def render_frame(self):
|
||||
print("RENDERING", self.anim_index)
|
||||
frame_name = self.framearray[self.anim_index]
|
||||
# prepend the s16 to make sure we get the actual array, not something that
|
||||
# points to the array
|
||||
frame_params = load_array_from_file(self.framesdata, "s16 " + frame_name)
|
||||
data_size = 1 + int(frame_params[0]) * 11
|
||||
frame_params = frame_params[:data_size]
|
||||
frame_params = [int(x) for x in frame_params]
|
||||
print(frame_params)
|
||||
# Now we follow the logic of RenderEntities.
|
||||
|
||||
# r->spriteSheetIdx = *animFrame++;
|
||||
spriteSheetIdx = frame_params[0]
|
||||
frame_params = frame_params[1:]
|
||||
|
||||
# Not prepared to handle this case.
|
||||
if spriteSheetIdx < 0 or spriteSheetIdx & 0x8000:
|
||||
print("I don't know what this is yet, need new program logic")
|
||||
exit()
|
||||
# Now skip to line 984. We're going to make an image from the individual images.
|
||||
overall_image = Image.new("RGBA", (view_w, view_h))
|
||||
for i in range(spriteSheetIdx):
|
||||
ax.clear()
|
||||
print(frame_params)
|
||||
frameFlags = frame_params[0] # line 989
|
||||
tpage = frame_params[6] # line 990
|
||||
tpage += unk5A # line 991
|
||||
runk0 = tpage & 3 # 992
|
||||
tpage >>= 2 # 993
|
||||
xpivot = frame_params[1] # 994
|
||||
ypivot = frame_params[2] # 995
|
||||
width = frame_params[3] # 996
|
||||
height = frame_params[4] # 997
|
||||
|
||||
# Skip all the logic with positioning and flipping.
|
||||
# Pick up at line 1062
|
||||
if arg_palette & 0x8000:
|
||||
clut = arg_palette & 0x7FFF
|
||||
else:
|
||||
clut = frame_params[5] + arg_palette
|
||||
u_0 = frame_params[7]
|
||||
v_0 = frame_params[8]
|
||||
u_1 = frame_params[9]
|
||||
v_1 = frame_params[10]
|
||||
assert u_1 - u_0 == width
|
||||
assert v_1 - v_0 == height
|
||||
image = self.textureDisplayer.get_image(
|
||||
tpage, clut, u_0, v_0, width, height
|
||||
)
|
||||
if frameFlags & 2:
|
||||
image = np.flip(image, 1)
|
||||
frameFlags -= 2
|
||||
if frameFlags != 0:
|
||||
print("We have frame flags. Ignoring for now.", frameFlags)
|
||||
pil_image = Image.fromarray(image)
|
||||
# pass pil_image twice to get transparency
|
||||
overall_image.paste(
|
||||
pil_image,
|
||||
(view_w // 2 + xpivot, view_h // 2 + ypivot),
|
||||
pil_image,
|
||||
)
|
||||
frame_params = frame_params[11:]
|
||||
ax.set_title(f"Frame #{self.anim_index} of {len(self.framearray)}")
|
||||
ax.imshow(overall_image)
|
||||
plt.draw()
|
||||
|
||||
shower = AnimationShower()
|
||||
fig, ax = plt.subplots()
|
||||
ax.set_xlim(-32, 32)
|
||||
ax.set_ylim(-32, 32)
|
||||
plt.subplots_adjust(bottom=0.15)
|
||||
prev_button = Button(plt.axes([0.1, 0.025, 0.3, 0.1], facecolor="k"), "Prev Frame")
|
||||
prev_button.on_clicked(shower.prev)
|
||||
next_button = Button(plt.axes([0.6, 0.025, 0.3, 0.1], facecolor="k"), "Next Frame")
|
||||
next_button.on_clicked(shower.next)
|
||||
shower.render_frame()
|
||||
plt.show()
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(description="Renders in-game animations from ANIMSET")
|
||||
|
||||
parser.add_argument("dump_filename")
|
||||
|
||||
parser.add_argument("overlay", help="Overlay name. dra, no3, cen, etc")
|
||||
parser.add_argument(
|
||||
"animset_num", type=lambda x: int(x, 0), help="Animset number; 2 for ANIMSET_DRA(2)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"e_palette", type=lambda x: int(x, 0), help="Entity's Palette param"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"view_width", type=lambda x: int(x, 0), help="Width of your view window"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"view_height", type=lambda x: int(x, 0), help="Height of your view window"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--unk5A",
|
||||
type=lambda x: int(x, 0),
|
||||
default=0,
|
||||
help="Entity's unk5A value (optional)",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
texture_data = dt.load_raw_dump(args.dump_filename)
|
||||
show_animset(
|
||||
args.overlay,
|
||||
args.animset_num,
|
||||
args.e_palette,
|
||||
args.view_width,
|
||||
args.view_height,
|
||||
args.unk5A,
|
||||
)
|
@ -52,10 +52,11 @@ def convert_rgb555(in_array):
|
||||
r = round(r / 31 * 255)
|
||||
g = round(g / 31 * 255)
|
||||
b = round(b / 31 * 255)
|
||||
pixel = [r, g, b]
|
||||
a = 255 if (r or g or b) else 0
|
||||
pixel = [r, g, b, a]
|
||||
out_row.append(pixel)
|
||||
out_array.append(out_row)
|
||||
return np.array(out_array)
|
||||
return np.array(out_array, dtype="uint8")
|
||||
|
||||
|
||||
# Once we have a tpage and a clut, apply that clut to color the tpage.
|
||||
@ -112,9 +113,7 @@ def get_clut(colored_dump, clutnum):
|
||||
|
||||
# For a given tpage and clut, retrieve the tpage from the raw dump, and apply
|
||||
# the clut to that tpage.
|
||||
def retrieve_colored_tpage(raw_dump, tpage_number, clut_number):
|
||||
# Load all the data as rgb555, the native format cluts are specified in.
|
||||
colored = convert_rgb555(raw_dump)
|
||||
def retrieve_colored_tpage(raw_dump, colored, tpage_number, clut_number):
|
||||
tpage_rendering_clut = get_clut(colored, clut_number)
|
||||
# Now we have our tpage rendering clut extracted, get the tpage, and apply that clut.
|
||||
tpage = get_tpage_by_number(raw_dump, tpage_number)
|
||||
@ -123,12 +122,21 @@ def retrieve_colored_tpage(raw_dump, tpage_number, clut_number):
|
||||
|
||||
|
||||
def draw_tpage_selection(raw_dump, tpage_number, clut_number, left, top, width, height):
|
||||
# Get the tpage with the proper clut applied
|
||||
ctp = retrieve_colored_tpage(raw_dump, tpage_number, clut_number)
|
||||
colored = convert_rgb555(raw_dump)
|
||||
image = get_tpage_selection(
|
||||
raw_dump, colored, tpage_number, clut_number, left, top, width, height
|
||||
)
|
||||
plt.imshow(image)
|
||||
plt.show()
|
||||
|
||||
|
||||
def get_tpage_selection(
|
||||
raw_dump, colored, tpage_number, clut_number, left, top, width, height
|
||||
):
|
||||
ctp = retrieve_colored_tpage(raw_dump, colored, tpage_number, clut_number)
|
||||
# Crop it to match the needed UV coords, and display it
|
||||
segment = ctp[top : top + height, left : left + width]
|
||||
plt.imshow(segment)
|
||||
plt.show()
|
||||
return segment
|
||||
|
||||
|
||||
# For the chosen filename for the vram dump, we load the bytes, convert
|
||||
@ -142,7 +150,8 @@ def load_raw_dump(filename):
|
||||
dumpbytes = response.read()
|
||||
print("VRAM fetched from PCSX.")
|
||||
except urllib.error.URLError as e:
|
||||
print("Error retrieving file:", e)
|
||||
print("Error retrieving textures from PCSX. Is it running?", e)
|
||||
exit()
|
||||
else: # Load from a specified filename
|
||||
with open(filename, "rb") as dumpfile:
|
||||
dumpbytes = dumpfile.read()
|
||||
@ -152,6 +161,15 @@ def load_raw_dump(filename):
|
||||
return np.reshape(bytestring, (512, int(datasize / 512)))
|
||||
|
||||
|
||||
class textureDisplayer:
|
||||
def __init__(self, vram_dump):
|
||||
self.rawvram = vram_dump
|
||||
self.colored = convert_rgb555(vram_dump)
|
||||
|
||||
def get_image(self, tpage, clut, x, y, w, h):
|
||||
return get_tpage_selection(self.rawvram, self.colored, tpage, clut, x, y, w, h)
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(description="Renders textures from VRAM dumps")
|
||||
parser.add_argument("dump_filename")
|
||||
# Load the numerical values; the lambda auto-detects decimal or hexadecimal and processes either.
|
||||
@ -165,17 +183,17 @@ parser.add_argument(
|
||||
"--showclut", action="store_true", help="Just show the 16 colors in the CLUT"
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = parser.parse_args()
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
array = load_raw_dump(args.dump_filename)
|
||||
if args.showclut:
|
||||
colored = convert_rgb555(array)
|
||||
clut = get_clut(colored, args.clut_num)
|
||||
clut = clut.reshape((1, 16, 3)) # reshape to turn the clut into a 1x16 image
|
||||
plt.imshow(clut)
|
||||
plt.show()
|
||||
elif args.whole:
|
||||
draw_tpage_selection(array, args.tpage_num, args.clut_num, 0, 0, 256, 256)
|
||||
else:
|
||||
draw_tpage_selection(array, args.tpage_num, args.clut_num, *args.UV_vals)
|
||||
array = load_raw_dump(args.dump_filename)
|
||||
if args.showclut:
|
||||
colored = convert_rgb555(array)
|
||||
clut = get_clut(colored, args.clut_num)
|
||||
clut = clut.reshape((1, 16, 3)) # reshape to turn the clut into a 1x16 image
|
||||
plt.imshow(clut)
|
||||
plt.show()
|
||||
elif args.whole:
|
||||
draw_tpage_selection(array, args.tpage_num, args.clut_num, 0, 0, 256, 256)
|
||||
else:
|
||||
draw_tpage_selection(array, args.tpage_num, args.clut_num, *args.UV_vals)
|
||||
|
Loading…
Reference in New Issue
Block a user