"""Real UE check: both Mixamo skeletons, animation-only FBXs and Sequencer poses.""" import json import unreal from ue_workshop import Scene, Sequence, camera, lighting, Renderer from ue_workshop.paths import PROJECT scene = Scene("SkeletalSmoke") base = PROJECT / "source_assets/character" characters = [] for name, model, walk, idle, attack, position in ( ("FighterSkeletalSmoke", "fighter/Maria WProp J J Ong.fbx", "fighter/great sword walk.fbx", "fighter/great sword idle.fbx", "fighter/great sword attack.fbx", (-160, 0, 0)), ("CreatureSkeletalSmoke", "creature/Maw J Laygo.fbx", "creature/mutant walking.fbx", "creature/mutant idle.fbx", "creature/mutant swiping.fbx", (160, 0, 0)), ): actor = scene.skeletal_model(base / model, name=name, location=position, animations={"walk": base / walk, "idle": base / idle, "attack": base / attack}) child, imported = scene._skeletal_models[actor.get_path_name()] assert isinstance(child, unreal.SkeletalMeshActor) assert isinstance(imported.mesh, unreal.SkeletalMesh) for alias in ("walk", "idle", "attack"): clip = imported.animations[alias] assert clip.get_editor_property("skeleton") == imported.mesh.get_editor_property("skeleton") assert clip.get_editor_property("sequence_length") > 0 if alias == "walk": tracks = unreal.AnimationLibrary.get_animation_track_names(clip) assert tracks, "Walk has no bone tracks" poses_differ = False for bone in tracks: first = unreal.AnimationLibrary.get_bone_pose_for_frame(clip, bone, 0, False) later = unreal.AnimationLibrary.get_bone_pose_for_frame(clip, bone, 10, False) if not first.rotation.equals(later.rotation, 0.001): poses_differ = True break assert poses_differ, "Walk has no changing bone rotations" characters.append(actor) if name == "FighterSkeletalSmoke": # Bundled sources are FBX 6.1, too old for Blender's FBX importer. # Export modern FBXs for the Blender round-trip fixture. folder = PROJECT / "build/SkeletalFixture" folder.mkdir(parents=True, exist_ok=True) for obj, filename in ((imported.mesh, "fighter.fbx"), (imported.animations["walk"], "walk.fbx")): task = unreal.AssetExportTask() task.set_editor_property("object", obj) task.set_editor_property("filename", str(folder / filename)) task.set_editor_property("automated", True) task.set_editor_property("prompt", False) task.set_editor_property("replace_identical", True) task.set_editor_property("options", unreal.FbxExportOption()) assert unreal.Exporter.run_asset_export_task(task) scene.primitive("Cube", name="Floor", location=(0, 0, -10), scale=(20, 20, 0.2)) lighting.studio(scene) cam = camera.create(scene, location=(600, -850, 400), target=(0, 0, 100), fov=55) shot = Sequence(scene, frames=48) shot.camera(cam) for actor in characters: first = shot.animation(actor, "walk", start=0, end=16) second = shot.animation(actor, "idle", start=16, end=32, speed=0.75) shot.animation(actor, "attack", start=32, end=48) assert first.get_editor_property("params").animation is not None assert abs(second.get_editor_property("params").play_rate.to_fixed_play_rate() - 0.75) < 1e-6 shot.transform_key(actor, 0, rotation=(0, 0, 0)) shot.transform_key(actor, 47, rotation=(0, 0, 90)) shot.save(open_editor=False) summary = {"characters": [actor.get_actor_label() for actor in characters], "frames": shot.frames} (PROJECT / "Saved/Workshop/skeletal_smoke.json").write_text(json.dumps(summary), encoding="utf-8") renderer = Renderer(width=640, height=360, samples=1, warmup=0, mp4=False)