Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement automated material display list generation from materials #21

Merged
merged 16 commits into from
Dec 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion gcft_ui/bunfoe_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,10 +356,11 @@ def set_widget_value(self, widget: QWidget, value, field_type: typing.Type, inst
widget.setText(value)
elif isinstance(widget, QComboBox):
assert issubclass(field_type, Enum)
if len(field_type) > 0:
if len(field_type) > 0 and isinstance(value, field_type):
index_of_value = list(field_type).index(value)
widget.setCurrentIndex(index_of_value)
else:
# TODO: Is there some way we could display invalid values for enums instead of a blank combobox?
widget.setCurrentIndex(-1)
elif isinstance(widget, QPushButton):
assert issubclass(field_type, RGBA)
Expand Down
20 changes: 1 addition & 19 deletions gcft_ui/gcm_tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,24 +428,6 @@ def show_gcm_files_tree_context_menu(self, pos):

basename, file_ext = os.path.splitext(file.name)

is_compressed_rarc = False
try:
if file_ext == ".szs":
file_data = self.gcm.get_changed_file_data(file.file_path)
if Yaz0.check_is_compressed(file_data):
magic = fs.read_str(file_data, 0x11, 4)
if magic == "RARC":
is_compressed_rarc = True
elif file_ext == ".szp":
file_data = self.gcm.get_changed_file_data(file.file_path)
if Yay0.check_is_compressed(file_data):
chunk_offset = fs.read_u32(file_data, 0xC)
magic = fs.read_str(file_data, chunk_offset, 4)
if magic == "RARC":
is_compressed_rarc = True
except Exception as e:
pass

if file_ext == ".bti" or self.is_banner_filename(file.name):
menu.addAction(self.ui.actionOpenGCMImage)
self.ui.actionOpenGCMImage.setData(file)
Expand All @@ -456,7 +438,7 @@ def show_gcm_files_tree_context_menu(self, pos):
self.ui.actionReplaceGCMImage.setDisabled(True)
else:
self.ui.actionReplaceGCMImage.setDisabled(False)
elif file_ext == ".arc" or is_compressed_rarc:
elif self.gcm.check_file_is_rarc(file.file_path):
menu.addAction(self.ui.actionOpenGCMRARC)
self.ui.actionOpenGCMRARC.setData(file)

Expand Down
43 changes: 35 additions & 8 deletions gcft_ui/j3d_tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@
from gclib.j3d_chunks.shp1 import SHP1
from gclib.j3d_chunks.mat3 import MAT3, Material
from gclib.j3d_chunks.mdl3 import MDL3, MDLEntry, BPRegister, XFRegister
from gclib.j3d_chunks.mdl_command import MDLCommand
import gclib.j3d_chunks.bp_command as BP
import gclib.j3d_chunks.xf_command as XF
from gclib.j3d_chunks.tex1 import TEX1
from gclib.j3d_chunks.trk1 import TRK1, ColorAnimation
from gclib.j3d_chunks.ttk1 import TTK1, UVAnimation
from gclib.bti import BTI

from gcft_ui.uic.ui_j3d_tab import Ui_J3DTab
from gcft_ui.bunfoe_editor import BunfoeEditor, BunfoeWidget
from gcft_ui.bunfoe_editor import BunfoeEditor, BunfoeWidget, BunfoeDialog

class J3DTab(BunfoeEditor):
def __init__(self):
Expand All @@ -49,6 +52,7 @@ def __init__(self):
self.chunk_type_is_expanded = {
"TEX1": True,
"MAT3": True,
"MDL3": False,
"TRK1": True,
}

Expand Down Expand Up @@ -486,7 +490,9 @@ def mdl_entry_selected(self, mdl_entry: MDLEntry):

entry_index = self.j3d.mdl3.entries.index(mdl_entry)
mat_name = self.j3d.mat3.mat_names[entry_index]
self.ui.j3d_sidebar_label.setText("Showing material display list for: %s" % mat_name)
sidebar_label_text = "Showing material display list for: %s" % mat_name
sidebar_label_text += "\n(MDL3 is generated automatically from MAT3.)"
self.ui.j3d_sidebar_label.setText(sidebar_label_text)

bp_commands_widget = QWidget()
bp_commands_layout = QFormLayout(bp_commands_widget)
Expand All @@ -512,21 +518,42 @@ def mdl_entry_selected(self, mdl_entry: MDLEntry):
else:
reg_name = f"0x{bp_command.register:02X}"

command_text = f"0x{bp_command.value:06X}"
command_text = f"0x{bp_command.bitfield:06X}"
field_widget = QPushButton(command_text)
field_widget.setProperty('mdl_command', bp_command)
field_widget.setSizePolicy(QSizePolicy.Policy.Maximum, field_widget.sizePolicy().verticalPolicy())
field_widget.clicked.connect(self.open_mdl_command_editor)

bp_commands_layout.addRow(reg_name, QLabel(command_text))
bp_commands_layout.addRow(reg_name, field_widget)

for xf_command in mdl_entry.xf_commands:
if xf_command.register in [entry.value for entry in XFRegister]:
reg_name = XFRegister(xf_command.register).name
else:
reg_name = f"0x{xf_command.register:04X}"

command_text = "\n".join([f"0x{arg:08X}" for arg in xf_command.args])
command_text = ", ".join([f"0x{arg.bitfield:08X}" for arg in xf_command.args])
field_widget = QPushButton(command_text)
field_widget.setProperty('mdl_command', xf_command)
field_widget.setSizePolicy(QSizePolicy.Policy.Maximum, field_widget.sizePolicy().verticalPolicy())
field_widget.clicked.connect(self.open_mdl_command_editor)

reg_label = QLabel(reg_name)
reg_label.setAlignment(Qt.AlignmentFlag.AlignTop)
xf_commands_layout.addRow(reg_label, QLabel(command_text))
xf_commands_layout.addRow(reg_name, field_widget)

def open_mdl_command_editor(self):
button: QPushButton = self.sender()
mdl_command: MDLCommand = button.property('mdl_command')
print(mdl_command)

BunfoeDialog.show_dialog_for_bunfoe(mdl_command, self, "Edit MDL Command")

# Update the text on the button.
self.j3d.mdl3.save()
if isinstance(mdl_command, BP.BPCommand):
command_text = f"0x{mdl_command.bitfield:06X}"
elif isinstance(mdl_command, XF.XFCommand):
command_text = ", ".join([f"0x{arg.bitfield:08X}" for arg in mdl_command.args])
button.setText(command_text)

def keyframe_selected(self, keyframe: AnimationKeyframe):
layout = self.ui.scrollAreaWidgetContents.layout()
Expand Down
17 changes: 7 additions & 10 deletions gcft_ui/j3d_viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import re
from io import BytesIO
import time
import copy
from PySide6.QtGui import *
from PySide6.QtCore import *
from PySide6.QtWidgets import *
Expand Down Expand Up @@ -373,11 +374,10 @@ def guesstimate_model_bbox(self, j3d_model: J3D) -> tuple[np.ndarray, np.ndarray
def get_preview_compatible_j3d(self, orig_j3d: J3D) -> J3D:
# return orig_j3d

# We have to save the original J3D for the changes to its chunks to be reflected properly.
# Simply copying orig_j3d.data is not sufficient on its own.
orig_j3d.save()
# TODO: implement copying just the instance, without having to serialize and deserialize it here.
hack_j3d = J3D(fs.make_copy_data(orig_j3d.data))
# Making a copy of the orig_j3d's data bytes or its instance wouldn't be enough for the changes
# to its chunks to be reflected properly. So we make a deepcopy of it instead.
# Then we modify that copy with some hacks and save that, then use those bytes for the preview.
hack_j3d = copy.deepcopy(orig_j3d)
chunks_modified = set()

# Wind Waker has a hardcoded system where the textures that control toon shading are dynamically
Expand Down Expand Up @@ -423,11 +423,8 @@ def get_preview_compatible_j3d(self, orig_j3d: J3D) -> J3D:
# del hack_j3d.mat3.materials[mat_index]
chunks_modified.add("MAT3")

if chunks_modified:
hack_j3d.save(only_chunks=chunks_modified)
return hack_j3d
else:
return orig_j3d
hack_j3d.save()
return hack_j3d

#region Animation

Expand Down
Loading