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

[OoT] Implemented quick cutscene import and single cutscene import #297

Merged
merged 6 commits into from
Aug 20, 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
24 changes: 19 additions & 5 deletions fast64_internal/oot/cutscene/importer/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import re

from dataclasses import dataclass
from typing import TYPE_CHECKING
from typing import Optional, TYPE_CHECKING
from bpy.types import Object, Armature
from ....utility import PluginError
from ..motion.utility import setupCutscene, getBlenderPosition, getInteger
Expand Down Expand Up @@ -47,8 +47,9 @@ class PropertyData:
class CutsceneImport(CutsceneObjectFactory):
"""This class contains functions to create the new cutscene Blender data"""

filePath: str # used when importing from the panel
fileData: str # used when importing the cutscenes when importing a scene
filePath: Optional[str] # used when importing from the panel
fileData: Optional[str] # used when importing the cutscenes when importing a scene
csName: Optional[str] # used when import a specific cutscene

def getCmdParams(self, data: str, cmdName: str, paramNumber: int):
"""Returns the list of every parameter of the given command"""
Expand Down Expand Up @@ -91,6 +92,13 @@ def getParsedCutscenes(self):
for oldName in oldNames:
fileData = fileData.replace(f"{oldName}(", f"{ootCSLegacyToNewCmdNames[oldName]}(")

# make a list of existing cutscene names, to skip importing them if found
existingCutsceneNames = [
csObj.name.removeprefix("Cutscene.")
for csObj in bpy.data.objects
if csObj.type == "EMPTY" and csObj.ootEmptyType == "Cutscene"
]

fileLines: list[str] = []
for line in fileData.split("\n"):
fileLines.append(line.strip())
Expand All @@ -102,16 +110,22 @@ def getParsedCutscenes(self):
for line in fileLines:
if not line.startswith("//") and not line.startswith("/*"):
if "CutsceneData " in line:
# split with "[" just in case the array has a set size
csName = line.split(" ")[1].split("[")[0]
if csName in existingCutsceneNames:
continue
foundCutscene = True

if foundCutscene:
sLine = line.strip()
csCmd = sLine.split("(")[0]
if "CutsceneData " not in line and "};" not in line and csCmd not in ootCutsceneCommandsC:
csData[-1] += line
if len(csData) > 0:
csData[-1] += line

if len(csData) == 0 or sLine.startswith("CS_") and not sLine.startswith("CS_FLOAT"):
csData.append(line)
if self.csName is None or self.csName == csName:
csData.append(line)

if "};" in line:
foundCutscene = False
Expand Down
5 changes: 3 additions & 2 deletions fast64_internal/oot/cutscene/importer/functions.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import bpy

from typing import Optional
from .classes import CutsceneImport


def importCutsceneData(filePath: str, sceneData: str):
def importCutsceneData(filePath: Optional[str], sceneData: Optional[str], csName: Optional[str] = None):
"""Initialises and imports the cutscene data from either a file or the scene data"""
# NOTE: ``sceneData`` is the data read when importing a scene
csMotionImport = CutsceneImport(filePath, sceneData)
csMotionImport = CutsceneImport(filePath, sceneData, csName)
return csMotionImport.setCutsceneData(bpy.context.scene.ootCSNumber)
11 changes: 8 additions & 3 deletions fast64_internal/oot/cutscene/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,16 +141,17 @@ def execute(self, context):

class OOT_ImportCutscene(Operator):
bl_idname = "object.oot_import_cutscenes"
bl_label = "Import All Cutscenes"
bl_options = {"REGISTER", "UNDO", "PRESET"}
bl_label = "Import Cutscenes"
bl_options = {"REGISTER", "UNDO"}

def execute(self, context):
try:
if context.mode != "OBJECT":
object.mode_set(mode="OBJECT")

path = abspath(context.scene.ootCutsceneImportPath)
context.scene.ootCSNumber = importCutsceneData(path, None)
csName = context.scene.ootCSImportName if len(context.scene.ootCSImportName) > 0 else None
context.scene.ootCSNumber = importCutsceneData(path, None, csName)

self.report({"INFO"}, "Successfully imported cutscenes")
return {"FINISHED"}
Expand Down Expand Up @@ -296,12 +297,16 @@ def cutscene_ops_register():
Scene.ootCutsceneExportPath = StringProperty(name="File", subtype="FILE_PATH")
Scene.ootCutsceneImportPath = StringProperty(name="File", subtype="FILE_PATH")
Scene.ootCSNumber = IntProperty(default=1, min=0)
Scene.ootCSImportName = StringProperty(
name="CS Name", description="Used to import a single cutscene, can be ``None``"
)


def cutscene_ops_unregister():
for cls in reversed(oot_cutscene_classes):
unregister_class(cls)

del Scene.ootCSImportName
del Scene.ootCSNumber
del Scene.ootCutsceneImportPath
del Scene.ootCutsceneExportPath
5 changes: 4 additions & 1 deletion fast64_internal/oot/cutscene/panels.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,12 @@ def draw(self, context):

importBox = layout.box()
importBox.label(text="Cutscene Importer")
prop_split(importBox, context.scene, "ootCutsceneImportPath", "Import From")
prop_split(importBox, context.scene, "ootCSImportName", "Import")
prop_split(importBox, context.scene, "ootCutsceneImportPath", "From")

col = importBox.column()
if len(context.scene.ootCSImportName) == 0:
col.label(text="All Cutscenes will be imported.")
col.operator(OOT_ImportCutscene.bl_idname)


