"""Transfer horizontal root motion to Sequencer actor keys without changing source clips.""" import math import hashlib import unreal from .paths import GENERATED def prepare(clip, child, scene_name): component = child.skeletal_mesh_component bone = component.get_bone_name(0) count = int(clip.get_editor_property("number_of_sampled_keys")) duration = clip.get_play_length() if count < 2 or duration <= 0: raise ValueError("keep_end_position wymaga animacji z co najmniej dwiema probkami.") poses = [unreal.AnimationLibrary.get_bone_pose_for_time( clip, bone, duration * index / (count - 1), False) for index in range(count)] first = poses[0].translation # Extract only horizontal travel. Keep vertical motion (jumps/bobbing), # every root rotation and scale, and all other bone tracks unchanged. positions = [unreal.Vector(first.x, first.y, pose.translation.z) for pose in poses] folder = f"{GENERATED}/{scene_name}/RootMotion" identity = hashlib.sha1(clip.get_path_name().encode("utf-8")).hexdigest()[:8] name = clip.get_name() + "_" + identity + "_InPlace" path = folder + "/" + name if unreal.EditorAssetLibrary.does_asset_exist(path): # This path contains only our generated derivative for this scene. if not unreal.EditorAssetLibrary.delete_asset(path): raise RuntimeError("Nie mozna odswiezyc wygenerowanego klipu " + path) copy = unreal.AssetToolsHelpers.get_asset_tools().duplicate_asset(name, folder, clip) if copy is None: raise RuntimeError("Nie mozna utworzyc kopii animacji dla keep_end_position.") controller = copy.get_editor_property("controller") if not controller.set_bone_track_keys(bone, positions, [pose.rotation for pose in poses], [pose.scale3d for pose in poses], should_transact=False): raise RuntimeError("Nie mozna przeniesc ruchu kosci glownej do Transform.") unreal.EditorAssetLibrary.save_loaded_asset(copy) return copy, poses, duration def displacement(poses, duration, elapsed, reverse=False): """Interpolate horizontal root travel, accumulating complete playback loops.""" elapsed = max(0.0, elapsed) loops = math.floor(elapsed / duration) time = elapsed - loops * duration if reverse: time = duration - time index = min(time / duration * (len(poses) - 1), len(poses) - 1) lower = int(index) upper = min(lower + 1, len(poses) - 1) alpha = index - lower a, b = poses[lower].translation, poses[upper].translation start, end = poses[0].translation, poses[-1].translation if reverse: start, end = end, start return unreal.Vector(a.x + (b.x - a.x) * alpha - start.x + loops * (end.x - start.x), a.y + (b.y - a.y) * alpha - start.y + loops * (end.y - start.y), 0)