70 lines
3.8 KiB
Python
70 lines
3.8 KiB
Python
"""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)
|