-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathViewer.js
More file actions
174 lines (153 loc) · 5.59 KB
/
Viewer.js
File metadata and controls
174 lines (153 loc) · 5.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import * as THREE from 'three';
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
import { GLTFExporter } from 'three/addons/exporters/GLTFExporter.js';
export class Viewer {
constructor() {
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.01, 100 );
camera.position.z = -0.25; camera.position.y = 0.2
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
const controls = new OrbitControls(camera, renderer.domElement);
controls.update();
this.controls = controls
controls.minDistance = 0.1;
controls.maxDistance = 5;
const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
scene.add( directionalLight );
const light = new THREE.AmbientLight( 0x404040 );
scene.add( light );
window.addEventListener('resize', this.resizeCanvas.bind(this));
this.scene = scene
this.camera = camera
this.renderer = renderer
this.render();
this.resizeCanvas();
}
render() {
const { renderer, camera, scene } = this
requestAnimationFrame( this.render.bind(this) );
renderer.render( scene, camera );
}
resizeCanvas(){
const { camera, renderer } = this
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
async loadGLTFTiles(urlArray, logFn) {
// Resizing/recentering code inspired by by gltf-viewer
// https://github.com/donmccurdy/three-gltf-viewer/blob/de78a07180e4141b0b87a0ff4572bc4f7aafec56/src/viewer.js#L246
const { scene, controls, camera } = this
// Remove any previous 3D Tiles we were rendering
if (this.tilesContainer) {
scene.remove(this.tilesContainer)
this.tilesContainer = null
}
const tilesContainer = new THREE.Object3D()
// Fetch individual glTF's and add them to the scene
const gltfArray = []
for (let i = 0; i < urlArray.length; i++) {
const url = urlArray[i]
if (logFn) logFn(`Fetching glTF ${i}/${urlArray.length}`)
const gltf = await fetchGltf(url)
gltfArray.push(gltf)
tilesContainer.add(gltf.scene)
}
if (logFn) logFn(`Normalizing & stitching together ${urlArray.length} glTF's`)
// Re-center the tiles around 0/0/0
const box = new THREE.Box3().setFromObject(tilesContainer)
const size = box.getSize(new THREE.Vector3()).length()
const center = box.getCenter(new THREE.Vector3())
for (let gltf of gltfArray) {
const object = gltf.scene.children[0]
const offset = object.position.clone().sub(center)
object.position.set(offset.x, offset.y, offset.z)
}
scene.add(tilesContainer)
// Calculate the quaternion to rotate the up vector to face north (positive Y-axis)
/*
- The tiles are positioned in ECEF at some position on the surface of the Earth
- They are oriented "up" in this position
- We (1) compute this vector and (2) reverse this rotation
- this way it's pointing up in the XYZ space centered around 0,0,0
*/
const upVector = center.normalize() // the direction the tiles are facing
const targetNorthVector = new THREE.Vector3(0, 1, 0); // the "up" direction we want
const rotationAxis = new THREE.Vector3();
rotationAxis.crossVectors(upVector, targetNorthVector).normalize();
const dotProduct = upVector.dot(targetNorthVector);
const rotationAngle = Math.acos(dotProduct);
const quaternion = new THREE.Quaternion();
quaternion.setFromAxisAngle(rotationAxis, rotationAngle);
tilesContainer.quaternion.multiply(quaternion) // rotate all the tiles
const newScale = (1 / size) // re-scale to [0, 1]
tilesContainer.scale.set(newScale, newScale, newScale)
controls.update()
// Save the tiles we added to remove them next time we add new tiles
this.tilesContainer = tilesContainer
this.gltfArray = gltfArray
}
generateCombineGltf(r_texSize) {
exportGLTF(this.scene, {
//maxTextureSize: 4096
maxTextureSize: r_texSize
})
}
}
const THREE_PATH = `https://unpkg.com/three@0.${THREE.REVISION}.x`
const DRACO_LOADER = new DRACOLoader( ).setDecoderPath( `${THREE_PATH}/examples/jsm/libs/draco/gltf/` );
const gltfLoader = new GLTFLoader();
gltfLoader.setDRACOLoader( DRACO_LOADER );
function fetchGltf(url) {
return new Promise((resolve, reject) => {
gltfLoader.load(url,
(gltf) => {
resolve(gltf)
}, () => {},
(error) => {
reject(error)
})
})
}
function exportGLTF( input, params ) {
const gltfExporter = new GLTFExporter();
const options = {
trs: params.trs,
onlyVisible: params.onlyVisible,
binary: params.binary,
maxTextureSize: params.maxTextureSize
};
gltfExporter.parse(
input,
function ( result ) {
if ( result instanceof ArrayBuffer ) {
saveArrayBuffer( result, 'combined_3d_tiles.glb' );
} else {
const output = JSON.stringify( result, null, 2 );
saveString( output, 'combined_3d_tiles.gltf' );
}
},
function ( error ) {
console.log( 'An error happened during parsing', error );
},
options
);
}
const link = document.createElement( 'a' );
link.style.display = 'none';
document.body.appendChild( link );
function save( blob, filename ) {
link.href = URL.createObjectURL( blob );
link.download = filename;
link.click();
}
function saveString( text, filename ) {
save( new Blob( [ text ], { type: 'text/plain' } ), filename );
}
function saveArrayBuffer( buffer, filename ) {
save( new Blob( [ buffer ], { type: 'application/octet-stream' } ), filename );
}