Add PoseIconStage editor component and Pose Icon scene
Applies a pose clip plus root position/rotation to the staged Paintman for authoring pose icons (screenshot pipeline follows later). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7e9f255de5
commit
083d0b8430
4 files changed
+1131
No files matched your search
@@ -0,0 +1,115 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Authoring tool for the Pose Icon scene: holds a list of pose entries (animation clip + root
|
||||
/// position/rotation) and applies the selected one to the paintman rig in edit mode, for framing
|
||||
/// each pose in front of the camera (icon screenshots are a later step). Clips are sampled via
|
||||
/// AnimationClip.SampleAnimation (no foot IK), matching how PlayerPoseController plays them.
|
||||
/// </summary>
|
||||
[ExecuteAlways]
|
||||
[DisallowMultipleComponent]
|
||||
public class PoseIconStage : MonoBehaviour
|
||||
{
|
||||
[System.Serializable]
|
||||
public class Entry
|
||||
{
|
||||
[Tooltip("Pose clip, sampled at time 0 onto the target rig.")]
|
||||
public AnimationClip clip;
|
||||
|
||||
[Tooltip("World position applied to the target root for this pose.")]
|
||||
public Vector3 position;
|
||||
|
||||
[Tooltip("World rotation (euler angles) applied to the target root for this pose.")]
|
||||
public Vector3 rotation;
|
||||
}
|
||||
|
||||
[Tooltip("Rig the poses are applied to (the paintman). Defaults to the Animator in this object's children.")]
|
||||
[SerializeField] private Animator target;
|
||||
|
||||
[Tooltip("Pose entries: clip + root position/rotation.")]
|
||||
[SerializeField] private List<Entry> entries = new();
|
||||
|
||||
[Tooltip("Index of the entry currently applied. Change to preview another pose.")]
|
||||
[Min(0)]
|
||||
[SerializeField] private int currentIndex;
|
||||
|
||||
private bool _dirty;
|
||||
|
||||
// Rest-pose snapshot of the rig, taken before the first sample this session. A clip only writes
|
||||
// the bones its curves cover, so switching between clips would otherwise leave residue from the
|
||||
// previous pose on un-keyed bones (same reasoning as PlayerPoseController.CaptureRestPose).
|
||||
// Not serialized: reload the scene to get a clean rest pose if it was saved mid-pose.
|
||||
private Transform[] _restBones;
|
||||
private Vector3[] _restPos;
|
||||
private Quaternion[] _restRot;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if (!target)
|
||||
target = GetComponentInChildren<Animator>();
|
||||
_dirty = true;
|
||||
}
|
||||
|
||||
void OnValidate()
|
||||
{
|
||||
// Only flag here; Update applies (coalesced to once per frame, safe object access).
|
||||
_dirty = true;
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.EditorApplication.QueuePlayerLoopUpdate();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (!_dirty)
|
||||
return;
|
||||
_dirty = false;
|
||||
Apply();
|
||||
}
|
||||
|
||||
[ContextMenu("Apply Current Pose")]
|
||||
private void Apply()
|
||||
{
|
||||
if (!target || entries.Count == 0)
|
||||
return;
|
||||
|
||||
Entry entry = entries[Mathf.Clamp(currentIndex, 0, entries.Count - 1)];
|
||||
if (entry == null || !entry.clip)
|
||||
return;
|
||||
|
||||
EnsureRestPose();
|
||||
ResetToRestPose();
|
||||
entry.clip.SampleAnimation(target.gameObject, 0f);
|
||||
|
||||
// Root placement wins over any root curves the clip may carry.
|
||||
target.transform.SetPositionAndRotation(entry.position, Quaternion.Euler(entry.rotation));
|
||||
}
|
||||
|
||||
private void EnsureRestPose()
|
||||
{
|
||||
if (_restBones != null)
|
||||
return;
|
||||
_restBones = target.GetComponentsInChildren<Transform>(true);
|
||||
int n = _restBones.Length;
|
||||
_restPos = new Vector3[n];
|
||||
_restRot = new Quaternion[n];
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
_restPos[i] = _restBones[i].localPosition;
|
||||
_restRot[i] = _restBones[i].localRotation;
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetToRestPose()
|
||||
{
|
||||
for (int i = 0; i < _restBones.Length; i++)
|
||||
{
|
||||
Transform t = _restBones[i];
|
||||
if (!t)
|
||||
continue;
|
||||
t.localPosition = _restPos[i];
|
||||
t.localRotation = _restRot[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf75c420c02c16249939d0a6f6515d01
|
||||
Reference in new issue
Block a user