Three.js: GLTFExporter

Created on 15 Aug 2017  ·  85Comments  ·  Source: mrdoob/three.js

I would like to use this issue to keep track of the GLTF2 exporter features. I've copied the initial list of features discussed on the PR https://github.com/mrdoob/three.js/pull/11917 and I'll keep updating the list as we progress in the implementation.

Features / TO-DO

  • [x] Export options

    • [x] trs to export TRS instead of matrix

    • [x] input:

    • [x] Single scene

    • [x] Array of scenes

    • [x] Single object

    • [x] Array of objects

    • [x] truncateDrawRange: force exporting just the attribute values defined by drawRange:

    • [x] Non-index buffer geometry

    • [x] Indexed buffer geometry

  • [x] Include userData in extras?
  • [x] Scenes

    • [x] Support for multiple scenes

  • [x] Nodes

    • [x] Meshes

    • [x] Primitive mode:



      • [x] TRIANGLES


      • [x] TRIANGLE_STRIP


      • [x] TRIANGLE_SPAN


      • [x] POINTS


      • [x] LINES


      • [x] LINE_STRIP


      • [x] LINE_LOOP



    • [x] Geometry types:



      • [x] BufferGeometry


      • [x] Geometry



    • [x] Primitive attributes:



      • [x] POSITION


      • [x] NORMAL


      • [x] TEXCOORD_0


      • [x] TEXCOORD_1


      • [x] COLOR_0


      • [x] JOINTS_0


      • [x] WEIGHTS_0


      • [x] TANGENT



    • [x] Multi-material meshes as primitives

    • [x] Lights

    • [x] Camera

    • [x] Skin

  • [ ] Materials:

    • [x] Ignore if default material is being used

    • [x] Export as lines if material.wireframe === true

    • [x] pbrMetallicRoughness for MeshStandardMaterial

    • [x] Attributes:



      • [x] baseColorFactor


      • [x] metallicFactor


      • [x] roughnessFactor


      • [x] baseColorTexture: It's supported (material.map) but the texCoord is always set to 0.



    • [x] doubleSided

    • [x] KHR_material_unlit

  • [x] Samplers
  • [ ] Images

    • [x] uri using map.image.src

    • [x] uri base64

    • [x] bufferView

    • [x] Deal with flipY images

    • [ ] Merge channels into one texture

  • [ ] Accessors

    • [ ] Use the same bufferView for the same componentType instead of creating a new one for each attribute (WIP @takahirox)
    • [x] Support sparse?
    • [ ] Attributes:
    • [x] bufferView
    • [ ] byteOffset: Currently it's using 0 always as I'm creating a new bufferView for each accessor.
    • [x] componentType
    • [x] count
    • [x] max
    • [x] min
    • [x] type:

      • [x] SCALAR

      • [x] VEC2

      • [x] VEC3

      • [x] VEC4

  • [ ] BufferViews: Currently I'm creating a new bufferView for each Accessor, this should be fixed to use just one for these attributes that share the same componentType

    • [x] Attributes:
    • [x] buffer
    • [x] byteOffset
    • [x] byteLength
    • [x] byteStride
    • [x] target
  • [x] Buffers: Currently I'm saving everything to a single buffer so it will be just one entry in the buffers array.

    • [x] byteLength

    • [x] uri

  • [x] Animations
  • [ ] misc:

    • [ ] Validate output (https://github.com/KhronosGroup/glTF-Validator)

    • [ ] Include stats option to log the number of items exported and maybe some timing?

  • [x] GLB

Example

Current demo:
image

Exported gltf loaded on @donmccurdy 's gltf viewer
image

GLTF: https://gist.github.com/fernandojsg/0e86638d81839708bcbb78ab67142640

Enhancement

Most helpful comment

Hope that Multi-material meshes will be implemented asap because most of 3D models are using Multi-materials.

As I said in the other thread, I'm working on it.

All 85 comments

This is looking really good!

By the way, we are planning to remove THREE.GLTFLoader and rename GLTF2LoaderGLTFLoader sometime soon*. Might be a good idea to rename the exporter as GLTFExporter before r87 lands, to avoid any confusion and so there won't be a name change required between releases. Oops, I missed that you already named it that way.. carry on! 😆


* @mrdoob, any preference on when that should happen? IMO we could do this now, unless we want to keep GLTFLoader in r87 with just a deprecation warning, and remove it in r88?

I think the sooner the better. As long as the new GLTFLoader is able to detect 1.0 and warn the user that we only support 2.0+.

IIRC we can detect by seeing asset as I mentioned before.

IIRC we can detect by seeing asset as I mentioned before.

✅ Yep! https://github.com/mrdoob/three.js/pull/11864

Cool! But I've found a minor bug. I'm making PR now. Let's merge before we rename.

Can we specify the items that someone is working on in the checklist?

@takahirox sure! people could just write comments here and I could update the list and point to a PR if there's something going on already

The next thing I'll be working is on the textures, to convert them to base64 instead of using just the url

Thanks! I wanna help making glTF exporter. I'm looking into what I can help in the checklist...

BTW have you purposely let two variables WEBGL_CONSTANTS and THREE_TO_WEBGL global?

@takahirox cool!
Regarding the two variables, this is something I'm going to address in the following PR to make it part of the WebGLUtils and just import it. It doesn't make sense that each one that needs these constants need to redefine them again every time.

@takahirox btw feel free to propose new items to the list of course! ;)

