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] Add missing versions to oot_versions_items and add overlay support for quick import #482

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
9 changes: 7 additions & 2 deletions fast64_internal/oot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,19 @@

oot_versions_items = [
("Custom", "Custom", "Custom"),
("ntsc-1.0", "ntsc-1.0", "ntsc-1.0"),
("ntsc-1.1", "ntsc-1.1", "ntsc-1.1"),
("pal-1.0", "pal-1.0", "pal-1.0"),
("ntsc-1.2", "ntsc-1.2", "ntsc-1.2"),
("pal-1.1", "pal-1.1", "pal-1.1"),
("gc-jp", "gc-jp", "gc-jp"),
("gc-jp-mq", "gc-jp-mq", "gc-jp-mq"),
("gc-jp-ce", "gc-jp-ce", "gc-jp-ce"),
("gc-us", "gc-us", "gc-us"),
("gc-us-mq", "gc-us-mq", "gc-us-mq"),
("gc-eu-mq-dbg", "gc-eu-mq-dbg", "gc-eu-mq-dbg"),
("gc-eu", "gc-eu", "gc-eu"),
("gc-eu-mq", "gc-eu-mq", "gc-eu-mq"),
("gc-eu-mq-dbg", "gc-eu-mq-dbg", "gc-eu-mq-dbg"),
("gc-jp-ce", "gc-jp-ce", "gc-jp-ce"),
("hackeroot-mq", "HackerOoT", "hackeroot-mq"), # TODO: force this value if HackerOoT features are enabled?
("legacy", "Legacy", "Older Decomp Version"),
]
Expand Down
16 changes: 14 additions & 2 deletions fast64_internal/oot/cutscene/importer/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ def getParsedCutscenes(self):
if csName in existingCutsceneNames:
continue
foundCutscene = True
print(f"INFO: Found cutscene '{csName}' in the file data.")

if foundCutscene:
sLine = line.strip()
Expand All @@ -129,7 +130,8 @@ def getParsedCutscenes(self):

if "};" in line:
foundCutscene = False
cutsceneList.append(csData)
if len(csData) > 0:
cutsceneList.append(csData)
csData = []

if len(cutsceneList) == 0:
Expand Down Expand Up @@ -187,7 +189,11 @@ def getParsedCutscenes(self):
elif not "CutsceneData" in curCmd and not "};" in curCmd:
print(f"WARNING: Unknown command found: ``{curCmd}``")
cmdListFound = False
parsedCutscenes.append(ParsedCutscene(csName, parsedCS))

if csName is not None and len(parsedCS) > 0:
parsedCutscenes.append(ParsedCutscene(csName, parsedCS))
else:
raise PluginError("ERROR: Something wrong happened during the parsing of the cutscene.")

return parsedCutscenes

Expand All @@ -200,6 +206,9 @@ def getCutsceneList(self):
# if it's none then there's no cutscene in the file
return None

if len(parsedCutscenes) == 0:
raise PluginError("ERROR: No cutscene was found.")

cutsceneList: list[Cutscene] = []

# for each cutscene from the list returned by getParsedCutscenes(),
Expand Down Expand Up @@ -486,6 +495,9 @@ def setCutsceneData(self, csNumber):
# if it's none then there's no cutscene in the file
return csNumber

if len(cutsceneList) == 0:
raise PluginError("ERROR: No cutscene was found.")

for i, cutscene in enumerate(cutsceneList, csNumber):
print(f'Found Cutscene "{cutscene.name}"! Importing...')
self.validateCameraData(cutscene)
Expand Down
20 changes: 12 additions & 8 deletions fast64_internal/oot/cutscene/preview.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import bpy

from math import isclose
from typing import TYPE_CHECKING
from bpy.types import Scene, Object, Node
from bpy.app.handlers import persistent
from ...utility import gammaInverse, hexOrDecInt
from .motion.utility import getCutsceneCamera

if TYPE_CHECKING:
from .properties import OOTCutscenePreviewSettingsProperty, OOTCutscenePreviewProperty


def getLerp(max: float, min: float, val: float):
# from ``Environment_LerpWeight()`` in decomp
Expand Down Expand Up @@ -112,13 +116,14 @@ def initFirstFrame(csObj: Object, useNodeFeatures: bool, defaultCam: Object):
def processCurrentFrame(csObj: Object, curFrame: float, useNodeFeatures: bool, cameraObjects: Object):
"""Execute the actions of each command to create the preview for the current frame"""
# this function was partially adapted from ``z_demo.c``
previewProp: "OOTCutscenePreviewProperty" = csObj.ootCutsceneProperty.preview
preview_settings: "OOTCutscenePreviewSettingsProperty" = bpy.context.scene.ootPreviewSettingsProperty

if curFrame == 0:
initFirstFrame(csObj, useNodeFeatures, cameraObjects[1])

