mirror of
https://github.com/ApfelTeeSaft/lightspeed64.git
synced 2026-08-26 19:33:24 +00:00
Updated animation importing to handle animation list pointers. Added pointer converter. Updated README with better import/export instructions.
This commit is contained in:
@@ -101,16 +101,43 @@ Alternatively, for simple static geolayouts you can export object hierarchies. G
|
||||
### Exporting Geolayouts and Skinned Meshes
|
||||
The N64 supports binary skinning, meaning each vertex will be influenced by one bone only. When skinning an exported geolayout, do NOT use automatic skinning, as this results in a smooth weight falloff. Instead, when weight painting set the weight to either 1 or 0, and set the brush Falloff to square. Skinning can only occur between an immediate parent and a child deform bone, not between siblings / across ancestors.
|
||||
|
||||
### Importing/Exporting SM64 Geolayouts
|
||||
Download these documents:
|
||||
|
||||
[SM64MainLevelScripts.txt](http://qubedstudios.rustedlogic.net/SM64MainLevelScripts.txt)
|
||||
|
||||
[SM64GeoLayoutPtrsByLevels.txt](http://qubedstudios.rustedlogic.net/SM64GeoLayoutPtrsByLevels.txt)
|
||||
|
||||
For importing/exporting geolayouts:
|
||||
- In SM64GeoLayoutPtrsByLevels.txt, search the name of the model.
|
||||
- There you can get the modelID (Obj) and geolayout start (ROM Address start + offset)
|
||||
- Note that these are decimal values, and must be converted to hex when used as inputs for fast64.
|
||||
- In SM64MainLevelScripts, search '22 08 00 MM' where MM = modelID.
|
||||
- There may be multiple instances, in which case you must use the offset field from before and check if it matches the last 3 bytes of the line.
|
||||
- There you can get the level command.
|
||||
|
||||
Plug these values into the SM64 Geolayout Exporter/Importer panels.
|
||||
|
||||
### Replacing Existing SM64 Geolayout Geometry
|
||||
SM64 geolayouts are often in strange rest poses, which makes it hard to modify their geometry. It often helps to import an animation belonging to that geolayout to see what the idle pose of a geolayout should be. Once you know, you can rotate the bones of the armature in pose mode to a usable position and then use the 'Apply as Rest Pose' operator under the SM64 Armature Tools header. Skin your new mesh to that armature, then rotate the bones back to the original position and use 'Apply as Rest Pose' again. You can now export the geolayout to SM64 and it will be able to use existing animations.
|
||||
|
||||
### Importing An Animation To Blender
|
||||
To import an animation to blender, select an armature for the animation to be exported to, and press 'Import animation'. Note that the armature's root 0x13 (i.e. regular) bone must be named 'root'. Animations can be found by looking at behaviour scripts where 27 and 28 commands are.
|
||||
### Importing/Exporting SM64 Animations (Not Mario)
|
||||
For importing/exporting animations:
|
||||
- Download Quad64, open the desired level, and go to Misc -> Script Dumps.
|
||||
- Go to the objects header, find the object you want, and view the Behaviour Script tab.
|
||||
- For most models with animation, you can will see a 27 command, and optionally a 28 command.
|
||||
- The last 4 bytes of the 27 command will be the animation list pointer.
|
||||
- Make sure 'Is DMA Animation' is unchecked, 'Is Anim List' is checked, and 'Is Segmented Pointer' is checked.
|
||||
- Set the animation importer start address as those 4 bytes.
|
||||
- If a 28 command exists, then the second byte will be the anim list index.
|
||||
- Otherwise, the anim list index is usually 0.
|
||||
|
||||
### Exporting Mario Animations
|
||||
Select an armature for the animation to be exported to, and press 'Import animation'. Note that the armature's root 0x13 (i.e. regular) bone must be named 'root'.
|
||||
|
||||
### Importing/Exporting Mario Animations
|
||||
Mario animations use a DMA table, which contains 8 byte entries of (offset from table start, animation size). Documentation about this table is here:
|
||||
https://dudaw.webs.com/sm64docs/sm64_marios_animation_table.txt.
|
||||
Basically, Mario's DMA table starts at 0x4EC000. There is an 8 byte header, and then the animation entries afterward. Thus the 'climb up ledge' DMA entry is at 0x4EC008. The first 8 bytes at that address indicate the offset from 0x4EC000 at which the actual animation exists. Using this table you can find animations you want to overwrite.
|
||||
Basically, Mario's DMA table starts at 0x4EC000. There is an 8 byte header, and then the animation entries afterward. Thus the 'climb up ledge' DMA entry is at 0x4EC008. The first 8 bytes at that address indicate the offset from 0x4EC000 at which the actual animation exists. Thus the 'climb up ledge' animation address is at 0x4EC690. Using this table you can find animations you want to overwrite. Make sure the 'Is DMA Animation' option is checked when importing/exporting.
|
||||
|
||||
### Animating Existing Geolayouts
|
||||
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.
|
||||
|
||||
+98
-6
@@ -169,6 +169,40 @@ class N64_AddF3dMat(bpy.types.Operator):
|
||||
self.report({'INFO'}, 'Created F3D material.')
|
||||
return {'FINISHED'} # must return a set
|
||||
|
||||
class SM64_AddrConv(bpy.types.Operator):
|
||||
# set bl_ properties
|
||||
bl_idname = 'object.addr_conv'
|
||||
bl_label = "Convert Address"
|
||||
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
|
||||
|
||||
segToVirt : bpy.props.BoolProperty()
|
||||
|
||||
def execute(self, context):
|
||||
address = int(context.scene.convertibleAddr, 16)
|
||||
try:
|
||||
importRom = context.scene.importRom
|
||||
romfileSrc = open(bpy.path.abspath(importRom), 'rb')
|
||||
checkExpanded(bpy.path.abspath(importRom))
|
||||
levelParsed = parseLevelAtPointer(romfileSrc,
|
||||
level_pointers[context.scene.levelConvert])
|
||||
segmentData = levelParsed.segmentData
|
||||
if self.segToVirt:
|
||||
ptr = decodeSegmentedAddr(
|
||||
address.to_bytes(4, 'big'), segmentData)
|
||||
self.report({'INFO'},
|
||||
'Virtual pointer is 0x' + format(ptr, '08X'))
|
||||
else:
|
||||
ptr = int.from_bytes(
|
||||
encodeSegmentedAddr(address, segmentData), 'big')
|
||||
self.report({'INFO'},
|
||||
'Segmented pointer is 0x' + format(ptr, '08X'))
|
||||
romfileSrc.close()
|
||||
return {'FINISHED'}
|
||||
except:
|
||||
romfileSrc.close()
|
||||
self.report({'ERROR'}, traceback.format_exc())
|
||||
return {'CANCELLED'} # must return a set
|
||||
|
||||
# See SM64GeoLayoutPtrsByLevels.txt by VLTone
|
||||
class SM64_ImportGeolayout(bpy.types.Operator):
|
||||
# set bl_ properties
|
||||
@@ -1013,18 +1047,28 @@ class SM64_ImportAnimMario(bpy.types.Operator):
|
||||
checkExpanded(bpy.path.abspath(context.scene.importRom))
|
||||
romfileSrc = open(bpy.path.abspath(context.scene.importRom), 'rb')
|
||||
try:
|
||||
# Note actual level doesn't matter for Mario, since he is in all of them
|
||||
#levelParsed = parseLevelAtPointer(romfileSrc, level_pointers['CC'])
|
||||
#segmentData = levelParsed.segmentData
|
||||
levelParsed = parseLevelAtPointer(romfileSrc,
|
||||
level_pointers[context.scene.levelAnimImport])
|
||||
segmentData = levelParsed.segmentData
|
||||
|
||||
animStart = int(context.scene.animStartImport, 16)
|
||||
if context.scene.animIsSegPtr:
|
||||
animStart = decodeSegmentedAddr(
|
||||
animStart.to_bytes(4, 'big'), segmentData)
|
||||
|
||||
if context.scene.animIsAnimList:
|
||||
romfileSrc.seek(animStart + 4 * context.scene.animListIndex)
|
||||
actualPtr = romfileSrc.read(4)
|
||||
animStart = decodeSegmentedAddr(actualPtr, segmentData)
|
||||
|
||||
if len(context.selected_objects) == 0:
|
||||
raise ValueError("Armature not selected.")
|
||||
armatureObj = context.active_object
|
||||
if type(armatureObj.data) is not bpy.types.Armature:
|
||||
raise ValueError("Armature not selected.")
|
||||
levelParsed = parseLevelAtPointer(romfileSrc, level_pointers [context.scene.levelAnimImport])
|
||||
segmentData = levelParsed.segmentData
|
||||
|
||||
importAnimationToBlender(romfileSrc,
|
||||
int(context.scene.animStartImport, 16), armatureObj,
|
||||
animStart, armatureObj,
|
||||
segmentData, context.scene.isDMAImport)
|
||||
romfileSrc.close()
|
||||
self.report({'INFO'}, 'Success!')
|
||||
@@ -1051,7 +1095,14 @@ class SM64_ImportAnimPanel(bpy.types.Panel):
|
||||
col = self.layout.column()
|
||||
propsAnimImport = col.operator(SM64_ImportAnimMario.bl_idname)
|
||||
col.prop(context.scene, 'isDMAImport')
|
||||
if not context.scene.isDMAImport:
|
||||
col.prop(context.scene, 'animIsAnimList')
|
||||
if context.scene.animIsAnimList:
|
||||
prop_split(col, context.scene, 'animListIndex',
|
||||
'Anim List Index')
|
||||
|
||||
prop_split(col, context.scene, 'animStartImport', 'Start Address')
|
||||
col.prop(context.scene, 'animIsSegPtr')
|
||||
col.prop(context.scene, 'levelAnimImport')
|
||||
|
||||
for i in range(panelSeparatorSize):
|
||||
@@ -1375,14 +1426,39 @@ class SM64_FileSettingsPanel(bpy.types.Panel):
|
||||
col.prop(context.scene, 'extendBank4')
|
||||
col.prop(context.scene, 'decomp_compatible')
|
||||
|
||||
class SM64_AddressConvertPanel(bpy.types.Panel):
|
||||
bl_idname = "SM64_PT_addr_conv"
|
||||
bl_label = "SM64 Address Converter"
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = 'Fast64'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
|
||||
# called every frame
|
||||
def draw(self, context):
|
||||
col = self.layout.column()
|
||||
segToVirtOp = col.operator(SM64_AddrConv.bl_idname,
|
||||
text = "Convert Segmented To Virtual")
|
||||
segToVirtOp.segToVirt = True
|
||||
virtToSegOp = col.operator(SM64_AddrConv.bl_idname,
|
||||
text = "Convert Virtual To Segmented")
|
||||
virtToSegOp.segToVirt = False
|
||||
prop_split(col, context.scene, 'convertibleAddr', 'Address')
|
||||
col.prop(context.scene, 'levelConvert')
|
||||
|
||||
classes = (
|
||||
ArmatureApplyWithMesh,
|
||||
AddBoneGroups,
|
||||
CreateMetarig,
|
||||
N64_AddF3dMat,
|
||||
SM64_AddrConv,
|
||||
|
||||
F3D_GlobalSettingsPanel,
|
||||
SM64_FileSettingsPanel,
|
||||
SM64_AddressConvertPanel,
|
||||
#SM64_ImportCharacterPanel,
|
||||
#SM64_ExportCharacterPanel,
|
||||
SM64_ImportGeolayoutPanel,
|
||||
@@ -1567,6 +1643,13 @@ def register():
|
||||
name = 'Overwrite DMA Entry')
|
||||
bpy.types.Scene.animInsertableBinaryPath = bpy.props.StringProperty(
|
||||
name = 'Filepath', subtype = 'FILE_PATH')
|
||||
bpy.types.Scene.animIsSegPtr = bpy.props.BoolProperty(
|
||||
name = 'Is Segmented Pointer', default = False)
|
||||
bpy.types.Scene.animIsAnimList = bpy.props.BoolProperty(
|
||||
name = 'Is Anim List', default = True)
|
||||
bpy.types.Scene.animListIndex = bpy.props.IntProperty(
|
||||
name = 'Anim List Index', min = 0)
|
||||
|
||||
|
||||
# Collision
|
||||
bpy.types.Scene.colExportPath = bpy.props.StringProperty(
|
||||
@@ -1602,6 +1685,10 @@ def register():
|
||||
hex(defaultExtendSegment4[1]) + ') and copies data from old bank')
|
||||
bpy.types.Scene.decomp_compatible = bpy.props.BoolProperty(
|
||||
name = 'Decomp Compatibility', default = True)
|
||||
bpy.types.Scene.convertibleAddr = bpy.props.StringProperty(
|
||||
name = 'Address')
|
||||
bpy.types.Scene.levelConvert = bpy.props.EnumProperty(
|
||||
items = level_enums, name = 'Level', default = 'IC')
|
||||
|
||||
bpy.types.Scene.characterIgnoreSwitch = \
|
||||
bpy.props.BoolProperty(name = 'Ignore Switch Nodes', default = True)
|
||||
@@ -1655,6 +1742,9 @@ def unregister():
|
||||
del bpy.types.Scene.animExportPath
|
||||
del bpy.types.Scene.animOverwriteDMAEntry
|
||||
del bpy.types.Scene.animInsertableBinaryPath
|
||||
del bpy.types.Scene.animIsSegPtr
|
||||
del bpy.types.Scene.animIsAnimList
|
||||
del bpy.types.Scene.animListIndex
|
||||
|
||||
# Character
|
||||
del bpy.types.Scene.characterIgnoreSwitch
|
||||
@@ -1711,6 +1801,8 @@ def unregister():
|
||||
del bpy.types.Scene.exportRom
|
||||
del bpy.types.Scene.outputRom
|
||||
del bpy.types.Scene.extendBank4
|
||||
del bpy.types.Scene.convertibleAddr
|
||||
del bpy.types.Scene.levelConvert
|
||||
|
||||
mat_unregister()
|
||||
bone_unregister()
|
||||
|
||||
Reference in New Issue
Block a user