summaryrefslogtreecommitdiff
path: root/Assets/ThirdParty/VRM/VRM/UniHumanoid/Scripts/Format/Bvh.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Assets/ThirdParty/VRM/VRM/UniHumanoid/Scripts/Format/Bvh.cs')
-rw-r--r--Assets/ThirdParty/VRM/VRM/UniHumanoid/Scripts/Format/Bvh.cs444
1 files changed, 444 insertions, 0 deletions
diff --git a/Assets/ThirdParty/VRM/VRM/UniHumanoid/Scripts/Format/Bvh.cs b/Assets/ThirdParty/VRM/VRM/UniHumanoid/Scripts/Format/Bvh.cs
new file mode 100644
index 00000000..6c39c3b8
--- /dev/null
+++ b/Assets/ThirdParty/VRM/VRM/UniHumanoid/Scripts/Format/Bvh.cs
@@ -0,0 +1,444 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+
+namespace UniHumanoid
+{
+ public class BvhException : Exception
+ {
+ public BvhException(string msg) : base(msg) { }
+ }
+
+ public enum Channel
+ {
+ Xposition,
+ Yposition,
+ Zposition,
+ Xrotation,
+ Yrotation,
+ Zrotation,
+ }
+ public static class ChannelExtensions
+ {
+ public static string ToProperty(this Channel ch)
+ {
+ switch (ch)
+ {
+ case Channel.Xposition: return "localPosition.x";
+ case Channel.Yposition: return "localPosition.y";
+ case Channel.Zposition: return "localPosition.z";
+ case Channel.Xrotation: return "localEulerAnglesBaked.x";
+ case Channel.Yrotation: return "localEulerAnglesBaked.y";
+ case Channel.Zrotation: return "localEulerAnglesBaked.z";
+ }
+
+ throw new BvhException("no property for " + ch);
+ }
+
+ public static bool IsLocation(this Channel ch)
+ {
+ switch (ch)
+ {
+ case Channel.Xposition:
+ case Channel.Yposition:
+ case Channel.Zposition: return true;
+ case Channel.Xrotation:
+ case Channel.Yrotation:
+ case Channel.Zrotation: return false;
+ }
+
+ throw new BvhException("no property for " + ch);
+ }
+ }
+
+ public struct Single3
+ {
+ public Single x;
+ public Single y;
+ public Single z;
+
+ public Single3(Single _x, Single _y, Single _z)
+ {
+ x = _x;
+ y = _y;
+ z = _z;
+ }
+ }
+
+ public class BvhNode
+ {
+ public String Name
+ {
+ get;
+ private set;
+ }
+
+ public Single3 Offset
+ {
+ get;
+ private set;
+ }
+
+ public Channel[] Channels
+ {
+ get;
+ private set;
+ }
+
+ public List<BvhNode> Children
+ {
+ get;
+ private set;
+ }
+
+ public BvhNode(string name)
+ {
+ Name = name;
+ Children = new List<BvhNode>();
+ }
+
+ public virtual void Parse(StringReader r)
+ {
+ Offset = ParseOffset(r.ReadLine());
+
+ Channels = ParseChannel(r.ReadLine());
+ }
+
+ static Single3 ParseOffset(string line)
+ {
+ var split = line.Trim().Split();
+ if (split[0] != "OFFSET")
+ {
+ throw new BvhException("OFFSET is not found");
+ }
+
+ var offset = split.Skip(1).Where(x => !string.IsNullOrEmpty(x)).Select(x => float.Parse(x, System.Globalization.CultureInfo.InvariantCulture)).ToArray();
+ return new Single3(offset[0], offset[1], offset[2]);
+ }
+
+ static Channel[] ParseChannel(string line)
+ {
+ var split = line.Trim().Split();
+ if (split[0] != "CHANNELS")
+ {
+ throw new BvhException("CHANNELS is not found");
+ }
+ var count = int.Parse(split[1]);
+ if (count + 2 != split.Length)
+ {
+ throw new BvhException("channel count is not match with split count");
+ }
+ return split.Skip(2).Select(x => (Channel)Enum.Parse(typeof(Channel), x)).ToArray();
+ }
+
+ public IEnumerable<BvhNode> Traverse()
+ {
+ yield return this;
+
+ foreach (var child in Children)
+ {
+ foreach (var descendant in child.Traverse())
+ {
+ yield return descendant;
+ }
+ }
+ }
+ }
+
+ public class EndSite : BvhNode
+ {
+ public EndSite(): base("")
+ {
+ }
+
+ public override void Parse(StringReader r)
+ {
+ r.ReadLine(); // offset
+ }
+ }
+
+ public class ChannelCurve
+ {
+ public float[] Keys
+ {
+ get;
+ private set;
+ }
+
+ public ChannelCurve(int frameCount)
+ {
+ Keys = new float[frameCount];
+ }
+
+ public void SetKey(int frame, float value)
+ {
+ Keys[frame] = value;
+ }
+ }
+
+ public class Bvh
+ {
+ public BvhNode Root
+ {
+ get;
+ private set;
+ }
+
+ public TimeSpan FrameTime
+ {
+ get;
+ private set;
+ }
+
+ public ChannelCurve[] Channels
+ {
+ get;
+ private set;
+ }
+
+ int m_frames;
+ public int FrameCount
+ {
+ get { return m_frames; }
+ }
+
+ public struct PathWithProperty
+ {
+ public string Path;
+ public string Property;
+ public bool IsLocation;
+ }
+
+ public bool TryGetPathWithPropertyFromChannel(ChannelCurve channel, out PathWithProperty pathWithProp)
+ {
+ var index = Channels.ToList().IndexOf(channel);
+ if (index == -1)
+ {
+ pathWithProp = default(PathWithProperty);
+ return false;
+ }
+
+ foreach(var node in Root.Traverse())
+ {
+ for(int i=0; i<node.Channels.Length; ++i, --index)
+ {
+ if (index == 0)
+ {
+ pathWithProp = new PathWithProperty
+ {
+ Path=GetPath(node),
+ Property=node.Channels[i].ToProperty(),
+ IsLocation=node.Channels[i].IsLocation(),
+ };
+ return true;
+ }
+ }
+ }
+
+ throw new BvhException("channel is not found");
+ }
+
+ public string GetPath(BvhNode node)
+ {
+ var list = new List<string>() { node.Name };
+
+ var current = node;
+ while (current!=null)
+ {
+ current = GetParent(current);
+ if (current != null)
+ {
+ list.Insert(0, current.Name);
+ }
+ }
+
+ return String.Join("/", list.ToArray());
+ }
+
+ BvhNode GetParent(BvhNode node)
+ {
+ foreach(var x in Root.Traverse())
+ {
+ if (x.Children.Contains(node))
+ {
+ return x;
+ }
+ }
+
+ return null;
+ }
+
+ public ChannelCurve GetChannel(BvhNode target, Channel channel)
+ {
+ var index = 0;
+ foreach (var node in Root.Traverse())
+ {
+ for (int i = 0; i < node.Channels.Length; ++i, ++index)
+ {
+ if(node==target && node.Channels[i] == channel)
+ {
+ return Channels[index];
+ }
+ }
+ }
+
+ throw new BvhException("channel is not found");
+ }
+
+ public override string ToString()
+ {
+ return string.Format("{0}nodes, {1}channels, {2}frames, {3:0.00}seconds"
+ , Root.Traverse().Count()
+ , Channels.Length
+ , m_frames
+ , m_frames * FrameTime.TotalSeconds);
+ }
+
+ public Bvh(BvhNode root, int frames, float seconds)
+ {
+ Root = root;
+ FrameTime = TimeSpan.FromSeconds(seconds);
+ m_frames = frames;
+ var channelCount = Root.Traverse()
+ .Where(x => x.Channels!=null)
+ .Select(x => x.Channels.Length)
+ .Sum();
+ Channels = Enumerable.Range(0, channelCount)
+ .Select(x => new ChannelCurve(frames))
+ .ToArray()
+ ;
+ }
+
+ public void ParseFrame(int frame, string line)
+ {
+ var split = line.Trim().Split().Where(x => !string.IsNullOrEmpty(x)).ToArray();
+ if (split.Length != Channels.Length)
+ {
+ throw new BvhException("frame key count is not match channel count");
+ }
+ for(int i=0; i<Channels.Length; ++i)
+ {
+ Channels[i].SetKey(frame, float.Parse(split[i], System.Globalization.CultureInfo.InvariantCulture));
+ }
+ }
+
+ public static Bvh Parse(string src)
+ {
+ using (var r = new StringReader(src))
+ {
+ if (r.ReadLine() != "HIERARCHY")
+ {
+ throw new BvhException("not start with HIERARCHY");
+ }
+
+ var root = ParseNode(r);
+ if (root == null)
+ {
+ return null;
+ }
+
+ var frames = 0;
+ var frameTime = 0.0f;
+ if (r.ReadLine() == "MOTION")
+ {
+ var frameSplit = r.ReadLine().Split(':');
+ if (frameSplit[0] != "Frames")
+ {
+ throw new BvhException("Frames is not found");
+ }
+ frames = int.Parse(frameSplit[1]);
+
+ var frameTimeSplit = r.ReadLine().Split(':');
+ if (frameTimeSplit[0] != "Frame Time")
+ {
+ throw new BvhException("Frame Time is not found");
+ }
+ frameTime = float.Parse(frameTimeSplit[1], System.Globalization.CultureInfo.InvariantCulture);
+ }
+
+ var bvh = new Bvh(root, frames, frameTime);
+
+ for(int i=0; i<frames; ++i)
+ {
+ var line = r.ReadLine();
+ bvh.ParseFrame(i, line);
+ }
+
+ return bvh;
+ }
+ }
+
+ static BvhNode ParseNode(StringReader r, int level = 0)
+ {
+ var firstline = r.ReadLine().Trim();
+ var split = firstline.Split();
+ if (split.Length != 2)
+ {
+ if (split.Length == 1)
+ {
+ if(split[0] == "}")
+ {
+ return null;
+ }
+ }
+ throw new BvhException(String.Format("split to {0}({1})", split.Length, firstline));
+ }
+
+ BvhNode node = null;
+ if (split[0] == "ROOT")
+ {
+ if (level != 0)
+ {
+ throw new BvhException("nested ROOT");
+ }
+ node = new BvhNode(split[1]);
+ }
+ else if (split[0] == "JOINT")
+ {
+ if (level == 0)
+ {
+ throw new BvhException("should ROOT, but JOINT");
+ }
+ node = new BvhNode(split[1]);
+ }
+ else if (split[0] == "End")
+ {
+ if (level == 0)
+ {
+ throw new BvhException("End in level 0");
+ }
+ node = new EndSite();
+ }
+ else
+ {
+ throw new BvhException("unknown type: " + split[0]);
+ }
+
+ if(r.ReadLine().Trim() != "{")
+ {
+ throw new BvhException("'{' is not found");
+ }
+
+ node.Parse(r);
+
+ // child nodes
+ while (true)
+ {
+ var child = ParseNode(r, level + 1);
+ if (child == null)
+ {
+ break;
+ }
+
+ if(!(child is EndSite))
+ {
+ node.Children.Add(child);
+ }
+ }
+
+ return node;
+ }
+ }
+}