ExtMeshFeaturesTests.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  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.IO;
  10. using System.Linq;
  11. using System.Numerics;
  12. namespace SharpGLTF.Schema2.Tiles3D
  13. {
  14. using VBTexture1 = VertexBuilder<VertexPosition, VertexTexture1, VertexEmpty>;
  15. [Category("Toolkit.Scenes")]
  16. public class ExtMeshFeaturesTests
  17. {
  18. [SetUp]
  19. public void SetUp()
  20. {
  21. Tiles3DExtensions.RegisterExtensions();
  22. }
  23. // Test files are from https://github.com/CesiumGS/3d-tiles-validator/tree/main/specs/data/gltfExtensions/meshFeatures
  24. [Test(Description = "Reads glTF's with EXT_Mesh_Features")]
  25. [TestCase(@"ValidFeatureIdAttributeDefault/ValidFeatureIdAttributeDefault.gltf", null)]
  26. [TestCase("FeatureIdAttributeAccessorNormalized.gltf", typeof(ModelException))]
  27. [TestCase("FeatureIdAttributeAccessorNotScalar.gltf", typeof(ModelException))]
  28. [TestCase("FeatureIdAttributeAttributeInvalidType.gltf", typeof(InvalidOperationException))]
  29. [TestCase("FeatureIdAttributeAttributeInvalidValue.gltf", typeof(ModelException))]
  30. [TestCase("FeatureIdAttributeFeatureCountInvalidType.gltf", typeof(InvalidOperationException))]
  31. [TestCase("FeatureIdAttributeFeatureCountInvalidValue.gltf", typeof(ModelException))]
  32. [TestCase("FeatureIdAttributeFeatureCountMismatch.gltf", typeof(ModelException))]
  33. [TestCase("FeatureIdAttributeFeatureCountMismatchForNullFeatureId.gltf", typeof(ModelException))]
  34. [TestCase("FeatureIdAttributeFeatureCountMissing.gltf", typeof(ModelException))]
  35. [TestCase("FeatureIdAttributeLabelInvalidType.gltf", typeof(ModelException))]
  36. [TestCase("FeatureIdAttributeLabelInvalidValue.gltf", typeof(ModelException))]
  37. [TestCase("FeatureIdAttributeNullFeatureIdInvalidType.gltf", typeof(InvalidOperationException))]
  38. [TestCase("FeatureIdAttributeNullFeatureIdInvalidValue.gltf", typeof(ModelException))]
  39. [TestCase("FeatureIdTextureFeatureCountMismatch.gltf", typeof(ModelException))]
  40. [TestCase("FeatureIdTextureSamplerInvalidFilterMode.gltf", typeof(ModelException))]
  41. [TestCase("FeatureIdTextureTextureChannelsInvalidElementType.gltf", typeof(InvalidOperationException))]
  42. [TestCase("FeatureIdTextureTextureChannelsInvalidType.gltf", typeof(SchemaException))]
  43. [TestCase("FeatureIdTextureTextureChannelsTooManyChannels.gltf", typeof(ModelException))]
  44. [TestCase("FeatureIdTextureTextureChannelsTooManyElements.gltf", typeof(ModelException))]
  45. [TestCase("FeatureIdTextureTextureImageDataInvalid.gltf", typeof(ModelException))]
  46. [TestCase("FeatureIdTextureTextureIndexInvalidType.gltf", typeof(InvalidOperationException))]
  47. [TestCase("FeatureIdTextureTextureIndexInvalidValue.gltf", typeof(LinkException))]
  48. [TestCase("FeatureIdTextureTextureInvalidType.gltf", typeof(SchemaException))]
  49. [TestCase("FeatureIdTextureTextureTexCoordInvalidType.gltf", typeof(InvalidOperationException))]
  50. [TestCase("FeatureIdTextureTextureTexCoordInvalidValue.gltf", typeof(ModelException))]
  51. [TestCase("ValidFeatureIdAttribute.gltf", null)]
  52. [TestCase("ValidFeatureIdAttributeWithByteStride.glb", null)]
  53. [TestCase("ValidFeatureIdAttributeWithLargerFeatureCount.gltf", null)]
  54. [TestCase("ValidFeatureIdAttributeWithNullFeatureId.gltf", null)]
  55. [TestCase("ValidFeatureIdTexture.glb", null)]
  56. [TestCase("ValidFeatureIdTexture.gltf", null)]
  57. [TestCase("ValidFeatureIdTextureUsingDefaultChannels.gltf", null)]
  58. public void ReadExtMeshFeaturesFiles(string file, Type exception = null)
  59. {
  60. var fileName = ResourceInfo.From($"meshFeatures/{file}");
  61. if (exception != null)
  62. {
  63. Assert.Throws(exception, delegate { ModelRoot.Load(fileName); });
  64. }
  65. else
  66. {
  67. var model = ModelRoot.Load(fileName);
  68. var meshFeaturesExtension = model.LogicalMeshes[0].Primitives[0].GetExtension<MeshExtMeshFeatures>();
  69. Assert.That(meshFeaturesExtension.FeatureIds, Is.Not.Null);
  70. Assert.That(meshFeaturesExtension.FeatureIds, Has.Count.GreaterThanOrEqualTo(1));
  71. var ctx = new ValidationResult(model, ValidationMode.Strict, true);
  72. model.ValidateContent(ctx.GetContext());
  73. }
  74. }
  75. [Test(Description = "Test for settting the FeatureIds with vertex attributes. See sample https://github.com/CesiumGS/3d-tiles-samples/blob/main/glTF/EXT_mesh_features/FeatureIdAttribute")]
  76. // In the sample html code, there is a shader that uses the feature ID to color the triangle
  77. public void FeaturesIdAttributeTest()
  78. {
  79. TestContext.CurrentContext.AttachGltfValidatorLinks();
  80. // Create a triangle with feature ID custom vertex attribute
  81. var featureId = 2;
  82. var material = MaterialBuilder.CreateDefault().WithDoubleSide(true);
  83. var mesh = new MeshBuilder<VertexPositionNormal, VertexWithFeatureId, VertexEmpty>("mesh");
  84. var prim = mesh.UsePrimitive(material);
  85. // All the vertices in the triangle have the same feature ID
  86. var vt0 = VertexBuilder.GetVertexWithFeatureId(new Vector3(-10, 0, 0), new Vector3(0, 0, 1), featureId);
  87. var vt1 = VertexBuilder.GetVertexWithFeatureId(new Vector3(10, 0, 0), new Vector3(0, 0, 1), featureId);
  88. var vt2 = VertexBuilder.GetVertexWithFeatureId(new Vector3(0, 10, 0), new Vector3(0, 0, 1), featureId);
  89. prim.AddTriangle(vt0, vt1, vt2);
  90. var scene = new SceneBuilder();
  91. scene.AddRigidMesh(mesh, Matrix4x4.Identity);
  92. var model = scene.ToGltf2();
  93. // Set the FeatureIds
  94. var featureIdAttribute = new FeatureIDBuilder(1, 0);
  95. model.LogicalMeshes[0].Primitives[0].AddMeshFeatureIds(featureIdAttribute);
  96. // Validate the FeatureIds
  97. var meshFeaturesExtension = (MeshExtMeshFeatures)model.LogicalMeshes[0].Primitives[0].Extensions.FirstOrDefault();
  98. Assert.That(meshFeaturesExtension.FeatureIds, Is.Not.Null);
  99. // Check there should be a custom vertex attribute with name _FEATURE_ID_{attribute}
  100. var attribute = meshFeaturesExtension.FeatureIds[0].Attribute;
  101. Assert.That(attribute == 0);
  102. var primitive = model.LogicalMeshes[0].Primitives[0];
  103. var featureIdVertexAccessor = primitive.GetVertexAccessor($"_FEATURE_ID_{attribute}");
  104. Assert.That(featureIdVertexAccessor, Is.Not.Null);
  105. var items = featureIdVertexAccessor.AsScalarArray();
  106. Assert.That(items, Is.EqualTo(new List<int> { featureId, featureId, featureId }));
  107. var ctx = new ValidationResult(model, ValidationMode.Strict, true);
  108. model.ValidateContent(ctx.GetContext());
  109. scene.AttachToCurrentTest("cesium_ext_mesh_features_feature_id_attribute.glb");
  110. scene.AttachToCurrentTest("cesium_ext_mesh_features_feature_id_attribute.gltf");
  111. scene.AttachToCurrentTest("cesium_ext_mesh_features_feature_id_attribute.plotly");
  112. }
  113. [Test(Description = "Test for settting the FeatureIds with a texture. See sample https://github.com/CesiumGS/3d-tiles-samples/blob/main/glTF/EXT_mesh_features/FeatureIdTexture")]
  114. // In the sample html code, there is a shader that uses the feature'ID from the texture to color the 2 triangles
  115. public void FeaturesIdTextureTest()
  116. {
  117. TestContext.CurrentContext.AttachGltfValidatorLinks();
  118. // Bitmap of 16*16 pixels, containing FeatureID's (0, 1, 2, 3) in the red channel
  119. var img0 = "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAJElEQVR42mNgYmBgoAQzDLwBgwcwY8FDzIDBDRiR8KgBNDAAAOKBAKByX2jMAAAAAElFTkSuQmCC";
  120. var imageBytes = Convert.FromBase64String(img0);
  121. var imageBuilder = ImageBuilder.From(imageBytes);
  122. var material = MaterialBuilder
  123. .CreateDefault()
  124. .WithMetallicRoughnessShader()
  125. .WithBaseColor(imageBuilder, new Vector4(1, 1, 1, 1))
  126. .WithDoubleSide(true)
  127. .WithAlpha(Materials.AlphaMode.OPAQUE)
  128. .WithMetallicRoughness(0, 1);
  129. var mesh = VBTexture1.CreateCompatibleMesh("mesh");
  130. var prim = mesh.UsePrimitive(material);
  131. prim.AddTriangle(
  132. new VBTexture1(new VertexPosition(0, 0, 0), new Vector2(0, 1)),
  133. new VBTexture1(new VertexPosition(1, 0, 0), new Vector2(1, 1)),
  134. new VBTexture1(new VertexPosition(0, 1, 0), new Vector2(0, 0)));
  135. prim.AddTriangle(
  136. new VBTexture1(new VertexPosition(1, 0, 0), new Vector2(1, 1)),
  137. new VBTexture1(new VertexPosition(1, 1, 0), new Vector2(1, 0)),
  138. new VBTexture1(new VertexPosition(0, 1, 0), new Vector2(0, 0)));
  139. var scene = new SceneBuilder();
  140. scene.AddRigidMesh(mesh, Matrix4x4.Identity);
  141. var model = scene.ToGltf2();
  142. // Set the FeatureIds, pointing to the red channel of the texture
  143. var featureId = new FeatureIDBuilder(4, model.LogicalTextures[0]);
  144. var primitive = model.LogicalMeshes[0].Primitives[0];
  145. primitive.AddMeshFeatureIds(featureId);
  146. var meshFeaturesExtension = (MeshExtMeshFeatures)primitive.Extensions.FirstOrDefault();
  147. Assert.That(meshFeaturesExtension.FeatureIds, Is.Not.Null);
  148. var firstFeatureId = meshFeaturesExtension.FeatureIds[0];
  149. var texture = firstFeatureId.GetTexture();
  150. var texCoord = texture.TextureCoordinate;
  151. var textureIdVertexAccessor = primitive.GetVertexAccessor($"TEXCOORD_{texCoord}");
  152. Assert.That(textureIdVertexAccessor, Is.Not.Null);
  153. Assert.That(textureIdVertexAccessor.AsVector2Array(), Has.Count.EqualTo(4));
  154. var ctx = new ValidationResult(model, ValidationMode.Strict, true);
  155. model.ValidateContent(ctx.GetContext());
  156. scene.AttachToCurrentTest("cesium_ext_mesh_features_feature_id_texture.glb");
  157. scene.AttachToCurrentTest("cesium_ext_mesh_features_feature_id_texture.gltf");
  158. scene.AttachToCurrentTest("cesium_ext_mesh_features_feature_id_texture.plotly");
  159. }
  160. }
  161. }