mirror of
https://github.com/ApfelTeeSaft/lightspeed64.git
synced 2026-08-26 19:33:24 +00:00
* Ignore IntelliJ editor data
* [OoT] Add skeleton import test scripts
In preparation for fixing skeleton imports from the MM decomp, make a
script for testing skeleton imports.
Running `python3 scripts/make_all_skeletons.py <decomp path> <output
folder>` will attempt to import all skeletons from every file in the
decomp that appears to contain a skeleton, and report on how many
files raised exceptions during the import. The generated .blend files
are stored in the output folder. Including a third argument of ` will
attempt to import all animations for files that contain a single
skeleton as well.
Running this on the mm decomp at commit
803ff1fb1593cdc0c62d14882973af04dc0f988e (from 2024-07-13) results in
only 3/174 (1.7%) of files with skeletons importing them without exceptions.
* [OoT] Strip comments in limb list
The MM decomp specifies `EnumName` values for limbs in the asset XML,
which generates an enum naming bone indicies. These enum values are
also generated in comments in the limb list:
void* gDekuButlerSkelLimbs[] = {
&gDekuButlerRootLimb, /* DEKU_BUTLER_LIMB_ROOT */
Fast64's current limb list parsing does not expect comments here,
which causes the limb list parse to fail.
Add a function to strip comments of this style, and strip them before
parsing limb list entires.
* [OoT] Identify and parse enums when importing skeletnos
The MM decomp defines EnumNames for limbs in asset XML, which causes
ZAPD to generate limb definitions that use these enum values for the
next child and next sibling (with an offset of 1).
StandardLimb gDekuButlerRootLimb = {
{ 0, 2775, 0 }, DEKU_BUTLER_LIMB_PELVIS - 1, LIMB_DONE,
ootGetLimb currently only supports int values, hex values, or
LIMB_DONE here.
In preparation for supporting limb enum values of this form, add the
object's header file to skeletonData and parse all enums found during
skeleton import. The next patch in this series will use the parsed
enums to handle limb definitions of this form.
* [OoT] Support limb enums for limb nextChild / nextSibling
Use the parsed enums to support next child and next sibling
definitions of the form `<limb enum value> - 1`.
With this change and the others from this patch series,
`make_all_skeletons.py` goes from just 3/174 (1.1%) of MM decomp files
with successful skeleton imports to 169/174 (97.1%).
98 lines
3.4 KiB
Python
98 lines
3.4 KiB
Python
import re
|
|
|
|
import bpy
|
|
import sys
|
|
|
|
# import path
|
|
from bpy.path import abspath
|
|
|
|
"""
|
|
A script that can be run in blender to import all skeletons and animations
|
|
(if there's only one skeleton) in a file from OOT or MM
|
|
|
|
Usage:
|
|
blender --background --python-exit-code 1 --python make_skeletons.py -- <path to decomp> <input file> <output blend file> <object name> ["1" to import animations too]
|
|
|
|
Example:
|
|
blender --background --python-exit-code 1 --python make_skeletons.py -- ~/git/mm ~/git/mm/assets/objects/object_dnj/object_dnj.c deku_butler.blend object_dnj 1
|
|
"""
|
|
args = sys.argv[(sys.argv.index("--") + 1) :]
|
|
|
|
decompPath = args[0]
|
|
inFile = args[1]
|
|
outFile = args[2]
|
|
objectName = args[3]
|
|
importAnimations = len(args) > 4 and args[4] == "1"
|
|
|
|
# objectName = path.basename(path.dirname(inFile))
|
|
print(f"decomp path {decompPath}")
|
|
print(f"inFile {inFile}")
|
|
print(f"outFile {outFile}")
|
|
print(f"object name {objectName}")
|
|
|
|
# delete the default cube
|
|
if bpy.context.view_layer.objects.active.name == "Cube":
|
|
bpy.ops.object.delete()
|
|
|
|
|
|
with open(inFile, "r") as file:
|
|
code = file.read()
|
|
|
|
# Identify all skeleton headers in the input file
|
|
skeletonNames = list(
|
|
m.group("name") for m in re.finditer(r"(Flex)?SkeletonHeader\s*(?P<name>[A-Za-z0-9\_]+)\s*=", code)
|
|
)
|
|
|
|
# Setup Fast64 settings
|
|
bpy.context.scene.gameEditorMode = "OOT"
|
|
bpy.context.scene.ootDecompPath = abspath(decompPath)
|
|
bpy.context.scene.fast64.oot.animImportSettings.folderName = objectName
|
|
|
|
# These aren't used by the script, but we may as well set them to reasonable values
|
|
# in case someone is going to work out of the output blend file
|
|
bpy.context.scene.fast64.oot.skeletonExportSettings.folder = objectName
|
|
bpy.context.scene.fast64.oot.animExportSettings.folderName = objectName
|
|
bpy.context.scene.fast64.oot.DLExportSettings.folder = objectName
|
|
bpy.context.scene.fast64.oot.collisionExportSettings.folder = objectName
|
|
|
|
# Import all skeletons from the file
|
|
errs = []
|
|
for skeletonName in skeletonNames:
|
|
imp = bpy.context.scene.fast64.oot.skeletonImportSettings
|
|
imp.name = skeletonName # e.g. gDekuButlerSkel
|
|
imp.folder = objectName # e.g. object_dnj
|
|
# TODO: maybe try to identify an appropriate overlay, or allow it as an argument
|
|
imp.actorOverlayName = "" # e.g. ovl_En_Dno
|
|
|
|
res = bpy.ops.object.oot_import_skeleton()
|
|
if "CANCELLED" in res:
|
|
errs.append(f"Failed to import skeleton {skeletonName}")
|
|
|
|
# Import animations if there's only one skeleton and animation import was anbled
|
|
if len(skeletonNames) == 1 and importAnimations:
|
|
animationNames = list(m.group("name") for m in re.finditer(r"AnimationHeader\s*(?P<name>[A-Za-z0-9\_]+)\s*=", code))
|
|
|
|
# select the armature
|
|
bpy.context.view_layer.objects.active = bpy.context.view_layer.objects[skeletonNames[0]]
|
|
|
|
# import each animation
|
|
for animationName in animationNames:
|
|
bpy.context.scene.fast64.oot.animImportSettings.animName = animationName
|
|
res = bpy.ops.object.oot_import_anim()
|
|
if "CANCELLED" in res:
|
|
errs.append(f"Failed to import animation {animationName}")
|
|
|
|
if len(errs) > 0:
|
|
raise RuntimeError(f"Errors running skeleton import: {errs}")
|
|
|
|
# Set viewport shading to show textures
|
|
for area in bpy.context.screen.areas:
|
|
if area.type == "VIEW_3D":
|
|
for space in area.spaces:
|
|
if space.type == "VIEW_3D":
|
|
space.shading.type = "MATERIAL"
|
|
|
|
# Save the file if anything was imported
|
|
if len(skeletonNames) > 0:
|
|
bpy.ops.wm.save_mainfile(filepath=outFile)
|