@inweb/viewer-three 25.3.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +63 -0
- package/dist/viewer-three.js +42841 -0
- package/dist/viewer-three.js.map +1 -0
- package/dist/viewer-three.min.js +6 -0
- package/dist/viewer-three.module.js +1102 -0
- package/dist/viewer-three.module.js.map +1 -0
- package/lib/ThreejsViewer/IComponent.d.ts +3 -0
- package/lib/ThreejsViewer/ThreejsViewer.d.ts +54 -0
- package/lib/ThreejsViewer/components/BackgroundComponent.d.ts +10 -0
- package/lib/ThreejsViewer/components/DefaultCameraPositionComponent.d.ts +9 -0
- package/lib/ThreejsViewer/components/LightComponent.d.ts +10 -0
- package/lib/ThreejsViewer/components/ObjectSelectionComponent.d.ts +16 -0
- package/lib/ThreejsViewer/components/RenderLoopComponent.d.ts +9 -0
- package/lib/ThreejsViewer/components/ResizeCanvasComponent.d.ts +9 -0
- package/lib/ThreejsViewer/draggers/ClippingPlaneDragger.d.ts +17 -0
- package/lib/ThreejsViewer/draggers/OrbitDragger.d.ts +10 -0
- package/lib/ThreejsViewer/draggers/PanDragger.d.ts +5 -0
- package/lib/ThreejsViewer/draggers/WalkDragger.d.ts +39 -0
- package/lib/ThreejsViewer/draggers/ZoomDragger.d.ts +5 -0
- package/lib/ThreejsViewer/loaders/GLTFLoadingManager.d.ts +11 -0
- package/lib/index.d.ts +2 -0
- package/package.json +42 -0
- package/src/ThreejsViewer/IComponent.ts +3 -0
- package/src/ThreejsViewer/ThreejsViewer.ts +369 -0
- package/src/ThreejsViewer/components/BackgroundComponent.ts +57 -0
- package/src/ThreejsViewer/components/DefaultCameraPositionComponent.ts +66 -0
- package/src/ThreejsViewer/components/LightComponent.ts +48 -0
- package/src/ThreejsViewer/components/ObjectSelectionComponent.ts +105 -0
- package/src/ThreejsViewer/components/RenderLoopComponent.ts +44 -0
- package/src/ThreejsViewer/components/ResizeCanvasComponent.ts +53 -0
- package/src/ThreejsViewer/draggers/ClippingPlaneDragger.ts +120 -0
- package/src/ThreejsViewer/draggers/OrbitDragger.ts +61 -0
- package/src/ThreejsViewer/draggers/PanDragger.ts +34 -0
- package/src/ThreejsViewer/draggers/WalkDragger.ts +260 -0
- package/src/ThreejsViewer/draggers/ZoomDragger.ts +34 -0
- package/src/ThreejsViewer/loaders/GLTFLoadingManager.ts +69 -0
- package/src/index.ts +25 -0
|
@@ -0,0 +1,1102 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
|
|
3
|
+
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
4
|
+
|
|
5
|
+
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
|
|
6
|
+
|
|
7
|
+
import { RoomEnvironment } from "three/examples/jsm/environments/RoomEnvironment.js";
|
|
8
|
+
|
|
9
|
+
class Commands {
|
|
10
|
+
constructor() {
|
|
11
|
+
this._commands = new Map;
|
|
12
|
+
}
|
|
13
|
+
registerCommand(id, handler, description, thisArg) {
|
|
14
|
+
this._commands.set(id, {
|
|
15
|
+
id: id,
|
|
16
|
+
handler: handler,
|
|
17
|
+
thisArg: thisArg,
|
|
18
|
+
description: description
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
registerCommandAlias(id, alias) {
|
|
22
|
+
this.registerCommand(alias, ((viewer, ...args) => this.executeCommand(id, viewer, ...args)));
|
|
23
|
+
}
|
|
24
|
+
getCommand(id) {
|
|
25
|
+
return this._commands.get(id);
|
|
26
|
+
}
|
|
27
|
+
getCommands() {
|
|
28
|
+
const map = new Map;
|
|
29
|
+
this._commands.forEach(((value, key) => map.set(key, value)));
|
|
30
|
+
return map;
|
|
31
|
+
}
|
|
32
|
+
executeCommand(id, viewer, ...args) {
|
|
33
|
+
const command = this._commands.get(id);
|
|
34
|
+
if (!command) {
|
|
35
|
+
if (viewer) {
|
|
36
|
+
const isDraggerCommand = viewer.draggers.includes(id);
|
|
37
|
+
if (isDraggerCommand) return viewer.setActiveDragger(id);
|
|
38
|
+
}
|
|
39
|
+
console.warn(`Command '${id}' not found`);
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
const {handler: handler, thisArg: thisArg} = command;
|
|
43
|
+
const result = handler.apply(thisArg, [ viewer, ...args ]);
|
|
44
|
+
viewer === null || viewer === void 0 ? void 0 : viewer.emit({
|
|
45
|
+
type: "command",
|
|
46
|
+
data: id,
|
|
47
|
+
args: args
|
|
48
|
+
});
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const _commands = new Map;
|
|
54
|
+
|
|
55
|
+
function commands(viewerType = "") {
|
|
56
|
+
let result = _commands.get(viewerType);
|
|
57
|
+
if (!result) {
|
|
58
|
+
result = new Commands;
|
|
59
|
+
_commands.set(viewerType, result);
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
commands("").registerCommand("noop", (() => {}));
|
|
65
|
+
|
|
66
|
+
commands("VisualizeJS").registerCommand("noop", (() => {}));
|
|
67
|
+
|
|
68
|
+
commands("ThreeJS").registerCommand("noop", (() => {}));
|
|
69
|
+
|
|
70
|
+
class Options {
|
|
71
|
+
constructor(emitter) {
|
|
72
|
+
this._emitter = emitter;
|
|
73
|
+
this._data = Options.defaults();
|
|
74
|
+
this.loadFromStorage();
|
|
75
|
+
}
|
|
76
|
+
static defaults() {
|
|
77
|
+
return {
|
|
78
|
+
showWCS: true,
|
|
79
|
+
cameraAnimation: true,
|
|
80
|
+
antialiasing: true,
|
|
81
|
+
groundShadow: false,
|
|
82
|
+
shadows: false,
|
|
83
|
+
cameraAxisXSpeed: 4,
|
|
84
|
+
cameraAxisYSpeed: 1,
|
|
85
|
+
ambientOcclusion: false,
|
|
86
|
+
enableStreamingMode: true,
|
|
87
|
+
enablePartialMode: false,
|
|
88
|
+
memoryLimit: 3294967296,
|
|
89
|
+
cuttingPlaneFillColor: {
|
|
90
|
+
red: 255,
|
|
91
|
+
green: 152,
|
|
92
|
+
blue: 0
|
|
93
|
+
},
|
|
94
|
+
edgesColor: {
|
|
95
|
+
r: 255,
|
|
96
|
+
g: 152,
|
|
97
|
+
b: 0
|
|
98
|
+
},
|
|
99
|
+
facesColor: {
|
|
100
|
+
r: 255,
|
|
101
|
+
g: 152,
|
|
102
|
+
b: 0
|
|
103
|
+
},
|
|
104
|
+
edgesVisibility: true,
|
|
105
|
+
edgesOverlap: true,
|
|
106
|
+
facesOverlap: false,
|
|
107
|
+
facesTransparancy: 200,
|
|
108
|
+
enableCustomHighlight: true,
|
|
109
|
+
sceneGraph: false,
|
|
110
|
+
edgeModel: true,
|
|
111
|
+
reverseZoomWheel: false,
|
|
112
|
+
enableZoomWheel: true,
|
|
113
|
+
enableGestures: true,
|
|
114
|
+
geometryType: "vsfx"
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
notifierChangeEvent() {
|
|
118
|
+
if (this._emitter !== undefined) {
|
|
119
|
+
this.saveToStorage();
|
|
120
|
+
this._emitter.emit({
|
|
121
|
+
type: "optionschange",
|
|
122
|
+
data: this
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
saveToStorage() {
|
|
127
|
+
if (typeof window !== "undefined") try {
|
|
128
|
+
localStorage.setItem("od-client-settings", JSON.stringify(this.data));
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error("Cannot save client settings.", error);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
loadFromStorage() {
|
|
134
|
+
if (typeof window !== "undefined") try {
|
|
135
|
+
const item = localStorage.getItem("od-client-settings");
|
|
136
|
+
if (item) {
|
|
137
|
+
const data = JSON.parse(item);
|
|
138
|
+
this.data = {
|
|
139
|
+
...data
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
} catch (error) {
|
|
143
|
+
console.error("Cannot load client settings.", error);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
resetToDefaults(fields) {
|
|
147
|
+
if (fields !== undefined) {
|
|
148
|
+
const defaults = Options.defaults();
|
|
149
|
+
const resetData = fields.reduce(((acc, field) => {
|
|
150
|
+
acc[field] = defaults[field];
|
|
151
|
+
return acc;
|
|
152
|
+
}), {});
|
|
153
|
+
this.data = {
|
|
154
|
+
...this.data,
|
|
155
|
+
...resetData
|
|
156
|
+
};
|
|
157
|
+
} else {
|
|
158
|
+
this.data = {
|
|
159
|
+
...this.data,
|
|
160
|
+
...Options.defaults()
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
get data() {
|
|
165
|
+
return this._data;
|
|
166
|
+
}
|
|
167
|
+
set data(value) {
|
|
168
|
+
const sceneGraph = value.enablePartialMode ? false : value.sceneGraph;
|
|
169
|
+
this._data = {
|
|
170
|
+
...Options.defaults(),
|
|
171
|
+
...this._data,
|
|
172
|
+
...value,
|
|
173
|
+
sceneGraph: sceneGraph
|
|
174
|
+
};
|
|
175
|
+
this.notifierChangeEvent();
|
|
176
|
+
}
|
|
177
|
+
get showWCS() {
|
|
178
|
+
return this._data.showWCS;
|
|
179
|
+
}
|
|
180
|
+
set showWCS(value) {
|
|
181
|
+
this._data.showWCS = value;
|
|
182
|
+
this.notifierChangeEvent();
|
|
183
|
+
}
|
|
184
|
+
get cameraAnimation() {
|
|
185
|
+
return this._data.cameraAnimation;
|
|
186
|
+
}
|
|
187
|
+
set cameraAnimation(value) {
|
|
188
|
+
this._data.cameraAnimation = value;
|
|
189
|
+
this.notifierChangeEvent();
|
|
190
|
+
}
|
|
191
|
+
get antialiasing() {
|
|
192
|
+
return this._data.antialiasing;
|
|
193
|
+
}
|
|
194
|
+
set antialiasing(value) {
|
|
195
|
+
this._data.antialiasing = value;
|
|
196
|
+
this.notifierChangeEvent();
|
|
197
|
+
}
|
|
198
|
+
get groundShadow() {
|
|
199
|
+
return this._data.groundShadow;
|
|
200
|
+
}
|
|
201
|
+
set groundShadow(value) {
|
|
202
|
+
this._data.groundShadow = value;
|
|
203
|
+
this.notifierChangeEvent();
|
|
204
|
+
}
|
|
205
|
+
get shadows() {
|
|
206
|
+
return this._data.shadows;
|
|
207
|
+
}
|
|
208
|
+
set shadows(value) {
|
|
209
|
+
this._data.shadows = value;
|
|
210
|
+
this.notifierChangeEvent();
|
|
211
|
+
}
|
|
212
|
+
get cameraAxisXSpeed() {
|
|
213
|
+
return this._data.cameraAxisXSpeed;
|
|
214
|
+
}
|
|
215
|
+
set cameraAxisXSpeed(value) {
|
|
216
|
+
this._data.cameraAxisXSpeed = value;
|
|
217
|
+
this.notifierChangeEvent();
|
|
218
|
+
}
|
|
219
|
+
get cameraAxisYSpeed() {
|
|
220
|
+
return this._data.cameraAxisYSpeed;
|
|
221
|
+
}
|
|
222
|
+
set cameraAxisYSpeed(value) {
|
|
223
|
+
this.cameraAxisYSpeed = value;
|
|
224
|
+
this.notifierChangeEvent();
|
|
225
|
+
}
|
|
226
|
+
get ambientOcclusion() {
|
|
227
|
+
return this._data.ambientOcclusion;
|
|
228
|
+
}
|
|
229
|
+
set ambientOcclusion(value) {
|
|
230
|
+
this._data.ambientOcclusion = value;
|
|
231
|
+
this.notifierChangeEvent();
|
|
232
|
+
}
|
|
233
|
+
get enableStreamingMode() {
|
|
234
|
+
return this._data.enableStreamingMode;
|
|
235
|
+
}
|
|
236
|
+
set enableStreamingMode(value) {
|
|
237
|
+
this._data.enableStreamingMode = value;
|
|
238
|
+
if (this._data.enableStreamingMode) {
|
|
239
|
+
this._data.enablePartialMode = false;
|
|
240
|
+
}
|
|
241
|
+
this.notifierChangeEvent();
|
|
242
|
+
}
|
|
243
|
+
get enablePartialMode() {
|
|
244
|
+
return this._data.enablePartialMode;
|
|
245
|
+
}
|
|
246
|
+
set enablePartialMode(value) {
|
|
247
|
+
this._data.enablePartialMode = value;
|
|
248
|
+
if (value) this._data.sceneGraph = false;
|
|
249
|
+
this.notifierChangeEvent();
|
|
250
|
+
}
|
|
251
|
+
get memoryLimit() {
|
|
252
|
+
return this._data.memoryLimit;
|
|
253
|
+
}
|
|
254
|
+
set memoryLimit(value) {
|
|
255
|
+
this._data.memoryLimit = value;
|
|
256
|
+
this.notifierChangeEvent();
|
|
257
|
+
}
|
|
258
|
+
get cuttingPlaneFillColor() {
|
|
259
|
+
return this._data.cuttingPlaneFillColor;
|
|
260
|
+
}
|
|
261
|
+
set cuttingPlaneFillColor(value) {
|
|
262
|
+
this._data.cuttingPlaneFillColor = value;
|
|
263
|
+
this.notifierChangeEvent();
|
|
264
|
+
}
|
|
265
|
+
get edgesColor() {
|
|
266
|
+
return this._data.edgesColor;
|
|
267
|
+
}
|
|
268
|
+
set edgesColor(value) {
|
|
269
|
+
this._data.edgesColor = value;
|
|
270
|
+
this.notifierChangeEvent();
|
|
271
|
+
}
|
|
272
|
+
get facesColor() {
|
|
273
|
+
return this._data.facesColor;
|
|
274
|
+
}
|
|
275
|
+
set facesColor(value) {
|
|
276
|
+
this._data.facesColor = value;
|
|
277
|
+
this.notifierChangeEvent();
|
|
278
|
+
}
|
|
279
|
+
get edgesVisibility() {
|
|
280
|
+
return this._data.edgesVisibility;
|
|
281
|
+
}
|
|
282
|
+
set edgesVisibility(value) {
|
|
283
|
+
this._data.edgesVisibility = value;
|
|
284
|
+
this.notifierChangeEvent();
|
|
285
|
+
}
|
|
286
|
+
get edgesOverlap() {
|
|
287
|
+
return this._data.edgesOverlap;
|
|
288
|
+
}
|
|
289
|
+
set edgesOverlap(value) {
|
|
290
|
+
this._data.edgesOverlap = value;
|
|
291
|
+
this.notifierChangeEvent();
|
|
292
|
+
}
|
|
293
|
+
get facesOverlap() {
|
|
294
|
+
return this._data.facesOverlap;
|
|
295
|
+
}
|
|
296
|
+
set facesOverlap(value) {
|
|
297
|
+
this._data.facesOverlap = value;
|
|
298
|
+
this.notifierChangeEvent();
|
|
299
|
+
}
|
|
300
|
+
get facesTransparancy() {
|
|
301
|
+
return this._data.facesTransparancy;
|
|
302
|
+
}
|
|
303
|
+
set facesTransparancy(value) {
|
|
304
|
+
this._data.facesTransparancy = value;
|
|
305
|
+
this.notifierChangeEvent();
|
|
306
|
+
}
|
|
307
|
+
get enableCustomHighlight() {
|
|
308
|
+
return this._data.enableCustomHighlight;
|
|
309
|
+
}
|
|
310
|
+
set enableCustomHighlight(value) {
|
|
311
|
+
this._data.enableCustomHighlight = value;
|
|
312
|
+
this.notifierChangeEvent();
|
|
313
|
+
}
|
|
314
|
+
get sceneGraph() {
|
|
315
|
+
return this._data.sceneGraph;
|
|
316
|
+
}
|
|
317
|
+
set sceneGraph(value) {
|
|
318
|
+
this._data.sceneGraph = value;
|
|
319
|
+
if (value) this._data.enablePartialMode = false;
|
|
320
|
+
this.notifierChangeEvent();
|
|
321
|
+
}
|
|
322
|
+
get edgeModel() {
|
|
323
|
+
return Boolean(this._data.edgeModel);
|
|
324
|
+
}
|
|
325
|
+
set edgeModel(value) {
|
|
326
|
+
this._data.edgeModel = Boolean(value);
|
|
327
|
+
this.notifierChangeEvent();
|
|
328
|
+
}
|
|
329
|
+
get reverseZoomWheel() {
|
|
330
|
+
return this._data.reverseZoomWheel;
|
|
331
|
+
}
|
|
332
|
+
set reverseZoomWheel(value) {
|
|
333
|
+
this._data.reverseZoomWheel = !!value;
|
|
334
|
+
this.notifierChangeEvent();
|
|
335
|
+
}
|
|
336
|
+
get enableZoomWheel() {
|
|
337
|
+
return this._data.enableZoomWheel;
|
|
338
|
+
}
|
|
339
|
+
set enableZoomWheel(value) {
|
|
340
|
+
this._data.enableZoomWheel = !!value;
|
|
341
|
+
this.notifierChangeEvent();
|
|
342
|
+
}
|
|
343
|
+
get enableGestures() {
|
|
344
|
+
return this._data.enableGestures;
|
|
345
|
+
}
|
|
346
|
+
set enableGestures(value) {
|
|
347
|
+
this._data.enableGestures = !!value;
|
|
348
|
+
this.notifierChangeEvent();
|
|
349
|
+
}
|
|
350
|
+
get geometryType() {
|
|
351
|
+
return this._data.geometryType;
|
|
352
|
+
}
|
|
353
|
+
set geometryType(value) {
|
|
354
|
+
this._data.geometryType = value;
|
|
355
|
+
this.notifierChangeEvent();
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const CANVAS_EVENTS = [ "click", "dblclick", "mousedown", "mousemove", "mouseup", "mouseleave", "pointerdown", "pointermove", "pointerup", "pointerleave", "pointercancel", "wheel", "touchstart", "touchmove", "touchend", "touchcancel" ];
|
|
360
|
+
|
|
361
|
+
class EventEmitter2 {
|
|
362
|
+
constructor() {
|
|
363
|
+
this._listeners = undefined;
|
|
364
|
+
}
|
|
365
|
+
addEventListener(type, listener) {
|
|
366
|
+
if (this._listeners === undefined) this._listeners = {};
|
|
367
|
+
if (this._listeners[type] === undefined) this._listeners[type] = [];
|
|
368
|
+
this._listeners[type].push(listener);
|
|
369
|
+
return this;
|
|
370
|
+
}
|
|
371
|
+
removeEventListener(type, listener) {
|
|
372
|
+
if (this._listeners === undefined) return this;
|
|
373
|
+
if (this._listeners[type] === undefined) return this;
|
|
374
|
+
const listeners = this._listeners[type].filter((x => x !== listener));
|
|
375
|
+
this._listeners[type] = listeners.length === 0 ? undefined : listeners;
|
|
376
|
+
return this;
|
|
377
|
+
}
|
|
378
|
+
removeAllListeners(type) {
|
|
379
|
+
if (type) this._listeners[type] = undefined; else this._listeners = undefined;
|
|
380
|
+
return this;
|
|
381
|
+
}
|
|
382
|
+
emitEvent(event) {
|
|
383
|
+
if (this._listeners === undefined) return false;
|
|
384
|
+
if (this._listeners[event.type] === undefined) return false;
|
|
385
|
+
const invoke = this._listeners[event.type].slice();
|
|
386
|
+
invoke.forEach((listener => listener.call(this, event)));
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
389
|
+
on(type, listener) {
|
|
390
|
+
return this.addEventListener(type, listener);
|
|
391
|
+
}
|
|
392
|
+
off(type, listener) {
|
|
393
|
+
return this.removeEventListener(type, listener);
|
|
394
|
+
}
|
|
395
|
+
emit(event, ...args) {
|
|
396
|
+
if (typeof event === "string") return this.emitEvent({
|
|
397
|
+
type: event,
|
|
398
|
+
args: args
|
|
399
|
+
}); else if (typeof event === "object") return this.emitEvent(event); else return false;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
class GLTFLoadingManager extends THREE.LoadingManager {
|
|
404
|
+
constructor(file, externalData = new Map, params = {}) {
|
|
405
|
+
super();
|
|
406
|
+
this.path = "";
|
|
407
|
+
this.resourcePath = "";
|
|
408
|
+
this.fileURL = "";
|
|
409
|
+
this.dataURLs = new Map;
|
|
410
|
+
this.path = params.path || "";
|
|
411
|
+
if (typeof file === "string") {
|
|
412
|
+
this.fileURL = file;
|
|
413
|
+
this.resourcePath = THREE.LoaderUtils.extractUrlBase(file);
|
|
414
|
+
} else {
|
|
415
|
+
externalData.forEach(((value, key) => this.fileURL = value === file ? key : this.fileURL));
|
|
416
|
+
externalData.set(this.fileURL, file);
|
|
417
|
+
}
|
|
418
|
+
externalData.forEach(((value, key) => {
|
|
419
|
+
let dataURL;
|
|
420
|
+
if (typeof value === "string") dataURL = value; else dataURL = URL.createObjectURL(new Blob([ value ]));
|
|
421
|
+
this.dataURLs.set(key, dataURL);
|
|
422
|
+
}));
|
|
423
|
+
this.setURLModifier((url => {
|
|
424
|
+
const key = decodeURI(url).replace(this.path, "").replace(this.resourcePath, "").replace(/^(\.?\/)/, "");
|
|
425
|
+
const dataURL = this.dataURLs.get(key);
|
|
426
|
+
return dataURL !== null && dataURL !== void 0 ? dataURL : url;
|
|
427
|
+
}));
|
|
428
|
+
}
|
|
429
|
+
dispose() {
|
|
430
|
+
this.dataURLs.forEach(URL.revokeObjectURL);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
class OrbitDragger extends OrbitControls {
|
|
435
|
+
constructor(viewer) {
|
|
436
|
+
super(viewer.camera, viewer.canvas);
|
|
437
|
+
this.geometryEnd = event => {
|
|
438
|
+
const {data: scene} = event;
|
|
439
|
+
const box = (new THREE.Box3).setFromObject(scene);
|
|
440
|
+
const size = box.getSize(new THREE.Vector3).length();
|
|
441
|
+
this.maxDistance = size * 10;
|
|
442
|
+
this.update();
|
|
443
|
+
};
|
|
444
|
+
this.updateViewer = () => {
|
|
445
|
+
this.viewer.update();
|
|
446
|
+
};
|
|
447
|
+
this.mouseButtons = {
|
|
448
|
+
LEFT: THREE.MOUSE.ROTATE,
|
|
449
|
+
MIDDLE: THREE.MOUSE.PAN,
|
|
450
|
+
RIGHT: THREE.MOUSE.PAN
|
|
451
|
+
};
|
|
452
|
+
this.touches = {
|
|
453
|
+
ONE: THREE.TOUCH.ROTATE,
|
|
454
|
+
TWO: THREE.TOUCH.DOLLY_PAN
|
|
455
|
+
};
|
|
456
|
+
this.screenSpacePanning = true;
|
|
457
|
+
this.rotateSpeed = .33;
|
|
458
|
+
this.viewer = viewer;
|
|
459
|
+
this.viewer.addEventListener("geometryend", this.geometryEnd);
|
|
460
|
+
this.addEventListener("change", this.updateViewer);
|
|
461
|
+
}
|
|
462
|
+
dispose() {
|
|
463
|
+
this.removeEventListener("change", this.updateViewer);
|
|
464
|
+
this.viewer.removeEventListener("geometryend", this.geometryEnd);
|
|
465
|
+
super.dispose();
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
class PanDragger extends OrbitDragger {
|
|
470
|
+
constructor(viewer) {
|
|
471
|
+
super(viewer);
|
|
472
|
+
this.mouseButtons = {
|
|
473
|
+
LEFT: THREE.MOUSE.PAN,
|
|
474
|
+
MIDDLE: THREE.MOUSE.PAN,
|
|
475
|
+
RIGHT: THREE.MOUSE.PAN
|
|
476
|
+
};
|
|
477
|
+
this.touches = {
|
|
478
|
+
ONE: THREE.TOUCH.PAN,
|
|
479
|
+
TWO: THREE.TOUCH.DOLLY_ROTATE
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
class ZoomDragger extends OrbitDragger {
|
|
485
|
+
constructor(viewer) {
|
|
486
|
+
super(viewer);
|
|
487
|
+
this.mouseButtons = {
|
|
488
|
+
LEFT: THREE.MOUSE.DOLLY,
|
|
489
|
+
MIDDLE: THREE.MOUSE.PAN,
|
|
490
|
+
RIGHT: THREE.MOUSE.PAN
|
|
491
|
+
};
|
|
492
|
+
this.touches = {
|
|
493
|
+
ONE: THREE.TOUCH.DOLLY_PAN,
|
|
494
|
+
TWO: THREE.TOUCH.DOLLY_PAN
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
class WalkDragger {
|
|
500
|
+
constructor(viewer) {
|
|
501
|
+
this.onKeyDown = event => {
|
|
502
|
+
switch (event.code) {
|
|
503
|
+
case "KeyW":
|
|
504
|
+
if (event.shiftKey) {
|
|
505
|
+
this.speed.z = this.walkSpeed * this.boostSpeed;
|
|
506
|
+
} else {
|
|
507
|
+
this.speed.z = this.walkSpeed;
|
|
508
|
+
}
|
|
509
|
+
break;
|
|
510
|
+
|
|
511
|
+
case "KeyS":
|
|
512
|
+
if (event.shiftKey) {
|
|
513
|
+
this.speed.z = -this.walkSpeed * this.boostSpeed;
|
|
514
|
+
} else {
|
|
515
|
+
this.speed.z = -this.walkSpeed;
|
|
516
|
+
}
|
|
517
|
+
break;
|
|
518
|
+
|
|
519
|
+
case "KeyA":
|
|
520
|
+
if (event.shiftKey) {
|
|
521
|
+
this.speed.x = this.walkSpeed * this.boostSpeed;
|
|
522
|
+
} else {
|
|
523
|
+
this.speed.x = this.walkSpeed;
|
|
524
|
+
}
|
|
525
|
+
break;
|
|
526
|
+
|
|
527
|
+
case "KeyD":
|
|
528
|
+
if (event.shiftKey) {
|
|
529
|
+
this.speed.x = -this.walkSpeed * this.boostSpeed;
|
|
530
|
+
} else {
|
|
531
|
+
this.speed.x = -this.walkSpeed;
|
|
532
|
+
}
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
this.onKeyUp = event => {
|
|
537
|
+
switch (event.code) {
|
|
538
|
+
case "KeyW":
|
|
539
|
+
this.speed.z = 0;
|
|
540
|
+
break;
|
|
541
|
+
|
|
542
|
+
case "KeyS":
|
|
543
|
+
this.speed.z = 0;
|
|
544
|
+
break;
|
|
545
|
+
|
|
546
|
+
case "KeyA":
|
|
547
|
+
this.speed.x = 0;
|
|
548
|
+
break;
|
|
549
|
+
|
|
550
|
+
case "KeyD":
|
|
551
|
+
this.speed.x = 0;
|
|
552
|
+
break;
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
this.onMouseDown = event => {
|
|
556
|
+
const {clientX: clientX, clientY: clientY} = event;
|
|
557
|
+
this.mouseStart.set(clientX, clientY);
|
|
558
|
+
this.mouseDelta.set(0, 0);
|
|
559
|
+
this.quaternion.copy(this.viewer.camera.quaternion);
|
|
560
|
+
this.viewer.addEventListener("mousemove", this.onMouseMove);
|
|
561
|
+
};
|
|
562
|
+
this.onMouseMove = event => {
|
|
563
|
+
const {clientX: clientX, clientY: clientY} = event;
|
|
564
|
+
this.mouseDelta.set(this.mouseStart.x - clientX, this.mouseStart.y - clientY);
|
|
565
|
+
this.rotateCamera(this.mouseDelta);
|
|
566
|
+
};
|
|
567
|
+
this.onMouseUp = event => {
|
|
568
|
+
this.speed.set(0, 0, 0);
|
|
569
|
+
this.mouseStart.set(0, 0);
|
|
570
|
+
this.mouseDelta.set(0, 0);
|
|
571
|
+
this.quaternion.copy(this.viewer.camera.quaternion);
|
|
572
|
+
this.viewer.removeEventListener("mousemove", this.onMouseMove);
|
|
573
|
+
};
|
|
574
|
+
this.onMouseWheel = event => {
|
|
575
|
+
event.preventDefault();
|
|
576
|
+
if (-event.deltaY < 0) {
|
|
577
|
+
this.walkSpeed = Math.max(.001, this.walkSpeed - 1);
|
|
578
|
+
} else if (-event.deltaY > 0) {
|
|
579
|
+
this.walkSpeed++;
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
this.onContextMenu = event => {
|
|
583
|
+
console.log(event);
|
|
584
|
+
event.preventDefault();
|
|
585
|
+
event.stopImmediatePropagation();
|
|
586
|
+
};
|
|
587
|
+
this.onTouchStart = event => {
|
|
588
|
+
if (event.touches.length > 1) {
|
|
589
|
+
this.touchStartDistance = this.getTouchsDistance(event.touches);
|
|
590
|
+
} else {
|
|
591
|
+
const {clientX: clientX, clientY: clientY} = event.touches[0];
|
|
592
|
+
this.mouseStart.set(clientX, clientY);
|
|
593
|
+
this.mouseDelta.set(0, 0);
|
|
594
|
+
this.quaternion.copy(this.viewer.camera.quaternion);
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
this.onTouchEnd = event => {
|
|
598
|
+
if (event.touches.length === 0) {
|
|
599
|
+
this.touchStartDistance = 0;
|
|
600
|
+
}
|
|
601
|
+
this.speed.set(0, 0, 0);
|
|
602
|
+
this.mouseStart.set(0, 0);
|
|
603
|
+
this.mouseDelta.set(0, 0);
|
|
604
|
+
this.quaternion.copy(this.viewer.camera.quaternion);
|
|
605
|
+
};
|
|
606
|
+
this.onTouchMove = event => {
|
|
607
|
+
if (event.touches.length === 1 && this.touchStartDistance === 0) {
|
|
608
|
+
const {clientX: clientX, clientY: clientY} = event.touches[0];
|
|
609
|
+
this.mouseDelta.set(this.mouseStart.x - clientX, this.mouseStart.y - clientY);
|
|
610
|
+
this.rotateCamera(this.mouseDelta);
|
|
611
|
+
}
|
|
612
|
+
if (event.touches.length === 2) {
|
|
613
|
+
const distance = this.getTouchsDistance(event.touches);
|
|
614
|
+
this.speed.z = (distance - this.touchStartDistance) / 2;
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
this.onRender = event => {
|
|
618
|
+
this.viewer.camera.matrix.extractBasis(this.xAxis, this.yAxis, this.zAxis);
|
|
619
|
+
this.viewer.camera.position.add(this.zAxis.multiplyScalar(-event.deltaTime * this.speed.z));
|
|
620
|
+
this.viewer.camera.position.add(this.xAxis.multiplyScalar(-event.deltaTime * this.speed.x));
|
|
621
|
+
};
|
|
622
|
+
this.viewer = viewer;
|
|
623
|
+
this._target = new THREE.Vector3;
|
|
624
|
+
this.quaternion = this.viewer.camera.quaternion.clone();
|
|
625
|
+
this.xRotation = new THREE.Quaternion;
|
|
626
|
+
this.yRotation = new THREE.Quaternion;
|
|
627
|
+
this.mouseStart = new THREE.Vector2;
|
|
628
|
+
this.mouseDelta = new THREE.Vector2;
|
|
629
|
+
this.xAxis = new THREE.Vector3;
|
|
630
|
+
this.yAxis = new THREE.Vector3;
|
|
631
|
+
this.zAxis = new THREE.Vector3;
|
|
632
|
+
this.yRotationAxis = new THREE.Vector3(1, 0, 0);
|
|
633
|
+
this.walkSpeed = 1;
|
|
634
|
+
this.boostSpeed = 5;
|
|
635
|
+
this.speed = new THREE.Vector3;
|
|
636
|
+
this._maxDistance = 1;
|
|
637
|
+
this.touchStartDistance = 0;
|
|
638
|
+
this.viewer.addEventListener("render", this.onRender);
|
|
639
|
+
this.viewer.addEventListener("contextmenu", this.onContextMenu);
|
|
640
|
+
this.viewer.addEventListener("mousedown", this.onMouseDown);
|
|
641
|
+
this.viewer.addEventListener("mouseup", this.onMouseUp);
|
|
642
|
+
this.viewer.addEventListener("touchstart", this.onTouchStart);
|
|
643
|
+
this.viewer.addEventListener("touchmove", this.onTouchMove);
|
|
644
|
+
this.viewer.addEventListener("touchend", this.onTouchEnd);
|
|
645
|
+
this.viewer.addEventListener("wheel", this.onMouseWheel);
|
|
646
|
+
document.addEventListener("keydown", this.onKeyDown);
|
|
647
|
+
document.addEventListener("keyup", this.onKeyUp);
|
|
648
|
+
}
|
|
649
|
+
dispose() {
|
|
650
|
+
this.viewer.removeEventListener("render", this.onRender);
|
|
651
|
+
this.viewer.removeEventListener("contextmenu", this.onContextMenu);
|
|
652
|
+
this.viewer.removeEventListener("mousedown", this.onMouseDown);
|
|
653
|
+
this.viewer.removeEventListener("mouseup", this.onMouseUp);
|
|
654
|
+
this.viewer.removeEventListener("mousemove", this.onMouseMove);
|
|
655
|
+
this.viewer.removeEventListener("touchstart", this.onTouchStart);
|
|
656
|
+
this.viewer.removeEventListener("touchmove", this.onTouchMove);
|
|
657
|
+
this.viewer.removeEventListener("touchend", this.onTouchEnd);
|
|
658
|
+
this.viewer.removeEventListener("wheel", this.onMouseWheel);
|
|
659
|
+
document.removeEventListener("keydown", this.onKeyDown);
|
|
660
|
+
document.removeEventListener("keyup", this.onKeyUp);
|
|
661
|
+
}
|
|
662
|
+
getTouchsDistance(touches) {
|
|
663
|
+
const [start, end] = touches;
|
|
664
|
+
const dx = start.clientX - end.clientX;
|
|
665
|
+
const dy = start.clientY - end.clientY;
|
|
666
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
667
|
+
return distance;
|
|
668
|
+
}
|
|
669
|
+
update() {}
|
|
670
|
+
rotateCamera(delta) {
|
|
671
|
+
const rotateX = Math.PI * delta.x / this.viewer.canvas.clientWidth;
|
|
672
|
+
const rotateY = Math.PI * delta.y / this.viewer.canvas.clientHeight;
|
|
673
|
+
const quaternion = this.quaternion.clone();
|
|
674
|
+
this.xRotation.setFromAxisAngle(this.viewer.camera.up, rotateX);
|
|
675
|
+
this.yRotation.setFromAxisAngle(this.yRotationAxis, rotateY);
|
|
676
|
+
quaternion.premultiply(this.xRotation).multiply(this.yRotation).normalize();
|
|
677
|
+
this.viewer.camera.setRotationFromQuaternion(quaternion);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
class ClippingPlaneDragger {
|
|
682
|
+
constructor(viewer) {
|
|
683
|
+
this.onPointerDown = event => {
|
|
684
|
+
this.viewer.addEventListener("pointermove", this.onPointerMove);
|
|
685
|
+
const {offsetX: offsetX, offsetY: offsetY} = event;
|
|
686
|
+
this.start.set(offsetX, offsetY, .5);
|
|
687
|
+
this.start = this.screenToPlane(this.start);
|
|
688
|
+
this.delta.set(0, 0, 0);
|
|
689
|
+
this.end.set(0, 0, 0);
|
|
690
|
+
};
|
|
691
|
+
this.onPointerUp = event => {
|
|
692
|
+
this.viewer.removeEventListener("pointermove", this.onPointerMove);
|
|
693
|
+
if (this.end.length() === 0) {
|
|
694
|
+
const plane = this.plane;
|
|
695
|
+
plane.normal.multiplyScalar(-1);
|
|
696
|
+
plane.constant *= -1;
|
|
697
|
+
} else {
|
|
698
|
+
const {offsetX: offsetX, offsetY: offsetY} = event;
|
|
699
|
+
this.end.set(offsetX, offsetY, .5);
|
|
700
|
+
this.end = this.screenToPlane(this.end);
|
|
701
|
+
}
|
|
702
|
+
};
|
|
703
|
+
this.onPointerMove = event => {
|
|
704
|
+
const {offsetX: offsetX, offsetY: offsetY} = event;
|
|
705
|
+
this.end.set(offsetX, offsetY, .5);
|
|
706
|
+
this.end = this.screenToPlane(this.end);
|
|
707
|
+
this.delta.copy(this.end).sub(this.start);
|
|
708
|
+
this.start.copy(this.end);
|
|
709
|
+
const plane = this.plane;
|
|
710
|
+
plane.translate(this.delta);
|
|
711
|
+
};
|
|
712
|
+
this.viewer = viewer;
|
|
713
|
+
this.viewer.addEventListener("pointerdown", this.onPointerDown);
|
|
714
|
+
this.viewer.addEventListener("pointerup", this.onPointerUp);
|
|
715
|
+
this.plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
|
|
716
|
+
this.start = new THREE.Vector3;
|
|
717
|
+
this.end = new THREE.Vector3;
|
|
718
|
+
this.delta = new THREE.Vector3;
|
|
719
|
+
if (!this.viewer.renderer.clippingPlanes) this.viewer.renderer.clippingPlanes = [];
|
|
720
|
+
this.viewer.renderer.clippingPlanes.push(this.plane);
|
|
721
|
+
this.planeHelper = new THREE.PlaneHelper(this.plane, 150, 16776960);
|
|
722
|
+
this.viewer.scene.add(this.planeHelper);
|
|
723
|
+
}
|
|
724
|
+
dispose() {
|
|
725
|
+
this.viewer.removeEventListener("pointerdown", this.onPointerDown);
|
|
726
|
+
this.viewer.removeEventListener("pointerup", this.onPointerUp);
|
|
727
|
+
this.viewer.removeEventListener("pointermove", this.onPointerMove);
|
|
728
|
+
this.viewer.renderer.clippingPlanes = this.viewer.renderer.clippingPlanes.filter((plane => this.plane !== plane));
|
|
729
|
+
this.planeHelper.removeFromParent();
|
|
730
|
+
}
|
|
731
|
+
update() {}
|
|
732
|
+
screenToWorld(v) {
|
|
733
|
+
v.x = 2 * v.x / this.viewer.canvas.clientWidth - 1;
|
|
734
|
+
v.y = 1 - 2 * v.y / this.viewer.canvas.clientHeight;
|
|
735
|
+
return v.unproject(this.viewer.camera);
|
|
736
|
+
}
|
|
737
|
+
screenToPlane(v) {
|
|
738
|
+
v = this.screenToWorld(v);
|
|
739
|
+
const direction = v.sub(this.viewer.camera.position).normalize();
|
|
740
|
+
const ray = new THREE.Ray(this.viewer.camera.position, direction);
|
|
741
|
+
let plane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);
|
|
742
|
+
const dot = plane.normal.dot(this.plane.normal);
|
|
743
|
+
if (Math.abs(dot) === 1) {
|
|
744
|
+
plane = new THREE.Plane(new THREE.Vector3(1, 0, 0), 0);
|
|
745
|
+
}
|
|
746
|
+
const result = new THREE.Vector3;
|
|
747
|
+
ray.intersectPlane(plane, result);
|
|
748
|
+
return result;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
class LightComponent {
|
|
753
|
+
constructor(viewer) {
|
|
754
|
+
this.viewer = viewer;
|
|
755
|
+
this.ambientLight = new THREE.AmbientLight(16777215, 0);
|
|
756
|
+
this.viewer.camera.add(this.ambientLight);
|
|
757
|
+
this.directLight = new THREE.DirectionalLight(16777215, 1);
|
|
758
|
+
this.directLight.position.set(.5, 0, .866);
|
|
759
|
+
this.viewer.camera.add(this.directLight);
|
|
760
|
+
}
|
|
761
|
+
dispose() {
|
|
762
|
+
this.ambientLight.removeFromParent();
|
|
763
|
+
this.directLight.removeFromParent();
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
class BackgroundComponent {
|
|
768
|
+
constructor(viewer) {
|
|
769
|
+
this.syncOptions = () => {
|
|
770
|
+
this.backgroundColor.setHex(16777215);
|
|
771
|
+
};
|
|
772
|
+
this.viewer = viewer;
|
|
773
|
+
this.backgroundColor = new THREE.Color(16777215);
|
|
774
|
+
const environment = new RoomEnvironment;
|
|
775
|
+
const pmremGenerator = new THREE.PMREMGenerator(this.viewer.renderer);
|
|
776
|
+
this.viewer.scene.background = this.backgroundColor;
|
|
777
|
+
this.viewer.scene.environment = pmremGenerator.fromScene(environment).texture;
|
|
778
|
+
this.viewer.addEventListener("optionschange", this.syncOptions);
|
|
779
|
+
environment.dispose();
|
|
780
|
+
}
|
|
781
|
+
dispose() {
|
|
782
|
+
this.viewer.removeEventListener("optionschange", this.syncOptions);
|
|
783
|
+
this.viewer.scene.environment = undefined;
|
|
784
|
+
this.viewer.scene.background = undefined;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
class DefaultCameraPositionComponent {
|
|
789
|
+
constructor(viewer) {
|
|
790
|
+
this.geometryEnd = event => {
|
|
791
|
+
const {data: scene} = event;
|
|
792
|
+
scene.updateMatrixWorld();
|
|
793
|
+
const box = (new THREE.Box3).setFromObject(scene);
|
|
794
|
+
const size = box.getSize(new THREE.Vector3).length();
|
|
795
|
+
const center = box.getCenter(new THREE.Vector3);
|
|
796
|
+
scene.position.x += scene.position.x - center.x;
|
|
797
|
+
scene.position.y += scene.position.y - center.y;
|
|
798
|
+
scene.position.z += scene.position.z - center.z;
|
|
799
|
+
this.viewer.camera.near = size / 100;
|
|
800
|
+
this.viewer.camera.far = size * 100;
|
|
801
|
+
this.viewer.camera.position.copy(center);
|
|
802
|
+
this.viewer.camera.position.x += size / 2;
|
|
803
|
+
this.viewer.camera.position.y += size / 5;
|
|
804
|
+
this.viewer.camera.position.z += size / 2;
|
|
805
|
+
this.viewer.camera.updateMatrixWorld();
|
|
806
|
+
this.viewer.camera.updateProjectionMatrix();
|
|
807
|
+
this.viewer.camera.lookAt(center);
|
|
808
|
+
};
|
|
809
|
+
this.viewer = viewer;
|
|
810
|
+
this.viewer.addEventListener("geometryend", this.geometryEnd);
|
|
811
|
+
}
|
|
812
|
+
dispose() {
|
|
813
|
+
this.viewer.removeEventListener("geometryend", this.geometryEnd);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
class ResizeCanvasComponent {
|
|
818
|
+
constructor(viewer) {
|
|
819
|
+
this.resizeViewer = entries => {
|
|
820
|
+
const {width: width, height: height} = entries[0].contentRect;
|
|
821
|
+
if (!width || !height) return;
|
|
822
|
+
this.viewer.camera.aspect = width / height;
|
|
823
|
+
this.viewer.camera.updateProjectionMatrix();
|
|
824
|
+
this.viewer.renderer.setSize(width, height, true);
|
|
825
|
+
this.viewer.update(true);
|
|
826
|
+
this.viewer.emitEvent({
|
|
827
|
+
type: "resize",
|
|
828
|
+
width: width,
|
|
829
|
+
height: height
|
|
830
|
+
});
|
|
831
|
+
};
|
|
832
|
+
this.viewer = viewer;
|
|
833
|
+
this.resizeObserver = new ResizeObserver(this.resizeViewer);
|
|
834
|
+
this.resizeObserver.observe(viewer.canvas.parentElement);
|
|
835
|
+
}
|
|
836
|
+
dispose() {
|
|
837
|
+
this.resizeObserver.disconnect();
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
class RenderLoopComponent {
|
|
842
|
+
constructor(viewer) {
|
|
843
|
+
this.animate = (time = 0) => {
|
|
844
|
+
this.requestID = requestAnimationFrame(this.animate);
|
|
845
|
+
this.viewer.render(time);
|
|
846
|
+
};
|
|
847
|
+
this.viewer = viewer;
|
|
848
|
+
this.animate();
|
|
849
|
+
}
|
|
850
|
+
dispose() {
|
|
851
|
+
cancelAnimationFrame(this.requestID);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
class ThreejsViewer extends EventEmitter2 {
|
|
856
|
+
constructor(client) {
|
|
857
|
+
super();
|
|
858
|
+
this._options = new Options(this);
|
|
859
|
+
this.client = client;
|
|
860
|
+
this.canvasEvents = CANVAS_EVENTS;
|
|
861
|
+
this.canvaseventlistener = event => this.emit(event);
|
|
862
|
+
this.draggerFactory = {
|
|
863
|
+
Pan: PanDragger,
|
|
864
|
+
Zoom: ZoomDragger,
|
|
865
|
+
Orbit: OrbitDragger,
|
|
866
|
+
Walk: WalkDragger,
|
|
867
|
+
Clipping: ClippingPlaneDragger
|
|
868
|
+
};
|
|
869
|
+
this._activeDragger = null;
|
|
870
|
+
this.models = [];
|
|
871
|
+
this.components = [];
|
|
872
|
+
this.selectedObjects = [];
|
|
873
|
+
this.renderTime = 0;
|
|
874
|
+
this.render = this.render.bind(this);
|
|
875
|
+
this.update = this.update.bind(this);
|
|
876
|
+
}
|
|
877
|
+
get options() {
|
|
878
|
+
return this._options;
|
|
879
|
+
}
|
|
880
|
+
get draggers() {
|
|
881
|
+
return Object.keys(this.draggerFactory);
|
|
882
|
+
}
|
|
883
|
+
initialize(canvas, onProgress) {
|
|
884
|
+
this.addEventListener("optionschange", (event => this.syncOptions(event.data)));
|
|
885
|
+
this.scene = new THREE.Scene;
|
|
886
|
+
const rect = canvas.parentElement.getBoundingClientRect();
|
|
887
|
+
const width = rect.width || 1;
|
|
888
|
+
const height = rect.height || 1;
|
|
889
|
+
this.camera = new THREE.PerspectiveCamera(45, width / height, .01, 1e3);
|
|
890
|
+
this.camera.up.set(0, 0, 1);
|
|
891
|
+
this.renderer = new THREE.WebGLRenderer({
|
|
892
|
+
canvas: canvas,
|
|
893
|
+
antialias: true
|
|
894
|
+
});
|
|
895
|
+
this.renderer.setPixelRatio(window.devicePixelRatio);
|
|
896
|
+
this.renderer.setSize(width, height);
|
|
897
|
+
this.renderer.toneMapping = THREE.LinearToneMapping;
|
|
898
|
+
this.canvas = canvas;
|
|
899
|
+
this.canvasEvents.forEach((x => canvas.addEventListener(x, this.canvaseventlistener)));
|
|
900
|
+
this.components.push(new LightComponent(this));
|
|
901
|
+
this.components.push(new BackgroundComponent(this));
|
|
902
|
+
this.components.push(new DefaultCameraPositionComponent(this));
|
|
903
|
+
this.components.push(new ResizeCanvasComponent(this));
|
|
904
|
+
this.components.push(new RenderLoopComponent(this));
|
|
905
|
+
this.options.notifierChangeEvent();
|
|
906
|
+
this.renderTime = performance.now();
|
|
907
|
+
this.render(this.renderTime);
|
|
908
|
+
if (typeof onProgress === "function") onProgress(new ProgressEvent("progress", {
|
|
909
|
+
lengthComputable: true,
|
|
910
|
+
loaded: 1,
|
|
911
|
+
total: 1
|
|
912
|
+
}));
|
|
913
|
+
return Promise.resolve(this);
|
|
914
|
+
}
|
|
915
|
+
dispose() {
|
|
916
|
+
this.cancel();
|
|
917
|
+
this.emitEvent({
|
|
918
|
+
type: "dispose"
|
|
919
|
+
});
|
|
920
|
+
this.components.forEach((component => component.dispose()));
|
|
921
|
+
this.components = [];
|
|
922
|
+
this.setActiveDragger("");
|
|
923
|
+
this.removeAllListeners();
|
|
924
|
+
if (this.canvas) {
|
|
925
|
+
this.canvasEvents.forEach((x => this.canvas.removeEventListener(x, this.canvaseventlistener)));
|
|
926
|
+
this.canvas = undefined;
|
|
927
|
+
}
|
|
928
|
+
if (this.renderer) this.renderer.dispose();
|
|
929
|
+
this.renderer = undefined;
|
|
930
|
+
this.camera = undefined;
|
|
931
|
+
this.scene = undefined;
|
|
932
|
+
return this;
|
|
933
|
+
}
|
|
934
|
+
isInitialized() {
|
|
935
|
+
return !!this.renderer;
|
|
936
|
+
}
|
|
937
|
+
render(time) {
|
|
938
|
+
if (!this.renderNeeded) return;
|
|
939
|
+
if (!this.renderer) return;
|
|
940
|
+
this.renderer.render(this.scene, this.camera);
|
|
941
|
+
this.renderNeeded = false;
|
|
942
|
+
const deltaTime = (time - this.renderTime) / 1e3;
|
|
943
|
+
this.renderTime = time;
|
|
944
|
+
this.emitEvent({
|
|
945
|
+
type: "render",
|
|
946
|
+
time: time,
|
|
947
|
+
deltaTime: deltaTime
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
update(force = false) {
|
|
951
|
+
this.renderNeeded = true;
|
|
952
|
+
if (force) this.render(performance.now());
|
|
953
|
+
this.emitEvent({
|
|
954
|
+
type: "update",
|
|
955
|
+
data: force
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
syncOptions(options = this.options.data) {}
|
|
959
|
+
loadReferences(model) {
|
|
960
|
+
return Promise.resolve(this);
|
|
961
|
+
}
|
|
962
|
+
async open(file) {
|
|
963
|
+
if (!this.renderer) return this;
|
|
964
|
+
this.cancel();
|
|
965
|
+
this.clear();
|
|
966
|
+
this.emitEvent({
|
|
967
|
+
type: "open",
|
|
968
|
+
file: file,
|
|
969
|
+
model: file
|
|
970
|
+
});
|
|
971
|
+
let model;
|
|
972
|
+
if (file) {
|
|
973
|
+
const models = await file.getModels() || [];
|
|
974
|
+
model = models.find((model => model.default)) || models[0];
|
|
975
|
+
}
|
|
976
|
+
if (!model) throw new Error("No default model found");
|
|
977
|
+
const geometryType = model.database.split(".").pop();
|
|
978
|
+
if (geometryType !== "gltf") throw new Error(`Unknown geometry type: ${geometryType}`);
|
|
979
|
+
const url = `${model.httpClient.serverUrl}${model.path}/${model.database}`;
|
|
980
|
+
const params = {
|
|
981
|
+
requestHeader: model.httpClient.headers
|
|
982
|
+
};
|
|
983
|
+
await this.loadReferences(model);
|
|
984
|
+
await this.loadGltfFile(url, undefined, params);
|
|
985
|
+
return this;
|
|
986
|
+
}
|
|
987
|
+
cancel() {
|
|
988
|
+
this.emitEvent({
|
|
989
|
+
type: "cancel"
|
|
990
|
+
});
|
|
991
|
+
return this;
|
|
992
|
+
}
|
|
993
|
+
openGltfFile(file, externalData = new Map, params = {}) {
|
|
994
|
+
if (!this.renderer) return Promise.resolve(this);
|
|
995
|
+
this.cancel();
|
|
996
|
+
this.clear();
|
|
997
|
+
this.emitEvent({
|
|
998
|
+
type: "open"
|
|
999
|
+
});
|
|
1000
|
+
return this.loadGltfFile(file, externalData, params);
|
|
1001
|
+
}
|
|
1002
|
+
async loadGltfFile(file, externalData = new Map, params = {}) {
|
|
1003
|
+
const manager = new GLTFLoadingManager(file, externalData, params);
|
|
1004
|
+
try {
|
|
1005
|
+
this.emitEvent({
|
|
1006
|
+
type: "geometrystart"
|
|
1007
|
+
});
|
|
1008
|
+
const loader = new GLTFLoader(manager);
|
|
1009
|
+
loader.setPath(manager.path);
|
|
1010
|
+
loader.setRequestHeader(params.requestHeader);
|
|
1011
|
+
loader.setCrossOrigin(params.crossOrigin || loader.crossOrigin);
|
|
1012
|
+
loader.setWithCredentials(params.withCredentials || loader.withCredentials);
|
|
1013
|
+
const gltf = await loader.loadAsync(manager.fileURL, (event => {
|
|
1014
|
+
const {lengthComputable: lengthComputable, loaded: loaded, total: total} = event;
|
|
1015
|
+
const progress = lengthComputable ? loaded / total : 1;
|
|
1016
|
+
this.emitEvent({
|
|
1017
|
+
type: "geometryprogress",
|
|
1018
|
+
data: progress
|
|
1019
|
+
});
|
|
1020
|
+
}));
|
|
1021
|
+
if (!this.scene) return this;
|
|
1022
|
+
if (!gltf.scene) throw new Error("No glTF scene found");
|
|
1023
|
+
this.models.push(gltf);
|
|
1024
|
+
this.scene.add(gltf.scene);
|
|
1025
|
+
this.update();
|
|
1026
|
+
this.emitEvent({
|
|
1027
|
+
type: "databasechunk"
|
|
1028
|
+
});
|
|
1029
|
+
this.emitEvent({
|
|
1030
|
+
type: "geometryend",
|
|
1031
|
+
data: gltf.scene
|
|
1032
|
+
});
|
|
1033
|
+
} catch (error) {
|
|
1034
|
+
this.emitEvent({
|
|
1035
|
+
type: "geometryerror",
|
|
1036
|
+
data: error
|
|
1037
|
+
});
|
|
1038
|
+
throw error;
|
|
1039
|
+
} finally {
|
|
1040
|
+
manager.dispose();
|
|
1041
|
+
}
|
|
1042
|
+
return this;
|
|
1043
|
+
}
|
|
1044
|
+
clear() {
|
|
1045
|
+
function disposeMaterial(material) {
|
|
1046
|
+
const materials = Array.isArray(material) ? material : [ material ];
|
|
1047
|
+
materials.forEach((material => {
|
|
1048
|
+
material.dispose();
|
|
1049
|
+
}));
|
|
1050
|
+
}
|
|
1051
|
+
function disposeObject(object) {
|
|
1052
|
+
if (object.geometry) object.geometry.dispose();
|
|
1053
|
+
if (object.material) disposeMaterial(object.material);
|
|
1054
|
+
}
|
|
1055
|
+
this.selectedObjects = [];
|
|
1056
|
+
this.models.forEach((gltf => gltf.scene.traverse(disposeObject)));
|
|
1057
|
+
this.models.forEach((gltf => gltf.scene.removeFromParent()));
|
|
1058
|
+
this.models = [];
|
|
1059
|
+
this.update();
|
|
1060
|
+
this.emitEvent({
|
|
1061
|
+
type: "clear"
|
|
1062
|
+
});
|
|
1063
|
+
return this;
|
|
1064
|
+
}
|
|
1065
|
+
activeDragger() {
|
|
1066
|
+
return this._activeDragger;
|
|
1067
|
+
}
|
|
1068
|
+
setActiveDragger(name) {
|
|
1069
|
+
if (!this._activeDragger || this._activeDragger.name !== name) {
|
|
1070
|
+
if (this._activeDragger) {
|
|
1071
|
+
this._activeDragger.dispose();
|
|
1072
|
+
this._activeDragger = null;
|
|
1073
|
+
}
|
|
1074
|
+
const Constructor = this.draggerFactory[name];
|
|
1075
|
+
if (Constructor) {
|
|
1076
|
+
this._activeDragger = new Constructor(this);
|
|
1077
|
+
this._activeDragger.name = name;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
return this._activeDragger;
|
|
1081
|
+
}
|
|
1082
|
+
resetActiveDragger() {
|
|
1083
|
+
const dragger = this._activeDragger;
|
|
1084
|
+
if (dragger) {
|
|
1085
|
+
this.setActiveDragger("");
|
|
1086
|
+
this.setActiveDragger(dragger.name);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
is3D() {
|
|
1090
|
+
return false;
|
|
1091
|
+
}
|
|
1092
|
+
executeCommand(id, ...args) {
|
|
1093
|
+
return commands("ThreeJS").executeCommand(id, this, ...args);
|
|
1094
|
+
}
|
|
1095
|
+
drawViewpoint(viewpoint) {}
|
|
1096
|
+
createViewpoint() {
|
|
1097
|
+
return {};
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
export { ThreejsViewer as Viewer, commands };
|
|
1102
|
+
//# sourceMappingURL=viewer-three.module.js.map
|