if useNodeFeatures:
previewProp = csObj.ootCutsceneProperty.preview
for transitionCmd in csObj.ootCutsceneProperty.preview.transitionList:
for transitionCmd in previewProp.transitionList:
startFrame = transitionCmd.startFrame
endFrame = transitionCmd.endFrame
frameCur = curFrame
Expand Down Expand Up @@ -168,7 +173,7 @@ def processCurrentFrame(csObj: Object, curFrame: float, useNodeFeatures: bool, c
color[3] = alpha
bpy.context.scene.node_tree.nodes["CSTrans_RGB"].outputs[0].default_value = color

for miscCmd in csObj.ootCutsceneProperty.preview.miscList:
for miscCmd in previewProp.miscList:
startFrame = miscCmd.startFrame
endFrame = miscCmd.endFrame

Expand All @@ -177,10 +182,9 @@ def processCurrentFrame(csObj: Object, curFrame: float, useNodeFeatures: bool, c

if curFrame == startFrame:
if miscCmd.type == "set_locked_viewpoint" and not None in cameraObjects:
bpy.context.scene.camera = cameraObjects[int(csObj.ootCutsceneProperty.preview.isFixedCamSet)]
csObj.ootCutsceneProperty.preview.isFixedCamSet ^= True

elif miscCmd.type == "stop_cutscene":
bpy.context.scene.camera = cameraObjects[int(previewProp.isFixedCamSet)]
previewProp.isFixedCamSet ^= True
elif not preview_settings.ignore_cs_misc_stop and miscCmd.type == "stop_cutscene":
# stop the playback and set the frame to 0
bpy.ops.screen.animation_cancel()
bpy.context.scene.frame_set(bpy.context.scene.frame_start)
Expand Down Expand Up @@ -219,7 +223,7 @@ def processCurrentFrame(csObj: Object, curFrame: float, useNodeFeatures: bool, c
@persistent
def cutscenePreviewFrameHandler(scene: Scene):
"""Preview frame handler, executes each frame when the cutscene is played"""
previewSettings = scene.ootPreviewSettingsProperty
previewSettings: "OOTCutscenePreviewSettingsProperty" = scene.ootPreviewSettingsProperty
csObj: Object = previewSettings.ootCSPreviewCSObj

if csObj is None or not csObj.type == "EMPTY" and not csObj.ootEmptyType == "Cutscene":
Expand Down
3 changes: 3 additions & 0 deletions fast64_internal/oot/cutscene/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,8 @@ class OOTCutscenePreviewSettingsProperty(PropertyGroup):
default="link_adult",
)

ignore_cs_misc_stop: BoolProperty(name="Ignore 'Stop Cutscene' Command", default=False)

# internal only
ootCSPreviewNodesReady: BoolProperty(default=False)
ootCSPreviewCSObj: PointerProperty(type=Object)
Expand Down Expand Up @@ -350,6 +352,7 @@ def draw_props(self, layout: UILayout):
prop_split(previewBox, self, "previewPlayerAge", "Player Age for Preview")
previewBox.prop(self, "useWidescreen")
previewBox.prop(self, "useOpaqueCamBg")
previewBox.prop(self, "ignore_cs_misc_stop")


class OOTCutsceneProperty(PropertyGroup):
Expand Down
68 changes: 37 additions & 31 deletions fast64_internal/oot/tools/quick_import.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from pathlib import Path
import os
import re

import bpy

from pathlib import Path
from typing import Optional
from ..f3d.properties import OOTDLImportSettings
from ..skeleton.properties import OOTSkeletonImportSettings
from ..animation.properties import OOTAnimImportSettingsProperty
Expand All @@ -21,18 +21,25 @@ def get_found_defs(path: Path, sym_name: str, sym_def_pattern: re.Pattern[str]):

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

if len(found_defs) > 0:
print(file_p, f"{found_defs=}")
all_found_defs[file_p] = found_defs

return all_found_defs

Expand All @@ -57,33 +64,30 @@ def quick_import_exec(context: bpy.types.Context, sym_name: str):

sym_def_pattern = re.compile(rf"([^\s]+)\s+{sym_name}\s*(\[[^\]]*\])?\s*=")

base_dir_p = Path(context.scene.ootDecompPath) / context.scene.fast64.oot.get_extracted_path()
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()
found_dir_p: Optional[Path] = None
base_dir_p = Path(context.scene.ootDecompPath)

for dirpath, dirnames, filenames in os.walk(found_dir_p):
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=}")
if found_defs:
all_found_defs[file_p] = found_defs
# this str cast completely useless, it's there to force linting to recognize a Path element
extracted_dir_p = base_dir_p / str(context.scene.fast64.oot.get_extracted_path())

assets_paths: list[Path] = [
# objects
extracted_dir_p / "assets" / "objects",
# scenes
extracted_dir_p / "assets" / "scenes",
# other assets embedded in actors (cutscenes for instance)
base_dir_p / "src" / "overlays" / "actors",
]

for path in assets_paths:
all_found_defs = get_found_defs(path, sym_name, sym_def_pattern)

if len(all_found_defs) > 0:
found_dir_p = path
break

assert found_dir_p is not None

# Ideally if for example sym_name was gLinkAdultHookshotTipDL,
# all_found_defs now contains:
Expand All @@ -93,13 +97,15 @@ def quick_import_exec(context: bpy.types.Context, sym_name: str):

if len(all_found_defs) == 0:
raise QuickImportAborted(f"Couldn't find a definition of {sym_name}, is the OoT Version correct?")

if len(all_found_defs) > 1:
raise QuickImportAborted(
f"Found definitions of {sym_name} in several files: "
+ ", ".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(found_dir_p)}")

Expand All @@ -109,7 +115,7 @@ def quick_import_exec(context: bpy.types.Context, sym_name: str):
folder_name = sym_file_p.relative_to(found_dir_p).parts[0]

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