@fernandojsg Sure! About the variables, I wanted to propose to move them to somewhere if they're purposely declared as global so it's nice to know that you do.

I wanna work on shared buffer view.

BufferViews: Currently I'm creating a new bufferView for each Accessor, this should be fixed to use just one for these attributes that share the same componentType

The reason why one for the attributes that share the same componentType, not one for all the attributes, is for data alignment, correct?

https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#data-alignment

Cool, I've just added you to the list 👍 Yep, basically you want to share the same buffer view for component with the same type, for example if you have position and normal you'll have two VEC3 accessors but they'll point to the same bufferview. That could be a great starting point ;)

I meant, the reason why we don't let buffer view be shared among different componentType (ex: float and short) is to keep good data alignment, correct?

I believe you can store in the same buffer view different component types as long as they've the same target, for example normal (Vec3), position (Vec3) and uv (Vec2) could be in the same buffer view but indices not. @donmccurdy could you confirm it?

Yep, agreed. And as this glTF specification mentions

https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#data-alignment

The offset of an accessor into a bufferView (i.e., accessor.byteOffset) and the offset of an accessor into a buffer (i.e., accessor.byteOffset + bufferView.byteOffset) must be a multiple of the size of the accessor's component type.

It's a good idea that we separate buffer views between the different componentType(=data type like float and short, not vec2 or vec3) for the simplicity. If we separate them between different data length componentType, it'd be more optimized.

BTW is there any special reasons why the current exporter supports only accessor.componentType float, uint, and ushort? glTF 2.0 can handle char, uchar, and short, in addition to them.

https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#accessorcomponenttype-white_check_mark

@takahirox not really, I just defined these by now because are the ones used for the type of attributes we support right now (positions, normals, colors, uvs, indices..).
The next step I'm working on is the textures so there we'll need others like uchar for example

OK, so I first will work on the accessor.componentTypes unless you already started to impl.

Almost ready but my PR should conflict with #11978.
So I send mine once #11978 is merged and I fix the conflict.

Would you add animation to the list?

@takahirox sure, it could be great to add animation. I just didn't add it because I wasn't familiar enough with the current state of the animation features on three.js, but if you feel like taking over it, it would be great ;)

Do you plan to support BufferGeometry groups?
Do the GLTF specs cover that or would it result in creating a new Mesh for every group?
This must also take care of, the material property of a Mesh being an array of materials.

@marcatec The glTF spec does have a "mesh" vs. "primitive" distinction that would allow you to create BufferGeometry groups that could each reference a different material. Currently THREE.GLTFLoader does not optimize loading primitives — it creates separate meshes — but that could be implemented.

Great work, great list and good to know there is already so much support on the format! Also works very well together with gltf blender exporter too. Can't wait for lights support! Keep up the great work.

I concur, great work!

Are there plans to add support for other materials apart from StandardMaterial?

Thanks!

@homerjam any material properties shared with MeshStandardMaterial will be preserved — so for example, a MeshPhongMaterial using map and normalMap would export with those textures intact, but when you import it back to three.js it will be a MeshStandardMaterial. The exporter currently does a naïve conversion to PBR for this.

Round-trip support (export Phong from GLTFExporter, load Phong from GLTFLoader) will require in-progress work on the glTF format: https://github.com/KhronosGroup/glTF/pull/1150

