您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

519 行
23 KiB

using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using UniGLTF.Utils;
using UnityEngine;
/// <summary>
/// * 胴体: hips, spine, chest, upperChest, neck, head => 6
/// * 腕: shoulder, upperArm, lowerArm, hand => 4 x 2
/// * 脚: upperLeg, lowerLeg, foot, toes => 4 x 2
/// = 22
/// * 指: proximal, inetermediate, distal => 3 x 5 x 2 = 30
/// </summary>
namespace VrmLib
{
public struct HumanoidHead
{
// required
public Node Head;
public Node Jaw;
public Node LeftEye;
public Node RightEye;
public bool HasRequiredBones => Head != null;
};
public struct HumanoidArm
{
public Node Shoulder;
// required
public Node Upper;
// required
public Node Lower;
// required
public Node Hand;
public bool HasRequiredBones => Upper != null && Lower != null && Hand != null;
public Vector3 Direction
{
get
{
if (Shoulder != null)
{
return Vector3.Normalize(Hand.Translation - Shoulder.Translation);
}
else
{
return Vector3.Normalize(Hand.Translation - Upper.Translation);
}
}
}
public void DirectTo(Vector3 dir)
{
if (Shoulder != null)
{
Shoulder.RotateFromTo(Upper.Translation - Shoulder.Translation, dir);
}
Upper.RotateFromTo(Lower.Translation - Upper.Translation, dir);
Lower.RotateFromTo(Hand.Translation - Lower.Translation, dir);
}
};
public struct HumanoidLeg
{
public Node Upper;
public Node Lower;
public Node Foot;
public Node Toe;
};
struct HumanoidFinger
{
public Node Proximal;
public Node Intermediate;
public Node Distal;
}
struct HumanoidThumb
{
public Node Metacarpal;
public Node Proximal;
public Node Distal;
}
struct HumanoidFingers
{
public HumanoidThumb Thumb;
public HumanoidFinger Index;
public HumanoidFinger Middle;
public HumanoidFinger Ring;
public HumanoidFinger Little;
};
/// <summary>
/// ヒューマノイドの姿勢を制御する
/// </summary>
public class Humanoid : IDictionary<HumanoidBones, Node>
{
public Node Hips;
public Node Spine;
public Node Chest;
public Node UpperChest;
public Node Neck;
HumanoidHead Head;
HumanoidArm LeftArm;
HumanoidFingers LeftFingers;
HumanoidArm RightArm;
HumanoidFingers RightFingers;
HumanoidLeg LeftLeg;
HumanoidLeg RightLeg;
public bool HasRequiredBones
{
get
{
if (Hips == null) return false;
if (Spine == null) return false;
if (!Head.HasRequiredBones) return false;
if (!LeftArm.HasRequiredBones) return false;
if (!RightArm.HasRequiredBones) return false;
// TODO
return true;
}
}
ICollection<HumanoidBones> IDictionary<HumanoidBones, Node>.Keys => throw new System.NotImplementedException();
ICollection<Node> IDictionary<HumanoidBones, Node>.Values => throw new System.NotImplementedException();
int ICollection<KeyValuePair<HumanoidBones, Node>>.Count => this.Select(_ => _).Count();
bool ICollection<KeyValuePair<HumanoidBones, Node>>.IsReadOnly => throw new System.NotImplementedException();
Node IDictionary<HumanoidBones, Node>.this[HumanoidBones key] { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); }
public Humanoid()
{
}
public Humanoid(Node root)
{
Assign(root);
}
public void Assign(Node root)
{
foreach (var node in root.Traverse())
{
if (node.HumanoidBone.HasValue)
{
Add(node.HumanoidBone.Value, node);
}
}
}
void CopyTraverse(Node src, Node dst)
{
dst.HumanoidBone = src.HumanoidBone;
dst.LocalScaling = src.LocalScaling;
dst.LocalRotation = src.LocalRotation;
dst.LocalTranslation = src.LocalTranslation;
foreach (var child in src.Children)
{
var dstChild = new Node(child.Name /*+ ".copy"*/);
dst.Add(dstChild);
CopyTraverse(child, dstChild);
}
}
public Humanoid CopyNodes()
{
// ヒエラルキーのコピーを作成する
var hips = new Node(Hips.Name /*+ ".copy"*/);
CopyTraverse(Hips, hips);
var humanoid = new Humanoid(hips);
return humanoid;
}
// Y軸180度回転
public (bool, string) Y180()
{
var sb = new System.Text.StringBuilder();
Hips.LocalRotation = Quaternion.Euler(0, MathFWrap.PI, 0);
Hips.CalcWorldMatrix();
return (true, sb.ToString());
}
/// <summary>
/// 上半身のTPose
/// </summary>
public (bool, string) MakeTPose()
{
var sb = new System.Text.StringBuilder();
bool modified = false;
// hipsのforward を -Z に向ける
// hipsのforward は (left.leg - right.leg) cross (0, 1, 0)
var left = Vector3.Normalize(LeftLeg.Upper.Translation - RightLeg.Upper.Translation);
var forward = Vector3.Cross(left, Vector3.up);
if (Vector3.Dot(forward, -Vector3.forward) < 1.0f - 0.1f)
{
Hips.RotateFromTo(forward, -Vector3.forward);
modified = true;
}
if (Vector3.Dot(LeftArm.Direction, -Vector3.right) < 1.0f - 0.1f)
{
LeftArm.DirectTo(-Vector3.right);
sb.Append("(fix left arm)");
modified = true;
}
if (Vector3.Dot(RightArm.Direction, Vector3.right) < 1.0f - 0.1f)
{
RightArm.DirectTo(Vector3.right);
sb.Append("(fix right arm)");
modified = true;
}
Hips.CalcWorldMatrix();
return (modified, sb.ToString());
}
public void RetargetTo(Humanoid srcTPose, Humanoid dst)
{
foreach (var kv in this)
{
var tposeNode = srcTPose[kv.Key];
if (dst.TryGetValue(kv.Key, out Node node))
{
// var t = tposeNode.LocalRotation;
// var t = Quaternion.identity;
// node.LocalRotationWithoutUpdate = Quaternion.Inverse(t) * kv.Value.LocalRotation;
// node.LocalRotationWithoutUpdate = kv.Value.LocalRotation * Quaternion.Inverse(t);
var t = tposeNode.Rotation;
// node.Rotation = Quaternion.Inverse(t) * kv.Value.Rotation;
node.Rotation = kv.Value.Rotation * Quaternion.Inverse(t);
// node.LocalRotationWithoutUpdate = kv.Value.LocalRotation * Quaternion.Inverse(t);
}
else
{
Console.WriteLine($"{kv.Key} not found");
}
}
dst.Hips.CalcWorldMatrix();
}
#region interface
public void Add(KeyValuePair<HumanoidBones, Node> item)
{
Add(item.Key, item.Value);
}
public void Add(HumanoidBones key, Node node)
{
if (key == HumanoidBones.unknown)
{
throw new ArgumentException();
}
node.HumanoidBone = key;
switch (node.HumanoidBone.Value)
{
case HumanoidBones.hips: Hips = node; break;
case HumanoidBones.spine: Spine = node; break;
case HumanoidBones.chest: Chest = node; break;
case HumanoidBones.upperChest: UpperChest = node; break;
case HumanoidBones.neck: Neck = node; break;
case HumanoidBones.head: Head.Head = node; break;
case HumanoidBones.jaw: Head.Jaw = node; break;
case HumanoidBones.leftEye: Head.LeftEye = node; break;
case HumanoidBones.rightEye: Head.RightEye = node; break;
case HumanoidBones.leftShoulder: LeftArm.Shoulder = node; break;
case HumanoidBones.leftUpperArm: LeftArm.Upper = node; break;
case HumanoidBones.leftLowerArm: LeftArm.Lower = node; break;
case HumanoidBones.leftHand: LeftArm.Hand = node; break;
case HumanoidBones.rightShoulder: RightArm.Shoulder = node; break;
case HumanoidBones.rightUpperArm: RightArm.Upper = node; break;
case HumanoidBones.rightLowerArm: RightArm.Lower = node; break;
case HumanoidBones.rightHand: RightArm.Hand = node; break;
case HumanoidBones.leftUpperLeg: LeftLeg.Upper = node; break;
case HumanoidBones.leftLowerLeg: LeftLeg.Lower = node; break;
case HumanoidBones.leftFoot: LeftLeg.Foot = node; break;
case HumanoidBones.leftToes: LeftLeg.Toe = node; break;
case HumanoidBones.rightUpperLeg: RightLeg.Upper = node; break;
case HumanoidBones.rightLowerLeg: RightLeg.Lower = node; break;
case HumanoidBones.rightFoot: RightLeg.Foot = node; break;
case HumanoidBones.rightToes: RightLeg.Toe = node; break;
case HumanoidBones.leftThumbMetacarpal: LeftFingers.Thumb.Metacarpal = node; break;
case HumanoidBones.leftThumbProximal: LeftFingers.Thumb.Proximal = node; break;
case HumanoidBones.leftThumbDistal: LeftFingers.Thumb.Distal = node; break;
case HumanoidBones.leftIndexProximal: LeftFingers.Index.Proximal = node; break;
case HumanoidBones.leftIndexIntermediate: LeftFingers.Index.Intermediate = node; break;
case HumanoidBones.leftIndexDistal: LeftFingers.Index.Distal = node; break;
case HumanoidBones.leftMiddleProximal: LeftFingers.Middle.Proximal = node; break;
case HumanoidBones.leftMiddleIntermediate: LeftFingers.Middle.Intermediate = node; break;
case HumanoidBones.leftMiddleDistal: LeftFingers.Middle.Distal = node; break;
case HumanoidBones.leftRingProximal: LeftFingers.Ring.Proximal = node; break;
case HumanoidBones.leftRingIntermediate: LeftFingers.Ring.Intermediate = node; break;
case HumanoidBones.leftRingDistal: LeftFingers.Ring.Distal = node; break;
case HumanoidBones.leftLittleProximal: LeftFingers.Little.Proximal = node; break;
case HumanoidBones.leftLittleIntermediate: LeftFingers.Little.Intermediate = node; break;
case HumanoidBones.leftLittleDistal: LeftFingers.Little.Distal = node; break;
case HumanoidBones.rightThumbMetacarpal: RightFingers.Thumb.Metacarpal = node; break;
case HumanoidBones.rightThumbProximal: RightFingers.Thumb.Proximal = node; break;
case HumanoidBones.rightThumbDistal: RightFingers.Thumb.Distal = node; break;
case HumanoidBones.rightIndexProximal: RightFingers.Index.Proximal = node; break;
case HumanoidBones.rightIndexIntermediate: RightFingers.Index.Intermediate = node; break;
case HumanoidBones.rightIndexDistal: RightFingers.Index.Distal = node; break;
case HumanoidBones.rightMiddleProximal: RightFingers.Middle.Proximal = node; break;
case HumanoidBones.rightMiddleIntermediate: RightFingers.Middle.Intermediate = node; break;
case HumanoidBones.rightMiddleDistal: RightFingers.Middle.Distal = node; break;
case HumanoidBones.rightRingProximal: RightFingers.Ring.Proximal = node; break;
case HumanoidBones.rightRingIntermediate: RightFingers.Ring.Intermediate = node; break;
case HumanoidBones.rightRingDistal: RightFingers.Ring.Distal = node; break;
case HumanoidBones.rightLittleProximal: RightFingers.Little.Proximal = node; break;
case HumanoidBones.rightLittleIntermediate: RightFingers.Little.Intermediate = node; break;
case HumanoidBones.rightLittleDistal: RightFingers.Little.Distal = node; break;
default: throw new NotImplementedException();
}
}
public bool ContainsKey(HumanoidBones key)
{
return TryGetValue(key, out Node _);
}
public bool Remove(HumanoidBones key)
{
if (!ContainsKey(key))
{
return false;
}
Add(key, null);
return true;
}
public Node this[HumanoidBones key]
{
get
{
if (TryGetValue(key, out Node node))
{
return node;
}
else
{
throw new KeyNotFoundException();
}
}
}
public Node this[Node node]
{
get
{
if (node.HumanoidBone.HasValue && node.HumanoidBone.Value != HumanoidBones.unknown)
{
return this[node.HumanoidBone.Value];
}
else
{
// とりあえず
return Hips.Traverse().First(x => x.Name == node.Name);
}
}
}
public bool TryGetValue(HumanoidBones key, out Node value)
{
switch (key)
{
case HumanoidBones.hips: value = Hips; return true;
case HumanoidBones.spine: value = Spine; return true;
case HumanoidBones.chest: value = Chest; return true;
case HumanoidBones.upperChest: value = UpperChest; return true;
case HumanoidBones.neck: value = Neck; return true;
case HumanoidBones.head: value = Head.Head; return true;
case HumanoidBones.jaw: value = Head.Jaw; return true;
case HumanoidBones.leftEye: value = Head.LeftEye; return true;
case HumanoidBones.rightEye: value = Head.RightEye; return true;
case HumanoidBones.leftShoulder: value = LeftArm.Shoulder; return true;
case HumanoidBones.leftUpperArm: value = LeftArm.Upper; return true;
case HumanoidBones.leftLowerArm: value = LeftArm.Lower; return true;
case HumanoidBones.leftHand: value = LeftArm.Hand; return true;
case HumanoidBones.rightShoulder: value = RightArm.Shoulder; return true;
case HumanoidBones.rightUpperArm: value = RightArm.Upper; return true;
case HumanoidBones.rightLowerArm: value = RightArm.Lower; return true;
case HumanoidBones.rightHand: value = RightArm.Hand; return true;
case HumanoidBones.leftUpperLeg: value = LeftLeg.Upper; return true;
case HumanoidBones.leftLowerLeg: value = LeftLeg.Lower; return true;
case HumanoidBones.leftFoot: value = LeftLeg.Foot; return true;
case HumanoidBones.leftToes: value = LeftLeg.Toe; return true;
case HumanoidBones.rightUpperLeg: value = RightLeg.Upper; return true;
case HumanoidBones.rightLowerLeg: value = RightLeg.Lower; return true;
case HumanoidBones.rightFoot: value = RightLeg.Foot; return true;
case HumanoidBones.rightToes: value = RightLeg.Toe; return true;
case HumanoidBones.leftThumbMetacarpal: value = LeftFingers.Thumb.Metacarpal; return true;
case HumanoidBones.leftThumbProximal: value = LeftFingers.Thumb.Proximal; return true;
case HumanoidBones.leftThumbDistal: value = LeftFingers.Thumb.Distal; return true;
case HumanoidBones.leftIndexProximal: value = LeftFingers.Index.Proximal; return true;
case HumanoidBones.leftIndexIntermediate: value = LeftFingers.Index.Intermediate; return true;
case HumanoidBones.leftIndexDistal: value = LeftFingers.Index.Distal; return true;
case HumanoidBones.leftMiddleProximal: value = LeftFingers.Middle.Proximal; return true;
case HumanoidBones.leftMiddleIntermediate: value = LeftFingers.Middle.Intermediate; return true;
case HumanoidBones.leftMiddleDistal: value = LeftFingers.Middle.Distal; return true;
case HumanoidBones.leftRingProximal: value = LeftFingers.Ring.Proximal; return true;
case HumanoidBones.leftRingIntermediate: value = LeftFingers.Ring.Intermediate; return true;
case HumanoidBones.leftRingDistal: value = LeftFingers.Ring.Distal; return true;
case HumanoidBones.leftLittleProximal: value = LeftFingers.Little.Proximal; return true;
case HumanoidBones.leftLittleIntermediate: value = LeftFingers.Little.Intermediate; return true;
case HumanoidBones.leftLittleDistal: value = LeftFingers.Little.Distal; return true;
case HumanoidBones.rightThumbProximal: value = LeftFingers.Thumb.Proximal; return true;
case HumanoidBones.rightThumbMetacarpal: value = LeftFingers.Thumb.Metacarpal; return true;
case HumanoidBones.rightThumbDistal: value = LeftFingers.Thumb.Distal; return true;
case HumanoidBones.rightIndexProximal: value = LeftFingers.Index.Proximal; return true;
case HumanoidBones.rightIndexIntermediate: value = LeftFingers.Index.Intermediate; return true;
case HumanoidBones.rightIndexDistal: value = LeftFingers.Index.Distal; return true;
case HumanoidBones.rightMiddleProximal: value = LeftFingers.Middle.Proximal; return true;
case HumanoidBones.rightMiddleIntermediate: value = LeftFingers.Middle.Intermediate; return true;
case HumanoidBones.rightMiddleDistal: value = LeftFingers.Middle.Distal; return true;
case HumanoidBones.rightRingProximal: value = LeftFingers.Ring.Proximal; return true;
case HumanoidBones.rightRingIntermediate: value = LeftFingers.Ring.Intermediate; return true;
case HumanoidBones.rightRingDistal: value = LeftFingers.Ring.Distal; return true;
case HumanoidBones.rightLittleProximal: value = LeftFingers.Little.Proximal; return true;
case HumanoidBones.rightLittleIntermediate: value = LeftFingers.Little.Intermediate; return true;
case HumanoidBones.rightLittleDistal: value = LeftFingers.Little.Distal; return true;
}
value = null;
return false;
}
public void Clear()
{
foreach (HumanoidBones key in CachedEnum.GetValues<HumanoidBones>())
{
Add(key, null);
}
}
public IEnumerator<KeyValuePair<HumanoidBones, Node>> GetEnumerator()
{
if (Hips != null) yield return new KeyValuePair<HumanoidBones, Node>(HumanoidBones.hips, Hips);
if (Spine != null) yield return new KeyValuePair<HumanoidBones, Node>(HumanoidBones.spine, Spine);
if (Chest != null) yield return new KeyValuePair<HumanoidBones, Node>(HumanoidBones.chest, Chest);
if (UpperChest != null) yield return new KeyValuePair<HumanoidBones, Node>(HumanoidBones.upperChest, UpperChest);
if (Neck != null) yield return new KeyValuePair<HumanoidBones, Node>(HumanoidBones.neck, Neck);
if (Head.Head != null) yield return new KeyValuePair<HumanoidBones, Node>(HumanoidBones.head, Head.Head);
if (LeftArm.Shoulder != null) yield return new KeyValuePair<HumanoidBones, Node>(HumanoidBones.leftShoulder, LeftArm.Shoulder);
if (LeftArm.Upper != null) yield return new KeyValuePair<HumanoidBones, Node>(HumanoidBones.leftUpperArm, LeftArm.Upper);
if (LeftArm.Lower != null) yield return new KeyValuePair<HumanoidBones, Node>(HumanoidBones.leftLowerArm, LeftArm.Lower);
if (LeftArm.Hand != null) yield return new KeyValuePair<HumanoidBones, Node>(HumanoidBones.leftHand, LeftArm.Hand);
if (RightArm.Shoulder != null) yield return new KeyValuePair<HumanoidBones, Node>(HumanoidBones.rightShoulder, RightArm.Shoulder);
if (RightArm.Upper != null) yield return new KeyValuePair<HumanoidBones, Node>(HumanoidBones.rightUpperArm, RightArm.Upper);
if (RightArm.Lower != null) yield return new KeyValuePair<HumanoidBones, Node>(HumanoidBones.rightLowerArm, RightArm.Lower);
if (RightArm.Hand != null) yield return new KeyValuePair<HumanoidBones, Node>(HumanoidBones.rightHand, RightArm.Hand);
if (LeftLeg.Upper != null) yield return new KeyValuePair<HumanoidBones, Node>(HumanoidBones.leftUpperLeg, LeftLeg.Upper);
if (LeftLeg.Lower != null) yield return new KeyValuePair<HumanoidBones, Node>(HumanoidBones.leftLowerLeg, LeftLeg.Lower);
if (LeftLeg.Foot != null) yield return new KeyValuePair<HumanoidBones, Node>(HumanoidBones.leftFoot, LeftLeg.Foot);
if (RightLeg.Upper != null) yield return new KeyValuePair<HumanoidBones, Node>(HumanoidBones.rightUpperLeg, RightLeg.Upper);
if (RightLeg.Lower != null) yield return new KeyValuePair<HumanoidBones, Node>(HumanoidBones.rightLowerLeg, RightLeg.Lower);
if (RightLeg.Foot != null) yield return new KeyValuePair<HumanoidBones, Node>(HumanoidBones.rightFoot, RightLeg.Foot);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#region NotImplement
bool ICollection<KeyValuePair<HumanoidBones, Node>>.Contains(KeyValuePair<HumanoidBones, Node> item)
{
throw new System.NotImplementedException();
}
void ICollection<KeyValuePair<HumanoidBones, Node>>.CopyTo(KeyValuePair<HumanoidBones, Node>[] array, int arrayIndex)
{
throw new System.NotImplementedException();
}
bool ICollection<KeyValuePair<HumanoidBones, Node>>.Remove(KeyValuePair<HumanoidBones, Node> item)
{
throw new System.NotImplementedException();
}
#endregion
#endregion
}
}