Skip to content
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

GLTFExporter: export from compressed texture data #23321

Merged
merged 7 commits into from
May 26, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions examples/jsm/exporters/GLTFExporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ import {
RepeatWrapping,
Scene,
Source,
SRGBColorSpace,
CompressedTexture,
Vector3
} from 'three';
import { decompress } from './../utils/TextureUtils.js';


/**
Expand Down Expand Up @@ -827,6 +828,18 @@ class GLTFWriter {

console.warn( 'THREE.GLTFExporter: Merged metalnessMap and roughnessMap textures.' );

if ( metalnessMap instanceof CompressedTexture ) {

metalnessMap = decompress( metalnessMap );

}

if ( roughnessMap instanceof CompressedTexture ) {

roughnessMap = decompress( roughnessMap );

}

const metalness = metalnessMap ? metalnessMap.image : null;
const roughness = roughnessMap ? roughnessMap.image : null;

Expand Down Expand Up @@ -1146,7 +1159,7 @@ class GLTFWriter {

} else {

throw new Error( 'THREE.GLTFExporter: Unsupported bufferAttribute component type.' );
throw new Error( 'THREE.GLTFExporter: Unsupported bufferAttribute component type: ' + attribute.array.constructor.name );

}

Expand Down Expand Up @@ -1236,7 +1249,7 @@ class GLTFWriter {

if ( format !== RGBAFormat ) {

console.error( 'GLTFExporter: Only RGBAFormat is supported.' );
console.error( 'GLTFExporter: Only RGBAFormat is supported.', format );

}

Expand Down Expand Up @@ -1344,13 +1357,22 @@ class GLTFWriter {
*/
processTexture( map ) {

const writer = this;
const options = writer.options;
const cache = this.cache;
const json = this.json;

if ( cache.textures.has( map ) ) return cache.textures.get( map );

if ( ! json.textures ) json.textures = [];

// make non-readable textures (e.g. CompressedTexture) readable by blitting them into a new texture
if ( map instanceof CompressedTexture ) {

map = decompress( map, options.maxTextureSize );

}

let mimeType = map.userData.mimeType;

if ( mimeType === 'image/webp' ) mimeType = 'image/png';
Expand Down
91 changes: 91 additions & 0 deletions examples/jsm/utils/TextureUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import {
PlaneGeometry,
ShaderMaterial,
Uniform,
Mesh,
PerspectiveCamera,
Scene,
WebGLRenderer,
Texture,
SRGBColorSpace
} from 'three';

let _renderer;
let fullscreenQuadGeometry;
let fullscreenQuadMaterial;
let fullscreenQuad;

export function decompress( texture, maxTextureSize, renderer = null ) {

if ( ! fullscreenQuadGeometry ) fullscreenQuadGeometry = new PlaneGeometry( 2, 2, 1, 1 );
if ( ! fullscreenQuadMaterial ) fullscreenQuadMaterial = new ShaderMaterial( {
uniforms: { blitTexture: new Uniform( texture ) },
vertexShader: `
varying vec2 vUv;
void main(){
vUv = uv;
gl_Position = vec4(position.xy * 1.0,0.,.999999);
}`,
fragmentShader: `
uniform sampler2D blitTexture;
varying vec2 vUv;

// took from threejs 05fc79cd52b79e8c3e8dec1e7dca72c5c39983a4
vec4 conv_LinearTosRGB( in vec4 value ) {
return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );
}
hybridherbst marked this conversation as resolved.
Show resolved Hide resolved

void main(){
gl_FragColor = vec4(vUv.xy, 0, 1);

#ifdef IS_SRGB
gl_FragColor = conv_LinearTosRGB( texture2D( blitTexture, vUv) );
#else
gl_FragColor = texture2D( blitTexture, vUv);
#endif
}`
} );

fullscreenQuadMaterial.uniforms.blitTexture.value = texture;
fullscreenQuadMaterial.defines.IS_SRGB = texture.colorSpace == SRGBColorSpace;
fullscreenQuadMaterial.needsUpdate = true;

if ( ! fullscreenQuad ) {

fullscreenQuad = new Mesh( fullscreenQuadGeometry, fullscreenQuadMaterial );
fullscreenQuad.frustrumCulled = false;

}

const _camera = new PerspectiveCamera();
const _scene = new Scene();
_scene.add( fullscreenQuad );

if ( ! renderer ) {

renderer = _renderer = new WebGLRenderer( { antialias: false } );

}

renderer.setSize( Math.min( texture.image.width, maxTextureSize ), Math.min( texture.image.height, maxTextureSize ) );
renderer.clear();
renderer.render( _scene, _camera );

const readableTexture = new Texture( renderer.domElement );

readableTexture.minFilter = texture.minFilter;
readableTexture.magFilter = texture.magFilter;
readableTexture.wrapS = texture.wrapS;
readableTexture.wrapT = texture.wrapT;
readableTexture.name = texture.name;

if ( _renderer ) {

_renderer.dispose();
_renderer = null;

}

return readableTexture;

}
45 changes: 43 additions & 2 deletions examples/misc_exporter_gltf.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@

import { GLTFExporter } from 'three/addons/exporters/GLTFExporter.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

function exportGLTF( input ) {
Expand Down Expand Up @@ -99,7 +101,7 @@
let container;

let camera, object, object2, material, geometry, scene1, scene2, renderer;
let gridHelper, sphere, model;
let gridHelper, sphere, model, coffeemat;

const params = {
trs: false,
Expand All @@ -111,7 +113,8 @@
exportSphere: exportSphere,
exportModel: exportModel,
exportObjects: exportObjects,
exportSceneObject: exportSceneObject
exportSceneObject: exportSceneObject,
exportCompressedObject: exportCompressedObject,
};

init();
Expand Down Expand Up @@ -451,6 +454,37 @@

window.addEventListener( 'resize', onWindowResize );

// ---------------------------------------------------------------------
// Exporting compressed textures and meshes (KTX2 / Draco) (TODO: Meshopt)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Draco compression won't leave any trace in the resulting three.js objects, and Meshopt requires only KHR_mesh_quantization (which is tested for export above). So it might just be KTX2 that is a concern here, perhaps we could use the existing coffeemat.glb?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe when I originally started the PR Draco compression did indeed not work for roundtrips since it could still end up as int8 or so, which wasn't supported in the exporter. May not be the case anymore!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tried using the existing one, but that actually doesn't export cleanly right now, with an error I don't understand - the readback canvas seems to have the right size but comes out as 0x0:

20230524-023523_chrome

I debugged a bit and am not really sure what's going on there...

// ---------------------------------------------------------------------
const ktx2Loader = new KTX2Loader()
.setTranscoderPath( 'jsm/libs/basis/' )
.detectSupport( renderer );

const dracoLoader = new DRACOLoader()
.setDecoderPath( 'jsm/libs/draco/' );

const gltfLoader = new GLTFLoader().setPath( 'models/gltf/' );
gltfLoader.setKTX2Loader( ktx2Loader );
gltfLoader.setDRACOLoader( dracoLoader );
gltfLoader.load( 'coffeemat.etc1s+draco.glb', function ( gltf ) {

// coffeemat.etc1s+draco.glb was produced from the source scene using gltf-transform:
// gltf-transform etc1s coffeemat/scene.gltf coffeemat.etc1s.glb
// gltf-transform draco coffeemat.etc1s.glb coffeemat.etc1s+draco.glb
// The resulting model uses KHR_texture_basisu (for texture compression using ETC1S) and DRACO mesh compression.

gltf.scene.position.x = 400;
gltf.scene.position.z = - 200;

scene1.add( gltf.scene );

coffeemat = gltf.scene;

} );

//

const gui = new GUI();

let h = gui.addFolder( 'Settings' );
Expand All @@ -466,6 +500,7 @@
h.add( params, 'exportModel' ).name( 'Export Model' );
h.add( params, 'exportObjects' ).name( 'Export Sphere With Grid' );
h.add( params, 'exportSceneObject' ).name( 'Export Scene 1 and Object' );
h.add( params, 'exportCompressedObject' ).name( 'Export Coffeemat (compressed)' );
hybridherbst marked this conversation as resolved.
Show resolved Hide resolved

gui.open();

Expand Down Expand Up @@ -507,6 +542,12 @@

}

function exportCompressedObject() {

exportGLTF( [ coffeemat ] );

}

function onWindowResize() {

camera.aspect = window.innerWidth / window.innerHeight;
Expand Down
Binary file added examples/models/gltf/coffeemat.etc1s+draco.glb
Binary file not shown.
Binary file modified examples/screenshots/misc_exporter_gltf.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.