Files
apt-nl-map/static/Magic4/js/three.js-dev/examples/webgl_loader_gltf_extensions.html
2024-12-04 10:21:04 +08:00

470 lines
12 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - glTF 2.0 - extensions</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
</head>
<body>
<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> -
<a href="https://github.com/KhronosGroup/glTF" target="_blank" rel="noopener">glTF</a> 2.0 loader<br />
<div id="description"></div>
</div>
<script type="module">
import * as THREE from '../build/three.module.js';
import { GUI } from './jsm/libs/dat.gui.module.js';
import { OrbitControls } from './jsm/controls/OrbitControls.js';
import { GLTFLoader } from './jsm/loaders/GLTFLoader.js';
import { DRACOLoader } from './jsm/loaders/DRACOLoader.js';
import { RGBELoader } from './jsm/loaders/RGBELoader.js';
let orbitControls;
let camera, scene, renderer, loader;
let gltf, background, envMap, mixer, gui, extensionControls;
const clock = new THREE.Clock();
const scenes = {
Boombox: {
name: 'BoomBox (PBR)',
url: './models/gltf/BoomBox/%s/BoomBox.gltf',
author: 'Microsoft',
authorURL: 'https://www.microsoft.com/',
cameraPos: new THREE.Vector3( 0.02, 0.01, 0.03 ),
objectRotation: new THREE.Euler( 0, Math.PI, 0 ),
extensions: [ 'glTF', 'glTF-pbrSpecularGlossiness', 'glTF-Binary' ],
addEnvMap: true
},
'Bot Skinned': {
name: 'Bot Skinned',
url: './models/gltf/BotSkinned/%s/Bot_Skinned.gltf',
author: 'MozillaVR',
authorURL: 'https://vr.mozilla.org/',
cameraPos: new THREE.Vector3( 0.5, 2, 2 ),
center: new THREE.Vector3( 0, 1.2, 0 ),
objectRotation: new THREE.Euler( 0, 0, 0 ),
addLights: true,
addGround: true,
shadows: true,
extensions: [ 'glTF-MaterialsUnlit' ]
},
MetalRoughSpheres: {
name: 'MetalRoughSpheres (PBR)',
url: './models/gltf/MetalRoughSpheres/%s/MetalRoughSpheres.gltf',
author: '@emackey',
authorURL: 'https://twitter.com/emackey',
cameraPos: new THREE.Vector3( 2, 1, 15 ),
objectRotation: new THREE.Euler( 0, 0, 0 ),
extensions: [ 'glTF', 'glTF-Embedded' ],
addEnvMap: true
},
'Clearcoat Test': {
name: 'Clearcoat Test',
url: './models/gltf/ClearcoatTest/ClearcoatTest.glb',
author: 'Ed Mackey (Analytical Graphics, Inc.)',
authorURL: 'https://www.agi.com/',
cameraPos: new THREE.Vector3( 0, 0, 20 ),
extensions: [ 'glTF' ],
addEnvMap: true
},
Duck: {
name: 'Duck',
url: './models/gltf/Duck/%s/Duck.gltf',
author: 'Sony',
authorURL: 'https://www.playstation.com/en-us/corporate/about/',
cameraPos: new THREE.Vector3( 0, 3, 5 ),
addLights: true,
addGround: true,
shadows: true,
extensions: [ 'glTF', 'glTF-Embedded', 'glTF-pbrSpecularGlossiness', 'glTF-Binary', 'glTF-Draco' ]
},
Monster: {
name: 'Monster',
url: './models/gltf/Monster/%s/Monster.gltf',
author: '3drt.com',
authorURL: 'http://www.3drt.com/downloads.htm',
cameraPos: new THREE.Vector3( 3, 1, 7 ),
objectScale: new THREE.Vector3( 0.04, 0.04, 0.04 ),
objectPosition: new THREE.Vector3( 0.2, 0.1, 0 ),
objectRotation: new THREE.Euler( 0, - 3 * Math.PI / 4, 0 ),
animationTime: 3,
addLights: true,
shadows: true,
addGround: true,
extensions: [ 'glTF', 'glTF-Embedded', 'glTF-Binary', 'glTF-Draco', 'glTF-lights' ]
},
'Cesium Man': {
name: 'Cesium Man',
url: './models/gltf/CesiumMan/%s/CesiumMan.gltf',
author: 'Cesium',
authorURL: 'https://cesiumjs.org/',
cameraPos: new THREE.Vector3( 0, 3, 10 ),
objectRotation: new THREE.Euler( 0, 0, 0 ),
addLights: true,
addGround: true,
shadows: true,
extensions: [ 'glTF', 'glTF-Embedded', 'glTF-Binary', 'glTF-Draco' ]
},
'Cesium Milk Truck': {
name: 'Cesium Milk Truck',
url: './models/gltf/CesiumMilkTruck/%s/CesiumMilkTruck.gltf',
author: 'Cesium',
authorURL: 'https://cesiumjs.org/',
cameraPos: new THREE.Vector3( 0, 3, 10 ),
addLights: true,
addGround: true,
shadows: true,
extensions: [ 'glTF', 'glTF-Embedded', 'glTF-Binary', 'glTF-Draco' ]
},
'Outlined Box': {
name: 'Outlined Box',
url: './models/gltf/OutlinedBox/OutlinedBox.gltf',
author: '@twittmann',
authorURL: 'https://github.com/twittmann',
cameraPos: new THREE.Vector3( 0, 5, 15 ),
objectScale: new THREE.Vector3( 0.01, 0.01, 0.01 ),
objectRotation: new THREE.Euler( 0, 90, 0 ),
addLights: true,
shadows: true,
extensions: [ 'glTF' ]
},
};
const state = {
scene: Object.keys( scenes )[ 0 ],
extension: scenes[ Object.keys( scenes )[ 0 ] ].extensions[ 0 ],
playAnimation: true
};
function onload() {
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1;
renderer.physicallyCorrectLights = true;
document.body.appendChild( renderer.domElement );
window.addEventListener( 'resize', onWindowResize );
// Load background and generate envMap
new RGBELoader()
.setDataType( THREE.UnsignedByteType )
.setPath( 'textures/equirectangular/' )
.load( 'venice_sunset_1k.hdr', function ( texture ) {
envMap = pmremGenerator.fromEquirectangular( texture ).texture;
pmremGenerator.dispose();
background = envMap;
//
buildGUI();
initScene( scenes[ state.scene ] );
animate();
} );
const pmremGenerator = new THREE.PMREMGenerator( renderer );
pmremGenerator.compileEquirectangularShader();
}
function initScene( sceneInfo ) {
const descriptionEl = document.getElementById( 'description' );
if ( sceneInfo.author && sceneInfo.authorURL ) {
descriptionEl.innerHTML = sceneInfo.name + ' by <a href="' + sceneInfo.authorURL + '" target="_blank" rel="noopener">' + sceneInfo.author + '</a>';
}
scene = new THREE.Scene();
scene.background = new THREE.Color( 0x222222 );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.001, 1000 );
scene.add( camera );
let spot1;
if ( sceneInfo.addLights ) {
const ambient = new THREE.AmbientLight( 0x222222 );
scene.add( ambient );
const directionalLight = new THREE.DirectionalLight( 0xdddddd, 4 );
directionalLight.position.set( 0, 0, 1 ).normalize();
scene.add( directionalLight );
spot1 = new THREE.SpotLight( 0xffffff, 1 );
spot1.position.set( 5, 10, 5 );
spot1.angle = 0.50;
spot1.penumbra = 0.75;
spot1.intensity = 100;
spot1.decay = 2;
if ( sceneInfo.shadows ) {
spot1.castShadow = true;
spot1.shadow.bias = 0.0001;
spot1.shadow.mapSize.width = 2048;
spot1.shadow.mapSize.height = 2048;
}
scene.add( spot1 );
}
if ( sceneInfo.shadows ) {
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
}
// TODO: Reuse existing OrbitControls, GLTFLoaders, and so on
orbitControls = new OrbitControls( camera, renderer.domElement );
if ( sceneInfo.addGround ) {
const groundMaterial = new THREE.MeshPhongMaterial( { color: 0xFFFFFF } );
const ground = new THREE.Mesh( new THREE.PlaneGeometry( 512, 512 ), groundMaterial );
ground.receiveShadow = !! sceneInfo.shadows;
if ( sceneInfo.groundPos ) {
ground.position.copy( sceneInfo.groundPos );
} else {
ground.position.z = - 70;
}
ground.rotation.x = - Math.PI / 2;
scene.add( ground );
}
loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath( 'js/libs/draco/gltf/' );
loader.setDRACOLoader( dracoLoader );
let url = sceneInfo.url.replace( /%s/g, state.extension );
if ( state.extension === 'glTF-Binary' ) {
url = url.replace( '.gltf', '.glb' );
}
const loadStartTime = performance.now();
loader.load( url, function ( data ) {
gltf = data;
const object = gltf.scene;
console.info( 'Load time: ' + ( performance.now() - loadStartTime ).toFixed( 2 ) + ' ms.' );
if ( sceneInfo.cameraPos ) {
camera.position.copy( sceneInfo.cameraPos );
}
if ( sceneInfo.center ) {
orbitControls.target.copy( sceneInfo.center );
}
if ( sceneInfo.objectPosition ) {
object.position.copy( sceneInfo.objectPosition );
if ( spot1 ) {
spot1.target.position.copy( sceneInfo.objectPosition );
}
}
if ( sceneInfo.objectRotation ) {
object.rotation.copy( sceneInfo.objectRotation );
}
if ( sceneInfo.objectScale ) {
object.scale.copy( sceneInfo.objectScale );
}
if ( sceneInfo.addEnvMap ) {
object.traverse( function ( node ) {
if ( node.material && ( node.material.isMeshStandardMaterial ||
( node.material.isShaderMaterial && node.material.envMap !== undefined ) ) ) {
node.material.envMap = envMap;
node.material.envMapIntensity = 1.5; // boombox seems too dark otherwise
}
} );
scene.background = background;
}
object.traverse( function ( node ) {
if ( node.isMesh || node.isLight ) node.castShadow = true;
} );
const animations = gltf.animations;
if ( animations && animations.length ) {
mixer = new THREE.AnimationMixer( object );
for ( let i = 0; i < animations.length; i ++ ) {
const animation = animations[ i ];
// There's .3333 seconds junk at the tail of the Monster animation that
// keeps it from looping cleanly. Clip it at 3 seconds
if ( sceneInfo.animationTime ) {
animation.duration = sceneInfo.animationTime;
}
const action = mixer.clipAction( animation );
if ( state.playAnimation ) action.play();
}
}
scene.add( object );
onWindowResize();
}, undefined, function ( error ) {
console.error( error );
} );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
if ( mixer ) mixer.update( clock.getDelta() );
orbitControls.update();
render();
}
function render() {
renderer.render( scene, camera );
}
function buildGUI() {
gui = new GUI( { width: 330 } );
gui.domElement.parentElement.style.zIndex = 101;
const sceneCtrl = gui.add( state, 'scene', Object.keys( scenes ) );
sceneCtrl.onChange( reload );
const animCtrl = gui.add( state, 'playAnimation' );
animCtrl.onChange( toggleAnimations );
updateGUI();
}
function updateGUI() {
if ( extensionControls ) extensionControls.remove();
const sceneInfo = scenes[ state.scene ];
if ( sceneInfo.extensions.indexOf( state.extension ) === - 1 ) {
state.extension = sceneInfo.extensions[ 0 ];
}
extensionControls = gui.add( state, 'extension', sceneInfo.extensions );
extensionControls.onChange( reload );
}
function toggleAnimations() {
for ( let i = 0; i < gltf.animations.length; i ++ ) {
const clip = gltf.animations[ i ];
const action = mixer.existingAction( clip );
state.playAnimation ? action.play() : action.stop();
}
}
function reload() {
if ( loader && mixer ) mixer.stopAllAction();
updateGUI();
initScene( scenes[ state.scene ] );
}
onload();
</script>
</body>
</html>