mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-05-09 12:11:35 -05:00
Merge pull request #641 from hiroj/add_animationImporter_interface
gltfのAnimationImporterをインターフェース化
This commit is contained in:
commit
2c72e00ffe
|
|
@ -316,6 +316,35 @@ namespace UniGLTF
|
|||
return result;
|
||||
}
|
||||
|
||||
public static float[] GetFloatArrayFromAccessor(this glTF self, int accessorIndex)
|
||||
{
|
||||
var vertexAccessor = self.accessors[accessorIndex];
|
||||
|
||||
if (vertexAccessor.count <= 0) return new float[] { };
|
||||
|
||||
var bufferCount = vertexAccessor.count * vertexAccessor.TypeCount;
|
||||
var result = (vertexAccessor.bufferView != -1)
|
||||
? self.GetAttrib<float>(bufferCount, vertexAccessor.byteOffset, self.bufferViews[vertexAccessor.bufferView])
|
||||
: new float[bufferCount]
|
||||
;
|
||||
|
||||
var sparse = vertexAccessor.sparse;
|
||||
if (sparse != null && sparse.count > 0)
|
||||
{
|
||||
// override sparse values
|
||||
var indices = self._GetIndices(self.bufferViews[sparse.indices.bufferView], sparse.count, sparse.indices.byteOffset, sparse.indices.componentType);
|
||||
var values = self.GetAttrib<float>(sparse.count * vertexAccessor.TypeCount, sparse.values.byteOffset, self.bufferViews[sparse.values.bufferView]);
|
||||
|
||||
var it = indices.GetEnumerator();
|
||||
for (int i = 0; i < sparse.count; ++i)
|
||||
{
|
||||
it.MoveNext();
|
||||
result[it.Current] = values[i];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ArraySegment<Byte> GetImageBytes(this glTF self, IStorage storage, int imageIndex, out string textureName)
|
||||
{
|
||||
var image = self.images[imageIndex];
|
||||
|
|
|
|||
|
|
@ -1,334 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public static class AnimationImporter
|
||||
{
|
||||
private enum TangentMode
|
||||
{
|
||||
Linear,
|
||||
Constant,
|
||||
Cubicspline
|
||||
}
|
||||
|
||||
private static TangentMode GetTangentMode(string interpolation)
|
||||
{
|
||||
if (interpolation == glTFAnimationTarget.Interpolations.LINEAR.ToString())
|
||||
{
|
||||
return TangentMode.Linear;
|
||||
}
|
||||
else if (interpolation == glTFAnimationTarget.Interpolations.STEP.ToString())
|
||||
{
|
||||
return TangentMode.Constant;
|
||||
}
|
||||
else if (interpolation == glTFAnimationTarget.Interpolations.CUBICSPLINE.ToString())
|
||||
{
|
||||
return TangentMode.Cubicspline;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private static void CalculateTangent(List<Keyframe> keyframes, int current)
|
||||
{
|
||||
int back = current - 1;
|
||||
if (back < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (current < keyframes.Count)
|
||||
{
|
||||
var rightTangent = (keyframes[current].value - keyframes[back].value) / (keyframes[current].time - keyframes[back].time);
|
||||
keyframes[back] = new Keyframe(keyframes[back].time, keyframes[back].value, keyframes[back].inTangent, rightTangent);
|
||||
|
||||
var leftTangent = (keyframes[back].value - keyframes[current].value) / (keyframes[back].time - keyframes[current].time);
|
||||
keyframes[current] = new Keyframe(keyframes[current].time, keyframes[current].value, leftTangent, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static Quaternion GetShortest(Quaternion last, Quaternion rot)
|
||||
{
|
||||
if (Quaternion.Dot(last, rot) > 0.0)
|
||||
{
|
||||
return rot;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Quaternion(-rot.x, -rot.y, -rot.z, -rot.w);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public delegate float[] ReverseZ(float[] current, float[] last);
|
||||
public static void SetAnimationCurve(
|
||||
AnimationClip targetClip,
|
||||
string relativePath,
|
||||
string[] propertyNames,
|
||||
float[] input,
|
||||
float[] output,
|
||||
string interpolation,
|
||||
Type curveType,
|
||||
ReverseZ reverse)
|
||||
{
|
||||
var tangentMode = GetTangentMode(interpolation);
|
||||
|
||||
var curveCount = propertyNames.Length;
|
||||
AnimationCurve[] curves = new AnimationCurve[curveCount];
|
||||
List<Keyframe>[] keyframes = new List<Keyframe>[curveCount];
|
||||
|
||||
int elementNum = curveCount;
|
||||
int inputIndex = 0;
|
||||
//Quaternion用
|
||||
float[] last = new float[curveCount];
|
||||
if (last.Length == 4)
|
||||
{
|
||||
last[3] = 1.0f;
|
||||
}
|
||||
for (inputIndex = 0; inputIndex < input.Length; ++inputIndex)
|
||||
{
|
||||
var time = input[inputIndex];
|
||||
var outputIndex = 0;
|
||||
if (tangentMode == TangentMode.Cubicspline)
|
||||
{
|
||||
outputIndex = inputIndex * elementNum * 3;
|
||||
var value = new float[curveCount];
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
value[i] = output[outputIndex + elementNum + i];
|
||||
}
|
||||
var reversed = reverse(value, last);
|
||||
last = reversed;
|
||||
for (int i = 0; i < keyframes.Length; i++)
|
||||
{
|
||||
if (keyframes[i] == null)
|
||||
keyframes[i] = new List<Keyframe>();
|
||||
keyframes[i].Add(new Keyframe(
|
||||
time,
|
||||
reversed[i],
|
||||
output[outputIndex + i],
|
||||
output[outputIndex + i + elementNum * 2]));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
outputIndex = inputIndex * elementNum;
|
||||
var value = new float[curveCount];
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
value[i] = output[outputIndex + i];
|
||||
}
|
||||
var reversed = reverse(value, last);
|
||||
last = reversed;
|
||||
|
||||
for (int i = 0; i < keyframes.Length; i++)
|
||||
{
|
||||
if (keyframes[i] == null)
|
||||
keyframes[i] = new List<Keyframe>();
|
||||
if (tangentMode == TangentMode.Linear)
|
||||
{
|
||||
keyframes[i].Add(new Keyframe(time, reversed[i], 0, 0));
|
||||
if (keyframes[i].Count > 0)
|
||||
{
|
||||
CalculateTangent(keyframes[i], keyframes[i].Count - 1);
|
||||
}
|
||||
}
|
||||
else if (tangentMode == TangentMode.Constant)
|
||||
keyframes[i].Add(new Keyframe(time, reversed[i], 0, float.PositiveInfinity));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < curves.Length; i++)
|
||||
{
|
||||
curves[i] = new AnimationCurve();
|
||||
for (int j = 0; j < keyframes[i].Count; j++)
|
||||
{
|
||||
curves[i].AddKey(keyframes[i][j]);
|
||||
}
|
||||
|
||||
targetClip.SetCurve(relativePath, curveType, propertyNames[i], curves[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<AnimationClip> ImportAnimationClip(ImporterContext ctx)
|
||||
{
|
||||
List<AnimationClip> animationClips = new List<AnimationClip>();
|
||||
for (int i = 0; i < ctx.GLTF.animations.Count; ++i)
|
||||
{
|
||||
var clip = new AnimationClip();
|
||||
clip.ClearCurves();
|
||||
clip.legacy = true;
|
||||
clip.name = ctx.GLTF.animations[i].name;
|
||||
if (string.IsNullOrEmpty(clip.name))
|
||||
{
|
||||
clip.name = "legacy_" + i;
|
||||
}
|
||||
clip.wrapMode = WrapMode.Loop;
|
||||
|
||||
var animation = ctx.GLTF.animations[i];
|
||||
if (string.IsNullOrEmpty(animation.name))
|
||||
{
|
||||
animation.name = string.Format("animation:{0}", i);
|
||||
}
|
||||
|
||||
foreach (var channel in animation.channels)
|
||||
{
|
||||
var targetTransform = ctx.Nodes[channel.target.node];
|
||||
var relativePath = targetTransform.RelativePathFrom(ctx.Root.transform);
|
||||
switch (channel.target.path)
|
||||
{
|
||||
case glTFAnimationTarget.PATH_TRANSLATION:
|
||||
{
|
||||
var sampler = animation.samplers[channel.sampler];
|
||||
var input = ctx.GLTF.GetArrayFromAccessor<float>(sampler.input);
|
||||
var outputVector = ctx.GLTF.GetArrayFromAccessor<Vector3>(sampler.output);
|
||||
var output = new float[outputVector.Count() * 3];
|
||||
ArrayExtensions.Copy<Vector3, float>(
|
||||
new ArraySegment<Vector3>(outputVector),
|
||||
new ArraySegment<float>(output));
|
||||
|
||||
AnimationImporter.SetAnimationCurve(
|
||||
clip,
|
||||
relativePath,
|
||||
new string[] { "localPosition.x", "localPosition.y", "localPosition.z" },
|
||||
input,
|
||||
output,
|
||||
sampler.interpolation,
|
||||
typeof(Transform),
|
||||
(values, last) =>
|
||||
{
|
||||
Vector3 temp = new Vector3(values[0], values[1], values[2]);
|
||||
return temp.ReverseZ().ToArray();
|
||||
}
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case glTFAnimationTarget.PATH_ROTATION:
|
||||
{
|
||||
var sampler = animation.samplers[channel.sampler];
|
||||
var input = ctx.GLTF.GetArrayFromAccessor<float>(sampler.input);
|
||||
var outputVector = ctx.GLTF.GetArrayFromAccessor<Vector4>(sampler.output);
|
||||
var output = new float[outputVector.Count() * 4];
|
||||
ArrayExtensions.Copy<Vector4, float>(
|
||||
new ArraySegment<Vector4>(outputVector),
|
||||
new ArraySegment<float>(output));
|
||||
|
||||
AnimationImporter.SetAnimationCurve(
|
||||
clip,
|
||||
relativePath,
|
||||
new string[] { "localRotation.x", "localRotation.y", "localRotation.z", "localRotation.w" },
|
||||
input,
|
||||
output,
|
||||
sampler.interpolation,
|
||||
typeof(Transform),
|
||||
(values, last) =>
|
||||
{
|
||||
Quaternion currentQuaternion = new Quaternion(values[0], values[1], values[2], values[3]);
|
||||
Quaternion lastQuaternion = new Quaternion(last[0], last[1], last[2], last[3]);
|
||||
return AnimationImporter.GetShortest(lastQuaternion, currentQuaternion.ReverseZ()).ToArray();
|
||||
}
|
||||
);
|
||||
|
||||
clip.EnsureQuaternionContinuity();
|
||||
}
|
||||
break;
|
||||
|
||||
case glTFAnimationTarget.PATH_SCALE:
|
||||
{
|
||||
var sampler = animation.samplers[channel.sampler];
|
||||
var input = ctx.GLTF.GetArrayFromAccessor<float>(sampler.input);
|
||||
var outputVector = ctx.GLTF.GetArrayFromAccessor<Vector3>(sampler.output);
|
||||
var output = new float[outputVector.Count() * 3];
|
||||
ArrayExtensions.Copy<Vector3, float>(
|
||||
new ArraySegment<Vector3>(outputVector),
|
||||
new ArraySegment<float>(output));
|
||||
|
||||
AnimationImporter.SetAnimationCurve(
|
||||
clip,
|
||||
relativePath,
|
||||
new string[] { "localScale.x", "localScale.y", "localScale.z" },
|
||||
input,
|
||||
output,
|
||||
sampler.interpolation,
|
||||
typeof(Transform),
|
||||
(values, last) => values);
|
||||
}
|
||||
break;
|
||||
|
||||
case glTFAnimationTarget.PATH_WEIGHT:
|
||||
{
|
||||
var node = ctx.GLTF.nodes[channel.target.node];
|
||||
var mesh = ctx.GLTF.meshes[node.mesh];
|
||||
var primitive = mesh.primitives.FirstOrDefault();
|
||||
var targets = primitive.targets;
|
||||
|
||||
if (!gltf_mesh_extras_targetNames.TryGet(mesh, out List<string> targetNames))
|
||||
{
|
||||
throw new Exception("glTF BlendShape Animation. targetNames invalid.");
|
||||
}
|
||||
|
||||
var keyNames = targetNames
|
||||
.Where(x => !string.IsNullOrEmpty(x))
|
||||
.Select(x => "blendShape." + x)
|
||||
.ToArray();
|
||||
|
||||
var sampler = animation.samplers[channel.sampler];
|
||||
var input = ctx.GLTF.GetArrayFromAccessor<float>(sampler.input);
|
||||
var output = ctx.GLTF.GetArrayFromAccessor<float>(sampler.output);
|
||||
AnimationImporter.SetAnimationCurve(
|
||||
clip,
|
||||
relativePath,
|
||||
keyNames,
|
||||
input,
|
||||
output,
|
||||
sampler.interpolation,
|
||||
typeof(SkinnedMeshRenderer),
|
||||
(values, last) =>
|
||||
{
|
||||
for (int j = 0; j < values.Length; j++)
|
||||
{
|
||||
values[j] *= 100.0f;
|
||||
}
|
||||
return values;
|
||||
});
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Debug.LogWarningFormat("unknown path: {0}", channel.target.path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
animationClips.Add(clip);
|
||||
}
|
||||
|
||||
return animationClips;
|
||||
}
|
||||
|
||||
public static void ImportAnimation(ImporterContext ctx)
|
||||
{
|
||||
// animation
|
||||
if (ctx.GLTF.animations != null && ctx.GLTF.animations.Any())
|
||||
{
|
||||
var animation = ctx.Root.AddComponent<Animation>();
|
||||
ctx.AnimationClips = ImportAnimationClip(ctx);
|
||||
foreach (var clip in ctx.AnimationClips)
|
||||
{
|
||||
animation.AddClip(clip, clip.name);
|
||||
}
|
||||
if (ctx.AnimationClips.Count > 0)
|
||||
{
|
||||
animation.clip = ctx.AnimationClips.First();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d602384685dd4f179350052013659720
|
||||
timeCreated: 1537445972
|
||||
316
Assets/UniGLTF/Runtime/UniGLTF/IO/AnimationImporterUtil.cs
Normal file
316
Assets/UniGLTF/Runtime/UniGLTF/IO/AnimationImporterUtil.cs
Normal file
|
|
@ -0,0 +1,316 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public static class AnimationImporterUtil
|
||||
{
|
||||
private enum TangentMode
|
||||
{
|
||||
Linear,
|
||||
Constant,
|
||||
Cubicspline
|
||||
}
|
||||
|
||||
private static TangentMode GetTangentMode(string interpolation)
|
||||
{
|
||||
if (interpolation == glTFAnimationTarget.Interpolations.LINEAR.ToString())
|
||||
{
|
||||
return TangentMode.Linear;
|
||||
}
|
||||
else if (interpolation == glTFAnimationTarget.Interpolations.STEP.ToString())
|
||||
{
|
||||
return TangentMode.Constant;
|
||||
}
|
||||
else if (interpolation == glTFAnimationTarget.Interpolations.CUBICSPLINE.ToString())
|
||||
{
|
||||
return TangentMode.Cubicspline;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private static void CalculateTanget(List<Keyframe> keyframes, int current)
|
||||
{
|
||||
int back = current - 1;
|
||||
if (back < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (current < keyframes.Count)
|
||||
{
|
||||
var rightTangent = (keyframes[current].value - keyframes[back].value) / (keyframes[current].time - keyframes[back].time);
|
||||
keyframes[back] = new Keyframe(keyframes[back].time, keyframes[back].value, keyframes[back].inTangent, rightTangent);
|
||||
|
||||
var leftTangent = (keyframes[back].value - keyframes[current].value) / (keyframes[back].time - keyframes[current].time);
|
||||
keyframes[current] = new Keyframe(keyframes[current].time, keyframes[current].value, leftTangent, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static Quaternion GetShortest(Quaternion last, Quaternion rot)
|
||||
{
|
||||
if (Quaternion.Dot(last, rot) > 0.0)
|
||||
{
|
||||
return rot;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Quaternion(-rot.x, -rot.y, -rot.z, -rot.w);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public delegate float[] ReverseZ(float[] current, float[] last);
|
||||
public static void SetAnimationCurve(
|
||||
AnimationClip targetClip,
|
||||
string relativePath,
|
||||
string[] propertyNames,
|
||||
float[] input,
|
||||
float[] output,
|
||||
string interpolation,
|
||||
Type curveType,
|
||||
ReverseZ reverse)
|
||||
{
|
||||
var tangentMode = GetTangentMode(interpolation);
|
||||
|
||||
var curveCount = propertyNames.Length;
|
||||
AnimationCurve[] curves = new AnimationCurve[curveCount];
|
||||
List<Keyframe>[] keyframes = new List<Keyframe>[curveCount];
|
||||
|
||||
int elementNum = curveCount;
|
||||
int inputIndex = 0;
|
||||
//Quaternion用
|
||||
float[] last = new float[curveCount];
|
||||
if (last.Length == 4)
|
||||
{
|
||||
last[3] = 1.0f;
|
||||
}
|
||||
for (inputIndex = 0; inputIndex < input.Length; ++inputIndex)
|
||||
{
|
||||
var time = input[inputIndex];
|
||||
var outputIndex = 0;
|
||||
if (tangentMode == TangentMode.Cubicspline)
|
||||
{
|
||||
outputIndex = inputIndex * elementNum * 3;
|
||||
var value = new float[curveCount];
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
value[i] = output[outputIndex + elementNum + i];
|
||||
}
|
||||
var reversed = reverse(value, last);
|
||||
last = reversed;
|
||||
for (int i = 0; i < keyframes.Length; i++)
|
||||
{
|
||||
if (keyframes[i] == null)
|
||||
keyframes[i] = new List<Keyframe>();
|
||||
keyframes[i].Add(new Keyframe(
|
||||
time,
|
||||
reversed[i],
|
||||
output[outputIndex + i],
|
||||
output[outputIndex + i + elementNum * 2]));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
outputIndex = inputIndex * elementNum;
|
||||
var value = new float[curveCount];
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
value[i] = output[outputIndex + i];
|
||||
}
|
||||
var reversed = reverse(value, last);
|
||||
last = reversed;
|
||||
|
||||
for (int i = 0; i < keyframes.Length; i++)
|
||||
{
|
||||
if (keyframes[i] == null)
|
||||
keyframes[i] = new List<Keyframe>();
|
||||
if (tangentMode == TangentMode.Linear)
|
||||
{
|
||||
keyframes[i].Add(new Keyframe(time, reversed[i], 0, 0));
|
||||
if (keyframes[i].Count > 0)
|
||||
{
|
||||
CalculateTanget(keyframes[i], keyframes[i].Count - 1);
|
||||
}
|
||||
}
|
||||
else if (tangentMode == TangentMode.Constant)
|
||||
keyframes[i].Add(new Keyframe(time, reversed[i], 0, float.PositiveInfinity));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < curves.Length; i++)
|
||||
{
|
||||
curves[i] = new AnimationCurve();
|
||||
for (int j = 0; j < keyframes[i].Count; j++)
|
||||
{
|
||||
curves[i].AddKey(keyframes[i][j]);
|
||||
}
|
||||
|
||||
targetClip.SetCurve(relativePath, curveType, propertyNames[i], curves[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public static string RelativePathFrom(List<glTFNode> nodes, glTFNode root, glTFNode target)
|
||||
{
|
||||
if (root == target) return "";
|
||||
var path = new List<string>();
|
||||
return RelativePathFrom(nodes, root, target, path);
|
||||
}
|
||||
|
||||
private static string RelativePathFrom(List<glTFNode> nodes, glTFNode root, glTFNode target, List<string> path)
|
||||
{
|
||||
if(path.Count == 0) path.Add(target.name);
|
||||
|
||||
var targetIndex = nodes.IndexOf(target);
|
||||
foreach (var parent in nodes)
|
||||
{
|
||||
if(parent.children == null || parent.children.Length == 0) continue;
|
||||
|
||||
foreach(var child in parent.children)
|
||||
{
|
||||
if(child != targetIndex) continue;
|
||||
|
||||
if(parent == root) return string.Join("/", path);
|
||||
|
||||
path.Insert(0, parent.name);
|
||||
return RelativePathFrom(nodes, root, parent, path);
|
||||
}
|
||||
}
|
||||
|
||||
return string.Join("/", path);
|
||||
}
|
||||
|
||||
public static AnimationClip ConvertAnimationClip(glTF gltf, glTFAnimation animation, glTFNode root = null)
|
||||
{
|
||||
var clip = new AnimationClip();
|
||||
clip.ClearCurves();
|
||||
clip.legacy = true;
|
||||
clip.name = animation.name;
|
||||
clip.wrapMode = WrapMode.Loop;
|
||||
|
||||
foreach (var channel in animation.channels)
|
||||
{
|
||||
var relativePath = RelativePathFrom(gltf.nodes, root, gltf.nodes[channel.target.node]);
|
||||
switch (channel.target.path)
|
||||
{
|
||||
case glTFAnimationTarget.PATH_TRANSLATION:
|
||||
{
|
||||
var sampler = animation.samplers[channel.sampler];
|
||||
var input = gltf.GetArrayFromAccessor<float>(sampler.input);
|
||||
var output = gltf.GetFloatArrayFromAccessor(sampler.output);
|
||||
|
||||
AnimationImporterUtil.SetAnimationCurve(
|
||||
clip,
|
||||
relativePath,
|
||||
new string[] { "localPosition.x", "localPosition.y", "localPosition.z" },
|
||||
input,
|
||||
output,
|
||||
sampler.interpolation,
|
||||
typeof(Transform),
|
||||
(values, last) =>
|
||||
{
|
||||
Vector3 temp = new Vector3(values[0], values[1], values[2]);
|
||||
return temp.ReverseZ().ToArray();
|
||||
}
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case glTFAnimationTarget.PATH_ROTATION:
|
||||
{
|
||||
var sampler = animation.samplers[channel.sampler];
|
||||
var input = gltf.GetArrayFromAccessor<float>(sampler.input);
|
||||
var output = gltf.GetFloatArrayFromAccessor(sampler.output);
|
||||
|
||||
AnimationImporterUtil.SetAnimationCurve(
|
||||
clip,
|
||||
relativePath,
|
||||
new string[] { "localRotation.x", "localRotation.y", "localRotation.z", "localRotation.w" },
|
||||
input,
|
||||
output,
|
||||
sampler.interpolation,
|
||||
typeof(Transform),
|
||||
(values, last) =>
|
||||
{
|
||||
Quaternion currentQuaternion = new Quaternion(values[0], values[1], values[2], values[3]);
|
||||
Quaternion lastQuaternion = new Quaternion(last[0], last[1], last[2], last[3]);
|
||||
return AnimationImporterUtil.GetShortest(lastQuaternion, currentQuaternion.ReverseZ()).ToArray();
|
||||
}
|
||||
);
|
||||
|
||||
clip.EnsureQuaternionContinuity();
|
||||
}
|
||||
break;
|
||||
|
||||
case glTFAnimationTarget.PATH_SCALE:
|
||||
{
|
||||
var sampler = animation.samplers[channel.sampler];
|
||||
var input = gltf.GetArrayFromAccessor<float>(sampler.input);
|
||||
var output = gltf.GetFloatArrayFromAccessor(sampler.output);
|
||||
|
||||
AnimationImporterUtil.SetAnimationCurve(
|
||||
clip,
|
||||
relativePath,
|
||||
new string[] { "localScale.x", "localScale.y", "localScale.z" },
|
||||
input,
|
||||
output,
|
||||
sampler.interpolation,
|
||||
typeof(Transform),
|
||||
(values, last) => values);
|
||||
}
|
||||
break;
|
||||
|
||||
case glTFAnimationTarget.PATH_WEIGHT:
|
||||
{
|
||||
var node = gltf.nodes[channel.target.node];
|
||||
var mesh = gltf.meshes[node.mesh];
|
||||
var primitive = mesh.primitives.FirstOrDefault();
|
||||
var targets = primitive.targets;
|
||||
|
||||
if (!gltf_mesh_extras_targetNames.TryGet(mesh, out List<string> targetNames))
|
||||
{
|
||||
throw new Exception("glTF BlendShape Animation. targetNames invalid.");
|
||||
}
|
||||
|
||||
var keyNames = targetNames
|
||||
.Where(x => !string.IsNullOrEmpty(x))
|
||||
.Select(x => "blendShape." + x)
|
||||
.ToArray();
|
||||
|
||||
var sampler = animation.samplers[channel.sampler];
|
||||
var input = gltf.GetArrayFromAccessor<float>(sampler.input);
|
||||
var output = gltf.GetArrayFromAccessor<float>(sampler.output);
|
||||
AnimationImporterUtil.SetAnimationCurve(
|
||||
clip,
|
||||
relativePath,
|
||||
keyNames,
|
||||
input,
|
||||
output,
|
||||
sampler.interpolation,
|
||||
typeof(SkinnedMeshRenderer),
|
||||
(values, last) =>
|
||||
{
|
||||
for (int j = 0; j < values.Length; j++)
|
||||
{
|
||||
values[j] *= 100.0f;
|
||||
}
|
||||
return values;
|
||||
});
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Debug.LogWarningFormat("unknown path: {0}", channel.target.path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return clip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a6e858fe43dba6342902d72392b1bdce
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
7
Assets/UniGLTF/Runtime/UniGLTF/IO/IAnimationImporter.cs
Normal file
7
Assets/UniGLTF/Runtime/UniGLTF/IO/IAnimationImporter.cs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
namespace UniGLTF
|
||||
{
|
||||
public interface IAnimationImporter
|
||||
{
|
||||
void Import(ImporterContext context);
|
||||
}
|
||||
}
|
||||
11
Assets/UniGLTF/Runtime/UniGLTF/IO/IAnimationImporter.cs.meta
Normal file
11
Assets/UniGLTF/Runtime/UniGLTF/IO/IAnimationImporter.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4f950ba9271cbff4cbc4345f5a23a5d8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -85,6 +85,26 @@ namespace UniGLTF
|
|||
}
|
||||
#endregion
|
||||
|
||||
#region Animation
|
||||
protected IAnimationImporter m_animationImporter;
|
||||
public void SetAnimationImporter(IAnimationImporter animationImporter)
|
||||
{
|
||||
m_animationImporter = animationImporter;
|
||||
}
|
||||
public IAnimationImporter AnimationImporter
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_animationImporter == null)
|
||||
{
|
||||
m_animationImporter = new RootAnimationImporter();
|
||||
}
|
||||
return m_animationImporter;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
IShaderStore m_shaderStore;
|
||||
public IShaderStore ShaderStore
|
||||
{
|
||||
|
|
@ -588,7 +608,7 @@ namespace UniGLTF
|
|||
{
|
||||
using (MeasureTime("AnimationImporter"))
|
||||
{
|
||||
AnimationImporter.ImportAnimation(this);
|
||||
AnimationImporter.Import(this);
|
||||
}
|
||||
})
|
||||
.ContinueWithCoroutine(Scheduler.MainThread, OnLoadModel)
|
||||
|
|
|
|||
55
Assets/UniGLTF/Runtime/UniGLTF/IO/RootAnimationImporter.cs
Normal file
55
Assets/UniGLTF/Runtime/UniGLTF/IO/RootAnimationImporter.cs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public sealed class RootAnimationImporter : IAnimationImporter
|
||||
{
|
||||
public void Import(ImporterContext context)
|
||||
{
|
||||
// animation
|
||||
if (context.GLTF.animations != null && context.GLTF.animations.Any())
|
||||
{
|
||||
var animation = context.Root.AddComponent<Animation>();
|
||||
context.AnimationClips = ImportAnimationClips(context.GLTF);
|
||||
|
||||
foreach (var clip in context.AnimationClips)
|
||||
{
|
||||
animation.AddClip(clip, clip.name);
|
||||
}
|
||||
if (context.AnimationClips.Count > 0)
|
||||
{
|
||||
animation.clip = context.AnimationClips.First();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<AnimationClip> ImportAnimationClips(glTF gltf)
|
||||
{
|
||||
var animationClips = new List<AnimationClip>();
|
||||
for (var i = 0; i < gltf.animations.Count; ++i)
|
||||
{
|
||||
var clip = new AnimationClip();
|
||||
clip.ClearCurves();
|
||||
clip.legacy = true;
|
||||
clip.name = gltf.animations[i].name;
|
||||
if (string.IsNullOrEmpty(clip.name))
|
||||
{
|
||||
clip.name = $"legacy_{i}";
|
||||
}
|
||||
clip.wrapMode = WrapMode.Loop;
|
||||
|
||||
var animation = gltf.animations[i];
|
||||
if (string.IsNullOrEmpty(animation.name))
|
||||
{
|
||||
animation.name = $"animation:{i}";
|
||||
}
|
||||
|
||||
animationClips.Add(AnimationImporterUtil.ConvertAnimationClip(gltf, animation));
|
||||
}
|
||||
|
||||
return animationClips;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 27640e6274339664ea492827be5a2217
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Loading…
Reference in New Issue
Block a user