Skip to content
Merged
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
2 changes: 1 addition & 1 deletion addon_updater_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def make_annotations(cls):
if bl_props:
if '__annotations__' not in cls.__dict__:
setattr(cls, '__annotations__', {})
annotations = cls.__dict__['__annotations__']
annotations = getattr(cls, "__annotations__", {})
for k, v in bl_props.items():
annotations[k] = v
delattr(cls, k)
Expand Down
1 change: 1 addition & 0 deletions fast64_internal/f3d/f3d_material_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def unlock_material(self):
"socket_type",
"in_out",
"item_type",
"inferred_structure_type",
"default_input", # poorly documented, what does it do?
)

Expand Down
13 changes: 10 additions & 3 deletions fast64_internal/f3d/flipbook.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,17 +256,24 @@ def ootFlipbookAnimUpdate(self, armatureObj: bpy.types.Object, segment: str, ind
# we use a handler since update functions are not called when a property is animated.
@persistent
def flipbookAnimHandler(dummy):
from ..utility_anim import get_fcurves

if bpy.context.scene.gameEditorMode in {"OOT", "MM"}:
for obj in bpy.data.objects:
if obj.type == "ARMATURE":
# we only want to update texture on keyframed armatures.
# this somewhat mitigates the issue of two skeletons using the same flipbook material.
if obj.animation_data is None or obj.animation_data.action is None:
continue
action = obj.animation_data.action
action_slot = None
if bpy.app.version >= (5, 0, 0):
action_slot = obj.animation_data.action_slot
if action_slot is None:
continue

fcurves = get_fcurves(obj.animation_data.action, action_slot)
if not (
action.fcurves.find("ootLinkTextureAnim.eyes") is None
or action.fcurves.find("ootLinkTextureAnim.mouth") is None
fcurves.find("ootLinkTextureAnim.eyes") is None or fcurves.find("ootLinkTextureAnim.mouth") is None
):
ootFlipbookAnimUpdate(obj.data, obj, "8", obj.ootLinkTextureAnim.eyes)
ootFlipbookAnimUpdate(obj.data, obj, "9", obj.ootLinkTextureAnim.mouth)
Expand Down
17 changes: 14 additions & 3 deletions fast64_internal/sm64/animation/exporting.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
toAlnum,
directory_path_checks,
)
from ...utility_anim import stashActionInArmature
from ...utility_anim import get_fcurves, stashActionInArmature, get_slots