Expand Down
58 changes: 48 additions & 10 deletions fast64_internal/oot/tools/quick_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from ..f3d.properties import OOTDLImportSettings
from ..skeleton.properties import OOTSkeletonImportSettings
from ..animation.properties import OOTAnimImportSettingsProperty
from ..cutscene.importer import importCutsceneData


class QuickImportAborted(Exception):
Expand All @@ -15,6 +16,27 @@ def __init__(self, message):
self.message = message


def get_found_defs(path: Path, sym_name: str, sym_def_pattern: re.Pattern[str]):
all_found_defs: dict[Path, list[tuple[str, str]]] = dict()

for dirpath, _, filenames in os.walk(path):
dirpath_p = Path(dirpath)
for filename in filenames:
file_p = dirpath_p / filename
# Only look into C files
if file_p.suffix != ".c":
continue
source = file_p.read_text()
# Simple check to see if we should look into this file any further
if sym_name not in source:
continue
found_defs = sym_def_pattern.findall(source)
print(file_p, f"{found_defs=}")
all_found_defs[file_p] = found_defs

return all_found_defs


def quick_import_exec(context: bpy.types.Context, sym_name: str):
sym_name = sym_name.strip()
if sym_name == "":
Expand All @@ -37,10 +59,17 @@ def quick_import_exec(context: bpy.types.Context, sym_name: str):

base_dir_p = Path(context.scene.ootDecompPath)
assets_objects_dir_p = base_dir_p / "assets" / "objects"
assets_scenes_dir_p = base_dir_p / "assets" / "scenes"
is_sym_object = True
all_found_defs = get_found_defs(assets_objects_dir_p, sym_name, sym_def_pattern)
if len(all_found_defs) == 0:
is_sym_object = False
all_found_defs = get_found_defs(assets_scenes_dir_p, sym_name, sym_def_pattern)

found_dir_p = assets_objects_dir_p if is_sym_object else assets_scenes_dir_p
all_found_defs: dict[Path, list[tuple[str, str]]] = dict()

for dirpath, dirnames, filenames in os.walk(assets_objects_dir_p):
for dirpath, dirnames, filenames in os.walk(found_dir_p):
dirpath_p = Path(dirpath)
for filename in filenames:
file_p = dirpath_p / filename
Expand All @@ -67,28 +96,34 @@ def quick_import_exec(context: bpy.types.Context, sym_name: str):
if len(all_found_defs) > 1:
raise QuickImportAborted(
f"Found definitions of {sym_name} in several files: "
+ ", ".join(str(p.relative_to(assets_objects_dir_p)) for p in all_found_defs.keys())
+ ", ".join(str(p.relative_to(found_dir_p)) for p in all_found_defs.keys())
)
assert len(all_found_defs) == 1
sym_file_p, sym_defs = list(all_found_defs.items())[0]
if len(sym_defs) > 1:
raise QuickImportAborted(
f"Found several definitions of {sym_name} in {sym_file_p.relative_to(assets_objects_dir_p)}"
)
raise QuickImportAborted(f"Found several definitions of {sym_name} in {sym_file_p.relative_to(found_dir_p)}")

# We found a single definition of the symbol
sym_def_type, sym_def_array_decl = sym_defs[0]
is_array = sym_def_array_decl != ""
object_name = sym_file_p.relative_to(assets_objects_dir_p).parts[0]
folder_name = sym_file_p.relative_to(found_dir_p).parts[0]

def raise_only_from_object(type: str):
if not is_sym_object:
raise QuickImportAborted(
f"Can only import {type} from an object ({sym_name} found in {sym_file_p.relative_to(base_dir_p)})"
)

if sym_def_type == "Gfx" and is_array:
raise_only_from_object("Gfx[]")
settings: OOTDLImportSettings = context.scene.fast64.oot.DLImportSettings
settings.name = sym_name
settings.folder = object_name
settings.folder = folder_name
Dragorn421 marked this conversation as resolved.
Show resolved Hide resolved
settings.actorOverlayName = ""
settings.isCustom = False
bpy.ops.object.oot_import_dl()
elif sym_def_type in {"SkeletonHeader", "FlexSkeletonHeader"} and not is_array:
raise_only_from_object(sym_def_type)
settings: OOTSkeletonImportSettings = context.scene.fast64.oot.skeletonImportSettings
settings.isCustom = False
if sym_name == "gLinkAdultSkel":
Expand All @@ -98,19 +133,22 @@ def quick_import_exec(context: bpy.types.Context, sym_name: str):
else:
settings.mode = "Generic"
settings.name = sym_name
settings.folder = object_name
settings.folder = folder_name
settings.actorOverlayName = ""
bpy.ops.object.oot_import_skeleton()
elif sym_def_type == "AnimationHeader" and not is_array:
raise_only_from_object(sym_def_type)
settings: OOTAnimImportSettingsProperty = context.scene.fast64.oot.animImportSettings
settings.isCustom = False
settings.isLink = False
settings.animName = sym_name
settings.folderName = object_name
settings.folderName = folder_name
bpy.ops.object.oot_import_anim()
elif sym_def_type == "CutsceneData" and is_array:
bpy.context.scene.ootCSNumber = importCutsceneData(f"{sym_file_p}", None, sym_name)
else:
raise QuickImportAborted(
f"Don't know how to import {sym_def_type}"
+ ("[]" if is_array else "")
+ f" (symbol found in {object_name})"
+ f" (symbol found in {sym_file_p.relative_to(base_dir_p)})"
)
Loading