LoadSpecialModelsTest.cs 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Numerics;
  5. using System.Text;
  6. using NUnit.Framework;
  7. namespace SharpGLTF.Schema2.LoadAndSave
  8. {
  9. /// <summary>
  10. /// Test cases for models found in <see href="https://github.com/KhronosGroup/glTF-Blender-Exporter"/>
  11. /// </summary>
  12. [TestFixture]
  13. [AttachmentPathFormat("*/TestResults/LoadAndSave/?", true)]
  14. [Category("Model Load and Save")]
  15. public class LoadSpecialModelsTest
  16. {
  17. #region setup
  18. [OneTimeSetUp]
  19. public void Setup()
  20. {
  21. // TestFiles.DownloadReferenceModels();
  22. }
  23. #endregion
  24. [Test]
  25. public void LoadEscapedUriModel()
  26. {
  27. var path = System.IO.Path.Combine(TestContext.CurrentContext.TestDirectory, "Assets\\white space.gltf");
  28. var model = ModelRoot.Load(path);
  29. Assert.That(model, Is.Not.Null);
  30. model.AttachToCurrentTest("white space.glb");
  31. }
  32. public void LoadWithCustomImageLoader()
  33. {
  34. // load Polly model
  35. var model = ModelRoot.Load(TestFiles.GetPollyFileModelPath());
  36. }
  37. [Test(Description = "Example of traversing the visual tree all the way to individual vertices and indices")]
  38. public void LoadPollyModel()
  39. {
  40. // load Polly model
  41. var model = ModelRoot.Load(TestFiles.GetPollyFileModelPath(), Validation.ValidationMode.TryFix);
  42. Assert.That(model, Is.Not.Null);
  43. var triangles = model.DefaultScene
  44. .EvaluateTriangles<Geometry.VertexTypes.VertexPosition, Geometry.VertexTypes.VertexTexture1>(null, model.LogicalAnimations[0], 0.5f)
  45. .ToList();
  46. // Save as GLB, and also evaluate all triangles and save as Wavefront OBJ
  47. model.AttachToCurrentTest("polly_out.glb");
  48. model.AttachToCurrentTest("polly_out.obj");
  49. // hierarchically browse some elements of the model:
  50. var scene = model.DefaultScene;
  51. var pollyNode = scene.FindNode(n => n.Name == "Polly_Display");
  52. var pollyPrimitive = pollyNode.Mesh.Primitives[0];
  53. var pollyIndices = pollyPrimitive.GetIndices();
  54. var pollyPositions = pollyPrimitive.GetVertices("POSITION").AsVector3Array();
  55. var pollyNormals = pollyPrimitive.GetVertices("NORMAL").AsVector3Array();
  56. for (int i = 0; i < pollyIndices.Count; i += 3)
  57. {
  58. var a = (int)pollyIndices[i + 0];
  59. var b = (int)pollyIndices[i + 1];
  60. var c = (int)pollyIndices[i + 2];
  61. var ap = pollyPositions[a];
  62. var bp = pollyPositions[b];
  63. var cp = pollyPositions[c];
  64. var an = pollyNormals[a];
  65. var bn = pollyNormals[b];
  66. var cn = pollyNormals[c];
  67. TestContext.WriteLine($"Triangle {ap} {an} {bp} {bn} {cp} {cn}");
  68. }
  69. // create a clone and apply a global axis transform.
  70. var clonedModel = model.DeepClone();
  71. var basisTransform
  72. = Matrix4x4.CreateScale(1, 2, 1)
  73. * Matrix4x4.CreateFromYawPitchRoll(1, 2, 3)
  74. * Matrix4x4.CreateTranslation(10,5,2);
  75. clonedModel.ApplyBasisTransform(basisTransform);
  76. clonedModel.AttachToCurrentTest("polly_out_transformed.glb");
  77. var wsettings = new WriteSettings();
  78. wsettings.ImageWriting = ResourceWriteMode.BufferView;
  79. wsettings.MergeBuffers = true;
  80. wsettings.BuffersMaxSize = 1024 * 1024 * 10;
  81. clonedModel.AttachToCurrentTest("polly_out_merged_10mb.gltf", wsettings);
  82. }
  83. [Test]
  84. public void LoadUniVRM()
  85. {
  86. var path = TestFiles.GetUniVRMModelPath();
  87. var model = ModelRoot.Load(path);
  88. Assert.That(model, Is.Not.Null);
  89. var flattenExtensions = model.GatherUsedExtensions().ToArray();
  90. model.AttachToCurrentTest("AliceModel.glb");
  91. }
  92. // [Test]
  93. public void LoadShrekshaoModel()
  94. {
  95. var path = "Assets\\SpecialCases\\shrekshao.glb";
  96. var model = ModelRoot.Load(path);
  97. Assert.That(model, Is.Not.Null);
  98. }
  99. [Test]
  100. public void LoadMouseModel()
  101. {
  102. // this model has several nodes with curve animations containing a single animation key,
  103. // which is causing some problems to the interpolator.
  104. var path = System.IO.Path.Combine(TestContext.CurrentContext.TestDirectory, "Assets\\SpecialCases\\mouse.glb");
  105. var model = ModelRoot.Load(path);
  106. var boundingSphere = Runtime.MeshDecoder.EvaluateBoundingSphere(model.DefaultScene);
  107. var sampler = model
  108. .LogicalNodes[5]
  109. .GetCurveSamplers(model.LogicalAnimations[1])
  110. .Rotation
  111. .CreateCurveSampler(true);
  112. var node5_R_00 = sampler.GetPoint(0);
  113. var node5_R_01 = sampler.GetPoint(1);
  114. Assert.That(node5_R_01, Is.EqualTo(node5_R_00));
  115. model.AttachToCurrentTest("mouse_00.obj", model.LogicalAnimations[1], 0f);
  116. model.AttachToCurrentTest("mouse_01.obj", model.LogicalAnimations[1], 1f);
  117. }
  118. [TestCase("SketchfabExport-WhatIsPBR.glb")] // model has exported tangents in the form <0,0,0,1>
  119. public void LoadSketchfabModels(string path)
  120. {
  121. // this model has several nodes with curve animations containing a single animation key,
  122. // which is causing some problems to the interpolator.
  123. path = System.IO.Path.Combine(TestContext.CurrentContext.TestDirectory, $"Assets\\SpecialCases\\{path}");
  124. var model = ModelRoot.Load(path, Validation.ValidationMode.TryFix);
  125. model.AttachToCurrentTest("output.glb");
  126. }
  127. // these models show normal mapping but lack tangents, which are expected to be
  128. // generated at runtime; These tests generate the tangents and check them against the baseline.
  129. [TestCase("NormalTangentTest.glb")]
  130. [TestCase("NormalTangentMirrorTest.glb")]
  131. public void LoadGeneratedTangetsTest(string fileName)
  132. {
  133. var path = TestFiles.GetSampleModelsPaths().FirstOrDefault(item => item.EndsWith(fileName));
  134. var model = ModelRoot.Load(path);
  135. var mesh = model.DefaultScene
  136. .EvaluateTriangles<Geometry.VertexTypes.VertexPositionNormalTangent, Geometry.VertexTypes.VertexTexture1>()
  137. .ToMeshBuilder();
  138. var editableScene = new Scenes.SceneBuilder();
  139. editableScene.AddRigidMesh(mesh, Matrix4x4.Identity);
  140. model.AttachToCurrentTest("original.glb");
  141. editableScene.ToGltf2().AttachToCurrentTest("WithTangents.glb");
  142. }
  143. [Test]
  144. public void LoadAndSaveToMemory()
  145. {
  146. var path = TestFiles.GetSampleModelsPaths().FirstOrDefault(item => item.EndsWith("Avocado.glb"));
  147. var model = ModelRoot.Load(path);
  148. // model.LogicalImages[0].TransferToSatelliteFile(); // TODO
  149. // we will use this dictionary as our in-memory model container.
  150. var dictionary = new Dictionary<string, ArraySegment<Byte>>();
  151. // write to dictionary
  152. var wcontext = WriteContext.CreateFromDictionary(dictionary);
  153. model.Save("avocado.gltf", wcontext);
  154. Assert.That(dictionary.ContainsKey("avocado.gltf"), Is.True);
  155. Assert.That(dictionary.ContainsKey("avocado.bin"), Is.True);
  156. // read back from dictionary
  157. var rcontext = ReadContext.CreateFromDictionary(dictionary);
  158. var model2 = ModelRoot.Load("avocado.gltf", rcontext);
  159. // TODO: verify
  160. }
  161. [Test]
  162. public void LoadInvalidModelWithJsonFix()
  163. {
  164. // try to load an invalid gltf with an empty array
  165. var path = System.IO.Path.Combine(TestContext.CurrentContext.TestDirectory, "Assets\\SpecialCases\\Invalid_EmptyArray.gltf");
  166. Assert.Throws<Validation.SchemaException>(() => ModelRoot.Load(path));
  167. // try to load an invalid gltf with an empty array, using a hook to fix the json before running the parser.
  168. var rsettings = new ReadSettings();
  169. rsettings.JsonPreprocessor = _RemoveEmptyArrayJsonProcessor;
  170. var model = ModelRoot.Load(path, rsettings);
  171. Assert.That(model, Is.Not.Null);
  172. // save the model, using a hook to modify the json before writing it to the file.
  173. var wsettings = new WriteSettings();
  174. wsettings.JsonPostprocessor = json =>
  175. {
  176. json = json.Replace("glTF 2.0 Validator test suite", "postprocessed json"); return json;
  177. };
  178. path = model.AttachToCurrentTest("modified.gltf", wsettings);
  179. model = ModelRoot.Load(path);
  180. Assert.That("postprocessed json", Is.EqualTo(model.Asset.Generator));
  181. }
  182. private string _RemoveEmptyArrayJsonProcessor(string json)
  183. {
  184. var obj = Newtonsoft.Json.Linq.JObject.Parse(json);
  185. var children = obj.Children().ToArray();
  186. children[1].Remove(); // remove the empty "meshes" array.
  187. json = obj.ToString();
  188. return json;
  189. }
  190. }
  191. }