Skip to content

Commit

Permalink
Merge pull request #91 from suzuryg/fix/misalignment-in-preview
Browse files Browse the repository at this point in the history
fix: Fix misalignment in expression preview and thumbnail rendering
  • Loading branch information
suzuryg authored Dec 13, 2023
2 parents 6426e65 + f33050d commit 21967b3
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,6 @@ public class AV3Constants
public static readonly string Path_GeneratedDir = $"Assets/Suzuryg/{DomainConstants.SystemName}/Generated";
public static readonly string Path_PrefabDir = $"Assets/Suzuryg/{DomainConstants.SystemName}/Prefabs";

public static readonly string GUID_TPoseClip = "645a7092829eff9478fb3a29f959a6fa";

public static readonly IReadOnlyList<HandGesture> EmoteSelectToGesture = new List<HandGesture>()
{
HandGesture.Neutral,
Expand Down
74 changes: 67 additions & 7 deletions Packages/jp.suzuryg.face-emo/Editor/Detail/AV3/AV3Utility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -346,15 +346,75 @@ public static Animator GetAnimator(AV3Setting aV3Setting)
return aV3Setting.TargetAvatar.GetComponent<Animator>();
}

public static AnimationClip GetAvatarPoseClip()
private static HumanPose _poseCache;
private static AnimationClip _poseClipCache;
public static AnimationClip GetAvatarPoseClip(VRCAvatarDescriptor avatarDescriptor)
{
// TODO: Support for avatar posture other than T-pose
var tPose = AssetDatabase.LoadAssetAtPath<AnimationClip>(AssetDatabase.GUIDToAssetPath(AV3Constants.GUID_TPoseClip));
if (tPose == null)
if (avatarDescriptor == null) { return null; }
var animator = avatarDescriptor.gameObject.GetComponent<Animator>();
if (animator == null || !animator.isHuman) { return null; }

var humanPoseHandler = new HumanPoseHandler(animator.avatar, animator.transform);
var humanPose = new HumanPose();
humanPoseHandler.GetHumanPose(ref humanPose);

if (_poseClipCache == null ||
!ArePosesSimilar(humanPose, _poseCache, float.Epsilon, float.Epsilon, float.Epsilon))
{
_poseCache = humanPose;
_poseClipCache = new AnimationClip() { legacy = false };
SetHumanPoseToClip(ref humanPose, _poseClipCache);
}

return _poseClipCache;
}

private static bool ArePosesSimilar(HumanPose pose1, HumanPose pose2, float positionThreshold, float rotationThreshold, float muscleThreshold)
{
// Compare body position
if (Vector3.Distance(pose1.bodyPosition, pose2.bodyPosition) > positionThreshold)
{
return false;
}

// Compare body rotation
if (Quaternion.Angle(pose1.bodyRotation, pose2.bodyRotation) > rotationThreshold)
{
return false;
}

// Compare muscle values
for (int i = 0; i < pose1.muscles.Length; i++)
{
if (Mathf.Abs(pose1.muscles[i] - pose2.muscles[i]) > muscleThreshold)
{
return false;
}
}

// If none of the values are beyond the thresholds, the poses are considered similar
return true;
}

private static void SetHumanPoseToClip(ref HumanPose humanPose, AnimationClip clip)
{
// Set body position and rotation
clip.SetCurve("", typeof(Animator), "RootT.x", new AnimationCurve(new Keyframe(0, humanPose.bodyPosition.x)));
clip.SetCurve("", typeof(Animator), "RootT.y", new AnimationCurve(new Keyframe(0, humanPose.bodyPosition.y)));
clip.SetCurve("", typeof(Animator), "RootT.z", new AnimationCurve(new Keyframe(0, humanPose.bodyPosition.z)));

clip.SetCurve("", typeof(Animator), "RootQ.x", new AnimationCurve(new Keyframe(0, humanPose.bodyRotation.x)));
clip.SetCurve("", typeof(Animator), "RootQ.y", new AnimationCurve(new Keyframe(0, humanPose.bodyRotation.y)));
clip.SetCurve("", typeof(Animator), "RootQ.z", new AnimationCurve(new Keyframe(0, humanPose.bodyRotation.z)));
clip.SetCurve("", typeof(Animator), "RootQ.w", new AnimationCurve(new Keyframe(0, humanPose.bodyRotation.w)));

// Set muscle values
for (int i = 0; i < humanPose.muscles.Length; i++)
{
Debug.LogError("T-pose animation clip not found.");
string muscleName = HumanTrait.MuscleName[i];
float muscleValue = humanPose.muscles[i];
clip.SetCurve("", typeof(Animator), muscleName, new AnimationCurve(new Keyframe(0, muscleValue)));
}
return tPose;
}

public static AnimationClip SynthesizeClip(AnimationClip baseClip, AnimationClip additionalClip)
Expand All @@ -375,7 +435,7 @@ public static AnimationClip SynthesizeClip(AnimationClip baseClip, AnimationClip
return synthesized;
}

public static AnimationClip SynthesizeAvatarPose(AnimationClip animationClip) => SynthesizeClip(GetAvatarPoseClip(), animationClip);
public static AnimationClip SynthesizeAvatarPose(AnimationClip animationClip, VRCAvatarDescriptor avatarDescriptor) => SynthesizeClip(GetAvatarPoseClip(avatarDescriptor), animationClip);

public static void CombineExpressions(AnimationClip leftHand, AnimationClip rightHand, AnimationClip destination)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ public void StopSampling()
public Vector3 GetAvatarViewPosition()
{
// Returns view position if previewable in T-pose.
if (AV3Utility.GetAvatarPoseClip() != null && (_aV3Setting?.TargetAvatar as VRCAvatarDescriptor)?.ViewPosition != null)
if (AV3Utility.GetAvatarPoseClip(_aV3Setting?.TargetAvatar as VRCAvatarDescriptor) != null && (_aV3Setting?.TargetAvatar as VRCAvatarDescriptor)?.ViewPosition != null)
{
return (_aV3Setting.TargetAvatar as VRCAvatarDescriptor).ViewPosition + new Vector3(PreviewAvatarPosX, PreviewAvatarPosY, PreviewAvatarPosZ);
}
Expand All @@ -199,7 +199,7 @@ public Vector3 GetAvatarHeadPosition()
var animator = clonedAvatar.GetComponent<Animator>();
if (animator != null && animator.isHuman)
{
var clip = AV3Utility.GetAvatarPoseClip();
var clip = AV3Utility.GetAvatarPoseClip(_aV3Setting?.TargetAvatar as VRCAvatarDescriptor);
if (clip == null) { clip = new AnimationClip(); }
AnimationMode.StartAnimationMode();
AnimationMode.BeginSampling();
Expand Down Expand Up @@ -566,7 +566,7 @@ private void RenderPreviewClip()

private void InitializePreviewClip()
{
_previewClip = AV3Utility.SynthesizeAvatarPose(Clip);
_previewClip = AV3Utility.SynthesizeAvatarPose(Clip, _aV3Setting?.TargetAvatar as VRCAvatarDescriptor);
RenderPreviewClip();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ private Texture2D RenderAnimatedAvatar(string clipGUID, GameObject animatorRoot,
}

// Synthesize avatar pose
var synthesized = AV3Utility.SynthesizeAvatarPose(clip);
var synthesized = AV3Utility.SynthesizeAvatarPose(clip, _aV3Setting?.TargetAvatar as VRCAvatarDescriptor);

// Sample animation clip and render
var positionCache = animatorRoot.transform.position;
Expand Down

0 comments on commit 21967b3

Please sign in to comment.