KhronosGroup / KhronosGroup/glTF
Enabling geometry variants in glTF
- Dominant language
- HTML
- Stars
- 7.8k
- Forks
- 1.2k
- Avg merge
- 17h 26m
- Merged PRs (30d)
- 5
Description
When using glTF models in a simulation or even on a e-commerce website, it may be useful to provide geometric variations inside a model. The [KHR_materials_variants](https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_variants/README.md) extension enables using variants in materials; this issue is for discussion on how to enable node/mesh variants in glTF.
The example I'm working with is of a car (and its components) that can have "damage states". These damage states may be:
```c++
enum DamageState {
Pristine = 0,
Damaged = 1,
Destroyed = 2
}
```
Let's look at what could actually change between damage states, in increasing order of flexibility:
1. A mesh is morphed
- This could be achieved by `KHR_material_variants` and morph targets/animation/skinning (essentially pre-baking static poses).
- This approach requires the least additions to glTF, but it could be really tricky to work with when generating data.
2. A mesh is switched
- This could be achieved by an extension that enables `variants` of a `mesh` in a `node`.
- This approach allows using completely different geometry but the node graph remains unchanged.
3. A node is switched
- This could be achieved by an extension that enables `variants` of a `node`.
- This could get tricky if we want to enable switching node "deeper" in the scene graph, but if all the glTF extension does is provide the variant mappings, it may be feasible to represent something like this:

Here's a snippet of a glTF where we use "switch nodes" with variants - using a simple extension inspired by `KHR_material_variants`. A "switch node" cannot have a `mesh` and its children can only be other switch nodes. When a variant is not selected, the `0`th variant is the default.
```json
{
"extensions": {
"EXT_node_variants": {
"variants": [
{ "name": "Pristine" },
{ "name": "Damaged" },
{ "name": "Destroyed" }
]
}
},
"nodes": [
{
"name": "Car Body Switch Node",
"children": [ 4 ],
"extensions": {
"EXT_node_variants": {
"mappings": [
{
"node": 1,
"variants": [0]
},
{
"node": 2,
"variants": [1]
},
{
"node": 3,
"variants": [2]
}
]
}
}
},
{
"name": "Car Body Pristine Node",
"mesh": 0
},
{
"name": "Car Body Damaged Node",
"mesh": 1
},
{
"name": "Car Body Pristine Node",
"mesh": 2
},
{
"name": "Car Door Switch Node",
"children": [ 8 ],
"extensions": {
"EXT_node_variants": {
"mappings": [
{
"node": 5,
"variants": [0]
},
{
"node": 6,
"variants": [1]
},
{
"node": 7,
"variants": [2]
}
]
}
}
},
{
"name": "Car Door Pristine Node",
"mesh": 3
},
{
"name": "Car Door Damaged Node",
"mesh": 4
},
{
"name": "Car Door Destroyed Node",
"mesh": 5
},
{
"name": "Car Door Handle Node",
"extensions": {
"EXT_node_variants": {
"mappings": [
{
"node": 9,
"variants": [0]
},
{
"node": 10,
"variants": [1, 2]
}
]
}
}
},
{
"name": "Car Door Handle Pristine Node",
"mesh": 6
},
{
"name": "Car Door Handle Damaged/Destroyed Node",
"mesh": 7
}
]
}
```
Here is a graphic for the schema above:

This is just an initial design and example. We're hoping to get some more insight on how to best achieve the functionality described. Also, discussions on other use cases for such functionality are welcome.
Contributor guide
Research direction
Start with the linked KHR_materials_variants extension README and compare its approach with the proposed EXT_node_variants JSON examples. The issue has no implementation file or test entry point; done would require an agreed design for node or mesh variants and a documented extension direction.
Written by the indexing model from the issue text.
Assessment
- Domain
- computer-graphics
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100