/**
[EasyMotionRecorder]
Copyright (c) 2018 Duo.inc
This software is released under the MIT License.
http://opensource.org/licenses/mit-license.php
*/
using UnityEngine;
using System;
using System.Text;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Entum
{
[Serializable]
public class MotionDataSettings
{
public enum Rootbonesystem
{
Hipbone,
Objectroot
}
///
/// Humanoid用のMuscleマッピング
///
public static Dictionary TraitPropMap = new Dictionary
{
{"Left Thumb 1 Stretched", "LeftHand.Thumb.1 Stretched"},
{"Left Thumb Spread", "LeftHand.Thumb.Spread"},
{"Left Thumb 2 Stretched", "LeftHand.Thumb.2 Stretched"},
{"Left Thumb 3 Stretched", "LeftHand.Thumb.3 Stretched"},
{"Left Index 1 Stretched", "LeftHand.Index.1 Stretched"},
{"Left Index Spread", "LeftHand.Index.Spread"},
{"Left Index 2 Stretched", "LeftHand.Index.2 Stretched"},
{"Left Index 3 Stretched", "LeftHand.Index.3 Stretched"},
{"Left Middle 1 Stretched", "LeftHand.Middle.1 Stretched"},
{"Left Middle Spread", "LeftHand.Middle.Spread"},
{"Left Middle 2 Stretched", "LeftHand.Middle.2 Stretched"},
{"Left Middle 3 Stretched", "LeftHand.Middle.3 Stretched"},
{"Left Ring 1 Stretched", "LeftHand.Ring.1 Stretched"},
{"Left Ring Spread", "LeftHand.Ring.Spread"},
{"Left Ring 2 Stretched", "LeftHand.Ring.2 Stretched"},
{"Left Ring 3 Stretched", "LeftHand.Ring.3 Stretched"},
{"Left Little 1 Stretched", "LeftHand.Little.1 Stretched"},
{"Left Little Spread", "LeftHand.Little.Spread"},
{"Left Little 2 Stretched", "LeftHand.Little.2 Stretched"},
{"Left Little 3 Stretched", "LeftHand.Little.3 Stretched"},
{"Right Thumb 1 Stretched", "RightHand.Thumb.1 Stretched"},
{"Right Thumb Spread", "RightHand.Thumb.Spread"},
{"Right Thumb 2 Stretched", "RightHand.Thumb.2 Stretched"},
{"Right Thumb 3 Stretched", "RightHand.Thumb.3 Stretched"},
{"Right Index 1 Stretched", "RightHand.Index.1 Stretched"},
{"Right Index Spread", "RightHand.Index.Spread"},
{"Right Index 2 Stretched", "RightHand.Index.2 Stretched"},
{"Right Index 3 Stretched", "RightHand.Index.3 Stretched"},
{"Right Middle 1 Stretched", "RightHand.Middle.1 Stretched"},
{"Right Middle Spread", "RightHand.Middle.Spread"},
{"Right Middle 2 Stretched", "RightHand.Middle.2 Stretched"},
{"Right Middle 3 Stretched", "RightHand.Middle.3 Stretched"},
{"Right Ring 1 Stretched", "RightHand.Ring.1 Stretched"},
{"Right Ring Spread", "RightHand.Ring.Spread"},
{"Right Ring 2 Stretched", "RightHand.Ring.2 Stretched"},
{"Right Ring 3 Stretched", "RightHand.Ring.3 Stretched"},
{"Right Little 1 Stretched", "RightHand.Little.1 Stretched"},
{"Right Little Spread", "RightHand.Little.Spread"},
{"Right Little 2 Stretched", "RightHand.Little.2 Stretched"},
{"Right Little 3 Stretched", "RightHand.Little.3 Stretched"},
};
}
///
/// モーションデータの中身
///
public class HumanoidPoses : ScriptableObject
{
#if UNITY_EDITOR
//Genericなanimファイルとして出力する
[ContextMenu("Export as Generic animation clips")]
public void ExportGenericAnim()
{
var clip = new AnimationClip { frameRate = 30 };
AnimationUtility.SetAnimationClipSettings(clip, new AnimationClipSettings { loopTime = false });
var bones = Poses[0].HumanoidBones;
for (int i = 0; i < bones.Count; i++)
{
var positionCurveX = new AnimationCurve();
var positionCurveY = new AnimationCurve();
var positionCurveZ = new AnimationCurve();
var rotationCurveX = new AnimationCurve();
var rotationCurveY = new AnimationCurve();
var rotationCurveZ = new AnimationCurve();
var rotationCurveW = new AnimationCurve();
foreach (var p in Poses)
{
positionCurveX.AddKey(p.Time, p.HumanoidBones[i].LocalPosition.x);
positionCurveY.AddKey(p.Time, p.HumanoidBones[i].LocalPosition.y);
positionCurveZ.AddKey(p.Time, p.HumanoidBones[i].LocalPosition.z);
rotationCurveX.AddKey(p.Time, p.HumanoidBones[i].LocalRotation.x);
rotationCurveY.AddKey(p.Time, p.HumanoidBones[i].LocalRotation.y);
rotationCurveZ.AddKey(p.Time, p.HumanoidBones[i].LocalRotation.z);
rotationCurveW.AddKey(p.Time, p.HumanoidBones[i].LocalRotation.w);
}
//pathは階層
//http://mebiustos.hatenablog.com/entry/2015/09/16/230000
AnimationUtility.SetEditorCurve(clip,
new EditorCurveBinding
{
path = Poses[0].HumanoidBones[i].Name,
type = typeof(Transform),
propertyName = "m_LocalPosition.x"
}, positionCurveX);
AnimationUtility.SetEditorCurve(clip,
new EditorCurveBinding
{
path = Poses[0].HumanoidBones[i].Name,
type = typeof(Transform),
propertyName = "m_LocalPosition.y"
}, positionCurveY);
AnimationUtility.SetEditorCurve(clip,
new EditorCurveBinding
{
path = Poses[0].HumanoidBones[i].Name,
type = typeof(Transform),
propertyName = "m_LocalPosition.z"
}, positionCurveZ);
AnimationUtility.SetEditorCurve(clip,
new EditorCurveBinding
{
path = Poses[0].HumanoidBones[i].Name,
type = typeof(Transform),
propertyName = "m_LocalRotation.x"
}, rotationCurveX);
AnimationUtility.SetEditorCurve(clip,
new EditorCurveBinding
{
path = Poses[0].HumanoidBones[i].Name,
type = typeof(Transform),
propertyName = "m_LocalRotation.y"
}, rotationCurveY);
AnimationUtility.SetEditorCurve(clip,
new EditorCurveBinding
{
path = Poses[0].HumanoidBones[i].Name,
type = typeof(Transform),
propertyName = "m_LocalRotation.z"
}, rotationCurveZ);
AnimationUtility.SetEditorCurve(clip,
new EditorCurveBinding
{
path = Poses[0].HumanoidBones[i].Name,
type = typeof(Transform),
propertyName = "m_LocalRotation.w"
}, rotationCurveW);
}
clip.EnsureQuaternionContinuity();
var path = string.Format("Assets/Resources/RecordMotion_{0:yyyy_MM_dd_HH_mm_ss}_Generic.anim", DateTime.Now);
var uniqueAssetPath = AssetDatabase.GenerateUniqueAssetPath(path);
AssetDatabase.CreateAsset(clip, uniqueAssetPath);
AssetDatabase.SaveAssets();
}
//Humanoidなanimファイルとして出力する。
[ContextMenu("Export as Humanoid animation clips")]
public void ExportHumanoidAnim()
{
var clip = new AnimationClip { frameRate = 30 };
AnimationUtility.SetAnimationClipSettings(clip, new AnimationClipSettings { loopTime = false });
// body position
{
var curveX = new AnimationCurve();
var curveY = new AnimationCurve();
var curveZ = new AnimationCurve();
foreach (var item in Poses)
{
curveX.AddKey(item.Time, item.BodyPosition.x);
curveY.AddKey(item.Time, item.BodyPosition.y);
curveZ.AddKey(item.Time, item.BodyPosition.z);
}
const string muscleX = "RootT.x";
clip.SetCurve("", typeof(Animator), muscleX, curveX);
const string muscleY = "RootT.y";
clip.SetCurve("", typeof(Animator), muscleY, curveY);
const string muscleZ = "RootT.z";
clip.SetCurve("", typeof(Animator), muscleZ, curveZ);
}
// Leftfoot position
{
var curveX = new AnimationCurve();
var curveY = new AnimationCurve();
var curveZ = new AnimationCurve();
foreach (var item in Poses)
{
curveX.AddKey(item.Time, item.LeftfootIK_Pos.x);
curveY.AddKey(item.Time, item.LeftfootIK_Pos.y);
curveZ.AddKey(item.Time, item.LeftfootIK_Pos.z);
}
const string muscleX = "LeftFootT.x";
clip.SetCurve("", typeof(Animator), muscleX, curveX);
const string muscleY = "LeftFootT.y";
clip.SetCurve("", typeof(Animator), muscleY, curveY);
const string muscleZ = "LeftFootT.z";
clip.SetCurve("", typeof(Animator), muscleZ, curveZ);
}
// Rightfoot position
{
var curveX = new AnimationCurve();
var curveY = new AnimationCurve();
var curveZ = new AnimationCurve();
foreach (var item in Poses)
{
curveX.AddKey(item.Time, item.RightfootIK_Pos.x);
curveY.AddKey(item.Time, item.RightfootIK_Pos.y);
curveZ.AddKey(item.Time, item.RightfootIK_Pos.z);
}
const string muscleX = "RightFootT.x";
clip.SetCurve("", typeof(Animator), muscleX, curveX);
const string muscleY = "RightFootT.y";
clip.SetCurve("", typeof(Animator), muscleY, curveY);
const string muscleZ = "RightFootT.z";
clip.SetCurve("", typeof(Animator), muscleZ, curveZ);
}
// body rotation
{
var curveX = new AnimationCurve();
var curveY = new AnimationCurve();
var curveZ = new AnimationCurve();
var curveW = new AnimationCurve();
foreach (var item in Poses)
{
curveX.AddKey(item.Time, item.BodyRotation.x);
curveY.AddKey(item.Time, item.BodyRotation.y);
curveZ.AddKey(item.Time, item.BodyRotation.z);
curveW.AddKey(item.Time, item.BodyRotation.w);
}
const string muscleX = "RootQ.x";
clip.SetCurve("", typeof(Animator), muscleX, curveX);
const string muscleY = "RootQ.y";
clip.SetCurve("", typeof(Animator), muscleY, curveY);
const string muscleZ = "RootQ.z";
clip.SetCurve("", typeof(Animator), muscleZ, curveZ);
const string muscleW = "RootQ.w";
clip.SetCurve("", typeof(Animator), muscleW, curveW);
}
// Leftfoot rotation
{
var curveX = new AnimationCurve();
var curveY = new AnimationCurve();
var curveZ = new AnimationCurve();
var curveW = new AnimationCurve();
foreach (var item in Poses)
{
curveX.AddKey(item.Time, item.LeftfootIK_Rot.x);
curveY.AddKey(item.Time, item.LeftfootIK_Rot.y);
curveZ.AddKey(item.Time, item.LeftfootIK_Rot.z);
curveW.AddKey(item.Time, item.LeftfootIK_Rot.w);
}
const string muscleX = "LeftFootQ.x";
clip.SetCurve("", typeof(Animator), muscleX, curveX);
const string muscleY = "LeftFootQ.y";
clip.SetCurve("", typeof(Animator), muscleY, curveY);
const string muscleZ = "LeftFootQ.z";
clip.SetCurve("", typeof(Animator), muscleZ, curveZ);
const string muscleW = "LeftFootQ.w";
clip.SetCurve("", typeof(Animator), muscleW, curveW);
}
// Rightfoot rotation
{
var curveX = new AnimationCurve();
var curveY = new AnimationCurve();
var curveZ = new AnimationCurve();
var curveW = new AnimationCurve();
foreach (var item in Poses)
{
curveX.AddKey(item.Time, item.RightfootIK_Rot.x);
curveY.AddKey(item.Time, item.RightfootIK_Rot.y);
curveZ.AddKey(item.Time, item.RightfootIK_Rot.z);
curveW.AddKey(item.Time, item.RightfootIK_Rot.w);
}
const string muscleX = "RightFootQ.x";
clip.SetCurve("", typeof(Animator), muscleX, curveX);
const string muscleY = "RightFootQ.y";
clip.SetCurve("", typeof(Animator), muscleY, curveY);
const string muscleZ = "RightFootQ.z";
clip.SetCurve("", typeof(Animator), muscleZ, curveZ);
const string muscleW = "RightFootQ.w";
clip.SetCurve("", typeof(Animator), muscleW, curveW);
}
// muscles
for (int i = 0; i < HumanTrait.MuscleCount; i++)
{
var curve = new AnimationCurve();
foreach (var item in Poses)
{
curve.AddKey(item.Time, item.Muscles[i]);
}
var muscle = HumanTrait.MuscleName[i];
if (MotionDataSettings.TraitPropMap.ContainsKey(muscle))
{
muscle = MotionDataSettings.TraitPropMap[muscle];
}
clip.SetCurve("", typeof(Animator), muscle, curve);
}
clip.EnsureQuaternionContinuity();
var path = string.Format("Assets/Resources/RecordMotion_{0:yyyy_MM_dd_HH_mm_ss}_Humanoid.anim", DateTime.Now);
var uniqueAssetPath = AssetDatabase.GenerateUniqueAssetPath(path);
AssetDatabase.CreateAsset(clip, uniqueAssetPath);
AssetDatabase.SaveAssets();
}
#endif
[Serializable]
public class SerializeHumanoidPose
{
public Vector3 BodyRootPosition;
public Quaternion BodyRootRotation;
public Vector3 BodyPosition;
public Quaternion BodyRotation;
public Vector3 LeftfootIK_Pos;
public Quaternion LeftfootIK_Rot;
public Vector3 RightfootIK_Pos;
public Quaternion RightfootIK_Rot;
public float[] Muscles;
//フレーム数
public int FrameCount;
//記録開始後の経過時間。処理落ち対策
public float Time;
[Serializable]
public class HumanoidBone
{
public string Name;
public Vector3 LocalPosition;
public Quaternion LocalRotation;
private static Dictionary _pathCache = new Dictionary();
private static string BuildRelativePath(Transform root, Transform target)
{
var path = "";
_pathCache.TryGetValue(target, out path);
if(path != null) return path;
var current = target;
while (true)
{
if (current == null) throw new Exception(target.name + "は" + root.name + "の子ではありません");
if (current == root) break;
path = (path == "") ? current.name : current.name + "/" + path;
current = current.parent;
}
_pathCache.Add(target, path);
return path;
}
public void Set(Transform root, Transform t)
{
Name = BuildRelativePath(root, t);
LocalPosition = t.localPosition;
LocalRotation = t.localRotation;
}
}
public List HumanoidBones = new List();
//CSVシリアライズ
public string SerializeCSV()
{
StringBuilder sb = new StringBuilder();
SerializeVector3(sb, BodyRootPosition);
SerializeQuaternion(sb, BodyRootRotation);
SerializeVector3(sb, BodyPosition);
SerializeQuaternion(sb, BodyRotation);
foreach (var muscle in Muscles)
{
sb.Append(muscle);
sb.Append(",");
}
sb.Append(FrameCount);
sb.Append(",");
sb.Append(Time);
sb.Append(",");
foreach (var humanoidBone in HumanoidBones)
{
sb.Append(humanoidBone.Name);
sb.Append(",");
SerializeVector3(sb, humanoidBone.LocalPosition);
SerializeQuaternion(sb, humanoidBone.LocalRotation);
}
sb.Length = sb.Length - 1; //最後のカンマ削除
return sb.ToString();
}
private static void SerializeVector3(StringBuilder sb, Vector3 vec)
{
sb.Append(vec.x);
sb.Append(",");
sb.Append(vec.y);
sb.Append(",");
sb.Append(vec.z);
sb.Append(",");
}
private static void SerializeQuaternion(StringBuilder sb, Quaternion q)
{
sb.Append(q.x);
sb.Append(",");
sb.Append(q.y);
sb.Append(",");
sb.Append(q.z);
sb.Append(",");
sb.Append(q.w);
sb.Append(",");
}
//CSVデシリアライズ
public void DeserializeCSV(string str)
{
string[] dataString = str.Split(',');
BodyRootPosition = DeserializeVector3(dataString, 0);
BodyRootRotation = DeserializeQuaternion(dataString, 3);
BodyPosition = DeserializeVector3(dataString, 7);
BodyRotation = DeserializeQuaternion(dataString, 10);
Muscles = new float[HumanTrait.MuscleCount];
for (int i = 0; i < HumanTrait.MuscleCount; i++)
{
Muscles[i] = float.Parse(dataString[i + 14]);
}
FrameCount = int.Parse(dataString[14 + HumanTrait.MuscleCount]);
Time = float.Parse(dataString[15 + HumanTrait.MuscleCount]);
var boneValues = Enum.GetValues(typeof(HumanBodyBones)) as HumanBodyBones[];
for (int i = 0; i < boneValues.Length; i++)
{
int startIndex = 16 + HumanTrait.MuscleCount + (i * 8);
if (dataString.Length <= startIndex)
{
break;
}
HumanoidBone bone = new HumanoidBone();
bone.Name = dataString[startIndex];
bone.LocalPosition = DeserializeVector3(dataString, startIndex + 1);
bone.LocalRotation = DeserializeQuaternion(dataString, startIndex + 4);
}
}
private static Vector3 DeserializeVector3(IList str, int startIndex)
{
return new Vector3(float.Parse(str[startIndex]), float.Parse(str[startIndex + 1]), float.Parse(str[startIndex + 2]));
}
private static Quaternion DeserializeQuaternion(IList str, int startIndex)
{
return new Quaternion(float.Parse(str[startIndex]), float.Parse(str[startIndex + 1]), float.Parse(str[startIndex + 2]), float.Parse(str[startIndex + 3]));
}
}
public List Poses = new List();
}
}