VrmAnimationExporter

This commit is contained in:
ousttrue 2023-06-15 15:22:32 +09:00
parent b9293f0c14
commit 60c79b5e7a
6 changed files with 261 additions and 0 deletions

View File

@ -26,6 +26,7 @@ namespace UniGLTF
{ typeof(Vector2), new ComponentVec(glComponentType.FLOAT, 2) },
{ typeof(Vector3), new ComponentVec(glComponentType.FLOAT, 3) },
{ typeof(Vector4), new ComponentVec(glComponentType.FLOAT, 4) },
{ typeof(Quaternion), new ComponentVec(glComponentType.FLOAT, 4) },
{ typeof(UShort4), new ComponentVec(glComponentType.UNSIGNED_SHORT, 4) },
{ typeof(Matrix4x4), new ComponentVec(glComponentType.FLOAT, 16) },
{ typeof(Color), new ComponentVec(glComponentType.FLOAT, 4) },

View File

@ -6,10 +6,14 @@ namespace UniVRM10
{
private const string UserMenuPrefix = VRMVersion.MENU;
private const string DevelopmentMenuPrefix = VRMVersion.MENU + "/Development";
private const string ExperimentalMenuPrefix = VRMVersion.MENU + "/Experimental";
[MenuItem(UserMenuPrefix + "/Export VRM-1.0", priority = 1)]
private static void OpenExportDialog() => VRM10ExportDialog.Open();
[MenuItem(ExperimentalMenuPrefix + "/Convert BVH to VRM-Animation", priority = 100)]
private static void ConvertVrmAnimation() => VrmAnimationMenu.BvhToVrmAnimationMenu();
#if VRM_DEVELOP
[MenuItem(UserMenuPrefix + "/VRM1 Window", false, 2)]
private static void OpenWindow() => VRM10Window.Open();

View File

@ -0,0 +1,25 @@
using System.IO;
using UnityEditor;
namespace UniVRM10
{
internal static class VrmAnimationMenu
{
public static void BvhToVrmAnimationMenu()
{
var path = EditorUtility.OpenFilePanel("select bvh", null, "bvh");
if (!string.IsNullOrEmpty(path))
{
var bytes = VrmAnimationExporter.BvhToVrmAnimation(path);
var dst = EditorUtility.SaveFilePanel("write vrma",
Path.GetDirectoryName(path),
Path.GetFileNameWithoutExtension(path),
"vrma");
if (!string.IsNullOrEmpty(dst))
{
File.WriteAllBytes(dst, bytes);
}
}
}
}
}

View File

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

View File

@ -0,0 +1,209 @@
using System;
using System.Collections.Generic;
using System.IO;
using UniGLTF;
using UniHumanoid;
using UnityEngine;
using VRMShaders;
namespace UniVRM10
{
public class VrmAnimationExporter : gltfExporter
{
public VrmAnimationExporter(
ExportingGltfData data,
GltfExportSettings settings)
: base(data, settings) { }
// private (INormalizedPoseProvider, ITPoseProvider) m_controlRig;
readonly List<float> m_times = new();
class PositionExporter
{
public List<Vector3> Values = new();
public Transform Node;
readonly Transform m_root;
public PositionExporter(Transform bone, Transform root)
{
Node = bone;
m_root = root;
}
public void Add()
{
Values.Add(m_root.worldToLocalMatrix.MultiplyPoint(Node.position));
}
}
PositionExporter m_position;
class RotationExporter
{
public List<Quaternion> Values = new();
readonly Transform Node;
public Transform m_parent;
public RotationExporter(Transform bone, Transform parent)
{
Node = bone;
m_parent = parent;
}
public void Add()
{
Values.Add(Quaternion.Inverse(m_parent.rotation) * Node.rotation);
}
}
readonly Dictionary<HumanBodyBones, RotationExporter> m_rotations = new();
static Transform GetParentBone(Dictionary<HumanBodyBones, Transform> map, Vrm10HumanoidBones bone)
{
while (true)
{
if (bone == Vrm10HumanoidBones.Hips)
{
break;
}
var parentBone = Vrm10HumanoidBoneSpecification.GetDefine(bone).ParentBone.Value;
var unityParentBone = Vrm10HumanoidBoneSpecification.ConvertToUnityBone(parentBone);
if (map.TryGetValue(unityParentBone, out var found))
{
return found;
}
bone = parentBone;
}
// hips has no parent
return null;
}
private void AddFrame(TimeSpan time)
{
m_times.Add((float)time.TotalSeconds);
m_position.Add();
foreach (var kv in m_rotations)
{
kv.Value.Add();
}
}
public void Export(BvhImporterContext bvh)
{
base.Export(new RuntimeTextureSerializer());
//
// setup
//
var map = new Dictionary<HumanBodyBones, Transform>();
var animator = bvh.Root.GetComponent<Animator>();
foreach (HumanBodyBones bone in Enum.GetValues(typeof(HumanBodyBones)))
{
if (bone == HumanBodyBones.LastBone)
{
continue;
}
var t = animator.GetBoneTransform(bone);
if (t == null)
{
continue;
}
map.Add(bone, t);
}
m_position = new PositionExporter(map[HumanBodyBones.Hips],
bvh.Root.transform);
foreach (var kv in map)
{
var vrmBone = Vrm10HumanoidBoneSpecification.ConvertFromUnityBone(kv.Key);
var parent = GetParentBone(map, vrmBone) ?? bvh.Root.transform;
m_rotations.Add(kv.Key, new RotationExporter(kv.Value, parent));
}
//
// get data
//
var animation = bvh.Root.gameObject.GetComponent<Animation>();
var clip = animation.clip;
var state = animation[clip.name];
var time = default(TimeSpan);
for (int i = 0; i < bvh.Bvh.FrameCount; ++i, time += bvh.Bvh.FrameTime)
{
state.time = (float)time.TotalSeconds;
animation.Sample();
AddFrame(time);
}
//
// export
//
var gltfAnimation = new glTFAnimation
{
};
_data.Gltf.animations.Add(gltfAnimation);
// time values
var input = _data.ExtendBufferAndGetAccessorIndex(m_times.ToArray());
{
var output = _data.ExtendBufferAndGetAccessorIndex(m_position.Values.ToArray());
var sampler = gltfAnimation.samplers.Count;
gltfAnimation.samplers.Add(new glTFAnimationSampler
{
input = input,
output = output,
interpolation = "LINEAR",
});
gltfAnimation.channels.Add(new glTFAnimationChannel
{
sampler = sampler,
target = new glTFAnimationTarget
{
node = Nodes.IndexOf(m_position.Node),
path = "translation",
},
});
}
foreach (var kv in m_rotations)
{
var output = _data.ExtendBufferAndGetAccessorIndex(kv.Value.Values.ToArray());
var sampler = gltfAnimation.samplers.Count;
gltfAnimation.samplers.Add(new glTFAnimationSampler
{
input = input,
output = output,
interpolation = "LINEAR",
});
gltfAnimation.channels.Add(new glTFAnimationChannel
{
sampler = sampler,
target = new glTFAnimationTarget
{
node = Nodes.IndexOf(m_position.Node),
path = "rotation",
},
});
}
}
public static byte[] BvhToVrmAnimation(string path)
{
var bvh = new BvhImporterContext();
bvh.Parse(path, File.ReadAllText(path));
bvh.Load();
var data = new ExportingGltfData();
using (var exporter = new VrmAnimationExporter(
data, new GltfExportSettings()))
{
exporter.Prepare(bvh.Root.gameObject);
exporter.Export(bvh);
return data.ToGlbBytes();
}
}
}
}

View File

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