-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
KHR_draco_mesh_compression #874
KHR_draco_mesh_compression #874
Conversation
@@ -0,0 +1,172 @@ | |||
# KHR_draco_geometry_compression |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This extension is proposed by Google, so should it use GOOGLE
(or EXT
) prefix instead of KHR
?
Also, since it extends mesh
object, maybe it should be called _mesh_draco_compression
to be aligned with other extension names?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We talked to Patrick and Neil and the plan was to preferably propose it as KHR extension and integrate it to glTF 2.0 if we can work something out before the 2.0 release soon. But we can start with GOOGLE and see how it goes it that's better?
The draco library could also compress point cloud, in that case the primitive will just not have indice accessor. Calling it geometry will be better since it includes mesh and point cloud.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In glTF, point clouds are represented via mesh
object by using different mesh.primitive.mode
value. So they're meshes in glTF terms. I think that extension's name should reflect its formal relations with core spec.
CC @pjcozzi
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed the name back to mesh. And removed geometryType
in extension. Added description for restriction in README.
"KHR_draco_geometry_compression" : { | ||
"buffer" : 10, | ||
"byteOffset" : 1024, | ||
"byteLength" : 10000, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use bufferView
object to address Draco stream instead of "re-inventing" the same concept (buffer
, byteOffset
, and byteLength
) here? Such bufferView
would have byteStride
and target
properties undefined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We thought about that. The reason for not using bufferView
is that we want to reduced the number of newly introduced layers. But I think it makes sense to use bufferView
here. Changed here and the json.
5126, | ||
5126 | ||
] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
attributesComponentTypes
and attributesType
must be switched to match core spec definitions.
attributesOrder
, attributesComponentTypes
, and attributesType
arrays must be of the same size and must list elements in the same order, right? So why not combine these properties into one structure:
{
"attributes" : [
{
"semantic": "POSITION",
"type": "VEC3",
"componentType": 5126
},
{
"semantic": "NORMAL",
"type": "VEC3",
"componentType": 5126
},
{
"semantic": "TEXCOORD_0",
"type": "VEC2",
"componentType": 5126
},
]
}
type: "VEC3"
and componentType: 5126
could be made default, since we could expect them to be the most frequent values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea! Changed.
"geometryType" : { | ||
"type" : "string", | ||
"description" : "The type of the geometry, mesh or point cloud.", | ||
"enum" : ["mesh", "pointcloud"], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
String-based enums should use uppercase.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Also, |
"properties" : { | ||
"semantic" : { | ||
"type" : "string", | ||
"description" : "Mesh attribute semantic. e.g. POSITION, NORMAL and TEXCOORD.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably should add that all restrictions on semantics names from the core spec still apply (i.e., TEXCOORD_%d
instead of just TEXCOORD
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
"type" : "integer", | ||
"description" : "The datatype of components in the attribute.", | ||
"enum" : [5120, 5121, 5122, 5123, 5125, 5126], | ||
"gltf_enumNames" : ["BYTE", "UNSIGNED_BYTE", "SHORT", "UNSIGNED_SHORT", "UNSIGNED_INT", "FLOAT"], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UNSIGNED_INT
value isn't allowed for attributes, so it shouldn't be here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Also removed unrelated description.
@@ -63,60 +64,53 @@ The extension then could be used like: | |||
"mode" : 4 | |||
"extensions" : { | |||
"KHR_draco_geometry_compression" : { | |||
"bufferView" : 5, | |||
"buffer" : 10, | |||
"byteOffset" : 1024, | |||
"byteLength" : 10000, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like buffer
, byteOffset
, and byteLength
should be removed from example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
For the extension name prefix, let's just start with Beyond that the complete extension name is up to the Draco team. |
**Figure 1**: Structure of geometry compression extension. | ||
![](figures/structure.png) | ||
|
||
In general, we will use the extension to point to the buffer that contains the compressed data. The major change is that `accessor` in extended `primitive` no |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggested tweak: "accessor
in an extended primitive
no longer points to a bufferView
"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
![](figures/structure.png) | ||
|
||
In general, we will use the extension to point to the buffer that contains the compressed data. The major change is that `accessor` in extended `primitive` no | ||
longer point to a `bufferView`. Instead, the `attributes` of a primitive will use the decompressed data. This is valid because in glTF 2.0, `bufferView` is not required in `accessor`, although if it is not present or the id is 0, it will be used with `sparse` field to act as a sparse accessor. In this extension, we will ignore the `bufferView` property. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this was discussed offline, but to confirm - does this create a scenario for bottom-up loaders where they might fill an accessor
with zeros thinking it is for a sparse accessor, but then later it is used for compressed data? Probably not, right? Because a loader will probably only access buffer
and bufferView
bottom, then access primitive
before touching an accessor
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. For bottom-up loading, even if the accessor is filled with zeros, it will be accessed through primitive
--> attributes
where the primitive
is extended, so the decompressed data will be used instead.
|
||
## Overview | ||
|
||
This extension defines a schema to use [Draco geometry compression](https://github.com/google/draco) libraries in glTF format. This allows glTF to support streaming compressed geometry data instead of the raw data. This extension specification is based on [Draco bitestream version 2.0](https://google.github.io/draco/spec/). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Draco bitestream version 2.2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
|
||
## glTF Schema Updates | ||
|
||
Draco geometry compression library could be used for `primitive` by adding an `extension` property to a primitive, and defining its `KHR_draco_mesh_compression` property. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If a primitive
contains an extension
property and the extension
property defines it's KHR_draco_mesh_compression
property, then the Draco geometry compression must be used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless the loader doesn't support Draco and use the fallback uncompressed data?
**Figure 1**: Structure of geometry compression extension. | ||
![](figures/structure.png) | ||
|
||
In general, we will use the extension to point to the buffer that contains the compressed data. One of the requirements is that a loader/engine should be able to load the glTF assets no matter it supports the extension or not. To achieve that, all the existing components of the glTF specification stays the same when the extension exists, so that a loader doesn't support decoding compressed assets could just ignore the extension. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Draco extension points to the buffer that contains the compressed data.
If the uncompressed version of the asset is not provided, then KHR_draco_mesh_compression
must be added to extensionsRequired
.
"extensionsRequired" : [
"KHR_draco_mesh_compression"
]
If KHR_draco_mesh_compression
is set in the extensionsRequired
property then the primitive
should only contain the Draco compressed data.
If a Draco compressed version of the asset is provided then KHR_draco_mesh_compression
must be added to extensionsUsed
.
"extensionsUsed" : [
"KHR_draco_mesh_compression"
]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
|
||
``` | ||
|
||
The extension then could be used like the following, note that all other nodes stay the same |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Below is an example of what a part of a glTF file may look like if the Draco extension is set. Note that all other nodes stay the same except primitives
:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
} | ||
|
||
``` | ||
We will explain each of the property in the following sections. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
properties
Should we just remove this line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed.
* If `KHR_draco_mesh_compression` is in `extensionsRequired` then the loader must support the extension or it will fail loading the assets. | ||
* If `KHR_draco_mesh_compression` is in `extensionsUsed` but not `extensionsRequired`: | ||
* Check if the loader supports the extension. If not, then load the glTF asset ignoring the compression extension in `primitive`. | ||
* If the loader supports the extension, then process the extension and ignore the attributes and indices of the primitive that are contained in the extension. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* If the loader does support the Draco extension, but will not process `KHR_draco_mesh_compression`, then the loader must load the glTF asset ignoring `KHR_draco_mesh_compression` in `primitive`.
* If the loader does support the Draco extension, and will process `KHR_draco_mesh_compression`, then:
* The loader must process `KHR_draco_mesh_compression` first. The loader must get the data from `KHR_draco_mesh_compression`'s `bufferView` property and decompress the data using a Draco decoder to a Draco geometry.
* Then the loader must process `attributes` and `indices` properties of the `primitive`. When loading each `accessor`, go to the previously Draco decoded geometry in the `primitive` to get indices and attributes data. A loader must use the decompressed data to overwrite `accessors` or render the decompressed geometry directly (e.g. ThreeJS).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
* When encountering a `primitive` with the extension the first time, you must process the extension first. Get the data from the pointed `bufferView` in the extension and decompress the data to a geometry of a specific format, e.g. Draco geometry. | ||
* Then, process `attributes` and `indices` properties of the `primitive`. When loading each `accessor`, go to the previously decoded geometry in the `primitive` to get indices and attributes data. A loader could use the decompressed data to overwrite `accessors` or render the decompressed geometry directly (e.g. ThreeJS). | ||
|
||
It is pretty straightforward for top-down loading of a glTF asset, e.g. only |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this section
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
definitely open to change this if there actually are some use cases that require | ||
loading `accessor` independently. | ||
|
||
The extension provides compressed alternatives to one or more of a primitive's uncompressed attributes. A loader may choose to use the uncompressed attributes instead — when the extension is not supported, or for other reasons. When using compressed Draco data, the corresponding uncompressed attributes defined by the primitive should be ignored. If additional uncompressed attributes are defined by the primitive, but are not provided by the Draco extension, the loader must proceed to use these additional attributes as usual. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can remove this section as well and replace it with this (which will be part of the list above.
* If additional attributes are defined in `primitive`'s `attributes`, but not defined in `KHR_draco_mesh_compression `'s `attributes`, then the loader must process the additional attributes as usual.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
|
||
* Implementation note: To prevent transmission of redundant data, exporters should generally write compressed Draco data into a separate buffer from the uncompressed fallback, and shared data into a third buffer. Loaders may then optimize to request only the necessary buffers. | ||
|
||
If the uncompressed version of asset is not provided for the fallback described above, then the extension must be added to `extensionsRequired` so that the loaders/engines don't support the extension could report failure. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can remove this line with the re-worked paragraph above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
@@ -0,0 +1,138 @@ | |||
# KHR_draco_mesh_compression | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All Khronos specifications should contain the standard Copyright Header:
https://www.khronos.org/members/login/groups/Agreements%20and%20Licenses/Open%20Source%20Repository%20Resources/Khronos%20Specification%20Copyright%20License%20Header%20V3%20May17.txt
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As the Draco extension is referring to significant external documents – namely the Bitstream specification we should also include, and be consistent with the additional header wording for normative clarity:
https://www.khronos.org/members/login/groups/Agreements%20and%20Licenses/Open%20Source%20Repository%20Resources/Khronos%20Copyright%20License%20Header%20Addition%20for%20NORMATIVE%20CLARITY%20Oct17.txt Note - this has some boiler plate that needs to be tweaked to fit the spec.
The key text in the header is:
Some parts of this Specification are purely informative and do not define requirements
necessary for compliance and so are outside the Scope of this Specification. These
parts of the Specification are marked by the "Note
" icon or designated "Informative
"
<replace/insert specific conventions for the specification here>.
And
Where this Specification includes normative references to external documents, only the
specifically identified sections and functionality of those external documents are in
Scope. Requirements defined by external documents not created by Khronos may contain
contributions from non-members of Khronos not covered by the Khronos Intellectual
Property Rights Policy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
* If the loader does support the Draco extension, but will not process `KHR_draco_mesh_compression`, then the loader must load the glTF asset ignoring `KHR_draco_mesh_compression` in `primitive`. | ||
* If the loader does support the Draco extension, and will process `KHR_draco_mesh_compression` then: | ||
* The loader must process `KHR_draco_mesh_compression` first. The loader must get the data from `KHR_draco_mesh_compression`'s `bufferView` property and decompress the data using a Draco decoder to a Draco geometry. | ||
* Then the loader must process `attributes` and `indices` properties` of the `primitive`. When loading each `accessor`, you must ignore the `bufferView` and go to the previously decoded Draco geometry in the `primitive` to get the data of indices and attributes. A loader must use the decompressed data to fill the `accessors` or render the decompressed Draco geometry directly (e.g. [ThreeJS (non-normative)](https://github.com/mrdoob/three.js/blob/dev/examples/js/loaders/draco/DRACOLoader.js)). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"indices
propertiesof the
primitive" -> "
indicesproperties of the
primitive`"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
|
||
The [conformance](#conformance) section specifies what an implementation must do when encountering this extension, and how the extension interacts with the attributes defined in the base specification. | ||
|
||
## glTF Schema Updates | ||
|
||
Draco geometry compression library could be used for `primitive` by adding an `extension` property to a primitive, and defining its `KHR_draco_mesh_compression` property. | ||
Draco geometry compression library could be used for `primitive` by adding an `extension` property to a primitive, and defining its `KHR_draco_mesh_compression` property. If a `primitive` contains an `extension` property and the `extension` property defines its `KHR_draco_mesh_compression` property, then the Draco geometry compression must be used. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this "Draco geometry compression library could be used for primitive
by adding an extension
property to a primitive, and defining its KHR_draco_mesh_compression
property."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Having just recently added morph targets to my glTF generation, I'm suddenly noticing that they're absent from Draco considerations. It's likely out of scope and in any case too late to add this to the initial release of the extension, but may I join @donmccurdy in advocating for its usefulness? Sparse accessors salvage morph targets from extreme bloat, but they're still likely to take up a disproportionate amount of space when all the other static geometry has been compressed. As a corollary, I know the Draco team is at least thinking about the temporaral dimensions viz. animations, and so it's worth noticing here that the 'weights' target for animations is likely very compressible. |
This is probably too close to final, as you said, but even if it weren't — if we added support for morph targets with the Draco format as it is now, I assume each target would need to be treated as a separate mesh by the decoder. This would limit the performance benefit, compared to using a temporal dimension per the long-term roadmap. So let's give feedback on the use cases in the Draco repository (say, google/draco#126) and consider bringing Draco-compressed morph targets to glTF as an official In the meantime it would certainly be possible to use vendor extensions for short-term needs. |
Hello, For our use-case (physics-based simulations) we are very interested in animations, hence I created a proof-of-concept implementation of adding morph targets to this extension. It is actually pretty simple, the attributes within Please see the simple additions I propose to add support for morph targets in the extension text and json schema in 13c1d79. To demonstrate the usefulness of this feature, you can have a look at :
(you can use Note that one of the reason why supporting this feature can be simple is by refactoring |
Great comments, thanks @JeremieA! I'll leave aside the question of whether it is too late in the process — I suspect that is so, but would defer to others. Noticing that you've used a These changes for implementation within The restriction on accessor IDs looks reasonable to me... is it considered OK for an extension to narrow the core spec when in use? I'm inclined to say this should be an implementation note, which we could still add after the extension is finished... This also buys me some time to work on implementation: I've been meaning to try to get DRACOLoader running in a Web Worker, with multiple workers in parallel for multiple meshes, which might get around the question of preprocessing. |
/cc @lexaknyazev for the question of accessor ID requirements. |
I think this is ready to merge. Any other feedback? |
Looks good to me. 👍 |
About unique IDs on accessors — the pending three.js implementation does not require unique IDs, but I am fine with including the restriction in some form. Morph targets will be left for a future version of the extension (or new, separate extension, TBD) covering animation, and should be omitted from the proposed text. A question about implications — suppose my model has three (3) compressed meshes, and each mesh is identical except for an application-specific attribute (say, a. Duplicate all compressed data for each mesh, in a new Draco-compressed bufferView. If I understand correctly, (a) and (b) are fine, but (c) would be disallowed with this requirement. |
@donmccurdy Yes a. and b. are fine, c. is disallowed. You can file a feature request for a future version if you think this use case is useful. |
I don't have any need for (c), just wanted to verify that restricting accessor IDs to be unique-per-mesh would have the implications expected. 👍 |
Is this ready to merge? |
Congrats on the official launch of this extension ! I will update my tools to reflect the final standard (disabling the morph targets support by default). Regarding the attributes from multiple mesh instances, option (b) is what I implemented in my compressor (see here). All primitives sharing the same indices and positions data are compressed as a single Draco-compressed mesh, as I assumed that it would be most efficient. While testing this compression on various glTF assets, I did not notice this case of two primitives using different attributes while referring to the same positions and indices. I suspect most exporters would duplicate all attributes in this case. But I know of at least Sorry @donmccurdy for not answering your earlier questions. The ZIP export that I implemented was done as a completely separate code from |
Hi,
We made an extension for using Google Draco geometry compression library with glTF. It could also be easily extended to more compression libraries if it suits well to glTF 2.0.
Let us know if there is any flaws in the design. Thank you very much.
We also provide an alternative approach in the appendix in case anyone is interested in discussing it.
@ondys
@FrankGalligan