瀏覽代碼

Basic i3dmloader example

Garrett Johnson 5 年之前
父節點
當前提交
3e01a6ab49
共有 6 個文件被更改,包括 306 次插入4 次删除
  1. 30 0
      example/i3dmExample.html
  2. 132 0
      example/i3dmExample.js
  3. 8 2
      src/base/I3DMLoaderBase.js
  4. 4 2
      src/three/CMPTLoader.js
  5. 16 0
      src/three/I3DMLoader.d.ts
  6. 116 0
      src/three/I3DMLoader.js

+ 30 - 0
example/i3dmExample.html

@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+    <head>
+		<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
+		<meta charset="utf-8"/>
+
+		<title>3D Tiles Renderer Instanced Example</title>
+
+        <style>
+            * {
+                margin: 0;
+                padding: 0;
+            }
+
+            html {
+                overflow: hidden;
+				font-family: Arial, Helvetica, sans-serif;
+				user-select: none;
+            }
+
+			canvas {
+				image-rendering: pixelated;
+				outline: none;
+			}
+        </style>
+    </head>
+    <body>
+        <script src="./i3dmExample.js"></script>
+    </body>
+</html>

+ 132 - 0
example/i3dmExample.js

@@ -0,0 +1,132 @@
+import { I3DMLoader } from '../src/index.js';
+import {
+	Scene,
+	DirectionalLight,
+	AmbientLight,
+	WebGLRenderer,
+	PerspectiveCamera,
+	Box3,
+	sRGBEncoding,
+	PCFSoftShadowMap,
+	Vector3,
+	Quaternion,
+	Matrix4,
+} from 'three';
+import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
+
+let camera, controls, scene, renderer;
+let box, dirLight;
+
+init();
+animate();
+
+function init() {
+
+	scene = new Scene();
+
+	// primary camera view
+	renderer = new WebGLRenderer( { antialias: true } );
+	renderer.setPixelRatio( window.devicePixelRatio );
+	renderer.setSize( window.innerWidth, window.innerHeight );
+	renderer.setClearColor( 0x151c1f );
+	renderer.shadowMap.enabled = true;
+	renderer.shadowMap.type = PCFSoftShadowMap;
+	renderer.outputEncoding = sRGBEncoding;
+
+	document.body.appendChild( renderer.domElement );
+
+	camera = new PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 4000 );
+	camera.position.set( 20, 20, 20 );
+
+	// controls
+	controls = new OrbitControls( camera, renderer.domElement );
+	controls.screenSpacePanning = false;
+	controls.minDistance = 1;
+	controls.maxDistance = 2000;
+
+	// lights
+	dirLight = new DirectionalLight( 0xffffff, 1.25 );
+	dirLight.position.set( 1, 2, 3 ).multiplyScalar( 40 );
+	dirLight.castShadow = true;
+	dirLight.shadow.bias = - 0.01;
+	dirLight.shadow.mapSize.setScalar( 2048 );
+
+	const shadowCam = dirLight.shadow.camera;
+	shadowCam.left = - 200;
+	shadowCam.bottom = - 200;
+	shadowCam.right = 200;
+	shadowCam.top = 200;
+	shadowCam.updateProjectionMatrix();
+
+	scene.add( dirLight );
+
+	const ambLight = new AmbientLight( 0xffffff, 0.05 );
+	scene.add( ambLight );
+
+	box = new Box3();
+
+	new I3DMLoader()
+		.load( 'https://raw.githubusercontent.com/CesiumGS/3d-tiles-samples/master/tilesets/TilesetWithTreeBillboards/tree.i3dm' )
+		.then( res => {
+
+			let instance = null;
+			res.scene.traverse( c => {
+
+				if ( ! instance && c.isInstancedMesh ) {
+
+					instance = c;
+
+				}
+
+			} );
+
+			const pos = new Vector3();
+			const quat = new Quaternion();
+			const sca = new Vector3();
+			const mat = new Matrix4();
+			const averagePos = new Vector3();
+
+			for ( let i = 0, l = instance.count; i < l; i ++ ) {
+
+				instance.getMatrixAt( i, mat );
+				mat.decompose( pos, quat, sca );
+				averagePos.add( pos );
+
+			}
+
+			averagePos.divideScalar( instance.count );
+			controls.target.copy( averagePos );
+			controls.update();
+
+			console.log( res );
+			scene.add( res.scene );
+
+		} );
+
+	onWindowResize();
+	window.addEventListener( 'resize', onWindowResize, false );
+
+}
+
+function onWindowResize() {
+
+	camera.aspect = window.innerWidth / window.innerHeight;
+	renderer.setPixelRatio( window.devicePixelRatio );
+	renderer.setSize( window.innerWidth, window.innerHeight );
+	camera.updateProjectionMatrix();
+
+}
+
+function animate() {
+
+	requestAnimationFrame( animate );
+
+	render();
+
+}
+
+function render() {
+
+	renderer.render( scene, camera );
+
+}

+ 8 - 2
src/base/I3DMLoaderBase.js

@@ -1,6 +1,7 @@
 // I3DM File Format
 // https://github.com/CesiumGS/3d-tiles/blob/master/specification/TileFormats/Instanced3DModel/README.md
 