from ..sm64_constants import BEHAVIOR_COMMANDS, BEHAVIOR_EXITS, defaultExtendSegment4, level_pointers
from ..sm64_utility import (
Expand Down Expand Up @@ -99,7 +99,8 @@ def get_entire_fcurve_data(
default_values = list(getattr(anim_owner, prop))
populated = [False] * len(default_values)

for fcurve in action.fcurves:
fcurves = get_fcurves(action, get_action_props(action).get_slot(action))
for fcurve in fcurves:
if fcurve.data_path == data_path:
array_index = fcurve.array_index
for frame in range(max_frame):
Expand Down Expand Up @@ -149,6 +150,9 @@ def to_xyz(row):
def read_full(actions, max_frames, anim_owners, trans_values, rot_values, obj, is_owner_obj):
pre_export_frame = bpy.context.scene.frame_current
pre_export_action = obj.animation_data.action
pre_export_slot = None
if bpy.app.version >= (5, 0, 0):
pre_export_slot = obj.animation_data.action_slot
was_playing = bpy.context.screen.is_animation_playing

try:
Expand All @@ -157,6 +161,11 @@ def read_full(actions, max_frames, anim_owners, trans_values, rot_values, obj, i
for action, action_trans, action_rot, max_frame in zip(actions, trans_values, rot_values, max_frames):
print(f'Reading animation data from action "{action.name}".')
obj.animation_data.action = action
if bpy.app.version >= (5, 0, 0):
slot = get_action_props(action).get_slot(action)
if slot is None:
raise PluginError(f'No action slot found for action "{action.name}"')
obj.animation_data.action_slot = slot
for frame in range(max_frame):
bpy.context.scene.frame_set(frame)

Expand All @@ -173,6 +182,8 @@ def read_full(actions, max_frames, anim_owners, trans_values, rot_values, obj, i
action_rot[index : index + 3, frame] = list(local_matrix.to_euler())
finally:
obj.animation_data.action = pre_export_action
if bpy.app.version >= (5, 0, 0):
obj.animation_data.action_slot = pre_export_slot
bpy.context.scene.frame_set(pre_export_frame)
if was_playing != bpy.context.screen.is_animation_playing:
bpy.ops.screen.animation_play()
Expand Down Expand Up @@ -327,7 +338,7 @@ def to_table_element_class(
return element

# Not reference
header_props, action = element_props.get_header(can_reference), element_props.get_action(can_reference)
action, header_props = element_props.get_action_header(can_reference)
if not action:
raise PluginError("Action is not set.")
if not header_props:
Expand Down
42 changes: 20 additions & 22 deletions fast64_internal/sm64/animation/importing.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from ...f3d.f3d_parser import math_eval
from ...utility import PluginError, decodeSegmentedAddr, filepath_checks, path_checks, intToHex
from ...utility_anim import create_basic_action
from ...utility_anim import create_basic_action, get_fcurves, create_new_fcurve

from ..sm64_constants import AnimInfo, level_pointers
from ..sm64_level_parser import parseLevelAtPointer
Expand All @@ -39,6 +39,9 @@
from .constants import ACTOR_PRESET_INFO, TABLE_ENUM_LIST_PATTERN, TABLE_ENUM_PATTERN, TABLE_PATTERN

if TYPE_CHECKING:
if bpy.app.version >= (5, 0, 0):
from bpy.types import ActionSlot

from .properties import (
SM64_AnimImportProperties,
SM64_ArmatureAnimProperties,
Expand Down Expand Up @@ -75,15 +78,13 @@ def naive_flip_diff(a1: np.ndarray, a2: np.ndarray) -> np.ndarray:
class FramesHolder:
frames: np.ndarray = dataclasses.field(default_factory=list)

def populate_action(self, action: Action, pose_bone: PoseBone, path: str):
for property_index in range(3):
f_curve = action.fcurves.new(
data_path=pose_bone.path_from_id(path),
index=property_index,
action_group=pose_bone.name,
)
def populate_action(self, action: Action, action_slot: "ActionSlot", pose_bone: PoseBone, path: str):
fcurves = get_fcurves(action, action_slot)
for index in range(3):
data_path = pose_bone.path_from_id(path)
f_curve = create_new_fcurve(fcurves, data_path, index=index, action_group=pose_bone.name)
for time, frame in enumerate(self.frames):
f_curve.keyframe_points.insert(time, frame[property_index], options={"FAST"})
f_curve.keyframe_points.insert(time, frame[index], options={"FAST"})


def euler_to_quaternion(euler_angles: np.ndarray):
Expand Down Expand Up @@ -133,7 +134,7 @@ def axis_angle(self):
result.append([x[1]] + list(x[0]))
return result

def populate_action(self, action: Action, pose_bone: PoseBone, path: str = ""):
def populate_action(self, action: Action, action_slot: "ActionSlot", pose_bone: PoseBone, path: str = ""):
rotation_mode = pose_bone.rotation_mode
rotation_mode_name = {
"QUATERNION": "rotation_quaternion",
Expand All @@ -149,14 +150,11 @@ def populate_action(self, action: Action, pose_bone: PoseBone, path: str = ""):
else:
rotations = self.get_euler(rotation_mode)
size = 3
for property_index in range(size):
f_curve = action.fcurves.new(
data_path=data_path,
index=property_index,
action_group=pose_bone.name,
)
fcurves = get_fcurves(action, action_slot)
for index in range(size):
f_curve = create_new_fcurve(fcurves, data_path, index=index, action_group=pose_bone.name)
for frame, rotation in enumerate(rotations):
f_curve.keyframe_points.insert(frame, rotation[property_index], options={"FAST"})
f_curve.keyframe_points.insert(frame, rotation[index], options={"FAST"})


@dataclasses.dataclass
Expand Down Expand Up @@ -201,9 +199,9 @@ def read_rotation(self, pairs: list["SM64_AnimPair"], continuity_filter: bool):
frames = self.continuity_filter(frames)
self.rotation.frames = frames

def populate_action(self, action: Action, pose_bone: PoseBone):
self.translation.populate_action(action, pose_bone, "location")
self.rotation.populate_action(action, pose_bone, "")
def populate_action(self, action: Action, action_slot: "ActionSlot", pose_bone: PoseBone):
self.translation.populate_action(action, action_slot, pose_bone, "location")
self.rotation.populate_action(action, action_slot, pose_bone, "")


def from_header_class(
Expand Down Expand Up @@ -371,7 +369,7 @@ def animation_import_to_blender(
force_quaternion: bool,
continuity_filter: bool,
):
action = create_basic_action(obj, "")
action, action_slot = create_basic_action(obj)
try:
if anim_import.data:
print("Converting pairs to intermidiate data.")
Expand All @@ -388,7 +386,7 @@ def animation_import_to_blender(
for pose_bone, bone_data in zip(bones, bones_data):
if force_quaternion:
pose_bone.rotation_mode = "QUATERNION"
bone_data.populate_action(action, pose_bone)
bone_data.populate_action(action, action_slot, pose_bone)

from_anim_class(get_action_props(action), action, anim_import, actor_name, use_custom_name, import_type)
return action
Expand Down
39 changes: 36 additions & 3 deletions fast64_internal/sm64/animation/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
animation_operator_checks,
check_for_headers_in_table,
get_action_props,
get_active_diff_slot,
get_anim_obj,
get_scene_anim_props,
get_anim_props,
Expand Down Expand Up @@ -43,7 +44,11 @@ def emulate_no_loop(scene: Scene):

frame = scene.frame_current
header_props = get_action_props(played_action).headers[anim_props.played_header]
_start, loop_start, end = header_props.get_loop_points(played_action)
_start, loop_start, end = (
anim_props.played_cached_start,
anim_props.played_cached_loop_start,
anim_props.played_cached_loop_end,
)
if header_props.backwards:
if frame < loop_start:
if header_props.no_loop:
Expand Down Expand Up @@ -72,14 +77,16 @@ def execute_operator(self, context):
played_action = get_action(self.played_action)
scene = context.scene
anim_props = scene.fast64.sm64.animation
action_props = get_action_props(played_action)

context.object.animation_data.action = played_action
action_props = get_action_props(played_action)
if bpy.app.version >= (5, 0, 0):
context.object.animation_data.action_slot = action_props.get_slot(played_action)

if self.played_header >= len(action_props.headers):
raise ValueError("Invalid Header Index")
header_props: SM64_AnimHeaderProperties = action_props.headers[self.played_header]
start_frame = header_props.get_loop_points(played_action)[0]
start_frame, loop_start, end = header_props.get_loop_points(played_action)
scene.frame_set(start_frame)
scene.render.fps = 30

Expand All @@ -89,6 +96,9 @@ def execute_operator(self, context):

anim_props.played_header = self.played_header
anim_props.played_action = played_action
anim_props.played_cached_start = start_frame
anim_props.played_cached_loop_start = loop_start
anim_props.played_cached_loop_end = end


# TODO: update these to use CollectionOperatorBase
Expand Down Expand Up @@ -247,6 +257,28 @@ def execute_operator(self, context):
anim_props.elements[-1].set_variant(action, header_variant)


class SM64_SetActionSlotFromObj(OperatorBase):
bl_idname = "scene.sm64_set_action_slot_from_object"
bl_label = "Set to active slot"
bl_description = "Sets the action slot to the object's active slot"
bl_options = {"REGISTER", "UNDO", "PRESET"}
context_mode = "OBJECT"
icon = "ACTION_SLOT"

action_name: StringProperty(name="Action Name", default="")

@classmethod
def is_enabled(cls, context: Context, action_name: str, **_kwargs):
return get_active_diff_slot(context, get_action(action_name)) is not None

def execute_operator(self, context):
animation_operator_checks(context)
obj = get_anim_obj(context)
action = get_action(self.action_name)
action_props = get_action_props(action)
action_props.slot_identifier = obj.animation_data.action_slot.identifier


class SM64_ExportAnimTable(OperatorBase):
bl_idname = "scene.sm64_export_anim_table"
bl_label = "Export Animation Table"
Expand Down Expand Up @@ -332,6 +364,7 @@ def update_enum(self, context: Context):
SM64_AnimTableOps,
SM64_AnimVariantOps,
SM64_AddNLATracksToTable,
SM64_SetActionSlotFromObj,
SM64_ImportAnim,
SM64_SearchAnimPresets,
SM64_SearchAnimatedBhvs,
Expand Down
Loading