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

[SM64] Animations Rewrite/Rework #467

Open
wants to merge 17 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
14 changes: 12 additions & 2 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
repo_settings_operators_unregister,
)

from .fast64_internal.sm64 import sm64_register, sm64_unregister
from .fast64_internal.sm64 import sm64_register, sm64_unregister, SM64_ActionProperty
from .fast64_internal.sm64.sm64_constants import sm64_world_defaults
from .fast64_internal.sm64.settings.properties import SM64_Properties
from .fast64_internal.sm64.sm64_geolayout_bone import SM64_BoneProperties
Expand Down Expand Up @@ -240,6 +240,14 @@ class Fast64_Properties(bpy.types.PropertyGroup):
renderSettings: bpy.props.PointerProperty(type=Fast64RenderSettings_Properties, name="Fast64 Render Settings")


class Fast64_ActionProperties(bpy.types.PropertyGroup):
"""
Properties in Action.fast64.
"""

sm64: bpy.props.PointerProperty(type=SM64_ActionProperty, name="SM64 Properties")


class Fast64_BoneProperties(bpy.types.PropertyGroup):
"""
Properties in bone.fast64 (bpy.types.Bone)
Expand Down Expand Up @@ -327,6 +335,7 @@ def draw(self, context):
Fast64RenderSettings_Properties,
ManualUpdatePreviewOperator,
Fast64_Properties,
Fast64_ActionProperties,
Fast64_BoneProperties,
Fast64_ObjectProperties,
F3D_GlobalSettingsPanel,
Expand Down Expand Up @@ -470,7 +479,7 @@ def register():
bpy.types.Scene.fast64 = bpy.props.PointerProperty(type=Fast64_Properties, name="Fast64 Properties")
bpy.types.Bone.fast64 = bpy.props.PointerProperty(type=Fast64_BoneProperties, name="Fast64 Bone Properties")
bpy.types.Object.fast64 = bpy.props.PointerProperty(type=Fast64_ObjectProperties, name="Fast64 Object Properties")

bpy.types.Action.fast64 = bpy.props.PointerProperty(type=Fast64_ActionProperties, name="Fast64 Action Properties")
bpy.app.handlers.load_post.append(after_load)


Expand Down Expand Up @@ -499,6 +508,7 @@ def unregister():
del bpy.types.Scene.fast64
del bpy.types.Bone.fast64
del bpy.types.Object.fast64
del bpy.types.Action.fast64

repo_settings_operators_unregister()

Expand Down
64 changes: 59 additions & 5 deletions fast64_internal/operators.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
import bpy, mathutils, math
from bpy.types import Operator, Context, UILayout
from cProfile import Profile
from pstats import SortKey, Stats
from typing import Optional

import bpy, mathutils
from bpy.types import Operator, Context, UILayout, EnumProperty
from bpy.utils import register_class, unregister_class
from .utility import *
from .f3d.f3d_material import *

from .utility import (
cleanupTempMeshes,
get_mode_set_from_context_mode,
raisePluginError,
parentObject,
store_original_meshes,
store_original_mtx,
)
from .f3d.f3d_material import createF3DMat


def addMaterialByName(obj, matName, preset):
Expand All @@ -14,20 +26,29 @@ def addMaterialByName(obj, matName, preset):
material.name = matName


PROFILE_ENABLED = False


class OperatorBase(Operator):
"""Base class for operators, keeps track of context mode and sets it back after running
execute_operator() and catches exceptions for raisePluginError()"""

context_mode: str = ""
icon = "NONE"

@classmethod
def is_enabled(cls, context: Context, **op_values):
return True

@classmethod
def draw_props(cls, layout: UILayout, icon="", text: Optional[str] = None, **op_values):
"""Op args are passed to the operator via setattr()"""
icon = icon if icon else cls.icon
layout = layout.column()
op = layout.operator(cls.bl_idname, icon=icon, text=text)
for key, value in op_values.items():
setattr(op, key, value)
layout.enabled = cls.is_enabled(bpy.context, **op_values)
return op

def execute_operator(self, context: Context):
Expand All @@ -40,7 +61,12 @@ def execute(self, context: Context):
try:
if self.context_mode and self.context_mode != starting_mode_set:
bpy.ops.object.mode_set(mode=self.context_mode)
self.execute_operator(context)
if PROFILE_ENABLED:
with Profile() as profile:
self.execute_operator(context)
print(Stats(profile).strip_dirs().sort_stats(SortKey.CUMULATIVE).print_stats())
else:
self.execute_operator(context)
return {"FINISHED"}
except Exception as exc:
raisePluginError(self, exc)
Expand All @@ -53,6 +79,34 @@ def execute(self, context: Context):
bpy.ops.object.mode_set(mode=starting_mode_set)


class SearchEnumOperatorBase(OperatorBase):
bl_description = "Search Enum"
bl_label = "Search"
bl_property = None
bl_options = {"UNDO"}

@classmethod
def draw_props(cls, layout: UILayout, data, prop: str, name: str):
row = layout.row()
if name:
row.label(text=name)
row.prop(data, prop, text="")
row.operator(cls.bl_idname, icon="VIEWZOOM", text="")

def update_enum(self, context: Context):
raise NotImplementedError()

def execute_operator(self, context: Context):
assert self.bl_property
self.report({"INFO"}, f"Selected: {getattr(self, self.bl_property)}")
self.update_enum(context)
context.region.tag_redraw()

def invoke(self, context: Context, _):
context.window_manager.invoke_search_popup(self)
return {"RUNNING_MODAL"}


class AddWaterBox(OperatorBase):
bl_idname = "object.add_water_box"
bl_label = "Add Water Box"
Expand Down
35 changes: 3 additions & 32 deletions fast64_internal/sm64/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,38 +52,7 @@ For example, for Mario you would rotate the four limb joints around the Y-axis 1

Then after applying the rest pose and skinning, you would apply those operations in reverse order then apply rest pose again.

### Importing/Exporting Binary SM64 Animations (Not Mario)
- Note: SM64 animations only allow for rotations, and translation only on the root bone.

- 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.

For importing:

- 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.

For exporting:

- Make sure 'Set Anim List Entry' is checked.
- Copy the addresses of the 27 command, which is the first number before the slash on that line.
- Optionally do the same for the 28 command, which may not exist.
- If a 28 command exists, then the second byte will be the anim list index.
- Otherwise, the anim list index is usually 0.

Select an armature for the animation, and press 'Import/Export animation'.

### Importing/Exporting Binary 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 4 bytes at that address indicate the offset from 0x4EC000 at which the actual animation exists. Thus the 'climb up ledge' animation entry address is at 0x4EC690. Using this table you can find animations you want to overwrite. Make sure the 'Is DMA Animation' option is checked and 'Is Segmented Pointer' is unchecked when importing/exporting. Check "Overwrite DMA Entry", set the start address to 4EC000 (for Mario), and set the entry address to the DMA entry obtained previously.

### 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.
### TODO: Link to animations docs

## Decomp
To start, set your base decomp folder in SM64 General Settings. This allows the plugin to automatically add headers/includes to the correct locations. You can always choose to export to a custom location, although headers/includes won't be written.
Expand Down Expand Up @@ -165,6 +134,8 @@ Insertable Binary exporting will generate a binary file, with a header containin
1 = Geolayout
2 = Animation
3 = Collision
4 = Animation Table
5 = Animation DMA Table

0x04-0x08 : Data Size (size in bytes of Data Section)
0x08-0x0C : Start Address (start address of data, relative to start of Data Section)
Expand Down
33 changes: 24 additions & 9 deletions fast64_internal/sm64/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from bpy.types import PropertyGroup
from bpy.props import PointerProperty
from bpy.utils import register_class, unregister_class

from .settings import (
settings_props_register,
settings_props_unregister,
Expand Down Expand Up @@ -83,14 +87,23 @@
sm64_dl_writer_unregister,
)

from .sm64_anim import (
sm64_anim_panel_register,
sm64_anim_panel_unregister,
sm64_anim_register,
sm64_anim_unregister,
from .animation import (
anim_panel_register,
anim_panel_unregister,
anim_register,
anim_unregister,
SM64_ActionAnimProperty,
)


class SM64_ActionProperty(PropertyGroup):
"""
Properties in Action.fast64.sm64.
"""

animation: PointerProperty(type=SM64_ActionAnimProperty, name="SM64 Properties")


def sm64_panel_register():
settings_panels_register()
tools_panels_register()
Expand All @@ -103,7 +116,7 @@ def sm64_panel_register():
sm64_spline_panel_register()
sm64_dl_writer_panel_register()
sm64_dl_parser_panel_register()
sm64_anim_panel_register()
anim_panel_register()


def sm64_panel_unregister():
Expand All @@ -118,12 +131,13 @@ def sm64_panel_unregister():
sm64_spline_panel_unregister()
sm64_dl_writer_panel_unregister()
sm64_dl_parser_panel_unregister()
sm64_anim_panel_unregister()
anim_panel_unregister()


def sm64_register(register_panels: bool):
tools_operators_register()
tools_props_register()
anim_register()
sm64_col_register()
sm64_bone_register()
sm64_cam_register()
Expand All @@ -134,8 +148,8 @@ def sm64_register(register_panels: bool):
sm64_spline_register()
sm64_dl_writer_register()
sm64_dl_parser_register()
sm64_anim_register()
settings_props_register()
register_class(SM64_ActionProperty)

if register_panels:
sm64_panel_register()
Expand All @@ -144,6 +158,7 @@ def sm64_register(register_panels: bool):
def sm64_unregister(unregister_panels: bool):
tools_operators_unregister()
tools_props_unregister()
anim_unregister()
sm64_col_unregister()
sm64_bone_unregister()
sm64_cam_unregister()
Expand All @@ -154,8 +169,8 @@ def sm64_unregister(unregister_panels: bool):
sm64_spline_unregister()
sm64_dl_writer_unregister()
sm64_dl_parser_unregister()
sm64_anim_unregister()
settings_props_unregister()
unregister_class(SM64_ActionProperty)

if unregister_panels:
sm64_panel_unregister()
15 changes: 15 additions & 0 deletions fast64_internal/sm64/animation/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from .operators import anim_ops_register, anim_ops_unregister
from .properties import anim_props_register, anim_props_unregister, SM64_ArmatureAnimProperties, SM64_ActionAnimProperty
from .panels import anim_panel_register, anim_panel_unregister
from .exporting import export_animation, export_animation_table
from .utility import get_anim_obj, is_obj_animatable


def anim_register():
anim_ops_register()
anim_props_register()


def anim_unregister():
anim_ops_unregister()
anim_props_unregister()
Loading
Loading