remove non used

This commit is contained in:
ousttrue 2021-03-30 14:24:51 +09:00
parent f8745ee17f
commit 75a12cbe87
28 changed files with 13 additions and 2623 deletions

View File

@ -6,7 +6,7 @@ namespace UniVRM10
{
public static class ModelLoader
{
public static Model Load(Vrm10Storage storage, string rootName, bool estimateHumanoid = false)
public static Model Load(Vrm10Storage storage, string rootName)
{
if (storage == null)
{
@ -81,11 +81,7 @@ namespace UniVRM10
model.Animations.AddRange(Enumerable.Range(0, storage.AnimationCount).Select(x => storage.CreateAnimation(x, model.Nodes)));
// VRM
if (!LoadVrm(model, storage) && estimateHumanoid)
{
// VRMでないときにボーン推定する
model.HumanoidBoneEstimate();
}
LoadVrm(model, storage);
return model;
}

View File

@ -8,7 +8,6 @@ using UnityEngine;
using UnityEngine.TestTools;
using UniVRM10;
using VrmLib;
using VrmLib.Diff;
namespace UniVRM10.Test
{

View File

@ -7,7 +7,7 @@ using UniGLTF;
using UniJSON;
using UnityEditor;
using UnityEngine;
using VrmLib.Diff;
namespace UniVRM10
{
@ -206,16 +206,16 @@ namespace UniVRM10
// vrmlib <= gltf
var loaded = deserialized.FromGltf(textures);
var context = ModelDiffContext.Create();
ModelDiffExtensions.MaterialEquals(context, vrmLibMaterial, loaded);
var diff = context.List
.Where(x => !s_ignoreKeys.Contains(x.Context))
.ToArray();
if (diff.Length > 0)
{
Debug.LogWarning(string.Join("\n", diff.Select(x => $"{x.Context}: {x.Message}")));
}
Assert.AreEqual(0, diff.Length);
// var context = ModelDiffContext.Create();
// ModelDiffExtensions.MaterialEquals(context, vrmLibMaterial, loaded);
// var diff = context.List
// .Where(x => !s_ignoreKeys.Contains(x.Context))
// .ToArray();
// if (diff.Length > 0)
// {
// Debug.LogWarning(string.Join("\n", diff.Select(x => $"{x.Context}: {x.Message}")));
// }
// Assert.AreEqual(0, diff.Length);
// <= vrmlib
var map = new Dictionary<VrmLib.Texture, Texture2D>();

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: ac2c48555929afa47903182006ab82d2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,360 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
namespace VrmLib.Bvh
{
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 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 splitted = line.Trim().Split().Where(x => !string.IsNullOrEmpty(x)).ToArray();
if (splitted.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(splitted[i]));
}
}
}
public static class BvhParser
{
static BvhNode ParseNode(StringReader r, int level = 0)
{
var firstline = r.ReadLine().Trim();
var splitted = firstline.Split();
if (splitted.Length != 2)
{
if (splitted.Length == 1)
{
if (splitted[0] == "}")
{
return null;
}
}
throw new BvhException(String.Format("splitted to {0}({1})", splitted.Length, firstline));
}
BvhNode node = null;
if (splitted[0] == "ROOT")
{
if (level != 0)
{
throw new BvhException("nested ROOT");
}
node = new BvhNode(splitted[1]);
}
else if (splitted[0] == "JOINT")
{
if (level == 0)
{
throw new BvhException("should ROOT, but JOINT");
}
node = new BvhNode(splitted[1]);
}
else if (splitted[0] == "End")
{
if (level == 0)
{
throw new BvhException("End in level 0");
}
node = new EndSite();
}
else
{
throw new BvhException("unknown type: " + splitted[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.AddChid(child);
}
}
return node;
}
public static Bvh FromPath(string path)
{
return Parse(File.ReadAllText(path));
}
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 frameSplitted = r.ReadLine().Split(':');
if (frameSplitted[0] != "Frames")
{
throw new BvhException("Frames is not found");
}
frames = int.Parse(frameSplitted[1]);
var frameTimeSplitted = r.ReadLine().Split(':');
if (frameTimeSplitted[0] != "Frame Time")
{
throw new BvhException("Frame Time is not found");
}
frameTime = float.Parse(frameTimeSplitted[1]);
}
var bvh = new Bvh(root, frames, frameTime);
for (int i = 0; i < frames; ++i)
{
var line = r.ReadLine();
bvh.ParseFrame(i, line);
}
bvh.Root.UpdatePosition(Vector3.Zero);
return bvh;
}
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 3876c0b36fb1fc54e9f7a5fc412967ec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,46 +0,0 @@
using System;
using System.Collections.Generic;
using System.Numerics;
namespace VrmLib.Bvh
{
public static class BvhAnimation
{
class CurveSet
{
BvhNode Node = default;
Func<float, float, float, Quaternion> EulerToRotation = default;
public CurveSet(BvhNode node)
{
Node = node;
}
public ChannelCurve PositionX = default;
public ChannelCurve PositionY = default;
public ChannelCurve PositionZ = default;
public Vector3 GetPosition(int i)
{
return new Vector3(
PositionX.Keys[i],
PositionY.Keys[i],
PositionZ.Keys[i]);
}
public ChannelCurve RotationX = default;
public ChannelCurve RotationY = default;
public ChannelCurve RotationZ = default;
public Quaternion GetRotation(int i)
{
if (EulerToRotation == null)
{
EulerToRotation = Node.GetEulerToRotation();
}
return EulerToRotation(
RotationX.Keys[i],
RotationY.Keys[i],
RotationZ.Keys[i]
);
}
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 556644864ce505b43b0f967425a25c0c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,34 +0,0 @@
using System;
using System.Linq;
using System.Numerics;
namespace VrmLib.Bvh
{
public static class BvhExtensions
{
public static Func<float, float, float, Quaternion> GetEulerToRotation(this BvhNode bvh)
{
var order = bvh.Channels.Where(x => x == Channel.Xrotation || x == Channel.Yrotation || x == Channel.Zrotation).ToArray();
return (x, y, z) =>
{
var xRot = Quaternion.CreateFromYawPitchRoll(x, 0, 0);
var yRot = Quaternion.CreateFromYawPitchRoll(0, y, 0);
var zRot = Quaternion.CreateFromYawPitchRoll(0, 0, z);
var r = Quaternion.Identity;
foreach (var ch in order)
{
switch (ch)
{
case Channel.Xrotation: r = r * xRot; break;
case Channel.Yrotation: r = r * yRot; break;
case Channel.Zrotation: r = r * zRot; break;
default: throw new BvhException("no rotation");
}
}
return r;
};
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: be2bfe50d0adaf24185001d94f5ff8e0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,124 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
namespace VrmLib.Bvh
{
public class BvhNode
{
public String Name
{
get;
set;
}
public override string ToString()
{
return $"{Name}";
}
public HumanoidBones Bone
{
get;
set;
}
// world position
public Vector3 SkeletonLocalPosition
{
get;
private set;
}
public void UpdatePosition(Vector3 parentPosition)
{
SkeletonLocalPosition = parentPosition + Offset;
foreach (var child in Children)
{
child.UpdatePosition(SkeletonLocalPosition);
}
}
public Vector3 Offset;
public Channel[] Channels
{
get;
private set;
}
List<BvhNode> m_children = new List<BvhNode>();
public IReadOnlyList<BvhNode> Children => m_children;
public void AddChid(BvhNode child)
{
child.Parent = this;
m_children.Add(child);
}
public BvhNode Parent
{
get;
private set;
}
public BvhNode(string name)
{
Name = name;
}
public virtual void Parse(StringReader r)
{
Offset = ParseOffset(r.ReadLine());
Channels = ParseChannel(r.ReadLine());
}
static Vector3 ParseOffset(string line)
{
var splited = line.Trim().Split();
if (splited[0] != "OFFSET")
{
throw new BvhException("OFFSET is not found");
}
var offset = splited.Skip(1).Where(x => !string.IsNullOrEmpty(x)).Select(x => float.Parse(x)).ToArray();
return new Vector3(offset[0], offset[1], offset[2]);
}
static Channel[] ParseChannel(string line)
{
var splited = line.Trim().Split();
if (splited[0] != "CHANNELS")
{
throw new BvhException("CHANNELS is not found");
}
var count = int.Parse(splited[1]);
if (count + 2 != splited.Length)
{
throw new BvhException("channel count is not match with splited count");
}
return splited.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 descentant in child.Traverse())
{
yield return descentant;
}
}
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: ac87eb7c7e9da9c458c0601587ed0bc9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,135 +0,0 @@
using System;
using System.Collections.Generic;
namespace VrmLib.Diff
{
static class ObjectExtensions
{
public static bool IsNull<T>(this T self)
{
if (typeof(T).IsClass)
{
return self == null;
}
else
{
return false;
}
}
}
public struct ModelDiff
{
public string Context;
public string Message;
public override string ToString()
{
return $"{Context}: {Message}";
}
}
public struct ModelDiffContext
{
public readonly string Path;
public readonly List<ModelDiff> List;
ModelDiffContext(string path, List<ModelDiff> list)
{
Path = path;
List = list;
}
public bool Push<T>(T lhs, T rhs, Func<ModelDiffContext, T, T, bool> pred = null)
{
if (pred != null)
{
if (!pred(this, lhs, rhs))
{
List.Add(new ModelDiff
{
Context = Path,
Message = $"{lhs} != {rhs}",
});
return false;
}
return true;
}
if (!RequireComapre(lhs, rhs, out bool equals))
{
return equals;
}
if (lhs.Equals(rhs))
{
return true;
}
else
{
List.Add(new ModelDiff
{
Context = Path,
Message = $"{lhs} != {rhs}",
});
return false;
}
}
public ModelDiffContext Enter(string key)
{
if (string.IsNullOrEmpty(Path))
{
return new ModelDiffContext(key, List);
}
else
{
return new ModelDiffContext(Path + "." + key, List);
}
}
public static ModelDiffContext Create()
{
return new ModelDiffContext("", new List<ModelDiff>());
}
public bool RequireComapre(object lhs, object rhs, out bool equals)
{
if (lhs is null)
{
if (rhs is null)
{
equals = true;
return false;
}
else
{
equals = false;
List.Add(new ModelDiff
{
Context = Path,
Message = "lhs is null"
});
return false;
}
}
else
{
if (rhs is null)
{
equals = false;
List.Add(new ModelDiff
{
Context = Path,
Message = "rhs is null"
});
return false;
}
}
equals = false;
return true;
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 1cef47aa218fe78498b3bc0e698b817a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,540 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using VrmLib.MToon;
namespace VrmLib.Diff
{
public static class ModelDiffExtensions
{
/// <summary>
/// 違うところを集める(debug用)
/// </summary>
public static List<ModelDiff> Diff(this Model lhs, Model rhs)
{
var context = ModelDiffContext.Create();
context.Enter(nameof(lhs.AssetGenerator)).Push(lhs.AssetGenerator, rhs.AssetGenerator, StringEquals);
context.Enter(nameof(lhs.AssetVersion)).Push(lhs.AssetVersion, rhs.AssetVersion, StringEquals);
context.Enter(nameof(lhs.AssetMinVersion)).Push(lhs.AssetMinVersion, rhs.AssetMinVersion, StringEquals);
context.Enter(nameof(lhs.AssetCopyright)).Push(lhs.AssetCopyright, rhs.AssetCopyright, StringEquals);
// Materialの参照で比較する
ListDiff(context.Enter("Materials"), lhs.Materials, rhs.Materials, MaterialEquals);
ListDiff(context.Enter("Meshes"), lhs.MeshGroups, rhs.MeshGroups, MeshGroupEquals);
ListDiff(context.Enter("Nodes"), lhs.Nodes, rhs.Nodes, NodeEquals);
ListDiff(context.Enter("Skins"), lhs.Skins, rhs.Skins, SkinEquals);
Vrm(context.Enter("Vrm"), lhs, rhs);
return context.List;
}
#region Private
static bool ListDiff<T>(ModelDiffContext context, List<T> lhs, List<T> rhs, Func<ModelDiffContext, T, T, bool> pred, Func<T, int> order = null)
{
var equals = true;
if (lhs.Count != rhs.Count)
{
equals = false;
context.List.Add(new ModelDiff
{
Context = context.Path,
Message = $"{lhs.Count} != {rhs.Count}",
});
}
var l = order != null ? lhs.OrderBy(order).GetEnumerator() : lhs.GetEnumerator();
var r = order != null ? rhs.OrderBy(order).GetEnumerator() : rhs.GetEnumerator();
for (int i = 0; i < lhs.Count; ++i)
{
l.MoveNext();
r.MoveNext();
if (!pred(context.Enter($"{i}"), l.Current, r.Current))
equals = false;
}
return equals;
}
const float EPSILON = 1e-5f;
static bool Vector3NearlyEquals(ModelDiffContext _, Vector3 l, Vector3 r)
{
if (Math.Abs(l.X - r.X) > EPSILON) return false;
if (Math.Abs(l.Y - r.Y) > EPSILON) return false;
if (Math.Abs(l.Z - r.Z) > EPSILON) return false;
return true;
}
static bool QuaternionNearlyEquals(ModelDiffContext _, Quaternion l, Quaternion r)
{
if (Math.Abs(l.X - r.X) > EPSILON) return false;
if (Math.Abs(l.Y - r.Y) > EPSILON) return false;
if (Math.Abs(l.Z - r.Z) > EPSILON) return false;
if (Math.Abs(l.W - r.W) > EPSILON) return false;
return true;
}
static bool StringEquals(ModelDiffContext _, string l, string r)
{
if (string.IsNullOrEmpty(l))
{
return string.IsNullOrEmpty(r);
}
else
{
if (string.IsNullOrEmpty(r))
{
return false;
}
else
{
return l == r;
}
}
}
static bool ImageBytesEquals(ModelDiffContext context, Image lhs, Image rhs)
{
if (lhs is null)
{
if (rhs is null)
{
return true;
}
else
{
return false;
}
}
if (rhs is null)
{
return false;
}
return lhs.Bytes.SequenceEqual(rhs.Bytes);
}
static void Image(ModelDiffContext context, Image lhs, Image rhs)
{
context.Enter($"{lhs.Name}:{rhs.Name}").Push(lhs, rhs, ImageBytesEquals);
}
static bool TextureInfoEquals(ModelDiffContext context, TextureInfo lhs, TextureInfo rhs)
{
if (!context.RequireComapre(lhs, rhs, out bool equals))
{
return equals;
}
if (lhs.Offset != rhs.Offset)
{
return false;
}
if (lhs.Scaling != rhs.Scaling)
{
return false;
}
return TextureEquals(context.Enter("Texture"), lhs.Texture, rhs.Texture);
}
static bool TextureEquals(ModelDiffContext context, Texture lhs, Texture rhs)
{
if (!context.RequireComapre(lhs, rhs, out bool equals))
{
if (!equals)
return false;
return true;
}
equals = true;
if (!context.Enter("Name").Push(lhs.Name, rhs.Name, StringEquals))
equals = false;
if (!context.Enter("MagFilter").Push(lhs.Sampler.MagFilter, rhs.Sampler.MagFilter))
equals = false;
if (!context.Enter("MinFilter").Push(lhs.Sampler.MinFilter, rhs.Sampler.MinFilter))
equals = false;
if (!context.Enter("WrapS").Push(lhs.Sampler.WrapS, rhs.Sampler.WrapS))
equals = false;
if (!context.Enter("WrapT").Push(lhs.Sampler.WrapT, rhs.Sampler.WrapT))
equals = false;
if (lhs is ImageTexture l && rhs is ImageTexture r)
{
if (!ImageBytesEquals(context, l.Image, r.Image))
equals = false;
return equals;
}
else
{
return false;
}
}
static void Texture(ModelDiffContext context, Texture lhs, Texture rhs)
{
context.Enter($"{lhs.Name}:{rhs.Name}").Push(lhs, rhs, TextureEquals);
}
static bool BaseMaterialEquals(ModelDiffContext context, Material lhs, Material rhs)
{
var equals = true;
if (!context.Enter(nameof(lhs.AlphaCutoff)).Push(lhs.AlphaCutoff, rhs.AlphaCutoff)) equals = false;
if (!context.Enter(nameof(lhs.AlphaMode)).Push(lhs.AlphaMode, rhs.AlphaMode)) equals = false;
if (!context.Enter(nameof(lhs.BaseColorFactor)).Push(lhs.BaseColorFactor, rhs.BaseColorFactor)) equals = false;
if (!context.Enter(nameof(lhs.BaseColorTexture)).Push(lhs.BaseColorTexture?.Texture, rhs.BaseColorTexture?.Texture, TextureEquals)) equals = false;
if (!context.Enter(nameof(lhs.DoubleSided)).Push(lhs.DoubleSided, rhs.DoubleSided)) equals = false;
return equals;
}
static bool PBRMaterialEquals(ModelDiffContext context, PBRMaterial lhs, PBRMaterial rhs)
{
var equals = true;
if (!BaseMaterialEquals(context, lhs, rhs)) equals = false;
if (!context.Enter(nameof(lhs.EmissiveFactor)).Push(lhs.EmissiveFactor, rhs.EmissiveFactor)) equals = false;
if (!context.Enter(nameof(lhs.EmissiveTexture)).Push(lhs.EmissiveTexture, rhs.EmissiveTexture, TextureEquals)) equals = false;
if (!context.Enter(nameof(lhs.MetallicFactor)).Push(lhs.MetallicFactor, rhs.MetallicFactor)) equals = false;
if (!context.Enter(nameof(lhs.MetallicRoughnessTexture)).Push(lhs.MetallicRoughnessTexture, rhs.MetallicRoughnessTexture, TextureEquals)) equals = false;
if (!context.Enter(nameof(lhs.NormalTexture)).Push(lhs.NormalTexture, rhs.NormalTexture, TextureEquals)) equals = false;
if (!context.Enter(nameof(lhs.OcclusionTexture)).Push(lhs.OcclusionTexture, rhs.OcclusionTexture, TextureEquals)) equals = false;
if (!context.Enter(nameof(lhs.RoughnessFactor)).Push(lhs.RoughnessFactor, rhs.RoughnessFactor)) equals = false;
return equals;
}
static bool MToonDefinitionEquals(ModelDiffContext context, object lhs, object rhs, Type t)
{
if (!context.RequireComapre(lhs, rhs, out bool equals))
{
return equals;
}
equals = true;
foreach (var fi in t.GetFields())
{
if (fi.FieldType == typeof(TextureInfo))
{
if (!context.Enter(fi.Name).Push(fi.GetValue(lhs) as TextureInfo, fi.GetValue(rhs) as TextureInfo, TextureInfoEquals))
equals = false;
}
else
{
if (!context.Enter(fi.Name).Push(fi.GetValue(lhs), fi.GetValue(rhs)))
equals = false;
}
}
return equals;
}
static bool MToonDefinitionEquals(ModelDiffContext context, MToonDefinition lhs, MToonDefinition rhs)
{
var equals = true;
if (!MToonDefinitionEquals(context.Enter(nameof(MetaDefinition)), lhs?.Meta, rhs?.Meta, typeof(MetaDefinition)))
equals = false;
if (!MToonDefinitionEquals(context.Enter(nameof(ColorDefinition)), lhs?.Color, rhs?.Color, typeof(ColorDefinition)))
equals = false;
if (!MToonDefinitionEquals(context.Enter(nameof(OutlineDefinition)), lhs?.Outline, rhs?.Outline, typeof(OutlineDefinition)))
equals = false;
if (!MToonDefinitionEquals(context.Enter(nameof(LightingInfluenceDefinition)), lhs?.Lighting.LightingInfluence, rhs?.Lighting.LightingInfluence, typeof(LightingInfluenceDefinition)))
equals = false;
if (!MToonDefinitionEquals(context.Enter(nameof(LitAndShadeMixingDefinition)), lhs?.Lighting.LitAndShadeMixing, rhs?.Lighting.LitAndShadeMixing, typeof(LitAndShadeMixingDefinition)))
equals = false;
if (!MToonDefinitionEquals(context.Enter(nameof(EmissionDefinition)), lhs?.Emission, rhs?.Emission, typeof(EmissionDefinition)))
equals = false;
if (!MToonDefinitionEquals(context.Enter(nameof(MatCapDefinition)), lhs?.MatCap, rhs?.MatCap, typeof(MatCapDefinition)))
equals = false;
if (!MToonDefinitionEquals(context.Enter(nameof(RimDefinition)), lhs?.Rim, rhs?.Rim, typeof(RimDefinition)))
equals = false;
if (!MToonDefinitionEquals(context.Enter(nameof(TextureUvCoordsDefinition)), lhs?.TextureOption, rhs?.TextureOption, typeof(TextureUvCoordsDefinition)))
equals = false;
return equals;
}
static bool MToonMaterialEquals(ModelDiffContext context, MToonMaterial lhs, MToonMaterial rhs)
{
var equals = true;
if (!BaseMaterialEquals(context, lhs, rhs))
equals = false;
if (!context.Enter(nameof(lhs._DebugMode)).Push(lhs._DebugMode, rhs._DebugMode))
equals = false;
if (!context.Enter(nameof(lhs._DstBlend)).Push(lhs._DstBlend, rhs._DstBlend))
equals = false;
if (!context.Enter(nameof(lhs._SrcBlend)).Push(lhs._SrcBlend, rhs._SrcBlend))
equals = false;
if (!context.Enter(nameof(lhs._ZWrite)).Push(lhs._ZWrite, rhs._ZWrite))
equals = false;
if (!MToonDefinitionEquals(context.Enter(nameof(lhs.Definition)), lhs.Definition, rhs.Definition))
equals = false;
// context.Enter(nameof(lhs.KeyWords)).Push( lhs.KeyWords, rhs.KeyWords);
return equals;
}
static bool UnlitMaterialEquals(ModelDiffContext context, UnlitMaterial lhs, UnlitMaterial rhs)
{
return BaseMaterialEquals(context, lhs, rhs);
}
public static bool MaterialEquals(ModelDiffContext context, Material l, Material r)
{
var equals = true;
if (!context.Enter("Name").Push(l.Name, r.Name, StringEquals))
equals = false;
// context.Enter($"{i}:{lhs[i].Name}:{rhs[i].Name}").Push( lhs[i], rhs[i], MaterialEquals);
if (l.GetType() != r.GetType())
{
context.Enter($"Type").Push(l.GetType(), r.GetType());
equals = false;
}
else if (l is PBRMaterial lp && r is PBRMaterial rp)
{
if (!PBRMaterialEquals(context.Enter($"(PBRMaterial)"), lp, rp))
equals = false;
}
else if (l is MToonMaterial lm && r is MToonMaterial rm)
{
if (!MToonMaterialEquals(context.Enter($"(MToonMaterial)"), lm, rm))
equals = false;
}
else if (l is UnlitMaterial lu && r is UnlitMaterial ru)
{
if (!UnlitMaterialEquals(context.Enter($"(UnlitMaterial)"), lu, ru))
equals = false;
}
else
{
throw new Exception();
}
return equals;
}
static bool AccessorEquals(ModelDiffContext context, BufferAccessor lhs, BufferAccessor rhs)
{
if (!context.RequireComapre(lhs, rhs, out bool equals))
{
return equals;
}
return lhs.Bytes.SequenceEqual(rhs.Bytes);
}
static bool VertexBufferEquals(ModelDiffContext context, VertexBuffer lhs, VertexBuffer rhs)
{
var equals = true;
foreach (var kv in lhs)
{
rhs.TryGetValue(kv.Key, out BufferAccessor accessor);
if (!context.Enter(kv.Key).Push(kv.Value, accessor, AccessorEquals)) equals = false;
}
return equals;
}
static bool MeshEquals(ModelDiffContext context, Mesh lhs, Mesh rhs)
{
return VertexBufferEquals(context.Enter(nameof(lhs.VertexBuffer)), lhs.VertexBuffer, rhs.VertexBuffer);
}
static bool MeshGroupEquals(ModelDiffContext context, MeshGroup lhs, MeshGroup rhs)
{
return ListDiff(context.Enter("Meshes"), lhs.Meshes, rhs.Meshes, MeshEquals);
}
static bool NodeEquals(ModelDiffContext context, Node lhs, Node rhs)
{
if (lhs is null)
{
if (rhs is null)
{
return true;
}
else
{
return false;
}
}
else
{
if (rhs is null)
{
return false;
}
}
var equals = true;
if (!context.Enter(nameof(lhs.Name)).Push(lhs.Name, rhs.Name, StringEquals)) equals = false;
if (!context.Enter(nameof(lhs.LocalTranslation)).Push(lhs.LocalTranslation, rhs.LocalTranslation, Vector3NearlyEquals)) equals = false;
if (!context.Enter(nameof(lhs.LocalRotation)).Push(lhs.LocalRotation, rhs.LocalRotation, QuaternionNearlyEquals)) equals = false;
if (!context.Enter(nameof(lhs.LocalScaling)).Push(lhs.LocalScaling, rhs.LocalScaling, Vector3NearlyEquals)) equals = false;
if (!context.Enter(nameof(lhs.Parent)).Push(lhs.Parent?.Name, rhs.Parent?.Name)) equals = false;
if (!context.Enter(nameof(lhs.HumanoidBone)).Push(lhs.HumanoidBone, rhs.HumanoidBone)) equals = false;
return equals;
}
static bool SkinEquals(ModelDiffContext context, Skin lhs, Skin rhs)
{
var equals = true;
if (!context.Enter("Root").Push(lhs.Root, rhs.Root, NodeEquals)) equals = false;
if (!ListDiff(context.Enter("Joints"), lhs.Joints, rhs.Joints, NodeEquals)) equals = false;
if (!context.Enter("InverseMatrices").Push(lhs.InverseMatrices, rhs.InverseMatrices, AccessorEquals)) equals = false;
return equals;
}
static void Vrm(ModelDiffContext context, Model lhs, Model rhs)
{
context.Enter(nameof(lhs.Vrm.SpecVersion)).Push(lhs.Vrm.SpecVersion, rhs.Vrm.SpecVersion);
context.Enter(nameof(lhs.Vrm.ExporterVersion)).Push(lhs.Vrm.ExporterVersion, rhs.Vrm.ExporterVersion);
VrmMeta(context.Enter(nameof(lhs.Vrm.Meta)), lhs.Vrm.Meta, rhs.Vrm.Meta);
ListDiff(context.Enter(nameof(lhs.Vrm.ExpressionManager)), lhs.Vrm.ExpressionManager.ExpressionList, rhs.Vrm.ExpressionManager.ExpressionList, VrmExpressionEquals, x => (int)x.Preset);
VrmFirstPerson(context.Enter(nameof(lhs.Vrm.FirstPerson)), lhs.Vrm.FirstPerson, rhs.Vrm.FirstPerson);
VrmLookAt(context.Enter(nameof(lhs.Vrm.LookAt)), lhs.Vrm.LookAt, rhs.Vrm.LookAt);
ListDiff(context.Enter("SpringBone.Springs"), lhs.Vrm.SpringBone.Springs, rhs.Vrm.SpringBone.Springs, VrmSpringBoneEquals);
}
static void VrmMeta(ModelDiffContext context, Meta lhs, Meta rhs)
{
context.Enter(nameof(lhs.Name)).Push(lhs.Name, rhs.Name);
context.Enter(nameof(lhs.Version)).Push(lhs.Version, rhs.Version);
context.Enter(nameof(lhs.CopyrightInformation)).Push(lhs.CopyrightInformation, rhs.CopyrightInformation);
context.Enter(nameof(lhs.Author)).Push(lhs.Author, rhs.Author);
context.Enter(nameof(lhs.ContactInformation)).Push(lhs.ContactInformation, rhs.ContactInformation);
context.Enter(nameof(lhs.Reference)).Push(lhs.Reference, rhs.Reference);
context.Enter(nameof(lhs.Thumbnail)).Push(lhs.Thumbnail, rhs.Thumbnail, ImageBytesEquals);
// AvatarPermission
context.Enter(nameof(lhs.AvatarPermission.AvatarUsage)).Push(lhs.AvatarPermission.AvatarUsage, rhs.AvatarPermission.AvatarUsage);
context.Enter(nameof(lhs.AvatarPermission.IsAllowedViolentUsage)).Push(lhs.AvatarPermission.IsAllowedViolentUsage, rhs.AvatarPermission.IsAllowedViolentUsage);
context.Enter(nameof(lhs.AvatarPermission.IsAllowedSexualUsage)).Push(lhs.AvatarPermission.IsAllowedSexualUsage, rhs.AvatarPermission.IsAllowedSexualUsage);
context.Enter(nameof(lhs.AvatarPermission.IsAllowedCommercialUsage)).Push(lhs.AvatarPermission.IsAllowedCommercialUsage, rhs.AvatarPermission.IsAllowedCommercialUsage);
context.Enter(nameof(lhs.AvatarPermission.CommercialUsage)).Push(lhs.AvatarPermission.CommercialUsage, rhs.AvatarPermission.CommercialUsage);
context.Enter(nameof(lhs.AvatarPermission.IsAllowedCommercialUsage)).Push(lhs.AvatarPermission.IsAllowedCommercialUsage, rhs.AvatarPermission.IsAllowedCommercialUsage);
context.Enter(nameof(lhs.AvatarPermission.IsAllowedCommercialUsage)).Push(lhs.AvatarPermission.IsAllowedCommercialUsage, rhs.AvatarPermission.IsAllowedCommercialUsage);
context.Enter(nameof(lhs.AvatarPermission.OtherPermissionUrl)).Push(lhs.AvatarPermission.OtherPermissionUrl, rhs.AvatarPermission.OtherPermissionUrl);
// RedistributionLicense
context.Enter(nameof(lhs.RedistributionLicense.License)).Push(lhs.RedistributionLicense.License, rhs.RedistributionLicense.License);
context.Enter(nameof(lhs.RedistributionLicense.OtherLicenseUrl)).Push(lhs.RedistributionLicense.OtherLicenseUrl, rhs.RedistributionLicense.OtherLicenseUrl);
}
static bool VrmExpressionEquals(ModelDiffContext context, Expression lhs, Expression rhs)
{
if (lhs.IsNull())
{
if (rhs.IsNull())
{
// ok
return true;
}
else
{
context.List.Add(new ModelDiff
{
Context = context.Path,
Message = "lhs is null",
});
return false;
}
}
else
{
if (rhs.IsNull())
{
context.List.Add(new ModelDiff
{
Context = context.Path,
Message = "rhs is null",
});
return false;
}
}
var equals = true;
if (!context.Enter(nameof(lhs.Preset)).Push(lhs.Preset, rhs.Preset)) equals = false;
if (!context.Enter(nameof(lhs.Name)).Push(lhs.Name, rhs.Name, StringEquals)) equals = false;
if (!context.Enter(nameof(lhs.IsBinary)).Push(lhs.IsBinary, rhs.IsBinary)) equals = false;
if (!ListDiff(context.Enter(nameof(lhs.MorphTargetBinds)), lhs.MorphTargetBinds, rhs.MorphTargetBinds, VrmExpressionBindValueEquals)) equals = false;
if (!ListDiff(context.Enter(nameof(lhs.MaterialColorBinds)), lhs.MaterialColorBinds, rhs.MaterialColorBinds, VrmMaterialBindValueEquals)) equals = false;
return equals;
}
static bool VrmExpressionBindValueEquals(ModelDiffContext context, MorphTargetBind lhs, MorphTargetBind rhs)
{
var equals = true;
if (!context.Enter("Node").Push(lhs.Node, rhs.Node, NodeEquals)) equals = false;
if (!context.Enter("Name").Push(lhs.Name, rhs.Name)) equals = false;
if (!context.Enter("Value").Push(lhs.Value, rhs.Value)) equals = false;
return equals;
}
static bool VrmMaterialBindValueEquals(ModelDiffContext context, MaterialColorBind lhs, MaterialColorBind rhs)
{
var equals = true;
if (!context.Enter("Material.Name").Push(lhs.Material.Name, rhs.Material.Name)) equals = false;
if (!context.Enter("Property").Push(lhs.Property, rhs.Property)) equals = false;
// if (!context.Enter("Value").Push(lhs.m_value, rhs.m_value)) equals = false;
if (!context.Enter("BindType").Push(lhs.BindType, rhs.BindType)) equals = false;
return equals;
}
static bool FirstPersonMeshAnnotationEquals(ModelDiffContext context, FirstPersonMeshAnnotation lhs, FirstPersonMeshAnnotation rhs)
{
var equals = true;
if (!context.Enter("Node").Push(lhs.Node, rhs.Node, NodeEquals)) equals = false;
if (!context.Enter("Flag").Push(lhs.FirstPersonFlag, rhs.FirstPersonFlag)) equals = false;
return equals;
}
static void VrmFirstPerson(ModelDiffContext context, FirstPerson lhs, FirstPerson rhs)
{
// context.Enter("HeadNode").Push(lhs.m_fp, rhs.m_fp, NodeEquals);
ListDiff(context.Enter("Annotations"), lhs.Annotations, rhs.Annotations, FirstPersonMeshAnnotationEquals);
}
static void VrmLookAt(ModelDiffContext context, LookAt lhs, LookAt rhs)
{
context.Enter("Offset").Push(lhs.OffsetFromHeadBone, rhs.OffsetFromHeadBone, Vector3NearlyEquals);
context.Enter(nameof(lhs.LookAtType)).Push(lhs.LookAtType, rhs.LookAtType);
VrmLookAtRangeMap(context.Enter(nameof(lhs.HorizontalInner)), lhs.HorizontalInner, rhs.HorizontalInner);
VrmLookAtRangeMap(context.Enter(nameof(lhs.HorizontalOuter)), lhs.HorizontalOuter, rhs.HorizontalOuter);
VrmLookAtRangeMap(context.Enter(nameof(lhs.VerticalUp)), lhs.VerticalUp, rhs.VerticalUp);
VrmLookAtRangeMap(context.Enter(nameof(lhs.VerticalDown)), lhs.VerticalDown, rhs.VerticalDown);
}
static void VrmLookAtRangeMap(ModelDiffContext context, LookAtRangeMap lhs, LookAtRangeMap rhs)
{
context.Enter(nameof(lhs.InputMaxValue)).Push(lhs.InputMaxValue, rhs.InputMaxValue);
context.Enter(nameof(lhs.OutputScaling)).Push(lhs.OutputScaling, rhs.OutputScaling);
context.Enter("Curve").Push(lhs.Curve, rhs.Curve);
}
static bool VrmSpringBoneJointEquals(ModelDiffContext context, SpringJoint lhs, SpringJoint rhs)
{
var equals = true;
if (!context.Enter("DragForce").Push(lhs.DragForce, rhs.DragForce)) equals = false;
if (!context.Enter("GravityDir").Push(lhs.GravityDir, rhs.GravityDir)) equals = false;
if (!context.Enter("GravityPower").Push(lhs.GravityPower, rhs.GravityPower)) equals = false;
if (!context.Enter("HitRadius").Push(lhs.HitRadius, rhs.HitRadius)) equals = false;
if (!context.Enter("Stiffness").Push(lhs.Stiffness, rhs.Stiffness)) equals = false;
return equals;
}
static bool VrmSpringBoneEquals(ModelDiffContext context, SpringBone lhs, SpringBone rhs)
{
var equals = true;
if (!context.Enter("Comment").Push(lhs.Comment, rhs.Comment)) equals = false;
if (!context.Enter("Origin").Push(lhs.Origin, rhs.Origin)) equals = false;
if (!ListDiff(context.Enter("Joint"), lhs.Joints, rhs.Joints, VrmSpringBoneJointEquals)) equals = false;
return equals;
}
static bool VrmSpringBoneColliderEquals(ModelDiffContext context, VrmSpringBoneCollider lhs, VrmSpringBoneCollider rhs)
{
var equals = true;
if (!context.Enter("Offset").Push(lhs.Offset, rhs.Offset)) equals = false;
if (!context.Enter("Radius").Push(lhs.Radius, rhs.Radius)) equals = false;
return equals;
}
static bool VrmSpringBoneColliderEquals(ModelDiffContext context, SpringBoneColliderGroup lhs, SpringBoneColliderGroup rhs)
{
var equals = true;
if (!context.Enter("Node").Push(lhs.Node, rhs.Node, NodeEquals)) equals = false;
if (!ListDiff(context.Enter("Colliders"), lhs.Colliders, rhs.Colliders, VrmSpringBoneColliderEquals)) equals = false;
return equals;
}
#endregion
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 93cf70a1afdd7784e8c964726d98edd6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,303 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using VrmLib.Bvh;
namespace VrmLib
{
public static class ModelExtensionsForBvh
{
static float ToRad(float src)
{
return src / 180.0f * MathFWrap.PI;
}
static BvhNode GetNode(BvhNode root, string path)
{
var splitted = path.Split('/');
var it = splitted.Select(x => x).GetEnumerator();
var current = root;
if (splitted[0] == path)
{
return current;
}
it.MoveNext();
while (it.MoveNext())
{
current = current.Children.First(x => x.Name == it.Current);
}
return current;
}
public static Model CreateFromBvh(BvhNode node)
{
// add nodes
var model = new Model(Coordinates.Vrm1);
model.Root.Name = "__bvh_root__";
AddBvhNodeRecursive(model, model.Root, node);
return model;
}
static void AddBvhNodeRecursive(Model model, Node parent, BvhNode node)
{
var newNode = new Node(node.Name)
{
HumanoidBone = node.Bone,
};
model.Nodes.Add(newNode);
parent.Add(newNode);
newNode.Translation = node.SkeletonLocalPosition;
foreach (var child in node.Children)
{
AddBvhNodeRecursive(model, newNode, child);
}
}
class BvhNodeCurves
{
public Bvh.ChannelCurve LocalPositionX;
public Bvh.ChannelCurve LocalPositionY;
public Bvh.ChannelCurve LocalPositionZ;
public Bvh.ChannelCurve EulerX;
public Bvh.ChannelCurve EulerY;
public Bvh.ChannelCurve EulerZ;
public void Set(string prop, Bvh.ChannelCurve curve)
{
switch (prop)
{
case "localPosition.x":
LocalPositionX = curve;
break;
case "localPosition.y":
LocalPositionY = curve;
break;
case "localPosition.z":
LocalPositionZ = curve;
break;
case "localEulerAnglesBaked.x":
EulerX = curve;
break;
case "localEulerAnglesBaked.y":
EulerY = curve;
break;
case "localEulerAnglesBaked.z":
EulerZ = curve;
break;
default:
break;
}
}
}
static Animation LoadAnimation(string name, Bvh.Bvh bvh, Model model, float scalingFactor)
{
var animation = new Animation(name);
Dictionary<string, BvhNodeCurves> pathMap = new Dictionary<string, BvhNodeCurves>();
for (int i = 0; i < bvh.Channels.Length; ++i)
{
var channel = bvh.Channels[i];
if (!bvh.TryGetPathWithPropertyFromChannel(channel, out Bvh.Bvh.PathWithProperty prop))
{
throw new Exception();
}
if (!pathMap.TryGetValue(prop.Path, out BvhNodeCurves curves))
{
curves = new BvhNodeCurves();
pathMap.Add(prop.Path, curves);
}
curves.Set(prop.Property, channel);
}
// setup time
var timeBytes = new byte[Marshal.SizeOf(typeof(float)) * bvh.FrameCount];
var timeSpan = SpanLike.Wrap<Single>(new ArraySegment<byte>(timeBytes));
var now = 0.0;
for (int i = 0; i < timeSpan.Length; ++i, now += bvh.FrameTime.TotalSeconds)
{
timeSpan[i] = (float)now;
}
var times = new BufferAccessor(new ArraySegment<byte>(timeBytes), AccessorValueType.FLOAT, AccessorVectorType.SCALAR, bvh.FrameCount);
foreach (var (key, nodeCurve) in pathMap)
{
var node = Model.GetNode(model.Root, key);
var bvhNode = GetNode(bvh.Root, key);
var curve = new NodeAnimation();
if (nodeCurve.LocalPositionX != null)
{
var values = new byte[Marshal.SizeOf(typeof(Vector3))
* nodeCurve.LocalPositionX.Keys.Length];
var span = SpanLike.Wrap<Vector3>(new ArraySegment<byte>(values));
for (int i = 0; i < nodeCurve.LocalPositionX.Keys.Length; ++i)
{
span[i] = new Vector3
{
X = nodeCurve.LocalPositionX.Keys[i] * scalingFactor,
Y = nodeCurve.LocalPositionY.Keys[i] * scalingFactor,
Z = nodeCurve.LocalPositionZ.Keys[i] * scalingFactor,
};
}
var sampler = new CurveSampler
{
In = times,
Out = new BufferAccessor(new ArraySegment<byte>(values),
AccessorValueType.FLOAT, AccessorVectorType.VEC3, span.Length)
};
curve.Curves.Add(AnimationPathType.Translation, sampler);
}
if (nodeCurve.EulerX != null)
{
var values = new byte[Marshal.SizeOf(typeof(Quaternion))
* nodeCurve.EulerX.Keys.Length];
var span = SpanLike.Wrap<Quaternion>(new ArraySegment<byte>(values));
Func<Quaternion, BvhNodeCurves, int, Quaternion> getRot = (q, c, i) => q;
foreach (var ch in bvhNode.Channels)
{
var tmp = getRot;
switch (ch)
{
case Channel.Xrotation:
getRot = (_, c, i) =>
{
return tmp(_, c, i) *
Quaternion.CreateFromAxisAngle(Vector3.UnitX, ToRad(c.EulerX.Keys[i]));
};
break;
case Channel.Yrotation:
getRot = (_, c, i) =>
{
return tmp(_, c, i) *
Quaternion.CreateFromAxisAngle(Vector3.UnitY, ToRad(c.EulerY.Keys[i]));
};
break;
case Channel.Zrotation:
getRot = (_, c, i) =>
{
return tmp(_, c, i) *
Quaternion.CreateFromAxisAngle(Vector3.UnitZ, ToRad(c.EulerZ.Keys[i]));
};
break;
default:
// throw new NotImplementedException();
break;
}
}
for (int i = 0; i < nodeCurve.EulerX.Keys.Length; ++i)
{
span[i] = getRot(Quaternion.Identity, nodeCurve, i);
}
var sampler = new CurveSampler
{
In = times,
Out = new BufferAccessor(new ArraySegment<byte>(values),
AccessorValueType.FLOAT, AccessorVectorType.VEC4, span.Length)
};
curve.Curves.Add(AnimationPathType.Rotation, sampler);
}
animation.AddCurve(node, curve);
}
return animation;
}
public static Model Load(string name, Bvh.Bvh bvh)
{
var model = CreateFromBvh(bvh.Root);
// estimate skeleton
var skeleton = SkeletonEstimator.Detect(model.Root);
if (skeleton == null)
{
throw new Exception("fail to estimate skeleton");
}
// foot to zero
var minY = model.Nodes.Min(x => x.Translation.Y);
var hips = model.Nodes.First(x => x.HumanoidBone == HumanoidBones.hips);
if (model.Root.Children.Count != 1)
{
throw new Exception();
}
if (model.Root.Children[0] != hips)
{
throw new Exception();
}
hips.Translation -= new Vector3(0, minY, 0);
// normalize scale
var pos = hips.Translation;
var factor = 1.0f;
if (pos.Y != 0)
{
factor = 1.0f / pos.Y;
foreach (var x in hips.Traverse())
{
x.LocalTranslation *= factor;
}
hips.Translation = new Vector3(pos.X, 1.0f, pos.Z);
}
// animation
model.Animations.Add(LoadAnimation(name, bvh, model, factor));
// add origin
var origin = new Node("origin");
origin.Add(model.Root.Children[0]);
model.Nodes.Add(origin);
model.Root.Add(origin);
return model;
}
public static void CreateBoxMan(this Model model)
{
// skin
var skin = new Skin();
skin.Joints.AddRange(model.Nodes);
skin.CalcInverseMatrices();
// mesh
var group = new MeshGroup("box-man")
{
Skin = skin,
};
var builder = new MeshBuilder();
builder.Build(model.Nodes);
group.Meshes.Add(builder.CreateMesh());
model.MeshGroups.Add(group);
// node
var meshNode = new Node("mesh");
meshNode.MeshGroup = group;
model.Nodes.Add(meshNode);
model.Root.Add(meshNode);
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 13b0cacd23e61e1468b9b9891e0e6656
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,193 +0,0 @@
using System;
using System.Linq;
using System.Numerics;
namespace VrmLib
{
public static class ModelExtensionsForHumanoid
{
static ValueTuple<Node, Node> GetUpperLower(Node root)
{
var legL = root.Traverse().FirstOrDefault(x => x.HumanoidBone == HumanoidBones.leftUpperLeg);
var legR = root.Traverse().FirstOrDefault(x => x.HumanoidBone == HumanoidBones.rightUpperLeg);
var head = root.Traverse().FirstOrDefault(x => x.HumanoidBone == HumanoidBones.head);
var parentL = legL.Parent;
var parentR = legR.Parent;
if (parentL != parentR)
{
throw new Exception("different leftLeg parent and rightLeg parent");
}
var lower = parentL;
var upperAncestors = head.Ancestors().ToList();
if (upperAncestors.Any(x => x == lower))
{
throw new Exception("lower is ancestor of head");
}
var lowerAncestors = legL.Ancestors().ToList();
while (true)
{
if (upperAncestors.Last() != lowerAncestors.Last())
{
break;
}
upperAncestors.RemoveAt(upperAncestors.Count - 1);
lowerAncestors.RemoveAt(lowerAncestors.Count - 1);
}
return (upperAncestors.Last(), lowerAncestors.Last());
}
/// <summary>
///
/// root
/// upper
/// lower
/// legL
/// legR
///
/// ↓
///
/// ①上半身をchestにする例
///
/// root(hips)
/// legL
/// legR
/// lower(spine: 上下反転するため頭の位置が変わる)
/// upper(chest)
///
/// ②上半身をspineにする例もありえる。その場合は、下半身とその親を近接させて
/// 下半身にはhumanoidボーンを割り当てない(hipsとみなす)
///
/// root(hips)
/// legL
/// legR
/// lower(上下反転するため頭の位置が変わる)
/// upper(spine)
///
/// ③もしくは下半身をhipsに繰り上げる
///
/// lower(hips: 上下反転するため頭の位置が変わる)
/// legL
/// legR
/// upper(spine)
///
/// </summary>
public static string FixInvertedPelvis(this Model model)
{
var (upper, lower) = GetUpperLower(model.Root);
if (upper == null)
{
return "FixInvertedPelvis: upper not found. this is not humanoid ? do nothing";
}
if (lower == null)
{
return "FixInvertedPelvis: lower not found. this is model's pelvis is not inverted. do nothing";
}
// found lower. fix inverted pelvis...
var hips = model.Root.FindBone(HumanoidBones.hips);
{
hips.HumanoidBone = null;
}
var spine = model.Root.FindBone(HumanoidBones.spine);
{
spine.HumanoidBone = null;
}
var chest = model.Root.FindBone(HumanoidBones.chest);
if (chest != null)
{
chest.HumanoidBone = null;
}
var legL = model.Root.FindBone(HumanoidBones.leftUpperLeg);
var legR = model.Root.FindBone(HumanoidBones.rightUpperLeg);
// [chest]
// 上半身を下半身の子にしてchestとなす
lower.Add(upper);
upper.HumanoidBone = HumanoidBones.chest;
// [hips]
// lowerの親をhipsとして両足の間に配置する
var newHips = lower.Parent;
newHips.Translation = new Vector3(0, legL.Translation.Y, legL.Translation.Z);
newHips.HumanoidBone = HumanoidBones.hips;
// [spine]
// 下半身をspineとして
lower.HumanoidBone = HumanoidBones.spine;
// hips と chest の中間に配置する
lower.Translation = (upper.Translation + hips.Translation) * 0.5f;
// [legs]
// 足の親を下半身からrootに変える
hips.Add(legL);
hips.Add(legR);
return $"FixInvertedPelvis: lower: {lower.Name}";
}
static void StringBuilder(System.Text.StringBuilder sb, Node n, string indent = "")
{
sb.Append($"{indent}{n}\n");
foreach (var child in n.Children)
{
StringBuilder(sb, child, indent + " ");
}
}
public static string HumanoidBoneEstimate(this Model model)
{
var sb = new System.Text.StringBuilder();
sb.Append("HumanoidBoneEstimate: ");
// estimate skeleton
var skeleton = SkeletonEstimator.Detect(model.Root);
if (skeleton == null)
{
return "fail to estimate skeleton";
}
// rename bone
foreach (var kv in skeleton)
{
kv.Value.Name = kv.Key.ToString();
}
if (model.Vrm == null)
{
sb.Append("add vrm humanoid");
model.Vrm = new Vrm(new Meta
{
}, "UniVRM-0.51.0", "0.0");
}
else
{
}
StringBuilder(sb, skeleton[HumanoidBones.hips]);
foreach (var skin in model.Skins)
{
if (skin.Root == null)
{
skin.Root = (Node)skeleton[HumanoidBones.hips].Parent;
sb.Append($"{skin}: set {skin.Root}\n");
}
else
{
sb.Append($"{skin}: {skin.Root}\n");
}
}
return sb.ToString(); ;
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: e290d6e2d1cfe684488ad5504f0ce8dc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,132 +0,0 @@
using System.Linq;
namespace VrmLib
{
public static class ModelExtensionsForSingleMesh
{
///
/// 各ノードのスキニングで使用されている回数
///
public static int[] GetNodeSkinUseCount(Model model)
{
// create new skin
var useCountList = new int[model.Nodes.Count];
foreach (var n in model.Root.Traverse().Skip(1))
{
var g = n.MeshGroup;
if (g == null)
{
continue;
}
if (g.Skin == null)
{
// Skin無し。そのMeshに乗る
var index = model.Nodes.IndexOf(n);
++useCountList[index];
}
else
{
// Skinあり。VertexBufferの JOINT_0 と WEIGHT_0 を見る
var skinJoints = g.Skin.Joints;
foreach (var m in g.Meshes)
{
var joints = m.VertexBuffer.GetOrCreateJoints();
var weights = m.VertexBuffer.GetOrCreateWeights();
for (int i = 0; i < joints.Length; ++i)
{
var j = joints[i];
var w = weights[i];
if (w.X > 0)
{
var node = skinJoints[j.Joint0];
var index = model.Nodes.IndexOf(node);
++useCountList[index];
}
if (w.Y > 0)
{
var node = skinJoints[j.Joint1];
var index = model.Nodes.IndexOf(node);
++useCountList[index];
}
if (w.Z > 0)
{
var node = skinJoints[j.Joint2];
var index = model.Nodes.IndexOf(node);
++useCountList[index];
}
if (w.W > 0)
{
var node = skinJoints[j.Joint3];
var index = model.Nodes.IndexOf(node);
++useCountList[index];
}
}
}
}
}
return useCountList;
}
/// <summary>
/// Integrate meshes to a single mesh
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public static MeshGroup CreateSingleMesh(this Model model, string name)
{
// new mesh to store result
var meshGroup = new MeshGroup(name);
var mesh = new Mesh
{
VertexBuffer = new VertexBuffer()
};
meshGroup.Meshes.Add(mesh);
var useCountList = GetNodeSkinUseCount(model);
// new Skin.
// Joints has include all joint
meshGroup.Skin = new Skin();
for (int i = 0; i < useCountList.Length; ++i)
{
if (useCountList[i] > 0)
{
// add joint that has bone weight
meshGroup.Skin.Joints.Add(model.Nodes[i]);
}
}
model.Skins.Clear();
model.Skins.Add(meshGroup.Skin);
// concatenate all mesh
foreach (var node in model.Root.Traverse().Skip(1))
{
var g = node.MeshGroup;
if (g != null)
{
foreach (var m in g.Meshes)
{
if (g.Skin != null && m.VertexBuffer.Joints != null && m.VertexBuffer.Weights != null)
{
var jointIndexMap = g.Skin.Joints.Select(x => meshGroup.Skin.Joints.IndexOf(x)).ToArray();
mesh.Append(m.VertexBuffer, m.IndexBuffer, m.Submeshes, m.MorphTargets, jointIndexMap);
}
else
{
var rootIndex = meshGroup.Skin.Joints.IndexOf(node);
mesh.Append(m.VertexBuffer, m.IndexBuffer, m.Submeshes, m.MorphTargets, null, rootIndex, node.Matrix);
}
}
}
}
foreach (var target in mesh.MorphTargets)
{
target.VertexBuffer.Resize(mesh.VertexBuffer.Count);
}
return meshGroup;
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: ff27373084460f24b8866d4853a9acda
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,72 +0,0 @@
using System;
using System.Linq;
namespace VrmLib
{
public static class ModelExtensionsForValidation
{
public static void Validate(this Model model, Node node, string message)
{
if (node is null)
{
throw new ArgumentNullException(message);
}
if (!model.Nodes.Contains(node))
{
throw new ArgumentException($"{message}: node found in nodes");
}
}
public static void Validate(this Model model)
{
foreach (var node in model.Root.Traverse().Skip(1))
{
model.Validate(node, "nodes must Contains node");
}
foreach (var skin in model.Skins)
{
foreach (var joint in skin.Joints)
{
model.Validate(joint, "nodes must Contatins joint");
}
}
if (model.Vrm != null)
{
if (model.Vrm.ExpressionManager != null)
{
foreach (var b in model.Vrm.ExpressionManager.ExpressionList)
{
foreach (var v in b.MorphTargetBinds)
{
model.Validate(v.Node, "MorphTargetBindValue.Node is null");
}
}
}
if (model.Vrm.FirstPerson != null)
{
foreach (var a in model.Vrm.FirstPerson.Annotations)
{
model.Validate(a.Node, "FirstPersonMeshAnnotation.Node is null");
}
}
var humanDict = model.Root.Traverse()
.Where(x => x.HumanoidBone.HasValue)
.ToDictionary(x => x.HumanoidBone.Value, x => x);
foreach (var required in new[]{
HumanoidBones.hips,
})
{
if (!humanDict.ContainsKey(required))
{
throw new Exception($"no {required}");
}
}
}
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: e7f73e8a96ec0774dadaa1f526c80210
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,365 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
namespace VrmLib
{
public static class ModelModifierExtensions
{
public static bool TryAdd<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue addValue)
{
bool canAdd = !dict.ContainsKey(key);
if (canAdd)
dict.Add(key, addValue);
return canAdd;
}
public static TValue GetValueOrDefault<TKey, TValue>(this System.Collections.Generic.IReadOnlyDictionary<TKey, TValue> dictionary, TKey key)
{
if (dictionary.TryGetValue(key, out TValue value))
{
return value;
}
else
{
return default;
}
}
static void ReplaceMorphTargetAnimationNode(IEnumerable<Animation> animations, Node dst)
{
foreach (var animation in animations)
{
var dstAnimation = animation.GetOrCreateNodeAnimation(dst);
foreach (var (node, nodeAnimation) in animation.NodeMap)
{
if (nodeAnimation.Curves.TryGetValue(AnimationPathType.Weights, out CurveSampler curve))
{
// remove
nodeAnimation.Curves.Remove(AnimationPathType.Weights);
// add
if (!dstAnimation.Curves.TryAdd(AnimationPathType.Weights, curve))
{
Console.Error.WriteLine($"already exists. skip: {node.Name}: {nodeAnimation}");
}
}
}
}
}
/// Expression
/// FirstPersonの置き換え
public static void MeshNodeReplace(this ModelModifier modifier, Node src, Node dst)
{
var vrm = modifier.Model.Vrm;
if (vrm is null)
{
return;
}
if (vrm.ExpressionManager != null)
{
foreach (var b in vrm.ExpressionManager.ExpressionList)
{
foreach (var v in b.MorphTargetBinds)
{
if (v.Node == src)
{
v.Node = dst;
}
}
}
}
if (vrm.FirstPerson != null)
{
foreach (var a in vrm.FirstPerson.Annotations)
{
if (a.Node == src)
{
a.Node = dst;
}
}
}
}
public static string SingleMesh(this ModelModifier modifier, string name)
{
var count = modifier.Model.MeshGroups.Sum(x => x.Meshes.Count);
var meshes = modifier.Model.Root.Traverse()
.Select(x => x.MeshGroup)
.Where(x => x != null)
.Select(x => $"[{x.Name}]")
.ToArray();
if (meshes.Length == 0)
{
return "SingleMesh: no mesh. do nothing";
}
if (meshes.Length <= 1)
{
return "SingleMesh: one mesh. do nothing";
}
var mesh = modifier.Model.CreateSingleMesh(name);
var meshNode = new Node(mesh.Name)
{
MeshGroup = mesh,
};
mesh.Skin.Root = meshNode;
// fix bone weight (0, x, 0, 0) => (x, 0, 0, 0)
// mesh.Meshes[0].VertexBuffer.FixBoneWeight();
// replace morphAnimation reference
ReplaceMorphTargetAnimationNode(modifier.Model.Animations, meshNode);
// update Model
foreach (var x in modifier.Model.MeshGroups.ToArray())
{
modifier.MeshReplace(x, mesh);
}
foreach (var node in modifier.Model.Nodes)
{
if (node.MeshGroup != null)
{
node.MeshGroup = null;
modifier.MeshNodeReplace(node, meshNode);
}
}
modifier.NodeAdd(meshNode);
var names = string.Join("", meshes);
// return $"SingleMesh: {names}";
return $"SingleMesh: {count} => {modifier.Model.MeshGroups.Sum(x => x.Meshes.Count)}";
}
public static void SepareteByMorphTarget(this ModelModifier modifier, MeshGroup mesh)
{
var (with, without) = mesh.SepareteByMorphTarget();
var list = new List<MeshGroup>();
if (with != null) list.Add(with);
if (without != null) list.Add(without);
// 分割モデルで置き換え
if (list.Any())
{
modifier.MeshReplace(mesh, list[0]);
// rename node
modifier.Model.Nodes.Find(x => x.MeshGroup == list[0]).Name = list[0].Name;
}
if (list.Count > 1)
{
// morph無しと有り両方存在する場合に2つ目を追加する
modifier.MeshReplace(null, list[1]);
modifier.NodeAdd(new Node(list[1].Name)
{
MeshGroup = list[1]
});
}
}
public static void SepareteByHeadBone(this ModelModifier modifier, MeshGroup mesh, HashSet<int> boneIndices)
{
var (with, without) = mesh.SepareteByHeadBone(boneIndices);
var list = new List<MeshGroup>();
if (with != null) list.Add(with);
if (without != null) list.Add(without);
// 分割モデルで置き換え
if (list.Any())
{
modifier.MeshReplace(mesh, list[0]);
// rename node
modifier.Model.Nodes.Find(x => x.MeshGroup == list[0]).Name = list[0].Name;
}
if (list.Count > 1)
{
// 頭と胴体で分割後2つ以上ある場合、2つ目を追加する
modifier.MeshReplace(null, list[1]);
modifier.NodeAdd(new Node(list[1].Name)
{
MeshGroup = list[1]
});
}
}
public static string NodeReduce(this ModelModifier modifier)
{
var count = modifier.Model.Nodes.Count;
var removeNames = new List<string>();
// ノードを削除する
foreach (var node in modifier.Model.GetRemoveNodes())
{
modifier.NodeRemove(node);
removeNames.Add($"[{node.Name}]");
foreach (var skin in modifier.Model.Skins)
{
var index = skin.Joints.IndexOf(node);
if (index != -1)
{
// remove
skin.Joints[index] = null;
}
}
}
// 削除されたノードを参照する頂点バッファを修正する
foreach (var meshGroup in modifier.Model.MeshGroups)
{
var skin = meshGroup.Skin;
if (skin != null && skin.Joints.Contains(null))
{
foreach (var mesh in meshGroup.Meshes)
{
skin.FixBoneWeight(mesh.VertexBuffer.Joints, mesh.VertexBuffer.Weights);
}
}
}
var joined = string.Join("", removeNames);
return $"NodeReduce: {count} => {modifier.Model.Nodes.Count}";
// return $"NodeReduce: {joined}";
}
public static string SkinningBake(this ModelModifier modifier)
{
foreach (var node in modifier.Model.Nodes)
{
var meshGroup = node.MeshGroup;
if (meshGroup == null)
{
continue;
}
if (meshGroup.Skin != null)
{
// 正規化されていれば1つしかない
// されていないと Primitive の数だけある
foreach (var mesh in meshGroup.Meshes)
{
{
// Skinningの出力先を自身にすることでBakeする
meshGroup.Skin.Skinning(mesh.VertexBuffer);
}
// morphのPositionは相対値が入っているはずなので、手を加えない正規化されていない場合、二重に補正が掛かる
/*
foreach (var morph in mesh.MorphTargets)
{
if (morph.VertexBuffer.Positions != null)
{
meshGroup.Skin.Skinning(morph.VertexBuffer);
}
}
*/
}
meshGroup.Skin.Root = null;
meshGroup.Skin.InverseMatrices = null;
}
else
{
foreach (var mesh in meshGroup.Meshes)
{
// nodeに対して疑似的にSkinningする
// 回転と拡縮を適用し位置は適用しない
mesh.ApplyRotationAndScaling(node.Matrix);
}
}
}
// 回転・拡縮を除去する
modifier.Model.ApplyRotationAndScale();
// inverse matrix の再計算
foreach (var node in modifier.Model.Nodes)
{
var meshGroup = node.MeshGroup;
if (meshGroup == null)
{
continue;
}
foreach (var mesh in meshGroup.Meshes)
{
if (meshGroup.Skin != null)
{
meshGroup.Skin.CalcInverseMatrices();
}
}
}
return "SkinningBake";
}
public static string CloneSharedMesh(this ModelModifier modifier)
{
Dictionary<MeshGroup, int> m_useMap = new Dictionary<MeshGroup, int>();
var cloned = new List<string>();
foreach (var node in modifier.Model.Nodes)
{
if (node.MeshGroup == null)
{
continue;
}
var n = m_useMap.GetValueOrDefault(node.MeshGroup);
if (n > 0)
{
// copy
node.MeshGroup = node.MeshGroup.Clone();
cloned.Add($"[{node.MeshGroup.Name}]");
}
m_useMap[node.MeshGroup] = n + 1;
}
if (!cloned.Any())
{
return "CloneSharedMesh: no shared mesh. do nothing";
}
else
{
var joined = string.Join("", cloned);
return $"CloneSharedMesh: copy {joined}";
}
}
public static string MaterialIntegrate(this ModelModifier modifier)
{
var sb = new System.Text.StringBuilder();
var materials = new List<Material>();
foreach (var material in modifier.Model.Materials.ToArray())
{
var found = materials.FirstOrDefault(x => x.CanIntegrate(material));
if (found != null)
{
// merge
modifier.MaterialReplace(material, found);
}
else
{
// add
materials.Add(material);
}
}
sb.Append($"MaterialIntegrate: {modifier.Model.Materials.Count} => {materials.Count}");
modifier.Model.Materials.Clear();
modifier.Model.Materials.AddRange(materials);
return sb.ToString();
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 54fd566dc84b51643886ce8858a671db
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,161 +0,0 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
using NUnit.Framework;
using VrmLib;
using VrmLib.Bvh;
namespace VrmLibTests
{
public class BvhTests
{
DirectoryInfo RootPath
{
get
{
return new FileInfo(GetType().Assembly.Location).Directory.Parent.Parent;
}
}
[Test]
[TestCase("Assets/StreamingAssets/VRM.Samples/Motions/test.txt")]
public void BvhTest(string filename)
{
var path = Path.Combine(RootPath.FullName, filename);
var text = File.ReadAllText(path, Encoding.UTF8);
var bvh = BvhParser.Parse(text);
Assert.AreEqual(4007, bvh.FrameCount);
var model = ModelExtensionsForBvh.Load(Path.GetFileName(path), bvh);
}
[Test]
[TestCase("Assets/StreamingAssets/VRM.Samples/Motions/test.txt")]
public void SkeletonEstimatorTest(string filename)
{
var path = Path.Combine(RootPath.FullName, filename);
var text = File.ReadAllText(path, Encoding.UTF8);
var bvh = BvhParser.Parse(text);
var model = ModelExtensionsForBvh.CreateFromBvh(bvh.Root);
var estimated = SkeletonEstimator.Detect(model.Root);
Assert.AreEqual(estimated[HumanoidBones.hips].Name, "Hips");
Assert.AreEqual(estimated[HumanoidBones.spine].Name, "Spine");
Assert.AreEqual(estimated[HumanoidBones.chest].Name, "Spine1");
Assert.AreEqual(estimated[HumanoidBones.neck].Name, "Neck");
Assert.AreEqual(estimated[HumanoidBones.head].Name, "Head");
Assert.AreEqual(estimated[HumanoidBones.leftShoulder].Name, "LeftShoulder");
Assert.AreEqual(estimated[HumanoidBones.leftUpperArm].Name, "LeftArm");
Assert.AreEqual(estimated[HumanoidBones.leftLowerArm].Name, "LeftForeArm");
Assert.AreEqual(estimated[HumanoidBones.leftHand].Name, "LeftHand");
Assert.AreEqual(estimated[HumanoidBones.rightShoulder].Name, "RightShoulder");
Assert.AreEqual(estimated[HumanoidBones.rightUpperArm].Name, "RightArm");
Assert.AreEqual(estimated[HumanoidBones.rightLowerArm].Name, "RightForeArm");
Assert.AreEqual(estimated[HumanoidBones.rightHand].Name, "RightHand");
Assert.AreEqual(estimated[HumanoidBones.leftUpperLeg].Name, "LeftUpLeg");
Assert.AreEqual(estimated[HumanoidBones.leftLowerLeg].Name, "LeftLeg");
Assert.AreEqual(estimated[HumanoidBones.leftFoot].Name, "LeftFoot");
Assert.AreEqual(estimated[HumanoidBones.leftToes].Name, "LeftToeBase");
Assert.AreEqual(estimated[HumanoidBones.rightUpperLeg].Name, "RightUpLeg");
Assert.AreEqual(estimated[HumanoidBones.rightLowerLeg].Name, "RightLeg");
Assert.AreEqual(estimated[HumanoidBones.rightFoot].Name, "RightFoot");
Assert.AreEqual(estimated[HumanoidBones.rightToes].Name, "RightToeBase");
}
DirectoryInfo TestModelPath
{
get
{
var env = Environment.GetEnvironmentVariable("VRM_TEST_MODELS");
return new DirectoryInfo(env);
}
}
[Test]
[TestCase("Motions/bvh/liveanimation/la_bvh_sample00.bvh")]
[TestCase("Motions/bvh/liveanimation/la_bvh_sample01.bvh")]
[TestCase("Motions/bvh/liveanimation/la_bvh_sample02.bvh")]
[TestCase("Motions/bvh/liveanimation/la_bvh_sample03.bvh")]
[TestCase("Motions/bvh/liveanimation/la_bvh_sample04.bvh")]
[TestCase("Motions/bvh/liveanimation/la_bvh_sample05.bvh")]
[TestCase("Motions/bvh/liveanimation/la_bvh_sample06.bvh")]
[TestCase("Motions/bvh/liveanimation/la_bvh_sample07.bvh")]
[TestCase("Motions/bvh/liveanimation/la_bvh_sample08.bvh")]
[TestCase("Motions/bvh/liveanimation/la_bvh_sample09.bvh")]
[TestCase("Motions/bvh/liveanimation/la_bvh_sample10.bvh")]
[TestCase("Motions/bvh/liveanimation/la_bvh_sample11.bvh")]
[TestCase("Motions/bvh/liveanimation/la_bvh_sample12.bvh")]
public void SkeletonEstimatorTestLA(string filename)
{
var path = Path.Combine(TestModelPath.FullName, filename);
var text = File.ReadAllText(path, Encoding.UTF8);
var bvh = BvhParser.Parse(text);
var model = ModelExtensionsForBvh.CreateFromBvh(bvh.Root);
var estimated = SkeletonEstimator.Detect(model.Root);
Assert.AreEqual(estimated[HumanoidBones.hips].Name, "Hips");
Assert.AreEqual(estimated[HumanoidBones.spine].Name, "Chest");
Assert.AreEqual(estimated[HumanoidBones.chest].Name, "Chest2");
Assert.AreEqual(estimated[HumanoidBones.neck].Name, "Neck");
Assert.AreEqual(estimated[HumanoidBones.head].Name, "Head");
Assert.AreEqual(estimated[HumanoidBones.leftShoulder].Name, "LeftCollar");
Assert.AreEqual(estimated[HumanoidBones.leftUpperArm].Name, "LeftShoulder");
Assert.AreEqual(estimated[HumanoidBones.leftLowerArm].Name, "LeftElbow");
Assert.AreEqual(estimated[HumanoidBones.leftHand].Name, "LeftWrist");
Assert.AreEqual(estimated[HumanoidBones.rightShoulder].Name, "RightCollar");
Assert.AreEqual(estimated[HumanoidBones.rightUpperArm].Name, "RightShoulder");
Assert.AreEqual(estimated[HumanoidBones.rightLowerArm].Name, "RightElbow");
Assert.AreEqual(estimated[HumanoidBones.rightHand].Name, "RightWrist");
Assert.AreEqual(estimated[HumanoidBones.leftUpperLeg].Name, "LeftHip");
Assert.AreEqual(estimated[HumanoidBones.leftLowerLeg].Name, "LeftKnee");
Assert.AreEqual(estimated[HumanoidBones.leftFoot].Name, "LeftAnkle");
Assert.AreEqual(estimated[HumanoidBones.rightUpperLeg].Name, "RightHip");
Assert.AreEqual(estimated[HumanoidBones.rightLowerLeg].Name, "RightKnee");
Assert.AreEqual(estimated[HumanoidBones.rightFoot].Name, "RightAnkle");
}
[Test]
[TestCase("Motions/bvh/accad/eric1.bvh")]
[TestCase("Motions/bvh/accad/ericdog.bvh")]
[TestCase("Motions/bvh/accad/ericrun.bvh")]
[TestCase("Motions/bvh/accad/flip.bvh")]
[TestCase("Motions/bvh/accad/swagger.bvh")]
public void SkeletonEstimatorTestAccad(string filename)
{
var path = Path.Combine(TestModelPath.FullName, filename);
var text = File.ReadAllText(path, Encoding.UTF8);
var bvh = BvhParser.Parse(text);
var bones = bvh.Root.Traverse().ToArray();
foreach (var bone in bones)
{
Console.WriteLine(bone.Name);
}
var model = ModelExtensionsForBvh.CreateFromBvh(bvh.Root);
var estimated = SkeletonEstimator.Detect(model.Root);
Assert.AreEqual(estimated[HumanoidBones.hips].Name, "root");
Assert.AreEqual(estimated[HumanoidBones.spine].Name, "lowerback");
Assert.AreEqual(estimated[HumanoidBones.chest].Name, "upperback");
Assert.AreEqual(estimated[HumanoidBones.upperChest].Name, "thorax");
Assert.AreEqual(estimated[HumanoidBones.neck].Name, "neck");
Assert.AreEqual(estimated[HumanoidBones.head].Name, "head");
Assert.AreEqual(estimated[HumanoidBones.leftShoulder].Name, "lshoulderjoint");
Assert.AreEqual(estimated[HumanoidBones.leftUpperArm].Name, "lhumerus");
Assert.AreEqual(estimated[HumanoidBones.leftLowerArm].Name, "lradius");
Assert.AreEqual(estimated[HumanoidBones.leftHand].Name, "lhand");
Assert.AreEqual(estimated[HumanoidBones.rightShoulder].Name, "rshoulderjoint");
Assert.AreEqual(estimated[HumanoidBones.rightUpperArm].Name, "rhumerus");
Assert.AreEqual(estimated[HumanoidBones.rightLowerArm].Name, "rradius");
Assert.AreEqual(estimated[HumanoidBones.rightHand].Name, "rhand");
Assert.AreEqual(estimated[HumanoidBones.rightUpperLeg].Name, "rfemur");
Assert.AreEqual(estimated[HumanoidBones.rightLowerLeg].Name, "rtibia");
Assert.AreEqual(estimated[HumanoidBones.rightFoot].Name, "rfoot");
Assert.AreEqual(estimated[HumanoidBones.rightToes].Name, "rtoes");
Assert.AreEqual(estimated[HumanoidBones.leftUpperLeg].Name, "lfemur");
Assert.AreEqual(estimated[HumanoidBones.leftLowerLeg].Name, "ltibia");
Assert.AreEqual(estimated[HumanoidBones.leftFoot].Name, "lfoot");
Assert.AreEqual(estimated[HumanoidBones.leftToes].Name, "ltoes");
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: c6a1d4e939982f84b9f3d94de009eb0e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: