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}"); } }
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}"); } }
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}"); } }
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}"); } }
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); } }
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); }
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); }
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); }
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); }
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; } } } } }
public static NativeArray <Vector3> GetPositions(this glTFPrimitives primitives, GltfData data) { return(data.GetArrayFromAccessor <Vector3>(primitives.attributes.POSITION)); }
/// <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); } }
/// <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); } }