using SharpGLTF.Validation; using System.Collections.Generic; namespace SharpGLTF.Schema2 { public partial class MeshExtInstanceFeatures { private Node _node; internal MeshExtInstanceFeatures(Node node) { _node = node; _featureIds = new List(); } public List FeatureIds { get { return _featureIds; } set { _featureIds = value; } } protected override void OnValidateReferences(ValidationContext validate) { var extInstanceFeatures = _node.GetExtension(); validate.NotNull(nameof(extInstanceFeatures), extInstanceFeatures); var extMeshGpInstancing = _node.GetExtension(); validate.NotNull(nameof(extMeshGpInstancing), extMeshGpInstancing); foreach (var instanceFeatureId in FeatureIds) { if (instanceFeatureId.Attribute.HasValue) { var expectedVertexAttribute = $"_FEATURE_ID_{instanceFeatureId.Attribute}"; var gpuInstancing = _node.GetGpuInstancing(); var featureIdAccessors = gpuInstancing.GetAccessor(expectedVertexAttribute); Guard.NotNull(featureIdAccessors, expectedVertexAttribute); } if (instanceFeatureId.PropertyTable.HasValue) { var metadataExtension = _node.LogicalParent.GetExtension(); Guard.NotNull(metadataExtension, nameof(metadataExtension), "EXT_Structural_Metadata extension is not found."); Guard.NotNull(metadataExtension.PropertyTables[instanceFeatureId.PropertyTable.Value], nameof(instanceFeatureId.PropertyTable), $"Property table index {instanceFeatureId.PropertyTable.Value} does not exist"); } } base.OnValidateReferences(validate); } protected override void OnValidateContent(ValidationContext validate) { var extInstanceFeatures = _node.GetExtension(); validate.NotNull(nameof(FeatureIds), extInstanceFeatures.FeatureIds); validate.IsTrue(nameof(FeatureIds), extInstanceFeatures.FeatureIds.Count > 0, "Instance FeatureIds has items"); foreach (var instanceFeatureId in FeatureIds) { Guard.MustBeGreaterThanOrEqualTo((int)instanceFeatureId.FeatureCount, 1, nameof(instanceFeatureId.FeatureCount)); if (instanceFeatureId.NullFeatureId.HasValue) { Guard.MustBeGreaterThanOrEqualTo((int)instanceFeatureId.NullFeatureId, 0, nameof(instanceFeatureId.NullFeatureId)); } if (instanceFeatureId.Label != null) { var regex = "^[a-zA-Z_][a-zA-Z0-9_]*$"; Guard.IsTrue(System.Text.RegularExpressions.Regex.IsMatch(instanceFeatureId.Label, regex), nameof(instanceFeatureId.Label)); } if (instanceFeatureId.Attribute.HasValue) { Guard.MustBeGreaterThanOrEqualTo((int)instanceFeatureId.Attribute, 0, nameof(instanceFeatureId.Attribute)); } if (instanceFeatureId.PropertyTable.HasValue) { Guard.MustBeGreaterThanOrEqualTo((int)instanceFeatureId.PropertyTable, 0, nameof(instanceFeatureId.PropertyTable)); } } base.OnValidateContent(validate); } } public partial class MeshExtInstanceFeatureID { public MeshExtInstanceFeatureID() { } public MeshExtInstanceFeatureID(int featureCount, int? attribute = null, int? propertyTable = null, string label = null, int? nullFeatureId = null) { _featureCount = featureCount; _attribute = attribute; _label = label; _propertyTable = propertyTable; _nullFeatureId = nullFeatureId; } /// /// The number of unique features in the attribute /// public int FeatureCount { get => _featureCount; } /// /// An attribute containing feature IDs. When this is omitted, then the feature IDs are assigned to the GPU instances by their index. /// public int? Attribute { get => _attribute; } /// /// A label assigned to this feature ID set /// public string Label { get => _label; } /// /// The index of the property table containing per-feature property values. Only applicable when using the `EXT_structural_metadata` extension. /// public int? PropertyTable { get => _propertyTable; } /// /// A value that indicates that no feature is associated with this instance /// public int? NullFeatureId { get => _nullFeatureId; } } public static class ExtInstanceFeatures { /// /// Set the instance feature ids for this node. /// /// /// public static void SetFeatureIds(this Node node, List instanceFeatureIds) { if (instanceFeatureIds == null) { node.RemoveExtensions(); return; } Guard.NotNullOrEmpty(instanceFeatureIds, nameof(instanceFeatureIds)); var extMeshGpInstancing = node.GetExtension(); Guard.NotNull(extMeshGpInstancing, nameof(extMeshGpInstancing)); var ext = node.UseExtension(); ext.FeatureIds = instanceFeatureIds; } } }