TestFiles.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using NUnit.Framework;
  6. namespace SharpGLTF
  7. {
  8. /// <summary>
  9. /// Encapsulates the access to test files.
  10. /// </summary>
  11. public static class TestFiles
  12. {
  13. #region lifecycle
  14. static TestFiles()
  15. {
  16. var wdir = TestContext.CurrentContext.WorkDirectory;
  17. _ExamplesFound = false;
  18. while (wdir.Length > 3)
  19. {
  20. _RootDir = System.IO.Path.Combine(wdir, "TestFiles");
  21. if (wdir.ToLower().EndsWith("tests") && System.IO.Directory.Exists(_RootDir))
  22. {
  23. _ExamplesFound = true;
  24. break;
  25. }
  26. wdir = System.IO.Path.GetDirectoryName(wdir);
  27. }
  28. _Check();
  29. _SchemaDir = System.IO.Path.Combine(_RootDir, "glTF-Schema");
  30. _ValidationDir = System.IO.Path.Combine(_RootDir, "glTF-Validator");
  31. _SampleModelsDir = System.IO.Path.Combine(_RootDir, "glTF-Sample-Models");
  32. _PollyModelsDir = System.IO.Path.Combine(_RootDir, "glTF-Blender-Exporter");
  33. _UniVRMModelsDir = System.IO.Path.Combine(_RootDir, "UniVRM");
  34. _BabylonJsMeshesDir = System.IO.Path.Combine(_RootDir, "BabylonJS-Assets");
  35. _BabylonJsPlaygroundDir = System.IO.Path.Combine(_RootDir, "BabylonJS-PlaygroundScenes");
  36. _GeneratedModelsDir = System.IO.Path.Combine(_RootDir, "GeneratedReferenceModels", "v_0_6_1");
  37. }
  38. #endregion
  39. #region data
  40. private static Boolean _ExamplesFound = false;
  41. private static readonly string _RootDir;
  42. private static readonly string _SchemaDir;
  43. private static readonly string _ValidationDir;
  44. internal static readonly string _SampleModelsDir;
  45. private static readonly string _PollyModelsDir;
  46. private static readonly string _UniVRMModelsDir;
  47. private static readonly string _BabylonJsMeshesDir;
  48. private static readonly string _BabylonJsPlaygroundDir;
  49. private static readonly string _GeneratedModelsDir;
  50. #endregion
  51. #region properties
  52. public static string RootDirectory { get { _Check(); return _RootDir; } }
  53. #endregion
  54. #region API
  55. private static void _Check()
  56. {
  57. Assert.IsTrue(_ExamplesFound, "TestFiles directory not found; please, run '1_DownloadTestFiles.cmd' before running the tests.");
  58. }
  59. public static IReadOnlyList<string> GetSchemaExtensionsModelsPaths()
  60. {
  61. _Check();
  62. return GetModelPathsInDirectory(_SchemaDir, "extensions", "2.0");
  63. }
  64. public static IEnumerable<string> GetReferenceModelPaths(bool useNegative = false)
  65. {
  66. _Check();
  67. var dirPath = _GeneratedModelsDir;
  68. if (dirPath.EndsWith(".zip")) dirPath = dirPath.Substring(0, dirPath.Length - 4);
  69. var manifestsPath = System.IO.Path.Combine(dirPath, useNegative? "Negative" : "Positive");
  70. var manifests = System.IO.Directory.GetFiles(manifestsPath, "Manifest.json", System.IO.SearchOption.AllDirectories)
  71. .Skip(1)
  72. .ToArray();
  73. foreach (var m in manifests)
  74. {
  75. var d = System.IO.Path.GetDirectoryName(m);
  76. var content = System.IO.File.ReadAllText(m);
  77. var doc = Newtonsoft.Json.Linq.JObject.Parse(content);
  78. var models = doc.SelectToken("models");
  79. foreach(var model in models)
  80. {
  81. var mdlPath = (String)model.SelectToken("fileName");
  82. var loadable = !useNegative;
  83. if (loadable) loadable = (Boolean)model.SelectToken("loadable");
  84. mdlPath = System.IO.Path.Combine(d, mdlPath);
  85. yield return mdlPath;
  86. }
  87. }
  88. yield break;
  89. }
  90. public static IReadOnlyList<string> GetSampleModelsPaths()
  91. {
  92. _Check();
  93. var entries = KhronosSampleModel.Load();
  94. var files = entries
  95. .SelectMany(item => item.GetPaths(_SampleModelsDir, "2.0"))
  96. .ToList();
  97. return files;
  98. }
  99. public static IReadOnlyList<string> GetKhronosValidationPaths()
  100. {
  101. _Check();
  102. var skip = new string[]
  103. {
  104. "empty_object.gltf", // need to look further
  105. "custom_property.gltf",
  106. "integer_written_as_float.gltf",
  107. "unknown_type.gltf",
  108. "valid.gltf", // valid just because objects are unused
  109. "get_elements_sparse.gltf", // valid just because objects are unused
  110. "invalid_elements_float.gltf", // sure, it has invalid floats, but then the accessor is not used.
  111. "not_found.gltf", // it fails at a tricky time
  112. "non_relative_uri.gltf", // absolute path pointing to a http which is not supported.
  113. "unrecognized_format.gltf", // might require to dig into the image
  114. "multiple_extensions.gltf", // it's theoretically tracked (it should give a warning) but then, objects should not be empty...
  115. "invalid_tangent.gltf", // it's theoretically tracked (it should give a warning) but then, objects should not be empty...
  116. "primitive_incompatible_mode.gltf", // it's theoretically tracked (it should give a warning) but then, objects should not be empty...
  117. "primitive_no_position.gltf", // it's theoretically tracked (it should give a warning) but then, objects should not be empty...
  118. "index_buffer_degenerate_triangle.gltf", // it's theoretically tracked (it should give a warning) but then, objects should not be empty...
  119. "node_skinned_mesh_without_skin.gltf", // it's theoretically tracked (it should give a warning) but then, objects should not be empty...
  120. "duplicate_extension_entry.gltf",
  121. "named_objects.gltf", // gltf-validator says valid, but Buffer should not be.
  122. "unused_objects.gltf",
  123. "ignored_animated_transform.gltf", // an channel animated a node with a skin has no effect (warning) since nodes with skin have no transform
  124. "ignored_local_transform.gltf", // a transform in a node with a skin has no effect (warning) since nodes with skin have no transform
  125. "ignored_parent_transform.gltf", // a transform in a node with a skin has no effect (warning) since nodes with skin have no transform
  126. "misplaced_bin_chunk.glb",
  127. "valid_placeholder.glb",
  128. "undeclared_extension.gltf",
  129. "unexpected_extension.gltf",
  130. "unresolved_source.gltf",
  131. "unresolved_light_empty_root_ext.gltf",
  132. "unresolved_light_no_root_ext.gltf",
  133. "invalid_image_mime_type.gltf", // actual images cannot be validated
  134. };
  135. var files = GetModelPathsInDirectory(_ValidationDir, "test")
  136. .Where(item => skip.All(f=>!item.EndsWith(f)));
  137. return files
  138. .OrderBy(item => item)
  139. .ToList();
  140. }
  141. public static IReadOnlyList<string> GetBabylonJSModelsPaths(bool validOrInvalid = true)
  142. {
  143. _Check();
  144. var skipAlways = new string[]
  145. {
  146. "\\Elf\\Elf.gltf", // validator reports invalid inverse bind matrices.
  147. "\\meshes\\Tests\\AssetGenerator", // already covered separately.
  148. "\\meshes\\KHR_materials_volume_testing.glb", // draco compression-
  149. "\\meshes\\Yeti\\MayaExport\\", // validator reports out of bounds accesor
  150. "\\meshes\\Demos\\retargeting\\riggedMesh.glb", // validator reports errors
  151. };
  152. var invalid = new string[]
  153. {
  154. };
  155. var files = GetModelPathsInDirectory(_BabylonJsMeshesDir);
  156. return files
  157. .Where(item => !item.ToLower().Contains("gltf-draco"))
  158. .Where(item => !item.ToLower().Contains("gltf-meshopt")) // not supported yet
  159. .Where(item => skipAlways.All(f => !item.Contains(f)))
  160. .Where(item => validOrInvalid == invalid.All(f => !item.EndsWith(f)))
  161. .OrderBy(item => item)
  162. .ToList();
  163. }
  164. public static string GetPollyFileModelPath()
  165. {
  166. _Check();
  167. return System.IO.Path.Combine(_PollyModelsDir, "polly", "project_polly.glb");
  168. }
  169. public static string GetUniVRMModelPath()
  170. {
  171. _Check();
  172. return System.IO.Path.Combine(_UniVRMModelsDir, "AliciaSolid_vrm-0.51.vrm");
  173. }
  174. private static IReadOnlyList<string> GetModelPathsInDirectory(params string[] paths)
  175. {
  176. _Check();
  177. var dirPath = System.IO.Path.Combine(paths);
  178. if (dirPath.EndsWith(".zip")) dirPath = dirPath.Substring(0, dirPath.Length-4);
  179. // if (!System.IO.Path.IsPathFullyQualified(dirPath)) throw new ArgumentException(nameof(dirPath));
  180. if (!System.IO.Path.IsPathRooted(dirPath)) throw new ArgumentException(nameof(dirPath));
  181. var gltf = System.IO.Directory.GetFiles(dirPath, "*.gltf", System.IO.SearchOption.AllDirectories);
  182. var glbb = System.IO.Directory.GetFiles(dirPath, "*.glb", System.IO.SearchOption.AllDirectories);
  183. return gltf.Concat(glbb).ToList();
  184. }
  185. #endregion
  186. }
  187. [System.Diagnostics.DebuggerDisplay("{Name}")]
  188. class KhronosSampleModel
  189. {
  190. public static KhronosSampleModel[] Load()
  191. {
  192. var path = System.IO.Path.Combine(TestFiles._SampleModelsDir, "2.0", "model-index.json");
  193. var text = System.IO.File.ReadAllText(path);
  194. return Read(text);
  195. }
  196. public static KhronosSampleModel[] Read(string json)
  197. {
  198. var opts = new System.Text.Json.JsonSerializerOptions
  199. {
  200. PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase,
  201. PropertyNameCaseInsensitive = true
  202. };
  203. return System.Text.Json.JsonSerializer.Deserialize<KhronosSampleModel[]>(json, opts);
  204. }
  205. public string Name { get; set; }
  206. public string Screenshot { get; set; }
  207. public Dictionary<string, string> Variants { get; set; } = new Dictionary<string, string>();
  208. public IEnumerable<string> GetPaths(params string[] basePath)
  209. {
  210. var rootPath = System.IO.Path.Combine(basePath);
  211. foreach(var variant in Variants)
  212. {
  213. if (variant.Key == "glTF-Draco") continue; // draco is not supported by SharpGLTF
  214. yield return System.IO.Path.Combine(rootPath, Name, variant.Key, variant.Value);
  215. }
  216. }
  217. }
  218. }