baseColorTexture: It's supported (material.map) but the texCoord is always set to 0

@fernandojsg could you clarify what is missing here? Since .map is always the first UV set in three.js, this sounds like the correct way to represent that in glTF?

Also a heads up, I crossed out three items on the list. Reasoning below:

  • tangents

    • three.js only computes them on the GPU; adding an implementation just for the exporter sounds not ideal.

  • sparse accessors
  • check if the material matches glTF default and omit it

    • feels like an edge case / clutter, feel free to implement if someone feels strongly though

By exporting to GLB from the editor I've noticed that alphaMap, roughnessMap and metalnessMap don't get exported.

In #13397 I said normalMap neither gets exported but seems like I was wrong.

By exporting to GLB from the editor I've noticed that alphaMap, roughnessMap and metalnessMap don't get exported.

I'll work on this today unless anyone already started.

@donmccurdy

sparse accessors
I think this is best left to post-export optimization, like mattdesl's script.

Feeling like letting exporter support sparse accessors for morph. I'll try later.

@takahirox cool! go ahead!

I don't think alphaMap is supported in glTF 2.0.

https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#material

Yeah, I feared that.... What about metalnessMap and roughnessMap?

I'm working on them now!

13415

Regarding image formats. glTF 2.0 supports only .png and .jpg as external image files. I'm considering how to handle non supported image format files (eg: .bmp) on non embedImages mode.

  1. Convert to .png or .jpg and embed
  2. Don't care. Export as original image files
  3. Don't export

I prefer 1. Any thoughts?

Wow, really appreciating the works of you guys.

Hope that Multi-material meshes will be implemented asap because most of 3D models are using Multi-materials.

  1. Convert to .png or .jpg and embed
  2. Don't care. Export as original image files
  3. Don't export

I vote for 3 and logging a warning in the console.

Hope that Multi-material meshes will be implemented asap because most of 3D models are using Multi-materials.

Agreed, for me this is the number one issue preventing use of the exporter.

Hope that Multi-material meshes will be implemented asap because most of 3D models are using Multi-materials.

As I said in the other thread, I'm working on it.

  1. Convert to .png or .jpg and embed
  2. Don't care. Export as original image files
  3. Don't export

I vote for 3 and logging a warning in the console

Yeah, I came to think 3. would be simpler and not confusing to users. Getting embedded image on non emedImages mode would be a bit confusing.

The reason why I prefer 1. was for converting from other formats to glTF. Some (or many) of other formats don't have image format limitation.

The exporter converts on embedImages mode. So adding "use embedImages option if you wanna convert" to console warning would be good, I'm thinking.

I'd go also for 3 too. As converting from other formats could be tedious, and you'll need anyway to prioritize some formats against others. Probably it's worth to do 3 right now and wait to see if gltf add support to new texture formats like ktx or so and we could revisit the implementation.

As discussed in https://github.com/mrdoob/three.js/pull/13415#issuecomment-369022383, it would be nice if the exporter was able to compose the ambientRoughnessMetalness texture for the user. Probably better to place that code in ImageUtils though.

I've updated the checklist with the latest changes. I've added @takahirox to the multimaterial item, and I'll take myself the images compose task.
I've added also material_unlit extension, although is still in draft I believe it's quite close to release and won't change that much (/cc @donmccurdy)

Hope that Multi-material meshes will be implemented asap because most of 3D models are using Multi-materials.

As I said in the other thread, I'm working on it.

WIP... (Miku has multi-material)

image

About non-supported image formats, ok let's go for 3.

@takahirox looking good! 👍

BTW, are you folks interested in zip archive support? .glTF + external .bin and textures would be fitting to other authoring tools (maybe), but hard to do with non-archive. So zip archive would be necessary. And we can reduce the exported file size.

I want it and tried in my local branch before, I can share later if you're interested.

Isn't that pretty much the same as gzipping a glb?

.glTF + external .bin and textures would be fitting to other authoring tools (maybe)

I hope authoring tools won't require separate files; we're encouraging everyone to use GLB by default. But it's easier to hand-edit an image if it isn't embedded, for sure.

