Beispiel #1
0
        public void DividedVertexBufferTest()
        {
            var glTF        = new glTF();
            var bytesBuffer = new ArrayByteBuffer(new byte[50 * 1024 * 1024]);
            var bufferIndex = glTF.AddBuffer(bytesBuffer);

            var Materials = new List <Material> {
                new Material(Shader.Find("Standard")), // A
                new Material(Shader.Find("Standard")), // B
            };

            var(go, mesh) = CreateMesh(Materials.ToArray());
            var meshExportSettings = new GltfExportSettings
            {
                DivideVertexBuffer = true
            };
            var axisInverter = Axes.X.Create();

            var unityMesh = MeshExportList.Create(go);

            var(gltfMesh, blendShapeIndexMap) = meshExportSettings.DivideVertexBuffer
                ? MeshExporter_DividedVertexBuffer.Export(glTF, bufferIndex, unityMesh, Materials, axisInverter, meshExportSettings)
                : MeshExporter_SharedVertexBuffer.Export(glTF, bufferIndex, unityMesh, Materials, axisInverter, meshExportSettings)
            ;

            {
                var indices = glTF.GetIndices(gltfMesh.primitives[0].indices);
                Assert.AreEqual(0, indices[0]);
                Assert.AreEqual(1, indices[1]);
                Assert.AreEqual(3, indices[2]);
                Assert.AreEqual(3, indices[3]);
                Assert.AreEqual(1, indices[4]);
                Assert.AreEqual(2, indices[5]);
            }
            {
                var positions = glTF.GetArrayFromAccessor <Vector3>(gltfMesh.primitives[0].attributes.POSITION);
                Assert.AreEqual(4, positions.Length);
            }

            {
                var indices = glTF.GetIndices(gltfMesh.primitives[1].indices);
                Assert.AreEqual(0, indices[0]);
                Assert.AreEqual(1, indices[1]);
                Assert.AreEqual(3, indices[2]);
                Assert.AreEqual(3, indices[3]);
                Assert.AreEqual(1, indices[4]);
                Assert.AreEqual(2, indices[5]);
            }
            {
                var positions = glTF.GetArrayFromAccessor <Vector3>(gltfMesh.primitives[1].attributes.POSITION);
                Assert.AreEqual(4, positions.Length);
            }
        }
        public static (Getter, int) GetAccessor(glTF gltf, int accessorIndex)
        {
            var gltfAccessor = gltf.accessors[accessorIndex];

            switch (gltfAccessor.componentType)
            {
            case glComponentType.UNSIGNED_BYTE:
            {
                var    array  = gltf.GetArrayFromAccessor <Byte4>(accessorIndex);
                Getter getter = (i) =>
                {
                    var value = array[i];
                    var inv   = 1.0f / byte.MaxValue;
                    return(value.x * inv, value.y *inv, value.z *inv, value.w *inv);
                };
                return(getter, array.Length);
            }

            case glComponentType.UNSIGNED_SHORT:
            {
                var    array  = gltf.GetArrayFromAccessor <UShort4>(accessorIndex);
                Getter getter = (i) =>
                {
                    var value = array[i];
                    var inv   = 1.0f / ushort.MaxValue;
                    return(value.x * inv, value.y *inv, value.z *inv, value.w *inv);
                };
                return(getter, array.Length);
            }

            case glComponentType.FLOAT:
            {
                var    array  = gltf.GetArrayFromAccessor <Vector4>(accessorIndex);
                Getter getter = (i) =>
                {
                    var value = array[i];
                    return(value.x, value.y, value.z, value.w);
                };
                return(getter, array.Length);
            }
            }
 public static (Getter, int) GetAccessor(glTF gltf, int accessorIndex)
 {
     var gltfAccessor = gltf.accessors[accessorIndex];
     switch (gltfAccessor.componentType)
     {
         case glComponentType.UNSIGNED_BYTE:
             {
                 var array = gltf.GetArrayFromAccessor<Byte4>(accessorIndex);
                 Getter getter = (i) =>
                     {
                         var value = array[i];
                         return (value.x, value.y, value.z, value.w);
                     };
                 return (getter, array.Length);
             }
Beispiel #4
0
        public static AnimationClip ConvertAnimationClip(glTF gltf, glTFAnimation animation, AxisInverter inverter, 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.FlatternFloatArrayFromAccessor(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(inverter.InvertVector3(temp).ToArray());
                        }
                        );
                }
                break;

                case glTFAnimationTarget.PATH_ROTATION:
                {
                    var sampler = animation.samplers[channel.sampler];
                    var input   = gltf.GetArrayFromAccessor <float>(sampler.input);
                    var output  = gltf.FlatternFloatArrayFromAccessor(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, inverter.InvertQuaternion(currentQuaternion)).ToArray());
                        }
                        );

                    clip.EnsureQuaternionContinuity();
                }
                break;

                case glTFAnimationTarget.PATH_SCALE:
                {
                    var sampler = animation.samplers[channel.sampler];
                    var input   = gltf.GetArrayFromAccessor <float>(sampler.input);
                    var output  = gltf.FlatternFloatArrayFromAccessor(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);
        }
