Skip to content

Instantly share code, notes, and snippets.

@lyuma
Created March 3, 2021 12:58
Show Gist options
  • Save lyuma/523956e04335e87281b8225ce4a4f25e to your computer and use it in GitHub Desktop.
Save lyuma/523956e04335e87281b8225ce4a4f25e to your computer and use it in GitHub Desktop.
Prints out pre- and post- quaternion values from Unity's Humanoid Avatar
// MIT License
// Copyright (c) 2021 Lyuma <[email protected]> and lox9973
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEditor;
public class VRMChecker : MonoBehaviour
{
static string tos(int f) {
return (f < 0 ? "-" : "+") + (f < 0 ? -f : f);
}
static string tos(float f) {
return (f < 0.0f ? "-" : "+") + string.Format("{0,1:#0.00000}", Mathf.Abs(f));
}
static string tos(Quaternion q) {
return "[" + tos(q.x) + "," + tos(q.y) + "," + tos(q.z) + "," + tos(q.w) + "]";
}
static string tos(Vector3Int v) {
return "[" + tos(v.x) + "," + tos(v.y) + "," + tos(v.z) + "]";
}
static string tos(Vector3 v) {
return "[" + tos(v.x) + "," + tos(v.y) + "," + tos(v.z) + "]";
}
[MenuItem("Tools/Lyuma Test: Orient Along Unity Bones")]
static void OrientAlongUnityBones() {
var selectedObject = Selection.activeGameObject;
if (selectedObject == null || selectedObject.GetComponent<Animator>() == null || !selectedObject.GetComponent<Animator>().isHuman) {
Debug.LogError("Please select an a object with a humanoid Animator component");
return;
}
var anim = selectedObject.GetComponent<Animator>();
var avatar = anim.avatar;
string s = "[ // " + anim.gameObject.name + "\n";
for (int i = 0; i < HumanTrait.BoneCount; i++) {
HumanBodyBones humanBone = (HumanBodyBones)i;
Transform bone = anim.GetBoneTransform(humanBone);
if (bone == null) {
continue;
}
// tip == bone.position + bone.rotation * postQ * Vector3.right * limit.axisLength
Quaternion postQ = (Quaternion)GetPostRotation.Invoke(avatar, new object[]{humanBone});
// bone.localRotation * postQ == preQ * SwingTwist(sign * degrees)
Quaternion preQ = (Quaternion)GetPreRotation.Invoke(avatar, new object[]{humanBone});
// abs(sign) != 1 is possible for custom limit range
Vector3 sign = GetLimitSignFull(avatar, humanBone);
s += ("[" + tos(preQ) + "," +
"" + tos(postQ) + "," +
"" + tos(Vector3Int.RoundToInt(sign)) + "], // Bone " + i + ": " + humanBone.ToString() + "\n");
}
s += "]; // preQ, postQ, axisSign\n";
Debug.Log(s);
}
// Following code is by lox9973, from:
// https://gitlab.com/lox9973/shadermotion/-/blob/master/Script/Common/HumanAxes.cs
static Vector3 GetLimitSignFull(Avatar avatar, HumanBodyBones humanBone) {
var sign = Vector3.zero;
for(var b = humanBone; (int)b >= 0; ) {
var s = (Vector3)GetLimitSign.Invoke(avatar, new object[]{b});
for(int i=0; i<3; i++)
if(HumanTrait.MuscleFromBone((int)b, i) < 0)
s[i] = 0;
else if(sign[i] == 0)
sign[i] = s[i];
if(s.x*s.y*s.z != 0) {
for(int i=0; i<3 && (sign.x*sign.y*sign.z) != (s.x*s.y*s.z); i++)
if(HumanTrait.MuscleFromBone((int)humanBone, i) < 0)
sign[i] *= -1;
return sign;
}
b = b == HumanBodyBones.LeftShoulder ? HumanBodyBones.LeftUpperArm :
b == HumanBodyBones.RightShoulder ? HumanBodyBones.RightUpperArm :
(HumanBodyBones)HumanTrait.GetParentBone((int)b);
}
return new Vector3(1, 1, 1);
}
static readonly MethodInfo GetPreRotation = typeof(Avatar).GetMethod("GetPreRotation", BindingFlags.NonPublic | BindingFlags.Instance);
static readonly MethodInfo GetPostRotation = typeof(Avatar).GetMethod("GetPostRotation", BindingFlags.NonPublic | BindingFlags.Instance);
static readonly MethodInfo GetLimitSign = typeof(Avatar).GetMethod("GetLimitSign", BindingFlags.NonPublic | BindingFlags.Instance);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment