Example #1
0
        public static NativeArray <Vector3> GetPositions(this glTFPrimitives primitives, GltfData data)
        {
            var accessor = data.GLTF.accessors[primitives.attributes.POSITION];

            if (accessor.type != "VEC3")
            {
                throw new ArgumentException($"unknown POSITION type: {accessor.componentType}:{accessor.type}");
            }

            if (accessor.componentType == glComponentType.FLOAT)
            {
                return(data.GetArrayFromAccessor <Vector3>(primitives.attributes.POSITION));
            }
            else if (accessor.componentType == glComponentType.UNSIGNED_SHORT)
            {
                // KHR_mesh_quantization
                // not UShort3 for 4byte alignment !
                var src   = data.GetArrayFromAccessor <UShort4>(primitives.attributes.POSITION);
                var array = data.NativeArrayManager.CreateNativeArray <Vector3>(src.Length);
                for (int i = 0; i < src.Length; ++i)
                {
                    var v = src[i];
                    array[i] = new Vector3(v.x, v.y, v.z);
                }
                return(array);
            }
            else
            {
                throw new NotImplementedException($"unknown POSITION type: {accessor.componentType}:{accessor.type}");
            }
        }
Example #2
0
        public static NativeArray <Vector3>?GetNormals(this glTFPrimitives primitives, GltfData data, int positionsLength)
        {
            if (!HasNormal(primitives))
            {
                return(null);
            }

            var accessor = data.GLTF.accessors[primitives.attributes.NORMAL];

            if (accessor.type != "VEC3")
            {
                throw new ArgumentException($"unknown NORMAL type: {accessor.componentType}:{accessor.type}");
            }

            if (accessor.componentType == glComponentType.FLOAT)
            {
                var result = data.GetArrayFromAccessor <Vector3>(primitives.attributes.NORMAL);
                if (result.Length != positionsLength)
                {
                    throw new Exception("NORMAL is different in length from POSITION");
                }
                return(result);
            }
            else if (accessor.componentType == glComponentType.BYTE)
            {
                // KHR_mesh_quantization
                // not Byte3 for 4byte alignment !
                var src   = data.GetArrayFromAccessor <SByte4>(primitives.attributes.NORMAL);
                var array = data.NativeArrayManager.CreateNativeArray <Vector3>(src.Length);
                if (accessor.normalized)
                {
                    var factor = 1.0f / 127.0f;
                    for (int i = 0; i < src.Length; ++i)
                    {
                        var v = src[i];
                        array[i] = new Vector3(
                            v.x * factor,
                            v.y * factor,
                            v.z * factor);
                    }
                }
                else
                {
                    for (int i = 0; i < src.Length; ++i)
                    {
                        var v = src[i];
                        array[i] = new Vector3(v.x, v.y, v.z);
                    }
                }
                return(array);
            }
            else
            {
                throw new NotImplementedException($"unknown NORMAL type: {accessor.componentType}:{accessor.type}");
            }
        }
Example #3
0
        public static NativeArray <Color>?GetColors(this glTFPrimitives primitives, GltfData data, int positionsLength)
        {
            if (!HasColor(primitives))
            {
                return(null);
            }

            switch (data.GLTF.accessors[primitives.attributes.COLOR_0].TypeCount)
            {
            case 3:
            {
                var vec3Color = data.GetArrayFromAccessor <Vector3>(primitives.attributes.COLOR_0);
                if (vec3Color.Length != positionsLength)
                {
                    throw new Exception("different length");
                }
                var colors = data.NativeArrayManager.CreateNativeArray <Color>(vec3Color.Length);

                for (var index = 0; index < vec3Color.Length; index++)
                {
                    var color = vec3Color[index];
                    colors[index] = new Color(color.x, color.y, color.z);
                }

                return(colors);
            }

            case 4:
                var result = data.GetArrayFromAccessor <Color>(primitives.attributes.COLOR_0);
                if (result.Length != positionsLength)
                {
                    throw new Exception("different length");
                }

                return(result);

            default:
                throw new NotImplementedException(
                          $"unknown color type {data.GLTF.accessors[primitives.attributes.COLOR_0].type}");
            }
        }
Example #4
0
        public static NativeArray <Vector2>?GetTexCoords0(this glTFPrimitives primitives, GltfData data, int positionsLength)
        {
            if (!HasTexCoord0(primitives))
            {
                return(null);
            }

            var accessor = data.GLTF.accessors[primitives.attributes.TEXCOORD_0];

            if (accessor.type != "VEC2")
            {
                throw new ArgumentException($"unknown TEXCOORD_0 type: {accessor.componentType}:{accessor.type}");
            }

            if (accessor.componentType == glComponentType.FLOAT)
            {
                var result = data.GetArrayFromAccessor <Vector2>(primitives.attributes.TEXCOORD_0);
                if (result.Length != positionsLength)
                {
                    throw new Exception("different length");
                }

                return(result);
            }
            if (accessor.componentType == glComponentType.UNSIGNED_SHORT)
            {
                // KHR_mesh_quantization
                var src   = data.GetArrayFromAccessor <UShort2>(primitives.attributes.TEXCOORD_0);
                var array = data.NativeArrayManager.CreateNativeArray <Vector2>(src.Length);
                for (int i = 0; i < src.Length; ++i)
                {
                    var v = src[i];
                    array[i] = new Vector2(v.x, v.y);
                }
                return(array);
            }
            else
            {
                throw new ArgumentException($"unknown TEXCOORD_0 type: {accessor.componentType}:{accessor.type}");
            }
        }
Example #5
0
        public static (Getter, int) GetAccessor(GltfData data, int accessorIndex)
        {
            var gltfAccessor = data.GLTF.accessors[accessorIndex];

            switch (gltfAccessor.componentType)
            {
            case glComponentType.UNSIGNED_BYTE:
            {
                var    array  = data.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  = data.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  = data.GetArrayFromAccessor <Vector4>(accessorIndex);
                Getter getter = (i) =>
                {
                    var value = array[i];
                    return(value.x, value.y, value.z, value.w);
                };
                return(getter, array.Length);
            }
            }
Example #6
0
        public static NativeArray <Vector2>?GetTexCoords1(this glTFPrimitives primitives, GltfData data, int positionsLength)
        {
            if (!HasTexCoord1(primitives))
            {
                return(null);
            }
            var result = data.GetArrayFromAccessor <Vector2>(primitives.attributes.TEXCOORD_1);

            if (result.Length != positionsLength)
            {
                throw new Exception("different length");
            }

            return(result);
        }
Example #7
0
        public static NativeArray <Vector3>?GetNormals(this glTFPrimitives primitives, GltfData data, int positionsLength)
        {
            if (!HasNormal(primitives))
            {
                return(null);
            }
            var result = data.GetArrayFromAccessor <Vector3>(primitives.attributes.NORMAL);

            if (result.Length != positionsLength)
            {
                throw new Exception("different length");
            }

            return(result);
        }
Example #8
0
        public static (Getter, int) GetAccessor(GltfData data, int accessorIndex)
        {
            var gltfAccessor = data.GLTF.accessors[accessorIndex];

            switch (gltfAccessor.componentType)
            {
            case glComponentType.UNSIGNED_BYTE:
            {
                var    array  = data.GetArrayFromAccessor <Byte4>(accessorIndex);
                Getter getter = (i) =>
                {
                    var value = array[i];
                    return(value.x, value.y, value.z, value.w);
                };
                return(getter, array.Length);
            }
Example #9
0
        public static AnimationClip ConvertAnimationClip(GltfData data, glTFAnimation animation, IAxisInverter 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(data.GLTF.nodes, root, data.GLTF.nodes[channel.target.node]);
                switch (channel.target.path)
                {
                case glTFAnimationTarget.PATH_TRANSLATION:
                {
                    var sampler = animation.samplers[channel.sampler];
                    var input   = data.GetArrayFromAccessor <float>(sampler.input);
                    var output  = data.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   = data.GetArrayFromAccessor <float>(sampler.input);
                    var output  = data.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   = data.GetArrayFromAccessor <float>(sampler.input);
                    var output  = data.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      = data.GLTF.nodes[channel.target.node];
                    var mesh      = data.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 UniGLTFNotSupportedException("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   = data.GetArrayFromAccessor <float>(sampler.input);
                    var output  = data.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);
        }
Example #10
0
        public static void SetupSkinning(GltfData data, 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 < data.GLTF.skins.Count)
                    {
                        // calculate internal values(boundingBox etc...) when sharedMesh assigned ?
                        skinnedMeshRenderer.sharedMesh = null;

                        var skin   = data.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 = data.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;
                        }
                    }
                }
            }
        }
Example #11
0
 public static NativeArray <Vector3> GetPositions(this glTFPrimitives primitives, GltfData data)
 {
     return(data.GetArrayFromAccessor <Vector3>(primitives.attributes.POSITION));
 }
Example #12
0
        /// <summary>
        ///
        /// 各primitiveが同じ attribute を共有している場合専用のローダー。
        ///
        /// </summary>
        /// <param name="ctx"></param>
        /// <param name="gltfMesh"></param>
        /// <returns></returns>
        private void ImportMeshSharingVertexBuffer(GltfData data, glTFMesh gltfMesh, IAxisInverter inverter)
        {
            var isOldVersion = data.GLTF.IsGeneratedUniGLTFAndOlder(1, 16);

            {
                //  同じVertexBufferを共有しているので先頭のモノを使う
                var primitives = gltfMesh.primitives.First();

                var positions  = primitives.GetPositions(data);
                var normals    = primitives.GetNormals(data, positions.Length);
                var texCoords0 = primitives.GetTexCoords0(data, positions.Length);
                var texCoords1 = primitives.GetTexCoords1(data, positions.Length);
                var colors     = primitives.GetColors(data, positions.Length);
                var skinning   = SkinningInfo.Create(data, gltfMesh, primitives);
                AssignBoneWeight = skinning.ShouldSetRendererNodeAsBone;

                CheckAttributeUsages(primitives);

                for (var i = 0; i < positions.Length; ++i)
                {
                    var position = inverter.InvertVector3(positions[i]);
                    var normal   = normals != null?inverter.InvertVector3(normals.Value[i]) : Vector3.zero;

                    var texCoord0 = Vector2.zero;
                    if (texCoords0 != null)
                    {
                        if (isOldVersion)
                        {
#pragma warning disable 0612
                            texCoord0 = texCoords0.Value[i].ReverseY();
#pragma warning restore 0612
                        }
                        else
                        {
                            texCoord0 = texCoords0.Value[i].ReverseUV();
                        }
                    }

                    var texCoord1 = texCoords1 != null ? texCoords1.Value[i].ReverseUV() : Vector2.zero;
                    var color     = colors != null ? colors.Value[i] : Color.white;

                    AddVertex(
                        new MeshVertex(
                            position,
                            normal,
                            texCoord0,
                            texCoord1,
                            color));
                    var skin =
                        skinning.GetSkinnedVertex(i);
                    if (skin.HasValue)
                    {
                        AddSkin(skin.Value);
                    }
                }

                // blendshape
                if (primitives.targets != null && primitives.targets.Count > 0)
                {
                    for (int i = 0; i < primitives.targets.Count; ++i)
                    {
                        var primTarget = primitives.targets[i];

                        var hasPosition = primTarget.POSITION != -1 && data.GLTF.accessors[primTarget.POSITION].count == positions.Length;
                        var hasNormal   = primTarget.NORMAL != -1 && data.GLTF.accessors[primTarget.NORMAL].count == positions.Length;
                        var hasTangent  = primTarget.TANGENT != -1 && data.GLTF.accessors[primTarget.TANGENT].count == positions.Length;

                        var blendShape = new BlendShape(i.ToString(), positions.Length, hasPosition, hasNormal, hasTangent);
                        _blendShapes.Add(blendShape);

                        if (hasPosition)
                        {
                            var morphPositions = data.GetArrayFromAccessor <Vector3>(primTarget.POSITION);
                            blendShape.Positions.Capacity = morphPositions.Length;
                            for (var j = 0; j < positions.Length; ++j)
                            {
                                blendShape.Positions.Add(inverter.InvertVector3(morphPositions[j]));
                            }
                        }

                        if (hasNormal)
                        {
                            var morphNormals = data.GetArrayFromAccessor <Vector3>(primTarget.NORMAL);
                            blendShape.Normals.Capacity = morphNormals.Length;
                            for (var j = 0; j < positions.Length; ++j)
                            {
                                blendShape.Normals.Add(inverter.InvertVector3(morphNormals[j]));
                            }
                        }

                        if (hasTangent)
                        {
                            var morphTangents = data.GetArrayFromAccessor <Vector3>(primTarget.TANGENT);
                            blendShape.Tangents.Capacity = morphTangents.Length;
                            for (var j = 0; j < positions.Length; ++j)
                            {
                                blendShape.Tangents.Add(inverter.InvertVector3(morphTangents[j]));
                            }
                        }
                    }
                }
            }

            foreach (var primitive in gltfMesh.primitives)
            {
                if (primitive.indices >= 0)
                {
                    var indexOffset = _currentIndexCount;
                    var indices     = data.GetIndicesFromAccessorIndex(primitive.indices);
                    PushIndices(indices, 0);
                    _subMeshes.Add(new SubMeshDescriptor(indexOffset, indices.Count));
                }
                else
                {
                    var indexOffset = _currentIndexCount;
                    var positions   = data.GLTF.accessors[primitive.attributes.POSITION];
                    for (int i = 0; i < positions.count; i += 3)
                    {
                        // flip triangle
                        AddIndex(i + 2);
                        AddIndex(i + 1);
                        AddIndex(i);
                    }
                    _subMeshes.Add(new SubMeshDescriptor(indexOffset, positions.count));
                }

                // material
                _materialIndices.Add(primitive.material);
            }
        }
Example #13
0
        /// <summary>
        /// 各 primitive の attribute の要素が同じでない。=> uv が有るものと無いものが混在するなど
        /// glTF 的にはありうる。
        ///
        /// primitive を独立した(Independent) Mesh として扱いこれを連結する。
        /// </summary>
        /// <param name="ctx"></param>
        /// <param name="gltfMesh"></param>
        /// <returns></returns>
        private void ImportMeshIndependentVertexBuffer(GltfData data, glTFMesh gltfMesh, IAxisInverter inverter)
        {
            bool isOldVersion = data.GLTF.IsGeneratedUniGLTFAndOlder(1, 16);

            foreach (var primitives in gltfMesh.primitives)
            {
                var vertexOffset     = _currentVertexCount;
                var indexBufferCount = primitives.indices;

                // position は必ずある
                var positions  = primitives.GetPositions(data);
                var normals    = primitives.GetNormals(data, positions.Length);
                var texCoords0 = primitives.GetTexCoords0(data, positions.Length);
                var texCoords1 = primitives.GetTexCoords1(data, positions.Length);
                var colors     = primitives.GetColors(data, positions.Length);
                var skinning   = SkinningInfo.Create(data, gltfMesh, primitives);
                AssignBoneWeight = skinning.ShouldSetRendererNodeAsBone;

                CheckAttributeUsages(primitives);

                for (var i = 0; i < positions.Length; ++i)
                {
                    var position = inverter.InvertVector3(positions[i]);
                    var normal   = normals != null?inverter.InvertVector3(normals.Value[i]) : Vector3.zero;

                    var texCoord0 = Vector2.zero;
                    if (texCoords0 != null)
                    {
                        if (isOldVersion)
                        {
#pragma warning disable 0612
                            // backward compatibility
                            texCoord0 = texCoords0.Value[i].ReverseY();
#pragma warning restore 0612
                        }
                        else
                        {
                            texCoord0 = texCoords0.Value[i].ReverseUV();
                        }
                    }

                    var texCoord1 = texCoords1 != null ? texCoords1.Value[i].ReverseUV() : Vector2.zero;

                    var color = colors != null ? colors.Value[i] : Color.white;
                    AddVertex(
                        new MeshVertex(
                            position,
                            normal,
                            texCoord0,
                            texCoord1,
                            color
                            ));
                    var skin = skinning.GetSkinnedVertex(i);
                    if (skin.HasValue)
                    {
                        AddSkin(skin.Value);
                    }
                }

                // blendshape
                if (primitives.targets != null && primitives.targets.Count > 0)
                {
                    for (var i = 0; i < primitives.targets.Count; ++i)
                    {
                        var primTarget = primitives.targets[i];
                        var blendShape = GetOrCreateBlendShape(i);
                        if (primTarget.POSITION != -1)
                        {
                            var array = data.GetArrayFromAccessor <Vector3>(primTarget.POSITION);
                            if (array.Length != positions.Length)
                            {
                                throw new Exception("different length");
                            }

                            blendShape.Positions.AddRange(array.Select(inverter.InvertVector3).ToArray());
                        }

                        if (primTarget.NORMAL != -1)
                        {
                            var array = data.GetArrayFromAccessor <Vector3>(primTarget.NORMAL);
                            if (array.Length != positions.Length)
                            {
                                throw new Exception("different length");
                            }

                            blendShape.Normals.AddRange(array.Select(inverter.InvertVector3).ToArray());
                        }

                        if (primTarget.TANGENT != -1)
                        {
                            var array = data.GetArrayFromAccessor <Vector3>(primTarget.TANGENT);
                            if (array.Length != positions.Length)
                            {
                                throw new Exception("different length");
                            }

                            blendShape.Tangents.AddRange(array.Select(inverter.InvertVector3).ToArray());
                        }
                    }
                }

                if (indexBufferCount >= 0)
                {
                    var indexOffset = _currentIndexCount;
                    var dataIndices = data.GetIndicesFromAccessorIndex(indexBufferCount);
                    PushIndices(dataIndices, vertexOffset);
                    _subMeshes.Add(new SubMeshDescriptor(indexOffset, dataIndices.Count));
                }
                else
                {
                    var indexOffset = _currentIndexCount;
                    for (int i = 0; i < positions.Count(); i += 3)
                    {
                        // flip triangle
                        AddIndex(i + vertexOffset + 2);
                        AddIndex(i + vertexOffset + 1);
                        AddIndex(i + vertexOffset);
                    }
                    _subMeshes.Add(new SubMeshDescriptor(indexOffset, positions.Count()));
                }

                // material
                _materialIndices.Add(primitives.material);
            }
        }