Browse Source

Merge pull request #10972 from donmccurdy/feat-gltf2-docs-and-examples

[glTF] Branch glTF/glTF2.0 examples, and update docs to GLTF2Loader.
Mr.doob 8 years ago
parent
commit
63c049e01d

+ 27 - 8
docs/examples/loaders/GLTFLoader.html → docs/examples/loaders/GLTF2Loader.html

@@ -12,22 +12,41 @@
 		<h1>[name]</h1>
 		<h1>[name]</h1>
 
 
 		<div class="desc">
 		<div class="desc">
-		A loader for loading a *gltf* resource in JSON format.
+		A loader for *glTF* 2.0 resources.
 		<br /><br />
 		<br /><br />
-		The <a href="https://www.khronos.org/gltf">glTF file format</a> is a JSON file format to enable rapid delivery and loading of 3D content.
+		<a href="https://www.khronos.org/gltf">glTF</a> (GL Transmission Format) is an open format
+		specification for efficient delivery and loading of 3D content. Assets may be provided either
+		in JSON (.gltf) or binary (.glb) format. External files store textures (.jpg, .png, ...) and
+		additional binary data (.bin). A glTF asset may deliver one or more scenes, including meshes,
+		materials, textures, shaders, skins, skeletons, animations, lights, and/or cameras. Morph target
+		animations are not yet finalized in the
+		<a href="https://github.com/KhronosGroup/glTF/tree/master/specification">glTF specification</a>.
 		</div>
 		</div>
 
 
-		<h2>Notes</h2>
+		<h2>Extensions</h2>
 
 
 		<div>
 		<div>
-		When using custom shaders provided within a glTF file [page:THREE.GLTFLoader.Shaders] should be updated on each render loop. See [example:webgl_loader_gltf] demo source code for example usage.
+		GLTF2Loader supports the following glTF extensions:
 		</div>
 		</div>
 
 
+		<ul>
+			<li>
+				<a target="_blank" href="https://github.com/KhronosGroup/glTF/blob/master/extensions/Khronos/KHR_binary_glTF">
+					KHR_binary_glTF
+				</a>
+			</li>
+			<li>
+				<a target="_blank" href="https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_materials_common">
+					KHR_materials_common
+				</a>
+			</li>
+		</ul>
+
 		<h2>Example</h2>
 		<h2>Example</h2>
 
 
 		<code>
 		<code>
 		// Instantiate a loader
 		// Instantiate a loader
-		var loader = new THREE.GLTFLoader();
+		var loader = new THREE.GLTF2Loader();
 
 
 		// Load a glTF resource
 		// Load a glTF resource
 		loader.load( 'models/gltf/duck/duck.gltf', function ( gltf ) {
 		loader.load( 'models/gltf/duck/duck.gltf', function ( gltf ) {
@@ -40,7 +59,7 @@
 		} );
 		} );
 		</code>
 		</code>
 
 
-		[example:webgl_loader_gltf]
+		[example:webgl_loader_gltf2]
 
 
 		<h2>Constructor</h2>
 		<h2>Constructor</h2>
 
 
@@ -88,11 +107,11 @@
 		[page:String path] — The base path from which to find subsequent glTF resources such as textures, GLSL shaders and .bin data files.<br />
 		[page:String path] — The base path from which to find subsequent glTF resources such as textures, GLSL shaders and .bin data files.<br />
 		</div>
 		</div>
 		<div>
 		<div>
-		Parse a glTF-based <em>JSON</em> structure and fire [page:Function callback] when complete. The argument to [page:Function callback] will be an [page:object] that contains loaded parts: .[page:Scene scene], .[page:Array cameras], .[page:Array animations] and .[page:Array shaders]
+		Parse a glTF-based <em>JSON</em> structure and fire [page:Function callback] when complete. The argument to [page:Function callback] will be an [page:object] that contains loaded parts: .[page:Scene scene], .[page:Array scenes], .[page:Array cameras], and .[page:Array animations].
 		</div>
 		</div>
 
 
 		<h2>Source</h2>
 		<h2>Source</h2>
 
 
-		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/GLTFLoader.js examples/js/loaders/GLTFLoader.js]
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/GLTF2Loader.js examples/js/loaders/GLTF2Loader.js]
 	</body>
 	</body>
 </html>
 </html>

+ 1 - 1
docs/list.js

