244 lines
8.6 KiB
C#
244 lines
8.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
|
|
|
|
namespace UniHumanoid
|
|
{
|
|
public interface ISkeletonDetector
|
|
{
|
|
Skeleton Detect(IList<IBone> bones);
|
|
}
|
|
|
|
|
|
public class BvhSkeletonEstimator : ISkeletonDetector
|
|
{
|
|
static IBone GetRoot(IList<IBone> bones)
|
|
{
|
|
var hips = bones.Where(x => x.Parent == null).ToArray();
|
|
if (hips.Length != 1)
|
|
{
|
|
throw new System.Exception("Require unique root");
|
|
}
|
|
return hips[0];
|
|
}
|
|
|
|
static IBone SelectBone(Func<IBone, IBone, IBone> selector, IList<IBone> bones)
|
|
{
|
|
if (bones == null || bones.Count == 0) throw new Exception("no bones");
|
|
var current = bones[0];
|
|
for (var i = 1; i < bones.Count; ++i)
|
|
{
|
|
current = selector(current, bones[i]);
|
|
}
|
|
return current;
|
|
}
|
|
|
|
static void GetSpineAndHips(IBone hips, out IBone spine, out IBone leg_L, out IBone leg_R)
|
|
{
|
|
if (hips.Children.Count != 3) throw new System.Exception("Hips require 3 children");
|
|
spine = SelectBone((l, r) => l.CenterOfDescendant().y > r.CenterOfDescendant().y ? l : r, hips.Children);
|
|
leg_L = SelectBone((l, r) => l.CenterOfDescendant().x < r.CenterOfDescendant().x ? l : r, hips.Children);
|
|
leg_R = SelectBone((l, r) => l.CenterOfDescendant().x > r.CenterOfDescendant().x ? l : r, hips.Children);
|
|
}
|
|
|
|
static void GetNeckAndArms(IBone chest, out IBone neck, out IBone arm_L, out IBone arm_R)
|
|
{
|
|
if (chest.Children.Count != 3) throw new System.Exception("Chest require 3 children");
|
|
neck = SelectBone((l, r) => l.CenterOfDescendant().y > r.CenterOfDescendant().y ? l : r, chest.Children);
|
|
arm_L = SelectBone((l, r) => l.CenterOfDescendant().x < r.CenterOfDescendant().x ? l : r, chest.Children);
|
|
arm_R = SelectBone((l, r) => l.CenterOfDescendant().x > r.CenterOfDescendant().x ? l : r, chest.Children);
|
|
}
|
|
|
|
struct Arm
|
|
{
|
|
public IBone Shoulder;
|
|
public IBone UpperArm;
|
|
public IBone LowerArm;
|
|
public IBone Hand;
|
|
}
|
|
|
|
Arm GetArm(IBone shoulder)
|
|
{
|
|
var bones = shoulder.Traverse().ToArray();
|
|
switch (bones.Length)
|
|
{
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
throw new NotImplementedException();
|
|
|
|
default:
|
|
return new Arm
|
|
{
|
|
Shoulder = bones[0],
|
|
UpperArm = bones[1],
|
|
LowerArm = bones[2],
|
|
Hand = bones[3],
|
|
};
|
|
}
|
|
}
|
|
|
|
struct Leg
|
|
{
|
|
public IBone UpperLeg;
|
|
public IBone LowerLeg;
|
|
public IBone Foot;
|
|
public IBone Toes;
|
|
}
|
|
|
|
Leg GetLeg(IBone leg)
|
|
{
|
|
var bones = leg.Traverse().Where(x => !x.Name.ToLower().Contains("buttock")).ToArray();
|
|
switch (bones.Length)
|
|
{
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
throw new NotImplementedException();
|
|
|
|
case 3:
|
|
return new Leg
|
|
{
|
|
UpperLeg = bones[0],
|
|
LowerLeg = bones[1],
|
|
Foot = bones[2],
|
|
};
|
|
|
|
default:
|
|
return new Leg
|
|
{
|
|
UpperLeg = bones[bones.Length - 4],
|
|
LowerLeg = bones[bones.Length - 3],
|
|
Foot = bones[bones.Length - 2],
|
|
Toes = bones[bones.Length - 1],
|
|
};
|
|
}
|
|
}
|
|
|
|
public Skeleton Detect(IList<IBone> bones)
|
|
{
|
|
//
|
|
// search bones
|
|
//
|
|
var root = GetRoot(bones);
|
|
var hips = root.Traverse().First(x => x.Children.Count == 3);
|
|
|
|
IBone spine, hip_L, hip_R;
|
|
GetSpineAndHips(hips, out spine, out hip_L, out hip_R);
|
|
var legLeft = GetLeg(hip_L);
|
|
var legRight = GetLeg(hip_R);
|
|
|
|
var spineToChest = new List<IBone>();
|
|
foreach(var x in spine.Traverse())
|
|
{
|
|
spineToChest.Add(x);
|
|
if (x.Children.Count == 3) break;
|
|
}
|
|
|
|
IBone neck, shoulder_L, shoulder_R;
|
|
GetNeckAndArms(spineToChest.Last(), out neck, out shoulder_L, out shoulder_R);
|
|
var armLeft = GetArm(shoulder_L);
|
|
var armRight = GetArm(shoulder_R);
|
|
|
|
var neckToHead = neck.Traverse().ToArray();
|
|
|
|
//
|
|
// set result
|
|
//
|
|
var skeleton = new Skeleton();
|
|
skeleton.Set(HumanBodyBones.Hips, bones, hips);
|
|
|
|
switch (spineToChest.Count)
|
|
{
|
|
case 0:
|
|
throw new Exception();
|
|
|
|
case 1:
|
|
skeleton.Set(HumanBodyBones.Spine, bones, spineToChest[0]);
|
|
break;
|
|
|
|
case 2:
|
|
skeleton.Set(HumanBodyBones.Spine, bones, spineToChest[0]);
|
|
skeleton.Set(HumanBodyBones.Chest, bones, spineToChest[1]);
|
|
break;
|
|
|
|
#if UNITY_5_6_OR_NEWER
|
|
case 3:
|
|
skeleton.Set(HumanBodyBones.Spine, bones, spineToChest[0]);
|
|
skeleton.Set(HumanBodyBones.Chest, bones, spineToChest[1]);
|
|
skeleton.Set(HumanBodyBones.UpperChest, bones, spineToChest[2]);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
skeleton.Set(HumanBodyBones.Spine, bones, spineToChest[0]);
|
|
#if UNITY_5_6_OR_NEWER
|
|
skeleton.Set(HumanBodyBones.Chest, bones, spineToChest[1]);
|
|
skeleton.Set(HumanBodyBones.UpperChest, bones, spineToChest.Last());
|
|
#else
|
|
skeleton.Set(HumanBodyBones.Chest, bones, spineToChest.Last());
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
switch (neckToHead.Length)
|
|
{
|
|
case 0:
|
|
throw new Exception();
|
|
|
|
case 1:
|
|
skeleton.Set(HumanBodyBones.Head, bones, neckToHead[0]);
|
|
break;
|
|
|
|
case 2:
|
|
skeleton.Set(HumanBodyBones.Neck, bones, neckToHead[0]);
|
|
skeleton.Set(HumanBodyBones.Head, bones, neckToHead[1]);
|
|
break;
|
|
|
|
default:
|
|
skeleton.Set(HumanBodyBones.Neck, bones, neckToHead[0]);
|
|
skeleton.Set(HumanBodyBones.Head, bones, neckToHead.Where(x => x.Parent.Children.Count==1).Last());
|
|
break;
|
|
}
|
|
|
|
skeleton.Set(HumanBodyBones.LeftUpperLeg, bones, legLeft.UpperLeg);
|
|
skeleton.Set(HumanBodyBones.LeftLowerLeg, bones, legLeft.LowerLeg);
|
|
skeleton.Set(HumanBodyBones.LeftFoot, bones, legLeft.Foot);
|
|
skeleton.Set(HumanBodyBones.LeftToes, bones, legLeft.Toes);
|
|
|
|
skeleton.Set(HumanBodyBones.RightUpperLeg, bones, legRight.UpperLeg);
|
|
skeleton.Set(HumanBodyBones.RightLowerLeg, bones, legRight.LowerLeg);
|
|
skeleton.Set(HumanBodyBones.RightFoot, bones, legRight.Foot);
|
|
skeleton.Set(HumanBodyBones.RightToes, bones, legRight.Toes);
|
|
|
|
skeleton.Set(HumanBodyBones.LeftShoulder, bones, armLeft.Shoulder);
|
|
skeleton.Set(HumanBodyBones.LeftUpperArm, bones, armLeft.UpperArm);
|
|
skeleton.Set(HumanBodyBones.LeftLowerArm, bones, armLeft.LowerArm);
|
|
skeleton.Set(HumanBodyBones.LeftHand, bones, armLeft.Hand);
|
|
|
|
skeleton.Set(HumanBodyBones.RightShoulder, bones, armRight.Shoulder);
|
|
skeleton.Set(HumanBodyBones.RightUpperArm, bones, armRight.UpperArm);
|
|
skeleton.Set(HumanBodyBones.RightLowerArm, bones, armRight.LowerArm);
|
|
skeleton.Set(HumanBodyBones.RightHand, bones, armRight.Hand);
|
|
|
|
return skeleton;
|
|
}
|
|
|
|
public Skeleton Detect(Bvh bvh)
|
|
{
|
|
var root = new BvhBone(bvh.Root.Name, Vector3.zero);
|
|
root.Build(bvh.Root);
|
|
return Detect(root.Traverse().Select(x => (IBone)x).ToList());
|
|
}
|
|
|
|
public Skeleton Detect(Transform t)
|
|
{
|
|
var root = new BvhBone(t.name, Vector3.zero);
|
|
root.Build(t);
|
|
return Detect(root.Traverse().Select(x => (IBone)x).ToList());
|
|
}
|
|
}
|
|
}
|