+import { FeatureTable, BatchTable } from '../utilities/FeatureTable.js';
 import { arrayToString } from '../utilities/arrayToString.js';
 
 export class I3DMLoaderBase {
@@ -75,9 +76,14 @@ export class I3DMLoaderBase {
 		// Batch Table
 		const BATCH_ID = featureTable.getData( 'BATCH_ID', 'UNSIGNED_SHORT' );
 		let maxBatchId = - 1;
-		for ( let i = 0, l = BATCH_ID.length; i < l; i ++ ) {
 
-			maxBatchId = Math.max( BATCH_ID[ i ], maxBatchId );
+		if ( BATCH_ID !== null ) {
+
+			for ( let i = 0, l = BATCH_ID.length; i < l; i ++ ) {
+
+				maxBatchId = Math.max( BATCH_ID[ i ], maxBatchId );
+
+			}
 
 		}
 

+ 4 - 2
src/three/CMPTLoader.js

@@ -24,7 +24,7 @@ export class CMPTLoader extends CMPTLoaderBase {
 
 			const { type, buffer } = result.tiles[ i ];
 			switch ( type ) {
-				case 'b3dm':
+				case 'b3dm': {
 
 					const slicedBuffer = buffer.slice();
 					const promise = new B3DMLoader( manager )
@@ -39,7 +39,8 @@ export class CMPTLoader extends CMPTLoaderBase {
 					promises.push(promise);
 					break;
 
-				case 'pnts':
+				}
+				case 'pnts': {
 
 					const slicedBuffer = buffer.slice();
 					const pointsResult = new PNTSLoader( manager ).parse( slicedBuffer.buffer );
@@ -47,6 +48,7 @@ export class CMPTLoader extends CMPTLoaderBase {
 					group.add( pointsResult.scene );
 					break;
 
+				}
 				case 'i3dm':
 				default:
 			}

+ 16 - 0
src/three/I3DMLoader.d.ts

@@ -0,0 +1,16 @@
+import { I3DMLoaderBase, I3DMBaseResult } from '../base/I3DMLoaderBase';
+import { GLTF } from 'three/examples/jsm/loaders/GLTFLoader';
+
+export interface I3DMResult extends GLTF, I3DMBaseResult {
+
+	batchTable : Object;
+	featureTable : Object;
+
+}
+
+export class I3DMLoader extends I3DMLoaderBase {
+
+	load( url : String ) : Promise< I3DMResult >;
+	parse( buffer : ArrayBuffer ) : I3DMResult;
+
+}

+ 116 - 0
src/three/I3DMLoader.js

@@ -0,0 +1,116 @@
+import { I3DMLoaderBase } from '../base/I3DMLoaderBase.js';
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
+import { Matrix4, InstancedMesh, Vector3, Quaternion, MeshBasicMaterial, BoxBufferGeometry } from 'three';
+
+const tempPos = new Vector3();
+const tempQuat = new Quaternion();
+const tempSca = new Vector3();
+const tempMat = new Matrix4();
+export class I3DMLoader extends I3DMLoaderBase {
+
+	constructor( manager ) {
+
+		super();
+		this.manager = manager;
+
+	}
+
+	parse( buffer ) {
+
+		return super
+			.parse( buffer )
+			.then( i3dm => {
+
+				const { featureTable, batchTable } = i3dm;
+				const gltfBuffer = i3dm.glbBytes.slice().buffer;
+				return new Promise( ( resolve, reject ) => {
+
+					const manager = this.manager;
+					new GLTFLoader( manager ).parse( gltfBuffer, null, model => {
+
+						const INSTANCES_LENGTH = featureTable.getData( 'INSTANCES_LENGTH' );
+
+						// RTC_CENTER
+						// QUANTIZED_VOLUME_OFFSET
+						// QUANTIZED_VOLUME_SCALE
+						// EAST_NORTH_UP
+
+						const POSITION = featureTable.getData( 'POSITION', INSTANCES_LENGTH, 'FLOAT', 'VEC3' );
+
+						// POSITION_QUANTIZED
+						// NORMAL_UP
+						// NORMAL_RIGHT
+						// NORMAL_UP_OCT32P
+						// NORMAL_RIGHT_OCT32P
+						// SCALE
+						// SCALE_NON_UNIFORM
+						// BATCH_ID
+
+						let instances = [];
+						const traverse = c => {
+
+							const children = c.children;
+							for ( let i = 0, l = children.length; i < l; i ++ ) {
+
+								const child = children[ i ];
+								if ( child.isMesh ) {
+
+									// TODO: Why is the model being rotated after?
+									const { geometry, material } = child;
+									const instancedMesh = new InstancedMesh( geometry, material, INSTANCES_LENGTH );
+									children[ i ] = instancedMesh;
+									instances.push( instancedMesh );
+
+									window.instancedMesh = instancedMesh;
+									child.material = new MeshBasicMaterial()
+
+								} else {
+
+									traverse( child );
+
+								}
+
+							}
+
+						};
+						traverse( model.scene );
+
+						for ( let i = 0; i < INSTANCES_LENGTH; i ++ ) {
+
+							// TODO: handle quantized position
+							tempPos.set(
+								POSITION[ i * 3 + 0 ],
+								POSITION[ i * 3 + 1 ],
+								POSITION[ i * 3 + 2 ],
+							);
+
+							// TODO: handle normal orientation features
+							tempQuat.set( 0, 0, 0, 1 );
+
+							// TODO: handle scale features
+							tempSca.set( 1, 1, 1 );
+
+							tempMat.compose( tempPos, tempQuat, tempSca );
+
+							for ( let j = 0, l = instances.length; j < l; j ++ ) {
+
+								const instance = instances[ j ];
+								instance.setMatrixAt( i, tempMat );
+
+							}
+
+						}
+
+						model.batchTable = batchTable;
+						model.featureTable = featureTable;
+						resolve( model );
+
+					}, reject );
+
+				} );
+
+			} );
+
+	}
+
+}