using NUnit.Framework; using SharpGLTF.Scenes; using SharpGLTF.Transforms; using SharpGLTF.Validation; using System; using System.IO; using System.Numerics; using System.Text.Json.Nodes; namespace SharpGLTF.Schema2.Tiles3D { [Category("Toolkit.Scenes")] public class ExtInstanceFeaturesTests { [SetUp] public void SetUp() { Tiles3DExtensions.RegisterExtensions(); } // Test files are from https://github.com/CesiumGS/3d-tiles-validator/tree/main/specs/data/gltfExtensions/instanceFeatures [Test(Description = "Reads glTF's with EXT_Instance_Features")] [TestCase(@"InstanceFeaturesFeatureIdAttributeInvalidValue.gltf", typeof(ModelException))] [TestCase(@"InstanceFeaturesWithoutMeshGpuInstancing.gltf", typeof(SchemaException))] [TestCase(@"ValidInstanceFeatures.gltf", null)] public void ReadExtInstanceFeatures(string file, Type exception = null) { var fileName = ResourceInfo.From($"instanceFeatures/{file}"); if (exception != null) { Assert.Throws(exception, delegate { ModelRoot.Load(fileName); }); } else { var model = ModelRoot.Load(fileName); var instanceFeaturesExtension = model.LogicalNodes[0].GetExtension(); Assert.That(instanceFeaturesExtension.FeatureIds, Is.Not.Null); Assert.That(instanceFeaturesExtension.FeatureIds, Has.Count.GreaterThanOrEqualTo(1)); var ctx = new ValidationResult(model, ValidationMode.Strict, true); model.ValidateContent(ctx.GetContext()); } } [Test(Description = "Creates a gpu_instancing glTF from a tree with Cesium EXT_Instance_Features")] // Sample model structure is from https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_instance_features public void AddExtGpuInstanceFeatures() { var settings = SceneBuilderSchema2Settings.WithGpuInstancing; settings.GpuMeshInstancingMinCount = 0; var modelRoot = ModelRoot.Load(ResourceInfo.From("tree.glb")); var meshBuilder = modelRoot.LogicalMeshes[0].ToMeshBuilder(); var sceneBuilder = new SceneBuilder(); var quaternion = Quaternion.CreateFromYawPitchRoll(0, 0, 0); var scale = Vector3.One; sceneBuilder. AddRigidMesh(meshBuilder, new AffineTransform(scale, quaternion, new Vector3(-10, 0, 10))). WithExtras(JsonNode.Parse("{\"_FEATURE_ID_0\":0}")); sceneBuilder. AddRigidMesh(meshBuilder, new AffineTransform(scale, quaternion, new Vector3(0, 0, 0))). WithExtras(JsonNode.Parse("{\"_FEATURE_ID_0\":1}")); var featureId0 = new FeatureIDBuilder(2, 0, label: "Forests"); var featureId1 = new FeatureIDBuilder(9, label: "Trees"); var model = sceneBuilder.ToGltf2(settings); model.LogicalNodes[0].AddInstanceFeatureIds(featureId0, featureId1); var cesiumExtInstanceFeaturesExtension = model.LogicalNodes[0].GetExtension(); Assert.That(cesiumExtInstanceFeaturesExtension.FeatureIds, Is.Not.Null); Assert.That(featureId0.Equals(cesiumExtInstanceFeaturesExtension.FeatureIds[0])); Assert.That(featureId1.Equals(cesiumExtInstanceFeaturesExtension.FeatureIds[1])); var ctx = new ValidationResult(model, ValidationMode.Strict, true); model.ValidateContent(ctx.GetContext()); model.AttachToCurrentTest("cesium_ext_instance_features.glb"); model.AttachToCurrentTest("cesium_ext_instance_features.gltf"); model.AttachToCurrentTest("cesium_ext_instance_features.plotly"); } } }