private void SaveIndices(UnityEngine.Mesh mesh, MeshPrimitive primitive, int i, ref EntryBufferView bufferView)
        {
            primitive.Mode = DrawMode.Triangles;

            if (_mesh2indices.ContainsKey(mesh) && _mesh2indices[mesh].ContainsKey(i))
            {
                primitive.Indices = _mesh2indices[mesh][i];
                return;
            }

            if (bufferView == null)
            {
                bufferView = CreateStreamBufferView(mesh.name + "-indices");
            }

            primitive.Indices = AccessorToId(
                ExporterUtils.PackToBuffer(bufferView.streamBuffer, mesh.GetTriangles(i), GLTFComponentType.UnsignedShort, (int[] data, int index) => {
                    var offset = index % 3;

                    return data[offset == 0 ? index : offset == 1 ? index + 1 : index - 1];
                }),
                bufferView
            );
            primitive.Indices.Value.Name += "-" + i;

            if (!_mesh2indices.ContainsKey(mesh))
            {
                _mesh2indices.Add(mesh, new Dictionary<int, AccessorId>());
            }

            _mesh2indices[mesh].Add(i, primitive.Indices);
        }
        public SkinId SaveSkin(Transform tr)
        {
            if (root.Skins == null)
            {
                root.Skins = new List<GLTF.Schema.Skin>();
            }

            var skinMesh = tr.GetComponent<SkinnedMeshRenderer>();

            if (_skin2ID.ContainsKey(skinMesh))
            {
                return _skin2ID[skinMesh];
            }

            var node = tr2node[tr];
            var skin = new Skin();
            skin.Name = "skeleton-" + skinMesh.rootBone.name + "-" + tr.name;
            skin.Skeleton = new NodeId { Id = root.Nodes.IndexOf(node), Root = root };
            skin.Joints = new List<NodeId>();

            foreach (var bone in skinMesh.bones)
            {
                if (!tr2node.ContainsKey(bone))
                {
                    throw new Exception("You are expoting a skinned mesh '" + node.Name + "', but not select bones!");
                }

                skin.Joints.Add(new NodeId { Id = root.Nodes.IndexOf(tr2node[bone]) });
            }

            // Create invBindMatrices accessor
            var bufferView = CreateStreamBufferView("invBind-" + skinMesh.rootBone.name + "-" + tr.name);

            Matrix4x4[] invBindMatrices = new Matrix4x4[skin.Joints.Count];
            for (int i = 0; i < skinMesh.bones.Length; ++i)
            {
                // Generates inverseWorldMatrix in right-handed coordinate system
                Matrix4x4 invBind = skinMesh.sharedMesh.bindposes[i];
                invBindMatrices[i] = Utils.ConvertMat4LeftToRightHandedness(ref invBind);
            }

            skin.InverseBindMatrices = AccessorToId(
                ExporterUtils.PackToBuffer(bufferView.streamBuffer, invBindMatrices, GLTFComponentType.Float),
                bufferView
            );

            root.Skins.Add(skin);

            var id = new SkinId { Id = root.Skins.Count - 1, Root = root };
            _skin2ID.Add(skinMesh, id);

            return id;
        }
        private List<string> BakeAnimationClip(GLTF.Schema.Animation anim, Transform tr, AnimationClip clip)
        {
            var needGenerate = !_animClip2Accessors.ContainsKey(clip);
            Dictionary<string, Dictionary<GLTFAnimationChannelPath, AnimationCurve[]>> curves = null;
            Dictionary<string, bool> rotationIsEuler = null;

            if (needGenerate)
            {
                curves = new Dictionary<string, Dictionary<GLTFAnimationChannelPath, AnimationCurve[]>>();
                rotationIsEuler = new Dictionary<string, bool>();
            }

            List<string> targets = new List<string>();

            foreach (var binding in AnimationUtility.GetCurveBindings(clip))
            {
                var path = binding.path;
                var curve = AnimationUtility.GetEditorCurve(clip, binding);

                if (!curves.ContainsKey(path))
                {
                    targets.Add(path);
                    if (needGenerate)
                    {
                        curves.Add(path, new Dictionary<GLTFAnimationChannelPath, AnimationCurve[]>());
                        rotationIsEuler.Add(path, false);
                    }
                }

                if (!needGenerate)
                {
                    continue;
                }

                var current = curves[path];
                if (binding.propertyName.Contains("m_LocalPosition"))
                {
                    if (!current.ContainsKey(GLTFAnimationChannelPath.translation))
                    {
                        current.Add(GLTFAnimationChannelPath.translation, new AnimationCurve[3]);
                    }
                    if (binding.propertyName.Contains(".x"))
                        current[GLTFAnimationChannelPath.translation][0] = curve;
                    else if (binding.propertyName.Contains(".y"))
                        current[GLTFAnimationChannelPath.translation][1] = curve;
                    else if (binding.propertyName.Contains(".z"))
                        current[GLTFAnimationChannelPath.translation][2] = curve;
                }
                else if (binding.propertyName.Contains("m_LocalScale"))
                {
                    if (!current.ContainsKey(GLTFAnimationChannelPath.scale))
                    {
                        current.Add(GLTFAnimationChannelPath.scale, new AnimationCurve[3]);
                    }
                    if (binding.propertyName.Contains(".x"))
                        current[GLTFAnimationChannelPath.scale][0] = curve;
                    else if (binding.propertyName.Contains(".y"))
                        current[GLTFAnimationChannelPath.scale][1] = curve;
                    else if (binding.propertyName.Contains(".z"))
                        current[GLTFAnimationChannelPath.scale][2] = curve;
                }
                else if (binding.propertyName.ToLower().Contains("localrotation"))
                {
                    if (!current.ContainsKey(GLTFAnimationChannelPath.rotation))
                    {
                        current.Add(GLTFAnimationChannelPath.rotation, new AnimationCurve[4]);
                    }
                    if (binding.propertyName.Contains(".x"))
                        current[GLTFAnimationChannelPath.rotation][0] = curve;
                    else if (binding.propertyName.Contains(".y"))
                        current[GLTFAnimationChannelPath.rotation][1] = curve;
                    else if (binding.propertyName.Contains(".z"))
                        current[GLTFAnimationChannelPath.rotation][2] = curve;
                    else if (binding.propertyName.Contains(".w"))
                        current[GLTFAnimationChannelPath.rotation][3] = curve;
                }
                // Takes into account 'localEuler', 'localEulerAnglesBaked' and 'localEulerAnglesRaw'
                else if (binding.propertyName.ToLower().Contains("localeuler"))
                {
                    if (!current.ContainsKey(GLTFAnimationChannelPath.rotation))
                    {
                        current.Add(GLTFAnimationChannelPath.rotation, new AnimationCurve[3]);
                        rotationIsEuler[path] = true;
                    }
                    if (binding.propertyName.Contains(".x"))
                        current[GLTFAnimationChannelPath.rotation][0] = curve;
                    else if (binding.propertyName.Contains(".y"))
                        current[GLTFAnimationChannelPath.rotation][1] = curve;
                    else if (binding.propertyName.Contains(".z"))
                        current[GLTFAnimationChannelPath.rotation][2] = curve;
                }
                //todo: weights
            }

            if (!needGenerate)
            {
                return targets;
            }

            
            var bufferView = CreateStreamBufferView("animation-" + anim.Name);
            int nbSamples = (int)(clip.length * 30);
            float deltaTime = clip.length / nbSamples;
            var accessors = new List<Dictionary<GLTFAnimationChannelPath, AccessorId>>();
            _animClip2Accessors.Add(clip, accessors);

            foreach (var path in curves.Keys)
            {
                var accessor = new Dictionary<GLTFAnimationChannelPath, AccessorId>();
                accessors.Add(accessor);
                float[] times = new float[nbSamples];
                Vector3[] translations = null;
                Vector3[] scales = null;
                Vector4[] rotations = null;
                foreach (var curve in curves[path])
                {
                    if (curve.Key == GLTFAnimationChannelPath.translation)
                    {
                        translations = new Vector3[nbSamples];
                    }
                    else if (curve.Key == GLTFAnimationChannelPath.scale)
                    {
                        scales = new Vector3[nbSamples];
                    }
                    else if (curve.Key == GLTFAnimationChannelPath.rotation)
                    {
                        rotations = new Vector4[nbSamples];
                    }
                }

                for (int i = 0; i < nbSamples; i += 1)
                {
                    var currentTime = i * deltaTime;
                    times[i] = currentTime;

                    if (translations != null)
                    {
                        var curve = curves[path][GLTFAnimationChannelPath.translation];
                        translations[i] = new Vector3(curve[0].Evaluate(currentTime), curve[1].Evaluate(currentTime), curve[2].Evaluate(currentTime));
                    }

                    if (scales != null)
                    {
                        var curve = curves[path][GLTFAnimationChannelPath.scale];
                        scales[i] = new Vector3(curve[0].Evaluate(currentTime), curve[1].Evaluate(currentTime), curve[2].Evaluate(currentTime));
                    }

                    if (rotations != null)
                    {
                        var curve = curves[path][GLTFAnimationChannelPath.rotation];
                        if (rotationIsEuler[path])
                        {
                            var q = Quaternion.Euler(curve[0].Evaluate(currentTime), curve[1].Evaluate(currentTime), curve[2].Evaluate(currentTime));
                            rotations[i] = new Vector4(q.x, q.y, q.z, q.w);
                        } else
                        {
                            rotations[i] = new Vector4(curve[0].Evaluate(currentTime), curve[1].Evaluate(currentTime), curve[2].Evaluate(currentTime), curve[3].Evaluate(currentTime));
                        }
                    }
                }

                if (!_animClip2TimeAccessor.ContainsKey(clip))
                {
                    var timeView = ExporterUtils.PackToBuffer(bufferView.streamBuffer, times, GLTFComponentType.Float);
                    _animClip2TimeAccessor.Add(clip, AccessorToId(timeView, bufferView));
                    timeView.Name += "-times";
                }

                AccessorId id = null;
                if (translations != null)
                {
                    id = AccessorToId(ExporterUtils.PackToBuffer(bufferView.streamBuffer, translations, GLTFComponentType.Float, (Vector3[] data, int i) => { return Utils.ConvertVector3LeftToRightHandedness(ref data[i]); }), bufferView);
                    accessor.Add(GLTFAnimationChannelPath.translation, id);
                    id.Value.Name += "-" + path + "-" + "translation";
                }

                if (scales != null)
                {
                    id = AccessorToId(ExporterUtils.PackToBuffer(bufferView.streamBuffer, scales, GLTFComponentType.Float), bufferView);
                    accessor.Add(GLTFAnimationChannelPath.scale, id);
                    id.Value.Name += "-" + path + "-" + "scales";
                }

                if (rotations != null)
                {
                    id = AccessorToId(ExporterUtils.PackToBuffer(bufferView.streamBuffer, rotations, GLTFComponentType.Float, (Vector4[] data, int i) => { return Utils.ConvertVector4LeftToRightHandedness(ref data[i]); }), bufferView);
                    accessor.Add(GLTFAnimationChannelPath.rotation, id);
                    id.Value.Name += "-" + path + "-" + "rotations";
                }
            }

            return targets;
        }
        private AccessorId PackAttrToBufferShort<DataType>(EntryBufferView bufferView, DataType[] data, int offset, Func<DataType[], int, DataType> getValueByIndex = null)
        {
            var accessor = ExporterUtils.PackToBuffer(bufferView.byteBuffer, data, GLTFComponentType.UnsignedShort, offset, bufferView.view.ByteStride, getValueByIndex);

            return AccessorToId(accessor, bufferView);
        }