-
-
Notifications
You must be signed in to change notification settings - Fork 36.1k
GLTFExporter: Prevent exporting empty geometry #13681
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
Conversation
| } | ||
|
|
||
| // Skip creating an accessor if the attribute doesn't have data to export | ||
| if ( count === 0) { |
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.
nit: missing space after 0
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.
Thanks!
|
|
||
| if ( primitives.length === 0 ) { | ||
|
|
||
| return -1; |
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 processNode we're returning null when no node is created. Would probably vote for null over -1 personally but either way let's make it consistent.
examples/misc_exporter_gltf.html
Outdated
| empty.name = 'Custom buffered empty (drawrange)'; | ||
| scene1.add( empty ); | ||
|
|
||
|
|
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.
What do you think about putting this case in GLTFExporter.tests.js instead? I think that might be a better fit than in the 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.
Yeah! I was thinking about it actually, but I didn't know we already had a GLTFExporter.tests.js 👼 I'll take a look and include it there thanks!
|
|
||
| if ( geometry.index !== null ) { | ||
| // Skip meshes without exportable attributes | ||
| if ( Object.keys(primitive.attributes).length > 0 ) { |
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.
You need spaces after and before primitive.attributes
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.
Thanks! done
| return null; | ||
|
|
||
| } | ||
|
|
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.
Adding
if ( Object.keys( attributes ).length === 0 ) {
return null;
}
after
for ( var attributeName in geometry.attributes ) {
....
}
above instead isn't good?
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.
Is there any use case where you could have just morph attributes but no other attribute?
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.
IMO, no. At least, glTF validator seems to raise MESH_PRIMITIVE_MORPH_TARGET_NO_BASE_ACCESSOR "No base accessor for this attribute semantic." error if corresponding attribute isn't in attributes.
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.
Yeah makes sense, added! thanks
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.
Nice. So I think you can remove this check here now.
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.
@mrdoob I believe he's referring to this #13681 (review) right? /cc @takahirox
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.
No. I think we can remove here
if ( primitives.length === 0 ) {
return null;
}
because we added above
if ( Object.keys( attributes ).length === 0 ) {
return null;
}
Or you care the case where groups is an empty array? (In other words, how can primitives.length be zero 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.
You could have buffer geometries without groups eg: the sphere in the example https://github.com/mrdoob/three.js/blob/dev/examples/misc_exporter_gltf.html#L248 or a custom buffergeometry: https://github.com/mrdoob/three.js/blob/dev/examples/misc_exporter_gltf.html#L312-L337 but I haven't worked with multimaterial before so I'm not sure how that case would be handled here.
Because if you have one of the previous objects with a multimaterial.
var groups = isMultiMaterial ? mesh.geometry.groups : [ { materialIndex: 0, start: undefined, count: undefined } ];
groups would be mesh.geometry.groups that would be empty.
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 confirmed rendering nothing for multi-material with empty groups.
So how about
if ( isMultiMaterial && mesh.geometry.groups.length === 0 ) return null;
it'd be more straightforward.
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.
|
@donmccurdy added unit test, although I can't get it running, are the examples test broken? I believe we should include three build on them or am I missing something? |
|
|
||
| if ( geometry.index !== null ) { | ||
| // Skip meshes without exportable attributes | ||
| if ( Object.keys( primitive.attributes ).length > 0 ) { |
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.
You can remove this check here now
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.
Nice catch! removed!
|
@fernandojsg They should be able to run in the browser if you use |
|
@donmccurdy thanks! I've just fixed it and test passed ^_^ |
|
Thanks! |
I've just found a bug when exporting empty buffer geometries. eg in a-painter I create a buffer geometry for each kind of material but maybe I don't use one of them so drawRange will be 0. If you try to export it it will generate the following glTF:
{ "asset": { "version": "2.0", "generator": "THREE.GLTFExporter" }, "scenes": [ { "nodes": [ 0 ], "name": "AuxScene" } ], "scene": 0, "nodes": [ { "name": "Custom buffered empty (drawrange)", "mesh": 0 } ], "bufferViews": [ { "buffer": 0, "byteOffset": 0, "byteLength": 0, "target": 34962, "byteStride": 12 }, { "buffer": 0, "byteOffset": 0, "byteLength": 0, "target": 34962, "byteStride": 12 } ], "buffers": [ { "byteLength": 0, "uri": "data:" } ], "accessors": [ { "bufferView": 0, "componentType": 5126, "count": 0, "max": [ null, null, null ], "min": [ null, null, null ], "type": "VEC3" }, { "bufferView": 1, "componentType": 5126, "count": 0, "max": [ null, null, null ], "min": [ null, null, null ], "type": "VEC3" } ], "materials": [ { "pbrMetallicRoughness": { "metallicFactor": 0, "roughnessFactor": 0.9 }, "extensions": { "KHR_materials_unlit": {} }, "doubleSided": true } ], "meshes": [ { "primitives": [ { "mode": 4, "attributes": { "POSITION": 0, "COLOR_0": 1 }, "material": 0 } ] } ], "extensionsUsed": [ "KHR_materials_unlit" ] }The accessors has an invalid range and the bufferviews are empty.
This PR prevents it to happens by skipping attributes that has no values, and primitives that has no attributes, and meshes that has no primitives.
I added a empty node and a button to test it. The expected output for the previous example will be: