void CreateGameObjects(Transform parent, byte[] bytes) { var primitives = new List <Primitive>(gltf.meshes.Length); var meshPrimitiveIndex = new int[gltf.meshes.Length + 1]; Texture2D[] images = null; resources = new List <UnityEngine.Object>(); if (gltf.images != null) { images = new Texture2D[gltf.images.Length]; for (int i = 0; i < images.Length; i++) { var img = gltf.images[i]; if (img.mimeType == "image/jpeg" || img.mimeType == "image/png") { if (img.bufferView >= 0) { var bufferView = gltf.bufferViews[img.bufferView]; var chunk = binChunks[bufferView.buffer]; var imgBytes = Extractor.CreateBufferViewCopy(bufferView, chunk, bytes); var txt = new UnityEngine.Texture2D(4, 4); txt.name = string.IsNullOrEmpty(img.name) ? string.Format("glb embed texture {0}", i) : img.name; txt.LoadImage(imgBytes); images[i] = txt; resources.Add(txt); } else if (!string.IsNullOrEmpty(img.uri)) { Debug.LogError("Loading from URI not supported"); } } else { Debug.LogErrorFormat("Unknown image mime type {0}", img.mimeType); } } } if (gltf.materials != null) { materials = new UnityEngine.Material[gltf.materials.Length]; for (int i = 0; i < materials.Length; i++) { materials[i] = materialGenerator.GenerateMaterial(gltf.materials[i], gltf.textures, images, resources); } } //foreach( var mesh in gltf.meshes ) { for (int meshIndex = 0; meshIndex < gltf.meshes.Length; meshIndex++) { var mesh = gltf.meshes[meshIndex]; meshPrimitiveIndex[meshIndex] = primitives.Count; foreach (var primitive in mesh.primitives) { // index var accessor = gltf.accessors[primitive.indices]; var bufferView = gltf.bufferViews[accessor.bufferView]; var buffer = bufferView.buffer; GlbBinChunk chunk = binChunks[buffer]; Assert.AreEqual(accessor.typeEnum, GLTFAccessorAttributeType.SCALAR); //Assert.AreEqual(accessor.count * GetLength(accessor.typeEnum) * 4 , (int) chunk.length); int[] indices = null; switch (accessor.componentType) { case GLTFComponentType.UnsignedByte: indices = Extractor.GetIndicesUInt8(bytes, accessor.byteOffset + bufferView.byteOffset + chunk.start, accessor.count); break; case GLTFComponentType.UnsignedShort: indices = Extractor.GetIndicesUInt16(bytes, accessor.byteOffset + bufferView.byteOffset + chunk.start, accessor.count); break; case GLTFComponentType.UnsignedInt: indices = Extractor.GetIndicesUInt32(bytes, accessor.byteOffset + bufferView.byteOffset + chunk.start, accessor.count); break; default: Debug.LogErrorFormat("Invalid index format {0}", accessor.componentType); break; } // position int pos = primitive.attributes.POSITION; Assert.IsTrue(pos >= 0); #if DEBUG Assert.AreEqual(GetAccessorTye(gltf.accessors[pos].typeEnum), typeof(Vector3)); #endif var positions = gltf.IsAccessorInterleaved(pos) ? GetAccessorDataInterleaved <Vector3>(pos, ref bytes, Extractor.GetVector3sInterleaved) : GetAccessorData <Vector3>(pos, ref bytes, Extractor.GetVector3s); Vector3[] normals = null; if (primitive.attributes.NORMAL >= 0) { #if DEBUG Assert.AreEqual(GetAccessorTye(gltf.accessors[primitive.attributes.NORMAL].typeEnum), typeof(Vector3)); #endif normals = gltf.IsAccessorInterleaved(pos) ? GetAccessorDataInterleaved <Vector3>(primitive.attributes.NORMAL, ref bytes, Extractor.GetVector3sInterleaved) : GetAccessorData <Vector3>(primitive.attributes.NORMAL, ref bytes, Extractor.GetVector3s); } Vector2[] uvs0 = GetUvs(primitive.attributes.TEXCOORD_0, ref bytes); Vector2[] uvs1 = GetUvs(primitive.attributes.TEXCOORD_1, ref bytes); Vector4[] tangents = null; if (primitive.attributes.TANGENT >= 0) { #if DEBUG Assert.AreEqual(GetAccessorTye(gltf.accessors[primitive.attributes.TANGENT].typeEnum), typeof(Vector4)); #endif tangents = gltf.IsAccessorInterleaved(pos) ? GetAccessorDataInterleaved <Vector4>(primitive.attributes.TANGENT, ref bytes, Extractor.GetVector4sInterleaved) : GetAccessorData <Vector4>(primitive.attributes.TANGENT, ref bytes, Extractor.GetVector4s); } Color32[] colors32; Color[] colors; GetColors(primitive.attributes.COLOR_0, ref bytes, out colors32, out colors); var msh = new UnityEngine.Mesh(); msh.name = mesh.name; msh.vertices = positions; msh.SetIndices(indices, MeshTopology.Triangles, 0); if (uvs0 != null) { msh.uv = uvs0; } if (uvs1 != null) { msh.uv2 = uvs1; } if (normals != null) { msh.normals = normals; } else { msh.RecalculateNormals(); } if (colors != null) { msh.colors = colors; } else if (colors32 != null) { msh.colors32 = colors32; } if (tangents != null) { msh.tangents = tangents; } else { msh.RecalculateTangents(); } primitives.Add(new Primitive(msh, primitive.material)); resources.Add(msh); } } meshPrimitiveIndex[gltf.meshes.Length] = primitives.Count; var nodes = new Transform[gltf.nodes.Length]; var relations = new Dictionary <uint, uint>(); for (uint nodeIndex = 0; nodeIndex < gltf.nodes.Length; nodeIndex++) { var node = gltf.nodes[nodeIndex]; if (node.children == null && node.mesh < 0) { continue; } var go = new GameObject(node.name ?? "Node"); nodes[nodeIndex] = go.transform; if (node.children != null) { foreach (var child in node.children) { relations[child] = nodeIndex; } } if (node.matrix != null) { Matrix4x4 m = new Matrix4x4(); m.m00 = node.matrix[0]; m.m10 = node.matrix[1]; m.m20 = node.matrix[2]; m.m30 = node.matrix[3]; m.m01 = node.matrix[4]; m.m11 = node.matrix[5]; m.m21 = node.matrix[6]; m.m31 = node.matrix[7]; m.m02 = node.matrix[8]; m.m12 = node.matrix[9]; m.m22 = node.matrix[10]; m.m32 = node.matrix[11]; m.m03 = node.matrix[12]; m.m13 = node.matrix[13]; m.m23 = node.matrix[14]; m.m33 = node.matrix[15]; if (m.ValidTRS()) { go.transform.localPosition = new Vector3(m.m03, m.m13, m.m23); go.transform.localRotation = m.rotation; go.transform.localScale = m.lossyScale; } else { Debug.LogErrorFormat("Invalid matrix on node {0}", nodeIndex); } } else { if (node.translation != null) { Assert.AreEqual(node.translation.Length, 3); go.transform.localPosition = new Vector3( node.translation[0], node.translation[1], node.translation[2] ); } if (node.rotation != null) { Assert.AreEqual(node.rotation.Length, 4); go.transform.localRotation = new Quaternion( node.rotation[0], node.rotation[1], node.rotation[2], node.rotation[3] ); } if (node.scale != null) { Assert.AreEqual(node.scale.Length, 3); go.transform.localScale = new Vector3( node.scale[0], node.scale[1], node.scale[2] ); } } if (node.mesh >= 0) { int end = meshPrimitiveIndex[node.mesh + 1]; GameObject meshGo = null; for (int i = meshPrimitiveIndex[node.mesh]; i < end; i++) { if (meshGo == null) { meshGo = go; } else { meshGo = new GameObject("Primitive"); meshGo.transform.SetParent(go.transform, false); } var mf = meshGo.AddComponent <MeshFilter>(); mf.mesh = primitives[i].mesh; var mr = meshGo.AddComponent <MeshRenderer>(); int materialIndex = primitives[i].materialIndex; if (materials != null && materialIndex >= 0 && materialIndex < materials.Length) { mr.material = materials[primitives[i].materialIndex]; } else { mr.material = materialGenerator.GetDefaultMaterial(); } } } } foreach (var rel in relations) { nodes[rel.Key]?.SetParent(nodes[rel.Value], false); } foreach (var scene in gltf.scenes) { var go = new GameObject(scene.name ?? "Scene"); go.transform.SetParent(parent, false); // glTF to unity space ( -z forward to z forward ) go.transform.localScale = new Vector3(1, 1, -1); foreach (var nodeIndex in scene.nodes) { nodes[nodeIndex]?.SetParent(go.transform, false); } } foreach (var bv in gltf.bufferViews) { if (gltf.buffers[bv.buffer].uri == null) { } } }
bool CreateGameObjects(Root gltf, Transform parent) { var primitives = new List <Primitive>(gltf.meshes.Length); var meshPrimitiveIndex = new int[gltf.meshes.Length + 1]; resources = new List <UnityEngine.Object>(); if (gltf.images != null) { if (images == null) { images = new Texture2D[gltf.images.Length]; } else { Assert.AreEqual(images.Length, gltf.images.Length); } for (int i = 0; i < images.Length; i++) { if (images[i] != null) { resources.Add(images[i]); } var img = gltf.images[i]; bool knownImageType = false; if (string.IsNullOrEmpty(img.mimeType)) { Debug.LogWarning("Image is missing mime type"); knownImageType = img.uri.EndsWith(".png", StringComparison.OrdinalIgnoreCase) || img.uri.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) || img.uri.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase); } else { knownImageType = img.mimeType == "image/jpeg" || img.mimeType == "image/png"; } if (knownImageType) { if (img.bufferView >= 0) { var bufferView = gltf.bufferViews[img.bufferView]; var buffer = GetBuffer(bufferView.buffer); var chunk = binChunks[bufferView.buffer]; var imgBytes = Extractor.CreateBufferViewCopy(bufferView, chunk, buffer); var txt = new UnityEngine.Texture2D(4, 4); txt.name = string.IsNullOrEmpty(img.name) ? string.Format("glb embed texture {0}", i) : img.name; txt.LoadImage(imgBytes); images[i] = txt; resources.Add(txt); } } } } if (gltf.materials != null) { materials = new UnityEngine.Material[gltf.materials.Length]; for (int i = 0; i < materials.Length; i++) { materials[i] = materialGenerator.GenerateMaterial(gltf.materials[i], gltf.textures, images, resources); } } //foreach( var mesh in gltf.meshes ) { for (int meshIndex = 0; meshIndex < gltf.meshes.Length; meshIndex++) { var mesh = gltf.meshes[meshIndex]; meshPrimitiveIndex[meshIndex] = primitives.Count; foreach (var primitive in mesh.primitives) { // index var accessor = gltf.accessors[primitive.indices]; var bufferView = gltf.bufferViews[accessor.bufferView]; var bufferIndex = bufferView.buffer; var buffer = GetBuffer(bufferIndex); GlbBinChunk chunk = binChunks[bufferIndex]; Assert.AreEqual(accessor.typeEnum, GLTFAccessorAttributeType.SCALAR); //Assert.AreEqual(accessor.count * GetLength(accessor.typeEnum) * 4 , (int) chunk.length); int[] indices = null; switch (accessor.componentType) { case GLTFComponentType.UnsignedByte: indices = Extractor.GetIndicesUInt8(buffer, accessor.byteOffset + bufferView.byteOffset + chunk.start, accessor.count); break; case GLTFComponentType.UnsignedShort: indices = Extractor.GetIndicesUInt16(buffer, accessor.byteOffset + bufferView.byteOffset + chunk.start, accessor.count); break; case GLTFComponentType.UnsignedInt: indices = Extractor.GetIndicesUInt32(buffer, accessor.byteOffset + bufferView.byteOffset + chunk.start, accessor.count); break; default: Debug.LogErrorFormat("Invalid index format {0}", accessor.componentType); return(false); } #if DEBUG if (accessor.min != null && accessor.min.Length > 0 && accessor.max != null && accessor.max.Length > 0) { int minInt = (int)accessor.min[0]; int maxInt = (int)accessor.max[0]; int minIndex = int.MaxValue; int maxIndex = int.MinValue; foreach (var index in indices) { Assert.IsTrue(index >= minInt); Assert.IsTrue(index <= maxInt); minIndex = Math.Min(minIndex, index); maxIndex = Math.Max(maxIndex, index); } if (minIndex != minInt || maxIndex != maxInt ) { Debug.LogErrorFormat("Faulty index bounds: is {0}:{1} expected:{2}:{3}", minIndex, maxIndex, minInt, maxInt); } } #endif // position int pos = primitive.attributes.POSITION; Assert.IsTrue(pos >= 0); #if DEBUG Assert.AreEqual(GetAccessorTye(gltf.accessors[pos].typeEnum), typeof(Vector3)); #endif var positions = gltf.IsAccessorInterleaved(pos) ? GetAccessorDataInterleaved <Vector3>(gltf, pos, ref buffer, Extractor.GetVector3sInterleaved) : GetAccessorData <Vector3>(gltf, pos, ref buffer, Extractor.GetVector3s); #if DEBUG var posAcc = gltf.accessors[pos]; Vector3 minPos = new Vector3((float)posAcc.min[0], (float)posAcc.min[1], (float)posAcc.min[2]); Vector3 maxPos = new Vector3((float)posAcc.max[0], (float)posAcc.max[1], (float)posAcc.max[2]); foreach (var p in positions) { if (!(p.x >= minPos.x && p.y >= minPos.y && p.z >= minPos.z && p.x <= maxPos.x && p.y <= maxPos.y && p.z <= maxPos.z )) { Debug.LogError("Vertex outside of limits"); break; } } var pUsage = new int[positions.Length]; foreach (var index in indices) { pUsage[index] += 1; } int pMin = int.MaxValue; foreach (var u in pUsage) { pMin = Math.Min(pMin, u); } if (pMin < 1) { Debug.LogError("Unused vertices"); } #endif Vector3[] normals = null; if (primitive.attributes.NORMAL >= 0) { #if DEBUG Assert.AreEqual(GetAccessorTye(gltf.accessors[primitive.attributes.NORMAL].typeEnum), typeof(Vector3)); #endif normals = gltf.IsAccessorInterleaved(pos) ? GetAccessorDataInterleaved <Vector3>(gltf, primitive.attributes.NORMAL, ref buffer, Extractor.GetVector3sInterleaved) : GetAccessorData <Vector3>(gltf, primitive.attributes.NORMAL, ref buffer, Extractor.GetVector3s); } Vector2[] uvs0 = GetUvs(gltf, primitive.attributes.TEXCOORD_0, ref buffer); Vector2[] uvs1 = GetUvs(gltf, primitive.attributes.TEXCOORD_1, ref buffer); Vector4[] tangents = null; if (primitive.attributes.TANGENT >= 0) { #if DEBUG Assert.AreEqual(GetAccessorTye(gltf.accessors[primitive.attributes.TANGENT].typeEnum), typeof(Vector4)); #endif tangents = gltf.IsAccessorInterleaved(pos) ? GetAccessorDataInterleaved <Vector4>(gltf, primitive.attributes.TANGENT, ref buffer, Extractor.GetVector4sInterleaved) : GetAccessorData <Vector4>(gltf, primitive.attributes.TANGENT, ref buffer, Extractor.GetVector4s); } Color32[] colors32; Color[] colors; GetColors(gltf, primitive.attributes.COLOR_0, ref buffer, out colors32, out colors); var msh = new UnityEngine.Mesh(); if (positions.Length > 65536) { #if UNITY_2017_3_OR_NEWER msh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; #else throw new System.Exception("Meshes with more than 65536 vertices are only supported from Unity 2017.3 onwards."); #endif } msh.name = mesh.name; msh.vertices = positions; msh.SetIndices(indices, MeshTopology.Triangles, 0); if (uvs0 != null) { msh.uv = uvs0; } if (uvs1 != null) { msh.uv2 = uvs1; } if (normals != null) { msh.normals = normals; } else { msh.RecalculateNormals(); } if (colors != null) { msh.colors = colors; } else if (colors32 != null) { msh.colors32 = colors32; } if (tangents != null) { msh.tangents = tangents; } else { msh.RecalculateTangents(); } primitives.Add(new Primitive(msh, primitive.material)); resources.Add(msh); } } meshPrimitiveIndex[gltf.meshes.Length] = primitives.Count; var nodes = new Transform[gltf.nodes.Length]; var relations = new Dictionary <uint, uint>(); for (uint nodeIndex = 0; nodeIndex < gltf.nodes.Length; nodeIndex++) { var node = gltf.nodes[nodeIndex]; if (node.children == null && node.mesh < 0) { continue; } var go = new GameObject(node.name ?? "Node"); nodes[nodeIndex] = go.transform; if (node.children != null) { foreach (var child in node.children) { relations[child] = nodeIndex; } } if (node.matrix != null) { Matrix4x4 m = new Matrix4x4(); m.m00 = node.matrix[0]; m.m10 = node.matrix[1]; m.m20 = node.matrix[2]; m.m30 = node.matrix[3]; m.m01 = node.matrix[4]; m.m11 = node.matrix[5]; m.m21 = node.matrix[6]; m.m31 = node.matrix[7]; m.m02 = node.matrix[8]; m.m12 = node.matrix[9]; m.m22 = node.matrix[10]; m.m32 = node.matrix[11]; m.m03 = node.matrix[12]; m.m13 = node.matrix[13]; m.m23 = node.matrix[14]; m.m33 = node.matrix[15]; if (m.ValidTRS()) { go.transform.localPosition = new Vector3(m.m03, m.m13, m.m23); go.transform.localRotation = m.rotation; go.transform.localScale = m.lossyScale; } else { Debug.LogErrorFormat("Invalid matrix on node {0}", nodeIndex); return(false); } } else { if (node.translation != null) { Assert.AreEqual(node.translation.Length, 3); go.transform.localPosition = new Vector3( node.translation[0], node.translation[1], node.translation[2] ); } if (node.rotation != null) { Assert.AreEqual(node.rotation.Length, 4); go.transform.localRotation = new Quaternion( node.rotation[0], node.rotation[1], node.rotation[2], node.rotation[3] ); } if (node.scale != null) { Assert.AreEqual(node.scale.Length, 3); go.transform.localScale = new Vector3( node.scale[0], node.scale[1], node.scale[2] ); } } if (node.mesh >= 0) { int end = meshPrimitiveIndex[node.mesh + 1]; GameObject meshGo = null; for (int i = meshPrimitiveIndex[node.mesh]; i < end; i++) { if (meshGo == null) { meshGo = go; } else { meshGo = new GameObject("Primitive"); meshGo.transform.SetParent(go.transform, false); } var mf = meshGo.AddComponent <MeshFilter>(); mf.mesh = primitives[i].mesh; var mr = meshGo.AddComponent <MeshRenderer>(); int materialIndex = primitives[i].materialIndex; if (materials != null && materialIndex >= 0 && materialIndex < materials.Length) { mr.material = materials[primitives[i].materialIndex]; } else { mr.material = materialGenerator.GetDefaultMaterial(); } } } } foreach (var rel in relations) { nodes[rel.Key]?.SetParent(nodes[rel.Value], false); } foreach (var scene in gltf.scenes) { var go = new GameObject(scene.name ?? "Scene"); go.transform.SetParent(parent, false); // glTF to unity space ( -z forward to z forward ) go.transform.localScale = new Vector3(1, 1, -1); foreach (var nodeIndex in scene.nodes) { nodes[nodeIndex]?.SetParent(go.transform, false); } } foreach (var bv in gltf.bufferViews) { if (gltf.buffers[bv.buffer].uri == null) { } } return(true); }