AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
GLTFSceneImporter.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using System.IO;
5 using System.Text.RegularExpressions;
6 using GLTF;
7 using GLTF.Schema;
8 using UnityEngine;
9 using UnityEngine.Networking;
10 using UnityEngine.Rendering;
11 using UnityGLTF.Cache;
12 using UnityGLTF.Extensions;
13 
14 namespace UnityGLTF
15 {
16  public class GLTFSceneImporter
17  {
18  public enum MaterialType
19  {
20  PbrMetallicRoughness,
21  KHR_materials_pbrSpecularGlossiness,
22  CommonConstant,
23  CommonPhong,
24  CommonBlinn,
25  CommonLambert
26  }
27 
28  private enum LoadType
29  {
30  Uri,
31  Stream
32  }
33 
34  protected struct GLBStream
35  {
36  public Stream Stream;
37  public long StartPosition;
38  }
39 
40  protected GameObject _lastLoadedScene;
41  protected readonly Transform _sceneParent;
42  protected readonly Dictionary<MaterialType, Shader> _shaderCache = new Dictionary<MaterialType, Shader>();
43  public int MaximumLod = 300;
44  protected readonly GLTF.Schema.Material DefaultMaterial = new GLTF.Schema.Material();
45  protected string _gltfUrl;
46  protected string _gltfDirectoryPath;
48  protected GLTFRoot _root;
51  protected bool _addColliders = false;
52  private LoadType _loadType;
53 
60  public GLTFSceneImporter(string gltfUrl, Transform parent = null, bool addColliders = false)
61  {
62  _gltfUrl = gltfUrl;
63  _gltfDirectoryPath = AbsoluteUriPath(gltfUrl);
64  _sceneParent = parent;
65  _asyncAction = new AsyncAction();
66  _loadType = LoadType.Uri;
67  _addColliders = addColliders;
68  }
69 
70  public GLTFSceneImporter(string rootPath, Stream stream, Transform parent = null, bool addColliders = false)
71  {
72  _gltfUrl = rootPath;
73  _gltfDirectoryPath = AbsoluteFilePath(rootPath);
74  _gltfStream = new GLBStream { Stream = stream, StartPosition = stream.Position };
75  _sceneParent = parent;
76  _asyncAction = new AsyncAction();
77  _loadType = LoadType.Stream;
78  _addColliders = addColliders;
79  }
80 
81  public GameObject LastLoadedScene
82  {
83  get { return _lastLoadedScene; }
84  }
85 
91  public virtual void SetShaderForMaterialType(MaterialType type, Shader shader)
92  {
93  _shaderCache.Add(type, shader);
94  }
95 
102  public IEnumerator Load(int sceneIndex = -1, bool isMultithreaded = false)
103  {
104  if (_loadType == LoadType.Uri)
105  {
106  var www = UnityWebRequest.Get(_gltfUrl);
107 
108 #if UNITY_2017_2_OR_NEWER
109  yield return www.SendWebRequest();
110 #else
111  yield return www.Send();
112 #endif
113 
114  if (www.responseCode >= 400 || www.responseCode == 0)
115  {
116  throw new WebRequestException(www);
117  }
118 
119  byte[] gltfData = www.downloadHandler.data;
120  _gltfStream.Stream = new MemoryStream(gltfData, 0, gltfData.Length, false, true);
121  }
122  else if (_loadType == LoadType.Stream)
123  {
124  // Do nothing, since the stream was passed in via the constructor.
125  }
126  else
127  {
128  throw new Exception("Invalid load type specified: " + _loadType);
129  }
130 
131  _root = GLTFParser.ParseJson(_gltfStream.Stream, _gltfStream.StartPosition);
132  yield return ImportScene(sceneIndex, isMultithreaded);
133  }
134 
141  protected IEnumerator ImportScene(int sceneIndex = -1, bool isMultithreaded = false)
142  {
143  Scene scene;
144  if (sceneIndex >= 0 && sceneIndex < _root.Scenes.Count)
145  {
146  scene = _root.Scenes[sceneIndex];
147  }
148  else
149  {
150  scene = _root.GetDefaultScene();
151  }
152 
153  if (scene == null)
154  {
155  throw new Exception("No default scene in gltf file.");
156  }
157 
158  _assetCache = new AssetCache(
159  _root.Images != null ? _root.Images.Count : 0,
160  _root.Textures != null ? _root.Textures.Count : 0,
161  _root.Materials != null ? _root.Materials.Count : 0,
162  _root.Buffers != null ? _root.Buffers.Count : 0,
163  _root.Meshes != null ? _root.Meshes.Count : 0
164  );
165 
166  if (_lastLoadedScene == null)
167  {
168  if (_root.Buffers != null)
169  {
170  // todo add fuzzing to verify that buffers are before uri
171  for (int i = 0; i < _root.Buffers.Count; ++i)
172  {
173  GLTF.Schema.Buffer buffer = _root.Buffers[i];
174  if (buffer.Uri != null)
175  {
176  yield return LoadBuffer(_gltfDirectoryPath, buffer, i);
177  }
178  else //null buffer uri indicates GLB buffer loading
179  {
180  GLTFParser.SeekToBinaryChunk(_gltfStream.Stream, i, _gltfStream.StartPosition);
181  _assetCache.BufferCache[i] = new BufferCacheData()
182  {
183  ChunkOffset = _gltfStream.Stream.Position,
184  Stream = _gltfStream.Stream
185  };
186  }
187  }
188  }
189 
190  if (_root.Images != null)
191  {
192  for (int i = 0; i < _root.Images.Count; ++i)
193  {
194  Image image = _root.Images[i];
195  yield return LoadImage(_gltfDirectoryPath, image, i);
196  }
197  }
198 
199  // generate these in advance instead of as-needed
200  if (isMultithreaded)
201  {
202  yield return _asyncAction.RunOnWorkerThread(() => BuildAttributesForMeshes());
203  }
204  }
205 
206  var sceneObj = CreateScene(scene);
207 
208  if (_sceneParent != null)
209  {
210  sceneObj.transform.SetParent(_sceneParent, false);
211  }
212 
213  _lastLoadedScene = sceneObj;
214  }
215 
216  protected virtual void BuildAttributesForMeshes()
217  {
218  for (int i = 0; i < _root.Meshes.Count; ++i)
219  {
220  GLTF.Schema.Mesh mesh = _root.Meshes[i];
221  if (_assetCache.MeshCache[i] == null)
222  {
223  _assetCache.MeshCache[i] = new MeshCacheData[mesh.Primitives.Count];
224  }
225 
226  for (int j = 0; j < mesh.Primitives.Count; ++j)
227  {
228  _assetCache.MeshCache[i][j] = new MeshCacheData();
229  var primitive = mesh.Primitives[j];
230  BuildMeshAttributes(primitive, i, j);
231  }
232  }
233  }
234 
235  protected virtual void BuildMeshAttributes(MeshPrimitive primitive, int meshID, int primitiveIndex)
236  {
237  if (_assetCache.MeshCache[meshID][primitiveIndex].MeshAttributes.Count == 0)
238  {
239  Dictionary<string, AttributeAccessor> attributeAccessors = new Dictionary<string, AttributeAccessor>(primitive.Attributes.Count + 1);
240  foreach (var attributePair in primitive.Attributes)
241  {
242  BufferCacheData bufferCacheData = _assetCache.BufferCache[attributePair.Value.Value.BufferView.Value.Buffer.Id];
243  AttributeAccessor AttributeAccessor = new AttributeAccessor()
244  {
245  AccessorId = attributePair.Value,
246  Stream = bufferCacheData.Stream,
247  Offset = bufferCacheData.ChunkOffset
248  };
249 
250  attributeAccessors[attributePair.Key] = AttributeAccessor;
251  }
252 
253  if (primitive.Indices != null)
254  {
255  BufferCacheData bufferCacheData = _assetCache.BufferCache[primitive.Indices.Value.BufferView.Value.Buffer.Id];
256  AttributeAccessor indexBuilder = new AttributeAccessor()
257  {
258  AccessorId = primitive.Indices,
259  Stream = bufferCacheData.Stream,
260  Offset = bufferCacheData.ChunkOffset
261  };
262 
263  attributeAccessors[SemanticProperties.INDICES] = indexBuilder;
264  }
265 
266  GLTFHelpers.BuildMeshAttributes(ref attributeAccessors);
267  TransformAttributes(ref attributeAccessors);
268  _assetCache.MeshCache[meshID][primitiveIndex].MeshAttributes = attributeAccessors;
269  }
270  }
271 
272  protected void TransformAttributes(ref Dictionary<string, AttributeAccessor> attributeAccessors)
273  {
274  // Flip vectors and triangles to the Unity coordinate system.
275  if (attributeAccessors.ContainsKey(SemanticProperties.POSITION))
276  {
277  AttributeAccessor attributeAccessor = attributeAccessors[SemanticProperties.POSITION];
279  }
280  if (attributeAccessors.ContainsKey(SemanticProperties.INDICES))
281  {
282  AttributeAccessor attributeAccessor = attributeAccessors[SemanticProperties.INDICES];
283  SchemaExtensions.FlipFaces(ref attributeAccessor);
284  }
285  if (attributeAccessors.ContainsKey(SemanticProperties.NORMAL))
286  {
287  AttributeAccessor attributeAccessor = attributeAccessors[SemanticProperties.NORMAL];
289  }
290  // TexCoord goes from 0 to 3 to match GLTFHelpers.BuildMeshAttributes
291  for (int i = 0; i < 4; i++)
292  {
293  if (attributeAccessors.ContainsKey(SemanticProperties.TexCoord(i)))
294  {
295  AttributeAccessor attributeAccessor = attributeAccessors[SemanticProperties.TexCoord(i)];
296  SchemaExtensions.FlipTexCoordArrayV(ref attributeAccessor);
297  }
298 
299  }
300  if (attributeAccessors.ContainsKey(SemanticProperties.TANGENT))
301  {
302  AttributeAccessor attributeAccessor = attributeAccessors[SemanticProperties.TANGENT];
304  }
305  }
306 
307  protected virtual GameObject CreateScene(Scene scene)
308  {
309  var sceneObj = new GameObject(scene.Name ?? "GLTFScene");
310 
311  foreach (var node in scene.Nodes)
312  {
313  var nodeObj = CreateNode(node.Value);
314  nodeObj.transform.SetParent(sceneObj.transform, false);
315  }
316 
317  return sceneObj;
318  }
319 
320  protected virtual GameObject CreateNode(Node node)
321  {
322  var nodeObj = new GameObject(node.Name ?? "GLTFNode");
323 
324  Vector3 position;
325  Quaternion rotation;
326  Vector3 scale;
327  node.GetUnityTRSProperties(out position, out rotation, out scale);
328  nodeObj.transform.localPosition = position;
329  nodeObj.transform.localRotation = rotation;
330  nodeObj.transform.localScale = scale;
331 
332  // TODO: Add support for skin/morph targets
333  if (node.Mesh != null)
334  {
335  CreateMeshObject(node.Mesh.Value, nodeObj.transform, node.Mesh.Id);
336  }
337 
338  /* TODO: implement camera (probably a flag to disable for VR as well)
339  if (camera != null)
340  {
341  GameObject cameraObj = camera.Value.Create();
342  cameraObj.transform.parent = nodeObj.transform;
343  }
344  */
345 
346  if (node.Children != null)
347  {
348  foreach (var child in node.Children)
349  {
350  var childObj = CreateNode(child.Value);
351  childObj.transform.SetParent(nodeObj.transform, false);
352  }
353  }
354 
355  return nodeObj;
356  }
357 
358  protected virtual void CreateMeshObject(GLTF.Schema.Mesh mesh, Transform parent, int meshId)
359  {
360  if (_assetCache.MeshCache[meshId] == null)
361  {
362  _assetCache.MeshCache[meshId] = new MeshCacheData[mesh.Primitives.Count];
363  }
364 
365  for (int i = 0; i < mesh.Primitives.Count; ++i)
366  {
367  var primitive = mesh.Primitives[i];
368  var primitiveObj = CreateMeshPrimitive(primitive, meshId, i);
369  primitiveObj.transform.SetParent(parent, false);
370  primitiveObj.SetActive(true);
371  }
372  }
373 
374  protected virtual GameObject CreateMeshPrimitive(MeshPrimitive primitive, int meshID, int primitiveIndex)
375  {
376  var primitiveObj = new GameObject("Primitive");
377 
378  var meshFilter = primitiveObj.AddComponent<MeshFilter>();
379 
380  if (_assetCache.MeshCache[meshID][primitiveIndex] == null)
381  {
382  _assetCache.MeshCache[meshID][primitiveIndex] = new MeshCacheData();
383  }
384  if (_assetCache.MeshCache[meshID][primitiveIndex].LoadedMesh == null)
385  {
386  if (_assetCache.MeshCache[meshID][primitiveIndex].MeshAttributes.Count == 0)
387  {
388  BuildMeshAttributes(primitive, meshID, primitiveIndex);
389  }
390  var meshAttributes = _assetCache.MeshCache[meshID][primitiveIndex].MeshAttributes;
391  var vertexCount = primitive.Attributes[SemanticProperties.POSITION].Value.Count;
392 
393  // todo optimize: There are multiple copies being performed to turn the buffer data into mesh data. Look into reducing them
394  UnityEngine.Mesh mesh = new UnityEngine.Mesh
395  {
396  vertices = primitive.Attributes.ContainsKey(SemanticProperties.POSITION)
397  ? meshAttributes[SemanticProperties.POSITION].AccessorContent.AsVertices.ToUnityVector3Raw()
398  : null,
399  normals = primitive.Attributes.ContainsKey(SemanticProperties.NORMAL)
400  ? meshAttributes[SemanticProperties.NORMAL].AccessorContent.AsNormals.ToUnityVector3Raw()
401  : null,
402 
403  uv = primitive.Attributes.ContainsKey(SemanticProperties.TexCoord(0))
404  ? meshAttributes[SemanticProperties.TexCoord(0)].AccessorContent.AsTexcoords.ToUnityVector2Raw()
405  : null,
406 
407  uv2 = primitive.Attributes.ContainsKey(SemanticProperties.TexCoord(1))
408  ? meshAttributes[SemanticProperties.TexCoord(1)].AccessorContent.AsTexcoords.ToUnityVector2Raw()
409  : null,
410 
411  uv3 = primitive.Attributes.ContainsKey(SemanticProperties.TexCoord(2))
412  ? meshAttributes[SemanticProperties.TexCoord(2)].AccessorContent.AsTexcoords.ToUnityVector2Raw()
413  : null,
414 
415  uv4 = primitive.Attributes.ContainsKey(SemanticProperties.TexCoord(3))
416  ? meshAttributes[SemanticProperties.TexCoord(3)].AccessorContent.AsTexcoords.ToUnityVector2Raw()
417  : null,
418 
419  colors = primitive.Attributes.ContainsKey(SemanticProperties.Color(0))
420  ? meshAttributes[SemanticProperties.Color(0)].AccessorContent.AsColors.ToUnityColorRaw()
421  : null,
422 
423  triangles = primitive.Indices != null
424  ? meshAttributes[SemanticProperties.INDICES].AccessorContent.AsTriangles.ToIntArrayRaw()
425  : MeshPrimitive.GenerateTriangles(vertexCount),
426 
427  tangents = primitive.Attributes.ContainsKey(SemanticProperties.TANGENT)
428  ? meshAttributes[SemanticProperties.TANGENT].AccessorContent.AsTangents.ToUnityVector4Raw()
429  : null
430  };
431 
432  _assetCache.MeshCache[meshID][primitiveIndex].LoadedMesh = mesh;
433  }
434 
435  meshFilter.sharedMesh = _assetCache.MeshCache[meshID][primitiveIndex].LoadedMesh;
436 
437  var materialWrapper = CreateMaterial(
438  primitive.Material != null ? primitive.Material.Value : DefaultMaterial,
439  primitive.Material != null ? primitive.Material.Id : -1
440  );
441 
442  var meshRenderer = primitiveObj.AddComponent<MeshRenderer>();
443  meshRenderer.material = materialWrapper.GetContents(primitive.Attributes.ContainsKey(SemanticProperties.Color(0)));
444 
445  if (_addColliders)
446  {
447  var meshCollider = primitiveObj.AddComponent<MeshCollider>();
448  meshCollider.sharedMesh = meshFilter.mesh;
449  }
450 
451  return primitiveObj;
452  }
453 
454  protected virtual MaterialCacheData CreateMaterial(GLTF.Schema.Material def, int materialIndex)
455  {
456  MaterialCacheData materialWrapper = null;
457  if (materialIndex < 0 || _assetCache.MaterialCache[materialIndex] == null)
458  {
459  Shader shader;
460 
461  // get the shader to use for this material
462  try
463  {
464  if (_root.ExtensionsUsed != null && _root.ExtensionsUsed.Contains("KHR_materials_pbrSpecularGlossiness"))
465  shader = _shaderCache[MaterialType.KHR_materials_pbrSpecularGlossiness];
466  else if (def.PbrMetallicRoughness != null)
467  shader = _shaderCache[MaterialType.PbrMetallicRoughness];
468  else if (_root.ExtensionsUsed != null && _root.ExtensionsUsed.Contains("KHR_materials_common")
469  && def.CommonConstant != null)
470  shader = _shaderCache[MaterialType.CommonConstant];
471  else
472  shader = _shaderCache[MaterialType.PbrMetallicRoughness];
473  }
474  catch (KeyNotFoundException)
475  {
476  Debug.LogWarningFormat("No shader supplied for type of glTF material {0}, using Standard fallback", def.Name);
477  shader = Shader.Find("Standard");
478  }
479 
480  shader.maximumLOD = MaximumLod;
481 
482  var material = new UnityEngine.Material(shader);
483 
484  if (def.AlphaMode == AlphaMode.MASK)
485  {
486  material.SetOverrideTag("RenderType", "TransparentCutout");
487  material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
488  material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
489  material.SetInt("_ZWrite", 1);
490  material.EnableKeyword("_ALPHATEST_ON");
491  material.DisableKeyword("_ALPHABLEND_ON");
492  material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
493  material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.AlphaTest;
494  material.SetFloat("_Cutoff", (float)def.AlphaCutoff);
495  }
496  else if (def.AlphaMode == AlphaMode.BLEND)
497  {
498  material.SetOverrideTag("RenderType", "Transparent");
499  material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
500  material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
501  material.SetInt("_ZWrite", 0);
502  material.DisableKeyword("_ALPHATEST_ON");
503  material.EnableKeyword("_ALPHABLEND_ON");
504  material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
505  material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
506  }
507  else
508  {
509  material.SetOverrideTag("RenderType", "Opaque");
510  material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
511  material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
512  material.SetInt("_ZWrite", 1);
513  material.DisableKeyword("_ALPHATEST_ON");
514  material.DisableKeyword("_ALPHABLEND_ON");
515  material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
516  material.renderQueue = -1;
517  }
518 
519  if (def.DoubleSided)
520  {
521  material.SetInt("_Cull", (int)CullMode.Off);
522  }
523  else
524  {
525  material.SetInt("_Cull", (int)CullMode.Back);
526  }
527 
528  if (def.PbrMetallicRoughness != null)
529  {
530  var pbr = def.PbrMetallicRoughness;
531 
532  material.SetColor("_Color", pbr.BaseColorFactor.ToUnityColorRaw());
533 
534  if (pbr.BaseColorTexture != null)
535  {
536  var textureDef = pbr.BaseColorTexture.Index.Value;
537  material.SetTexture("_MainTex", CreateTexture(textureDef));
538 
539  ApplyTextureTransform(pbr.BaseColorTexture, material, "_MainTex");
540  }
541 
542  material.SetFloat("_Metallic", (float)pbr.MetallicFactor);
543 
544  if (pbr.MetallicRoughnessTexture != null)
545  {
546  var texture = pbr.MetallicRoughnessTexture.Index.Value;
547  material.SetTexture("_MetallicRoughnessMap", CreateTexture(texture));
548 
549  ApplyTextureTransform(pbr.MetallicRoughnessTexture, material, "_MetallicRoughnessMap");
550  }
551 
552  material.SetFloat("_Roughness", (float)pbr.RoughnessFactor);
553  }
554 
555  if (def.Extensions != null && def.Extensions.ContainsKey(KHR_materials_pbrSpecularGlossinessExtensionFactory.EXTENSION_NAME))
556  {
557  KHR_materials_pbrSpecularGlossinessExtension specGloss = def.Extensions[KHR_materials_pbrSpecularGlossinessExtensionFactory.EXTENSION_NAME] as KHR_materials_pbrSpecularGlossinessExtension;
558 
559  if (specGloss.DiffuseTexture != null)
560  {
561  var texture = specGloss.DiffuseTexture.Index.Value;
562  material.SetTexture("_MainTex", CreateTexture(texture));
563 
564  ApplyTextureTransform(specGloss.DiffuseTexture, material, "_MainTex");
565  }
566  else
567  {
568  material.SetColor("_Color", specGloss.DiffuseFactor.ToUnityColorRaw());
569  }
570 
571  if (specGloss.SpecularGlossinessTexture != null)
572  {
573  var texture = specGloss.SpecularGlossinessTexture.Index.Value;
574  material.SetTexture("_SpecGlossMap", CreateTexture(texture));
575  material.EnableKeyword("_SPECGLOSSMAP");
576 
577  ApplyTextureTransform(specGloss.SpecularGlossinessTexture, material, "_SpecGlossMap");
578  }
579  else
580  {
581  material.SetVector("_SpecColor", specGloss.SpecularFactor.ToUnityVector3Raw());
582  material.SetFloat("_Glossiness", (float)specGloss.GlossinessFactor);
583  }
584  }
585 
586  if (def.CommonConstant != null)
587  {
588  material.SetColor("_AmbientFactor", def.CommonConstant.AmbientFactor.ToUnityColorRaw());
589 
590  if (def.CommonConstant.LightmapTexture != null)
591  {
592  material.EnableKeyword("LIGHTMAP_ON");
593 
594  var texture = def.CommonConstant.LightmapTexture.Index.Value;
595  material.SetTexture("_LightMap", CreateTexture(texture));
596  material.SetInt("_LightUV", def.CommonConstant.LightmapTexture.TexCoord);
597 
598  ApplyTextureTransform(def.CommonConstant.LightmapTexture, material, "_LightMap");
599  }
600 
601  material.SetColor("_LightFactor", def.CommonConstant.LightmapFactor.ToUnityColorRaw());
602  }
603 
604  if (def.NormalTexture != null)
605  {
606  var texture = def.NormalTexture.Index.Value;
607  material.SetTexture("_BumpMap", CreateTexture(texture));
608  material.SetFloat("_BumpScale", (float)def.NormalTexture.Scale);
609  material.EnableKeyword("_NORMALMAP");
610 
611  ApplyTextureTransform(def.NormalTexture, material, "_BumpMap");
612  }
613 
614  if (def.OcclusionTexture != null)
615  {
616  var texture = def.OcclusionTexture.Index;
617 
618  material.SetFloat("_OcclusionStrength", (float)def.OcclusionTexture.Strength);
619 
620  if (def.PbrMetallicRoughness != null
621  && def.PbrMetallicRoughness.MetallicRoughnessTexture != null
622  && def.PbrMetallicRoughness.MetallicRoughnessTexture.Index.Id == texture.Id)
623  {
624  material.EnableKeyword("OCC_METAL_ROUGH_ON");
625  }
626  else
627  {
628  material.SetTexture("_OcclusionMap", CreateTexture(texture.Value));
629 
630  ApplyTextureTransform(def.OcclusionTexture, material, "_OcclusionMap");
631  }
632  }
633 
634  if (def.EmissiveTexture != null)
635  {
636  var texture = def.EmissiveTexture.Index.Value;
637  material.EnableKeyword("EMISSION_MAP_ON");
638  material.EnableKeyword("_EMISSION");
639  material.SetTexture("_EmissionMap", CreateTexture(texture));
640  material.SetInt("_EmissionUV", def.EmissiveTexture.TexCoord);
641 
642  ApplyTextureTransform(def.EmissiveTexture, material, "_EmissionMap");
643  }
644 
645  material.SetColor("_EmissionColor", def.EmissiveFactor.ToUnityColorRaw());
646 
647  materialWrapper = new MaterialCacheData
648  {
649  UnityMaterial = material,
650  UnityMaterialWithVertexColor = new UnityEngine.Material(material),
651  GLTFMaterial = def
652  };
653 
654  materialWrapper.UnityMaterialWithVertexColor.EnableKeyword("VERTEX_COLOR_ON");
655 
656  if (materialIndex > 0)
657  {
658  _assetCache.MaterialCache[materialIndex] = materialWrapper;
659  }
660  }
661 
662  return materialIndex > 0 ? _assetCache.MaterialCache[materialIndex] : materialWrapper;
663  }
664 
665  protected virtual UnityEngine.Texture CreateTexture(GLTF.Schema.Texture texture)
666  {
667  if (_assetCache.TextureCache[texture.Source.Id] == null)
668  {
669  var source = _assetCache.ImageCache[texture.Source.Id];
670  var desiredFilterMode = FilterMode.Bilinear;
671  var desiredWrapMode = UnityEngine.TextureWrapMode.Repeat;
672 
673  if (texture.Sampler != null)
674  {
675  var sampler = texture.Sampler.Value;
676  switch (sampler.MinFilter)
677  {
678  case MinFilterMode.Nearest:
679  desiredFilterMode = FilterMode.Point;
680  break;
681  case MinFilterMode.Linear:
682  default:
683  desiredFilterMode = FilterMode.Bilinear;
684  break;
685  }
686 
687  switch (sampler.WrapS)
688  {
689  case GLTF.Schema.WrapMode.ClampToEdge:
690  desiredWrapMode = UnityEngine.TextureWrapMode.Clamp;
691  break;
692  case GLTF.Schema.WrapMode.Repeat:
693  default:
694  desiredWrapMode = UnityEngine.TextureWrapMode.Repeat;
695  break;
696  }
697  }
698 
699  if (source.filterMode == desiredFilterMode && source.wrapMode == desiredWrapMode)
700  {
701  _assetCache.TextureCache[texture.Source.Id] = source;
702  }
703  else
704  {
705  var unityTexture = UnityEngine.Object.Instantiate(source);
706  unityTexture.filterMode = desiredFilterMode;
707  unityTexture.wrapMode = desiredWrapMode;
708  _assetCache.TextureCache[texture.Source.Id] = unityTexture;
709  }
710  }
711 
712  return _assetCache.TextureCache[texture.Source.Id];
713  }
714 
715  protected virtual void ApplyTextureTransform(TextureInfo def, UnityEngine.Material mat, string texName)
716  {
717  IExtension extension;
718  if (_root.ExtensionsUsed != null &&
719  _root.ExtensionsUsed.Contains(ExtTextureTransformExtensionFactory.EXTENSION_NAME) &&
720  def.Extensions != null &&
721  def.Extensions.TryGetValue(ExtTextureTransformExtensionFactory.EXTENSION_NAME, out extension))
722  {
723  ExtTextureTransformExtension ext = (ExtTextureTransformExtension)extension;
724 
725  Vector2 temp = ext.Offset.ToUnityVector2Raw();
726  temp = new Vector2(temp.x, -temp.y);
727  mat.SetTextureOffset(texName, temp);
728 
729  mat.SetTextureScale(texName, ext.Scale.ToUnityVector2Raw());
730  }
731  }
732 
733  protected const string Base64StringInitializer = "^data:[a-z-]+/[a-z-]+;base64,";
734 
735  protected virtual IEnumerator LoadImage(string rootPath, Image image, int imageID)
736  {
737  if (_assetCache.ImageCache[imageID] == null)
738  {
739  Texture2D texture = null;
740  if (image.Uri != null)
741  {
742  var uri = image.Uri;
743 
744  Regex regex = new Regex(Base64StringInitializer);
745  Match match = regex.Match(uri);
746  if (match.Success)
747  {
748  var base64Data = uri.Substring(match.Length);
749  var textureData = Convert.FromBase64String(base64Data);
750  texture = new Texture2D(0, 0);
751  texture.LoadImage(textureData);
752  }
753  else if (_loadType == LoadType.Uri)
754  {
755  var www = UnityWebRequest.Get(Path.Combine(rootPath, uri));
756  www.downloadHandler = new DownloadHandlerTexture();
757 
758 #if UNITY_2017_2_OR_NEWER
759  yield return www.SendWebRequest();
760 #else
761  yield return www.Send();
762 #endif
763 
764  // HACK to enable mipmaps :(
765  var tempTexture = DownloadHandlerTexture.GetContent(www);
766  if (tempTexture != null)
767  {
768  texture = new Texture2D(tempTexture.width, tempTexture.height, tempTexture.format, true);
769  texture.SetPixels(tempTexture.GetPixels());
770  texture.Apply(true);
771  }
772  else
773  {
774  Debug.LogFormat("{0} {1}", www.responseCode, www.url);
775  texture = new Texture2D(16, 16);
776  }
777  }
778  else if (_loadType == LoadType.Stream)
779  {
780  var pathToLoad = Path.Combine(rootPath, uri);
781  var file = File.OpenRead(pathToLoad);
782  byte[] bufferData = new byte[file.Length];
783  file.Read(bufferData, 0, (int)file.Length);
784 #if !WINDOWS_UWP
785  file.Close();
786 #else
787  file.Dispose();
788 #endif
789  texture = new Texture2D(0, 0);
790  texture.LoadImage(bufferData);
791  }
792  }
793  else
794  {
795  texture = new Texture2D(0, 0);
796  var bufferView = image.BufferView.Value;
797  var data = new byte[bufferView.ByteLength];
798 
799  var bufferContents = _assetCache.BufferCache[bufferView.Buffer.Id];
800  bufferContents.Stream.Position = bufferView.ByteOffset + bufferContents.ChunkOffset;
801  bufferContents.Stream.Read(data, 0, data.Length);
802  texture.LoadImage(data);
803  }
804 
805  _assetCache.ImageCache[imageID] = texture;
806  }
807  }
808 
812  protected virtual IEnumerator LoadBuffer(string sourceUri, GLTF.Schema.Buffer buffer, int bufferIndex)
813  {
814  if (buffer.Uri != null)
815  {
816  Stream bufferStream = null;
817  var uri = buffer.Uri;
818 
819  Regex regex = new Regex(Base64StringInitializer);
820  Match match = regex.Match(uri);
821  if (match.Success)
822  {
823  string base64String = uri.Substring(match.Length);
824  byte[] base64ByteData = Convert.FromBase64String(base64String);
825  bufferStream = new MemoryStream(base64ByteData, 0, base64ByteData.Length, false, true);
826  }
827  else if (_loadType == LoadType.Uri)
828  {
829  var www = UnityWebRequest.Get(Path.Combine(sourceUri, uri));
830 
831 #if UNITY_2017_2_OR_NEWER
832  yield return www.SendWebRequest();
833 #else
834  yield return www.Send();
835 #endif
836 
837  bufferStream = new MemoryStream(www.downloadHandler.data, 0, www.downloadHandler.data.Length, false, true);
838  }
839  else if (_loadType == LoadType.Stream)
840  {
841  var pathToLoad = Path.Combine(sourceUri, uri);
842  bufferStream = File.OpenRead(pathToLoad);
843  }
844 
845  _assetCache.BufferCache[bufferIndex] = new BufferCacheData()
846  {
847  Stream = bufferStream
848  };
849  }
850  }
851 
857  protected static string AbsoluteUriPath(string gltfPath)
858  {
859  var uri = new Uri(gltfPath);
860  var partialPath = uri.AbsoluteUri.Remove(uri.AbsoluteUri.Length - uri.Query.Length - uri.Segments[uri.Segments.Length - 1].Length);
861  return partialPath;
862  }
863 
869  protected static string AbsoluteFilePath(string gltfPath)
870  {
871  var fileName = Path.GetFileName(gltfPath);
872  var lastIndex = gltfPath.IndexOf(fileName);
873  var partialPath = gltfPath.Substring(0, lastIndex);
874  return partialPath;
875  }
876  }
877 }
virtual void BuildMeshAttributes(MeshPrimitive primitive, int meshID, int primitiveIndex)
IEnumerator ImportScene(int sceneIndex=-1, bool isMultithreaded=false)
Creates a scene based off loaded JSON. Includes loading in binary and image data to construct the mes...
static void ConvertVector3CoordinateSpace(ref AttributeAccessor attributeAccessor, GLTF.Math.Vector3 coordinateSpaceCoordinateScale)
Converts vector3 to specified coordinate space
IEnumerator Load(int sceneIndex=-1, bool isMultithreaded=false)
Loads via a web call the gltf file and then constructs a scene
UnityEngine.Material UnityMaterialWithVertexColor
readonly GLTF.Schema.Material DefaultMaterial
virtual IEnumerator LoadBuffer(string sourceUri, GLTF.Schema.Buffer buffer, int bufferIndex)
Load the remote URI data into a byte array.
virtual IEnumerator LoadImage(string rootPath, Image image, int imageID)
readonly Transform _sceneParent
static string AbsoluteUriPath(string gltfPath)
Get the absolute path to a gltf uri reference.
List< MeshCacheData[]> MeshCache
Cache of loaded meshes
Definition: AssetCache.cs:35
Texture2D [] ImageCache
Raw loaded images
Definition: AssetCache.cs:15
GLTFSceneImporter(string rootPath, Stream stream, Transform parent=null, bool addColliders=false)
virtual GameObject CreateMeshPrimitive(MeshPrimitive primitive, int meshID, int primitiveIndex)
void TransformAttributes(ref Dictionary< string, AttributeAccessor > attributeAccessors)
static void ConvertVector4CoordinateSpace(ref AttributeAccessor attributeAccessor, GLTF.Math.Vector4 coordinateSpaceCoordinateScale)
Converts vector4 to specified coordinate space
MaterialCacheData [] MaterialCache
Cache for materials to be applied to the meshes
Definition: AssetCache.cs:25
static string AbsoluteFilePath(string gltfPath)
Get the absolute path a gltf file directory
Caches data in order to construct a unity object
Definition: AssetCache.cs:10
readonly Dictionary< MaterialType, Shader > _shaderCache
Dictionary< int, BufferCacheData > BufferCache
Byte buffers that represent the binary contents that get parsed
Definition: AssetCache.cs:30
GLTFSceneImporter(string gltfUrl, Transform parent=null, bool addColliders=false)
Creates a GLTFSceneBuilder object which will be able to construct a scene based off a url ...
virtual UnityEngine.Texture CreateTexture(GLTF.Schema.Texture texture)
virtual void CreateMeshObject(GLTF.Schema.Mesh mesh, Transform parent, int meshId)
static readonly GLTF.Math.Vector4 TangentSpaceConversionScale
virtual GameObject CreateNode(Node node)
static void FlipFaces(ref AttributeAccessor attributeAccessor)
Rewinds the indices into Unity coordinate space from glTF space
virtual void SetShaderForMaterialType(MaterialType type, Shader shader)
Configures shaders in the shader cache for a given material type
virtual void BuildAttributesForMeshes()
Creates a thread to run multithreaded operations on
Definition: AsyncAction.cs:14
IEnumerator RunOnWorkerThread(Action action)
Definition: AsyncAction.cs:19
static readonly GLTF.Math.Vector3 CoordinateSpaceConversionScale
static void FlipTexCoordArrayV(ref AttributeAccessor attributeAccessor)
Flips the V component of the UV (1-V) to put from glTF into Unity space
virtual MaterialCacheData CreateMaterial(GLTF.Schema.Material def, int materialIndex)
virtual GameObject CreateScene(Scene scene)
virtual void ApplyTextureTransform(TextureInfo def, UnityEngine.Material mat, string texName)
Texture [] TextureCache
Textures to be used for assets. Textures from image cache with samplers applied
Definition: AssetCache.cs:20