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