From 2c709cace080f488fa93de37ceb1c72deeb2f786 Mon Sep 17 00:00:00 2001 From: kurethedead Date: Mon, 3 Feb 2020 12:58:24 -0800 Subject: [PATCH] Added automatic header modification for actor geolayout exporting. --- README.md | 4 +- __init__.py | 21 ++++++++- fast64_internal/sm64_geolayout_writer.py | 59 +++++++++++++++--------- fast64_internal/utility.py | 20 +++++++- 4 files changed, 79 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 91ab5b6..1326603 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,9 @@ Basically, Mario's DMA table starts at 0x4EC000. There is an 8 byte header, and Often times it is hard to rig an existing SM64 geolayout, as there are many intermediate non-deform bones and bones don't point to their children. To make this easier you can use the 'Create Animatable Metarig' operator in the SM64 Armature Tools header. This will generate a metarig which can be used with IK. The metarig bones will be placed on armature layers 3 and 4. ### Exporting Geolayouts to C -To replace an actor model in decomp, replace its geo.inc.c and model.inc.c contents with the geolayout file and the dl file respectively. Use the contents of the header file to replace existing extern declarations in one of the group header files (ex. mario is in group0.h). Make sure that the name of your geolayout is the same the name of the geolayout you're replacing. Note that any function addresses in geolayout nodes will be converted to decomp function names if possible. +To replace an actor model in decomp, make sure "Write Headers for Actor" is checked and set the correct group name. Make sure the "Name" field is the folder name of the actor, and the directory is the /actors folder. + +To replace an actor model manually, replace its geo.inc.c and model.inc.c contents with the geolayout file and the dl file respectively. Use the contents of the header file to replace existing extern declarations in one of the group header files (ex. mario is in group0.h). Make sure that the name of your geolayout is the same the name of the geolayout you're replacing. Note that any function addresses in geolayout nodes will be converted to decomp function names if possible. ### Switch Statements To create a switch node, and a bone to your armature and set its geolayout type to "Switch". Any bones that will be switched should be parented to this switch bone. The switch bone can do either material, draw layer, or mesh switching. diff --git a/__init__.py b/__init__.py index 9704920..4f3e468 100644 --- a/__init__.py +++ b/__init__.py @@ -355,7 +355,9 @@ class SM64_ExportGeolayoutObject(bpy.types.Operator): bpy.context.scene.geoTexDir, bpy.context.scene.geoSaveTextures, bpy.context.scene.geoSeparateTextureDef, - levelCamera) + levelCamera, bpy.context.scene.geoGroupName if \ + bpy.context.scene.geoWriteHeaders else None, + context.scene.geoName) self.report({'INFO'}, 'Success! Geolayout at ' + \ context.scene.geoExportPath) elif context.scene.geoExportType == 'Insertable Binary': @@ -522,7 +524,9 @@ class SM64_ExportGeolayoutArmature(bpy.types.Operator): bpy.context.scene.geoTexDir, bpy.context.scene.geoSaveTextures, bpy.context.scene.geoSeparateTextureDef, - levelCamera) + levelCamera, bpy.context.scene.geoGroupName if \ + bpy.context.scene.geoWriteHeaders else None, + context.scene.geoName) self.report({'INFO'}, 'Success! Geolayout at ' + \ context.scene.geoExportPath) elif context.scene.geoExportType == 'Insertable Binary': @@ -641,6 +645,10 @@ class SM64_ExportGeolayoutPanel(bpy.types.Panel): col.prop(context.scene, 'geoExportType') if context.scene.geoExportType == 'C': col.prop(context.scene, 'geoExportPath') + prop_split(col, context.scene, 'geoName', 'Name') + col.prop(context.scene, 'geoWriteHeaders') + if context.scene.geoWriteHeaders: + prop_split(col, context.scene, 'geoGroupName', 'Group Name') col.prop(context.scene, 'geoSaveTextures') if context.scene.geoSaveTextures: col.prop(context.scene, 'geoTexDir') @@ -1703,6 +1711,12 @@ def register(): name = 'Filepath', subtype = 'FILE_PATH') bpy.types.Scene.geoIsSegPtr = bpy.props.BoolProperty( name = 'Is Segmented Address') + bpy.types.Scene.geoName = bpy.props.StringProperty( + name = 'Name', default = 'mario') + bpy.types.Scene.geoGroupName = bpy.props.StringProperty( + name = 'Name', default = 'group0') + bpy.types.Scene.geoWriteHeaders = bpy.props.BoolProperty( + name = 'Write Headers For Actor', default = True) # Level bpy.types.Scene.levelLevel = bpy.props.EnumProperty(items = level_enums, @@ -1824,6 +1838,9 @@ def unregister(): del bpy.types.Scene.geoSeparateTextureDef del bpy.types.Scene.geoInsertableBinaryPath del bpy.types.Scene.geoIsSegPtr + del bpy.types.Scene.geoName + del bpy.types.Scene.geoGroupName + del bpy.types.Scene.geoWriteHeaders # Animation del bpy.types.Scene.animStartImport diff --git a/fast64_internal/sm64_geolayout_writer.py b/fast64_internal/sm64_geolayout_writer.py index a295370..8233f65 100644 --- a/fast64_internal/sm64_geolayout_writer.py +++ b/fast64_internal/sm64_geolayout_writer.py @@ -88,7 +88,7 @@ def appendRevertToGeolayout(geolayoutGraph, fModel): # Convert to Geolayout def convertArmatureToGeolayout(armatureObj, obj, convertTransformMatrix, - f3dType, isHWv1, camera): + f3dType, isHWv1, camera, name): fModel = FModel(f3dType, isHWv1) @@ -107,12 +107,12 @@ def convertArmatureToGeolayout(armatureObj, obj, convertTransformMatrix, # Start geolayout if camera is not None: - geolayoutGraph = GeolayoutGraph(armatureObj.name + '_level') + geolayoutGraph = GeolayoutGraph(name + '_level') cameraObj = getCameraObj(camera) meshGeolayout = saveCameraSettingsToGeolayout( geolayoutGraph, cameraObj, armatureObj) else: - geolayoutGraph = GeolayoutGraph(armatureObj.name) + geolayoutGraph = GeolayoutGraph(name + "_geo") rootNode = TransformNode(StartNode()) geolayoutGraph.startGeolayout.nodes.append(rootNode) meshGeolayout = geolayoutGraph.startGeolayout @@ -126,7 +126,7 @@ def convertArmatureToGeolayout(armatureObj, obj, convertTransformMatrix, return geolayoutGraph, fModel def convertObjectToGeolayout(obj, convertTransformMatrix, - f3dType, isHWv1, camera): + f3dType, isHWv1, camera, name): fModel = FModel(f3dType, isHWv1) @@ -135,12 +135,12 @@ def convertObjectToGeolayout(obj, convertTransformMatrix, # Start geolayout if camera is not None: - geolayoutGraph = GeolayoutGraph(obj.name + '_level') + geolayoutGraph = GeolayoutGraph(name + '_level') cameraObj = getCameraObj(camera) meshGeolayout = saveCameraSettingsToGeolayout( geolayoutGraph, cameraObj, obj) else: - geolayoutGraph = GeolayoutGraph(obj.name) + geolayoutGraph = GeolayoutGraph(name + '_geo') rootNode = TransformNode(StartNode()) geolayoutGraph.startGeolayout.nodes.append(rootNode) meshGeolayout = geolayoutGraph.startGeolayout @@ -181,23 +181,25 @@ def convertObjectToGeolayout(obj, convertTransformMatrix, # C Export def exportGeolayoutArmatureC(armatureObj, obj, convertTransformMatrix, - f3dType, isHWv1, dirPath, texDir, savePNG, texSeparate, camera): + f3dType, isHWv1, dirPath, texDir, savePNG, texSeparate, camera, groupName, name): geolayoutGraph, fModel = convertArmatureToGeolayout(armatureObj, obj, - convertTransformMatrix, f3dType, isHWv1, camera) + convertTransformMatrix, f3dType, isHWv1, camera, name) - saveGeolayoutC(armatureObj.name, geolayoutGraph, fModel, dirPath, texDir, - savePNG, texSeparate) + saveGeolayoutC(name, geolayoutGraph, fModel, dirPath, texDir, + savePNG, texSeparate, groupName) def exportGeolayoutObjectC(obj, convertTransformMatrix, - f3dType, isHWv1, dirPath, texDir, savePNG, texSeparate, camera): + f3dType, isHWv1, dirPath, texDir, savePNG, texSeparate, camera, groupName, name): geolayoutGraph, fModel = convertObjectToGeolayout(obj, - convertTransformMatrix, f3dType, isHWv1, camera) + convertTransformMatrix, f3dType, isHWv1, camera, name) - saveGeolayoutC(obj.name, geolayoutGraph, fModel, dirPath, texDir, - savePNG, texSeparate) + saveGeolayoutC(name, geolayoutGraph, fModel, dirPath, texDir, + savePNG, texSeparate, groupName) def saveGeolayoutC(dirName, geolayoutGraph, fModel, dirPath, texDir, savePNG, - texSeparate): + texSeparate, groupName): + dirName = toAlnum(dirName) + groupName = toAlnum(groupName) geoDirPath = os.path.join(dirPath, toAlnum(dirName)) if not os.path.exists(geoDirPath): @@ -213,6 +215,11 @@ def saveGeolayoutC(dirName, geolayoutGraph, fModel, dirPath, texDir, savePNG, dlFile = open(modelPath, 'w') dlFile.write(dlData) dlFile.close() + + # group.c include + if groupName is not None: + groupPathC = os.path.join(dirPath, groupName + ".c") + writeIfNotFound(groupPathC, '#include "' + dirName + '/model.inc.c"', False) geoPath = os.path.join(geoDirPath, 'geo.inc.c') geoData = geolayoutGraph.to_c() @@ -220,25 +227,35 @@ def saveGeolayoutC(dirName, geolayoutGraph, fModel, dirPath, texDir, savePNG, geoFile.write(geoData) geoFile.close() + # group_geo.c include + if groupName is not None: + groupPathGeoC = os.path.join(dirPath, groupName + "_geo.c") + writeIfNotFound(groupPathGeoC, '#include "' + dirName + '/geo.inc.c"', False) + headerPath = os.path.join(geoDirPath, 'header.h') cDefine = geolayoutGraph.to_c_def() + fModel.to_c_def(True) cDefFile = open(headerPath, 'w') cDefFile.write(cDefine) cDefFile.close() + # group.h declaration + if groupName is not None: + groupPathH = os.path.join(dirPath, groupName + ".h") + writeIfNotFound(groupPathH, '#include "' + dirName + '/header.h"', True) + # Insertable Binary def exportGeolayoutArmatureInsertableBinary(armatureObj, obj, convertTransformMatrix, f3dType, isHWv1, filepath, camera): geolayoutGraph, fModel = convertArmatureToGeolayout(armatureObj, obj, - convertTransformMatrix, f3dType, isHWv1, camera) + convertTransformMatrix, f3dType, isHWv1, camera, armatureObj.name) saveGeolayoutInsertableBinary(geolayoutGraph, fModel, filepath, f3dType) def exportGeolayoutObjectInsertableBinary(obj, convertTransformMatrix, f3dType, isHWv1, filepath, camera): geolayoutGraph, fModel = convertObjectToGeolayout(obj, - convertTransformMatrix, f3dType, isHWv1, camera) + convertTransformMatrix, f3dType, isHWv1, camera, obj.name) saveGeolayoutInsertableBinary(geolayoutGraph, fModel, filepath, f3dType) @@ -259,7 +276,7 @@ def exportGeolayoutArmatureBinaryBank0(romfile, armatureObj, obj, exportRange, f3dType, isHWv1, RAMAddr, camera): geolayoutGraph, fModel = convertArmatureToGeolayout(armatureObj, obj, - convertTransformMatrix, f3dType, isHWv1, camera) + convertTransformMatrix, f3dType, isHWv1, camera, armatureObj.name) return saveGeolayoutBinaryBank0(romfile, fModel, geolayoutGraph, exportRange, levelCommandPos, modelID, textDumpFilePath, RAMAddr) @@ -269,7 +286,7 @@ def exportGeolayoutObjectBinaryBank0(romfile, obj, exportRange, f3dType, isHWv1, RAMAddr, camera): geolayoutGraph, fModel = convertObjectToGeolayout(obj, - convertTransformMatrix, f3dType, isHWv1, camera) + convertTransformMatrix, f3dType, isHWv1, camera, obj.name) return saveGeolayoutBinaryBank0(romfile, fModel, geolayoutGraph, exportRange, levelCommandPos, modelID, textDumpFilePath, RAMAddr) @@ -320,7 +337,7 @@ def exportGeolayoutArmatureBinary(romfile, armatureObj, obj, exportRange, textDumpFilePath, f3dType, isHWv1, camera): geolayoutGraph, fModel = convertArmatureToGeolayout(armatureObj, obj, - convertTransformMatrix, f3dType, isHWv1, camera) + convertTransformMatrix, f3dType, isHWv1, camera, armatureObj.name) return saveGeolayoutBinary(romfile, geolayoutGraph, fModel, exportRange, levelData, levelCommandPos, modelID, textDumpFilePath) @@ -330,7 +347,7 @@ def exportGeolayoutObjectBinary(romfile, obj, exportRange, textDumpFilePath, f3dType, isHWv1, camera): geolayoutGraph, fModel = convertObjectToGeolayout(obj, - convertTransformMatrix, f3dType, isHWv1, camera) + convertTransformMatrix, f3dType, isHWv1, camera, obj.name) return saveGeolayoutBinary(romfile, geolayoutGraph, fModel, exportRange, levelData, levelCommandPos, modelID, textDumpFilePath) diff --git a/fast64_internal/utility.py b/fast64_internal/utility.py index 4e54e88..8e7417a 100644 --- a/fast64_internal/utility.py +++ b/fast64_internal/utility.py @@ -5,6 +5,24 @@ from .sm64_constants import * from .sm64_geolayout_constants import * import random import string +import os + +def writeIfNotFound(filePath, stringValue, hasEndIf): + if os.path.exists(filePath): + fileData = open(filePath, 'r') + fileData.seek(0) + stringData = fileData.read() + fileData.close() + if stringValue not in stringData: + if hasEndIf: + stringData = stringData[:-7] + '\n' + stringValue + '\n\n#endif' + else: + stringData += stringValue + fileData = open(filePath, 'w') + fileData.write(stringData) + fileData.close() + else: + raise ValueError(filePath + " does not exist. Group name may be invalid.") def selectMeshChildrenOnly(obj, ignoreAttr): ignoreObj = ignoreAttr is not None and getattr(obj, ignoreAttr) @@ -153,7 +171,7 @@ def prop_split(layout, data, field, name): split.prop(data, field, text = '') def toAlnum(name): - if name == '': + if name is None or name == '': return None for i in range(len(name)): if not name[i].isalnum():