@@ -340,7 +340,7 @@ var list = {
 		"Loaders": [
 		"Loaders": [
 			[ "BabylonLoader", "examples/loaders/BabylonLoader" ],
 			[ "BabylonLoader", "examples/loaders/BabylonLoader" ],
 			[ "ColladaLoader", "examples/loaders/ColladaLoader" ],
 			[ "ColladaLoader", "examples/loaders/ColladaLoader" ],
-			[ "GLTFLoader", "examples/loaders/GLTFLoader" ],
+			[ "GLTF2Loader", "examples/loaders/GLTF2Loader" ],
 			[ "MTLLoader", "examples/loaders/MTLLoader" ],
 			[ "MTLLoader", "examples/loaders/MTLLoader" ],
 			[ "OBJLoader", "examples/loaders/OBJLoader" ],
 			[ "OBJLoader", "examples/loaders/OBJLoader" ],
 			[ "PCDLoader", "examples/loaders/PCDLoader" ],
 			[ "PCDLoader", "examples/loaders/PCDLoader" ],

+ 1 - 0
examples/files.js

@@ -89,6 +89,7 @@ var files = {
 		"webgl_loader_ctm_materials",
 		"webgl_loader_ctm_materials",
 		"webgl_loader_fbx",
 		"webgl_loader_fbx",
 		"webgl_loader_gltf",
 		"webgl_loader_gltf",
+		"webgl_loader_gltf2",
 		"webgl_loader_json_blender",
 		"webgl_loader_json_blender",
 		"webgl_loader_json_claraio",
 		"webgl_loader_json_claraio",
 		"webgl_loader_json_objconverter",
 		"webgl_loader_json_objconverter",

+ 5 - 14
examples/webgl_loader_gltf.html

@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <!DOCTYPE html>
 <html lang="en">
 <html lang="en">
 	<head>
 	<head>
-		<title>three.js webgl - glTF</title>
+		<title>three.js webgl - glTF 1.0</title>
 		<meta charset="utf-8">
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 		<style>
 		<style>
@@ -63,11 +63,10 @@
 	<body>
 	<body>
 		<div id="info">
 		<div id="info">
 		<a href="http://threejs.org" target="_blank">three.js</a> -
 		<a href="http://threejs.org" target="_blank">three.js</a> -
-		<a href="https://github.com/KhronosGroup/glTF" target="_blank">glTF</a> loader
+		<a href="https://github.com/KhronosGroup/glTF" target="_blank">glTF</a> 1.0 loader
 		<br>
 		<br>
 		monster by <a href="http://www.3drt.com/downloads.htm" target="_blank">3drt</a> - COLLADA duck by Sony -
 		monster by <a href="http://www.3drt.com/downloads.htm" target="_blank">3drt</a> - COLLADA duck by Sony -
-		Cesium models by <a href="http://cesiumjs.org/" target="_blank">Cesium</a> -
-		BoomBox by <a href="https://www.microsoft.com/" target="_blank">Microsoft</a>
+		Cesium models by <a href="http://cesiumjs.org/" target="_blank">Cesium</a>
 		</div>
 		</div>
 	<div id="container"></div>
 	<div id="container"></div>
 	<div id="controls">
 	<div id="controls">
@@ -96,7 +95,7 @@
 	</div>
 	</div>
 		<script src="../build/three.js"></script>
 		<script src="../build/three.js"></script>
 		<script src="js/controls/OrbitControls.js"></script>
 		<script src="js/controls/OrbitControls.js"></script>
-		<script src="js/loaders/GLTF2Loader.js"></script>
+		<script src="js/loaders/GLTFLoader.js"></script>
 
 
 		<script>
 		<script>
 			var orbitControls = null;
 			var orbitControls = null;
@@ -203,7 +202,7 @@
 					scene.add(ground);
 					scene.add(ground);
 				}
 				}
 
 
-				loader = new THREE.GLTF2Loader();
+				loader = new THREE.GLTFLoader();
 
 
 				for (var i = 0; i < extensionSelect.children.length; i++) {
 				for (var i = 0; i < extensionSelect.children.length; i++) {
 					var child = extensionSelect.children[i];
 					var child = extensionSelect.children[i];
@@ -382,14 +381,6 @@
 					addGround:true,
 					addGround:true,
 					extensions: ["glTF", "glTF-MaterialsCommon", "glTF-Binary"]
 					extensions: ["glTF", "glTF-MaterialsCommon", "glTF-Binary"]
 				},
 				},
-				{
-					name : "BoomBox (PBR)", url : "./models/gltf/BoomBox/%s/BoomBox.gltf",
-					cameraPos: new THREE.Vector3(2, 1, 3),
-					objectRotation: new THREE.Euler(0, Math.PI, 0),
-					addLights:true,
-					extensions: ["glTF"]
-				},
-
 				{
 				{
 					name : "Duck", url : "./models/gltf/duck/%s/duck.gltf",
 					name : "Duck", url : "./models/gltf/duck/%s/duck.gltf",
 					cameraPos: new THREE.Vector3(0, 3, 5),
 					cameraPos: new THREE.Vector3(0, 3, 5),

+ 517 - 0
examples/webgl_loader_gltf2.html

@@ -0,0 +1,517 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - glTF 2.0</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				font-family: Monospace;
+				background-color: #222222;
+				margin: 0px;
+				overflow: hidden;
+			}
+
+			#info {
+				color: #808080;
+				position: absolute;
+				top: 10px;
+				width: 100%;
+				text-align: center;
+				z-index: 100;
+				display:block;
+			}
+
+			#container {
+				position: absolute;
+				top: 0px;
+				width:100%;
+				height:100%;
+				z-index: -1;
+			}
+
+			#controls {
+				position: absolute;
+				width: 200px;
+				bottom: 0px;
+				left: 0px;
+				padding: 10px;
+				background-color: White;
+				font: 13px "Lucida Grande", Lucida, Verdana, sans-serif;
+			}
+
+			#controls > div {
+				margin-bottom: 8px;
+			}
+
+			#controls hr {
+				border: 0px;
+				height: 1px;
+				margin-bottom: 10px;
+				background-color: #bbb;
+			}
+
+			#info a, .button {
+				color: #f00;
+				font-weight: bold;
+				text-decoration: underline;
+				cursor: pointer
+			}
+		</style>
+	</head>
+
+	<body>
+		<div id="info">
+		<a href="http://threejs.org" target="_blank">three.js</a> -
+		<a href="https://github.com/KhronosGroup/glTF" target="_blank">glTF</a> 2.0 loader
+		<br>
+		BoomBox by <a href="https://www.microsoft.com/" target="_blank">Microsoft</a>
+		</div>
+	<div id="container"></div>
+	<div id="controls">
+		<div id="status">Loading...</div>
+		<hr />
+		<div>
+			Model
+			<select id="scenes_list" size="1" onchange="selectScene();" ondblclick="selectScene();"></select>
+		</div>
+		<div>
+			Camera
+			<select id="cameras_list" size="1" onchange="selectCamera();" ondblclick="selectCamera();"></select>
+		</div>
+		<div>
+			Animations
+			<input type="checkbox" checked onclick="toggleAnimations();">Play</input>
+		</div>
+		<div>
+			Extension
+			<select id="extensions_list" onchange="selectExtension();">
+				<option value="glTF">None</option>
+				<option value="glTF-MaterialsCommon">Built-in shaders</option>
+				<option value="glTF-Binary">Binary</option>
+			</select>
+		</div>
+	</div>
+		<script src="../build/three.js"></script>
+		<script src="js/controls/OrbitControls.js"></script>
+		<script src="js/loaders/GLTF2Loader.js"></script>
+
+		<script>
+			var orbitControls = null;
+			var container, camera, scene, renderer, loader;
+
+			var cameraIndex = 0;
+			var cameras = [];
+			var cameraNames = [];
+			var defaultCamera = null;
+			var gltf = null;
+			var mixer = null;
+			var clock = new THREE.Clock();
+
+			function onload() {
+
+				window.addEventListener( 'resize', onWindowResize, false );
+				document.addEventListener( 'keydown', function(e) { onKeyDown(e); }, false );
+
+				buildSceneList();
+				switchScene(0);
+				animate();
+
+			}
+
+			function initScene(index) {
+
+				container = document.getElementById( 'container' );
+
+				scene = new THREE.Scene();
+
+				defaultCamera = new THREE.PerspectiveCamera( 45, container.offsetWidth / container.offsetHeight, 1, 20000 );
+
+				//defaultCamera.up = new THREE.Vector3( 0, 1, 0 );
+				scene.add( defaultCamera );
+				camera = defaultCamera;
+
+				var sceneInfo = sceneList[index];
+
+				var spot1 = null;
+
+				if (sceneInfo.addLights) {
+
+					var ambient = new THREE.AmbientLight( 0x222222 );
+					scene.add( ambient );
+
+					var directionalLight = new THREE.DirectionalLight( 0xdddddd );
+					directionalLight.position.set( 0, 0, 1 ).normalize();
+					scene.add( directionalLight );
+
+					spot1   = new THREE.SpotLight( 0xffffff, 1 );
+					spot1.position.set( 10, 20, 10 );
+					spot1.angle = 0.25;
+					spot1.distance = 1024;
+					spot1.penumbra = 0.75;
+
+					if ( sceneInfo.shadows ) {
+
+						spot1.castShadow = true;
+						spot1.shadow.bias = 0.0001;
+						spot1.shadow.mapSize.width = 2048;
+						spot1.shadow.mapSize.height = 2048;
+
+					}
+
+					scene.add( spot1 );
+
+				}
+
+				// RENDERER
+
+				renderer = new THREE.WebGLRenderer({antialias:true});
+				renderer.setClearColor( 0x222222 );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+				if (sceneInfo.shadows) {
+					renderer.shadowMap.enabled = true;
+					renderer.shadowMap.type = THREE.PCFSoftShadowMap;
+				}
+
+				container.appendChild( renderer.domElement );
+
+				var ground = null;
+
+				if (sceneInfo.addGround) {
+					var groundMaterial = new THREE.MeshPhongMaterial({
+							color: 0xFFFFFF,
+							shading: THREE.SmoothShading
+						});
+					ground = new THREE.Mesh( new THREE.PlaneBufferGeometry(512, 512), groundMaterial);
+
+					if (sceneInfo.shadows) {
+						ground.receiveShadow = true;
+					}
+
+					if (sceneInfo.groundPos) {
+						ground.position.copy(sceneInfo.groundPos);
+					} else {
+						ground.position.z = -70;
+					}
+
+					ground.rotation.x = -Math.PI / 2;
+
+					scene.add(ground);
+				}
+
+				loader = new THREE.GLTF2Loader();
+
+				for (var i = 0; i < extensionSelect.children.length; i++) {
+					var child = extensionSelect.children[i];
+					child.disabled = sceneInfo.extensions.indexOf(child.value) === -1;
+					if (child.disabled && child.selected) {
+						extensionSelect.value = extension = 'glTF';
+					}
+				}
+
+				var url = sceneInfo.url;
+				var r = eval("/" + '\%s' + "/g");
+				url = url.replace(r, extension);
+
+				if (extension === 'glTF-Binary') {
+					url = url.replace('.gltf', '.glb');
+				}
+
+				var loadStartTime = performance.now();
+				var status = document.getElementById("status");
+				status.innerHTML = "Loading...";
+
+				loader.load( url, function(data) {
+
+					gltf = data;
+
+					var object = gltf.scene;
+
+					status.innerHTML = "Load time: " + ( performance.now() - loadStartTime ).toFixed( 2 ) + " ms.";
+
+					if (sceneInfo.cameraPos)
+						defaultCamera.position.copy(sceneInfo.cameraPos);
+
+					if (sceneInfo.center) {
+						orbitControls.target.copy(sceneInfo.center);
+					}
+
+					if (sceneInfo.objectPosition) {
+						object.position.copy(sceneInfo.objectPosition);
+
+						if (spot1) {
+							spot1.position.set(sceneInfo.objectPosition.x - 100, sceneInfo.objectPosition.y + 200, sceneInfo.objectPosition.z - 100 );
+							spot1.target.position.copy(sceneInfo.objectPosition);
+						}
+					}
+
+					if (sceneInfo.objectRotation)
+						object.rotation.copy(sceneInfo.objectRotation);
+
+					if (sceneInfo.objectScale)
+						object.scale.copy(sceneInfo.objectScale);
+
+					cameraIndex = 0;
+					cameras = [];
+					cameraNames = [];
+
+					if (gltf.cameras && gltf.cameras.length) {
+
+						var i, len = gltf.cameras.length;
+
+						for (i = 0; i < len; i++) {
+
+							var addCamera = true;
+							var cameraName = gltf.cameras[i].parent.name;
+
+							if (sceneInfo.cameras && !(cameraName in sceneInfo.cameras)) {
+									addCamera = false;
+							}
+
+							if (addCamera) {
+								cameraNames.push(cameraName);
+								cameras.push(gltf.cameras[i]);
+							}
+
+						}
+
+						updateCamerasList();
+						switchCamera(1);
+
+					} else {
+
+						updateCamerasList();
+						switchCamera(0);
+
+					}
+
+					var animations = gltf.animations;
+
+					if ( animations && animations.length ) {
+
+						mixer = new THREE.AnimationMixer( object );
+
+						for ( var i = 0; i < animations.length; i ++ ) {
+
+							var 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;
+
+							mixer.clipAction( animation ).play();
+
+						}
+
+					}
+
+					scene.add( object );
+					onWindowResize();
+
+				});
+
+			orbitControls = new THREE.OrbitControls(defaultCamera, renderer.domElement);
+
+			}
+
+			function onWindowResize() {
+
+				defaultCamera.aspect = container.offsetWidth / container.offsetHeight;
+				defaultCamera.updateProjectionMatrix();
+
+				var i, len = cameras.length;
+
+				for (i = 0; i < len; i++) { // just do it for default
+					cameras[i].aspect = container.offsetWidth / container.offsetHeight;
+					cameras[i].updateProjectionMatrix();
+				}
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function animate() {
+				requestAnimationFrame( animate );
+				if (mixer) mixer.update(clock.getDelta());
+				if (cameraIndex == 0)
+					orbitControls.update();
+				render();
+			}
+
+			function render() {
+				renderer.render( scene, camera );
+			}
+
+			function onKeyDown(event) {
+
+				var chr = String.fromCharCode(event.keyCode);
+
+				if (chr == ' ') {
+
+					index = cameraIndex + 1;
+					if (index > cameras.length)
+					index = 0;
+					switchCamera(index);
+
+				} else {
+
+					var index = parseInt(chr);
+					if (!isNaN(index)	&& (index <= cameras.length)) {
+						switchCamera(index);
+					}
+
+				}
+
+			}
+
+			var sceneList = [
+				{
+					name : "BoomBox (PBR)", url : "./models/gltf/BoomBox/%s/BoomBox.gltf",
+					cameraPos: new THREE.Vector3(2, 1, 3),
+					objectRotation: new THREE.Euler(0, Math.PI, 0),
+					addLights:true,
+					extensions: ["glTF"]
+				}
+			];
+
+			function buildSceneList() {
+
+				var elt = document.getElementById('scenes_list');
+
+				while( elt.hasChildNodes() ){
+					elt.removeChild(elt.lastChild);
+				}
+
+				var i, len = sceneList.length;
+
+				for (i = 0; i < len; i++) {
+					option = document.createElement("option");
+					option.text=sceneList[i].name;
+					elt.add(option);
+				}
+
+			}
+
+			function switchScene(index) {
+
+				cleanup();
+				initScene(index);
+				var elt = document.getElementById('scenes_list');
+				elt.selectedIndex = index;
+
+			}
+
+			function selectScene() {
+
+				var select = document.getElementById("scenes_list");
+				var index = select.selectedIndex;
+
+				if (index >= 0) {
+					switchScene(index);
+				}
+
+			}
+
+			function switchCamera(index) {
+
+				cameraIndex = index;
+
+				if (cameraIndex == 0) {
+					camera = defaultCamera;
+				}
+
+				if (cameraIndex >= 1 && cameraIndex <= cameras.length) {
+					camera = cameras[cameraIndex - 1];
+				}
+
+				var elt = document.getElementById('cameras_list');
+				elt.selectedIndex = cameraIndex;
+
+			}
+
+			function updateCamerasList() {
+
+				var elt = document.getElementById('cameras_list');
+
+				while( elt.hasChildNodes() ){
+					elt.removeChild(elt.lastChild);
+				}
+
+				option = document.createElement("option");
+				option.text="[default]";
+				elt.add(option);
+
+				var i, len = cameraNames.length;
+
+				for (i = 0; i < len; i++) {
+					option = document.createElement("option");
+					option.text=cameraNames[i];
+					elt.add(option);
+				}
+
+			}
+
+			function selectCamera() {
+
+				var select = document.getElementById("cameras_list");
+				var index = select.selectedIndex;
+
+				if (index >= 0) {
+					switchCamera(index);
+				}
+
+			}
+
+			function toggleAnimations() {
+
+				var i, len = gltf.animations.length;
+
+				for (i = 0; i < len; i++) {
+
+					var clip = gltf.animations[i];
+					var action = mixer.existingAction( clip );
+
+					if (action.isRunning()) {
+						action.stop();
+					} else {
+						action.play();
+					}
+
+				}
+
+			}
+
+			var extensionSelect = document.getElementById("extensions_list");
+			var extension = extensionSelect.value;
+			function selectExtension()
+			{
+				extension = extensionSelect.value;
+				selectScene();
+			}
+
+			function cleanup() {
+
+				if (container && renderer) {
+					container.removeChild(renderer.domElement);
+				}
+
+				cameraIndex = 0;
+				cameras = [];
+				cameraNames = [];
+				defaultCamera = null;
+
+				if (!loader || !mixer)
+					return;
+
+				mixer.stopAllAction();
+
+			}
+
+			onload();
+		</script>
+
+	</body>
+</html>