using System; using System.Collections.Generic; using System.IO; using System.Linq; using UniGLTF; using UniHumanoid; using UnityEngine; using VRMShaders; namespace UniVRM10 { public class VrmAnimationExporter : gltfExporter { public VrmAnimationExporter( ExportingGltfData data, GltfExportSettings settings) : base(data, settings) { settings.InverseAxis = Axes.X; } readonly List m_times = new(); class PositionExporter { public List Values = new(); public Transform Node; readonly Transform m_root; public PositionExporter(Transform bone, Transform root) { Node = bone; m_root = root; } public void Add() { var p = m_root.worldToLocalMatrix.MultiplyPoint(Node.position); // reverse-X Values.Add(new Vector3(-p.x, p.y, p.z)); } } PositionExporter m_position; public void SetPositionBoneAndParent(Transform bone, Transform parent) { m_position = new PositionExporter(bone, parent); } class RotationExporter { public List Values = new(); public readonly Transform Node; public Transform m_parent; public RotationExporter(Transform bone, Transform parent) { Node = bone; m_parent = parent; } public void Add() { var q = Quaternion.Inverse(m_parent.rotation) * Node.rotation; // reverse-X Values.Add(new Quaternion(q.x, -q.y, -q.z, q.w)); } } readonly Dictionary m_rotations = new(); public void AddRotationBoneAndParent(HumanBodyBones bone, Transform transform, Transform parent) { m_rotations.Add(bone, new RotationExporter(transform, parent)); } public 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(Action addFrames) { base.Export(new RuntimeTextureSerializer()); addFrames(this); // // export // var gltfAnimation = new glTFAnimation { }; _data.Gltf.animations.Add(gltfAnimation); // this.Nodes には 右手左手変換後のコピーが入っている // 代替として名前で逆引きする var names = Nodes.Select(x => x.name).ToList(); // 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 = names.IndexOf(m_position.Node.name), 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 = names.IndexOf(kv.Value.Node.name), path = "rotation", }, }); } // VRMC_vrm_animation var vrmAnimation = VrmAnimationUtil.Create(m_rotations.ToDictionary(kv => kv.Key, kv => kv.Value.Node), names); UniGLTF.Extensions.VRMC_vrm_animation.GltfSerializer.SerializeTo( ref _data.Gltf.extensions , vrmAnimation); } } }