Reorganize JS to separate threejs scene setup from Basis texture loading.
diff --git a/webgl/gltf-demo/BasisTextureLoader.js b/webgl/gltf-demo/BasisTextureLoader.js new file mode 100644 index 0000000..2c8cfb9 --- /dev/null +++ b/webgl/gltf-demo/BasisTextureLoader.js
@@ -0,0 +1,169 @@ +/** + * @author Don McCurdy / https://www.donmccurdy.com + * @author Austin Eng / ? + * @author Shrek Shao / https://github.com/shrekshao + */ + +/** + * An example three.js loader for Basis compressed textures. + */ +THREE.BasisTextureLoader = class BasisTextureLoader { + + constructor () { + + this.etcSupported = false; + this.dxtSupported = false; + this.pvrtcSupported = false; + + this.format = null; + + } + + detectSupport ( renderer ) { + + const context = renderer.context; + + this.etcSupported = context.getExtension('WEBGL_compressed_texture_etc1'); + this.dxtSupported = context.getExtension('WEBGL_compressed_texture_s3tc'); + this.pvrtcSupported = context.getExtension('WEBGL_compressed_texture_pvrtc') + || context.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc'); + + if ( ! this.etcSupported && ! this.dxtSupported && ! this.pvrtcSupported ) { + + throw new Error( 'THREE.BasisTextureLoader: No suitable compressed texture format found.' ); + + } + + this.format = this.etcSupported + ? BASIS_FORMAT.cTFETC1 + : ( this.dxtSupported + ? BASIS_FORMAT.cTFBC1 + : BASIS_FORMAT.cTFPVRTC1_4_OPAQUE_ONLY ); + + return this; + + } + + load ( url, onLoad, onProgress, onError ) { + + fetch( url ) + .then( ( res ) => res.arrayBuffer() ) + .then( (arrayBuffer) => this._createTexture( arrayBuffer ) ) + .then( onLoad ) + .catch( onError ); + + } + + _createTexture ( arrayBuffer ) { + + const BASIS_FORMAT = THREE.BasisTextureLoader.BASIS_FORMAT; + const DXT_FORMAT_MAP = THREE.BasisTextureLoader.DXT_FORMAT_MAP; + + const basisFile = new BasisFile( new Uint8Array( arrayBuffer ) ); + + const width = basisFile.getImageWidth( 0, 0 ); + const height = basisFile.getImageHeight( 0, 0 ); + const images = basisFile.getNumImages(); + const levels = basisFile.getNumLevels( 0 ); + + function cleanup () { + + basisFile.close(); + basisFile.delete(); + + } + + if ( ! width || ! height || ! images || ! levels ) { + + cleanup(); + throw new Error( 'THREE.BasisTextureLoader: Invalid .basis file' ); + + } + + if ( ! basisFile.startTranscoding() ) { + + cleanup(); + throw new Error( 'THREE.BasisTextureLoader: .startTranscoding failed' ); + + } + + const dst = new Uint8Array( basisFile.getImageTranscodedSizeInBytes( 0, 0, this.format ) ); + + const startTime = performance.now(); + + const status = basisFile.transcodeImage( + dst, + 0, + 0, + this.format, + this.etcSupported ? 0 : ( this.dxtSupported ? 1 : 0 ), + 0 + ); + + console.log( `THREE.BasisTextureLoader: Transcode time: ${performance.now() - startTime}ms`, dst ); + + cleanup(); + + if ( ! status ) { + + throw new Error( 'THREE.BasisTextureLoader: .transcodeImage failed.' ); + + } + + const mipmaps = [ { data: dst, width, height } ]; + + let texture; + + if ( this.etcSupported ) { + + texture = new THREE.CompressedTexture( mipmaps, width, height, THREE.RGB_ETC1_Format ); + + } else if ( this.dxtSupported ) { + + texture = new THREE.CompressedTexture( mipmaps, width, height, DXT_FORMAT_MAP[ this.format ], THREE.UnsignedByteType ); + + } else if ( this.pvrtcSupported ) { + + texture = new THREE.CompressedTexture( mipmaps, width, height, THREE.RGB_PVRTC_4BPPV1_Format ); + + } else { + + throw new Error( 'THREE.BasisTextureLoader: No supported format available.' ); + + } + + texture.minFilter = THREE.LinearMipMapLinearFilter; + texture.magFilter = THREE.LinearFilter; + texture.encoding = THREE.sRGBEncoding; + texture.generateMipmaps = false; + texture.flipY = false; + texture.needsUpdate = true; + + return texture; + + } +} + +const BASIS_FORMAT = { + cTFETC1: 0, + cTFBC1: 1, + cTFBC4: 2, + cTFPVRTC1_4_OPAQUE_ONLY: 3, + cTFBC7_M6_OPAQUE_ONLY: 4, + cTFETC2: 5, + cTFBC3: 6, + cTFBC5: 7, +}; + +// DXT formats, from: +// http://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/ +const DXT_FORMAT_MAP = {}; +const COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0; +const COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1; +const COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2; +const COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3; +DXT_FORMAT_MAP[ BASIS_FORMAT.cTFBC1 ] = COMPRESSED_RGB_S3TC_DXT1_EXT; +DXT_FORMAT_MAP[ BASIS_FORMAT.cTFBC3 ] = COMPRESSED_RGBA_S3TC_DXT5_EXT; + +THREE.BasisTextureLoader.BASIS_FORMAT = BASIS_FORMAT; +THREE.BasisTextureLoader.DXT_FORMAT_MAP = DXT_FORMAT_MAP;
diff --git a/webgl/gltf-demo/GLTFLoader.js b/webgl/gltf-demo/GLTFLoader.js index bb67be4..31bc8af 100644 --- a/webgl/gltf-demo/GLTFLoader.js +++ b/webgl/gltf-demo/GLTFLoader.js
@@ -1,4 +1,9 @@ /** + * Copyright 2019 the three.js authors + * SPDX-License-Identifier: MIT + */ + +/** * @author Rich Tibbett / https://github.com/richtr * @author mrdoob / http://mrdoob.com/ * @author Tony Parisi / http://www.tonyparisi.com/ @@ -6,6 +11,12 @@ * @author Don McCurdy / https://www.donmccurdy.com */ +/** + * A modified version of THREE.GLTFLoader, with support for the (hypothetical) + * GOOGLE_texture_basis extension. This extension is defined for the sake of + * example only – the glTF format will officially reference Basis files within + * a KTX2 wrapper. + */ THREE.GLTFLoader = ( function () { function GLTFLoader( manager ) {
diff --git a/webgl/gltf-demo/index.html b/webgl/gltf-demo/index.html index 8cf04b2..c37a66c 100644 --- a/webgl/gltf-demo/index.html +++ b/webgl/gltf-demo/index.html
@@ -3,220 +3,118 @@ <head> <script src="https://cdn.jsdelivr.net/npm/three@v0.104.0"></script> <script src="GLTFLoader.js"></script> + <script src="BasisTextureLoader.js"></script> <script src="https://cdn.jsdelivr.net/npm/three@v0.104.0/examples/js/controls/OrbitControls.js"></script> <style> html, body { width:100%; height:100%; margin:0; padding:0; } canvas { display:block; } - #pannel { position: absolute; top: 10px; left: 10px; color: white; background-color:rgba(0.3, 0.3, 0.3, 0.3); padding: 0.5em; max-width: 25%;} + #panel { position: absolute; top: 10px; left: 10px; color: white; background-color:rgba(0.3, 0.3, 0.3, 0.3); padding: 0.5em; max-width: 400px;} </style> </head> <body> <script src="./wasm/build/basis_transcoder.js"></script> <script> - function log(s) { - var div = document.createElement('div'); - div.innerHTML = s; - document.getElementById('log').appendChild(div); - } - - function logTime(desc, t) { - log(desc + t.toFixed(1) + ' ms'); - } - + /** + * Demo created by donmccurdy@, austin@, shrekshao@, with thanks to the Cesium team for + * providing the sample model. + */ Module.onRuntimeInitialized = () => { + const { BasisFile, initializeBasis } = Module; - // DXT formats, from: - // http://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/ - COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0; - COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1; - COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2; - COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3; - - const BASIS_FORMAT = { - cTFETC1: 0, - cTFBC1: 1, - cTFBC4: 2, - cTFPVRTC1_4_OPAQUE_ONLY: 3, - cTFBC7_M6_OPAQUE_ONLY: 4, - cTFETC2: 5, - cTFBC3: 6, - cTFBC5: 7, - }; - - BASIS_FORMAT_NAMES = {}; - for (var name in BASIS_FORMAT) { - BASIS_FORMAT_NAMES[BASIS_FORMAT[name]] = name; - } - - DXT_FORMAT_MAP = {}; - DXT_FORMAT_MAP[BASIS_FORMAT.cTFBC1] = COMPRESSED_RGB_S3TC_DXT1_EXT; - DXT_FORMAT_MAP[BASIS_FORMAT.cTFBC3] = COMPRESSED_RGBA_S3TC_DXT5_EXT; + window.BasisFile = BasisFile; initializeBasis(); + // Initialize three.js scene and renderer. - /** - * ThreeJS glTF demo created by donmccurdy@, austin@, shrekshao@ below. - */ + const renderer = new THREE.WebGLRenderer( { antialias: true } ); + renderer.setSize( window.innerWidth, window.innerHeight ); + renderer.gammaOutput = true; + renderer.gammaFactor = 2.2; - (function () { + const scene = new THREE.Scene(); + scene.background = new THREE.Color( 0xf0f0f0 ); - const renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.gammaOutput = true; - renderer.gammaFactor = 2.2; + const light = new THREE.AmbientLight(); + scene.add( light ); - const scene = new THREE.Scene(); - scene.background = new THREE.Color(0xf0f0f0); + const light2 = new THREE.PointLight(); + scene.add( light2 ); - const light = new THREE.AmbientLight(); - scene.add(light); + const camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1, 100 ); + camera.position.set( 8, 6, 5 ); + camera.lookAt( new THREE.Vector3( 0, -2, 0 ) ); - const light2 = new THREE.PointLight(); - scene.add(light2); + const controls = new THREE.OrbitControls( camera, renderer.domElement ); + controls.autoRotate = true; - const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 100); - camera.position.set(12, 8, 10); - camera.lookAt(new THREE.Vector3(0, -2, 0)); + // Create BasisTextureLoader and detect supported target formats. - const controls = new THREE.OrbitControls( camera, renderer.domElement ); - controls.autoRotate = true; + const basisLoader = new THREE.BasisTextureLoader(); + basisLoader.detectSupport( renderer ); - window.addEventListener('resize', () => { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - renderer.setSize(window.innerWidth, window.innerHeight); - }, false); + const formatName = basisLoader.etcSupported + ? 'ETC1' + : ( basisLoader.dxtSupported ? 'BC1' : 'PVRTC' ); + log(`Transcode to: <strong>${formatName}</strong>`); - const etcSupported = renderer.context.getExtension('WEBGL_compressed_texture_etc1'); - const dxtSupported = renderer.context.getExtension('WEBGL_compressed_texture_s3tc'); - const pvrtcSupported = renderer.context.getExtension('WEBGL_compressed_texture_pvrtc') || - renderer.context.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc'); + // Register BasisTextureLoader for .basis extension. - if (!etcSupported && !dxtSupported && !pvrtcSupported) { - log('No suitable compressed texture format supported!!'); - return; - } + THREE.Loader.Handlers.add( /\.basis$/, basisLoader ); - let format = etcSupported ? BASIS_FORMAT.cTFETC1 : (dxtSupported ? BASIS_FORMAT.cTFBC1 : BASIS_FORMAT.cTFPVRTC1_4_OPAQUE_ONLY); + // Create GLTFLoader, load model, and render. - log('Transcode to Compressed Texture Format: <strong>' + BASIS_FORMAT_NAMES[format] + '</strong>'); + const loader = new THREE.GLTFLoader(); - class BasisTextureLoader { - constructor() { - this.count = 0; - } + loader.load( 'AGI_HQ/AgiHQ.gltf', ( gltf ) => { - load(url, onLoad, onProgress, onError) { + const model = gltf.scene; + model.scale.set( 0.01, 0.01, 0.01 ); - fetch(url).then(res => res.arrayBuffer()).then(buf => { + scene.add( model ); - log('<strong> Texture ' + (this.count++) + '</strong>'); + document.body.appendChild( renderer.domElement ); - const startTime = performance.now(); + animate(); - const basisFile = new BasisFile(new Uint8Array(buf)); + }, undefined, ( e ) => console.error( e ) ); - const width = basisFile.getImageWidth(0, 0); - const height = basisFile.getImageHeight(0, 0); - const images = basisFile.getNumImages(); - const levels = basisFile.getNumLevels(0); + // Main render loop. - if (!width || !height || !images || !levels) { - console.warn('Invalid .basis file'); - basisFile.close(); - basisFile.delete(); - return; - } + function animate() { - if (!basisFile.startTranscoding()) { - console.warn('startTranscoding failed'); - basisFile.close(); - basisFile.delete(); - return; - } + requestAnimationFrame( animate ); + controls.update(); + renderer.render( scene, camera ); - const dst = new Uint8Array(basisFile.getImageTranscodedSizeInBytes(0, 0, format)); + } - if (!basisFile.transcodeImage(dst, 0, 0, format, etcSupported ? 0 : (dxtSupported ? 1 : 0), 0)) { - console.warn('transcodeImage failed'); - basisFile.close(); - basisFile.delete(); - return; - } + // Support viewport resizing. - const elapsed = performance.now() - startTime; + window.addEventListener( 'resize', () => { - basisFile.close(); - basisFile.delete(); + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize( window.innerWidth, window.innerHeight ); - console.log('Transcode success!', dst); - console.log('Time:', elapsed, 'ms'); + }, false ); - logTime('Transcode Time: ', elapsed); + } - const mipmaps = [ { data: dst, width, height } ]; - let texture; - if (etcSupported) { - texture = new THREE.CompressedTexture( mipmaps, width, height, THREE.RGB_ETC1_Format ); - } else if (dxtSupported) { - const type = dxtSupported ? THREE.UnsignedByteType : THREE.UnsignedShort565Type; - const f = dxtSupported ? DXT_FORMAT_MAP[ format ] : THREE.RGBFormat; - texture = new THREE.CompressedTexture( mipmaps, width, height, f, type ); - } else if (pvrtcSupported) { - texture = new THREE.CompressedTexture( mipmaps, width, height, THREE.RGB_PVRTC_4BPPV1_Format ); - } + function log(s) { - texture.minFilter = THREE.LinearFilter; - texture.magFilter = THREE.LinearFilter; - texture.encoding = THREE.sRGBEncoding; - texture.generateMipmaps = false; - texture.flipY = false; - texture.needsUpdate = true; + const div = document.createElement('div'); + div.innerHTML = s; + document.getElementById('log').appendChild(div); - onLoad( texture ); - - }); - - } - } - - THREE.Loader.Handlers.add(/\.basis$/, new BasisTextureLoader()); - - const loader = new THREE.GLTFLoader(); - - function animate() { - requestAnimationFrame(animate); - controls.update(); - renderer.render(scene, camera); - } - - loader.load('AGI_HQ/AgiHQ.gltf', (gltf) => { - - const model = gltf.scene; - model.scale.set(0.01, 0.01, 0.01); - - const bbox = new THREE.Box3(); - bbox.setFromObject(model); - console.log(bbox); - - scene.add(model); - - document.body.appendChild(renderer.domElement); - - animate(); - - }, undefined, (e) => console.error(e)); - - }()); } </script> - <div id="pannel"> + <div id="panel"> <strong>Basis Texture Transcoder glTF Demo</strong> <div id="log"></div> - + </div> </body>
diff --git a/webgl/gltf-demo/third-party-licenses.txt b/webgl/gltf-demo/third-party-licenses.txt deleted file mode 100644 index 7467971..0000000 --- a/webgl/gltf-demo/third-party-licenses.txt +++ /dev/null
@@ -1,23 +0,0 @@ -Three.js - -> The MIT License - -Copyright © 2010-2019 three.js authors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file