ExtStructuralMetadataTests.cs 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072
  1. using NUnit.Framework;
  2. using SharpGLTF.Geometry;
  3. using SharpGLTF.Geometry.VertexTypes;
  4. using SharpGLTF.Materials;
  5. using SharpGLTF.Scenes;
  6. using SharpGLTF.Validation;
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Linq;
  10. using System.Numerics;
  11. namespace SharpGLTF.Schema2.Tiles3D
  12. {
  13. using VBTexture1 = VertexBuilder<VertexPosition, VertexTexture1, VertexEmpty>;
  14. [Category("Toolkit.Scenes")]
  15. public class ExtStructuralMetadataTests
  16. {
  17. [SetUp]
  18. public void SetUp()
  19. {
  20. Tiles3DExtensions.RegisterExtensions();
  21. }
  22. // Test files are from https://github.com/CesiumGS/3d-tiles-validator/tree/main/specs/data/gltfExtensions/structuralMetadata
  23. [Test(Description = "Reads glTF's with EXT_Structural_Metadata")]
  24. [TestCase("ExtensionInMeshPrimitiveWithoutTopLevelObject.gltf", typeof(ModelException))]
  25. [TestCase("PropertyAttributesClassPropertyArray.gltf", typeof(ModelException))]
  26. [TestCase("PropertyAttributesClassPropertyInvalidComponentType.gltf", typeof(ModelException))]
  27. [TestCase("PropertyAttributesClassPropertyInvalidEnumValueType.gltf", typeof(ModelException))]
  28. // Todo: Minmax [TestCase("PropertyAttributesClassPropertyMaxNotInRange.gltf", typeof(ModelException))]
  29. // Todo: Minmax [TestCase("PropertyAttributesClassPropertyMinNotInRange.gltf", typeof(ModelException))]
  30. [TestCase("PropertyAttributesClassPropertyString.gltf", typeof(ModelException))]
  31. [TestCase("PropertyAttributesMeshPrimitivePropertyAttributesInvalidElementType.gltf", typeof(InvalidOperationException))]
  32. [TestCase("PropertyAttributesMeshPrimitivePropertyAttributesInvalidElementValue.gltf", typeof(LinkException))]
  33. [TestCase("PropertyAttributesMeshPrimitivePropertyAttributesInvalidLength.gltf", typeof(SchemaException))]
  34. [TestCase("PropertyAttributesMeshPrimitivePropertyAttributesInvalidType.gltf", typeof(SchemaException))]
  35. [TestCase("PropertyAttributesPropertyAttributePropertyInvalidAttribute.gltf", typeof(ModelException))]
  36. // Todo: Minmax [TestCase("PropertyAttributesPropertyAttributePropertyMaxMismatch.gltf", typeof(ModelException))]
  37. // Todo: Minmax [TestCase("PropertyAttributesPropertyAttributePropertyMaxNotInRange.gltf", typeof(ModelException))]
  38. // Todo: Minmax [TestCase("PropertyAttributesPropertyAttributePropertyMinMismatch.gltf", typeof(ModelException))]
  39. // Todo: Minmax [TestCase("PropertyAttributesPropertyAttributePropertyMinNotInRange.gltf", typeof(ModelException))]
  40. // todo minmax with texture [TestCase("PropertyTextureClassPropertyMaxNotInRange.gltf", typeof(ModelException))]
  41. // todo minmax with texture [TestCase("PropertyTextureClassPropertyMinNotInRange.gltf", typeof(ModelException))]
  42. // todo minmax with texture [TestCase("PropertyTextureClassPropertyWithOffsetScaleMinNotInRange.gltf", typeof(ModelException))]
  43. // todo minmax with texture [TestCase("PropertyTextureEnumsInvalidEnumValue.gltf", typeof(ModelException))]
  44. [TestCase("PropertyTextureInvalidPropertyTypeA.gltf", typeof(ModelException))]
  45. [TestCase("PropertyTextureInvalidPropertyTypeB.gltf", typeof(ModelException))]
  46. [TestCase("PropertyTextureMeshPrimitivePropertyTexturesInvalidElementType.gltf", typeof(InvalidOperationException))]
  47. [TestCase("PropertyTextureMeshPrimitivePropertyTexturesInvalidElementValue.gltf", typeof(LinkException))]
  48. [TestCase("PropertyTextureMeshPrimitivePropertyTexturesInvalidLength.gltf", typeof(SchemaException))]
  49. [TestCase("PropertyTextureMeshPrimitivePropertyTexturesInvalidType.gltf", typeof(SchemaException))]
  50. [TestCase("PropertyTextureMeshPrimitivePropertyTextureTexCoordInvalidValue.gltf", typeof(ModelException))]
  51. [TestCase("PropertyTexturePropertyChannelsSizeMismatch.gltf", typeof(ModelException))]
  52. [TestCase("PropertyTexturePropertyIndexInvalidType.gltf", typeof(InvalidOperationException))]
  53. [TestCase("PropertyTexturePropertyIndexInvalidValue.gltf", typeof(LinkException))]
  54. // todo minmax with texture [TestCase("PropertyTexturePropertyTexturePropertyMaxMismatch.gltf", typeof(ModelException))]
  55. // todo minmax with texture [TestCase("PropertyTexturePropertyTexturePropertyMaxNotInRange.gltf", typeof(ModelException))]
  56. // todo minmax with texture [TestCase("PropertyTexturePropertyTexturePropertyMinMismatch.gltf", typeof(ModelException))]
  57. // todo minmax with texture[TestCase("PropertyTexturePropertyTexturePropertyMinNotInRange.gltf", typeof(ModelException))]
  58. [TestCase("StructuralMetadataMissingSchema.gltf", typeof(ModelException))]
  59. [TestCase("StructuralMetadataSchemaAndSchemaUri.gltf", typeof(ModelException))]
  60. [TestCase("ValidMultipleClasses.gltf", null)]
  61. [TestCase("ValidPropertyAttributes.gltf", null)]
  62. [TestCase("ValidPropertyTexture.gltf", null)]
  63. [TestCase("ValidPropertyTextureEnums.gltf", null)]
  64. public void ReadExtStructuralMetadata(string file, Type exception = null)
  65. {
  66. var fileName = ResourceInfo.From($"structuralMetadata/{file}");
  67. if (exception != null)
  68. {
  69. Assert.Throws(exception, delegate { ModelRoot.Load(fileName); });
  70. }
  71. else
  72. {
  73. var model = ModelRoot.Load(fileName);
  74. var structuralMetadataExtension = model.GetExtension<EXTStructuralMetadataRoot>();
  75. var ctx = new ValidationResult(model, ValidationMode.Strict, true);
  76. model.ValidateContent(ctx.GetContext());
  77. }
  78. }
  79. /// <summary>
  80. /// In this test a single triangle is defined, it has attributes defined for all types with a noData value,
  81. /// but the values are set to the noData value. In CesiumJS the triangle is rendered but the
  82. /// attritutes are not shown (because noData).
  83. /// </summary>
  84. [Test(Description = "MetadataAndNullValuesAttributeSample")]
  85. public void MetadataNullValuesAttributeSample()
  86. {
  87. TestContext.CurrentContext.AttachGltfValidatorLinks();
  88. int featureId = 0;
  89. var material = MaterialBuilder.CreateDefault().WithDoubleSide(true);
  90. var mesh = new MeshBuilder<VertexPositionNormal, VertexWithFeatureId, VertexEmpty>("mesh");
  91. var prim = mesh.UsePrimitive(material);
  92. var vt0 = VertexBuilder.GetVertexWithFeatureId(new Vector3(0, 0, 0), new Vector3(0, 0, 1), featureId);
  93. var vt1 = VertexBuilder.GetVertexWithFeatureId(new Vector3(1, 0, 0), new Vector3(0, 0, 1), featureId);
  94. var vt2 = VertexBuilder.GetVertexWithFeatureId(new Vector3(0, 1, 0), new Vector3(0, 0, 1), featureId);
  95. prim.AddTriangle(vt0, vt1, vt2);
  96. var scene = new SceneBuilder();
  97. scene.AddRigidMesh(mesh, Matrix4x4.Identity);
  98. var model = scene.ToGltf2();
  99. var rootMetadata = model.UseStructuralMetadata();
  100. var schema = rootMetadata.UseEmbeddedSchema("schema_001");
  101. var schemaClass = schema.UseClassMetadata("triangles");
  102. var speciesEnum = schema.UseEnumMetadata("speciesEnum", ("Unspecified", 0), ("Oak", 1), ("Pine", 2), ("Maple", 3));
  103. speciesEnum.Name = "Species";
  104. speciesEnum.Description = "An example enum for tree species.";
  105. var descriptionProperty = schemaClass
  106. .UseProperty("description")
  107. .WithStringType();
  108. // for this property, the default value (byte.MaxValue) should be shown in the client when the actual value is
  109. // equal to the noData value (byte.MinValue)
  110. var uint8Property = schemaClass
  111. .UseProperty("uint8")
  112. .WithUInt8Type(byte.MinValue, byte.MaxValue);
  113. var int8Property = schemaClass
  114. .UseProperty("int8")
  115. .WithInt8Type(sbyte.MinValue);
  116. var int16Property = schemaClass
  117. .UseProperty("int16")
  118. .WithInt16Type(short.MinValue);
  119. var uint16Property = schemaClass
  120. .UseProperty("uint16")
  121. .WithUInt16Type(ushort.MinValue);
  122. var int32Property = schemaClass
  123. .UseProperty("int32")
  124. .WithInt32Type(int.MinValue);
  125. var uint32Property = schemaClass
  126. .UseProperty("uint32")
  127. .WithUInt32Type(uint.MinValue);
  128. var int64Property = schemaClass
  129. .UseProperty("int64")
  130. .WithInt64Type(long.MinValue);
  131. var uint64Property = schemaClass
  132. .UseProperty("uint64")
  133. .WithUInt64Type(ulong.MinValue);
  134. // when using float.MinValue there is an error in the validator: ""The value has type FLOAT32 and must be in [-3.4028234663852886e+38,3.4028234663852886e+38], but is -3.4028235e+38"
  135. // And the noData value is shown in CesiumJS. Therefore we use -10.0f here.
  136. var float32Property = schemaClass
  137. .UseProperty("float32")
  138. .WithFloat32Type(-10.0f);
  139. var float64Property = schemaClass
  140. .UseProperty("float64")
  141. .WithFloat64Type(double.MinValue);
  142. var stringProperty = schemaClass
  143. .UseProperty("string")
  144. .WithStringType("noData", "-");
  145. var speciesProperty = schemaClass
  146. .UseProperty("species")
  147. .WithDescription("Type of tree.")
  148. .WithEnumeration(speciesEnum, "Unspecified")
  149. .WithRequired(false);
  150. var vector3Property = schemaClass
  151. .UseProperty("vector3")
  152. .WithVector3Type(new Vector3(-10.0f, -10.0f, -10.0f));
  153. var matrix4x4Property = schemaClass
  154. .UseProperty("matrix4x4")
  155. .WithMatrix4x4Type(Matrix4x4.Identity * -10);
  156. var propertyTable = schemaClass.AddPropertyTable(1);
  157. propertyTable
  158. .UseProperty(descriptionProperty)
  159. .SetValues("Description of the triangle");
  160. propertyTable
  161. .UseProperty(uint8Property)
  162. .SetValues(byte.MinValue);
  163. propertyTable
  164. .UseProperty(int8Property)
  165. .SetValues(sbyte.MinValue);
  166. propertyTable
  167. .UseProperty(int16Property)
  168. .SetValues(short.MinValue);
  169. propertyTable
  170. .UseProperty(uint16Property)
  171. .SetValues(ushort.MinValue);
  172. propertyTable
  173. .UseProperty(int32Property)
  174. .SetValues(int.MinValue);
  175. propertyTable
  176. .UseProperty(uint32Property)
  177. .SetValues(uint.MinValue);
  178. propertyTable
  179. .UseProperty(int64Property)
  180. .SetValues(long.MinValue);
  181. propertyTable
  182. .UseProperty(uint64Property)
  183. .SetValues(ulong.MinValue);
  184. propertyTable
  185. .UseProperty(float32Property)
  186. .SetValues(-10f);
  187. propertyTable
  188. .UseProperty(float64Property)
  189. .SetValues(double.MinValue);
  190. propertyTable
  191. .UseProperty(stringProperty)
  192. .SetValues("noData");
  193. propertyTable
  194. .UseProperty(speciesProperty)
  195. .SetValues((short)0);
  196. propertyTable
  197. .UseProperty(vector3Property)
  198. .SetValues(new Vector3(10.0f,10.0f,10.0f));
  199. var m4 = Matrix4x4.Identity;
  200. propertyTable
  201. .UseProperty(matrix4x4Property)
  202. .SetValues(m4);
  203. foreach (var primitive in model.LogicalMeshes[0].Primitives)
  204. {
  205. var featureIdAttribute = new FeatureIDBuilder(1, 0, propertyTable);
  206. primitive.AddMeshFeatureIds(featureIdAttribute);
  207. }
  208. // create files
  209. var ctx = new ValidationResult(model, ValidationMode.Strict, true);
  210. model.AttachToCurrentTest("cesium_ext_structural_minimal_metadata_sample.glb");
  211. model.AttachToCurrentTest("cesium_ext_structural_minimal_metadata_sample.gltf");
  212. model.AttachToCurrentTest("cesium_ext_structural_minimal_metadata_sample.plotly");
  213. }
  214. [Test(Description = "MinimalMetadataAttributeSample")]
  215. public void MinimalMetadataAttributeSample()
  216. {
  217. TestContext.CurrentContext.AttachGltfValidatorLinks();
  218. int featureId = 0;
  219. var material = MaterialBuilder.CreateDefault().WithDoubleSide(true);
  220. var mesh = new MeshBuilder<VertexPositionNormal, VertexWithFeatureId, VertexEmpty>("mesh");
  221. var prim = mesh.UsePrimitive(material);
  222. var vt0 = VertexBuilder.GetVertexWithFeatureId(new Vector3(0, 0, 0), new Vector3(0, 0, 1), featureId);
  223. var vt1 = VertexBuilder.GetVertexWithFeatureId(new Vector3(1, 0, 0), new Vector3(0, 0, 1), featureId);
  224. var vt2 = VertexBuilder.GetVertexWithFeatureId(new Vector3(0, 1, 0), new Vector3(0, 0, 1), featureId);
  225. prim.AddTriangle(vt0, vt1, vt2);
  226. var scene = new SceneBuilder();
  227. scene.AddRigidMesh(mesh, Matrix4x4.Identity);
  228. var model = scene.ToGltf2();
  229. var rootMetadata = model.UseStructuralMetadata();
  230. var schema = rootMetadata.UseEmbeddedSchema("schema_001");
  231. var schemaClass = schema.UseClassMetadata("triangles");
  232. var nameProperty = schemaClass
  233. .UseProperty("name")
  234. .WithStringType();
  235. var propertyTable = schemaClass.AddPropertyTable(1);
  236. propertyTable
  237. .UseProperty(nameProperty)
  238. .SetValues("this is featureId0");
  239. foreach (var primitive in model.LogicalMeshes[0].Primitives)
  240. {
  241. var featureIdAttribute = new FeatureIDBuilder(1, 0, propertyTable);
  242. primitive.AddMeshFeatureIds(featureIdAttribute);
  243. }
  244. // create files
  245. var ctx = new ValidationResult(model, ValidationMode.Strict, true);
  246. model.AttachToCurrentTest("cesium_ext_structural_minimal_metadata_sample.glb");
  247. model.AttachToCurrentTest("cesium_ext_structural_minimal_metadata_sample.gltf");
  248. model.AttachToCurrentTest("cesium_ext_structural_minimal_metadata_sample.plotly");
  249. }
  250. [Test(Description = "TestWith2PrimitivesAndMetadata")]
  251. public void MultiplePrimitivesAndMetadata()
  252. {
  253. TestContext.CurrentContext.AttachGltfValidatorLinks();
  254. int featureId = 0;
  255. var material = MaterialBuilder.CreateDefault().WithDoubleSide(true);
  256. var mesh = new MeshBuilder<VertexPositionNormal, VertexWithFeatureId, VertexEmpty>("mesh");
  257. var prim = mesh.UsePrimitive(material);
  258. var vt0 = VertexBuilder.GetVertexWithFeatureId(new Vector3(0, 0, 0), new Vector3(0, 0, 1), featureId);
  259. var vt1 = VertexBuilder.GetVertexWithFeatureId(new Vector3(1, 0, 0), new Vector3(0, 0, 1), featureId);
  260. var vt2 = VertexBuilder.GetVertexWithFeatureId(new Vector3(0, 1, 0), new Vector3(0, 0, 1), featureId);
  261. prim.AddTriangle(vt0, vt1, vt2);
  262. // featureId = 1 and 2 (other material)
  263. var material2 = new MaterialBuilder()
  264. .WithDoubleSide(true)
  265. .WithMetallicRoughnessShader()
  266. .WithChannelParam(KnownChannel.BaseColor, KnownProperty.RGBA, new Vector4(1, 0, 1, 1));
  267. var prim2 = mesh.UsePrimitive(material2);
  268. featureId = 1;
  269. var vt3 = VertexBuilder.GetVertexWithFeatureId(new Vector3(2, 0, 0), new Vector3(0, 0, 1), featureId);
  270. var vt4 = VertexBuilder.GetVertexWithFeatureId(new Vector3(3, 0, 0), new Vector3(0, 0, 1), featureId);
  271. var vt5 = VertexBuilder.GetVertexWithFeatureId(new Vector3(2, 1, 0), new Vector3(0, 0, 1), featureId);
  272. prim2.AddTriangle(vt3, vt4, vt5);
  273. featureId = 2;
  274. var vt6 = VertexBuilder.GetVertexWithFeatureId(new Vector3(4, 0, 0), new Vector3(0, 0, 1), featureId);
  275. var vt7 = VertexBuilder.GetVertexWithFeatureId(new Vector3(5, 0, 0), new Vector3(0, 0, 1), featureId);
  276. var vt8 = VertexBuilder.GetVertexWithFeatureId(new Vector3(4, 1, 0), new Vector3(0, 0, 1), featureId);
  277. prim2.AddTriangle(vt6, vt7, vt8);
  278. var scene = new SceneBuilder();
  279. scene.AddRigidMesh(mesh, Matrix4x4.Identity);
  280. var model = scene.ToGltf2();
  281. var rootMetadata = model.UseStructuralMetadata();
  282. var schema = rootMetadata.UseEmbeddedSchema("schema_001");
  283. schema.Name = "schema 001";
  284. schema.Description = "an example schema";
  285. schema.Version = "3.5.1";
  286. var trianglesClass = schema
  287. .UseClassMetadata("triangles")
  288. .WithName("Triangle");
  289. var nameProperty = trianglesClass
  290. .UseProperty("name")
  291. .WithStringType();
  292. var isTriangle = trianglesClass
  293. .UseProperty("IsTriangle")
  294. .WithBooleanType();
  295. var propertyTable = trianglesClass
  296. .AddPropertyTable(3, "PropertyTable");
  297. propertyTable
  298. .UseProperty(nameProperty)
  299. .SetValues("this is featureId0", "this is featureId1", "this is featureId2");
  300. propertyTable
  301. .UseProperty(isTriangle)
  302. .SetValues(false, true, false);
  303. foreach (var primitive in model.LogicalMeshes[0].Primitives)
  304. {
  305. var triangles = primitive.EvaluateTriangles().Count();
  306. var featureIdAttribute = new FeatureIDBuilder(triangles, 0, propertyTable);
  307. primitive.AddMeshFeatureIds(featureIdAttribute);
  308. }
  309. // create files
  310. var ctx = new ValidationResult(model, ValidationMode.Strict, true);
  311. model.AttachToCurrentTest("cesium_ext_structural_metadata_multiple_primitives.glb");
  312. model.AttachToCurrentTest("cesium_ext_structural_metadata_multiple_primitives.gltf");
  313. model.AttachToCurrentTest("cesium_ext_structural_metadata_multiple_primitives.plotly");
  314. }
  315. [Test(Description = "First test with ext_structural_metadata")]
  316. // This test creates a simple triangle (featureId = 0) with ext_structural_metadata (4 tree attributes like
  317. // species (Enumeration), age (Scalar), height (Scalar) and diameter (Scalar) and a property table.
  318. // following the structure described in https://github.com/CesiumGS/glTF/tree/proposal-EXT_structural_metadata/extensions/2.0/Vendor/EXT_structural_metadata
  319. public void TriangleWithMetadataTest()
  320. {
  321. TestContext.CurrentContext.AttachGltfValidatorLinks();
  322. var material = MaterialBuilder.CreateDefault().WithDoubleSide(true);
  323. var mesh = new MeshBuilder<VertexPositionNormal, VertexWithFeatureId, VertexEmpty>("mesh");
  324. var prim = mesh.UsePrimitive(material);
  325. var features = new List<int>() { 0, 1 };
  326. var vt0 = VertexBuilder.GetVertexWithFeatureId(new Vector3(0, 0, 0), new Vector3(0, 0, 1), features[0]);
  327. var vt1 = VertexBuilder.GetVertexWithFeatureId(new Vector3(1, 0, 0), new Vector3(0, 0, 1), features[0]);
  328. var vt2 = VertexBuilder.GetVertexWithFeatureId(new Vector3(0, 1, 0), new Vector3(0, 0, 1), features[0]);
  329. prim.AddTriangle(vt0, vt1, vt2);
  330. var vt3 = VertexBuilder.GetVertexWithFeatureId(new Vector3(2, 0, 0), new Vector3(0, 0, 1), features[1]);
  331. var vt4 = VertexBuilder.GetVertexWithFeatureId(new Vector3(3, 0, 0), new Vector3(0, 0, 1), features[1]);
  332. var vt5 = VertexBuilder.GetVertexWithFeatureId(new Vector3(2, 1, 0), new Vector3(0, 0, 1), features[1]);
  333. prim.AddTriangle(vt3, vt4, vt5);
  334. var scene = new SceneBuilder();
  335. scene.AddRigidMesh(mesh, Matrix4x4.Identity);
  336. var model = scene.ToGltf2();
  337. var rootMetadata = model.UseStructuralMetadata();
  338. var schema = rootMetadata.UseEmbeddedSchema("schema_001");
  339. schema.Name = "schema 001";
  340. schema.Description = "an example schema";
  341. schema.Version = "3.5.1";
  342. var speciesEnum = schema.UseEnumMetadata("speciesEnum", ("Unspecified", 0), ("Oak", 1), ("Pine", 2), ("Maple",3));
  343. speciesEnum.Name = "Species";
  344. speciesEnum.Description = "An example enum for tree species.";
  345. var treeClass = schema
  346. .UseClassMetadata("tree")
  347. .WithName("Tree")
  348. .WithDescription("Woody, perennial plant.");
  349. // species property
  350. var speciesProperty = treeClass
  351. .UseProperty("species")
  352. .WithDescription("Type of tree.")
  353. .WithEnumeration(speciesEnum)
  354. .WithRequired(true);
  355. // age property
  356. var ageProperty = treeClass
  357. .UseProperty("age")
  358. .WithDescription("The age of the tree, in years")
  359. .WithUInt32Type()
  360. .WithRequired(true);
  361. // Height property
  362. var heightProperty = treeClass
  363. .UseProperty("height")
  364. .WithDescription("Height of tree measured from ground level, in meters");
  365. heightProperty.WithFloat32Type();
  366. // Diameter property
  367. var diameterProperty = treeClass
  368. .UseProperty("diameter")
  369. .WithDescription("Diameter at trunk base, in meters.");
  370. diameterProperty.WithFloat32Type();
  371. var propertyTable = treeClass
  372. .AddPropertyTable(features.Count, "PropertyTable");
  373. propertyTable
  374. .UseProperty(ageProperty)
  375. .SetValues((uint)100, (uint)101);
  376. propertyTable
  377. .UseProperty(speciesProperty)
  378. .SetValues((short)0, (short)3);
  379. propertyTable.UseProperty(heightProperty)
  380. .SetValues(10.0f, 11f);
  381. propertyTable.UseProperty(diameterProperty)
  382. .SetValues(1.5f, 2f);
  383. // Set the FeatureIds
  384. var cnt = propertyTable.Count;
  385. var featureIdAttribute = new FeatureIDBuilder(2, 0, propertyTable);
  386. model.LogicalMeshes[0].Primitives[0].AddMeshFeatureIds(featureIdAttribute);
  387. // create files
  388. var ctx = new ValidationResult(model, ValidationMode.Strict, true);
  389. model.AttachToCurrentTest("cesium_ext_structural_metadata_basic_triangle.glb");
  390. model.AttachToCurrentTest("cesium_ext_structural_metadata_basic_triangle.gltf");
  391. model.AttachToCurrentTest("cesium_ext_structural_metadata_basic_triangle.plotly");
  392. }
  393. [Test(Description = "ext_structural_metadata with FeatureId Texture and Property Table")]
  394. // sample see https://github.com/CesiumGS/3d-tiles-samples/tree/main/glTF/EXT_structural_metadata/FeatureIdTextureAndPropertyTable
  395. public void FeatureIdTextureAndPropertytableTest()
  396. {
  397. TestContext.CurrentContext.AttachGltfValidatorLinks();
  398. var img0 = "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAIvklEQVR42u3csW5URxsG4BHBRRoklxROEQlSRCJCKShoXFJZiDSpQEqX2pYii8ZVlDZF7oNcAAURDREdpCEXQKoIlAKFEE3O4s0KoV17zxm8Z+Z8j6zvj6Nfj7Q663k968y8aXd3NxtjYk6a/U9OafDwPN+uFwA8LwA8QJ4XAO/Mw26+6Garm6vd/NbzBfA8X79fGQCXuvll/v1P3XzZ8wXwPF+/X+sjwL/zJBm6BeF5vk6/VgC8nG8nhr4Anufr9GsFwA/d/FzwAnier9OfGgC/d/NdwV8heZ6v158YAH908203/wx8ATzP1+1XBsDsL4hfdfNq4H+H5Hm+fr8yAD6Z/Z/vTZ8XwPN8/d5JQJ53EtAD5HkB4AHyfLwAMMboA5CgPO8jgAfI8wLAA+R5fQDuU/O8PgD3qXleH4D71DyvD8B9ap7XB+A+Nc/rA+B5Xh8Az/P6AHie1wfA87w+AJ7nHQXmeV4A8DyvD8AYow+A53kfAXieFwA8z+sD4HleHwDP8/oAeJ7XB8DzvD4Anuf1AfA8rw+A53l9ADzP6wPgeV4fAM/zjgLzPC8AeJ7XB2CMOaEPIBV88TzfrhcAPC8APECeFwDvfj3p5lI3W91c7eZhzxfA83z1fnUA3O7mx/n333fzdc8XwPN89X51AHzazd/z7//s5vOeL4Dn+er96gD4+JR/P+0F8DxfvV8dAOm9f9/q+QJ4nq/e2wHwvB3Akq/Punk5//6v+V8U+7wAnuer96sD4Jv5Xw///yvi7Z4vgOf56v3qAPh1/pfEj+bp8aTnC+B5vnrvJCDPOwnoAfK8APAAeT5eABhj9AFIUJ73EcAD5HkB4AHyfOAAcJ+a5/UBLE4SuU/N85Pz+gB4PrB3G5DnA3t9ADwf2NsB8LwdwJIv96l5fvJeHwDPB/b6AHg+sHcSkOedBPQAeV4AeIA8Hy8AjDH6AMZLsJQHD+83IN/6RwABIAB4ASAABABfSwBs8j7zkh/sK1dyfvw459evc370KOfLl/stoFB+7PePb9bX0Qew5Af76dOcb906/v7OnePF0GcBhfJjv398s76OPoA1trqz34QlW+hJ+7HfP75ZX8dtwBN+8M+dy/nu3Zzv3Ru2gEL4sd8/vllfRx/Aih/+8+dzfvEi5zdvcr55s/8CCuPHfv/4Zn31O4DZ3LiR8/Pnw7fQk/d+A/IffAewyfvM/gbw4f8G4D4830wfwJIf7GfPjv9T2Oz769dzvn+/3wIK5cd+//hmfR19AEt+sK9dO/5PYbPffA8e5HzxYr8FFMqP/f7xzXonAZ0E5J0EFAACgBcAAkAA8PECwBijD8AOwA6A9xFAAAgAXgAIAAHABw4AfQD6AHh9AGkT95n1AegD4Efx+gD0AfCBvT4AfQC824Bp3PvM+gD0AfCjeH0A+gB4O4A07n1mfwPQB8CP4vUB6APgA3t9APoA+MDeSUAnAXknAQWAAOAFgAAQAHy8ADDG6AOwA7AD4H0EEAACgBcAAkAA8IEDQB+APgBeH0DaxH1mfQD6APhRvD4AfQB8YK8PQB8A7zZgGvc+sz4AfQD8KF4fgD4A3g4gjXuf2d8A9AHwo3h9APoA+MBeH4A+AD6wdxLQSUDeSUABIAB4ASAABAAfLwCMMfoAJCjP+wjgAfK8APAAeT5wALhPzfP6ABYnidyn5vnJ+eQ+Nc/H9cltKp6P65P71Dwf19sB8LwdwJIv96l5fvI+uU/N83F9cp+a5+N6JwF53klAD5DnBYAHyPPxAsAYow9AgvK8jwAeIM8LAA+Q5wMHgPvUPK8PYHGSyH1qnp+c1wfA84G924A8H9jrA+D5wN4OgOftAJZ8uU/N85P3+gB4PrDXB8Dzgb2TgDzvJKAHyPMCwAPk+XgBYIzRByBB+UH+6Oho8NTgfQSwAHgBIAAsAF4ACIDjL/ep+TX9qsV1eHiYt7e3By/gTfnI758+AL7YL1tYBwcHeWdn5+2llCELeJM+8vunD4Av9ssW1oULF/Le3t7gBbxJH/n9cxuQL/bLFtb+/v7bfw5dwJv0kd8/fQB8sT9pgQ1dwJv0kd8/OwD+THYAzQeAPoDkPjW/lp9kAOgDSO5T82v5SQaAPoDkPjW/lp9kAOgDcBKOdxLQUWALgBcAAsAC4AXARAPAGKMPwG9A3g7ARwALgBcAAsAC4AVA4ABwH57XB6APYHGSyH14vkcA6ANI+gD4GF4fQLvebUC+2OsDaNfrA+CLvT6Adr0dAH8mOwB9AK3vANyH5/UBTP790wfAF3t9AO16fQB8sdcH0K53EpB3EtBJQAuAFwACwALgBUC8ADDG6APwG5C3A/ARwALgBYAAsAB4ARA4ANyH5/UB6ANYnCRyH57vEQD6AJI+AD6G1wfQrncbkC/2+gDa9foA+GKvD6BdbwfAn8kOQB9A6zsA9+F5fQCTf//0AfDFXh9Au14fAF/s9QG0650E5J0EdBLQAuAFgACwAHgBEC8AjDH6APwG5O0AfASwAHgBIAAsAF4ABA4A9+F5fQD6ABYnidyH53sEgD6ApA+Aj+H1AbTr3Qbki70+gHa9PgC+2OsDaNfbAfBnsgPQB9D6DsB9eF4fwOTfP30AfLHXB9Cu1wfAF3t9AO16JwF5JwGdBLQAeAEgACwAXgDECwBjjD4AvwF5OwAfASwAXgAIAAuAFwCBA8B9eF4fgD6AxUki9+H5HgGgDyDpA+BjeH0A7Xq3Aflirw+gXa8PgC/2+gDa9XYA/JnsAPQBtL4DcB+e1wcw+fdPHwBf7PUBtOv1AfDFXh9Au95JQN5JQCcBLQBeAAgAC4AXAPECwBijD8BvQN4OwEcAC4AXAALAAuAFQOAAcB+e1wegD2Bxksh9eL5HAOgDSPoA+BheH0C73m1AvtjrA2jX6wPgi70+gHa9HQB/JjsAfQCt7wDch+f1AUz+/dMHwBd7fQDten0AfLHXB9CudxKQdxLQSUALgBcAAsAC4AVAqPfvPyVxz6xUBN7bAAAAAElFTkSuQmCC";
  399. var imageBytes0 = Convert.FromBase64String(img0);
  400. var imageBuilder0 = ImageBuilder.From(imageBytes0);
  401. var img1 = "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAJElEQVR42mNgYmBgoAQzDLwBgwcwY8FDzIDBDRiR8KgBNDAAAOKBAKByX2jMAAAAAElFTkSuQmCC";
  402. var imageBytes1 = Convert.FromBase64String(img1);
  403. var imageBuilder1 = ImageBuilder.From(imageBytes1);
  404. var material = MaterialBuilder
  405. .CreateDefault()
  406. .WithMetallicRoughnessShader()
  407. .WithBaseColor(imageBuilder0, new Vector4(1, 1, 1, 1))
  408. .WithDoubleSide(true)
  409. .WithAlpha(Materials.AlphaMode.OPAQUE)
  410. .WithMetallicRoughness(0, 1)
  411. .WithMetallicRoughness(imageBuilder1);
  412. var mesh = VBTexture1.CreateCompatibleMesh("mesh");
  413. var prim = mesh.UsePrimitive(material);
  414. prim.AddTriangle(
  415. new VBTexture1(new VertexPosition(0, 0, 0), new Vector2(0, 1)),
  416. new VBTexture1(new VertexPosition(1, 0, 0), new Vector2(1, 1)),
  417. new VBTexture1(new VertexPosition(0, 1, 0), new Vector2(0, 0)));
  418. prim.AddTriangle(
  419. new VBTexture1(new VertexPosition(1, 0, 0), new Vector2(1, 1)),
  420. new VBTexture1(new VertexPosition(1, 1, 0), new Vector2(1, 0)),
  421. new VBTexture1(new VertexPosition(0, 1, 0), new Vector2(0, 0)));
  422. var scene = new SceneBuilder();
  423. scene.AddRigidMesh(mesh, Matrix4x4.Identity);
  424. var model = scene.ToGltf2();
  425. var rootMetadata = model.UseStructuralMetadata();
  426. var schema = rootMetadata.UseEmbeddedSchema("FeatureIdTextureAndPropertyTableSchema");
  427. // define schema
  428. var buildingComponentsClass = schema
  429. .UseClassMetadata("buildingComponents")
  430. .WithName("Building components")
  431. .WithDescription("The components of a building.");
  432. var componentProp = buildingComponentsClass
  433. .UseProperty("component")
  434. .WithName("Component")
  435. .WithStringType();
  436. var yearProp = buildingComponentsClass
  437. .UseProperty("yearBuilt")
  438. .WithName("Year built")
  439. .WithInt16Type();
  440. var propertyTable = buildingComponentsClass
  441. .AddPropertyTable(4, "Example property table");
  442. propertyTable
  443. .UseProperty(componentProp)
  444. .SetValues("Wall", "Door", "Roof", "Window");
  445. propertyTable
  446. .UseProperty(yearProp)
  447. .SetValues((short)1960, (short)1996, (short)1985, (short)2002);
  448. // Set the FeatureIds, pointing to the red channel of the texture
  449. var featureId = new FeatureIDBuilder(4, null, propertyTable);
  450. var primitive = model.LogicalMeshes[0].Primitives[0];
  451. primitive.AddMeshFeatureIds(featureId);
  452. var ctx = new ValidationResult(model, ValidationMode.Strict, true);
  453. model.AttachToCurrentTest("cesium_ext_structural_metadata_featureid_texture_and_property_table.glb");
  454. model.AttachToCurrentTest("cesium_ext_structural_metadata_featureid_texture_and_property_table.gltf");
  455. model.AttachToCurrentTest("cesium_ext_structural_metadata_featureid_texture_and_property_table.plotly");
  456. }
  457. [Test(Description = "ext_structural_metadata with simple property texture")]
  458. // sample see https://github.com/CesiumGS/3d-tiles-samples/tree/main/glTF/EXT_structural_metadata/SimplePropertyTexture
  459. public void SimplePropertyTextureTest()
  460. {
  461. TestContext.CurrentContext.AttachGltfValidatorLinks();
  462. var img0 = "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";
  463. var imageBytes0 = Convert.FromBase64String(img0);
  464. var imageBuilder0 = ImageBuilder.From(imageBytes0);
  465. var img1 = "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABLUlEQVR42mVSSxbDIAh0GzUxKZrmCF3n/oerIx9pupgHIswAGtblE7bIKN0vqSOyXSOjPLAtktv9sCFxmcXj7EgsFj8zN00yYxrBZZJBRYk2LdC4WCDUfAdab7bpDm1lCyBW+7lpDnyNS34gcTQRltTPbAeEdFjcSQ0X9EOhGPYjhgLA7xh3kjxEEpMj1qQj7iAzAYoPELzYtuwK02M06WywAFDfX1MdJEoOtSZ7Allz1mYmWZDNL0pNF6ezu9jsQJUcNK7qzbWvMdSYQ8Jo7KKK8/uo4dxreHe0/HgF2/IqBen/za+Di69Sf8cZz5jmk+hcuhdd2tWLz8IE5MbFnRWT+yyU5vZJRtAOqlvq6MDeOrstu0UidsoO0Ak9xGwE+67+34salNEBSCxX7Bexg0rbq6TFvwAAAABJRU5ErkJggg==";
  466. var imageBytes1 = Convert.FromBase64String(img1);
  467. var imageBuilder1 = ImageBuilder.From(imageBytes1);
  468. var material = MaterialBuilder
  469. .CreateDefault()
  470. .WithMetallicRoughnessShader()
  471. .WithBaseColor(imageBuilder0, new Vector4(1, 1, 1, 1))
  472. .WithDoubleSide(true)
  473. .WithAlpha(Materials.AlphaMode.OPAQUE)
  474. .WithMetallicRoughness(0, 1)
  475. .WithMetallicRoughness(imageBuilder1);
  476. var mesh = VBTexture1.CreateCompatibleMesh("mesh");
  477. var prim = mesh.UsePrimitive(material);
  478. prim.AddTriangle(
  479. new VBTexture1(new VertexPosition(0, 0, 0), new Vector2(0, 1)),
  480. new VBTexture1(new VertexPosition(1, 0, 0), new Vector2(1, 1)),
  481. new VBTexture1(new VertexPosition(0, 1, 0), new Vector2(0, 0)));
  482. prim.AddTriangle(
  483. new VBTexture1(new VertexPosition(1, 0, 0), new Vector2(1, 1)),
  484. new VBTexture1(new VertexPosition(1, 1, 0), new Vector2(1, 0)),
  485. new VBTexture1(new VertexPosition(0, 1, 0), new Vector2(0, 0)));
  486. var scene = new SceneBuilder();
  487. scene.AddRigidMesh(mesh, Matrix4x4.Identity);
  488. var model = scene.ToGltf2();
  489. // --------------------------------------------------------------
  490. var rootMetadata = model.UseStructuralMetadata();
  491. var schema = rootMetadata.UseEmbeddedSchema("SimplePropertyTextureSchema");
  492. // define schema
  493. var exampleMetadataClass = schema
  494. .UseClassMetadata("buildingComponents")
  495. .WithName("Building properties");
  496. exampleMetadataClass
  497. .UseProperty("insideTemperature")
  498. .WithName("Inside temperature")
  499. .WithUInt8Type();
  500. exampleMetadataClass
  501. .UseProperty("outsideTemperature")
  502. .WithName("Outside temperature")
  503. .WithUInt8Type();
  504. exampleMetadataClass
  505. .UseProperty("insulation")
  506. .WithName("Insulation Thickness")
  507. .WithUInt8Type()
  508. .WithNormalized(true);
  509. // define texture property
  510. var buildingPropertyTexture = exampleMetadataClass.AddPropertyTexture();
  511. buildingPropertyTexture.CreateProperty("insideTemperature", model.LogicalTextures[1], new int[] {0});
  512. buildingPropertyTexture.CreateProperty("outsideTemperature", model.LogicalTextures[1], new int[] {1});
  513. buildingPropertyTexture.CreateProperty("insulation", model.LogicalTextures[1], new int[] {2});
  514. // assign to primitive
  515. var primitive = model.LogicalMeshes[0].Primitives[0];
  516. primitive.AddPropertyTexture(buildingPropertyTexture);
  517. var ctx = new ValidationResult(model, ValidationMode.Strict, true);
  518. model.AttachToCurrentTest("cesium_ext_structural_metadata_simple_property_texture.glb");
  519. model.AttachToCurrentTest("cesium_ext_structural_metadata_simple_property_texture.gltf");
  520. model.AttachToCurrentTest("cesium_ext_structural_metadata_simple_property_texture.plotly");
  521. }
  522. [Test(Description = "ext_structural_metadata with Multiple Feature IDs and Properties")]
  523. // sample see https://github.com/CesiumGS/3d-tiles-samples/tree/main/glTF/EXT_structural_metadata/MultipleFeatureIdsAndProperties
  524. public void MultipleFeatureIdsAndPropertiesTest()
  525. {
  526. TestContext.CurrentContext.AttachGltfValidatorLinks();
  527. var material = MaterialBuilder.CreateDefault().WithDoubleSide(true);
  528. var mesh = new MeshBuilder<VertexPosition, VertexWithFeatureIds, VertexEmpty>("mesh");
  529. var prim = mesh.UsePrimitive(material);
  530. // first triangle has _feature_id_0 = 0 and _feature_id_1 = 1
  531. var vt0 = VertexBuilder.GetVertexWithFeatureIds(new Vector3(0, 0, 0), new Vector3(0, 0, 1), 0, 1);
  532. var vt1 = VertexBuilder.GetVertexWithFeatureIds(new Vector3(1, 0, 0), new Vector3(0, 0, 1), 0, 1);
  533. var vt2 = VertexBuilder.GetVertexWithFeatureIds(new Vector3(0, 1, 0), new Vector3(0, 0, 1), 0, 1);
  534. // second triangle has _feature_id_0 = 1 and _feature_id_1 = 0
  535. var vt3 = VertexBuilder.GetVertexWithFeatureIds(new Vector3(1, 1, 0), new Vector3(0, 0, 1), 1, 0);
  536. var vt4 = VertexBuilder.GetVertexWithFeatureIds(new Vector3(0, 0, 0), new Vector3(0, 0, 1), 1, 0);
  537. var vt5 = VertexBuilder.GetVertexWithFeatureIds(new Vector3(1, 0, 0), new Vector3(0, 0, 1), 1, 0);
  538. prim.AddTriangle(vt0, vt1, vt2);
  539. prim.AddTriangle(vt3, vt4, vt5);
  540. var scene = new SceneBuilder();
  541. scene.AddRigidMesh(mesh, Matrix4x4.Identity);
  542. var model = scene.ToGltf2();
  543. // --------------------------------------------------------------
  544. var rootMetadata = model.UseStructuralMetadata();
  545. var schema = rootMetadata.UseEmbeddedSchema("MultipleFeatureIdsAndPropertiesSchema");
  546. // define schema
  547. var exampleMetadataClass = schema
  548. .UseClassMetadata("exampleMetadataClass")
  549. .WithName("Example metadata class")
  550. .WithDescription("An example metadata class");
  551. var vec3Property = exampleMetadataClass
  552. .UseProperty("example_VEC3_FLOAT32")
  553. .WithName("Example VEC3 FLOAT32 property")
  554. .WithDescription("An example property, with type VEC3, with component type FLOAT32")
  555. .WithVector3Type();
  556. var stringProperty = exampleMetadataClass
  557. .UseProperty("example_STRING")
  558. .WithName("Example STRING property")
  559. .WithDescription("An example property, with type STRING")
  560. .WithStringType();
  561. // define table
  562. var examplePropertyTable = exampleMetadataClass.AddPropertyTable(2, "Example property table");
  563. examplePropertyTable
  564. .UseProperty(vec3Property)
  565. .SetValues(new Vector3(3, 3.0999999046325684f, 3.200000047683716f), new Vector3(103, 103.0999999046325684f, 103.200000047683716f));
  566. examplePropertyTable
  567. .UseProperty(stringProperty)
  568. .SetValues("Rain 🌧", "Thunder ⛈");
  569. // assign to primitive
  570. var featureId0 = new FeatureIDBuilder(2, 0, examplePropertyTable);
  571. var featureId1 = new FeatureIDBuilder(2, 1, examplePropertyTable);
  572. model.LogicalMeshes[0].Primitives[0].AddMeshFeatureIds( featureId0, featureId1 );
  573. var ctx = new ValidationResult(model, ValidationMode.Strict, true);
  574. model.AttachToCurrentTest("cesium_ext_structural_metadata_featureid_attribute_and_property_table.glb");
  575. model.AttachToCurrentTest("cesium_ext_structural_metadata_featureid_attribute_and_property_table.gltf");
  576. model.AttachToCurrentTest("cesium_ext_structural_metadata_featureid_attribute_and_property_table.plotly");
  577. }
  578. // sample see https://github.com/CesiumGS/3d-tiles-samples/tree/main/glTF/EXT_structural_metadata/FeatureIdAttributeAndPropertyTable
  579. [Test(Description = "ext_structural_metadata with FeatureIdAttributeAndPropertyTable")]
  580. public void FeatureIdAndPropertyTableTest()
  581. {
  582. TestContext.CurrentContext.AttachGltfValidatorLinks();
  583. var material = MaterialBuilder.CreateDefault().WithDoubleSide(true);
  584. var mesh = new MeshBuilder<VertexPosition, VertexWithFeatureId, VertexEmpty>("mesh");
  585. var prim = mesh.UsePrimitive(material);
  586. // All the vertices in the triangle have the same feature ID
  587. var vt0 = VertexBuilder.GetVertexWithFeatureId(new Vector3(-1, 0, 0), new Vector3(0, 0, 1), 0);
  588. var vt1 = VertexBuilder.GetVertexWithFeatureId(new Vector3(1, 0, 0), new Vector3(0, 0, 1), 0);
  589. var vt2 = VertexBuilder.GetVertexWithFeatureId(new Vector3(0, 1, 0), new Vector3(0, 0, 1), 0);
  590. prim.AddTriangle(vt0, vt1, vt2);
  591. var scene = new SceneBuilder();
  592. scene.AddRigidMesh(mesh, Matrix4x4.Identity);
  593. var model = scene.ToGltf2();
  594. // --------------------------------------------------------------
  595. var rootMetadata = model.UseStructuralMetadata();
  596. var schema = rootMetadata.UseEmbeddedSchema("FeatureIdAttributeAndPropertyTableSchema");
  597. // define schema
  598. var exampleMetadataClass = schema
  599. .UseClassMetadata("exampleMetadataClass")
  600. .WithName("Example metadata class")
  601. .WithDescription("An example metadata class");
  602. var vector3Property = exampleMetadataClass
  603. .UseProperty("example_VEC3_FLOAT32")
  604. .WithName("Example VEC3 FLOAT32 property")
  605. .WithDescription("An example property, with type VEC3, with component type FLOAT32")
  606. .WithVector3Type();
  607. var matrix4x4Property = exampleMetadataClass
  608. .UseProperty("example_MAT4_FLOAT32")
  609. .WithName("Example MAT4 FLOAT32 property")
  610. .WithDescription("An example property, with type MAT4, with component type FLOAT32")
  611. .WithMatrix4x4Type();
  612. // define table
  613. var examplePropertyTable = exampleMetadataClass.AddPropertyTable(1, "Example property table");
  614. examplePropertyTable
  615. .UseProperty(vector3Property)
  616. .SetValues(new Vector3(3, 3.0999999046325684f, 3.200000047683716f));
  617. examplePropertyTable
  618. .UseProperty(matrix4x4Property)
  619. .SetValues(Matrix4x4.Identity);
  620. // assign to primitive
  621. var featureId = new FeatureIDBuilder(1, 0, examplePropertyTable);
  622. model.LogicalMeshes[0].Primitives[0].AddMeshFeatureIds(featureId);
  623. var ctx = new ValidationResult(model, ValidationMode.Strict, true);
  624. model.AttachToCurrentTest("cesium_ext_structural_metadata_multiple_featureids_and_properties.glb");
  625. model.AttachToCurrentTest("cesium_ext_structural_metadata_multiple_featureids_and_properties.gltf");
  626. model.AttachToCurrentTest("cesium_ext_structural_metadata_multiple_featureids_and_properties.plotly");
  627. }
  628. // sample see https://github.com/CesiumGS/3d-tiles-samples/blob/main/glTF/EXT_structural_metadata/ComplexTypes/
  629. [Test(Description = "ext_structural_metadata with complex types")]
  630. public void ComplexTypesTest()
  631. {
  632. TestContext.CurrentContext.AttachGltfValidatorLinks();
  633. var material = MaterialBuilder.CreateDefault().WithDoubleSide(true);
  634. var mesh = new MeshBuilder<VertexPosition, VertexWithFeatureId, VertexEmpty>("mesh");
  635. var prim = mesh.UsePrimitive(material);
  636. // All the vertices in the triangle have the same feature ID
  637. var vt0 = VertexBuilder.GetVertexWithFeatureId(new Vector3(-1, 0, 0), new Vector3(0, 0, 1), 0);
  638. var vt1 = VertexBuilder.GetVertexWithFeatureId(new Vector3(1, 0, 0), new Vector3(0, 0, 1), 0);
  639. var vt2 = VertexBuilder.GetVertexWithFeatureId(new Vector3(0, 1, 0), new Vector3(0, 0, 1), 0);
  640. prim.AddTriangle(vt0, vt1, vt2);
  641. var scene = new SceneBuilder();
  642. scene.AddRigidMesh(mesh, Matrix4x4.Identity);
  643. var model = scene.ToGltf2();
  644. // --------------------------------------------------------------
  645. var rootMetadata = model.UseStructuralMetadata();
  646. var schema = rootMetadata.UseEmbeddedSchema("FeatureIdAttributeAndPropertyTableSchema");
  647. // define schema
  648. var exampleMetadataClass = schema
  649. .UseClassMetadata("exampleMetadataClass")
  650. .WithName("Example metadata class A")
  651. .WithDescription("First example metadata class");
  652. // enums
  653. var exampleEnum = schema.UseEnumMetadata("exampleEnumType", ("ExampleEnumValueA", 0), ("ExampleEnumValueB", 1), ("ExampleEnumValueC", 2));
  654. //// class properties
  655. var uint8ArrayProperty = exampleMetadataClass
  656. .UseProperty("example_variable_length_ARRAY_normalized_UINT8")
  657. .WithName("Example variable-length ARRAY normalized INT8 property")
  658. .WithDescription("An example property, with type ARRAY, with component type UINT8, normalized, and variable length")
  659. .WithUInt8ArrayType()
  660. .WithNormalized(false);
  661. var fixedLengthBooleanProperty = exampleMetadataClass
  662. .UseProperty("example_fixed_length_ARRAY_BOOLEAN")
  663. .WithName("Example fixed-length ARRAY BOOLEAN property")
  664. .WithDescription("An example property, with type ARRAY, with component type BOOLEAN, and fixed length ")
  665. .WithBooleanArrayType(4)
  666. .WithNormalized(false);
  667. var variableLengthStringArrayProperty = exampleMetadataClass
  668. .UseProperty("example_variable_length_ARRAY_STRING")
  669. .WithName("Example variable-length ARRAY STRING property")
  670. .WithDescription("An example property, with type ARRAY, with component type STRING, and variable length")
  671. .WithStringArrayType();
  672. var fixed_length_ARRAY_ENUM = exampleMetadataClass
  673. .UseProperty("example_fixed_length_ARRAY_ENUM")
  674. .WithName("Example fixed-length ARRAY ENUM property")
  675. .WithDescription("An example property, with type ARRAY, with component type ENUM, and fixed length")
  676. .WithEnumArrayType(exampleEnum, 2);
  677. var examplePropertyTable = exampleMetadataClass.AddPropertyTable(1, "Example property table");
  678. var bytes = new List<List<byte>>();
  679. bytes.Add(new List<byte>() { 0, 1, 2, 3, 4, 5, 6, 7 });
  680. examplePropertyTable
  681. .UseProperty(uint8ArrayProperty)
  682. .SetArrayValues(bytes);
  683. var bools = new List<List<bool>>();
  684. bools.Add(new List<bool>() { true, false, true, false });
  685. examplePropertyTable
  686. .UseProperty(fixedLengthBooleanProperty)
  687. .SetArrayValues(bools);
  688. var strings = new List<List<string>>();
  689. strings.Add(["Example string 1", "Example string 2", "Example string 3"]);
  690. examplePropertyTable
  691. .UseProperty(variableLengthStringArrayProperty)
  692. .SetArrayValues(strings);
  693. // Fill property table with enum values
  694. var shorts = new List<List<short>>();
  695. shorts.Add([0, 1]);
  696. examplePropertyTable
  697. .UseProperty(fixed_length_ARRAY_ENUM)
  698. .SetArrayValues(shorts);
  699. // add to primitive
  700. var featureId = new FeatureIDBuilder(1, 0, examplePropertyTable);
  701. model.LogicalMeshes[0].Primitives[0].AddMeshFeatureIds(featureId);
  702. var ctx = new ValidationResult(model, ValidationMode.Strict, true);
  703. model.AttachToCurrentTest("cesium_ext_structural_metadata_complex_types.glb");
  704. model.AttachToCurrentTest("cesium_ext_structural_metadata_complex_types.gltf");
  705. model.AttachToCurrentTest("cesium_ext_structural_metadata_complex_types.plotly");
  706. }
  707. // Sample see https://github.com/CesiumGS/3d-tiles-samples/blob/main/glTF/EXT_structural_metadata/MultipleClasses/
  708. [Test(Description = "ext_structural_metadata with multiple classes")]
  709. public void MultipleClassesTest()
  710. {
  711. var material = MaterialBuilder.CreateDefault().WithDoubleSide(true);
  712. var mesh = new MeshBuilder<VertexPositionNormal, VertexWithFeatureIds, VertexEmpty>("mesh");
  713. var prim = mesh.UsePrimitive(material);
  714. // All the vertices in the triangle have the same feature ID
  715. var vt0 = VertexBuilder.GetVertexWithFeatureIds(new Vector3(-10, 0, 0), new Vector3(0, 0, 1), 0, 0);
  716. var vt1 = VertexBuilder.GetVertexWithFeatureIds(new Vector3(10, 0, 0), new Vector3(0, 0, 1), 0, 0);
  717. var vt2 = VertexBuilder.GetVertexWithFeatureIds(new Vector3(0, 10, 0), new Vector3(0, 0, 1), 0, 0);
  718. prim.AddTriangle(vt0, vt1, vt2);
  719. var scene = new SceneBuilder();
  720. scene.AddRigidMesh(mesh, Matrix4x4.Identity);
  721. var model = scene.ToGltf2();
  722. var rootMetadata = model.UseStructuralMetadata();
  723. var schema = rootMetadata.UseEmbeddedSchema("MultipleClassesSchema");
  724. // classes
  725. var classA = schema
  726. .UseClassMetadata("exampleMetadataClassA")
  727. .WithName("Example metadata class A")
  728. .WithDescription("First example metadata class");
  729. var classAp0 = classA.UseProperty("example_FLOAT32")
  730. .WithName("Example FLOAT32 property")
  731. .WithDescription("An example property, with component type FLOAT32")
  732. .WithFloat32Type();
  733. var classAp1 = classA.UseProperty("example_INT64")
  734. .WithName("Example INT64 property")
  735. .WithDescription("An example property, with component type INT64")
  736. .WithInt64Type();
  737. var classB = schema.UseClassMetadata("exampleMetadataClassB")
  738. .WithName("Example metadata class B")
  739. .WithDescription("Second example metadata class");
  740. var classBp0 = classB.UseProperty("example_UINT16")
  741. .WithName("Example UINT16 property")
  742. .WithDescription("An example property, with component type UINT16")
  743. .WithUInt16Type();
  744. var classBp1 = classB.UseProperty("example_FLOAT64")
  745. .WithName("Example FLOAT64 property")
  746. .WithDescription("An example property, with component type FLOAT64")
  747. .WithFloat64Type();
  748. // properties
  749. var firstPropertyTable = classA.AddPropertyTable(1, "First example property table");
  750. firstPropertyTable.UseProperty(classAp0).SetValues<float>(100);
  751. firstPropertyTable.UseProperty(classAp1).SetValues<long>(101);
  752. var secondPropertyTable = classB.AddPropertyTable(1, "Second example property table");
  753. secondPropertyTable.UseProperty(classBp0).SetValues<ushort>(102);
  754. secondPropertyTable.UseProperty(classBp1).SetValues<double>(103);
  755. // features
  756. // FeatureID 0: featureCount=1, attribute=0, propertyTable=0
  757. var featureId0 = new FeatureIDBuilder(1, 0, firstPropertyTable);
  758. // FeatureID 1: featureCount=1, attribute=1, prorpertyTable=1
  759. var featureId1 = new FeatureIDBuilder(1, 1, secondPropertyTable);
  760. model.LogicalMeshes[0].Primitives[0].AddMeshFeatureIds(featureId0, featureId1);
  761. var ctx = new ValidationResult(model, ValidationMode.Strict, true);
  762. model.AttachToCurrentTest("cesium_ext_structural_metadata_multiple_classes.glb");
  763. model.AttachToCurrentTest("cesium_ext_structural_metadata_multiple_classes.gltf");
  764. model.AttachToCurrentTest("cesium_ext_structural_metadata_multiple_classes.plotly");
  765. }
  766. // Sample see https://github.com/CesiumGS/3d-tiles-samples/blob/main/glTF/EXT_structural_metadata/PropertyAttributesPointCloud/
  767. // Note in the sample an external json file (MetadataSchema.json) is used to define the schema, which is not supported
  768. // in this library yet.
  769. // This test uses the same schema but defines it in code instead.
  770. [Test(Description = "ext_structural_metadata with pointcloud and custom attributes")]
  771. public void CreatePointCloudWithCustomAttributesTest()
  772. {
  773. var material = new MaterialBuilder("material1").WithUnlitShader();
  774. var mesh = new MeshBuilder<VertexPosition, VertexPointcloud, VertexEmpty>("mesh");
  775. var pointCloud = mesh.UsePrimitive(material, 1);
  776. var redColor = new Vector4(1f, 0f, 0f, 1f);
  777. var rand = new Random();
  778. for (var x = -10; x < 10; x++)
  779. {
  780. for (var y = -10; y < 10; y++)
  781. {
  782. for (var z = -10; z < 10; z++)
  783. {
  784. // intensity values is based on x-axis values
  785. // classification of points is 0 or 1 (random)
  786. var vt0 = VertexBuilder.GetVertexPointcloud(new Vector3(x, y, z), redColor, x, rand.Next(0, 2));
  787. pointCloud.AddPoint(vt0);
  788. }
  789. }
  790. }
  791. var model = ModelRoot.CreateModel();
  792. model.CreateMeshes(mesh);
  793. // create a scene, a node, and assign the first mesh (the terrain)
  794. model.UseScene("Default")
  795. .CreateNode().WithMesh(model.LogicalMeshes[0]);
  796. // --------------------------------------------------------------
  797. var rootMetadata = model.UseStructuralMetadata();
  798. var schema = rootMetadata.UseEmbeddedSchema();
  799. var classA = schema
  800. .UseClassMetadata("exampleMetadataClass")
  801. .WithName("Example metadata class")
  802. .WithDescription("An example metadata class for property attributes");
  803. classA.UseProperty("intensity")
  804. .WithName("Example intensity property")
  805. .WithDescription("An example property for the intensity, with component type FLOAT32")
  806. .WithFloat32Type();
  807. var speciesEnum = schema.UseEnumMetadata("classificationEnumType", ("MediumVegetation", 0), ("Buildings", 1));
  808. classA
  809. .UseProperty("classification")
  810. .WithName("Example classification property")
  811. .WithDescription("An example property for the classification, with the classificationEnumType")
  812. .WithEnumeration(speciesEnum);
  813. var propertyAttribute = rootMetadata.AddPropertyAttribute(classA);
  814. var intensityAttribute = propertyAttribute.CreateProperty("intensity");
  815. intensityAttribute.Attribute = "_INTENSITY";
  816. var classificationAttribute = propertyAttribute.CreateProperty("classification");
  817. classificationAttribute.Attribute = "_CLASSIFICATION";
  818. var ctx = new ValidationResult(model, ValidationMode.Strict, true);
  819. foreach (var primitive in model.LogicalMeshes[0].Primitives)
  820. {
  821. primitive.AddPropertyAttribute(propertyAttribute);
  822. }
  823. model.AttachToCurrentTest("cesium_ext_structural_metadata_with_pointcloud_attributes.glb");
  824. model.AttachToCurrentTest("cesium_ext_structural_metadata_with_pointcloud_attributes.gltf");
  825. model.AttachToCurrentTest("cesium_ext_structural_metadata_with_pointcloud_attributes.plotly");
  826. }
  827. }
  828. }