diff --git a/Assets/UniGLTF/Runtime/Extensions/glTFExtensions.cs b/Assets/UniGLTF/Runtime/Extensions/glTFExtensions.cs
index 7551e18e63..02edf3e123 100644
--- a/Assets/UniGLTF/Runtime/Extensions/glTFExtensions.cs
+++ b/Assets/UniGLTF/Runtime/Extensions/glTFExtensions.cs
@@ -316,6 +316,35 @@ public static T[] GetArrayFromAccessor<T>(this glTF self, int accessorIndex) whe
             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];
diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/AnimationImporter.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/AnimationImporter.cs
deleted file mode 100644
index 85e2e9e080..0000000000
--- a/Assets/UniGLTF/Runtime/UniGLTF/IO/AnimationImporter.cs
+++ /dev/null
@@ -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();
-                }
-            }
-        }
-
-    }
-}
diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/AnimationImporter.cs.meta b/Assets/UniGLTF/Runtime/UniGLTF/IO/AnimationImporter.cs.meta
deleted file mode 100644
index 8f83fec206..0000000000
--- a/Assets/UniGLTF/Runtime/UniGLTF/IO/AnimationImporter.cs.meta
+++ /dev/null
@@ -1,3 +0,0 @@
-fileFormatVersion: 2
-guid: d602384685dd4f179350052013659720
-timeCreated: 1537445972
\ No newline at end of file
diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/AnimationImporterUtil.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/AnimationImporterUtil.cs
new file mode 100644
index 0000000000..9cd5fd680d
--- /dev/null
+++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/AnimationImporterUtil.cs
@@ -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;
+        }
+    }
+}
diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/AnimationImporterUtil.cs.meta b/Assets/UniGLTF/Runtime/UniGLTF/IO/AnimationImporterUtil.cs.meta
new file mode 100644
index 0000000000..70c082d51e
--- /dev/null
+++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/AnimationImporterUtil.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a6e858fe43dba6342902d72392b1bdce
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/IAnimationImporter.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/IAnimationImporter.cs
new file mode 100644
index 0000000000..2c65bbd110
--- /dev/null
+++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/IAnimationImporter.cs
@@ -0,0 +1,7 @@
+namespace UniGLTF
+{
+    public interface IAnimationImporter
+    {
+        void Import(ImporterContext context);
+    }
+}
\ No newline at end of file
diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/IAnimationImporter.cs.meta b/Assets/UniGLTF/Runtime/UniGLTF/IO/IAnimationImporter.cs.meta
new file mode 100644
index 0000000000..83586e0860
--- /dev/null
+++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/IAnimationImporter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4f950ba9271cbff4cbc4345f5a23a5d8
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/ImporterContext.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/ImporterContext.cs
index 8684aa54a4..abf45ef75e 100644
--- a/Assets/UniGLTF/Runtime/UniGLTF/IO/ImporterContext.cs
+++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/ImporterContext.cs
@@ -85,6 +85,26 @@ public string GetSpeedLog()
         }
         #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 @@ protected virtual Schedulable<Unit> LoadAsync()
                 {
                     using (MeasureTime("AnimationImporter"))
                     {
-                        AnimationImporter.ImportAnimation(this);
+                        AnimationImporter.Import(this);
                     }
                 })
                 .ContinueWithCoroutine(Scheduler.MainThread, OnLoadModel)
diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/RootAnimationImporter.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/RootAnimationImporter.cs
new file mode 100644
index 0000000000..5c3b4e406f
--- /dev/null
+++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/RootAnimationImporter.cs
@@ -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;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/RootAnimationImporter.cs.meta b/Assets/UniGLTF/Runtime/UniGLTF/IO/RootAnimationImporter.cs.meta
new file mode 100644
index 0000000000..2ea8cfbed7
--- /dev/null
+++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/RootAnimationImporter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 27640e6274339664ea492827be5a2217
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: