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
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3a8ce55c449664a45a89238c11843bb5
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -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