From 9264483c8140c9460acb2f6222a71435ff371000 Mon Sep 17 00:00:00 2001 From: Don McCurdy <1848368+donmccurdy@users.noreply.github.com> Date: Tue, 31 Mar 2026 15:27:36 -0400 Subject: [PATCH 1/7] EXT_mesh_primitive_restart: Performance optimizations and schema updates --- .../EXT_mesh_primitive_restart/README.md | 143 ++++-------------- .../EXT_mesh_primitive_restart.schema.json | 27 ---- .../schema/primitiveGroup.schema.json | 39 ----- 3 files changed, 28 insertions(+), 181 deletions(-) delete mode 100644 extensions/2.0/Vendor/EXT_mesh_primitive_restart/schema/EXT_mesh_primitive_restart.schema.json delete mode 100644 extensions/2.0/Vendor/EXT_mesh_primitive_restart/schema/primitiveGroup.schema.json diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_restart/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_restart/README.md index b77497001c..72964c7aa2 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_restart/README.md +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_restart/README.md @@ -7,7 +7,8 @@ SPDX-License-Identifier: CC-BY-4.0 ## Contributors -* Paul Connelly, Bentley Systems, [@pmconne](https://github.com/pmconne) +- Paul Connelly, Bentley Systems, [@pmconne](https://github.com/pmconne) +- Don McCurdy, Bentley Systems, [@donmccurdy](https://github.com/donmccurdy) ## Status @@ -25,7 +26,28 @@ glTF 2.0 explicitly prohibits index buffers from containing maximal index values > `indices` accessor **MUST NOT** contain the maximum possible value for the component type used (i.e., 255 for unsigned bytes, 65535 for unsigned shorts, 4294967295 for unsigned ints). -This extension permits the above prohibition to be selectively relaxed, while providing a trivial fallback for implementations that don't support primitive restart. +This extension removes the restriction above, allowing `indices` accessors to contain the maximum possible value for the component type in select primitive draw modes, and specifying that these values indicate primitive restart commands. + +Because the extension does not provide a way to specify fallback indices without restart values, files that use the extension must specify it in `extensionsRequired` array - the extension is not optional. + +> **Implementation Note:** Implementations on graphics APIs without primitive restart may still support the extension, by rewriting primitive indices. Compared to processing many more primitives and accessors, the extension may still provide performance advantages even for these implementations, in assets with many primitives. + +## Extending Mesh Indices + +When the `EXT_mesh_primitive_restart` extension is supported, `indices` accessors may contain the maximum possible index values, as primitive restart values, for the following primitive draw modes: + +- `2 LINE_LOOP` +- `3 LINE_STRIP` +- `5 TRIANGLE_STRIP` +- `6 TRIANGLE_FAN` + +The applicable primitive restart value is determined by the accessor component type: + +| `accessor.componentType` | restart value | +| ---------------------------- | ------------- | +| `5121` (UNSIGNED_BYTE) | `255` | +| `5123` (UNSIGNED_SHORT) | `65535` | +| `5125` (UNSIGNED_INT) | `4294967295` | ## Example @@ -35,128 +57,19 @@ Consider the simple example of a pair of line strings with a total of 5 vertices [0, 1, 255, 2, 3, 4] ``` -Over this same buffer, we can alternatively produce 2 separate accessors - one per line string - splitting at (and omitting) the prohibited primitive restart value: +Without `EXT_mesh_primitive_restart`, this pair of line strings would require two separate mesh primitives - one per line string - with two separate indices accessors, splitting at (and omitting) the prohibited primitive restart value: ``` [0, 1, 255, 2, 3, 4] [0, 1] [2, 3, 4] ``` -This permits two equivalent representations of the geometry without duplicating any binary data. In glTF, the accessors look like this: - -```json - "accessors": [ - { - "bufferView": 0, - "count": 2, - "componentType": 5121, - "type": "SCALAR", - "name": "Line string 1 indices" - }, - { - "bufferView": 0, - "byteOffset": 3, - "count": 3, - "componentType": 5121, - "type": "SCALAR", - "name": "Line string 2 indices" - }, - { - "bufferView": 0, - "componentType": 5121, - "count": 6, - "type": "SCALAR", - "name": "All indices" - }, - { - "bufferView": 1, - "componentType": 5126, - "count": 5, - "type": "VEC3", - "max": [ - 0.5, - 0.5, - 0.0 - ], - "min": [ - -0.5, - -0.5, - 0.0 - ], - "name": "Positions accessor" - } - ], -``` - -The mesh looks like this: - -```json - "meshes": [ - { - "primitives": [ - { - "attributes": { - "POSITION": 3 - }, - "indices": 0, - "material": 0, - "mode": 3 - }, - { - "attributes": { - "POSITION": 3 - }, - "indices": 1, - "material": 0, - "mode": 3 - } - ], - "extensions": { - "EXT_mesh_primitive_restart": { - "primitiveGroups": [ - { - "primitives": [ - 0, - 1 - ], - "indices": 2 - } - ] - } - } - } - ], -``` - -By default, this mesh draws two separate line strip primitives, each using its own `indices` accessor. The `EXT_mesh_primitive_restart` extension specifies that both primitives can be replaced with a single one using a combined `indices` accessor containing primitive restart values. - -## glTF Schema Updates - -The `EXT_mesh_primitive_restart` extension is applied to a mesh. Its `primitiveGroups` property is a list of groups of primitives that can be replaced with a single primitive using primitive restart. Each group is described by a list of indices into `mesh.primitives`, along with the index of the accessor that supplies the vertex indices for the replacement primitive. - -## Constraints - -The extension is subject to the following constraints. Violation of any constraint renders the entire extension invalid, in which case the extension **SHOULD** be ignored and the `mesh.primitives` objects **SHOULD** be rendered as defined in the glTF 2.0 specification. - -- A given primitive index **MUST NOT** appear in more than one primitive group. - -- Each primitive index in a primitive group **MUST NOT** appear more than once in that group. - -- Each primitive in each group **MUST** use one of the following topology types, as specified by the `mode` property: 2 (line loop), 3 (line strip), 5 (triangle strip), or 6 (triangle fan). No other topology types are permitted. - -- All primitives in a given group **MUST** have identical property values (e.g., attributes, material, mode, etc), with the exception of `indices`. This includes the `extensions` property - e.g., if any primitive in a group has a `KHR_materials_variants` extension object, then all other primitives in the same group **MUST** have that extension with identical content. - -- Each primitive in each group **MUST** define an `indices` property, i.e., they **MUST** use indexed geometry. - -- The `indices` accessor specified by each primitive group **MUST** be a valid index accessor as per the base glTF 2.0 specification, i.e., their types **MUST** be scalar, their component types **MUST** be any of the unsigned integer types, and their buffer views (if defined) **MUST NOT** be used for any purpose other than vertex indices. - -- Primitives referred to by this extension **MUST NOT** have morph targets. +For large collections of line strings and other primitive topologies, encoding indices with restart values can greatly reduce the number of mesh primitives and accessors required, and the associated JSON data. ## JSON Schema -- [EXT_mesh_primitive_restart.schema.json](schema/EXT_mesh_primitive_restart.schema.json) -- [primitiveGroup.schema.json](schema/primitiveGroup.schema.json) +The `"EXT_mesh_primitive_restart"` string must be added to the root-level `extensionsUsed` and `extensionsRequired` arrays. The extension is always required. No additional extensions are added to meshes or mesh primitives; all mesh primitives with applicable draw modes are permitted to use primitive restart values. ## Known Implementations -- [iTwin.js](https://github.com/iTwin/itwinjs-core/pull/8312) +- TODO diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_restart/schema/EXT_mesh_primitive_restart.schema.json b/extensions/2.0/Vendor/EXT_mesh_primitive_restart/schema/EXT_mesh_primitive_restart.schema.json deleted file mode 100644 index 3a9ae8e188..0000000000 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_restart/schema/EXT_mesh_primitive_restart.schema.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "EXT_mesh_primitive_restart", - "title": "EXT_mesh_primitive_restart glTF Mesh Primitive Extension", - "type": "object", - "description": "glTF extension enabling the use of primitive restart values in index buffers", - "allOf": [ - { - "$ref": "glTFProperty.schema.json" - } - ], - "properties": { - "primitiveGroups": { - "type": "array", - "items": { - "$ref": "primitiveGroup.schema.json" - }, - "minItems": 1, - "description": "The list of groups of primitives that can be drawn using a single index buffer with primitive restart" - }, - "extensions": {}, - "extras": {} - }, - "required": [ - "primitiveGroups" - ] -} diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_restart/schema/primitiveGroup.schema.json b/extensions/2.0/Vendor/EXT_mesh_primitive_restart/schema/primitiveGroup.schema.json deleted file mode 100644 index b8fc58f72c..0000000000 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_restart/schema/primitiveGroup.schema.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "primitiveGroup", - "title": "Primitive Group", - "type": "object", - "description": "A group of primitives that can be drawn together using a single index buffer containing restart values", - "allOf": [ - { - "$ref": "glTFProperty.schema.json" - } - ], - "properties": { - "primitives": { - "type": "array", - "items": { - "$ref": "glTFid.schema.json" - }, - "uniqueItems": true, - "minItems": 1, - "description": "The indices of the primitives that will be combined and drawn using a single set of indices", - "gltf_detailedDescription": "The indices of the primitives that will be combined and drawn using a single set of indices. All properties except for `indices` (e.g., `material`, `mode`, and `attributes`) will be obtained from the first primitive in the list, and all other primitives in the list **MUST** have identical values for those properties. The `mode` must be TRIANGLE_FAN, TRIANGLE_STRIP, LINE_LOOP, or LINE_STRIP." - }, - "indices": { - "allOf": [ - { - "$ref": "glTFid.schema.json" - } - ], - "description": "The index of the accessor that contains the vertex indices", - "gltf_detailedDescription": "The index of the accessor that contains the vertex indices. The accessor **MUST** have `SCALAR` type and an unsigned integer component type. The indices are permitted to include the maximal index value for the component type, indicating the start of a new primitive." - }, - "extensions": {}, - "extras": {} - }, - "required": [ - "primitives", - "indices" - ] -} From 8f3d09998572bf7a029be0a9f3dd8018272a8288 Mon Sep 17 00:00:00 2001 From: Don McCurdy <1848368+donmccurdy@users.noreply.github.com> Date: Thu, 16 Apr 2026 17:11:05 -0400 Subject: [PATCH 2/7] Rename to KHR_mesh_primitive_restart --- .../KHR_mesh_primitive_restart}/README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) rename extensions/2.0/{Vendor/EXT_mesh_primitive_restart => Khronos/KHR_mesh_primitive_restart}/README.md (84%) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_restart/README.md b/extensions/2.0/Khronos/KHR_mesh_primitive_restart/README.md similarity index 84% rename from extensions/2.0/Vendor/EXT_mesh_primitive_restart/README.md rename to extensions/2.0/Khronos/KHR_mesh_primitive_restart/README.md index 72964c7aa2..bd8a2fce82 100644 --- a/extensions/2.0/Vendor/EXT_mesh_primitive_restart/README.md +++ b/extensions/2.0/Khronos/KHR_mesh_primitive_restart/README.md @@ -3,16 +3,17 @@ Copyright 2025 Bentley Systems, Incorporated SPDX-License-Identifier: CC-BY-4.0 --> -# EXT_mesh_primitive_restart +# KHR_mesh_primitive_restart ## Contributors - Paul Connelly, Bentley Systems, [@pmconne](https://github.com/pmconne) - Don McCurdy, Bentley Systems, [@donmccurdy](https://github.com/donmccurdy) +- TODO ## Status -Complete +Draft ## Dependencies @@ -20,7 +21,7 @@ Written against the glTF 2.0 spec. ## Overview -"Primitive restart" is a feature of the input assembly stage that restarts the current primitive when the vertex index value is the maximum possible value for a given index buffer type. For example, the line strip primitive usually produces one continuous connected series of line segments, but with primitive restart enabled, a maximal vertex index value (e.g., 65535 for unsigned 16-bit integer indices) indicates the beginning of a new line string disconnected from those preceding it. Primitive restart can be useful for batching multiple line strips, line loops, triangle strips, or triangle fans into a single draw call. Alternatively, batching can be achieved by decomposing the primitives into lines or triangles, but this may introduce many redundant vertices, increasing the amount of data required to describe the geometry. +"Primitive restart" is a feature of the input assembly stage that restarts the current primitive when the vertex index value is the maximum possible value for a given index buffer type. For example, the line strip primitive usually produces one continuous connected series of line segments, but with primitive restart enabled, a maximal vertex index value (e.g., 65535 for unsigned 16-bit integer indices) indicates the beginning of a new line string disconnected from those preceding it. Primitive restart can be useful for batching multiple line strips, line loops, triangle strips, or triangle fans into a single draw call. Alternatively, batching can be achieved by decomposing the primitives into lines or triangles, but this may introduce many redundant vertices, increase the amount of data required to describe the geometry, or discard useful topological information. glTF 2.0 explicitly prohibits index buffers from containing maximal index values because support for primitive restart varies amongst graphics APIs. Per [section 3.7.2.1](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes-overview) of the spec, @@ -28,13 +29,13 @@ glTF 2.0 explicitly prohibits index buffers from containing maximal index values This extension removes the restriction above, allowing `indices` accessors to contain the maximum possible value for the component type in select primitive draw modes, and specifying that these values indicate primitive restart commands. -Because the extension does not provide a way to specify fallback indices without restart values, files that use the extension must specify it in `extensionsRequired` array - the extension is not optional. +Because the extension does not provide a way to specify fallback indices without restart values, assets that use the extension must specify it in `extensionsRequired` array - the extension is not optional. -> **Implementation Note:** Implementations on graphics APIs without primitive restart may still support the extension, by rewriting primitive indices. Compared to processing many more primitives and accessors, the extension may still provide performance advantages even for these implementations, in assets with many primitives. +> **Implementation Note:** Implementations on graphics APIs without primitive restart may still support the extension, by rewriting primitive indices. Compared to processing a larger number of primitives and accessors, the extension may still provide performance advantages even for these implementations. ## Extending Mesh Indices -When the `EXT_mesh_primitive_restart` extension is supported, `indices` accessors may contain the maximum possible index values, as primitive restart values, for the following primitive draw modes: +When the `KHR_mesh_primitive_restart` extension is supported, `indices` accessors may contain the maximum possible index values, as primitive restart values, for the following primitive draw modes: - `2 LINE_LOOP` - `3 LINE_STRIP` @@ -57,7 +58,7 @@ Consider the simple example of a pair of line strings with a total of 5 vertices [0, 1, 255, 2, 3, 4] ``` -Without `EXT_mesh_primitive_restart`, this pair of line strings would require two separate mesh primitives - one per line string - with two separate indices accessors, splitting at (and omitting) the prohibited primitive restart value: +Without `KHR_mesh_primitive_restart`, this pair of line strings would require two separate mesh primitives - one per line string - with two separate indices accessors, splitting at (and omitting) the prohibited primitive restart value: ``` [0, 1, 255, 2, 3, 4] @@ -68,7 +69,7 @@ For large collections of line strings and other primitive topologies, encoding i ## JSON Schema -The `"EXT_mesh_primitive_restart"` string must be added to the root-level `extensionsUsed` and `extensionsRequired` arrays. The extension is always required. No additional extensions are added to meshes or mesh primitives; all mesh primitives with applicable draw modes are permitted to use primitive restart values. +The `"KHR_mesh_primitive_restart"` string must be added to the root-level `extensionsUsed` and `extensionsRequired` arrays. The extension is always required. No additional extensions are added to meshes or mesh primitives; all mesh primitives with applicable draw modes are permitted to use primitive restart values. ## Known Implementations From e5cf57bc8d915e5b0352eecfdbad293553b1f496 Mon Sep 17 00:00:00 2001 From: Don McCurdy <1848368+donmccurdy@users.noreply.github.com> Date: Thu, 16 Apr 2026 17:38:08 -0400 Subject: [PATCH 3/7] Restore deleted files --- .../EXT_mesh_primitive_restart/README.md | 162 ++++++++++++++++++ .../EXT_mesh_primitive_restart.schema.json | 27 +++ .../schema/primitiveGroup.schema.json | 39 +++++ 3 files changed, 228 insertions(+) create mode 100644 extensions/2.0/Vendor/EXT_mesh_primitive_restart/README.md create mode 100644 extensions/2.0/Vendor/EXT_mesh_primitive_restart/schema/EXT_mesh_primitive_restart.schema.json create mode 100644 extensions/2.0/Vendor/EXT_mesh_primitive_restart/schema/primitiveGroup.schema.json diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_restart/README.md b/extensions/2.0/Vendor/EXT_mesh_primitive_restart/README.md new file mode 100644 index 0000000000..b77497001c --- /dev/null +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_restart/README.md @@ -0,0 +1,162 @@ + + +# EXT_mesh_primitive_restart + +## Contributors + +* Paul Connelly, Bentley Systems, [@pmconne](https://github.com/pmconne) + +## Status + +Complete + +## Dependencies + +Written against the glTF 2.0 spec. + +## Overview + +"Primitive restart" is a feature of the input assembly stage that restarts the current primitive when the vertex index value is the maximum possible value for a given index buffer type. For example, the line strip primitive usually produces one continuous connected series of line segments, but with primitive restart enabled, a maximal vertex index value (e.g., 65535 for unsigned 16-bit integer indices) indicates the beginning of a new line string disconnected from those preceding it. Primitive restart can be useful for batching multiple line strips, line loops, triangle strips, or triangle fans into a single draw call. Alternatively, batching can be achieved by decomposing the primitives into lines or triangles, but this may introduce many redundant vertices, increasing the amount of data required to describe the geometry. + +glTF 2.0 explicitly prohibits index buffers from containing maximal index values because support for primitive restart varies amongst graphics APIs. Per [section 3.7.2.1](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes-overview) of the spec, + +> `indices` accessor **MUST NOT** contain the maximum possible value for the component type used (i.e., 255 for unsigned bytes, 65535 for unsigned shorts, 4294967295 for unsigned ints). + +This extension permits the above prohibition to be selectively relaxed, while providing a trivial fallback for implementations that don't support primitive restart. + +## Example + +Consider the simple example of a pair of line strings with a total of 5 vertices where vertices 0 and 1 make up the first line string and vertices 2, 3, and 4 make up the second. An unsigned byte index buffer representing these line strings using primitive restart would look like the following, where index 255 marks the disconnect between the two line strings: + +``` +[0, 1, 255, 2, 3, 4] +``` + +Over this same buffer, we can alternatively produce 2 separate accessors - one per line string - splitting at (and omitting) the prohibited primitive restart value: + +``` +[0, 1, 255, 2, 3, 4] +[0, 1] [2, 3, 4] +``` + +This permits two equivalent representations of the geometry without duplicating any binary data. In glTF, the accessors look like this: + +```json + "accessors": [ + { + "bufferView": 0, + "count": 2, + "componentType": 5121, + "type": "SCALAR", + "name": "Line string 1 indices" + }, + { + "bufferView": 0, + "byteOffset": 3, + "count": 3, + "componentType": 5121, + "type": "SCALAR", + "name": "Line string 2 indices" + }, + { + "bufferView": 0, + "componentType": 5121, + "count": 6, + "type": "SCALAR", + "name": "All indices" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 5, + "type": "VEC3", + "max": [ + 0.5, + 0.5, + 0.0 + ], + "min": [ + -0.5, + -0.5, + 0.0 + ], + "name": "Positions accessor" + } + ], +``` + +The mesh looks like this: + +```json + "meshes": [ + { + "primitives": [ + { + "attributes": { + "POSITION": 3 + }, + "indices": 0, + "material": 0, + "mode": 3 + }, + { + "attributes": { + "POSITION": 3 + }, + "indices": 1, + "material": 0, + "mode": 3 + } + ], + "extensions": { + "EXT_mesh_primitive_restart": { + "primitiveGroups": [ + { + "primitives": [ + 0, + 1 + ], + "indices": 2 + } + ] + } + } + } + ], +``` + +By default, this mesh draws two separate line strip primitives, each using its own `indices` accessor. The `EXT_mesh_primitive_restart` extension specifies that both primitives can be replaced with a single one using a combined `indices` accessor containing primitive restart values. + +## glTF Schema Updates + +The `EXT_mesh_primitive_restart` extension is applied to a mesh. Its `primitiveGroups` property is a list of groups of primitives that can be replaced with a single primitive using primitive restart. Each group is described by a list of indices into `mesh.primitives`, along with the index of the accessor that supplies the vertex indices for the replacement primitive. + +## Constraints + +The extension is subject to the following constraints. Violation of any constraint renders the entire extension invalid, in which case the extension **SHOULD** be ignored and the `mesh.primitives` objects **SHOULD** be rendered as defined in the glTF 2.0 specification. + +- A given primitive index **MUST NOT** appear in more than one primitive group. + +- Each primitive index in a primitive group **MUST NOT** appear more than once in that group. + +- Each primitive in each group **MUST** use one of the following topology types, as specified by the `mode` property: 2 (line loop), 3 (line strip), 5 (triangle strip), or 6 (triangle fan). No other topology types are permitted. + +- All primitives in a given group **MUST** have identical property values (e.g., attributes, material, mode, etc), with the exception of `indices`. This includes the `extensions` property - e.g., if any primitive in a group has a `KHR_materials_variants` extension object, then all other primitives in the same group **MUST** have that extension with identical content. + +- Each primitive in each group **MUST** define an `indices` property, i.e., they **MUST** use indexed geometry. + +- The `indices` accessor specified by each primitive group **MUST** be a valid index accessor as per the base glTF 2.0 specification, i.e., their types **MUST** be scalar, their component types **MUST** be any of the unsigned integer types, and their buffer views (if defined) **MUST NOT** be used for any purpose other than vertex indices. + +- Primitives referred to by this extension **MUST NOT** have morph targets. + +## JSON Schema + +- [EXT_mesh_primitive_restart.schema.json](schema/EXT_mesh_primitive_restart.schema.json) +- [primitiveGroup.schema.json](schema/primitiveGroup.schema.json) + +## Known Implementations + +- [iTwin.js](https://github.com/iTwin/itwinjs-core/pull/8312) diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_restart/schema/EXT_mesh_primitive_restart.schema.json b/extensions/2.0/Vendor/EXT_mesh_primitive_restart/schema/EXT_mesh_primitive_restart.schema.json new file mode 100644 index 0000000000..3a9ae8e188 --- /dev/null +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_restart/schema/EXT_mesh_primitive_restart.schema.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "EXT_mesh_primitive_restart", + "title": "EXT_mesh_primitive_restart glTF Mesh Primitive Extension", + "type": "object", + "description": "glTF extension enabling the use of primitive restart values in index buffers", + "allOf": [ + { + "$ref": "glTFProperty.schema.json" + } + ], + "properties": { + "primitiveGroups": { + "type": "array", + "items": { + "$ref": "primitiveGroup.schema.json" + }, + "minItems": 1, + "description": "The list of groups of primitives that can be drawn using a single index buffer with primitive restart" + }, + "extensions": {}, + "extras": {} + }, + "required": [ + "primitiveGroups" + ] +} diff --git a/extensions/2.0/Vendor/EXT_mesh_primitive_restart/schema/primitiveGroup.schema.json b/extensions/2.0/Vendor/EXT_mesh_primitive_restart/schema/primitiveGroup.schema.json new file mode 100644 index 0000000000..b8fc58f72c --- /dev/null +++ b/extensions/2.0/Vendor/EXT_mesh_primitive_restart/schema/primitiveGroup.schema.json @@ -0,0 +1,39 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "primitiveGroup", + "title": "Primitive Group", + "type": "object", + "description": "A group of primitives that can be drawn together using a single index buffer containing restart values", + "allOf": [ + { + "$ref": "glTFProperty.schema.json" + } + ], + "properties": { + "primitives": { + "type": "array", + "items": { + "$ref": "glTFid.schema.json" + }, + "uniqueItems": true, + "minItems": 1, + "description": "The indices of the primitives that will be combined and drawn using a single set of indices", + "gltf_detailedDescription": "The indices of the primitives that will be combined and drawn using a single set of indices. All properties except for `indices` (e.g., `material`, `mode`, and `attributes`) will be obtained from the first primitive in the list, and all other primitives in the list **MUST** have identical values for those properties. The `mode` must be TRIANGLE_FAN, TRIANGLE_STRIP, LINE_LOOP, or LINE_STRIP." + }, + "indices": { + "allOf": [ + { + "$ref": "glTFid.schema.json" + } + ], + "description": "The index of the accessor that contains the vertex indices", + "gltf_detailedDescription": "The index of the accessor that contains the vertex indices. The accessor **MUST** have `SCALAR` type and an unsigned integer component type. The indices are permitted to include the maximal index value for the component type, indicating the start of a new primitive." + }, + "extensions": {}, + "extras": {} + }, + "required": [ + "primitives", + "indices" + ] +} From 7be0b3caa3d92dbfea417da83f6bf916e63779a2 Mon Sep 17 00:00:00 2001 From: Don McCurdy <1848368+donmccurdy@users.noreply.github.com> Date: Wed, 24 Jun 2026 10:18:28 -0400 Subject: [PATCH 4/7] Reformat implementation note Co-authored-by: Adam Morris --- extensions/2.0/Khronos/KHR_mesh_primitive_restart/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/2.0/Khronos/KHR_mesh_primitive_restart/README.md b/extensions/2.0/Khronos/KHR_mesh_primitive_restart/README.md index bd8a2fce82..45138a47e5 100644 --- a/extensions/2.0/Khronos/KHR_mesh_primitive_restart/README.md +++ b/extensions/2.0/Khronos/KHR_mesh_primitive_restart/README.md @@ -31,7 +31,8 @@ This extension removes the restriction above, allowing `indices` accessors to co Because the extension does not provide a way to specify fallback indices without restart values, assets that use the extension must specify it in `extensionsRequired` array - the extension is not optional. -> **Implementation Note:** Implementations on graphics APIs without primitive restart may still support the extension, by rewriting primitive indices. Compared to processing a larger number of primitives and accessors, the extension may still provide performance advantages even for these implementations. +> [!NOTE] +> Implementations on graphics APIs without primitive restart may still support the extension, by rewriting primitive indices. Compared to processing a larger number of primitives and accessors, the extension may still provide performance advantages even for these implementations. ## Extending Mesh Indices From 3114ddb30d3110090ddd03ab3945f1b7496a8a2e Mon Sep 17 00:00:00 2001 From: Don McCurdy <1848368+donmccurdy@users.noreply.github.com> Date: Wed, 24 Jun 2026 10:19:31 -0400 Subject: [PATCH 5/7] Address PR feedback - Prefer 'restart index' to 'restart value' - Clarify that maximum possible index values are still restricted for list topologies - Minor cleanup --- .../KHR_mesh_primitive_restart/README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_mesh_primitive_restart/README.md b/extensions/2.0/Khronos/KHR_mesh_primitive_restart/README.md index 45138a47e5..1b417c2696 100644 --- a/extensions/2.0/Khronos/KHR_mesh_primitive_restart/README.md +++ b/extensions/2.0/Khronos/KHR_mesh_primitive_restart/README.md @@ -1,5 +1,5 @@ @@ -27,25 +27,25 @@ glTF 2.0 explicitly prohibits index buffers from containing maximal index values > `indices` accessor **MUST NOT** contain the maximum possible value for the component type used (i.e., 255 for unsigned bytes, 65535 for unsigned shorts, 4294967295 for unsigned ints). -This extension removes the restriction above, allowing `indices` accessors to contain the maximum possible value for the component type in select primitive draw modes, and specifying that these values indicate primitive restart commands. +This extension modifies the restriction above, allowing `indices` accessors to contain the maximum possible value for the component type in select primitive draw modes, and specifying that these values indicate primitive restart commands. -Because the extension does not provide a way to specify fallback indices without restart values, assets that use the extension must specify it in `extensionsRequired` array - the extension is not optional. +Because the extension does not provide a way to specify fallback indices without restart indices, assets that use the extension must specify it in `extensionsRequired` array - the extension is not optional. > [!NOTE] > Implementations on graphics APIs without primitive restart may still support the extension, by rewriting primitive indices. Compared to processing a larger number of primitives and accessors, the extension may still provide performance advantages even for these implementations. ## Extending Mesh Indices -When the `KHR_mesh_primitive_restart` extension is supported, `indices` accessors may contain the maximum possible index values, as primitive restart values, for the following primitive draw modes: +When the `KHR_mesh_primitive_restart` extension is supported, `indices` accessors may contain the maximum possible index values, as primitive restart indices, for the following primitive draw modes: - `2 LINE_LOOP` - `3 LINE_STRIP` - `5 TRIANGLE_STRIP` - `6 TRIANGLE_FAN` -The applicable primitive restart value is determined by the accessor component type: +The applicable primitive restart index is determined by the accessor component type: -| `accessor.componentType` | restart value | +| `accessor.componentType` | restart index | | ---------------------------- | ------------- | | `5121` (UNSIGNED_BYTE) | `255` | | `5123` (UNSIGNED_SHORT) | `65535` | @@ -59,18 +59,18 @@ Consider the simple example of a pair of line strings with a total of 5 vertices [0, 1, 255, 2, 3, 4] ``` -Without `KHR_mesh_primitive_restart`, this pair of line strings would require two separate mesh primitives - one per line string - with two separate indices accessors, splitting at (and omitting) the prohibited primitive restart value: +Without `KHR_mesh_primitive_restart`, this pair of line strings would require two separate mesh primitives - one per line string - with two separate indices accessors, splitting at (and omitting) the prohibited primitive restart index: ``` [0, 1, 255, 2, 3, 4] [0, 1] [2, 3, 4] ``` -For large collections of line strings and other primitive topologies, encoding indices with restart values can greatly reduce the number of mesh primitives and accessors required, and the associated JSON data. +For large collections of line strings and other primitive topologies, encoding indices with restart indices can greatly reduce the number of mesh primitives and accessors required, and the associated JSON data. ## JSON Schema -The `"KHR_mesh_primitive_restart"` string must be added to the root-level `extensionsUsed` and `extensionsRequired` arrays. The extension is always required. No additional extensions are added to meshes or mesh primitives; all mesh primitives with applicable draw modes are permitted to use primitive restart values. +The `"KHR_mesh_primitive_restart"` string must be added to the root-level `extensionsUsed` and `extensionsRequired` arrays. The extension is always required. No additional extensions are added to meshes or mesh primitives; all mesh primitives with applicable draw modes are permitted to use primitive restart indices. ## Known Implementations From d23a73d3d7ccd4aab197376baf2cdfe108786c99 Mon Sep 17 00:00:00 2001 From: Don McCurdy <1848368+donmccurdy@users.noreply.github.com> Date: Wed, 24 Jun 2026 11:21:34 -0400 Subject: [PATCH 6/7] Update copyright banner --- extensions/2.0/Khronos/KHR_mesh_primitive_restart/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/2.0/Khronos/KHR_mesh_primitive_restart/README.md b/extensions/2.0/Khronos/KHR_mesh_primitive_restart/README.md index 1b417c2696..37329b5971 100644 --- a/extensions/2.0/Khronos/KHR_mesh_primitive_restart/README.md +++ b/extensions/2.0/Khronos/KHR_mesh_primitive_restart/README.md @@ -1,6 +1,6 @@ # KHR_mesh_primitive_restart From 9811e8407d4533500cfc6b10e3bc408345035a6f Mon Sep 17 00:00:00 2001 From: Don McCurdy <1848368+donmccurdy@users.noreply.github.com> Date: Wed, 24 Jun 2026 11:28:00 -0400 Subject: [PATCH 7/7] Wording: 'modifies the restriction' -> 'relaxes the restriction' --- extensions/2.0/Khronos/KHR_mesh_primitive_restart/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/2.0/Khronos/KHR_mesh_primitive_restart/README.md b/extensions/2.0/Khronos/KHR_mesh_primitive_restart/README.md index 37329b5971..102993c403 100644 --- a/extensions/2.0/Khronos/KHR_mesh_primitive_restart/README.md +++ b/extensions/2.0/Khronos/KHR_mesh_primitive_restart/README.md @@ -27,7 +27,7 @@ glTF 2.0 explicitly prohibits index buffers from containing maximal index values > `indices` accessor **MUST NOT** contain the maximum possible value for the component type used (i.e., 255 for unsigned bytes, 65535 for unsigned shorts, 4294967295 for unsigned ints). -This extension modifies the restriction above, allowing `indices` accessors to contain the maximum possible value for the component type in select primitive draw modes, and specifying that these values indicate primitive restart commands. +This extension relaxes the restriction above, allowing `indices` accessors to contain the maximum possible value for the component type in select primitive draw modes, and specifying that these values indicate primitive restart commands. Because the extension does not provide a way to specify fallback indices without restart indices, assets that use the extension must specify it in `extensionsRequired` array - the extension is not optional.