No strong opinion about whether we want to put that functionality into THREE.GLTFExporter directly... but I almost think that we should not have too many options that could instead be post-optimizations on the glTF. Another example, Draco is sort of complicated and requires several external files, so maybe it's better to let specialized glTF-to-glTF tools do that optimization? And similarly we could make a glb-unpacker (opposite of http://glb-packer.glitch.me/) to help people unpack files from GLB to ZIP if we find out that people need it.

From https://github.com/KhronosGroup/glTF/issues/1256

... the original intent of gltf-pipeline - and really glTF in general - make exporters as simple as possible and push the optimizations to a common tool. It will also, of course, help with fragmentation.

that said, no glb-unpacker exists yet that I know of...

@mrdoob

I wanted texture images to be external, rather than .glTF vs .glb.

@donmccurdy

I followed up https://github.com/KhronosGroup/glTF/issues/1117 discussion and agree with encouraging .glb + embedded files and pipeline approach now. One .glb is good for data transmission especially for web and pipeline approach can keep exporters and tools simple and reusable. (I like UNIX/Linux command pipeline approach, too!)

So I don't think the exporter needs zip archive support now. And maybe it also doesn't need sparse accessor and draco support because of the same reason.

Regarding glb-unpacker, I may be gonna make it in my free time. I think some artists like .glTF + external files because they're readable without any glTF specific tools. And sometimes external files can reduce loading time due to parallel loading file so it could be used for optimization purpose.

Regarding pipeline/optimization tools, I wanna note that we don't wanna transfer huge data over network. Users wanna optimize/compress before ~transforming~ transferring data. So glTF optimization web service sometimes doesn't work well for huge data because user needs to send huge file to the server.

Plus, for Three.js and other JavaScript browser based engines, we'd be happy if we have glTF optimization tools which run on browser. We can optimize/compress before data is passed to users. Without them users need to manually download exported data and then pass it to pipeline tools because of browser limitations.

From this point of view, I want a tool to be able to run anywhere, on browser, on server, CUI, and so on, to have it more common and reusable. We don't wanna make the same purpose tools twice or more for different platforms. So node.js based tool would be good? Does glTF (pipeline) team have any suggestions? (Maybe this discussion should be done in glTF, not here.)

Just in case, in GLTFLoader binary support is implemented as extension but .glb is in core spec of glTF 2.0, correct?

Just in case, in GLTFLoader binary support is implemented as extension but .glb is in core spec of glTF 2.0, correct?

Yeah, it was an extension in glTF 1.0 and I just never relocated or renamed that code after it became part of core glTF 2.0 spec.

From this point of view, I want a [optimization tools] to be able to run anywhere, on browser, on server, CUI, and so on, to have it more common and reusable. So node.js based tool would be good? Does glTF (pipeline) team have any suggestions? (Maybe this discussion should be done in glTF, not here.)

It would be worth asking about the glTF-Pipeline roadmap... not sure how general they want glTF-Pipeline to be, or if it is mainly for Cesium uses, or if it's just an issue of limited developer time. Also glTF-Toolkit looks relevant, but (currently) only runs on Windows. I personally like Node.js but C++ or Rust could be reasonable choices with compilation to WASM.

Oh, I was missing the compilation to WASM. Specifying some recommended dev platforms would be nice to optimization devs. So I'd propose to an appropriate thread.

I agree with @donmccurdy as I feel these optimizations on the pipeline could live in a different repo than three.js, so everyone could benefit from them. I still need to check the differences between the gltf pipeline and toolkit tools, but I would expect this kind of features to be included on them.
I also agree that as long as we'll have a WASM it wouldn't really matter the source language, but it's also true that if it's written in node.js probably many of the community around the 3d web engines could help improving them as right now are the main target for this file format anyway.

I'm not sure I understand about "optimizing before transforming" ... there are several types of transformations a pipeline might do on a model, and optimizations are probably the most common type of transformation?

Agreed beyond that. Good to have low level, focused tools that can be used to build other tools or plugged into a more user-friendly GUI.

Oops, it's typo. Not transforming but transferring. I meant, most users wanna optimize/compress before sending data over network. I've updated the posts to have them clearer.

Hi guys

I'm using the THREE.js GLTF Exporter to export an entire aframe scene as a gltf object.
How can I get the a-animation tags defined on aframe to be a part of the animations in the gltf object ?

@donmccurdy @fernandojsg @mrdoob

