274 lines
7.7 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace UniHumanoid
{
public class Bvh
{
public BvhNode Root
{
get;
private set;
}
public TimeSpan FrameTime
{
get;
private set;
}
public BvhChannelCurve[] 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(BvhChannelCurve 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 BvhChannelCurve GetChannel(BvhNode target, BvhChannel 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 BvhChannelCurve(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))
{
var first = r.ReadLine();
if (first != "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 BvhEndSite();
}
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 BvhEndSite))
{
node.Children.Add(child);
}
}
return node;
}
}
}