Beispiel #5
0
        public static void SetupSkinning(glTF gltf, List <TransformWithSkin> nodes, int i, IAxisInverter inverter)
        {
            var x = nodes[i];
            var skinnedMeshRenderer = x.Transform.GetComponent <SkinnedMeshRenderer>();

            if (skinnedMeshRenderer != null)
            {
                var mesh = skinnedMeshRenderer.sharedMesh;
                if (x.SkinIndex.HasValue)
                {
                    if (mesh == null)
                    {
                        throw new Exception();
                    }
                    if (skinnedMeshRenderer == null)
                    {
                        throw new Exception();
                    }

                    if (x.SkinIndex.Value < gltf.skins.Count)
                    {
                        // calculate internal values(boundingBox etc...) when sharedMesh assigned ?
                        skinnedMeshRenderer.sharedMesh = null;

                        var skin   = gltf.skins[x.SkinIndex.Value];
                        var joints = skin.joints.Select(y => nodes[y].Transform).ToArray();
                        if (joints.Any())
                        {
                            // have bones
                            skinnedMeshRenderer.bones = joints;

                            if (skin.inverseBindMatrices != -1)
                            {
                                var bindPoses = gltf.GetArrayFromAccessor <Matrix4x4>(skin.inverseBindMatrices)
                                                .Select(inverter.InvertMat4)
                                                .ToArray()
                                ;
                                mesh.bindposes = bindPoses;
                            }
                            else
                            {
                                //
                                // calc default matrices
                                // https://docs.unity3d.com/ScriptReference/Mesh-bindposes.html
                                //
                                var meshCoords          = skinnedMeshRenderer.transform; // ?
                                var calculatedBindPoses = joints.Select(y => y.worldToLocalMatrix * meshCoords.localToWorldMatrix).ToArray();
                                mesh.bindposes = calculatedBindPoses;
                            }
                        }
                        else
                        {
                            // BlendShape only ?
                        }

                        skinnedMeshRenderer.sharedMesh = mesh;
                        if (skin.skeleton >= 0 && skin.skeleton < nodes.Count)
                        {
                            skinnedMeshRenderer.rootBone = nodes[skin.skeleton].Transform;
                        }
                    }
                }
            }
        }
        static MeshWithMaterials ImportMesh(glTF gltf, int meshIndex, glTFMesh gltfMesh,
                                            List <Material> materials,
                                            GetBlendShapeName getBlendShapeName
                                            )
        {
            glTFAttributes lastAttributes   = null;
            var            sharedAttributes = true;

            foreach (var prim in gltfMesh.primitives)
            {
                if (lastAttributes != null && !prim.attributes.Equals(lastAttributes))
                {
                    sharedAttributes = false;
                    break;
                }
                lastAttributes = prim.attributes;
            }

            var positions       = new List <Vector3>();
            var normals         = new List <Vector3>();
            var tangents        = new List <Vector4>();
            var colors          = new List <Vector4>();
            var uv              = new List <Vector2>();
            var boneWeights     = new List <BoneWeight>();
            var subMeshes       = new List <int[]>();
            var materialIndices = new List <int>();

            BlendShape[] blendShapes = null;

            if (sharedAttributes)
            {
                // multiple submesh sharing same VertexBuffer
                {
                    var prim = gltfMesh.primitives.First();
                    positions.AddRange(gltf.GetArrayFromAccessor <Vector3>(prim.attributes.POSITION).Select(x => x.ReverseZ()));

                    // normal
                    if (prim.attributes.NORMAL != -1)
                    {
                        normals.AddRange(gltf.GetArrayFromAccessor <Vector3>(prim.attributes.NORMAL).Select(x => x.ReverseZ()));
                    }

                    // tangent
                    if (prim.attributes.TANGENT != -1)
                    {
                        tangents.AddRange(gltf.GetArrayFromAccessor <Vector4>(prim.attributes.TANGENT).Select(x => x.ReverseZ()));
                    }

                    // vertex color
                    if (prim.attributes.COLOR_0 != -1)
                    {
                        colors.AddRange(gltf.GetArrayFromAccessor <Vector4>(prim.attributes.COLOR_0));
                    }

                    // uv
                    if (prim.attributes.TEXCOORD_0 != -1)
                    {
                        uv.AddRange(gltf.GetArrayFromAccessor <Vector2>(prim.attributes.TEXCOORD_0).Select(x => x.ReverseY()));
                    }
                    else
                    {
                        // for inconsistent attributes in primitives
                        uv.AddRange(new Vector2[positions.Count]);
                    }

                    // skin
                    if (prim.attributes.JOINTS_0 != -1 && prim.attributes.WEIGHTS_0 != -1)
                    {
                        var joints0  = gltf.GetArrayFromAccessor <UShort4>(prim.attributes.JOINTS_0); // uint4
                        var weights0 = gltf.GetArrayFromAccessor <Float4>(prim.attributes.WEIGHTS_0).Select(x => x.One()).ToArray();

                        for (int j = 0; j < joints0.Length; ++j)
                        {
                            var bw = new BoneWeight();

                            bw.boneIndex0 = joints0[j].x;
                            bw.weight0    = weights0[j].x;

                            bw.boneIndex1 = joints0[j].y;
                            bw.weight1    = weights0[j].y;

                            bw.boneIndex2 = joints0[j].z;
                            bw.weight2    = weights0[j].z;

                            bw.boneIndex3 = joints0[j].w;
                            bw.weight3    = weights0[j].w;

                            boneWeights.Add(bw);
                        }
                    }

                    // blendshape
                    if (prim.targets != null && prim.targets.Count > 0)
                    {
                        if (blendShapes == null)
                        {
                            blendShapes = prim.targets.Select((x, i) => new BlendShape(getBlendShapeName(meshIndex, i))).ToArray();
                        }
                        for (int i = 0; i < prim.targets.Count; ++i)
                        {
                            //var name = string.Format("target{0}", i++);
                            var primTarget = prim.targets[i];
                            var blendShape = blendShapes[i];

                            if (primTarget.POSITION != -1)
                            {
                                blendShape.Positions.AddRange(
                                    gltf.GetArrayFromAccessor <Vector3>(primTarget.POSITION).Select(x => x.ReverseZ()).ToArray());
                            }
                            if (primTarget.NORMAL != -1)
                            {
                                blendShape.Normals.AddRange(
                                    gltf.GetArrayFromAccessor <Vector3>(primTarget.NORMAL).Select(x => x.ReverseZ()).ToArray());
                            }
                            if (primTarget.TANGENT != -1)
                            {
                                blendShape.Tangents.AddRange(
                                    gltf.GetArrayFromAccessor <Vector3>(primTarget.TANGENT).Select(x => x.ReverseZ()).ToArray());
                            }
                        }
                    }
                }

                foreach (var prim in gltfMesh.primitives)
                {
                    var indices = gltf.GetIndices(prim.indices).Select(x => x).ToArray();
                    subMeshes.Add(indices);

                    // material
                    materialIndices.Add(prim.material);
                }
            }
            else
            {
                // multiple submMesh is not sharing a VertexBuffer.
                // each subMesh use a independent VertexBuffer.

                var targets = gltfMesh.primitives[0].targets;
                for (int i = 1; i < gltfMesh.primitives.Count; ++i)
                {
                    if (!gltfMesh.primitives[i].targets.SequenceEqual(targets))
                    {
                        throw new NotImplementedException(string.Format("diffirent targets: {0} with {1}",
                                                                        gltfMesh.primitives[i],
                                                                        targets));
                    }
                }

                foreach (var prim in gltfMesh.primitives)
                {
                    var indexOffset = positions.Count;
                    var indexBuffer = prim.indices;

                    var positionCount = positions.Count;
                    positions.AddRange(gltf.GetArrayFromAccessor <Vector3>(prim.attributes.POSITION).Select(x => x.ReverseZ()));
                    positionCount = positions.Count - positionCount;

                    // normal
                    if (prim.attributes.NORMAL != -1)
                    {
                        normals.AddRange(gltf.GetArrayFromAccessor <Vector3>(prim.attributes.NORMAL).Select(x => x.ReverseZ()));
                    }

                    if (prim.attributes.TANGENT != -1)
                    {
                        tangents.AddRange(gltf.GetArrayFromAccessor <Vector4>(prim.attributes.TANGENT).Select(x => x.ReverseZ()));
                    }

                    // vertex color
                    if (prim.attributes.COLOR_0 != -1)
                    {
                        colors.AddRange(gltf.GetArrayFromAccessor <Vector4>(prim.attributes.COLOR_0));
                    }

                    // uv
                    if (prim.attributes.TEXCOORD_0 != -1)
                    {
                        uv.AddRange(gltf.GetArrayFromAccessor <Vector2>(prim.attributes.TEXCOORD_0).Select(x => x.ReverseY()));
                    }
                    else
                    {
                        // for inconsistent attributes in primitives
                        uv.AddRange(new Vector2[positionCount]);
                    }

                    // skin
                    if (prim.attributes.JOINTS_0 != -1 && prim.attributes.WEIGHTS_0 != -1)
                    {
                        var joints0  = gltf.GetArrayFromAccessor <UShort4>(prim.attributes.JOINTS_0); // uint4
                        var weights0 = gltf.GetArrayFromAccessor <Float4>(prim.attributes.WEIGHTS_0).Select(x => x.One()).ToArray();

                        for (int j = 0; j < joints0.Length; ++j)
                        {
                            var bw = new BoneWeight();

                            bw.boneIndex0 = joints0[j].x;
                            bw.weight0    = weights0[j].x;

                            bw.boneIndex1 = joints0[j].y;
                            bw.weight1    = weights0[j].y;

                            bw.boneIndex2 = joints0[j].z;
                            bw.weight2    = weights0[j].z;

                            bw.boneIndex3 = joints0[j].w;
                            bw.weight3    = weights0[j].w;

                            boneWeights.Add(bw);
                        }
                    }

                    // blendshape
                    if (prim.targets != null && prim.targets.Count > 0)
                    {
                        if (blendShapes == null)
                        {
                            blendShapes = prim.targets.Select((x, i) => new BlendShape(i.ToString())).ToArray();
                        }
                        for (int i = 0; i < prim.targets.Count; ++i)
                        {
                            //var name = string.Format("target{0}", i++);
                            var primTarget = prim.targets[i];
                            var blendShape = blendShapes[i];

                            if (primTarget.POSITION != -1)
                            {
                                blendShape.Positions.AddRange(
                                    gltf.GetArrayFromAccessor <Vector3>(primTarget.POSITION).Select(x => x.ReverseZ()).ToArray());
                            }
                            if (primTarget.NORMAL != -1)
                            {
                                blendShape.Normals.AddRange(
                                    gltf.GetArrayFromAccessor <Vector3>(primTarget.NORMAL).Select(x => x.ReverseZ()).ToArray());
                            }
                            if (primTarget.TANGENT != -1)
                            {
                                blendShape.Tangents.AddRange(
                                    gltf.GetArrayFromAccessor <Vector3>(primTarget.TANGENT).Select(x => x.ReverseZ()).ToArray());
                            }
                        }
                    }

                    var indices =
                        (indexBuffer >= 0)
                     ? gltf.GetIndices(indexBuffer).Select(x => x + indexOffset).ToArray()
                     : TriangleUtil.FlipTriangle(Enumerable.Range(0, positions.Count)).ToArray() // without index array
                    ;
                    subMeshes.Add(indices);

                    // material
                    materialIndices.Add(prim.material);
                }
            }
            if (!materialIndices.Any())
            {
                materialIndices.Add(0);
            }

            //Debug.Log(prims.ToJson());
            var mesh = new Mesh();

            mesh.name = gltfMesh.name;

            if (positions.Count > UInt16.MaxValue)
            {
#if UNITY_2017_3_OR_NEWER
                mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
#else
                Debug.LogWarningFormat("vertices {0} exceed 65535. not implemented. Unity2017.3 supports large mesh", positions.Count);
#endif
            }

            mesh.vertices = positions.ToArray();
            if (normals.Any())
            {
                mesh.normals = normals.ToArray();
            }
            else
            {
                mesh.RecalculateNormals();
            }
            if (tangents.Any())
            {
                mesh.tangents = tangents.ToArray();
            }
            else
            {
                mesh.RecalculateTangents();
            }
            if (colors.Any())
            {
                mesh.colors = colors.ConvertAll(x => new Color(x.x, x.y, x.z, 1f)).ToArray();
            }
            if (uv.Any())
            {
                mesh.uv = uv.ToArray();
            }
            if (boneWeights.Any())
            {
                mesh.boneWeights = boneWeights.ToArray();
            }
            mesh.subMeshCount = subMeshes.Count;
            for (int i = 0; i < subMeshes.Count; ++i)
            {
                mesh.SetTriangles(subMeshes[i], i);
            }
            var result = new MeshWithMaterials
            {
                Mesh      = mesh,
                Materials = materialIndices.Select(x => materials[x]).ToArray()
            };

            List <Material> matList = result.Materials.ToList();
            matList.RemoveAll(x => x == null);
            result.Materials = matList.ToArray();

            if (blendShapes != null)
            {
                foreach (var blendShape in blendShapes)
                {
                    if (blendShape.Positions.Count > 0)
                    {
                        if (blendShape.Positions.Count == mesh.vertexCount)
                        {
                            mesh.AddBlendShapeFrame(blendShape.Name, FRAME_WEIGHT,
                                                    blendShape.Positions.ToArray(),
                                                    normals.Count == mesh.vertexCount ? blendShape.Normals.ToArray() : null,
                                                    null
                                                    );
                        }
                        else
                        {
                            Debug.LogWarningFormat("May be partial primitive has blendShape. Rquire separete mesh or extend blend shape, but not implemented: {0}", blendShape.Name);
                        }
                    }
                }
            }

            return(result);
        }
        public static GameObject Import(IImporterContext ctx, string json,
                                        ArraySegment <Byte> glbBinChunk,
                                        OnLoadCallback callback             = null,
                                        GetBlendShapeName getBlendShapeName = null,
                                        CreateMaterialFunc createMaterial   = null
                                        )
        {
            if (getBlendShapeName == null)
            {
                getBlendShapeName = DefaultGetBlendShapeName;
            }

            // exclude not gltf-2.0
            var parsed = json.ParseAsJson();

            try
            {
                if (parsed["asset"]["version"].GetString() != "2.0")
                {
                    Debug.LogWarningFormat("is not gltf-2.0: {0}", ctx.Path);
                    return(null);
                }
            }
            catch (Exception)
            {
                Debug.LogWarningFormat("{0}: fail to parse json", ctx.Path);
                return(null);
            }

            // parse json
            glTF gltf = null;

            try
            {
                gltf = JsonUtility.FromJson <glTF>(json);
            }
            catch (Exception)
            {
                Debug.LogWarningFormat("{0}: fail to parse json", ctx.Path);
                return(null);
            }
            if (gltf == null)
            {
                Debug.LogWarningFormat("{0}: fail to parse json", ctx.Path);
                return(null);
            }

            if (gltf.asset.version != "2.0")
            {
                Debug.LogWarningFormat("unknown gltf version {0}", gltf.asset.version);
                return(null);
            }

            gltf.baseDir = Path.GetDirectoryName(ctx.Path);
            //Debug.LogFormat("{0}: {1}", ctx.Path, gltf);

            foreach (var buffer in gltf.buffers)
            {
                buffer.OpenStorage(gltf.baseDir, glbBinChunk);
            }

            // textures
            var textures = ImportTextures(gltf)
                           .Select(x =>
            {
                var samplerIndex = gltf.textures[x.TextureIndex].sampler;
                var sampler      = gltf.samplers[samplerIndex];

                if (x.Texture == null)
                {
                    Debug.LogWarningFormat("May be import order, not yet texture is not imported. Later, manualy reimport {0}", ctx.Path);
                }
                else
                {
                    SetSampler(x.Texture, sampler);

                    if (!x.IsAsset)
                    {
                        ctx.AddObjectToAsset(x.Texture.name, x.Texture);
                    }
                }
                return(x);
            })
                           .ToArray();

            if (createMaterial == null)
            {
                createMaterial = CreateMaterialFuncFromShader(Shader.Find("Standard"));
            }

            // materials
            List <Material> materials = new List <Material>();

            if (gltf.materials == null || !gltf.materials.Any())
            {
                materials.Add(createMaterial(ctx, 0, null, textures));
            }
            else
            {
                for (int i = 0; i < gltf.materials.Count; ++i)
                {
                    Material mat = null;

                    // [Maquette Code]
                    if (Maquette.Unity.MqAddonUtility.NeedsMaquetteMaterial(gltf.materials[i].name))
                    {
                        string materialName = gltf.materials[i].name;
                        materialName = materialName.Replace(" (Instance)", "");
                        mat          = Maquette.Unity.MqAddonUtility.CreateMaquetteMaterial(materialName);
                        if (gltf.materials[i].pbrMetallicRoughness.baseColorTexture != null)
                        {
                            int baseTextureIndex = gltf.materials[i].pbrMetallicRoughness.baseColorTexture.index;
                            if (baseTextureIndex >= 0 && baseTextureIndex < textures.Length)
                            {
                                var texture = textures[baseTextureIndex];
                                mat.mainTexture = texture.Texture;
                            }
                        }
                    }
                    else
                    {
                        mat = createMaterial(ctx, i, gltf.materials[i], textures);
                    }

                    materials.Add(mat);
                }
            }
            foreach (var material in materials)
            {
                if (material != null)
                {
                    ctx.AddObjectToAsset(material.name, material);
                }
            }

            // meshes
            var meshes = gltf.meshes.Select((x, i) =>
            {
                var meshWithMaterials = ImportMesh(gltf, i, x, materials, getBlendShapeName);
                var mesh = meshWithMaterials.Mesh;
                if (string.IsNullOrEmpty(mesh.name))
                {
                    mesh.name = string.Format("UniGLTF import#{0}", i);
                }

                ctx.AddObjectToAsset(mesh.name, mesh);

                return(meshWithMaterials);
            }).ToArray();

            // nodes
            var _nodes = gltf.nodes.Select(x => ImportNode(x)).ToArray();

            var nodes = _nodes.Select((go, i) =>
            {
                if (string.IsNullOrEmpty(go.name))
                {
                    go.name = string.Format("node{0:000}", i);
                }

                var nodeWithSkin = new TransformWithSkin
                {
                    Transform = go.transform,
                };

                var node = gltf.nodes[i];

                //
                // build hierachy
                //
                if (node.children != null)
                {
                    foreach (var child in node.children)
                    {
                        _nodes[child].transform.SetParent(_nodes[i].transform,
                                                          false // node has local transform
                                                          );
                    }
                }

                //
                // attach mesh
                //
                if (node.mesh != -1)
                {
                    var mesh = meshes[node.mesh];
                    if (mesh.Mesh.blendShapeCount == 0 && node.skin == -1)
                    {
                        // without blendshape and bone skinning
                        var filter               = go.AddComponent <MeshFilter>();
                        filter.sharedMesh        = mesh.Mesh;
                        var renderer             = go.AddComponent <MeshRenderer>();
                        renderer.sharedMaterials = mesh.Materials;
                    }
                    else
                    {
                        var renderer = go.AddComponent <SkinnedMeshRenderer>();

                        if (node.skin != -1)
                        {
                            nodeWithSkin.SkinIndex = node.skin;
                        }

                        renderer.sharedMesh      = mesh.Mesh;
                        renderer.sharedMaterials = mesh.Materials;
                    }
                }

                return(nodeWithSkin);
            }).ToArray();

            //
            // fix node's coordinate. z-back to z-forward
            //
            var globalTransformMap = nodes.ToDictionary(x => x.Transform, x => new PosRot
            {
                Position = x.Transform.position,
                Rotation = x.Transform.rotation,
            });

            foreach (var x in gltf.rootnodes)
            {
                // fix nodes coordinate
                // reverse Z in global
                var t = nodes[x].Transform;
                //t.SetParent(root.transform, false);

                foreach (var transform in t.Traverse())
                {
                    var g = globalTransformMap[transform];
                    transform.position = g.Position.ReverseZ();
                    transform.rotation = g.Rotation.ReverseZ();
                }
            }

            // skinning
            foreach (var x in nodes)
            {
                var skinnedMeshRenderer = x.Transform.GetComponent <SkinnedMeshRenderer>();
                if (skinnedMeshRenderer != null)
                {
                    var mesh = skinnedMeshRenderer.sharedMesh;
                    if (x.SkinIndex.HasValue)
                    {
                        if (mesh == null)
                        {
                            throw new Exception();
                        }
                        if (skinnedMeshRenderer == null)
                        {
                            throw new Exception();
                        }

                        if (x.SkinIndex.Value < gltf.skins.Count)
                        {
                            var skin = gltf.skins[x.SkinIndex.Value];

                            skinnedMeshRenderer.sharedMesh = null;

                            var joints = skin.joints.Select(y => nodes[y].Transform).ToArray();
                            skinnedMeshRenderer.bones = joints;
                            //skinnedMeshRenderer.rootBone = nodes[skin.skeleton].Transform;

                            if (skin.inverseBindMatrices != -1)
                            {
                                // BlendShape only ?
#if false
                                // https://docs.unity3d.com/ScriptReference/Mesh-bindposes.html
                                var hipsParent          = nodes[0].Transform;
                                var calculatedBindPoses = joints.Select(y => y.worldToLocalMatrix * hipsParent.localToWorldMatrix).ToArray();
                                mesh.bindposes = calculatedBindPoses;
#else
                                var bindPoses = gltf.GetArrayFromAccessor <Matrix4x4>(skin.inverseBindMatrices)
                                                .Select(y => y.ReverseZ())
                                                .ToArray()
                                ;
                                mesh.bindposes = bindPoses;
#endif
                            }

                            skinnedMeshRenderer.sharedMesh = mesh;
                        }
                    }
                }
            }

            var root = new GameObject("_root_");
            ctx.SetMainGameObject("root", root);

            foreach (var x in gltf.rootnodes)
            {
                // fix nodes coordinate
                // reverse Z in global
                var t = nodes[x].Transform;
                t.SetParent(root.transform, false);
            }

            // animation
            if (gltf.animations != null && gltf.animations.Any())
            {
                var clip = new AnimationClip();
                clip.name = ANIMATION_NAME;
                clip.ClearCurves();

                ImportAnimation(root.transform, clip, gltf.animations, nodes.Select(x => x.Transform).ToArray(), gltf);

                clip.legacy   = true;
                clip.name     = "legacy";
                clip.wrapMode = WrapMode.Loop;
                var animation = root.AddComponent <Animation>();
                animation.clip = clip;

                ctx.AddObjectToAsset(ANIMATION_NAME, clip);
            }

            if (callback != null)
            {
                callback(ctx, json,
                         nodes.Select(x => x.Transform).ToArray(),
                         meshes.Select(x => x.Mesh).ToList()
                         );
            }

            Debug.LogFormat("Import {0}", ctx.Path);

            return(root);
        }
        public static void ImportAnimation(Transform root, AnimationClip clip, List <glTFAnimation> animations, Transform[] nodes, glTF gltf)
        {
            for (int i = 0; i < animations.Count; ++i)
            {
                var animation = animations[i];
                if (string.IsNullOrEmpty(animation.name))
                {
                    animation.name = string.Format("animation:{0}", i);
                }

                foreach (var y in animation.channels)
                {
                    var targetTransform = nodes[y.target.node];
                    var relativePath    = targetTransform.RelativePathFrom(root);
                    switch (y.target.path)
                    {
                    case glTFAnimationTarget.PATH_TRANSLATION:
                    {
                        var curveX = new AnimationCurve();
                        var curveY = new AnimationCurve();
                        var curveZ = new AnimationCurve();

                        var sampler = animation.samplers[y.sampler];
                        var input   = gltf.GetArrayFromAccessor <float>(sampler.input);
                        var output  = gltf.GetArrayFromAccessor <Vector3>(sampler.output);
                        for (int j = 0; j < input.Length; ++j)
                        {
                            var time = input[j];
                            var pos  = output[j].ReverseZ();
                            curveX.AddKey(time, pos.x);
                            curveY.AddKey(time, pos.y);
                            curveZ.AddKey(time, pos.z);
                        }

                        clip.SetCurve(relativePath, typeof(Transform), "localPosition.x", curveX);
                        clip.SetCurve(relativePath, typeof(Transform), "localPosition.y", curveY);
                        clip.SetCurve(relativePath, typeof(Transform), "localPosition.z", curveZ);
                    }
                    break;

                    case glTFAnimationTarget.PATH_ROTATION:
                    {
                        var curveX = new AnimationCurve();
                        var curveY = new AnimationCurve();
                        var curveZ = new AnimationCurve();
                        var curveW = new AnimationCurve();

                        var sampler = animation.samplers[y.sampler];
                        var input   = gltf.GetArrayFromAccessor <float>(sampler.input);
                        var output  = gltf.GetArrayFromAccessor <Quaternion>(sampler.output);
                        for (int j = 0; j < input.Length; ++j)
                        {
                            var time = input[j];
                            var rot  = output[j].ReverseZ();
                            curveX.AddKey(time, rot.x);
                            curveY.AddKey(time, rot.y);
                            curveZ.AddKey(time, rot.z);
                            curveW.AddKey(time, rot.w);
                        }

                        clip.SetCurve(relativePath, typeof(Transform), "localRotation.x", curveX);
                        clip.SetCurve(relativePath, typeof(Transform), "localRotation.y", curveY);
                        clip.SetCurve(relativePath, typeof(Transform), "localRotation.z", curveZ);
                        clip.SetCurve(relativePath, typeof(Transform), "localRotation.w", curveW);
                    }
                    break;

                    case glTFAnimationTarget.PATH_SCALE:
                    {
                        var curveX = new AnimationCurve();
                        var curveY = new AnimationCurve();
                        var curveZ = new AnimationCurve();

                        var sampler = animation.samplers[y.sampler];
                        var input   = gltf.GetArrayFromAccessor <float>(sampler.input);
                        var output  = gltf.GetArrayFromAccessor <Vector3>(sampler.output);
                        for (int j = 0; j < input.Length; ++j)
                        {
                            var time  = input[j];
                            var scale = output[j];
                            curveX.AddKey(time, scale.x);
                            curveY.AddKey(time, scale.y);
                            curveZ.AddKey(time, scale.z);
                        }

                        clip.SetCurve(relativePath, typeof(Transform), "localScale.x", curveX);
                        clip.SetCurve(relativePath, typeof(Transform), "localScale.y", curveY);
                        clip.SetCurve(relativePath, typeof(Transform), "localScale.z", curveZ);
                    }
                    break;

                    case glTFAnimationTarget.PATH_WEIGHT:
                    {
                        var node = gltf.nodes[y.target.node];
                        var mesh = gltf.meshes[node.mesh];
                        for (int k = 0; k < mesh.weights.Length; ++k)
                        {
                            //var weight = mesh.weights[k];
                            var curve   = new AnimationCurve();
                            var sampler = animation.samplers[y.sampler];
                            var input   = gltf.GetArrayFromAccessor <float>(sampler.input);
                            var output  = gltf.GetArrayFromAccessor <float>(sampler.output);
                            for (int j = 0; j < input.Length; ++j)
                            {
                                curve.AddKey(input[j], output[j]);
                            }

                            clip.SetCurve(relativePath, typeof(SkinnedMeshRenderer), "blendShape." + k, curve);
                        }
                    }
                    break;
                    }
                }
            }
        }