Hi @siddhartpaiTHREE.GLTFExporter only converts THREE.AnimationClip objects to glTF animations, whereas A-Frame's animation system uses TweenJS. So currently this isn't possible. You may want to open an issue on A-Frame or the A-Frame Inspector, which also use GLTFExporter, to request it as a future feature.

Multi-material support #13536

I've just noticed that the validator throws an error on every normal element on a bufferview that is not normalized. eg If I stored uninitialized values like [0,0,0] it will throw that error.
As it's a error and not a warning/notice I see it's sensitive to fix. What do you think about ensure the normal bufferview elements are normalized? Even so, for values that can't be normalized such as [0,0,0], should we use a valid unit vector? /cc @donmccurdy

Seems like NORMAL should be normalized.

https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#meshes

NORMAL | "VEC3" | 5126 (FLOAT) | Normalized XYZ vertex normals

Agreed with ensuring because Three.js normal doesn't have such a limitation.

Yep, but what to do when you don't have an actual normal, like an unused value of [0,0,0], just create a valid one and that's all right? let's say [1,0,0]. So we should modify the bufferview code to detect that we're parsing a normal attribute and normalize each one before saving it to the dataview.

what to do when you don't have an actual normal, like an unused value of [0,0,0]

Hm.... replacing with a valid one and displaying warning?

So we should modify the bufferview code to detect that we're parsing a normal attribute and normalize each one before saving it to the dataview.

I prefer doing that in processMesh() because it'd be simpler, like

var originalNormal = geometry.attributes.normal;

if ( hasNonNormalizedValues( originalNormal ) ) {

    geometry.attributes.normal = createNormalizedAttribute( originalNormal );

}

processAccessorHere();

geometry.attributes.normal = originalNormal;

If we do that in processBufferView(), the code would become a bit complex because we need to care if data is shared among different attributes, for example position and normal. (I know it's very rare use case but Three.js doesn't restrict.)

Yep I like that approach, I was afraid to modify the normals after exporting, but it should be ok if we save a reference put them back again after finishing. :+1: Would you mind pushing a PR with these changes? or want me to do it?

OK, I will. (Are you in hurry to fix that?)

@takahirox cool, thanks! but no rush I was just reviewing the state of the exporter ^_^

OK, then I'll do ~tomorrow~ this week.

Right, glTF does not allow for omitting normals on particular vertices but not others in a single primitive. We'll need to provide some sort of value, strip these vertices, or throw an error.

I would prefer to make things easier for the user so my vote is for creating a new normals array normalizing them and adding a (0,1,0) value for the empty ones.

Seems good. If it's slow for large models we might want a checkNormals option or something like that, so users who don't need this can opt out, rather than scanning every vertex.

Yep I was just about to write the same! :D

If it's slow for large models we might want a checkNormals option or something like that, so users who don't need this can opt out, rather than scanning every vertex.

I'm gonna make PR without that option first. Let's add when/if necessary. Personally I suppose this check doesn't slow much.

I'm gonna make PR without that option first. Let's add when/if necessary. Personally I suppose this check doesn't slow much.

I was normalizing the whole buffers when loading each stroke on a-painter and it quite slow

Even if just checking if they're normalized?

@takahirox you will need to compute the length anyway so I guess it won't change that much

Hm, ok. I'll evaluate with the PR.

It is the first GLTFExporter feature we've introduced that does any computation with each vertex (except relative/absolute morph target conversion) so yeah potentially slower.. either way though.

Great work! IMHO should be merged into core three.js, rather than in "examples".
Would love to see KHR_lights_punctual support!

PR https://github.com/mrdoob/three.js/pull/15519 adds KHR_lights_punctual. :)

I think this issue can probably be closed — the remaining items are less critical convenience or optimization, and can be tracked elsewhere:

  • [ ] reuse bufferviews
  • [ ] automatically merge metal/rough/ao textures

Hey guys, does anyone know how can I export a custom shaped mesh by morph changes applying the morphs and removing it from the final object?
Like this question https://stackoverflow.com/questions/57423471/how-to-export-morph-changed-meshes-from-threejs-application
Thanks in advance!

@vini-guerrero Please use the forums (https://discourse.threejs.org/) or Stack Overflow for help, rather than GitHub issues.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

zsitro picture zsitro  ·  3Comments

boyravikumar picture boyravikumar  ·  3Comments

makc picture makc  ·  3Comments

akshaysrin picture akshaysrin  ·  3Comments

jack-jun picture jack-jun  ·  3Comments