UNPKG

260 kBJavaScriptView Raw
1/*
2Copyright (c) 2020-present NAVER Corp.
3name: @egjs/view3d
4license: MIT
5author: NAVER Corp.
6repository: https://github.com/naver/egjs-view3d
7version: 1.1.0
8*/
9import { Vector2, PCFSoftShadowMap, WebGLRenderer, sRGBEncoding, Clock, Vector3, Scene as Scene$1, Group, PerspectiveCamera, AnimationMixer, Box3, Matrix4, Object3D, Vector4, LoadingManager, PointsMaterial, MeshStandardMaterial, Points, Mesh, DirectionalLight, Quaternion, Euler, PlaneGeometry, ShadowMaterial, FileLoader, LoaderUtils, Color, AmbientLight, TextureLoader as TextureLoader$1, CubeTextureLoader, WebGLCubeRenderTarget, RingGeometry, MeshBasicMaterial, DoubleSide, BufferGeometry, LineBasicMaterial, Line, Ray, Plane, CanvasTexture, CircleGeometry, Geometry, Sphere, CylinderBufferGeometry, Texture } from 'three';
10import * as THREE from 'three';
11export { THREE };
12import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
13import { GLTFLoader as GLTFLoader$1 } from 'three/examples/jsm/loaders/GLTFLoader';
14import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader';
15
16/*! *****************************************************************************
17Copyright (c) Microsoft Corporation. All rights reserved.
18Licensed under the Apache License, Version 2.0 (the "License"); you may not use
19this file except in compliance with the License. You may obtain a copy of the
20License at http://www.apache.org/licenses/LICENSE-2.0
21
22THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
23KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
24WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
25MERCHANTABLITY OR NON-INFRINGEMENT.
26
27See the Apache Version 2.0 License for specific language governing permissions
28and limitations under the License.
29***************************************************************************** */
30
31/* global Reflect, Promise */
32var extendStatics = function (d, b) {
33 extendStatics = Object.setPrototypeOf || {
34 __proto__: []
35 } instanceof Array && function (d, b) {
36 d.__proto__ = b;
37 } || function (d, b) {
38 for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
39 };
40
41 return extendStatics(d, b);
42};
43
44function __extends(d, b) {
45 extendStatics(d, b);
46
47 function __() {
48 this.constructor = d;
49 }
50
51 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
52}
53var __assign = function () {
54 __assign = Object.assign || function __assign(t) {
55 for (var s, i = 1, n = arguments.length; i < n; i++) {
56 s = arguments[i];
57
58 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
59 }
60
61 return t;
62 };
63
64 return __assign.apply(this, arguments);
65};
66function __awaiter(thisArg, _arguments, P, generator) {
67 function adopt(value) {
68 return value instanceof P ? value : new P(function (resolve) {
69 resolve(value);
70 });
71 }
72
73 return new (P || (P = Promise))(function (resolve, reject) {
74 function fulfilled(value) {
75 try {
76 step(generator.next(value));
77 } catch (e) {
78 reject(e);
79 }
80 }
81
82 function rejected(value) {
83 try {
84 step(generator["throw"](value));
85 } catch (e) {
86 reject(e);
87 }
88 }
89
90 function step(result) {
91 result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
92 }
93
94 step((generator = generator.apply(thisArg, _arguments || [])).next());
95 });
96}
97function __generator(thisArg, body) {
98 var _ = {
99 label: 0,
100 sent: function () {
101 if (t[0] & 1) throw t[1];
102 return t[1];
103 },
104 trys: [],
105 ops: []
106 },
107 f,
108 y,
109 t,
110 g;
111 return g = {
112 next: verb(0),
113 "throw": verb(1),
114 "return": verb(2)
115 }, typeof Symbol === "function" && (g[Symbol.iterator] = function () {
116 return this;
117 }), g;
118
119 function verb(n) {
120 return function (v) {
121 return step([n, v]);
122 };
123 }
124
125 function step(op) {
126 if (f) throw new TypeError("Generator is already executing.");
127
128 while (_) try {
129 if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
130 if (y = 0, t) op = [op[0] & 2, t.value];
131
132 switch (op[0]) {
133 case 0:
134 case 1:
135 t = op;
136 break;
137
138 case 4:
139 _.label++;
140 return {
141 value: op[1],
142 done: false
143 };
144
145 case 5:
146 _.label++;
147 y = op[1];
148 op = [0];
149 continue;
150
151 case 7:
152 op = _.ops.pop();
153
154 _.trys.pop();
155
156 continue;
157
158 default:
159 if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
160 _ = 0;
161 continue;
162 }
163
164 if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
165 _.label = op[1];
166 break;
167 }
168
169 if (op[0] === 6 && _.label < t[1]) {
170 _.label = t[1];
171 t = op;
172 break;
173 }
174
175 if (t && _.label < t[2]) {
176 _.label = t[2];
177
178 _.ops.push(op);
179
180 break;
181 }
182
183 if (t[2]) _.ops.pop();
184
185 _.trys.pop();
186
187 continue;
188 }
189
190 op = body.call(thisArg, _);
191 } catch (e) {
192 op = [6, e];
193 y = 0;
194 } finally {
195 f = t = 0;
196 }
197
198 if (op[0] & 5) throw op[1];
199 return {
200 value: op[0] ? op[1] : void 0,
201 done: true
202 };
203 }
204}
205function __values(o) {
206 var s = typeof Symbol === "function" && Symbol.iterator,
207 m = s && o[s],
208 i = 0;
209 if (m) return m.call(o);
210 if (o && typeof o.length === "number") return {
211 next: function () {
212 if (o && i >= o.length) o = void 0;
213 return {
214 value: o && o[i++],
215 done: !o
216 };
217 }
218 };
219 throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
220}
221function __read(o, n) {
222 var m = typeof Symbol === "function" && o[Symbol.iterator];
223 if (!m) return o;
224 var i = m.call(o),
225 r,
226 ar = [],
227 e;
228
229 try {
230 while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
231 } catch (error) {
232 e = {
233 error: error
234 };
235 } finally {
236 try {
237 if (r && !r.done && (m = i["return"])) m.call(i);
238 } finally {
239 if (e) throw e.error;
240 }
241 }
242
243 return ar;
244}
245function __spread() {
246 for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
247
248 return ar;
249}
250
251/*
252 * Copyright (c) 2020 NAVER Corp.
253 * egjs projects are licensed under the MIT license
254 */
255
256var EventEmitter =
257/*#__PURE__*/
258function () {
259 function EventEmitter() {
260 this._listenerMap = {};
261 }
262
263 var __proto = EventEmitter.prototype;
264
265 __proto.on = function (eventName, callback) {
266 var listenerMap = this._listenerMap;
267 var listeners = listenerMap[eventName];
268
269 if (listeners && listeners.indexOf(callback) < 0) {
270 listeners.push(callback);
271 } else {
272 listenerMap[eventName] = [callback];
273 }
274
275 return this;
276 };
277
278 __proto.once = function (eventName, callback) {
279
280 var listenerMap = this._listenerMap;
281 var listeners = listenerMap[eventName];
282
283 if (listeners && listeners.indexOf(callback) < 0) {
284 listeners.push(callback);
285 } else {
286 listenerMap[eventName] = [callback];
287 }
288
289 return this;
290 };
291
292 __proto.off = function (eventName, callback) {
293 var listenerMap = this._listenerMap;
294
295 if (!eventName) {
296 this._listenerMap = {};
297 return this;
298 }
299
300 if (!callback) {
301 delete listenerMap[eventName];
302 return this;
303 }
304
305 var listeners = listenerMap[eventName];
306
307 if (listeners) {
308 var callbackIdx = listeners.indexOf(callback);
309
310 if (callbackIdx >= 0) {
311 listeners.splice(callbackIdx, 1);
312 }
313 }
314
315 return this;
316 };
317
318 __proto.emit = function (eventName) {
319 var event = [];
320
321 for (var _i = 1; _i < arguments.length; _i++) {
322 event[_i - 1] = arguments[_i];
323 }
324
325 var listeners = this._listenerMap[eventName];
326
327 if (listeners) {
328 listeners.forEach(function (callback) {
329 callback.apply(void 0, __spread(event));
330 });
331 }
332
333 return this;
334 };
335
336 return EventEmitter;
337}();
338
339/*
340 * Copyright (c) 2020 NAVER Corp.
341 * egjs projects are licensed under the MIT license
342 */
343/**
344 * Renderer that renders View3D's Scene
345 * @category Core
346 */
347
348var Renderer =
349/*#__PURE__*/
350function () {
351 /**
352 * Create new Renderer instance
353 * @param canvas \<canvas\> element to render 3d model
354 */
355 function Renderer(canvas) {
356 this._canvas = canvas;
357 this._renderer = new WebGLRenderer({
358 canvas: this._canvas,
359 alpha: true,
360 antialias: true,
361 preserveDrawingBuffer: true
362 });
363 this._renderer.xr.enabled = true;
364 this._renderer.outputEncoding = sRGBEncoding;
365 this._clock = new Clock(false);
366 this.enableShadow();
367 }
368
369 var __proto = Renderer.prototype;
370 Object.defineProperty(__proto, "canvas", {
371 /**
372 * {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement HTMLCanvasElement} given when creating View3D instance
373 * @type HTMLCanvasElement
374 * @readonly
375 */
376 get: function () {
377 return this._canvas;
378 },
379 enumerable: false,
380 configurable: true
381 });
382 Object.defineProperty(__proto, "context", {
383 /**
384 * Current {@link https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext WebGLRenderingContext}
385 * @type WebGLRenderingContext
386 * @readonly
387 */
388 get: function () {
389 return this._renderer.context;
390 },
391 enumerable: false,
392 configurable: true
393 });
394 Object.defineProperty(__proto, "size", {
395 /**
396 * The width and height of the renderer's output canvas
397 * @see https://threejs.org/docs/#api/en/math/Vector2
398 * @type THREE.Vector2
399 */
400 get: function () {
401 return this._renderer.getSize(new Vector2());
402 },
403 set: function (val) {
404 this._renderer.setSize(val.x, val.y, false);
405 },
406 enumerable: false,
407 configurable: true
408 });
409 Object.defineProperty(__proto, "threeRenderer", {
410 /**
411 * Three.js {@link https://threejs.org/docs/#api/en/renderers/WebGLRenderer WebGLRenderer} instance
412 * @type THREE.WebGLRenderer
413 * @readonly
414 */
415 get: function () {
416 return this._renderer;
417 },
418 enumerable: false,
419 configurable: true
420 });
421 /**
422 * Resize the renderer based on current canvas width / height
423 * @returns {void} Nothing
424 */
425
426 __proto.resize = function () {
427 var renderer = this._renderer;
428 var canvas = this._canvas;
429 if (renderer.xr.isPresenting) return;
430 renderer.setPixelRatio(window.devicePixelRatio);
431 renderer.setSize(canvas.offsetWidth, canvas.offsetHeight, false);
432 };
433 /**
434 * Render a scene using a camera.
435 * @see https://threejs.org/docs/#api/en/renderers/WebGLRenderer.render
436 * @param scene {@link Scene} to render
437 * @param camera {@link Camera} to render
438 */
439
440
441 __proto.render = function (scene, camera) {
442 this._renderer.render(scene.root, camera.threeCamera);
443 };
444
445 __proto.setAnimationLoop = function (callback) {
446 var _this = this;
447
448 this._clock.start();
449
450 this._renderer.setAnimationLoop(function (timestamp, frame) {
451 var delta = _this._clock.getDelta();
452
453 callback(delta, frame);
454 });
455 };
456
457 __proto.stopAnimationLoop = function () {
458 this._clock.stop(); // See https://threejs.org/docs/#api/en/renderers/WebGLRenderer.setAnimationLoop
459
460
461 this._renderer.setAnimationLoop(null);
462 };
463 /**
464 * Enable shadow map
465 */
466
467
468 __proto.enableShadow = function () {
469 var threeRenderer = this._renderer;
470 threeRenderer.shadowMap.enabled = true;
471 threeRenderer.shadowMap.type = PCFSoftShadowMap;
472 };
473 /**
474 * Disable shadow map
475 */
476
477
478 __proto.disableShadow = function () {
479 var threeRenderer = this._renderer;
480 threeRenderer.shadowMap.enabled = false;
481 };
482
483 return Renderer;
484}();
485
486/*
487 * Copyright (c) 2020 NAVER Corp.
488 * egjs projects are licensed under the MIT license
489 */
490// Constants that used internally
491// Texture map names that used in THREE#MeshStandardMaterial
492var STANDARD_MAPS = ["alphaMap", "aoMap", "bumpMap", "displacementMap", "emissiveMap", "envMap", "lightMap", "map", "metalnessMap", "normalMap", "roughnessMap"];
493
494/*
495 * Copyright (c) 2020 NAVER Corp.
496 * egjs projects are licensed under the MIT license
497 */
498
499var View3DError =
500/*#__PURE__*/
501function (_super) {
502 __extends(View3DError, _super);
503
504 function View3DError(message, code) {
505 var _this = _super.call(this, message) || this;
506
507 _this.message = message;
508 _this.code = code;
509 Object.setPrototypeOf(_this, View3DError.prototype);
510 _this.name = "View3DError";
511 return _this;
512 }
513
514 return View3DError;
515}(Error);
516
517/*
518 * Copyright (c) 2020 NAVER Corp.
519 * egjs projects are licensed under the MIT license
520 */
521
522/**
523 * Error codes of {@link View3DError}
524 * @name ERROR_CODES
525 * @memberof Constants
526 * @type object
527 * @property {number} WRONG_TYPE 0
528 * @property {number} ELEMENT_NOT_FOUND 1
529 * @property {number} CANVAS_NOT_FOUND 2
530 * @property {number} WEBGL_NOT_SUPPORTED 3
531 * @property {number} ADD_CONTROL_FIRST 4
532 * @property {number} PROVIDE_WIDTH_OR_HEIGHT 5
533 */
534var CODES = {
535 WRONG_TYPE: 0,
536 ELEMENT_NOT_FOUND: 1,
537 ELEMENT_NOT_CANVAS: 2,
538 WEBGL_NOT_SUPPORTED: 3,
539 ADD_CONTROL_FIRST: 4,
540 PROVIDE_WIDTH_OR_HEIGHT: 5
541};
542var MESSAGES = {
543 WRONG_TYPE: function (val, types) {
544 return typeof val + " is not a " + types.map(function (type) {
545 return "\"" + type + "\"";
546 }).join(" or ") + ".";
547 },
548 ELEMENT_NOT_FOUND: function (query) {
549 return "Element with selector \"" + query + "\" not found.";
550 },
551 ELEMENT_NOT_CANVAS: function (el) {
552 return "Given element <" + el.tagName + "> is not a canvas.";
553 },
554 WEBGL_NOT_SUPPORTED: "WebGL is not supported on this browser.",
555 ADD_CONTROL_FIRST: "Control is enabled before setting a target element.",
556 PROVIDE_WIDTH_OR_HEIGHT: "Either width or height should be given."
557};
558
559/*
560 * Copyright (c) 2020 NAVER Corp.
561 * egjs projects are licensed under the MIT license
562 */
563function getElement(el, parent) {
564 var targetEl = null;
565
566 if (typeof el === "string") {
567 var parentEl = parent ? parent : document;
568 var queryResult = parentEl.querySelector(el);
569
570 if (!queryResult) {
571 throw new View3DError(MESSAGES.ELEMENT_NOT_FOUND(el), CODES.ELEMENT_NOT_FOUND);
572 }
573
574 targetEl = queryResult;
575 } else if (el && el.nodeType === Node.ELEMENT_NODE) {
576 targetEl = el;
577 }
578
579 return targetEl;
580}
581function getCanvas(el) {
582 var targetEl = getElement(el);
583
584 if (!targetEl) {
585 throw new View3DError(MESSAGES.WRONG_TYPE(el, ["HTMLElement", "string"]), CODES.WRONG_TYPE);
586 }
587
588 if (!/^canvas$/i.test(targetEl.tagName)) {
589 throw new View3DError(MESSAGES.ELEMENT_NOT_CANVAS(targetEl), CODES.ELEMENT_NOT_CANVAS);
590 }
591
592 return targetEl;
593}
594function range(end) {
595 if (!end || end <= 0) {
596 return [];
597 }
598
599 return Array.apply(0, Array(end)).map(function (undef, idx) {
600 return idx;
601 });
602}
603function toRadian(x) {
604 return x * Math.PI / 180;
605}
606function clamp(x, min, max) {
607 return Math.max(Math.min(x, max), min);
608}
609function findIndex(target, list) {
610 var e_1, _a;
611
612 var index = -1;
613
614 try {
615 for (var _b = __values(range(list.length)), _c = _b.next(); !_c.done; _c = _b.next()) {
616 var itemIndex = _c.value;
617
618 if (list[itemIndex] === target) {
619 index = itemIndex;
620 break;
621 }
622 }
623 } catch (e_1_1) {
624 e_1 = {
625 error: e_1_1
626 };
627 } finally {
628 try {
629 if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
630 } finally {
631 if (e_1) throw e_1.error;
632 }
633 }
634
635 return index;
636} // Linear interpolation between a and b
637
638function mix(a, b, t) {
639 return a * (1 - t) + b * t;
640}
641function circulate(val, min, max) {
642 var size = Math.abs(max - min);
643
644 if (val < min) {
645 var offset = (min - val) % size;
646 val = max - offset;
647 } else if (val > max) {
648 var offset = (val - max) % size;
649 val = min + offset;
650 }
651
652 return val;
653}
654function merge(target) {
655 var srcs = [];
656
657 for (var _i = 1; _i < arguments.length; _i++) {
658 srcs[_i - 1] = arguments[_i];
659 }
660
661 srcs.forEach(function (source) {
662 Object.keys(source).forEach(function (key) {
663 var value = source[key];
664
665 if (Array.isArray(target[key]) && Array.isArray(value)) {
666 target[key] = __spread(target[key], value);
667 } else {
668 target[key] = value;
669 }
670 });
671 });
672 return target;
673}
674function getBoxPoints(box) {
675 return [box.min.clone(), new Vector3(box.min.x, box.min.y, box.max.z), new Vector3(box.min.x, box.max.y, box.min.z), new Vector3(box.min.x, box.max.y, box.max.z), new Vector3(box.max.x, box.min.y, box.min.z), new Vector3(box.max.x, box.min.y, box.max.z), new Vector3(box.max.x, box.max.y, box.min.z), box.max.clone()];
676}
677function toPowerOfTwo(val) {
678 var result = 1;
679
680 while (result < val) {
681 result *= 2;
682 }
683
684 return result;
685}
686function getPrimaryAxisIndex(basis, viewDir) {
687 var primaryIdx = 0;
688 var maxDot = 0;
689 basis.forEach(function (axes, axesIdx) {
690 var dotProduct = Math.abs(viewDir.dot(axes));
691
692 if (dotProduct > maxDot) {
693 primaryIdx = axesIdx;
694 maxDot = dotProduct;
695 }
696 });
697 return primaryIdx;
698} // In radian
699
700function getRotationAngle(center, v1, v2) {
701 var centerToV1 = new Vector2().subVectors(v1, center).normalize();
702 var centerToV2 = new Vector2().subVectors(v2, center).normalize(); // Get the rotation angle with the model's NDC coordinates as the center.
703
704 var deg = centerToV2.angle() - centerToV1.angle();
705 var compDeg = -Math.sign(deg) * (2 * Math.PI - Math.abs(deg)); // Take the smaller deg
706
707 var rotationAngle = Math.abs(deg) < Math.abs(compDeg) ? deg : compDeg;
708 return rotationAngle;
709}
710
711/*
712 * Copyright (c) 2020 NAVER Corp.
713 * egjs projects are licensed under the MIT license
714 */
715/**
716 * Scene that View3D will render.
717 * All model datas including Mesh, Lights, etc. will be included on this
718 * @category Core
719 */
720
721var Scene =
722/*#__PURE__*/
723function () {
724 /**
725 * Create new Scene instance
726 */
727 function Scene() {
728 this._root = new Scene$1();
729 this._userObjects = new Group();
730 this._envObjects = new Group();
731 this._envs = [];
732 var root = this._root;
733 var userObjects = this._userObjects;
734 var envObjects = this._envObjects;
735 userObjects.name = "userObjects";
736 envObjects.name = "envObjects";
737 root.add(userObjects);
738 root.add(envObjects);
739 }
740
741 var __proto = Scene.prototype;
742 Object.defineProperty(__proto, "root", {
743 /**
744 * Root {@link https://threejs.org/docs/#api/en/scenes/Scene THREE.Scene} object
745 */
746 get: function () {
747 return this._root;
748 },
749 enumerable: false,
750 configurable: true
751 });
752 Object.defineProperty(__proto, "environments", {
753 /**
754 * {@link Environment}s inside scene
755 */
756 get: function () {
757 return this._envs;
758 },
759 enumerable: false,
760 configurable: true
761 });
762 Object.defineProperty(__proto, "visible", {
763 /**
764 * Return the visibility of the root scene
765 */
766 get: function () {
767 return this._root.visible;
768 },
769 enumerable: false,
770 configurable: true
771 });
772 /**
773 * Update scene to fit the given model
774 * @param model model to fit
775 * @param override options for specific environments
776 */
777
778 __proto.update = function (model, override) {
779 this._envs.forEach(function (env) {
780 return env.fit(model, override);
781 });
782 };
783 /**
784 * Reset scene to initial state
785 * @returns {void} Nothing
786 */
787
788
789 __proto.reset = function () {
790 this.resetModel();
791 this.resetEnv();
792 };
793 /**
794 * Fully remove all objects that added by calling {@link Scene#add add()}
795 * @returns {void} Nothing
796 */
797
798
799 __proto.resetModel = function () {
800 this._removeChildsOf(this._userObjects);
801 };
802 /**
803 * Remove all objects that added by calling {@link Scene#addEnv addEnv()}
804 * This will also reset scene background & envmap
805 * @returns {void} Nothing
806 */
807
808
809 __proto.resetEnv = function () {
810 this._removeChildsOf(this._envObjects);
811
812 this._envs = [];
813 this._root.background = null;
814 this._root.environment = null;
815 };
816 /**
817 * Add new Three.js {@link https://threejs.org/docs/#api/en/core/Object3D Object3D} into the scene
818 * @param object {@link https://threejs.org/docs/#api/en/core/Object3D THREE.Object3D}s to add
819 * @returns {void} Nothing
820 */
821
822
823 __proto.add = function () {
824 var _a;
825
826 var object = [];
827
828 for (var _i = 0; _i < arguments.length; _i++) {
829 object[_i] = arguments[_i];
830 }
831
832 (_a = this._userObjects).add.apply(_a, __spread(object));
833 };
834 /**
835 * Add new {@link Environment} or Three.js {@link https://threejs.org/docs/#api/en/core/Object3D Object3D}s to the scene, which won't be removed after displaying another 3D model
836 * @param envs {@link Environment} | {@link https://threejs.org/docs/#api/en/core/Object3D THREE.Object3D}s to add
837 * @returns {void} Nothing
838 */
839
840
841 __proto.addEnv = function () {
842 var _this = this;
843
844 var envs = [];
845
846 for (var _i = 0; _i < arguments.length; _i++) {
847 envs[_i] = arguments[_i];
848 }
849
850 envs.forEach(function (env) {
851 var _a;
852
853 if (env.isObject3D) {
854 _this._envObjects.add(env);
855 } else {
856 _this._envs.push(env);
857
858 (_a = _this._envObjects).add.apply(_a, __spread(env.objects));
859 }
860 });
861 };
862 /**
863 * Remove Three.js {@link https://threejs.org/docs/#api/en/core/Object3D Object3D} into the scene
864 * @param object {@link https://threejs.org/docs/#api/en/core/Object3D THREE.Object3D}s to add
865 * @returns {void} Nothing
866 */
867
868
869 __proto.remove = function () {
870 var _a;
871
872 var object = [];
873
874 for (var _i = 0; _i < arguments.length; _i++) {
875 object[_i] = arguments[_i];
876 }
877
878 (_a = this._userObjects).remove.apply(_a, __spread(object));
879 };
880 /**
881 * Remove {@link Environment} or Three.js {@link https://threejs.org/docs/#api/en/core/Object3D Object3D}s to the scene, which won't be removed after displaying another 3D model
882 * @param envs {@link Environment} | {@link https://threejs.org/docs/#api/en/core/Object3D THREE.Object3D}s to add
883 * @returns {void} Nothing
884 */
885
886
887 __proto.removeEnv = function () {
888 var _this = this;
889
890 var envs = [];
891
892 for (var _i = 0; _i < arguments.length; _i++) {
893 envs[_i] = arguments[_i];
894 }
895
896 envs.forEach(function (env) {
897 var _a;
898
899 if (env.isObject3D) {
900 _this._envObjects.remove(env);
901 } else {
902 var envIndex = findIndex(env, _this._envs);
903
904 if (envIndex > -1) {
905 _this._envs.splice(envIndex, 1);
906 }
907
908 (_a = _this._envObjects).remove.apply(_a, __spread(env.objects));
909 }
910 });
911 };
912 /**
913 * Set background of the scene.
914 * @see {@link https://threejs.org/docs/#api/en/scenes/Scene.background THREE.Scene.background}
915 * @param background A texture to set as background
916 * @returns {void} Nothing
917 */
918
919
920 __proto.setBackground = function (background) {
921 // Three.js's type definition does not include WebGLCubeRenderTarget, but it works and defined on their document
922 // See https://threejs.org/docs/#api/en/scenes/Scene.background
923 this._root.background = background;
924 };
925 /**
926 * Set scene's environment map that affects all physical materials in the scene
927 * @see {@link https://threejs.org/docs/#api/en/scenes/Scene.environment THREE.Scene.environment}
928 * @param envmap A texture to set as environment map
929 * @returns {void} Nothing
930 */
931
932
933 __proto.setEnvMap = function (envmap) {
934 // Next line written to silence Three.js's warning
935 var environment = envmap.texture ? envmap.texture : envmap;
936 this._root.environment = environment;
937 };
938 /**
939 * Make this scene visible
940 * @returns {void} Nothing
941 */
942
943
944 __proto.show = function () {
945 this._root.visible = true;
946 };
947 /**
948 * Make this scene invisible
949 * @returns {void} Nothing
950 */
951
952
953 __proto.hide = function () {
954 this._root.visible = false;
955 };
956
957 __proto._removeChildsOf = function (obj) {
958 obj.traverse(function (child) {
959 if (child.isMesh) {
960 var mesh = child; // Release geometry & material memory
961
962 mesh.geometry.dispose();
963 var materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
964 materials.forEach(function (mat) {
965 STANDARD_MAPS.forEach(function (map) {
966 if (mat[map]) {
967 mat[map].dispose();
968 }
969 });
970 });
971 }
972 });
973
974 while (obj.children.length > 0) {
975 obj.remove(obj.children[0]);
976 }
977 };
978
979 return Scene;
980}();
981
982/*
983 * Copyright (c) 2020 NAVER Corp.
984 * egjs projects are licensed under the MIT license
985 */
986/**
987 * Controller that controls camera of the View3D
988 * @category Core
989 */
990
991var Controller =
992/*#__PURE__*/
993function () {
994 /**
995 * Create new Controller instance
996 */
997 function Controller(canvas, camera) {
998 this._controls = [];
999 this._canvas = canvas;
1000 this._camera = camera;
1001 }
1002
1003 var __proto = Controller.prototype;
1004 Object.defineProperty(__proto, "controls", {
1005 /**
1006 * {@link CameraControl CameraControl} instances that is added on this controller.
1007 */
1008 get: function () {
1009 return this._controls;
1010 },
1011 enumerable: false,
1012 configurable: true
1013 });
1014 /**
1015 * Remove all attached controls, and removes all event listeners attached by that controls.
1016 * @returns {void} Nothing
1017 */
1018
1019 __proto.clear = function () {
1020 this._controls.forEach(function (control) {
1021 return control.destroy();
1022 });
1023
1024 this._controls = [];
1025 };
1026 /**
1027 * Add a new control
1028 * @param control {@link CameraControl CameraControl} instance to add
1029 * @see Adding Controls
1030 * @returns {void} Nothing
1031 */
1032
1033
1034 __proto.add = function (control) {
1035 var canvas = this._canvas;
1036
1037 if (!control.element) {
1038 // Set canvas as default element
1039 control.setElement(canvas);
1040 }
1041
1042 control.sync(this._camera);
1043 control.enable();
1044
1045 this._controls.push(control);
1046 };
1047 /**
1048 * Remove control and disable it
1049 * @param control {@link CameraControl CameraControl} instance to remove
1050 * @returns removed control or null if it doesn't exists
1051 */
1052
1053
1054 __proto.remove = function (control) {
1055 var controls = this._controls;
1056 var controlIndex = findIndex(control, controls);
1057 if (controlIndex < 0) return null;
1058 var removedControl = controls.splice(controlIndex, 1)[0];
1059 removedControl.disable();
1060 return removedControl;
1061 };
1062 /**
1063 * Enable all controls attached
1064 * @returns {void} Nothing
1065 */
1066
1067
1068 __proto.enableAll = function () {
1069 this._controls.forEach(function (control) {
1070 return control.enable();
1071 });
1072
1073 this.syncToCamera();
1074 };
1075 /**
1076 * Disable all controls attached
1077 * @returns {void} Nothing
1078 */
1079
1080
1081 __proto.disableAll = function () {
1082 this._controls.forEach(function (control) {
1083 return control.disable();
1084 });
1085 };
1086 /**
1087 * Update all controls attached to given screen size.
1088 * @param size Size of the screen. Noramlly size of the canvas is used.
1089 * @returns {void} Nothing
1090 */
1091
1092
1093 __proto.resize = function (size) {
1094 this._controls.forEach(function (control) {
1095 return control.resize(size);
1096 });
1097 };
1098 /**
1099 * Synchronize all controls attached to current camera position.
1100 * @returns {void} Nothing
1101 */
1102
1103
1104 __proto.syncToCamera = function () {
1105 var _this = this;
1106
1107 this._controls.forEach(function (control) {
1108 return control.sync(_this._camera);
1109 });
1110 };
1111 /**
1112 * Update all controls attached to this controller & update camera position based on it.
1113 * @param delta number of seconds to update controls
1114 * @returns {void} Nothing
1115 */
1116
1117
1118 __proto.update = function (delta) {
1119 var deltaMiliSec = delta * 1000;
1120 var camera = this._camera;
1121
1122 this._controls.forEach(function (control) {
1123 control.update(camera, deltaMiliSec);
1124 });
1125
1126 camera.updatePosition();
1127 };
1128
1129 return Controller;
1130}();
1131
1132/*
1133 * Copyright (c) 2020 NAVER Corp.
1134 * egjs projects are licensed under the MIT license
1135 */
1136/**
1137 * Data class of camera's pose
1138 */
1139
1140var Pose =
1141/*#__PURE__*/
1142function () {
1143 /**
1144 * Create new instance of pose
1145 * @param yaw yaw
1146 * @param pitch pitch
1147 * @param distance distance
1148 * @param pivot pivot
1149 * @see https://threejs.org/docs/#api/en/math/Vector3
1150 * @example
1151 * import { THREE, Pose } from "@egjs/view3d";
1152 *
1153 * const pose = new Pose(180, 45, 150, new THREE.Vector3(5, -1, 3));
1154 */
1155 function Pose(yaw, pitch, distance, pivot) {
1156 if (pivot === void 0) {
1157 pivot = new Vector3(0, 0, 0);
1158 }
1159
1160 this.yaw = yaw;
1161 this.pitch = pitch;
1162 this.distance = distance;
1163 this.pivot = pivot;
1164 }
1165 /**
1166 * Clone this pose
1167 * @returns Cloned pose
1168 */
1169
1170
1171 var __proto = Pose.prototype;
1172
1173 __proto.clone = function () {
1174 return new Pose(this.yaw, this.pitch, this.distance, this.pivot.clone());
1175 };
1176
1177 return Pose;
1178}();
1179
1180/*
1181 * Copyright (c) 2020 NAVER Corp.
1182 * egjs projects are licensed under the MIT license
1183 */
1184
1185/**
1186 * Collection of easing functions
1187 * @namespace EASING
1188 * @example
1189 * import View3D, { RotateControl, EASING } from "@egjs/view3d";
1190 *
1191 * new RotateControl({
1192 * easing: EASING.EASE_OUT_CUBIC,
1193 * });
1194 */
1195
1196/**
1197 * @memberof EASING
1198 * @name SINE_WAVE
1199 */
1200var SINE_WAVE = function (x) {
1201 return Math.sin(x * Math.PI * 2);
1202};
1203/**
1204 * @memberof EASING
1205 * @name EASE_OUT_CUBIC
1206 */
1207
1208var EASE_OUT_CUBIC = function (x) {
1209 return 1 - Math.pow(1 - x, 3);
1210};
1211/**
1212 * @memberof EASING
1213 * @name EASE_OUT_BOUNCE
1214 */
1215
1216var EASE_OUT_BOUNCE = function (x) {
1217 var n1 = 7.5625;
1218 var d1 = 2.75;
1219
1220 if (x < 1 / d1) {
1221 return n1 * x * x;
1222 } else if (x < 2 / d1) {
1223 return n1 * (x -= 1.5 / d1) * x + 0.75;
1224 } else if (x < 2.5 / d1) {
1225 return n1 * (x -= 2.25 / d1) * x + 0.9375;
1226 } else {
1227 return n1 * (x -= 2.625 / d1) * x + 0.984375;
1228 }
1229};
1230
1231var easing = {
1232 __proto__: null,
1233 SINE_WAVE: SINE_WAVE,
1234 EASE_OUT_CUBIC: EASE_OUT_CUBIC,
1235 EASE_OUT_BOUNCE: EASE_OUT_BOUNCE
1236};
1237
1238/*
1239 * Copyright (c) 2020 NAVER Corp.
1240 * egjs projects are licensed under the MIT license
1241 */
1242var MODEL_SIZE = 80; // Animation related
1243
1244var EASING = EASE_OUT_CUBIC;
1245var ANIMATION_DURATION = 500;
1246var ANIMATION_LOOP = false;
1247var ANIMATION_RANGE = {
1248 min: 0,
1249 max: 1
1250}; // Camera related
1251
1252var CAMERA_POSE = new Pose(0, 0, 100, new Vector3(0, 0, 0));
1253var INFINITE_RANGE = {
1254 min: -Infinity,
1255 max: Infinity
1256};
1257var PITCH_RANGE = {
1258 min: -89.9,
1259 max: 89.9
1260};
1261var DISTANCE_RANGE = {
1262 min: 0,
1263 max: 500
1264};
1265var MINIMUM_DISTANCE = 1;
1266var MAXIMUM_DISTANCE = 500;
1267var NULL_ELEMENT = null;
1268var DRACO_DECODER_URL = "https://www.gstatic.com/draco/v1/decoders/";
1269
1270/*
1271 * Copyright (c) 2020 NAVER Corp.
1272 * egjs projects are licensed under the MIT license
1273 */
1274
1275var Motion =
1276/*#__PURE__*/
1277function () {
1278 function Motion(_a) {
1279 var _b = _a === void 0 ? {} : _a,
1280 _c = _b.duration,
1281 duration = _c === void 0 ? ANIMATION_DURATION : _c,
1282 _d = _b.loop,
1283 loop = _d === void 0 ? ANIMATION_LOOP : _d,
1284 _e = _b.range,
1285 range = _e === void 0 ? ANIMATION_RANGE : _e,
1286 _f = _b.easing,
1287 easing = _f === void 0 ? EASING : _f;
1288
1289 this._duration = duration;
1290 this._loop = loop;
1291 this._range = range;
1292 this._easing = easing;
1293 this._activated = false;
1294 this.reset(0);
1295 }
1296
1297 var __proto = Motion.prototype;
1298 Object.defineProperty(__proto, "val", {
1299 get: function () {
1300 return this._val;
1301 },
1302 enumerable: false,
1303 configurable: true
1304 });
1305 Object.defineProperty(__proto, "start", {
1306 get: function () {
1307 return this._start;
1308 },
1309 enumerable: false,
1310 configurable: true
1311 });
1312 Object.defineProperty(__proto, "end", {
1313 get: function () {
1314 return this._end;
1315 },
1316 enumerable: false,
1317 configurable: true
1318 });
1319 Object.defineProperty(__proto, "progress", {
1320 get: function () {
1321 return this._progress;
1322 },
1323 enumerable: false,
1324 configurable: true
1325 });
1326 Object.defineProperty(__proto, "duration", {
1327 get: function () {
1328 return this._duration;
1329 },
1330 set: function (val) {
1331 this._duration = val;
1332 },
1333 enumerable: false,
1334 configurable: true
1335 });
1336 Object.defineProperty(__proto, "loop", {
1337 get: function () {
1338 return this._loop;
1339 },
1340 set: function (val) {
1341 this._loop = val;
1342 },
1343 enumerable: false,
1344 configurable: true
1345 });
1346 Object.defineProperty(__proto, "range", {
1347 get: function () {
1348 return this._range;
1349 },
1350 set: function (val) {
1351 this._range = val;
1352 },
1353 enumerable: false,
1354 configurable: true
1355 });
1356 Object.defineProperty(__proto, "easing", {
1357 get: function () {
1358 return this._easing;
1359 },
1360 set: function (val) {
1361 this._easing = val;
1362 },
1363 enumerable: false,
1364 configurable: true
1365 });
1366 /**
1367 * Update motion and progress it by given deltaTime
1368 * @param deltaTime number of milisec to update motion
1369 * @returns Difference(delta) of the value from the last update.
1370 */
1371
1372 __proto.update = function (deltaTime) {
1373 if (!this._activated) return 0;
1374 var start = this._start;
1375 var end = this._end;
1376 var duration = this._duration;
1377 var prev = this._val;
1378 var loop = this._loop;
1379 var nextProgress = this._progress + deltaTime / duration;
1380 this._progress = loop ? circulate(nextProgress, 0, 1) : clamp(nextProgress, 0, 1);
1381
1382 var easedProgress = this._easing(this._progress);
1383
1384 this._val = mix(start, end, easedProgress);
1385
1386 if (!loop && this._progress >= 1) {
1387 this._activated = false;
1388 }
1389
1390 return this._val - prev;
1391 };
1392
1393 __proto.reset = function (defaultVal) {
1394 var range = this._range;
1395 var val = clamp(defaultVal, range.min, range.max);
1396 this._start = val;
1397 this._end = val;
1398 this._val = val;
1399 this._progress = 0;
1400 this._activated = false;
1401 };
1402
1403 __proto.setEndDelta = function (delta) {
1404 var range = this._range;
1405 this._start = this._val;
1406 this._end = clamp(this._end + delta, range.min, range.max);
1407 this._progress = 0;
1408 this._activated = true;
1409 };
1410
1411 return Motion;
1412}();
1413
1414/*
1415 * Copyright (c) 2020 NAVER Corp.
1416 * egjs projects are licensed under the MIT license
1417 */
1418/**
1419 * Control that animates model without user input
1420 * @category Controls
1421 */
1422
1423var AnimationControl =
1424/*#__PURE__*/
1425function () {
1426 /**
1427 * Create new instance of AnimationControl
1428 * @param from Start pose
1429 * @param to End pose
1430 * @param {object} options Options
1431 * @param {number} [options.duration=500] Animation duration
1432 * @param {function} [options.easing=(x: number) => 1 - Math.pow(1 - x, 3)] Animation easing function
1433 */
1434 function AnimationControl(from, to, _a) {
1435 var _b = _a === void 0 ? {} : _a,
1436 _c = _b.duration,
1437 duration = _c === void 0 ? ANIMATION_DURATION : _c,
1438 _d = _b.easing,
1439 easing = _d === void 0 ? EASING : _d;
1440
1441 this._enabled = false;
1442 this._finishCallbacks = [];
1443 from = from.clone();
1444 to = to.clone();
1445 from.yaw = circulate(from.yaw, 0, 360);
1446 to.yaw = circulate(to.yaw, 0, 360); // Take the smaller degree
1447
1448 if (Math.abs(to.yaw - from.yaw) > 180) {
1449 to.yaw = to.yaw < from.yaw ? to.yaw + 360 : to.yaw - 360;
1450 }
1451
1452 this._motion = new Motion({
1453 duration: duration,
1454 range: ANIMATION_RANGE,
1455 easing: easing
1456 });
1457 this._from = from;
1458 this._to = to;
1459 }
1460
1461 var __proto = AnimationControl.prototype;
1462 Object.defineProperty(__proto, "element", {
1463 get: function () {
1464 return null;
1465 },
1466 enumerable: false,
1467 configurable: true
1468 });
1469 Object.defineProperty(__proto, "enabled", {
1470 /**
1471 * Whether this control is enabled or not
1472 * @readonly
1473 */
1474 get: function () {
1475 return this._enabled;
1476 },
1477 enumerable: false,
1478 configurable: true
1479 });
1480 Object.defineProperty(__proto, "duration", {
1481 /**
1482 * Duration of the animation
1483 */
1484 get: function () {
1485 return this._motion.duration;
1486 },
1487 set: function (val) {
1488 this._motion.duration = val;
1489 },
1490 enumerable: false,
1491 configurable: true
1492 });
1493 Object.defineProperty(__proto, "easing", {
1494 /**
1495 * Easing function of the animation
1496 */
1497 get: function () {
1498 return this._motion.easing;
1499 },
1500 set: function (val) {
1501 this._motion.easing = val;
1502 },
1503 enumerable: false,
1504 configurable: true
1505 });
1506 /**
1507 * Destroy the instance and remove all event listeners attached
1508 * This also will reset CSS cursor to intial
1509 * @returns {void} Nothing
1510 */
1511
1512 __proto.destroy = function () {
1513 this.disable();
1514 };
1515 /**
1516 * Update control by given deltaTime
1517 * @param camera Camera to update position
1518 * @param deltaTime Number of milisec to update
1519 * @returns {void} Nothing
1520 */
1521
1522
1523 __proto.update = function (camera, deltaTime) {
1524 if (!this._enabled) return;
1525 var from = this._from;
1526 var to = this._to;
1527 var motion = this._motion;
1528 motion.update(deltaTime); // Progress that easing is applied
1529
1530 var progress = motion.val;
1531 camera.yaw = mix(from.yaw, to.yaw, progress);
1532 camera.pitch = mix(from.pitch, to.pitch, progress);
1533 camera.distance = mix(from.distance, to.distance, progress);
1534 camera.pivot = from.pivot.clone().lerp(to.pivot, progress);
1535
1536 if (motion.progress >= 1) {
1537 this.disable();
1538
1539 this._finishCallbacks.forEach(function (callback) {
1540 return callback();
1541 });
1542 }
1543 };
1544 /**
1545 * Enable this input and add event listeners
1546 * @returns {void} Nothing
1547 */
1548
1549
1550 __proto.enable = function () {
1551 if (this._enabled) return;
1552 this._enabled = true;
1553
1554 this._motion.reset(0);
1555
1556 this._motion.setEndDelta(1);
1557 };
1558 /**
1559 * Disable this input and remove all event handlers
1560 * @returns {void} Nothing
1561 */
1562
1563
1564 __proto.disable = function () {
1565 if (!this._enabled) return;
1566 this._enabled = false;
1567 };
1568 /**
1569 * Add callback which is called when animation is finished
1570 * @param callback Callback that will be called when animation finishes
1571 * @returns {void} Nothing
1572 */
1573
1574
1575 __proto.onFinished = function (callback) {
1576 this._finishCallbacks.push(callback);
1577 };
1578 /**
1579 * Remove all onFinished callbacks
1580 * @returns {void} Nothing
1581 */
1582
1583
1584 __proto.clearFinished = function () {
1585 this._finishCallbacks = [];
1586 };
1587
1588 __proto.resize = function (size) {// DO NOTHING
1589 };
1590
1591 __proto.setElement = function (element) {// DO NOTHING
1592 };
1593
1594 __proto.sync = function (camera) {// Do nothing
1595 };
1596
1597 return AnimationControl;
1598}();
1599
1600/*
1601 * Copyright (c) 2020 NAVER Corp.
1602 * egjs projects are licensed under the MIT license
1603 */
1604/**
1605 * Camera that renders the scene of View3D
1606 * @category Core
1607 */
1608
1609var Camera =
1610/*#__PURE__*/
1611function () {
1612 /**
1613 * Create new Camera instance
1614 * @param canvas \<canvas\> element to render 3d model
1615 */
1616 function Camera(canvas) {
1617 this._minDistance = MINIMUM_DISTANCE;
1618 this._maxDistance = MAXIMUM_DISTANCE;
1619 this._defaultPose = CAMERA_POSE;
1620 this._currentPose = this._defaultPose.clone();
1621 this._threeCamera = new PerspectiveCamera();
1622 this._controller = new Controller(canvas, this);
1623 }
1624
1625 var __proto = Camera.prototype;
1626 Object.defineProperty(__proto, "threeCamera", {
1627 /**
1628 * Three.js {@link https://threejs.org/docs/#api/en/cameras/PerspectiveCamera PerspectiveCamera} instance
1629 * @readonly
1630 * @type THREE.PerspectiveCamera
1631 */
1632 get: function () {
1633 return this._threeCamera;
1634 },
1635 enumerable: false,
1636 configurable: true
1637 });
1638 Object.defineProperty(__proto, "controller", {
1639 /**
1640 * Controller of the camera
1641 * @readonly
1642 * @type Controller
1643 */
1644 get: function () {
1645 return this._controller;
1646 },
1647 enumerable: false,
1648 configurable: true
1649 });
1650 Object.defineProperty(__proto, "defaultPose", {
1651 /**
1652 * Camera's default pose(yaw, pitch, distance, pivot)
1653 * This will be new currentPose when {@link Camera#reset reset()} is called
1654 * @readonly
1655 * @type Pose
1656 */
1657 get: function () {
1658 return this._defaultPose;
1659 },
1660 enumerable: false,
1661 configurable: true
1662 });
1663 Object.defineProperty(__proto, "currentPose", {
1664 /**
1665 * Camera's current pose value
1666 * @readonly
1667 * @type Pose
1668 */
1669 get: function () {
1670 return this._currentPose.clone();
1671 },
1672 enumerable: false,
1673 configurable: true
1674 });
1675 Object.defineProperty(__proto, "yaw", {
1676 /**
1677 * Camera's current yaw
1678 * {@link Camera#updatePosition} should be called after changing this value, and normally it is called every frame.
1679 * @type number
1680 */
1681 get: function () {
1682 return this._currentPose.yaw;
1683 },
1684 set: function (val) {
1685 this._currentPose.yaw = val;
1686 },
1687 enumerable: false,
1688 configurable: true
1689 });
1690 Object.defineProperty(__proto, "pitch", {
1691 /**
1692 * Camera's current pitch
1693 * {@link Camera#updatePosition} should be called after changing this value, and normally it is called every frame.
1694 * @type number
1695 */
1696 get: function () {
1697 return this._currentPose.pitch;
1698 },
1699 set: function (val) {
1700 this._currentPose.pitch = val;
1701 },
1702 enumerable: false,
1703 configurable: true
1704 });
1705 Object.defineProperty(__proto, "distance", {
1706 /**
1707 * Camera's current distance
1708 * {@link Camera#updatePosition} should be called after changing this value, and normally it is called every frame.
1709 * @type number
1710 */
1711 get: function () {
1712 return this._currentPose.distance;
1713 },
1714 set: function (val) {
1715 this._currentPose.distance = val;
1716 },
1717 enumerable: false,
1718 configurable: true
1719 });
1720 Object.defineProperty(__proto, "pivot", {
1721 /**
1722 * Current pivot point of camera rotation
1723 * @readonly
1724 * @type THREE.Vector3
1725 * @see {@link https://threejs.org/docs/#api/en/math/Vector3 THREE#Vector3}
1726 */
1727 get: function () {
1728 return this._currentPose.pivot;
1729 },
1730 set: function (val) {
1731 this._currentPose.pivot = val;
1732 },
1733 enumerable: false,
1734 configurable: true
1735 });
1736 Object.defineProperty(__proto, "minDistance", {
1737 /**
1738 * Minimum distance from lookAtPosition
1739 * @type number
1740 * @example
1741 * import View3D from "@egjs/view3d";
1742 *
1743 * const view3d = new View3D("#view3d-canvas");
1744 * view3d.camera.minDistance = 100;
1745 */
1746 get: function () {
1747 return this._minDistance;
1748 },
1749 set: function (val) {
1750 this._minDistance = val;
1751 },
1752 enumerable: false,
1753 configurable: true
1754 });
1755 Object.defineProperty(__proto, "maxDistance", {
1756 /**
1757 * Maximum distance from lookAtPosition
1758 * @type number
1759 * @example
1760 * import View3D from "@egjs/view3d";
1761 *
1762 * const view3d = new View3D("#view3d-canvas");
1763 * view3d.camera.maxDistance = 400;
1764 */
1765 get: function () {
1766 return this._maxDistance;
1767 },
1768 set: function (val) {
1769 this._maxDistance = val;
1770 },
1771 enumerable: false,
1772 configurable: true
1773 });
1774 Object.defineProperty(__proto, "fov", {
1775 /**
1776 * Camera's focus of view value
1777 * @type number
1778 * @see {@link https://threejs.org/docs/#api/en/cameras/PerspectiveCamera.fov THREE#PerspectiveCamera}
1779 */
1780 get: function () {
1781 return this._threeCamera.fov;
1782 },
1783 set: function (val) {
1784 this._threeCamera.fov = val;
1785
1786 this._threeCamera.updateProjectionMatrix();
1787 },
1788 enumerable: false,
1789 configurable: true
1790 });
1791 Object.defineProperty(__proto, "renderWidth", {
1792 /**
1793 * Camera's frustum width on current distance value
1794 * @type number
1795 */
1796 get: function () {
1797 return this.renderHeight * this._threeCamera.aspect;
1798 },
1799 enumerable: false,
1800 configurable: true
1801 });
1802 Object.defineProperty(__proto, "renderHeight", {
1803 /**
1804 * Camera's frustum height on current distance value
1805 * @type number
1806 */
1807 get: function () {
1808 return 2 * this.distance * Math.tan(toRadian(this.fov / 2));
1809 },
1810 enumerable: false,
1811 configurable: true
1812 });
1813 Object.defineProperty(__proto, "pose", {
1814 set: function (val) {
1815 this._currentPose = val;
1816
1817 this._controller.syncToCamera();
1818 },
1819 enumerable: false,
1820 configurable: true
1821 });
1822 /**
1823 * Reset camera to default pose
1824 * @param duration Duration of the reset animation
1825 * @param easing Easing function for the reset animation
1826 * @returns Promise that resolves when the animation finishes
1827 */
1828
1829 __proto.reset = function (duration, easing) {
1830 if (duration === void 0) {
1831 duration = 0;
1832 }
1833
1834 if (easing === void 0) {
1835 easing = EASING;
1836 }
1837
1838 var controller = this._controller;
1839 var currentPose = this._currentPose;
1840 var defaultPose = this._defaultPose;
1841
1842 if (duration <= 0) {
1843 // Reset camera immediately
1844 this._currentPose = defaultPose.clone();
1845 controller.syncToCamera();
1846 return Promise.resolve();
1847 } else {
1848 // Add reset animation control to controller
1849 var resetControl_1 = new AnimationControl(currentPose, defaultPose);
1850 resetControl_1.duration = duration;
1851 resetControl_1.easing = easing;
1852 return new Promise(function (resolve) {
1853 resetControl_1.onFinished(function () {
1854 controller.remove(resetControl_1);
1855 controller.syncToCamera();
1856 resolve();
1857 });
1858 controller.add(resetControl_1);
1859 });
1860 }
1861 };
1862 /**
1863 * Update camera's aspect to given size
1864 * @param size {@link THREE.Vector2} instance that has width(x), height(y)
1865 * @returns {void} Nothing
1866 */
1867
1868
1869 __proto.resize = function (size) {
1870 var cam = this._threeCamera;
1871 var aspect = size.x / size.y;
1872 cam.aspect = aspect;
1873 cam.updateProjectionMatrix();
1874
1875 this._controller.resize(size);
1876 };
1877 /**
1878 * Set default position of camera relative to the 3d model
1879 * New default pose will be used when {@link Camera#reset reset()} is called
1880 * @param newDefaultPose new default pose to apply
1881 * @returns {void} Nothing
1882 */
1883
1884
1885 __proto.setDefaultPose = function (newDefaultPose) {
1886 var defaultPose = this._defaultPose;
1887 var yaw = newDefaultPose.yaw,
1888 pitch = newDefaultPose.pitch,
1889 distance = newDefaultPose.distance,
1890 pivot = newDefaultPose.pivot;
1891
1892 if (yaw != null) {
1893 defaultPose.yaw = yaw;
1894 }
1895
1896 if (pitch != null) {
1897 defaultPose.pitch = pitch;
1898 }
1899
1900 if (distance != null) {
1901 defaultPose.distance = distance;
1902 }
1903
1904 if (pivot != null) {
1905 defaultPose.pivot = pivot;
1906 }
1907 };
1908 /**
1909 * Update camera position base on the {@link Camera#currentPose currentPose} value
1910 * @returns {void} Nothing
1911 */
1912
1913
1914 __proto.updatePosition = function () {
1915 this._clampCurrentPose();
1916
1917 var threeCamera = this._threeCamera;
1918 var pose = this._currentPose;
1919 var yaw = toRadian(pose.yaw);
1920 var pitch = toRadian(pose.pitch); // Should use minimum distance to prevent distance becomes 0, which makes whole x,y,z to 0 regardless of pose
1921
1922 var distance = Math.max(pose.distance + this._minDistance, MINIMUM_DISTANCE);
1923 var newCamPos = new Vector3(0, 0, 0);
1924 newCamPos.y = distance * Math.sin(pitch);
1925 newCamPos.z = distance * Math.cos(pitch);
1926 newCamPos.x = newCamPos.z * Math.sin(-yaw);
1927 newCamPos.z = newCamPos.z * Math.cos(-yaw);
1928 newCamPos.add(pose.pivot);
1929 threeCamera.position.copy(newCamPos);
1930 threeCamera.lookAt(pose.pivot);
1931 threeCamera.updateProjectionMatrix();
1932 };
1933
1934 __proto._clampCurrentPose = function () {
1935 var currentPose = this._currentPose;
1936 currentPose.yaw = circulate(currentPose.yaw, 0, 360);
1937 currentPose.pitch = clamp(currentPose.pitch, PITCH_RANGE.min, PITCH_RANGE.max);
1938 currentPose.distance = clamp(currentPose.distance, this._minDistance, this._maxDistance);
1939 };
1940
1941 return Camera;
1942}();
1943
1944/*
1945 * Copyright (c) 2020 NAVER Corp.
1946 * egjs projects are licensed under the MIT license
1947 */
1948/**
1949 * Component that manages animations of the 3D Model
1950 * @category Core
1951 */
1952
1953var ModelAnimator =
1954/*#__PURE__*/
1955function () {
1956 /**
1957 * Create new ModelAnimator instance
1958 * @param scene {@link https://threejs.org/docs/index.html#api/en/scenes/Scene THREE.Scene} instance that is root of all 3d objects
1959 */
1960 function ModelAnimator(scene) {
1961 this._mixer = new AnimationMixer(scene);
1962 this._clips = [];
1963 this._actions = [];
1964 }
1965
1966 var __proto = ModelAnimator.prototype;
1967 Object.defineProperty(__proto, "clips", {
1968 /**
1969 * Three.js {@link https://threejs.org/docs/#api/en/animation/AnimationClip AnimationClip}s that stored
1970 * @type THREE.AnimationClip
1971 * @readonly
1972 */
1973 get: function () {
1974 return this._clips;
1975 },
1976 enumerable: false,
1977 configurable: true
1978 });
1979 Object.defineProperty(__proto, "mixer", {
1980 /**
1981 * {@link https://threejs.org/docs/index.html#api/en/animation/AnimationMixer THREE.AnimationMixer} instance
1982 * @type THREE.AnimationMixer
1983 * @readonly
1984 */
1985 get: function () {
1986 return this._mixer;
1987 },
1988 enumerable: false,
1989 configurable: true
1990 });
1991 /**
1992 * Store the given clips
1993 * @param clips Three.js {@link https://threejs.org/docs/#api/en/animation/AnimationClip AnimationClip}s of the model
1994 * @returns {void} Nothing
1995 * @example
1996 * // After loading model
1997 * view3d.animator.setClips(model.animations);
1998 */
1999
2000 __proto.setClips = function (clips) {
2001 var mixer = this._mixer;
2002 this._clips = clips;
2003 this._actions = clips.map(function (clip) {
2004 return mixer.clipAction(clip);
2005 });
2006 };
2007 /**
2008 * Play one of the model's animation
2009 * @param index Index of the animation to play
2010 * @returns {void} Nothing
2011 */
2012
2013
2014 __proto.play = function (index) {
2015 var action = this._actions[index];
2016
2017 if (action) {
2018 action.play();
2019 }
2020 };
2021 /**
2022 * Pause one of the model's animation
2023 * If you want to stop animation completely, you should call {@link ModelAnimator#stop stop} instead
2024 * You should call {@link ModelAnimator#resume resume} to resume animation
2025 * @param index Index of the animation to pause
2026 * @returns {void} Nothing
2027 */
2028
2029
2030 __proto.pause = function (index) {
2031 var action = this._actions[index];
2032
2033 if (action) {
2034 action.timeScale = 0;
2035 }
2036 };
2037 /**
2038 * Resume one of the model's animation
2039 * This will play animation from the point when the animation is paused
2040 * @param index Index of the animation to resume
2041 * @returns {void} Nothing
2042 */
2043
2044
2045 __proto.resume = function (index) {
2046 var action = this._actions[index];
2047
2048 if (action) {
2049 action.timeScale = 1;
2050 }
2051 };
2052 /**
2053 * Fully stops one of the model's animation
2054 * @param index Index of the animation to stop
2055 * @returns {void} Nothing
2056 */
2057
2058
2059 __proto.stop = function (index) {
2060 var action = this._actions[index];
2061
2062 if (action) {
2063 action.stop();
2064 }
2065 };
2066 /**
2067 * Update animations
2068 * @param delta number of seconds to play animations attached
2069 * @returns {void} Nothing
2070 */
2071
2072
2073 __proto.update = function (delta) {
2074 this._mixer.update(delta);
2075 };
2076 /**
2077 * Reset the instance and remove all cached animation clips attached to it
2078 * @returns {void} Nothing
2079 */
2080
2081
2082 __proto.reset = function () {
2083 var mixer = this._mixer;
2084 mixer.uncacheRoot(mixer.getRoot());
2085 this._clips = [];
2086 this._actions = [];
2087 };
2088
2089 return ModelAnimator;
2090}();
2091
2092/*
2093 * Copyright (c) 2020 NAVER Corp.
2094 * egjs projects are licensed under the MIT license
2095 */
2096/**
2097 * XRManager that manages XRSessions
2098 * @category Core
2099 */
2100
2101var XRManager =
2102/*#__PURE__*/
2103function () {
2104 /**
2105 * Create a new instance of the XRManager
2106 * @param view3d Instance of the View3D
2107 */
2108 function XRManager(view3d) {
2109 this._view3d = view3d;
2110 this._sessions = [];
2111 this._currentSession = null;
2112 }
2113
2114 var __proto = XRManager.prototype;
2115 Object.defineProperty(__proto, "sessions", {
2116 /**
2117 * Array of {@link XRSession}s added
2118 */
2119 get: function () {
2120 return this._sessions;
2121 },
2122 enumerable: false,
2123 configurable: true
2124 });
2125 Object.defineProperty(__proto, "currentSession", {
2126 /**
2127 * Current entry session. Note that only WebXR type session can be returned.
2128 */
2129 get: function () {
2130 return this._currentSession;
2131 },
2132 enumerable: false,
2133 configurable: true
2134 });
2135 /**
2136 * Return a Promise containing whether any of the added session is available
2137 */
2138
2139 __proto.isAvailable = function () {
2140 return __awaiter(this, void 0, void 0, function () {
2141 var results;
2142 return __generator(this, function (_a) {
2143 switch (_a.label) {
2144 case 0:
2145 return [4
2146 /*yield*/
2147 , Promise.all(this._sessions.map(function (session) {
2148 return session.isAvailable();
2149 }))];
2150
2151 case 1:
2152 results = _a.sent();
2153 return [2
2154 /*return*/
2155 , results.some(function (result) {
2156 return result === true;
2157 })];
2158 }
2159 });
2160 });
2161 };
2162 /**
2163 * Add new {@link XRSession}.
2164 * The XRSession's order added is the same as the order of entry.
2165 * @param xrSession XRSession to add
2166 */
2167
2168
2169 __proto.addSession = function () {
2170 var _a;
2171
2172 var xrSession = [];
2173
2174 for (var _i = 0; _i < arguments.length; _i++) {
2175 xrSession[_i] = arguments[_i];
2176 }
2177
2178 (_a = this._sessions).push.apply(_a, __spread(xrSession));
2179 };
2180 /**
2181 * Enter XR Session.
2182 */
2183
2184
2185 __proto.enter = function () {
2186 return __awaiter(this, void 0, void 0, function () {
2187 return __generator(this, function (_a) {
2188 return [2
2189 /*return*/
2190 , this._enterSession(0, [])];
2191 });
2192 });
2193 };
2194 /**
2195 * Exit current XR Session.
2196 */
2197
2198
2199 __proto.exit = function () {
2200 if (this._currentSession) {
2201 this._currentSession.exit(this._view3d);
2202
2203 this._currentSession = null;
2204 }
2205 };
2206
2207 __proto._enterSession = function (sessionIdx, errors) {
2208 return __awaiter(this, void 0, void 0, function () {
2209 var view3d, sessions, xrSession, isSessionAvailable;
2210
2211 var _this = this;
2212
2213 return __generator(this, function (_a) {
2214 switch (_a.label) {
2215 case 0:
2216 view3d = this._view3d;
2217 sessions = this._sessions;
2218
2219 if (sessionIdx >= sessions.length) {
2220 if (errors.length < 1) {
2221 errors.push(new Error("No sessions available"));
2222 }
2223
2224 return [2
2225 /*return*/
2226 , Promise.reject(errors)];
2227 }
2228
2229 xrSession = sessions[sessionIdx];
2230 return [4
2231 /*yield*/
2232 , xrSession.isAvailable()];
2233
2234 case 1:
2235 isSessionAvailable = _a.sent();
2236
2237 if (!isSessionAvailable) {
2238 return [2
2239 /*return*/
2240 , this._enterSession(sessionIdx + 1, errors)];
2241 }
2242
2243 return [4
2244 /*yield*/
2245 , xrSession.enter(view3d).then(function () {
2246 if (xrSession.isWebXRSession) {
2247 // Non-webxr sessions are ignored
2248 _this._currentSession = xrSession;
2249 xrSession.session.addEventListener("end", function () {
2250 _this._currentSession = null;
2251 });
2252 }
2253
2254 return errors;
2255 }).catch(function (e) {
2256 errors.push(e);
2257 return _this._enterSession(sessionIdx + 1, errors);
2258 })];
2259
2260 case 2:
2261 return [2
2262 /*return*/
2263 , _a.sent()];
2264 }
2265 });
2266 });
2267 };
2268
2269 return XRManager;
2270}();
2271
2272/*
2273 * Copyright (c) 2020 NAVER Corp.
2274 * egjs projects are licensed under the MIT license
2275 */
2276var EVENTS = {
2277 MOUSE_DOWN: "mousedown",
2278 MOUSE_MOVE: "mousemove",
2279 MOUSE_UP: "mouseup",
2280 TOUCH_START: "touchstart",
2281 TOUCH_MOVE: "touchmove",
2282 TOUCH_END: "touchend",
2283 WHEEL: "wheel",
2284 RESIZE: "resize",
2285 CONTEXT_MENU: "contextmenu",
2286 MOUSE_ENTER: "mouseenter",
2287 MOUSE_LEAVE: "mouseleave"
2288}; // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent.button
2289
2290var MOUSE_BUTTON;
2291
2292(function (MOUSE_BUTTON) {
2293 MOUSE_BUTTON[MOUSE_BUTTON["LEFT"] = 0] = "LEFT";
2294 MOUSE_BUTTON[MOUSE_BUTTON["MIDDLE"] = 1] = "MIDDLE";
2295 MOUSE_BUTTON[MOUSE_BUTTON["RIGHT"] = 2] = "RIGHT";
2296})(MOUSE_BUTTON || (MOUSE_BUTTON = {}));
2297
2298/*
2299 * Copyright (c) 2020 NAVER Corp.
2300 * egjs projects are licensed under the MIT license
2301 */
2302/**
2303 * Yet another 3d model viewer
2304 * @category Core
2305 * @mermaid
2306 * classDiagram
2307 * class View3D
2308 * View3D *-- Camera
2309 * View3D *-- Renderer
2310 * View3D *-- Scene
2311 * View3D *-- ModelAnimator
2312 * Camera *-- Controller
2313 * @event resize
2314 * @event beforeRender
2315 * @event afterRender
2316 */
2317
2318var View3D =
2319/*#__PURE__*/
2320function (_super) {
2321 __extends(View3D, _super);
2322 /**
2323 * Creates new View3D instance
2324 * @example
2325 * import View3D, { ERROR_CODES } from "@egjs/view3d";
2326 *
2327 * try {
2328 * const view3d = new View3D("#wrapper")
2329 * } catch (e) {
2330 * if (e.code === ERROR_CODES.ELEMENT_NOT_FOUND) {
2331 * console.error("Element not found")
2332 * }
2333 * }
2334 * @throws {View3DError} `CODES.WRONG_TYPE`<br/>When the parameter is not either string or the canvas element.
2335 * @throws {View3DError} `CODES.ELEMENT_NOT_FOUND`<br/>When the element with given query does not exist.
2336 * @throws {View3DError} `CODES.ELEMENT_NOT_CANVAS`<br/>When the element given is not a \<canvas\> element.
2337 * @throws {View3DError} `CODES.WEBGL_NOT_SUPPORTED`<br/>When browser does not support WebGL.
2338 * @see Model
2339 * @see Camera
2340 * @see Renderer
2341 * @see Scene
2342 * @see Controller
2343 * @see XRManager
2344 */
2345
2346
2347 function View3D(el) {
2348 var _this = _super.call(this) || this;
2349 /**
2350 * Resize View3D instance to fit current canvas size
2351 * @method
2352 * @returns {void} Nothing
2353 */
2354
2355
2356 _this.resize = function () {
2357 _this._renderer.resize();
2358
2359 var newSize = _this._renderer.size;
2360
2361 _this._camera.resize(newSize);
2362
2363 _this.emit("resize", __assign(__assign({}, newSize), {
2364 target: _this
2365 }));
2366 };
2367 /**
2368 * View3D's basic render loop function
2369 * @param delta Number of seconds passed since last frame
2370 */
2371
2372
2373 _this.renderLoop = function (delta) {
2374 var renderer = _this._renderer;
2375 var scene = _this._scene;
2376 var camera = _this._camera;
2377 var controller = _this.controller;
2378 var animator = _this._animator;
2379 animator.update(delta);
2380 controller.update(delta);
2381
2382 _this.emit("beforeRender", _this);
2383
2384 renderer.render(scene, camera);
2385
2386 _this.emit("afterRender", _this);
2387 };
2388
2389 var canvas = getCanvas(el);
2390 _this._renderer = new Renderer(canvas);
2391 _this._camera = new Camera(canvas);
2392 _this._scene = new Scene();
2393 _this._animator = new ModelAnimator(_this._scene.root);
2394 _this._xr = new XRManager(_this);
2395 _this._model = null;
2396
2397 _this.resize();
2398
2399 window.addEventListener(EVENTS.RESIZE, _this.resize);
2400 return _this;
2401 }
2402
2403 var __proto = View3D.prototype;
2404 Object.defineProperty(__proto, "renderer", {
2405 /**
2406 * {@link Renderer} instance of the View3D
2407 * @type {Renderer}
2408 */
2409 get: function () {
2410 return this._renderer;
2411 },
2412 enumerable: false,
2413 configurable: true
2414 });
2415 Object.defineProperty(__proto, "scene", {
2416 /**
2417 * {@link Scene} instance of the View3D
2418 * @type {Scene}
2419 */
2420 get: function () {
2421 return this._scene;
2422 },
2423 enumerable: false,
2424 configurable: true
2425 });
2426 Object.defineProperty(__proto, "camera", {
2427 /**
2428 * {@link Camera} instance of the View3D
2429 * @type {Camera}
2430 */
2431 get: function () {
2432 return this._camera;
2433 },
2434 enumerable: false,
2435 configurable: true
2436 });
2437 Object.defineProperty(__proto, "controller", {
2438 /**
2439 * {@link Controller} instance of the Camera
2440 * @type {Controller}
2441 */
2442 get: function () {
2443 return this._camera.controller;
2444 },
2445 enumerable: false,
2446 configurable: true
2447 });
2448 Object.defineProperty(__proto, "animator", {
2449 /**
2450 * {@link ModelAnimator} instance of the View3D
2451 * @type {ModelAnimator}
2452 */
2453 get: function () {
2454 return this._animator;
2455 },
2456 enumerable: false,
2457 configurable: true
2458 });
2459 Object.defineProperty(__proto, "xr", {
2460 /**
2461 * {@link XRManager} instance of the View3D
2462 * @type {XRManager}
2463 */
2464 get: function () {
2465 return this._xr;
2466 },
2467 enumerable: false,
2468 configurable: true
2469 });
2470 Object.defineProperty(__proto, "model", {
2471 /**
2472 * {@link Model} that View3D is currently showing
2473 * @type {Model|null}
2474 */
2475 get: function () {
2476 return this._model;
2477 },
2478 enumerable: false,
2479 configurable: true
2480 });
2481 /**
2482 * Destroy View3D instance and remove all events attached to it
2483 * @returns {void} Nothing
2484 */
2485
2486 __proto.destroy = function () {
2487 this._scene.reset();
2488
2489 this.controller.clear();
2490 this._model = null;
2491 window.removeEventListener(EVENTS.RESIZE, this.resize);
2492 };
2493 /**
2494 * Display the given model.
2495 * This method will remove the current displaying model, and reset the camera & control to default position.
2496 * View3D can only show one model at a time
2497 * @param model {@link Model} instance to show
2498 * @param {object} [options={}] Display options
2499 * @param {number} [options.applySize=true] Whether to change model size to given "size" option.
2500 * @param {number} [options.size=80] Size of the model to show as.
2501 * @param {boolean} [options.resetView=true] Whether to reset the view to the camera's default pose.
2502 * @returns {void} Nothing
2503 */
2504
2505
2506 __proto.display = function (model, _a) {
2507 var _b = _a === void 0 ? {} : _a,
2508 _c = _b.applySize,
2509 applySize = _c === void 0 ? true : _c,
2510 _d = _b.size,
2511 size = _d === void 0 ? MODEL_SIZE : _d,
2512 _e = _b.resetView,
2513 resetView = _e === void 0 ? true : _e;
2514
2515 var renderer = this._renderer;
2516 var scene = this._scene;
2517 var camera = this._camera;
2518 var animator = this._animator;
2519
2520 if (applySize) {
2521 model.size = size;
2522 } // model.moveToOrigin();
2523
2524
2525 scene.resetModel();
2526
2527 if (resetView) {
2528 camera.reset();
2529 }
2530
2531 animator.reset();
2532 this._model = model;
2533 scene.add(model.scene);
2534 animator.setClips(model.animations);
2535 scene.update(model);
2536 renderer.stopAnimationLoop();
2537 renderer.setAnimationLoop(this.renderLoop);
2538 };
2539 /**
2540 * Current version of the View3D
2541 */
2542
2543
2544 View3D.VERSION = "1.1.0";
2545 return View3D;
2546}(EventEmitter);
2547
2548/*
2549 * Copyright (c) 2020 NAVER Corp.
2550 * egjs projects are licensed under the MIT license
2551 */
2552/**
2553 * Data class for loaded 3d model
2554 * @category Core
2555 */
2556
2557var Model =
2558/*#__PURE__*/
2559function () {
2560 /**
2561 * Create new Model instance
2562 */
2563 function Model(_a) {
2564 var scenes = _a.scenes,
2565 _b = _a.animations,
2566 animations = _b === void 0 ? [] : _b,
2567 _c = _a.fixSkinnedBbox,
2568 fixSkinnedBbox = _c === void 0 ? false : _c,
2569 _d = _a.castShadow,
2570 castShadow = _d === void 0 ? true : _d,
2571 _e = _a.receiveShadow,
2572 receiveShadow = _e === void 0 ? false : _e; // This guarantees model's root has identity matrix at creation
2573
2574 this._scene = new Group();
2575 var pivot = new Object3D();
2576 pivot.name = "Pivot";
2577 pivot.add.apply(pivot, __spread(scenes));
2578
2579 this._scene.add(pivot);
2580
2581 this._animations = animations;
2582 this._fixSkinnedBbox = fixSkinnedBbox;
2583 this._cachedLights = null;
2584 this._cachedMeshes = null;
2585
2586 this._setInitialBbox();
2587
2588 var bboxCenter = this._initialBbox.getCenter(new Vector3());
2589
2590 pivot.position.copy(bboxCenter.negate());
2591
2592 this._moveInitialBboxToCenter();
2593
2594 this._originalSize = this.size;
2595 this.castShadow = castShadow;
2596 this.receiveShadow = receiveShadow;
2597 }
2598
2599 var __proto = Model.prototype;
2600 Object.defineProperty(__proto, "scene", {
2601 /**
2602 * Scene of the model, see {@link https://threejs.org/docs/#api/en/objects/Group THREE.Group}
2603 * @readonly
2604 */
2605 get: function () {
2606 return this._scene;
2607 },
2608 enumerable: false,
2609 configurable: true
2610 });
2611 Object.defineProperty(__proto, "animations", {
2612 /**
2613 * {@link https://threejs.org/docs/#api/en/animation/AnimationClip THREE.AnimationClip}s inside model
2614 */
2615 get: function () {
2616 return this._animations;
2617 },
2618 enumerable: false,
2619 configurable: true
2620 });
2621 Object.defineProperty(__proto, "lights", {
2622 /***
2623 * {@link https://threejs.org/docs/#api/en/lights/Light THREE.Light}s inside model if there's any.
2624 * @readonly
2625 */
2626 get: function () {
2627 return this._cachedLights ? this._cachedLights : this._getAllLights();
2628 },
2629 enumerable: false,
2630 configurable: true
2631 });
2632 Object.defineProperty(__proto, "meshes", {
2633 /**
2634 * {@link https://threejs.org/docs/#api/en/objects/Mesh THREE.Mesh}es inside model if there's any.
2635 * @readonly
2636 */
2637 get: function () {
2638 return this._cachedMeshes ? this._cachedMeshes : this._getAllMeshes();
2639 },
2640 enumerable: false,
2641 configurable: true
2642 });
2643 Object.defineProperty(__proto, "bbox", {
2644 /**
2645 * Get a copy of model's current bounding box
2646 * @type THREE#Box3
2647 * @see https://threejs.org/docs/#api/en/math/Box3
2648 */
2649 get: function () {
2650 return this._getTransformedBbox();
2651 },
2652 enumerable: false,
2653 configurable: true
2654 });
2655 Object.defineProperty(__proto, "initialBbox", {
2656 /**
2657 * Get a copy of model's initial bounding box without transform
2658 */
2659 get: function () {
2660 return this._initialBbox.clone();
2661 },
2662 enumerable: false,
2663 configurable: true
2664 });
2665 Object.defineProperty(__proto, "size", {
2666 /**
2667 * Model's bounding box size
2668 * Changing this will scale the model.
2669 * @type number
2670 * @example
2671 * import { GLTFLoader } from "@egjs/view3d";
2672 * new GLTFLoader().load(URL_TO_GLTF)
2673 * .then(model => {
2674 * model.size = 100;
2675 * })
2676 */
2677 get: function () {
2678 return this._getTransformedBbox().getSize(new Vector3()).length();
2679 },
2680 set: function (val) {
2681 var scene = this._scene;
2682 var initialBbox = this._initialBbox; // Modify scale
2683
2684 var bboxSize = initialBbox.getSize(new Vector3());
2685 var scale = val / bboxSize.length();
2686 scene.scale.setScalar(scale);
2687 scene.updateMatrix();
2688 },
2689 enumerable: false,
2690 configurable: true
2691 });
2692 Object.defineProperty(__proto, "fixSkinnedBbox", {
2693 /**
2694 * Whether to apply inference from skeleton when calculating bounding box
2695 * This can fix some models with skinned mesh when it has wrong bounding box
2696 * @type boolean
2697 */
2698 get: function () {
2699 return this._fixSkinnedBbox;
2700 },
2701 set: function (val) {
2702 this._fixSkinnedBbox = val;
2703 },
2704 enumerable: false,
2705 configurable: true
2706 });
2707 Object.defineProperty(__proto, "originalSize", {
2708 /**
2709 * Return the model's original bbox size before applying any transform
2710 * @type number
2711 */
2712 get: function () {
2713 return this._originalSize;
2714 },
2715 enumerable: false,
2716 configurable: true
2717 });
2718 Object.defineProperty(__proto, "castShadow", {
2719 /**
2720 * Whether the model's meshes gets rendered into shadow map
2721 * @type boolean
2722 * @example
2723 * model.castShadow = true;
2724 */
2725 set: function (val) {
2726 var meshes = this.meshes;
2727 meshes.forEach(function (mesh) {
2728 return mesh.castShadow = val;
2729 });
2730 },
2731 enumerable: false,
2732 configurable: true
2733 });
2734 Object.defineProperty(__proto, "receiveShadow", {
2735 /**
2736 * Whether the model's mesh materials receive shadows
2737 * @type boolean
2738 * @example
2739 * model.receiveShadow = true;
2740 */
2741 set: function (val) {
2742 var meshes = this.meshes;
2743 meshes.forEach(function (mesh) {
2744 return mesh.receiveShadow = val;
2745 });
2746 },
2747 enumerable: false,
2748 configurable: true
2749 });
2750 /**
2751 * Translate the model to center the model's bounding box to world origin (0, 0, 0).
2752 */
2753
2754 __proto.moveToOrigin = function () {
2755 // Translate scene position to origin
2756 var scene = this._scene;
2757
2758 var initialBbox = this._initialBbox.clone();
2759
2760 initialBbox.min.multiply(scene.scale);
2761 initialBbox.max.multiply(scene.scale);
2762 var bboxCenter = initialBbox.getCenter(new Vector3());
2763 scene.position.copy(bboxCenter.negate());
2764 scene.updateMatrix();
2765 };
2766
2767 __proto._setInitialBbox = function () {
2768 this._scene.updateMatrixWorld();
2769
2770 if (this._fixSkinnedBbox && this._hasSkinnedMesh()) {
2771 this._initialBbox = this._getSkeletonBbox();
2772 } else {
2773 this._initialBbox = new Box3().setFromObject(this._scene);
2774 }
2775 };
2776
2777 __proto._getSkeletonBbox = function () {
2778 var bbox = new Box3();
2779 this.meshes.forEach(function (mesh) {
2780 if (!mesh.isSkinnedMesh) {
2781 bbox.expandByObject(mesh);
2782 return;
2783 }
2784
2785 var geometry = mesh.geometry;
2786 var positions = geometry.attributes.position;
2787 var skinIndicies = geometry.attributes.skinIndex;
2788 var skinWeights = geometry.attributes.skinWeight;
2789 var skeleton = mesh.skeleton;
2790 skeleton.update();
2791 var boneMatricies = skeleton.boneMatrices;
2792 var finalMatrix = new Matrix4();
2793
2794 var _loop_1 = function (posIdx) {
2795 finalMatrix.identity();
2796 var skinned = new Vector4();
2797 skinned.set(0, 0, 0, 0);
2798 var skinVertex = new Vector4();
2799 skinVertex.set(positions.getX(posIdx), positions.getY(posIdx), positions.getZ(posIdx), 1).applyMatrix4(mesh.bindMatrix);
2800 var weights = [skinWeights.getX(posIdx), skinWeights.getY(posIdx), skinWeights.getZ(posIdx), skinWeights.getW(posIdx)];
2801 var indicies = [skinIndicies.getX(posIdx), skinIndicies.getY(posIdx), skinIndicies.getZ(posIdx), skinIndicies.getW(posIdx)];
2802 weights.forEach(function (weight, index) {
2803 var boneMatrix = new Matrix4().fromArray(boneMatricies, indicies[index] * 16);
2804 skinned.add(skinVertex.clone().applyMatrix4(boneMatrix).multiplyScalar(weight));
2805 });
2806 var transformed = new Vector3().fromArray(skinned.applyMatrix4(mesh.bindMatrixInverse).toArray());
2807 transformed.applyMatrix4(mesh.matrixWorld);
2808 bbox.expandByPoint(transformed);
2809 };
2810
2811 for (var posIdx = 0; posIdx < positions.count; posIdx++) {
2812 _loop_1(posIdx);
2813 }
2814 });
2815 return bbox;
2816 };
2817
2818 __proto._moveInitialBboxToCenter = function () {
2819 var bboxCenter = this._initialBbox.getCenter(new Vector3());
2820
2821 this._initialBbox.translate(bboxCenter.negate());
2822 };
2823
2824 __proto._getAllLights = function () {
2825 var lights = [];
2826
2827 this._scene.traverse(function (obj) {
2828 if (obj.isLight) {
2829 lights.push(obj);
2830 }
2831 });
2832
2833 this._cachedLights = lights;
2834 return lights;
2835 };
2836 /**
2837 * Get all {@link https://threejs.org/docs/#api/en/objects/Mesh THREE.Mesh}es inside model if there's any.
2838 * @private
2839 * @returns Meshes found at model's scene
2840 */
2841
2842
2843 __proto._getAllMeshes = function () {
2844 var meshes = [];
2845
2846 this._scene.traverse(function (obj) {
2847 if (obj.isMesh) {
2848 meshes.push(obj);
2849 }
2850 });
2851
2852 this._cachedMeshes = meshes;
2853 return meshes;
2854 };
2855
2856 __proto._hasSkinnedMesh = function () {
2857 return this.meshes.some(function (mesh) {
2858 return mesh.isSkinnedMesh;
2859 });
2860 };
2861
2862 __proto._getTransformedBbox = function () {
2863 return this._initialBbox.clone().applyMatrix4(this._scene.matrix);
2864 };
2865
2866 return Model;
2867}();
2868
2869/*
2870 * Copyright (c) 2020 NAVER Corp.
2871 * egjs projects are licensed under the MIT license
2872 */
2873/**
2874 * Control that animates model without user input
2875 * @category Controls
2876 */
2877
2878var AutoControl =
2879/*#__PURE__*/
2880function () {
2881 /**
2882 * Create new RotateControl instance
2883 * @param {object} options Options
2884 * @param {HTMLElement | string | null} [options.element=null] Attaching element / selector of the element
2885 * @param {number} [options.delay=2000] Reactivation delay after mouse input in milisecond
2886 * @param {number} [options.delayOnMouseLeave=0] Reactivation delay after mouse leave
2887 * @param {number} [options.speed=1] Y-axis(yaw) rotation speed
2888 * @param {boolean} [options.pauseOnHover=false] Whether to pause rotation on mouse hover
2889 * @param {boolean} [options.canInterrupt=true] Whether user can interrupt the rotation with click/wheel input
2890 * @param {boolean} [options.disableOnInterrupt=false] Whether to disable control on user interrupt
2891 * @tutorial Adding Controls
2892 */
2893 function AutoControl(_a) {
2894 var _this = this;
2895
2896 var _b = _a === void 0 ? {} : _a,
2897 _c = _b.element,
2898 element = _c === void 0 ? NULL_ELEMENT : _c,
2899 _d = _b.delay,
2900 delay = _d === void 0 ? 2000 : _d,
2901 _e = _b.delayOnMouseLeave,
2902 delayOnMouseLeave = _e === void 0 ? 0 : _e,
2903 _f = _b.speed,
2904 speed = _f === void 0 ? 1 : _f,
2905 _g = _b.pauseOnHover,
2906 pauseOnHover = _g === void 0 ? false : _g,
2907 _h = _b.canInterrupt,
2908 canInterrupt = _h === void 0 ? true : _h,
2909 _j = _b.disableOnInterrupt,
2910 disableOnInterrupt = _j === void 0 ? false : _j; // Internal values
2911
2912
2913 this._targetEl = null;
2914 this._enabled = false;
2915 this._interrupted = false;
2916 this._interruptionTimer = -1;
2917 this._hovering = false;
2918
2919 this._onMouseDown = function (evt) {
2920 if (!_this._canInterrupt) return;
2921 if (evt.button !== MOUSE_BUTTON.LEFT && evt.button !== MOUSE_BUTTON.RIGHT) return;
2922 _this._interrupted = true;
2923
2924 _this._clearTimeout();
2925
2926 window.addEventListener(EVENTS.MOUSE_UP, _this._onMouseUp, false);
2927 };
2928
2929 this._onMouseUp = function () {
2930 window.removeEventListener(EVENTS.MOUSE_UP, _this._onMouseUp, false);
2931
2932 _this._setUninterruptedAfterDelay(_this._delay);
2933 };
2934
2935 this._onTouchStart = function () {
2936 if (!_this._canInterrupt) return;
2937 _this._interrupted = true;
2938
2939 _this._clearTimeout();
2940 };
2941
2942 this._onTouchEnd = function () {
2943 _this._setUninterruptedAfterDelay(_this._delay);
2944 };
2945
2946 this._onMouseEnter = function () {
2947 if (!_this._pauseOnHover) return;
2948 _this._interrupted = true;
2949 _this._hovering = true;
2950 };
2951
2952 this._onMouseLeave = function () {
2953 if (!_this._pauseOnHover) return;
2954 _this._hovering = false;
2955
2956 _this._setUninterruptedAfterDelay(_this._delayOnMouseLeave);
2957 };
2958
2959 this._onWheel = function () {
2960 if (!_this._canInterrupt) return;
2961 _this._interrupted = true;
2962
2963 _this._setUninterruptedAfterDelay(_this._delay);
2964 };
2965
2966 var targetEl = getElement(element);
2967
2968 if (targetEl) {
2969 this.setElement(targetEl);
2970 }
2971
2972 this._delay = delay;
2973 this._delayOnMouseLeave = delayOnMouseLeave;
2974 this._speed = speed;
2975 this._pauseOnHover = pauseOnHover;
2976 this._canInterrupt = canInterrupt;
2977 this._disableOnInterrupt = disableOnInterrupt;
2978 }
2979
2980 var __proto = AutoControl.prototype;
2981 Object.defineProperty(__proto, "element", {
2982 /**
2983 * Control's current target element to attach event listeners
2984 * @readonly
2985 */
2986 get: function () {
2987 return this._targetEl;
2988 },
2989 enumerable: false,
2990 configurable: true
2991 });
2992 Object.defineProperty(__proto, "enabled", {
2993 /**
2994 * Whether this control is enabled or not
2995 * @readonly
2996 */
2997 get: function () {
2998 return this._enabled;
2999 },
3000 enumerable: false,
3001 configurable: true
3002 });
3003 Object.defineProperty(__proto, "delay", {
3004 /**
3005 * Reactivation delay after mouse input in milisecond
3006 */
3007 get: function () {
3008 return this._delay;
3009 },
3010 set: function (val) {
3011 this._delay = val;
3012 },
3013 enumerable: false,
3014 configurable: true
3015 });
3016 Object.defineProperty(__proto, "delayOnMouseLeave", {
3017 /**
3018 * Reactivation delay after mouse leave
3019 * This option only works when {@link AutoControl#pauseOnHover pauseOnHover} is activated
3020 */
3021 get: function () {
3022 return this._delayOnMouseLeave;
3023 },
3024 set: function (val) {
3025 this._delayOnMouseLeave = val;
3026 },
3027 enumerable: false,
3028 configurable: true
3029 });
3030 Object.defineProperty(__proto, "speed", {
3031 /**
3032 * Y-axis(yaw) rotation speed
3033 * @default 1
3034 */
3035 get: function () {
3036 return this._speed;
3037 },
3038 set: function (val) {
3039 this._speed = val;
3040 },
3041 enumerable: false,
3042 configurable: true
3043 });
3044 Object.defineProperty(__proto, "pauseOnHover", {
3045 /**
3046 * Whether to pause rotation on mouse hover
3047 * @default false
3048 */
3049 get: function () {
3050 return this._pauseOnHover;
3051 },
3052 set: function (val) {
3053 this._pauseOnHover = val;
3054 },
3055 enumerable: false,
3056 configurable: true
3057 });
3058 Object.defineProperty(__proto, "canInterrupt", {
3059 /**
3060 * Whether user can interrupt the rotation with click/wheel input
3061 * @default true
3062 */
3063 get: function () {
3064 return this._canInterrupt;
3065 },
3066 set: function (val) {
3067 this._canInterrupt = val;
3068 },
3069 enumerable: false,
3070 configurable: true
3071 });
3072 Object.defineProperty(__proto, "disableOnInterrupt", {
3073 /**
3074 * Whether to disable control on user interrupt
3075 * @default false
3076 */
3077 get: function () {
3078 return this._disableOnInterrupt;
3079 },
3080 set: function (val) {
3081 this._disableOnInterrupt = val;
3082 },
3083 enumerable: false,
3084 configurable: true
3085 });
3086 /**
3087 * Destroy the instance and remove all event listeners attached
3088 * This also will reset CSS cursor to intial
3089 * @returns {void} Nothing
3090 */
3091
3092 __proto.destroy = function () {
3093 this.disable();
3094 };
3095 /**
3096 * Update control by given deltaTime
3097 * @param camera Camera to update position
3098 * @param deltaTime Number of milisec to update
3099 * @returns {void} Nothing
3100 */
3101
3102
3103 __proto.update = function (camera, deltaTime) {
3104 if (!this._enabled) return;
3105
3106 if (this._interrupted) {
3107 if (this._disableOnInterrupt) {
3108 this.disable();
3109 }
3110
3111 return;
3112 }
3113
3114 camera.yaw += this._speed * deltaTime / 100;
3115 }; // This is not documetned on purpose as it doesn't do nothing
3116
3117
3118 __proto.resize = function (size) {// DO NOTHING
3119 };
3120 /**
3121 * Enable this input and add event listeners
3122 * @returns {void} Nothing
3123 */
3124
3125
3126 __proto.enable = function () {
3127 if (this._enabled) return;
3128
3129 if (!this._targetEl) {
3130 throw new View3DError(MESSAGES.ADD_CONTROL_FIRST, CODES.ADD_CONTROL_FIRST);
3131 }
3132
3133 var targetEl = this._targetEl;
3134 targetEl.addEventListener(EVENTS.MOUSE_DOWN, this._onMouseDown, false);
3135 targetEl.addEventListener(EVENTS.TOUCH_START, this._onTouchStart, false);
3136 targetEl.addEventListener(EVENTS.TOUCH_END, this._onTouchEnd, false);
3137 targetEl.addEventListener(EVENTS.MOUSE_ENTER, this._onMouseEnter, false);
3138 targetEl.addEventListener(EVENTS.MOUSE_LEAVE, this._onMouseLeave, false);
3139 targetEl.addEventListener(EVENTS.WHEEL, this._onWheel, false);
3140 this._enabled = true;
3141 };
3142 /**
3143 * Disable this input and remove all event handlers
3144 * @returns {void} Nothing
3145 */
3146
3147
3148 __proto.disable = function () {
3149 if (!this._enabled || !this._targetEl) return;
3150 var targetEl = this._targetEl;
3151 targetEl.removeEventListener(EVENTS.MOUSE_DOWN, this._onMouseDown, false);
3152 window.removeEventListener(EVENTS.MOUSE_UP, this._onMouseUp, false);
3153 targetEl.removeEventListener(EVENTS.TOUCH_START, this._onTouchStart, false);
3154 targetEl.removeEventListener(EVENTS.TOUCH_END, this._onTouchEnd, false);
3155 targetEl.removeEventListener(EVENTS.MOUSE_ENTER, this._onMouseEnter, false);
3156 targetEl.removeEventListener(EVENTS.MOUSE_LEAVE, this._onMouseLeave, false);
3157 targetEl.removeEventListener(EVENTS.WHEEL, this._onWheel, false);
3158 this._enabled = false;
3159 this._interrupted = false;
3160 this._hovering = false;
3161
3162 this._clearTimeout();
3163 }; // This does nothing
3164
3165
3166 __proto.sync = function (camera) {// Do nothing
3167 };
3168 /**
3169 * Set target element to attach event listener
3170 * @param element target element
3171 * @returns {void} Nothing
3172 */
3173
3174
3175 __proto.setElement = function (element) {
3176 this._targetEl = element;
3177 };
3178
3179 __proto._setUninterruptedAfterDelay = function (delay) {
3180 var _this = this;
3181
3182 if (this._hovering) return;
3183
3184 this._clearTimeout();
3185
3186 if (delay > 0) {
3187 this._interruptionTimer = window.setTimeout(function () {
3188 _this._interrupted = false;
3189 _this._interruptionTimer = -1;
3190 }, delay);
3191 } else {
3192 this._interrupted = false;
3193 this._interruptionTimer = -1;
3194 }
3195 };
3196
3197 __proto._clearTimeout = function () {
3198 if (this._interruptionTimer >= 0) {
3199 window.clearTimeout(this._interruptionTimer);
3200 this._interruptionTimer = -1;
3201 }
3202 };
3203
3204 return AutoControl;
3205}();
3206
3207/*
3208 * Copyright (c) 2020 NAVER Corp.
3209 * egjs projects are licensed under the MIT license
3210 */
3211var CURSOR = {
3212 GRAB: "grab",
3213 GRABBING: "grabbing"
3214};
3215
3216/*
3217 * Copyright (c) 2020 NAVER Corp.
3218 * egjs projects are licensed under the MIT license
3219 */
3220/**
3221 * Model's rotation control that supports both mouse & touch
3222 * @category Controls
3223 */
3224
3225var RotateControl =
3226/*#__PURE__*/
3227function () {
3228 /**
3229 * Create new RotateControl instance
3230 * @param {object} options Options
3231 * @param {HTMLElement | null} [options.element] Target element.
3232 * @param {number} [options.duration=500] Motion's duration.
3233 * @param {function} [options.easing=(x: number) => 1 - Math.pow(1 - x, 3)] Motion's easing function.
3234 * @param {THREE.Vector2} [options.scale=new THREE.Vector2(1, 1)] Scale factor for panning, x is for horizontal and y is for vertical panning.
3235 * @param {boolean} [options.useGrabCursor=true] Whether to apply CSS style `cursor: grab` on the target element or not.
3236 * @param {boolean} [options.scaleToElement=true] Whether to scale control to fit element size.
3237 * @tutorial Adding Controls
3238 */
3239 function RotateControl(_a) {
3240 var _this = this;
3241
3242 var _b = _a === void 0 ? {} : _a,
3243 _c = _b.element,
3244 element = _c === void 0 ? NULL_ELEMENT : _c,
3245 _d = _b.duration,
3246 duration = _d === void 0 ? ANIMATION_DURATION : _d,
3247 _e = _b.easing,
3248 easing = _e === void 0 ? EASING : _e,
3249 _f = _b.scale,
3250 scale = _f === void 0 ? new Vector2(1, 1) : _f,
3251 _g = _b.useGrabCursor,
3252 useGrabCursor = _g === void 0 ? true : _g,
3253 _h = _b.scaleToElement,
3254 scaleToElement = _h === void 0 ? true : _h; // Internal values
3255
3256
3257 this._targetEl = null;
3258 this._screenScale = new Vector2(0, 0);
3259 this._prevPos = new Vector2(0, 0);
3260 this._enabled = false;
3261
3262 this._onMouseDown = function (evt) {
3263 if (evt.button !== MOUSE_BUTTON.LEFT) return;
3264 var targetEl = _this._targetEl;
3265 evt.preventDefault();
3266 targetEl.focus ? targetEl.focus() : window.focus();
3267
3268 _this._prevPos.set(evt.clientX, evt.clientY);
3269
3270 window.addEventListener(EVENTS.MOUSE_MOVE, _this._onMouseMove, false);
3271 window.addEventListener(EVENTS.MOUSE_UP, _this._onMouseUp, false);
3272
3273 _this._setCursor(CURSOR.GRABBING);
3274 };
3275
3276 this._onMouseMove = function (evt) {
3277 evt.preventDefault();
3278 var prevPos = _this._prevPos;
3279 var rotateDelta = new Vector2(evt.clientX, evt.clientY).sub(prevPos).multiply(_this._userScale);
3280
3281 if (_this._scaleToElement) {
3282 rotateDelta.multiply(_this._screenScale);
3283 }
3284
3285 _this._xMotion.setEndDelta(rotateDelta.x);
3286
3287 _this._yMotion.setEndDelta(rotateDelta.y);
3288
3289 prevPos.set(evt.clientX, evt.clientY);
3290 };
3291
3292 this._onMouseUp = function () {
3293 _this._prevPos.set(0, 0);
3294
3295 window.removeEventListener(EVENTS.MOUSE_MOVE, _this._onMouseMove, false);
3296 window.removeEventListener(EVENTS.MOUSE_UP, _this._onMouseUp, false);
3297
3298 _this._setCursor(CURSOR.GRAB);
3299 };
3300
3301 this._onTouchStart = function (evt) {
3302 evt.preventDefault();
3303 var touch = evt.touches[0];
3304
3305 _this._prevPos.set(touch.clientX, touch.clientY);
3306 };
3307
3308 this._onTouchMove = function (evt) {
3309 // Only the one finger motion should be considered
3310 if (evt.touches.length > 1) return;
3311
3312 if (evt.cancelable !== false) {
3313 evt.preventDefault();
3314 }
3315
3316 evt.stopPropagation();
3317 var touch = evt.touches[0];
3318 var prevPos = _this._prevPos;
3319 var rotateDelta = new Vector2(touch.clientX, touch.clientY).sub(prevPos).multiply(_this._userScale);
3320
3321 if (_this._scaleToElement) {
3322 rotateDelta.multiply(_this._screenScale);
3323 }
3324
3325 _this._xMotion.setEndDelta(rotateDelta.x);
3326
3327 _this._yMotion.setEndDelta(rotateDelta.y);
3328
3329 prevPos.set(touch.clientX, touch.clientY);
3330 };
3331
3332 this._onTouchEnd = function (evt) {
3333 var touch = evt.touches[0];
3334
3335 if (touch) {
3336 _this._prevPos.set(touch.clientX, touch.clientY);
3337 } else {
3338 _this._prevPos.set(0, 0);
3339 }
3340 };
3341
3342 var targetEl = getElement(element);
3343
3344 if (targetEl) {
3345 this.setElement(targetEl);
3346 }
3347
3348 this._userScale = scale;
3349 this._useGrabCursor = useGrabCursor;
3350 this._scaleToElement = scaleToElement;
3351 this._xMotion = new Motion({
3352 duration: duration,
3353 range: INFINITE_RANGE,
3354 easing: easing
3355 });
3356 this._yMotion = new Motion({
3357 duration: duration,
3358 range: PITCH_RANGE,
3359 easing: easing
3360 });
3361 }
3362
3363 var __proto = RotateControl.prototype;
3364 Object.defineProperty(__proto, "element", {
3365 /**
3366 * Control's current target element to attach event listeners
3367 * @readonly
3368 */
3369 get: function () {
3370 return this._targetEl;
3371 },
3372 enumerable: false,
3373 configurable: true
3374 });
3375 Object.defineProperty(__proto, "scale", {
3376 /**
3377 * Scale factor for panning, x is for horizontal and y is for vertical panning.
3378 * @type THREE.Vector2
3379 * @see https://threejs.org/docs/#api/en/math/Vector2
3380 * @example
3381 * const rotateControl = new View3D.RotateControl();
3382 * rotateControl.scale.setX(2);
3383 */
3384 get: function () {
3385 return this._userScale;
3386 },
3387 set: function (val) {
3388 this._userScale.copy(val);
3389 },
3390 enumerable: false,
3391 configurable: true
3392 });
3393 Object.defineProperty(__proto, "useGrabCursor", {
3394 /**
3395 * Whether to apply CSS style `cursor: grab` on the target element or not
3396 * @default true
3397 * @example
3398 * const rotateControl = new View3D.RotateControl();
3399 * rotateControl.useGrabCursor = true;
3400 */
3401 get: function () {
3402 return this._useGrabCursor;
3403 },
3404 set: function (val) {
3405 if (!val) {
3406 this._setCursor("");
3407
3408 this._useGrabCursor = false;
3409 } else {
3410 this._useGrabCursor = true;
3411
3412 this._setCursor(CURSOR.GRAB);
3413 }
3414 },
3415 enumerable: false,
3416 configurable: true
3417 });
3418 Object.defineProperty(__proto, "scaleToElement", {
3419 /**
3420 * Whether to scale control to fit element size.
3421 * When this is true and {@link RotateControl#scale scale.x} is 1, panning through element's width will make 3d model's yaw rotate 360°.
3422 * When this is true and {@link RotateControl#scale scale.y} is 1, panning through element's height will make 3d model's pitch rotate 180°.
3423 * @default true
3424 * @example
3425 * import View3D, { RotateControl } from "@egjs/view3d";
3426 * const view3d = new View3D("#view3d-canvas");
3427 * const rotateControl = new RotateControl();
3428 * rotateControl.scaleToElement = true;
3429 * view3d.controller.add(rotateControl);
3430 * view3d.resize();
3431 */
3432 get: function () {
3433 return this._scaleToElement;
3434 },
3435 set: function (val) {
3436 this._scaleToElement = val;
3437 },
3438 enumerable: false,
3439 configurable: true
3440 });
3441 Object.defineProperty(__proto, "enabled", {
3442 /**
3443 * Whether this control is enabled or not
3444 * @readonly
3445 */
3446 get: function () {
3447 return this._enabled;
3448 },
3449 enumerable: false,
3450 configurable: true
3451 });
3452 /**
3453 * Destroy the instance and remove all event listeners attached
3454 * This also will reset CSS cursor to intial
3455 * @returns {void} Nothing
3456 */
3457
3458 __proto.destroy = function () {
3459 this.disable();
3460 };
3461 /**
3462 * Update control by given deltaTime
3463 * @param camera Camera to update position
3464 * @param deltaTime Number of milisec to update
3465 * @returns {void} Nothing
3466 */
3467
3468
3469 __proto.update = function (camera, deltaTime) {
3470 var xMotion = this._xMotion;
3471 var yMotion = this._yMotion;
3472 var delta = new Vector2(xMotion.update(deltaTime), yMotion.update(deltaTime));
3473 camera.yaw += delta.x;
3474 camera.pitch += delta.y;
3475 };
3476 /**
3477 * Resize control to match target size
3478 * This method is only meaningful when {@link RotateControl#scaleToElement scaleToElement} is enabled
3479 * @param size {@link https://threejs.org/docs/#api/en/math/Vector2 THREE.Vector2} instance of width(x), height(y)
3480 */
3481
3482
3483 __proto.resize = function (size) {
3484 this._screenScale.set(360 / size.x, 180 / size.y);
3485 };
3486 /**
3487 * Enable this input and add event listeners
3488 * @returns {void} Nothing
3489 */
3490
3491
3492 __proto.enable = function () {
3493 if (this._enabled) return;
3494
3495 if (!this._targetEl) {
3496 throw new View3DError(MESSAGES.ADD_CONTROL_FIRST, CODES.ADD_CONTROL_FIRST);
3497 }
3498
3499 var targetEl = this._targetEl;
3500 targetEl.addEventListener(EVENTS.MOUSE_DOWN, this._onMouseDown, false);
3501 targetEl.addEventListener(EVENTS.TOUCH_START, this._onTouchStart, false);
3502 targetEl.addEventListener(EVENTS.TOUCH_MOVE, this._onTouchMove, false);
3503 targetEl.addEventListener(EVENTS.TOUCH_END, this._onTouchEnd, false);
3504 this._enabled = true;
3505
3506 this._setCursor(CURSOR.GRAB);
3507 };
3508 /**
3509 * Disable this input and remove all event handlers
3510 * @returns {void} Nothing
3511 */
3512
3513
3514 __proto.disable = function () {
3515 if (!this._enabled || !this._targetEl) return;
3516 var targetEl = this._targetEl;
3517 targetEl.removeEventListener(EVENTS.MOUSE_DOWN, this._onMouseDown, false);
3518 window.removeEventListener(EVENTS.MOUSE_MOVE, this._onMouseMove, false);
3519 window.removeEventListener(EVENTS.MOUSE_UP, this._onMouseUp, false);
3520 targetEl.removeEventListener(EVENTS.TOUCH_START, this._onTouchStart, false);
3521 targetEl.removeEventListener(EVENTS.TOUCH_MOVE, this._onTouchMove, false);
3522 targetEl.removeEventListener(EVENTS.TOUCH_END, this._onTouchEnd, false);
3523
3524 this._setCursor("");
3525
3526 this._enabled = false;
3527 };
3528 /**
3529 * Synchronize this control's state to given camera position
3530 * @param camera Camera to match state
3531 * @returns {void} Nothing
3532 */
3533
3534
3535 __proto.sync = function (camera) {
3536 this._xMotion.reset(camera.yaw);
3537
3538 this._yMotion.reset(camera.pitch);
3539 };
3540 /**
3541 * Set target element to attach event listener
3542 * @param element target element
3543 * @returns {void} Nothing
3544 */
3545
3546
3547 __proto.setElement = function (element) {
3548 this._targetEl = element;
3549 this.resize(new Vector2(element.offsetWidth, element.offsetHeight));
3550 };
3551
3552 __proto._setCursor = function (val) {
3553 var targetEl = this._targetEl;
3554 if (!this._useGrabCursor || !targetEl || !this._enabled) return;
3555 targetEl.style.cursor = val;
3556 };
3557
3558 return RotateControl;
3559}();
3560
3561/*
3562 * Copyright (c) 2020 NAVER Corp.
3563 * egjs projects are licensed under the MIT license
3564 */
3565/**
3566 * Model's translation control that supports both mouse & touch
3567 * @category Controls
3568 */
3569
3570var TranslateControl =
3571/*#__PURE__*/
3572function () {
3573 /**
3574 * Create new TranslateControl instance
3575 * @param {object} options Options
3576 * @param {HTMLElement | null} [options.element] Target element.
3577 * @param {function} [options.easing=(x: number) => 1 - Math.pow(1 - x, 3)] Motion's easing function.
3578 * @param {THREE.Vector2} [options.scale=new THREE.Vector2(1, 1)] Scale factor for translation.
3579 * @param {boolean} [options.useGrabCursor=true] Whether to apply CSS style `cursor: grab` on the target element or not.
3580 * @param {boolean} [options.scaleToElement=true] Whether to scale control to fit element size.
3581 * @tutorial Adding Controls
3582 */
3583 function TranslateControl(_a) {
3584 var _this = this;
3585
3586 var _b = _a === void 0 ? {} : _a,
3587 _c = _b.element,
3588 element = _c === void 0 ? NULL_ELEMENT : _c,
3589 _d = _b.easing,
3590 easing = _d === void 0 ? EASING : _d,
3591 _e = _b.scale,
3592 scale = _e === void 0 ? new Vector2(1, 1) : _e,
3593 _f = _b.useGrabCursor,
3594 useGrabCursor = _f === void 0 ? true : _f,
3595 _g = _b.scaleToElement,
3596 scaleToElement = _g === void 0 ? true : _g; // Internal values
3597
3598
3599 this._targetEl = null;
3600 this._enabled = false; // Sometimes, touchstart for second finger doesn't triggered.
3601 // This flag checks whether that happened
3602
3603 this._touchInitialized = false;
3604 this._prevPos = new Vector2(0, 0);
3605 this._screenSize = new Vector2(0, 0);
3606
3607 this._onMouseDown = function (evt) {
3608 if (evt.button !== MOUSE_BUTTON.RIGHT) return;
3609 var targetEl = _this._targetEl;
3610 evt.preventDefault();
3611 targetEl.focus ? targetEl.focus() : window.focus();
3612
3613 _this._prevPos.set(evt.clientX, evt.clientY);
3614
3615 window.addEventListener(EVENTS.MOUSE_MOVE, _this._onMouseMove, false);
3616 window.addEventListener(EVENTS.MOUSE_UP, _this._onMouseUp, false);
3617
3618 _this._setCursor(CURSOR.GRABBING);
3619 };
3620
3621 this._onMouseMove = function (evt) {
3622 evt.preventDefault();
3623 var prevPos = _this._prevPos;
3624 var delta = new Vector2(evt.clientX, evt.clientY).sub(prevPos).multiply(_this._userScale); // X value is negated to match cursor direction
3625
3626 _this._xMotion.setEndDelta(-delta.x);
3627
3628 _this._yMotion.setEndDelta(delta.y);
3629
3630 prevPos.set(evt.clientX, evt.clientY);
3631 };
3632
3633 this._onMouseUp = function () {
3634 _this._prevPos.set(0, 0);
3635
3636 window.removeEventListener(EVENTS.MOUSE_MOVE, _this._onMouseMove, false);
3637 window.removeEventListener(EVENTS.MOUSE_UP, _this._onMouseUp, false);
3638
3639 _this._setCursor(CURSOR.GRAB);
3640 };
3641
3642 this._onTouchStart = function (evt) {
3643 // Only the two finger motion should be considered
3644 if (evt.touches.length !== 2) return;
3645 evt.preventDefault();
3646
3647 _this._prevPos.copy(_this._getTouchesMiddle(evt.touches));
3648
3649 _this._touchInitialized = true;
3650 };
3651
3652 this._onTouchMove = function (evt) {
3653 // Only the two finger motion should be considered
3654 if (evt.touches.length !== 2) return;
3655
3656 if (evt.cancelable !== false) {
3657 evt.preventDefault();
3658 }
3659
3660 evt.stopPropagation();
3661 var prevPos = _this._prevPos;
3662
3663 var middlePoint = _this._getTouchesMiddle(evt.touches);
3664
3665 if (!_this._touchInitialized) {
3666 prevPos.copy(middlePoint);
3667 _this._touchInitialized = true;
3668 return;
3669 }
3670
3671 var delta = new Vector2().subVectors(middlePoint, prevPos).multiply(_this._userScale); // X value is negated to match cursor direction
3672
3673 _this._xMotion.setEndDelta(-delta.x);
3674
3675 _this._yMotion.setEndDelta(delta.y);
3676
3677 prevPos.copy(middlePoint);
3678 };
3679
3680 this._onTouchEnd = function (evt) {
3681 // Only the two finger motion should be considered
3682 if (evt.touches.length !== 2) {
3683 _this._touchInitialized = false;
3684 return;
3685 } // Three fingers to two fingers
3686
3687
3688 _this._prevPos.copy(_this._getTouchesMiddle(evt.touches));
3689
3690 _this._touchInitialized = true;
3691 };
3692
3693 this._onContextMenu = function (evt) {
3694 evt.preventDefault();
3695 };
3696
3697 var targetEl = getElement(element);
3698
3699 if (targetEl) {
3700 this.setElement(targetEl);
3701 }
3702
3703 this._xMotion = new Motion({
3704 duration: 0,
3705 range: INFINITE_RANGE,
3706 easing: easing
3707 });
3708 this._yMotion = new Motion({
3709 duration: 0,
3710 range: INFINITE_RANGE,
3711 easing: easing
3712 });
3713 this._userScale = scale;
3714 this._useGrabCursor = useGrabCursor;
3715 this._scaleToElement = scaleToElement;
3716 }
3717
3718 var __proto = TranslateControl.prototype;
3719 Object.defineProperty(__proto, "element", {
3720 /**
3721 * Control's current target element to attach event listeners
3722 * @readonly
3723 */
3724 get: function () {
3725 return this._targetEl;
3726 },
3727 enumerable: false,
3728 configurable: true
3729 });
3730 Object.defineProperty(__proto, "scale", {
3731 /**
3732 * Scale factor for translation
3733 * @type THREE.Vector2
3734 * @see https://threejs.org/docs/#api/en/math/Vector2
3735 * @example
3736 * import { TranslateControl } from "@egjs/view3d";
3737 * const translateControl = new TranslateControl();
3738 * translateControl.scale.set(2, 2);
3739 */
3740 get: function () {
3741 return this._userScale;
3742 },
3743 set: function (val) {
3744 this._userScale.copy(val);
3745 },
3746 enumerable: false,
3747 configurable: true
3748 });
3749 Object.defineProperty(__proto, "useGrabCursor", {
3750 /**
3751 * Whether to apply CSS style `cursor: grab` on the target element or not
3752 * @default true
3753 * @example
3754 * import { TranslateControl } from "@egjs/view3d";
3755 * const translateControl = new TranslateControl();
3756 * translateControl.useGrabCursor = true;
3757 */
3758 get: function () {
3759 return this._useGrabCursor;
3760 },
3761 set: function (val) {
3762 if (!val) {
3763 this._setCursor("");
3764
3765 this._useGrabCursor = false;
3766 } else {
3767 this._useGrabCursor = true;
3768
3769 this._setCursor(CURSOR.GRAB);
3770 }
3771 },
3772 enumerable: false,
3773 configurable: true
3774 });
3775 Object.defineProperty(__proto, "scaleToElement", {
3776 /**
3777 * Scale control to fit element size.
3778 * When this is true, camera's pivot change will correspond same amount you've dragged.
3779 * @default true
3780 * @example
3781 * import View3D, { TranslateControl } from "@egjs/view3d";
3782 * const view3d = new View3D("#view3d-canvas");
3783 * const translateControl = new TranslateControl();
3784 * translateControl.scaleToElement = true;
3785 * view3d.controller.add(translateControl);
3786 * view3d.resize();
3787 */
3788 get: function () {
3789 return this._scaleToElement;
3790 },
3791 set: function (val) {
3792 this._scaleToElement = val;
3793 },
3794 enumerable: false,
3795 configurable: true
3796 });
3797 Object.defineProperty(__proto, "enabled", {
3798 /**
3799 * Whether this control is enabled or not
3800 * @readonly
3801 */
3802 get: function () {
3803 return this._enabled;
3804 },
3805 enumerable: false,
3806 configurable: true
3807 });
3808 /**
3809 * Destroy the instance and remove all event listeners attached
3810 * This also will reset CSS cursor to intial
3811 * @returns {void} Nothing
3812 */
3813
3814 __proto.destroy = function () {
3815 this.disable();
3816 };
3817 /**
3818 * Update control by given deltaTime
3819 * @param deltaTime Number of milisec to update
3820 * @returns {void} Nothing
3821 */
3822
3823
3824 __proto.update = function (camera, deltaTime) {
3825 var screenSize = this._screenSize;
3826 var delta = new Vector2(this._xMotion.update(deltaTime), this._yMotion.update(deltaTime));
3827 var viewXDir = new Vector3(1, 0, 0).applyQuaternion(camera.threeCamera.quaternion);
3828 var viewYDir = new Vector3(0, 1, 0).applyQuaternion(camera.threeCamera.quaternion);
3829
3830 if (this._scaleToElement) {
3831 var screenScale = new Vector2(camera.renderWidth, camera.renderHeight).divide(screenSize);
3832 delta.multiply(screenScale);
3833 }
3834
3835 camera.pivot.add(viewXDir.multiplyScalar(delta.x));
3836 camera.pivot.add(viewYDir.multiplyScalar(delta.y));
3837 };
3838 /**
3839 * Resize control to match target size
3840 * This method is only meaningful when {@link RotateControl#scaleToElementSize scaleToElementSize} is enabled
3841 * @param size {@link https://threejs.org/docs/#api/en/math/Vector2 THREE.Vector2} instance of width(x), height(y)
3842 */
3843
3844
3845 __proto.resize = function (size) {
3846 var screenSize = this._screenSize;
3847 screenSize.copy(size);
3848 };
3849 /**
3850 * Enable this input and add event listeners
3851 * @returns {void} Nothing
3852 */
3853
3854
3855 __proto.enable = function () {
3856 if (this._enabled) return;
3857
3858 if (!this._targetEl) {
3859 throw new View3DError(MESSAGES.ADD_CONTROL_FIRST, CODES.ADD_CONTROL_FIRST);
3860 }
3861
3862 var targetEl = this._targetEl;
3863 targetEl.addEventListener(EVENTS.MOUSE_DOWN, this._onMouseDown, false);
3864 targetEl.addEventListener(EVENTS.TOUCH_START, this._onTouchStart, false);
3865 targetEl.addEventListener(EVENTS.TOUCH_MOVE, this._onTouchMove, false);
3866 targetEl.addEventListener(EVENTS.TOUCH_END, this._onTouchEnd, false);
3867 targetEl.addEventListener(EVENTS.CONTEXT_MENU, this._onContextMenu, false);
3868 this._enabled = true;
3869
3870 this._setCursor(CURSOR.GRAB);
3871 };
3872 /**
3873 * Disable this input and remove all event handlers
3874 * @returns {void} Nothing
3875 */
3876
3877
3878 __proto.disable = function () {
3879 if (!this._enabled || !this._targetEl) return;
3880 var targetEl = this._targetEl;
3881 targetEl.removeEventListener(EVENTS.MOUSE_DOWN, this._onMouseDown, false);
3882 window.removeEventListener(EVENTS.MOUSE_MOVE, this._onMouseMove, false);
3883 window.removeEventListener(EVENTS.MOUSE_UP, this._onMouseUp, false);
3884 targetEl.removeEventListener(EVENTS.TOUCH_START, this._onTouchStart, false);
3885 targetEl.removeEventListener(EVENTS.TOUCH_MOVE, this._onTouchMove, false);
3886 targetEl.removeEventListener(EVENTS.TOUCH_END, this._onTouchEnd, false);
3887 targetEl.removeEventListener(EVENTS.CONTEXT_MENU, this._onContextMenu, false);
3888
3889 this._setCursor("");
3890
3891 this._enabled = false;
3892 };
3893 /**
3894 * Synchronize this control's state to given camera position
3895 * @param camera Camera to match state
3896 * @returns {void} Nothing
3897 */
3898
3899
3900 __proto.sync = function (camera) {
3901 this._xMotion.reset(0);
3902
3903 this._yMotion.reset(0);
3904 };
3905 /**
3906 * Set target element to attach event listener
3907 * @param element target element
3908 * @returns {void} Nothing
3909 */
3910
3911
3912 __proto.setElement = function (element) {
3913 this._targetEl = element;
3914 this.resize(new Vector2(element.offsetWidth, element.offsetHeight));
3915 };
3916
3917 __proto._setCursor = function (val) {
3918 var targetEl = this._targetEl;
3919 if (!this._useGrabCursor || !targetEl || !this._enabled) return;
3920 targetEl.style.cursor = val;
3921 };
3922
3923 __proto._getTouchesMiddle = function (touches) {
3924 return new Vector2(touches[0].clientX + touches[1].clientX, touches[0].clientY + touches[1].clientY).multiplyScalar(0.5);
3925 };
3926
3927 return TranslateControl;
3928}();
3929
3930/*
3931 * Copyright (c) 2020 NAVER Corp.
3932 * egjs projects are licensed under the MIT license
3933 */
3934/**
3935 * Distance controller handling both mouse wheel and pinch zoom
3936 * @category Controls
3937 */
3938
3939var DistanceControl =
3940/*#__PURE__*/
3941function () {
3942 /**
3943 * Create new DistanceControl instance
3944 * @tutorial Adding Controls
3945 * @param {object} options Options
3946 * @param {HTMLElement | string | null} [options.element=null] Attaching element / selector of the element.
3947 * @param {number} [options.duration=500] Motion's duration.
3948 * @param {object} [options.range={min: 0, max: 500}] Motion's range.
3949 * @param {function} [options.easing=(x: number) => 1 - Math.pow(1 - x, 3)] Motion's easing function.
3950 */
3951 function DistanceControl(_a) {
3952 var _this = this;
3953
3954 var _b = _a === void 0 ? {} : _a,
3955 _c = _b.element,
3956 element = _c === void 0 ? NULL_ELEMENT : _c,
3957 _d = _b.duration,
3958 duration = _d === void 0 ? ANIMATION_DURATION : _d,
3959 _e = _b.range,
3960 range = _e === void 0 ? DISTANCE_RANGE : _e,
3961 _f = _b.easing,
3962 easing = _f === void 0 ? EASING : _f; // Options
3963
3964
3965 this._scale = 1; // Internal values
3966
3967 this._targetEl = null;
3968 this._scaleModifier = 0.25;
3969 this._prevTouchDistance = -1;
3970 this._enabled = false;
3971
3972 this._onWheel = function (evt) {
3973 if (evt.deltaY === 0) return;
3974 evt.preventDefault();
3975 evt.stopPropagation();
3976 var animation = _this._motion;
3977 var delta = _this._scale * _this._scaleModifier * evt.deltaY;
3978 animation.setEndDelta(delta);
3979 };
3980
3981 this._onTouchMove = function (evt) {
3982 var touches = evt.touches;
3983 if (touches.length !== 2) return;
3984
3985 if (evt.cancelable !== false) {
3986 evt.preventDefault();
3987 }
3988
3989 evt.stopPropagation();
3990 var animation = _this._motion;
3991 var prevTouchDistance = _this._prevTouchDistance;
3992 var touchPoint1 = new Vector2(touches[0].pageX, touches[0].pageY);
3993 var touchPoint2 = new Vector2(touches[1].pageX, touches[1].pageY);
3994 var touchDiff = touchPoint1.sub(touchPoint2);
3995
3996 var touchDistance = touchDiff.length() * _this._scale * _this._scaleModifier;
3997
3998 var delta = -(touchDistance - prevTouchDistance);
3999 _this._prevTouchDistance = touchDistance;
4000 if (prevTouchDistance < 0) return;
4001 animation.setEndDelta(delta);
4002 };
4003
4004 this._onTouchEnd = function () {
4005 _this._prevTouchDistance = -1;
4006 };
4007
4008 var targetEl = getElement(element);
4009
4010 if (targetEl) {
4011 this.setElement(targetEl);
4012 }
4013
4014 this._motion = new Motion({
4015 duration: duration,
4016 range: range,
4017 easing: easing
4018 });
4019 }
4020
4021 var __proto = DistanceControl.prototype;
4022 Object.defineProperty(__proto, "element", {
4023 /**
4024 * Control's current target element to attach event listeners
4025 * @readonly
4026 */
4027 get: function () {
4028 return this._targetEl;
4029 },
4030 enumerable: false,
4031 configurable: true
4032 });
4033 Object.defineProperty(__proto, "scale", {
4034 /**
4035 * Scale factor of the distance
4036 * @type number
4037 * @example
4038 * import { DistanceControl } from "@egjs/view3d";
4039 * const distanceControl = new DistanceControl();
4040 * distanceControl.scale = 2;
4041 */
4042 get: function () {
4043 return this._scale;
4044 },
4045 set: function (val) {
4046 this._scale = val;
4047 },
4048 enumerable: false,
4049 configurable: true
4050 });
4051 Object.defineProperty(__proto, "enabled", {
4052 /**
4053 * Whether this control is enabled or not
4054 * @readonly
4055 */
4056 get: function () {
4057 return this._enabled;
4058 },
4059 enumerable: false,
4060 configurable: true
4061 });
4062 /**
4063 * Destroy the instance and remove all event listeners attached
4064 * @returns {void} Nothing
4065 */
4066
4067 __proto.destroy = function () {
4068 this.disable();
4069 };
4070 /**
4071 * Update control by given deltaTime
4072 * @param camera Camera to update position
4073 * @param deltaTime Number of milisec to update
4074 * @returns {void} Nothing
4075 */
4076
4077
4078 __proto.update = function (camera, deltaTime) {
4079 var motion = this._motion;
4080 camera.distance += motion.update(deltaTime);
4081 }; // This is not documetned on purpose as it doesn't do nothing
4082
4083
4084 __proto.resize = function (size) {// DO NOTHING
4085 };
4086 /**
4087 * Enable this input and add event listeners
4088 * @returns {void} Nothing
4089 */
4090
4091
4092 __proto.enable = function () {
4093 if (this._enabled) return;
4094
4095 if (!this._targetEl) {
4096 throw new View3DError(MESSAGES.ADD_CONTROL_FIRST, CODES.ADD_CONTROL_FIRST);
4097 }
4098
4099 var targetEl = this._targetEl;
4100 targetEl.addEventListener(EVENTS.WHEEL, this._onWheel, false);
4101 targetEl.addEventListener(EVENTS.TOUCH_MOVE, this._onTouchMove, false);
4102 targetEl.addEventListener(EVENTS.TOUCH_END, this._onTouchEnd, false);
4103 this._enabled = true;
4104 };
4105 /**
4106 * Disable this input and remove all event handlers
4107 * @returns {void} Nothing
4108 */
4109
4110
4111 __proto.disable = function () {
4112 if (!this._enabled || !this._targetEl) return;
4113 var targetEl = this._targetEl;
4114 targetEl.removeEventListener(EVENTS.WHEEL, this._onWheel, false);
4115 targetEl.removeEventListener(EVENTS.TOUCH_MOVE, this._onTouchMove, false);
4116 targetEl.removeEventListener(EVENTS.TOUCH_END, this._onTouchEnd, false);
4117 this._enabled = false;
4118 };
4119 /**
4120 * Synchronize this control's state to given camera position
4121 * @param camera Camera to match state
4122 * @returns {void} Nothing
4123 */
4124
4125
4126 __proto.sync = function (camera) {
4127 this._motion.range.min = camera.minDistance;
4128 this._motion.range.max = camera.maxDistance;
4129
4130 this._motion.reset(camera.distance);
4131 };
4132 /**
4133 * Set target element to attach event listener
4134 * @param element target element
4135 * @returns {void} Nothing
4136 */
4137
4138
4139 __proto.setElement = function (element) {
4140 this._targetEl = element;
4141 };
4142
4143 return DistanceControl;
4144}();
4145
4146/*
4147 * Copyright (c) 2020 NAVER Corp.
4148 * egjs projects are licensed under the MIT license
4149 */
4150/**
4151 * Aggregation of {@link RotateControl}, {@link TranslateControl}, and {@link DistanceControl}.
4152 * @category Controls
4153 */
4154
4155var OrbitControl =
4156/*#__PURE__*/
4157function () {
4158 /**
4159 * Create new OrbitControl instance
4160 * @param {object} options Options
4161 * @param {HTMLElement | string | null} [options.element=null] Attaching element / selector of the element
4162 * @param {object} [options.rotate={}] Constructor options of {@link RotateControl}
4163 * @param {object} [options.translate={}] Constructor options of {@link TranslateControl}
4164 * @param {object} [options.distance={}] Constructor options of {@link DistanceControl}
4165 * @tutorial Adding Controls
4166 */
4167 function OrbitControl(_a) {
4168 var _b = _a === void 0 ? {} : _a,
4169 _c = _b.element,
4170 element = _c === void 0 ? NULL_ELEMENT : _c,
4171 _d = _b.rotate,
4172 rotate = _d === void 0 ? {} : _d,
4173 _e = _b.translate,
4174 translate = _e === void 0 ? {} : _e,
4175 _f = _b.distance,
4176 distance = _f === void 0 ? {} : _f;
4177
4178 this._enabled = false;
4179 this._targetEl = getElement(element);
4180 this._rotateControl = new RotateControl(__assign(__assign({}, rotate), {
4181 element: rotate.element || this._targetEl
4182 }));
4183 this._translateControl = new TranslateControl(__assign(__assign({}, translate), {
4184 element: translate.element || this._targetEl
4185 }));
4186 this._distanceControl = new DistanceControl(__assign(__assign({}, distance), {
4187 element: distance.element || this._targetEl
4188 }));
4189 }
4190
4191 var __proto = OrbitControl.prototype;
4192 Object.defineProperty(__proto, "element", {
4193 /**
4194 * Control's current target element to attach event listeners
4195 * @readonly
4196 */
4197 get: function () {
4198 return this._targetEl;
4199 },
4200 enumerable: false,
4201 configurable: true
4202 });
4203 Object.defineProperty(__proto, "enabled", {
4204 /**
4205 * Whether this control is enabled or not
4206 * @readonly
4207 */
4208 get: function () {
4209 return this._enabled;
4210 },
4211 enumerable: false,
4212 configurable: true
4213 });
4214 Object.defineProperty(__proto, "rotate", {
4215 /**
4216 * {@link RotateControl} of this control
4217 */
4218 get: function () {
4219 return this._rotateControl;
4220 },
4221 enumerable: false,
4222 configurable: true
4223 });
4224 Object.defineProperty(__proto, "translate", {
4225 /**
4226 * {@link TranslateControl} of this control
4227 */
4228 get: function () {
4229 return this._translateControl;
4230 },
4231 enumerable: false,
4232 configurable: true
4233 });
4234 Object.defineProperty(__proto, "distance", {
4235 /**
4236 * {@link DistanceControl} of this control
4237 */
4238 get: function () {
4239 return this._distanceControl;
4240 },
4241 enumerable: false,
4242 configurable: true
4243 });
4244 /**
4245 * Destroy the instance and remove all event listeners attached
4246 * This also will reset CSS cursor to intial
4247 * @returns {void} Nothing
4248 */
4249
4250 __proto.destroy = function () {
4251 this._rotateControl.destroy();
4252
4253 this._translateControl.destroy();
4254
4255 this._distanceControl.destroy();
4256 };
4257 /**
4258 * Update control by given deltaTime
4259 * @param camera Camera to update position
4260 * @param deltaTime Number of milisec to update
4261 * @returns {void} Nothing
4262 */
4263
4264
4265 __proto.update = function (camera, deltaTime) {
4266 this._rotateControl.update(camera, deltaTime);
4267
4268 this._translateControl.update(camera, deltaTime);
4269
4270 this._distanceControl.update(camera, deltaTime);
4271 };
4272 /**
4273 * Resize control to match target size
4274 * @param size {@link https://threejs.org/docs/#api/en/math/Vector2 THREE.Vector2} instance of width(x), height(y)
4275 */
4276
4277
4278 __proto.resize = function (size) {
4279 this._rotateControl.resize(size);
4280
4281 this._translateControl.resize(size);
4282
4283 this._distanceControl.resize(size);
4284 };
4285 /**
4286 * Enable this control and add event listeners
4287 * @returns {void} Nothingß
4288 */
4289
4290
4291 __proto.enable = function () {
4292 if (this._enabled) return;
4293
4294 if (!this._targetEl) {
4295 throw new View3DError(MESSAGES.ADD_CONTROL_FIRST, CODES.ADD_CONTROL_FIRST);
4296 }
4297
4298 this._rotateControl.enable();
4299
4300 this._translateControl.enable();
4301
4302 this._distanceControl.enable();
4303
4304 this._enabled = true;
4305 };
4306 /**
4307 * Disable this control and remove all event handlers
4308 * @returns {void} Nothing
4309 */
4310
4311
4312 __proto.disable = function () {
4313 if (!this._enabled || !this._targetEl) return;
4314
4315 this._rotateControl.disable();
4316
4317 this._translateControl.disable();
4318
4319 this._distanceControl.disable();
4320
4321 this._enabled = false;
4322 };
4323 /**
4324 * Synchronize this control's state to given camera position
4325 * @param camera Camera to match state
4326 * @returns {void} Nothing
4327 */
4328
4329
4330 __proto.sync = function (camera) {
4331 this._rotateControl.sync(camera);
4332
4333 this._translateControl.sync(camera);
4334
4335 this._distanceControl.sync(camera);
4336 };
4337 /**
4338 * Set target element to attach event listener
4339 * @param element target element
4340 * @returns {void} Nothing
4341 */
4342
4343
4344 __proto.setElement = function (element) {
4345 this._targetEl = element;
4346
4347 this._rotateControl.setElement(element);
4348
4349 this._translateControl.setElement(element);
4350
4351 this._distanceControl.setElement(element);
4352 };
4353
4354 return OrbitControl;
4355}();
4356
4357/**
4358 * DracoLoader
4359 * @category Loaders
4360 */
4361
4362var DracoLoader =
4363/*#__PURE__*/
4364function () {
4365 function DracoLoader() {}
4366 /**
4367 * Load new DRC model from the given url
4368 * @param url URL to fetch .drc file
4369 * @param options Options for a loaded model
4370 * @returns Promise that resolves {@link Model}
4371 */
4372
4373
4374 var __proto = DracoLoader.prototype;
4375
4376 __proto.load = function (url, options) {
4377 var _this = this;
4378
4379 if (options === void 0) {
4380 options = {};
4381 }
4382
4383 var loader = new DRACOLoader();
4384 loader.setCrossOrigin("anonymous");
4385 loader.setDecoderPath(DRACO_DECODER_URL);
4386 loader.manager = new LoadingManager();
4387 return new Promise(function (resolve, reject) {
4388 loader.load(url, function (geometry) {
4389 var model = _this._parseToModel(geometry, options);
4390
4391 loader.dispose();
4392 resolve(model);
4393 }, undefined, function (err) {
4394 loader.dispose();
4395 reject(err);
4396 });
4397 });
4398 };
4399
4400 __proto._parseToModel = function (geometry, _a) {
4401 var _b = _a === void 0 ? {} : _a,
4402 _c = _b.fixSkinnedBbox,
4403 fixSkinnedBbox = _c === void 0 ? false : _c,
4404 _d = _b.color,
4405 color = _d === void 0 ? 0xffffff : _d,
4406 _e = _b.point,
4407 point = _e === void 0 ? false : _e,
4408 _f = _b.pointOptions,
4409 pointOptions = _f === void 0 ? {} : _f;
4410
4411 geometry.computeVertexNormals();
4412 var material = point ? new PointsMaterial(__assign({
4413 color: color
4414 }, pointOptions)) : new MeshStandardMaterial({
4415 color: color
4416 });
4417 var mesh = point ? new Points(geometry, material) : new Mesh(geometry, material);
4418 var model = new Model({
4419 scenes: [mesh],
4420 fixSkinnedBbox: fixSkinnedBbox
4421 });
4422 return model;
4423 };
4424
4425 return DracoLoader;
4426}();
4427
4428/*
4429 * Copyright (c) 2020 NAVER Corp.
4430 * egjs projects are licensed under the MIT license
4431 */
4432/**
4433 * THREE.DirectionalLight wrapper that will automatically update its shadow size to model
4434 * Shadow is enabled by default, use {@link AutoDirectionalLight#disableShadow disableShadow} to disable it
4435 * @category Environment
4436 */
4437
4438var AutoDirectionalLight =
4439/*#__PURE__*/
4440function () {
4441 /**
4442 * Create new instance of AutoDirectionalLight
4443 * @param [color="#ffffff"] Color of the light
4444 * @param [intensity=1] Intensity of the light
4445 * @param {object} [options={}] Additional options
4446 * @param {THREE.Vector3} [options.direction=new THREE.Vector3(-1, -1, -1)] Direction of the light
4447 */
4448 function AutoDirectionalLight(color, intensity, _a) {
4449 if (color === void 0) {
4450 color = "#ffffff";
4451 }
4452
4453 if (intensity === void 0) {
4454 intensity = 1;
4455 }
4456
4457 var _b = (_a === void 0 ? {} : _a).direction,
4458 direction = _b === void 0 ? new Vector3(-1, -1, -1) : _b;
4459 this._light = new DirectionalLight(color, intensity); // Set the default position ratio of the directional light
4460
4461 var light = this._light;
4462 light.castShadow = true; // Is enabled by default
4463
4464 light.shadow.mapSize.width = 2048;
4465 light.shadow.mapSize.height = 2048;
4466 light.matrixAutoUpdate = false;
4467 this._direction = direction.clone().normalize();
4468 }
4469
4470 var __proto = AutoDirectionalLight.prototype;
4471 Object.defineProperty(__proto, "objects", {
4472 /**
4473 * Array of lights that used in this preset
4474 * @see https://threejs.org/docs/#api/en/lights/Light
4475 */
4476 get: function () {
4477 return [this._light, this._light.target];
4478 },
4479 enumerable: false,
4480 configurable: true
4481 });
4482 Object.defineProperty(__proto, "light", {
4483 /**
4484 * The actual THREE.DirectionalLight
4485 * @type THREE#DirectionalLight
4486 * @see https://threejs.org/docs/#api/en/lights/DirectionalLight
4487 */
4488 get: function () {
4489 return this._light;
4490 },
4491 enumerable: false,
4492 configurable: true
4493 });
4494 Object.defineProperty(__proto, "position", {
4495 /**
4496 * Position of the light
4497 * @type THREE#Vector3
4498 * @see https://threejs.org/docs/#api/en/math/Vector3
4499 */
4500 get: function () {
4501 return this._light.position;
4502 },
4503 enumerable: false,
4504 configurable: true
4505 });
4506 Object.defineProperty(__proto, "direction", {
4507 get: function () {
4508 return this._direction;
4509 },
4510 enumerable: false,
4511 configurable: true
4512 });
4513 /**
4514 * Make light cast a shadow
4515 */
4516
4517 __proto.enableShadow = function () {
4518 this._light.castShadow = true;
4519 };
4520 /**
4521 * Make light don't cast a shadow
4522 */
4523
4524
4525 __proto.disableShadow = function () {
4526 this._light.castShadow = false;
4527 };
4528 /**
4529 * Modify light's position & shadow camera size from model's bounding box
4530 * @param model Model to fit size
4531 * @param scale Scale factor for shadow camera size
4532 */
4533
4534
4535 __proto.fit = function (model, _a) {
4536 var _b = (_a === void 0 ? {} : _a).scale,
4537 scale = _b === void 0 ? 1.5 : _b;
4538 var bbox = model.bbox;
4539 var light = this._light;
4540 var direction = this._direction;
4541 var boxSize = bbox.getSize(new Vector3()).length();
4542 var boxCenter = bbox.getCenter(new Vector3()); // Position fitting
4543
4544 var newPos = new Vector3().addVectors(boxCenter, direction.clone().negate().multiplyScalar(boxSize * 0.5));
4545 light.position.copy(newPos);
4546 light.target.position.copy(boxCenter);
4547 light.updateMatrix(); // Shadowcam fitting
4548
4549 var shadowCam = light.shadow.camera;
4550 shadowCam.near = 0;
4551 shadowCam.far = 2 * boxSize;
4552 shadowCam.position.copy(newPos);
4553 shadowCam.lookAt(boxCenter);
4554 shadowCam.left = -1;
4555 shadowCam.right = 1;
4556 shadowCam.top = 1;
4557 shadowCam.bottom = -1;
4558 shadowCam.updateMatrixWorld();
4559 shadowCam.updateProjectionMatrix();
4560 var bboxPoints = getBoxPoints(bbox);
4561 var projectedPoints = bboxPoints.map(function (position) {
4562 return position.project(shadowCam);
4563 });
4564 var screenBbox = new Box3().setFromPoints(projectedPoints);
4565 shadowCam.left *= -scale * screenBbox.min.x;
4566 shadowCam.right *= scale * screenBbox.max.x;
4567 shadowCam.top *= scale * screenBbox.max.y;
4568 shadowCam.bottom *= -scale * screenBbox.min.y;
4569 shadowCam.updateProjectionMatrix();
4570 };
4571
4572 return AutoDirectionalLight;
4573}();
4574
4575/*
4576 * Copyright (c) 2020 NAVER Corp.
4577 * egjs projects are licensed under the MIT license
4578 */
4579/**
4580 * Helper class to easily add shadow plane under your 3D model
4581 * @category Environment
4582 * @example
4583 * import View3D, { ShadowPlane } from "@egjs/view3d";
4584 *
4585 * const view3d = new View3D("#view3d-canvas");
4586 * const shadowPlane = new ShadowPlane();
4587 * view3d.scene.addEnv(shadowPlane);
4588 */
4589
4590var ShadowPlane =
4591/*#__PURE__*/
4592function () {
4593 /**
4594 * Create new shadow plane
4595 * @param {object} options Options
4596 * @param {number} [options.size=10000] Size of the shadow plane
4597 * @param {number} [options.opacity=0.3] Opacity of the shadow
4598 */
4599 function ShadowPlane(_a) {
4600 var _b = _a === void 0 ? {} : _a,
4601 _c = _b.size,
4602 size = _c === void 0 ? 10000 : _c,
4603 _d = _b.opacity,
4604 opacity = _d === void 0 ? 0.3 : _d;
4605
4606 this.geometry = new PlaneGeometry(size, size, 100, 100);
4607 this.material = new ShadowMaterial({
4608 opacity: opacity
4609 });
4610 this.mesh = new Mesh(this.geometry, this.material);
4611 var mesh = this.mesh;
4612 mesh.rotateX(-Math.PI / 2);
4613 mesh.receiveShadow = true;
4614 }
4615
4616 var __proto = ShadowPlane.prototype;
4617 Object.defineProperty(__proto, "objects", {
4618 get: function () {
4619 return [this.mesh];
4620 },
4621 enumerable: false,
4622 configurable: true
4623 });
4624 Object.defineProperty(__proto, "opacity", {
4625 /**
4626 * Shadow opacity, value can be between 0(invisible) and 1(solid)
4627 * @type number
4628 */
4629 get: function () {
4630 return this.material.opacity;
4631 },
4632 set: function (val) {
4633 this.material.opacity = val;
4634 },
4635 enumerable: false,
4636 configurable: true
4637 });
4638 /**
4639 * Fit shadow plane's size & position to given model
4640 * @param model Model to fit
4641 */
4642
4643 __proto.fit = function (model, _a) {
4644 var _b = _a === void 0 ? {} : _a,
4645 floorPosition = _b.floorPosition,
4646 _c = _b.floorRotation,
4647 floorRotation = _c === void 0 ? new Quaternion(0, 0, 0, 1) : _c;
4648
4649 var modelPosition = model.scene.position;
4650 var localYAxis = new Vector3(0, 1, 0).applyQuaternion(floorRotation); // Apply position
4651
4652 if (floorPosition) {
4653 // Apply a tiny offset to prevent z-fighting with original model
4654 this.mesh.position.copy(floorPosition.clone().add(localYAxis.clone().multiplyScalar(0.001)));
4655 } else {
4656 var modelBbox = model.bbox;
4657 var modelBboxYOffset = modelBbox.getCenter(new Vector3()).y - modelBbox.min.y;
4658 var modelFloor = new Vector3().addVectors(modelPosition, // Apply a tiny offset to prevent z-fighting with original model
4659 localYAxis.multiplyScalar(-modelBboxYOffset + 0.0001));
4660 this.mesh.position.copy(modelFloor);
4661 } // Apply rotation
4662
4663
4664 var rotX90 = new Quaternion().setFromEuler(new Euler(-Math.PI / 2, 0, 0));
4665 var shadowRotation = new Quaternion().multiplyQuaternions(floorRotation, rotX90);
4666 this.mesh.quaternion.copy(shadowRotation);
4667 this.mesh.updateMatrix();
4668 };
4669
4670 return ShadowPlane;
4671}();
4672
4673/*
4674 * Copyright (c) 2020 NAVER Corp.
4675 * egjs projects are licensed under the MIT license
4676 */
4677/**
4678 * GLTFLoader
4679 * @category Loaders
4680 */
4681
4682var GLTFLoader =
4683/*#__PURE__*/
4684function () {
4685 /**
4686 * Create a new instance of GLTFLoader
4687 */
4688 function GLTFLoader() {
4689 this._loader = new GLTFLoader$1();
4690 this._dracoLoader = new DRACOLoader();
4691 var loader = this._loader;
4692 loader.setCrossOrigin("anonymous");
4693 var dracoLoader = this._dracoLoader;
4694 dracoLoader.setDecoderPath(DRACO_DECODER_URL);
4695 loader.setDRACOLoader(dracoLoader);
4696 }
4697
4698 var __proto = GLTFLoader.prototype;
4699 Object.defineProperty(__proto, "loader", {
4700 get: function () {
4701 return this._loader;
4702 },
4703 enumerable: false,
4704 configurable: true
4705 });
4706 Object.defineProperty(__proto, "dracoLoader", {
4707 get: function () {
4708 return this._dracoLoader;
4709 },
4710 enumerable: false,
4711 configurable: true
4712 });
4713 /**
4714 * Load new GLTF model from the given url
4715 * @param url URL to fetch glTF/glb file
4716 * @param options Options for a loaded model
4717 * @returns Promise that resolves {@link Model}
4718 */
4719
4720 __proto.load = function (url, options) {
4721 var _this = this;
4722
4723 if (options === void 0) {
4724 options = {};
4725 }
4726
4727 var loader = this._loader;
4728 loader.manager = new LoadingManager();
4729 return new Promise(function (resolve, reject) {
4730 loader.load(url, function (gltf) {
4731 var model = _this._parseToModel(gltf, options);
4732
4733 resolve(model);
4734 }, undefined, function (err) {
4735 reject(err);
4736 });
4737 });
4738 };
4739 /**
4740 * Load preset generated from View3D editor.
4741 * @param viewer Instance of the {@link View3D}.
4742 * @param url Preset url
4743 * @param {object} options Options
4744 * @param {string} [options.path] Base path for additional files.
4745 * @param {function} [options.onLoad] Callback which called after each model LOD is loaded.
4746 * @returns {Model} Model instance with highest LOD
4747 */
4748
4749
4750 __proto.loadPreset = function (viewer, url, options) {
4751 var _this = this;
4752
4753 if (options === void 0) {
4754 options = {};
4755 }
4756
4757 var loader = this._loader;
4758 var fileLoader = new FileLoader();
4759 return fileLoader.loadAsync(url).then(function (jsonRaw) {
4760 return new Promise(function (resolve, reject) {
4761 var json = JSON.parse(jsonRaw);
4762 var baseURL = LoaderUtils.extractUrlBase(url); // Reset
4763
4764 viewer.scene.reset();
4765 viewer.camera.reset();
4766 viewer.animator.reset();
4767 var modelOptions = json.model;
4768 var cameraOptions = json.camera;
4769 var environmentOptions = json.env;
4770 viewer.camera.setDefaultPose({
4771 yaw: cameraOptions.yaw,
4772 pitch: cameraOptions.pitch
4773 });
4774 viewer.camera.minDistance = cameraOptions.distanceRange[0];
4775 viewer.camera.maxDistance = cameraOptions.distanceRange[1];
4776
4777 if (environmentOptions.background) {
4778 viewer.scene.setBackground(new Color(environmentOptions.background));
4779 }
4780
4781 var shadowPlane = new ShadowPlane();
4782 shadowPlane.opacity = environmentOptions.shadow.opacity;
4783 viewer.scene.addEnv(shadowPlane);
4784 var ambientOptions = environmentOptions.ambient;
4785 var ambient = new AmbientLight(new Color(ambientOptions.color), ambientOptions.intensity);
4786 viewer.scene.addEnv(ambient);
4787 var lightOptions = [environmentOptions.light1, environmentOptions.light2, environmentOptions.light3];
4788 lightOptions.forEach(function (lightOption) {
4789 var lightDirection = new Vector3(lightOption.x, lightOption.y, lightOption.z).negate();
4790 var directional = new AutoDirectionalLight(new Color(lightOption.color), lightOption.intensity, {
4791 direction: lightDirection
4792 });
4793 directional.light.castShadow = lightOption.castShadow;
4794 directional.light.updateMatrixWorld();
4795 viewer.scene.addEnv(directional);
4796 });
4797 var isFirstLoad = true;
4798 var loadFlags = json.LOD.map(function () {
4799 return false;
4800 });
4801 json.LOD.forEach(function (fileName, lodIndex) {
4802 var glbURL = _this._resolveURL("" + baseURL + fileName, options.path || "");
4803
4804 loader.load(glbURL, function (gltf) {
4805 loadFlags[lodIndex] = true;
4806 var higherLODLoaded = loadFlags.slice(lodIndex + 1).some(function (loaded) {
4807 return loaded;
4808 });
4809 if (higherLODLoaded) return;
4810
4811 var model = _this._parseToModel(gltf);
4812
4813 viewer.display(model, {
4814 size: modelOptions.size,
4815 resetView: isFirstLoad
4816 });
4817 isFirstLoad = false;
4818 model.castShadow = modelOptions.castShadow;
4819 model.receiveShadow = modelOptions.receiveShadow;
4820
4821 if (options.onLoad) {
4822 options.onLoad(model, lodIndex);
4823 }
4824
4825 if (lodIndex === json.LOD.length - 1) {
4826 resolve(model);
4827 }
4828 }, undefined, function (err) {
4829 reject(err);
4830 });
4831 });
4832 });
4833 });
4834 };
4835 /**
4836 * Load new GLTF model from the given files
4837 * @param files Files that has glTF/glb and all its associated resources like textures and .bin data files
4838 * @param options Options for a loaded model
4839 * @returns Promise that resolves {@link Model}
4840 */
4841
4842
4843 __proto.loadFromFiles = function (files, options) {
4844 var _this = this;
4845
4846 if (options === void 0) {
4847 options = {};
4848 }
4849
4850 var objectURLs = [];
4851
4852 var revokeURLs = function () {
4853 objectURLs.forEach(function (url) {
4854 URL.revokeObjectURL(url);
4855 });
4856 };
4857
4858 return new Promise(function (resolve, reject) {
4859 if (files.length <= 0) {
4860 reject(new Error("No files found"));
4861 return;
4862 }
4863
4864 var gltfFile = files.find(function (file) {
4865 return /\.(gltf|glb)$/i.test(file.name);
4866 });
4867
4868 if (!gltfFile) {
4869 reject(new Error("No glTF file found"));
4870 return;
4871 }
4872
4873 var filesMap = new Map();
4874 files.forEach(function (file) {
4875 filesMap.set(file.name, file);
4876 });
4877 var gltfURL = URL.createObjectURL(gltfFile);
4878 objectURLs.push(gltfURL);
4879 var manager = new LoadingManager();
4880 manager.setURLModifier(function (fileURL) {
4881 var fileNameResult = /[^\/|\\]+$/.exec(fileURL);
4882 var fileName = fileNameResult && fileNameResult[0] || "";
4883
4884 if (filesMap.has(fileName)) {
4885 var blob = filesMap.get(fileName);
4886 var blobURL = URL.createObjectURL(blob);
4887 objectURLs.push(blobURL);
4888 return blobURL;
4889 }
4890
4891 return fileURL;
4892 });
4893 var loader = _this._loader;
4894 loader.manager = manager;
4895 loader.load(gltfURL, function (gltf) {
4896 var model = _this._parseToModel(gltf, options);
4897
4898 resolve(model);
4899 revokeURLs();
4900 }, undefined, function (err) {
4901 reject(err);
4902 revokeURLs();
4903 });
4904 });
4905 };
4906 /**
4907 * Parse from array buffer
4908 * @param data glTF asset to parse, as an ArrayBuffer or JSON string.
4909 * @param path The base path from which to find subsequent glTF resources such as textures and .bin data files.
4910 * @param options Options for a loaded model
4911 * @returns Promise that resolves {@link Model}
4912 */
4913
4914
4915 __proto.parse = function (data, path, options) {
4916 var _this = this;
4917
4918 if (options === void 0) {
4919 options = {};
4920 }
4921
4922 var loader = this._loader;
4923 loader.manager = new LoadingManager();
4924 return new Promise(function (resolve, reject) {
4925 loader.parse(data, path, function (gltf) {
4926 var model = _this._parseToModel(gltf, options);
4927
4928 resolve(model);
4929 }, function (err) {
4930 reject(err);
4931 });
4932 });
4933 };
4934
4935 __proto._parseToModel = function (gltf, _a) {
4936 var _b = (_a === void 0 ? {} : _a).fixSkinnedBbox,
4937 fixSkinnedBbox = _b === void 0 ? false : _b;
4938 var model = new Model({
4939 scenes: gltf.scenes,
4940 animations: gltf.animations,
4941 fixSkinnedBbox: fixSkinnedBbox
4942 });
4943 model.meshes.forEach(function (mesh) {
4944 var materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
4945 materials.forEach(function (mat) {
4946 if (mat.map) {
4947 mat.map.encoding = sRGBEncoding;
4948 }
4949 });
4950 });
4951 return model;
4952 }; // Grabbed from three.js/GLTFLoader
4953 // Original code: https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/GLTFLoader.js#L1221
4954 // License: MIT
4955
4956
4957 __proto._resolveURL = function (url, path) {
4958 // Invalid URL
4959 if (typeof url !== "string" || url === "") return ""; // Host Relative URL
4960
4961 if (/^https?:\/\//i.test(path) && /^\//.test(url)) {
4962 path = path.replace(/(^https?:\/\/[^\/]+).*/i, "$1");
4963 } // Absolute URL http://,https://,//
4964
4965
4966 if (/^(https?:)?\/\//i.test(url)) return url; // Data URI
4967
4968 if (/^data:.*,.*$/i.test(url)) return url; // Blob URL
4969
4970 if (/^blob:.*$/i.test(url)) return url; // Relative URL
4971
4972 return path + url;
4973 };
4974
4975 return GLTFLoader;
4976}();
4977
4978/*
4979 * Copyright (c) 2020 NAVER Corp.
4980 * egjs projects are licensed under the MIT license
4981 */
4982/**
4983 * Texture loader
4984 * @category Loaders
4985 */
4986
4987var TextureLoader =
4988/*#__PURE__*/
4989function () {
4990 /**
4991 * Create new TextureLoader instance
4992 * @param renderer {@link Renderer} instance of View3D
4993 */
4994 function TextureLoader(renderer) {
4995 this._renderer = renderer;
4996 }
4997 /**
4998 * Create new {@link https://threejs.org/docs/index.html#api/en/textures/Texture Texture} with given url
4999 * Texture's {@link https://threejs.org/docs/index.html#api/en/textures/Texture.flipY flipY} property is `true` by Three.js's policy, so be careful when using it as a map texture.
5000 * @param url url to fetch image
5001 */
5002
5003
5004 var __proto = TextureLoader.prototype;
5005
5006 __proto.load = function (url) {
5007 return new Promise(function (resolve, reject) {
5008 var loader = new TextureLoader$1();
5009 loader.load(url, resolve, undefined, reject);
5010 });
5011 };
5012 /**
5013 * Create new {@link https://threejs.org/docs/#api/en/renderers/WebGLCubeRenderTarget WebGLCubeRenderTarget} with given equirectangular image url
5014 * Be sure that equirectangular image has height of power of 2, as it will be resized if it isn't
5015 * @param url url to fetch equirectangular image
5016 * @returns WebGLCubeRenderTarget created
5017 */
5018
5019
5020 __proto.loadEquirectagularTexture = function (url) {
5021 var _this = this;
5022
5023 return new Promise(function (resolve, reject) {
5024 var loader = new TextureLoader$1();
5025 loader.load(url, function (skyboxTexture) {
5026 resolve(_this._equirectToCubemap(skyboxTexture));
5027 }, undefined, reject);
5028 });
5029 };
5030 /**
5031 * Create new {@link https://threejs.org/docs/#api/en/textures/CubeTexture CubeTexture} with given cubemap image urls
5032 * Image order should be: px, nx, py, ny, pz, nz
5033 * @param urls cubemap image urls
5034 * @returns CubeTexture created
5035 */
5036
5037
5038 __proto.loadCubeTexture = function (urls) {
5039 return new Promise(function (resolve, reject) {
5040 var loader = new CubeTextureLoader();
5041 loader.load(urls, resolve, undefined, reject);
5042 });
5043 };
5044 /**
5045 * Create new texture with given HDR(RGBE) image url
5046 * @param url image url
5047 * @param isEquirectangular Whether to read this image as a equirectangular texture
5048 */
5049
5050
5051 __proto.loadHDRTexture = function (url, isEquirectangular) {
5052 var _this = this;
5053
5054 if (isEquirectangular === void 0) {
5055 isEquirectangular = true;
5056 }
5057
5058 return new Promise(function (resolve, reject) {
5059 var loader = new RGBELoader();
5060 loader.load(url, function (texture) {
5061 if (isEquirectangular) {
5062 resolve(_this._equirectToCubemap(texture));
5063 } else {
5064 resolve(texture);
5065 }
5066 }, undefined, reject);
5067 });
5068 };
5069
5070 __proto._equirectToCubemap = function (texture) {
5071 return new WebGLCubeRenderTarget(texture.image.height).fromEquirectangularTexture(this._renderer.threeRenderer, texture);
5072 };
5073
5074 return TextureLoader;
5075}();
5076
5077/*
5078 * Copyright (c) 2020 NAVER Corp.
5079 * egjs projects are licensed under the MIT license
5080 */
5081var QUICKLOOK_SUPPORTED = function () {
5082 var anchorEl = document.createElement("a");
5083 return anchorEl.relList && anchorEl.relList.supports && anchorEl.relList.supports("ar");
5084}();
5085var WEBXR_SUPPORTED = navigator.xr && navigator.xr.isSessionSupported;
5086var HIT_TEST_SUPPORTED = window.XRSession && window.XRSession.prototype.requestHitTestSource;
5087var DOM_OVERLAY_SUPPORTED = window.XRDOMOverlayState != null;
5088var SESSION = {
5089 AR: "immersive-ar",
5090 VR: "immersive-ar"
5091};
5092var REFERENCE_SPACE = {
5093 LOCAL: "local",
5094 LOCAL_FLOOR: "local-floor",
5095 VIEWER: "viewer"
5096};
5097var EVENTS$1 = {
5098 SELECT_START: "selectstart",
5099 SELECT: "select",
5100 SELECT_END: "selectend"
5101};
5102var INPUT_PROFILE = {
5103 TOUCH: "generic-touchscreen"
5104};
5105var FEATURES = {
5106 HIT_TEST: {
5107 requiredFeatures: ["hit-test"]
5108 },
5109 DOM_OVERLAY: function (root) {
5110 return {
5111 optionalFeatures: ["dom-overlay"],
5112 domOverlay: {
5113 root: root
5114 }
5115 };
5116 }
5117}; // For type definition
5118
5119var EMPTY_FEATURES = {};
5120var SCENE_VIEWER = {
5121 INTENT_AR_CORE: function (params, fallback) {
5122 return "intent://arvr.google.com/scene-viewer/1.1?" + params + "#Intent;scheme=https;package=com.google.ar.core;action=android.intent.action.VIEW;" + (fallback ? "S.browser_fallback_url=" + fallback + ";" : "") + "end;";
5123 },
5124 INTENT_SEARCHBOX: function (params, fallback) {
5125 return "intent://arvr.google.com/scene-viewer/1.1?" + params + "#Intent;scheme=https;package=com.google.android.googlequicksearchbox;action=android.intent.action.VIEW;" + (fallback ? "S.browser_fallback_url=" + fallback + ";" : "") + "end;";
5126 },
5127 FALLBACK_DEFAULT: function (params) {
5128 return "https://arvr.google.com/scene-viewer?" + params;
5129 }
5130};
5131
5132/*
5133 * Copyright (c) 2020 NAVER Corp.
5134 * egjs projects are licensed under the MIT license
5135 */
5136/**
5137 * Manager for WebXR dom-overlay feature
5138 * @category XR
5139 */
5140
5141var DOMOverlay =
5142/*#__PURE__*/
5143function () {
5144 /**
5145 * Create new DOMOverlay instance
5146 * @param {object} [options] Options
5147 * @param {HTMLElement} [options.root] Overlay root element
5148 * @param {HTMLElement | null} [options.loadingEl] Model loading indicator element which will be invisible after placing model on the floor.
5149 */
5150 function DOMOverlay(options) {
5151 this._root = options.root;
5152 this._loadingEl = options.loadingEl;
5153 }
5154
5155 var __proto = DOMOverlay.prototype;
5156 Object.defineProperty(__proto, "root", {
5157 /**
5158 * Overlay root element
5159 */
5160 get: function () {
5161 return this._root;
5162 },
5163 enumerable: false,
5164 configurable: true
5165 });
5166 Object.defineProperty(__proto, "loadingElement", {
5167 /**
5168 * Loading indicator element, if there's any
5169 */
5170 get: function () {
5171 return this._loadingEl;
5172 },
5173 enumerable: false,
5174 configurable: true
5175 });
5176 Object.defineProperty(__proto, "features", {
5177 /**
5178 * {@link https://developer.mozilla.org/en-US/docs/Web/API/XRSessionInit XRSessionInit} object for dom-overlay feature
5179 */
5180 get: function () {
5181 return FEATURES.DOM_OVERLAY(this._root);
5182 },
5183 enumerable: false,
5184 configurable: true
5185 });
5186 /**
5187 * Return whether dom-overlay feature is available
5188 */
5189
5190 __proto.isAvailable = function () {
5191 return DOM_OVERLAY_SUPPORTED;
5192 };
5193 /**
5194 * Show loading indicator, if there's any
5195 */
5196
5197
5198 __proto.showLoading = function () {
5199 if (!this._loadingEl) return;
5200 this._loadingEl.style.visibility = "visible";
5201 };
5202 /**
5203 * Hide loading indicator, if there's any
5204 */
5205
5206
5207 __proto.hideLoading = function () {
5208 if (!this._loadingEl) return;
5209 this._loadingEl.style.visibility = "hidden";
5210 };
5211
5212 return DOMOverlay;
5213}();
5214
5215/*
5216 * Copyright (c) 2020 NAVER Corp.
5217 * egjs projects are licensed under the MIT license
5218 */
5219/**
5220 * WebXR based abstract AR session class
5221 * @category XR
5222 * @fires WebARSession#start
5223 * @fires WebARSession#end
5224 * @fires WebARSession#canPlace
5225 * @fires WebARSession#modelPlaced
5226 */
5227
5228var WebARSession =
5229/*#__PURE__*/
5230function (_super) {
5231 __extends(WebARSession, _super);
5232 /**
5233 * Emitted when session is started.
5234 * @event start
5235 * @category XR
5236 * @memberof WebARSession
5237 * @type void
5238 */
5239
5240 /**
5241 * Emitted when session is ended.
5242 * @event end
5243 * @category XR
5244 * @memberof WebARSession
5245 * @type void
5246 */
5247
5248 /**
5249 * Emitted when model can be placed on the space.
5250 * @event canPlace
5251 * @category XR
5252 * @memberof WebARSession
5253 * @type void
5254 */
5255
5256 /**
5257 * Emitted when model is placed.
5258 * @event modelPlaced
5259 * @category XR
5260 * @memberof WebARSession
5261 * @type void
5262 */
5263
5264 /**
5265 * Create new instance of WebARSession
5266 * @param {object} [options={}] Options
5267 * @param {object} [options.features={}] You can set additional features(see {@link https://developer.mozilla.org/en-US/docs/Web/API/XRSessionInit XRSessionInit}) with this option.
5268 * @param {number} [options.maxModelSize=Infinity] If model's size is too big to show on AR, you can restrict it's size with this option. Model with size bigger than this value will clamped to this value.
5269 * @param {HTMLElement|string|null} [options.overlayRoot=null] If this value is set, dom-overlay feature will be automatically added for this session. And this value will be used as dom-overlay's root element. You can set either HTMLElement or query selector for that element.
5270 * @param {HTMLElement|string|null} [options.loadingEl=null] This will be used for loading indicator element, which will automatically invisible after placing 3D model by setting `visibility: hidden`. This element must be placed under `overlayRoot`. You can set either HTMLElement or query selector for that element.
5271 * @param {boolean} [options.forceOverlay=false] Whether to apply `dom-overlay` feature as required. If set to false, `dom-overlay` will be optional feature.
5272 */
5273
5274
5275 function WebARSession(_a) {
5276 var _b = _a === void 0 ? {} : _a,
5277 _c = _b.features,
5278 userFeatures = _c === void 0 ? EMPTY_FEATURES : _c,
5279 // https://developer.mozilla.org/en-US/docs/Web/API/XRSessionInit
5280 _d = _b.maxModelSize,
5281 // https://developer.mozilla.org/en-US/docs/Web/API/XRSessionInit
5282 maxModelSize = _d === void 0 ? Infinity : _d,
5283 _e = _b.overlayRoot,
5284 overlayRoot = _e === void 0 ? NULL_ELEMENT : _e,
5285 _f = _b.loadingEl,
5286 loadingEl = _f === void 0 ? NULL_ELEMENT : _f,
5287 _g = _b.forceOverlay,
5288 forceOverlay = _g === void 0 ? false : _g;
5289
5290 var _this = _super.call(this) || this;
5291 /**
5292 * Whether it's webxr-based session or not
5293 * @type true
5294 */
5295
5296
5297 _this.isWebXRSession = true;
5298 _this._session = null;
5299 _this._domOverlay = null;
5300 var overlayEl = getElement(overlayRoot);
5301 var features = [];
5302
5303 if (overlayEl) {
5304 _this._domOverlay = new DOMOverlay({
5305 root: overlayEl,
5306 loadingEl: getElement(loadingEl, overlayEl)
5307 });
5308 features.push(_this._domOverlay.features);
5309 }
5310
5311 _this._features = merge.apply(void 0, __spread([{}], features, [userFeatures]));
5312 _this._maxModelSize = maxModelSize;
5313 _this._forceOverlay = forceOverlay;
5314 return _this;
5315 }
5316
5317 var __proto = WebARSession.prototype;
5318 Object.defineProperty(__proto, "session", {
5319 /**
5320 * {@link https://developer.mozilla.org/en-US/docs/Web/API/XRSession XRSession} of this session
5321 * This value is only available after calling enter
5322 */
5323 get: function () {
5324 return this._session;
5325 },
5326 enumerable: false,
5327 configurable: true
5328 });
5329 Object.defineProperty(__proto, "features", {
5330 /**
5331 * {@link https://developer.mozilla.org/en-US/docs/Web/API/XRSessionInit XRSessionInit} object for this session.
5332 */
5333 get: function () {
5334 return this._features;
5335 },
5336 enumerable: false,
5337 configurable: true
5338 });
5339 /**
5340 * Return availability of this session
5341 * @returns {Promise} A Promise that resolves availability of this session(boolean).
5342 */
5343
5344 __proto.isAvailable = function () {
5345 var domOverlay = this._domOverlay;
5346 if (!WEBXR_SUPPORTED || !HIT_TEST_SUPPORTED) return Promise.resolve(false);
5347
5348 if (this._forceOverlay) {
5349 if (domOverlay && !domOverlay.isAvailable()) return Promise.resolve(false);
5350 }
5351
5352 return navigator.xr.isSessionSupported(SESSION.AR);
5353 };
5354 /**
5355 * Enter session
5356 * @param view3d Instance of the View3D
5357 * @returns {Promise}
5358 */
5359
5360
5361 __proto.enter = function (view3d) {
5362 var _this = this; // Model not loaded yet
5363
5364
5365 if (!view3d.model) return Promise.reject("3D Model is not loaded");
5366 var model = view3d.model;
5367 return navigator.xr.requestSession(SESSION.AR, this._features).then(function (session) {
5368 var renderer = view3d.renderer;
5369 var threeRenderer = renderer.threeRenderer;
5370 var xrContext = {
5371 view3d: view3d,
5372 model: model,
5373 session: session
5374 }; // Cache original values
5375
5376 var originalMatrix = model.scene.matrix.clone();
5377 var originalModelSize = model.size;
5378 var originalBackground = view3d.scene.root.background;
5379 var arModelSize = Math.min(model.originalSize, _this._maxModelSize);
5380 model.size = arModelSize;
5381 model.moveToOrigin();
5382 view3d.scene.setBackground(null); // Cache original model rotation
5383
5384 threeRenderer.xr.setReferenceSpaceType(REFERENCE_SPACE.LOCAL);
5385 threeRenderer.xr.setSession(session);
5386 threeRenderer.setPixelRatio(1);
5387
5388 _this.onStart(xrContext);
5389
5390 session.addEventListener("end", function () {
5391 _this.onEnd(xrContext); // Restore original values
5392
5393
5394 model.scene.matrix.copy(originalMatrix);
5395 model.scene.matrix.decompose(model.scene.position, model.scene.quaternion, model.scene.scale);
5396 model.size = originalModelSize;
5397 model.moveToOrigin();
5398 view3d.scene.update(model);
5399 view3d.scene.setBackground(originalBackground); // Restore renderer values
5400
5401 threeRenderer.xr.setSession(null);
5402 threeRenderer.setPixelRatio(window.devicePixelRatio); // Restore render loop
5403
5404 renderer.stopAnimationLoop();
5405 renderer.setAnimationLoop(view3d.renderLoop);
5406 }, {
5407 once: true
5408 }); // Set XR session render loop
5409
5410 renderer.stopAnimationLoop();
5411 renderer.setAnimationLoop(function (delta, frame) {
5412 var xrCam = threeRenderer.xr.getCamera(new PerspectiveCamera());
5413 var referenceSpace = threeRenderer.xr.getReferenceSpace();
5414 var glLayer = session.renderState.baseLayer;
5415 var size = {
5416 width: glLayer.framebufferWidth,
5417 height: glLayer.framebufferHeight
5418 };
5419
5420 var renderContext = __assign(__assign({}, xrContext), {
5421 delta: delta,
5422 frame: frame,
5423 referenceSpace: referenceSpace,
5424 xrCam: xrCam,
5425 size: size
5426 });
5427
5428 _this._beforeRender(renderContext);
5429
5430 view3d.renderLoop(delta);
5431 });
5432 });
5433 };
5434 /**
5435 * Exit this session
5436 * @param view3d Instance of the View3D
5437 */
5438
5439
5440 __proto.exit = function (view3d) {
5441 var session = view3d.renderer.threeRenderer.xr.getSession();
5442 session.end();
5443 };
5444
5445 __proto.onStart = function (ctx) {
5446 var _a;
5447
5448 this._session = ctx.session;
5449 (_a = this._domOverlay) === null || _a === void 0 ? void 0 : _a.showLoading();
5450 this.emit("start");
5451 };
5452
5453 __proto.onEnd = function (ctx) {
5454 var _a;
5455
5456 this._session = null;
5457 (_a = this._domOverlay) === null || _a === void 0 ? void 0 : _a.hideLoading();
5458 this.emit("end");
5459 };
5460
5461 return WebARSession;
5462}(EventEmitter);
5463
5464/*
5465 * Copyright (c) 2020 NAVER Corp.
5466 * egjs projects are licensed under the MIT license
5467 */
5468/**
5469 * Manager for WebXR hit-test feature
5470 * @category XR
5471 */
5472
5473var HitTest =
5474/*#__PURE__*/
5475function () {
5476 function HitTest() {
5477 this._source = null;
5478 }
5479
5480 var __proto = HitTest.prototype;
5481 Object.defineProperty(__proto, "ready", {
5482 /**
5483 * Return whether hit-test is ready
5484 */
5485 get: function () {
5486 return this._source != null;
5487 },
5488 enumerable: false,
5489 configurable: true
5490 });
5491 Object.defineProperty(__proto, "features", {
5492 /**
5493 * {@link https://developer.mozilla.org/en-US/docs/Web/API/XRSessionInit XRSessionInit} object for hit-test feature
5494 */
5495 get: function () {
5496 return FEATURES.HIT_TEST;
5497 },
5498 enumerable: false,
5499 configurable: true
5500 });
5501 /**
5502 * Destroy instance
5503 */
5504
5505 __proto.destroy = function () {
5506 if (this._source) {
5507 this._source.cancel();
5508
5509 this._source = null;
5510 }
5511 };
5512 /**
5513 * Initialize hit-test feature
5514 * @param {XRSession} session XRSession instance
5515 */
5516
5517
5518 __proto.init = function (session) {
5519 var _this = this;
5520
5521 session.requestReferenceSpace(REFERENCE_SPACE.VIEWER).then(function (referenceSpace) {
5522 session.requestHitTestSource({
5523 space: referenceSpace
5524 }).then(function (source) {
5525 _this._source = source;
5526 });
5527 });
5528 };
5529 /**
5530 * Return whether hit-test feature is available
5531 */
5532
5533
5534 __proto.isAvailable = function () {
5535 return HIT_TEST_SUPPORTED;
5536 };
5537 /**
5538 * Get hit-test results
5539 * @param {XRFrame} frame XRFrame instance
5540 */
5541
5542
5543 __proto.getResults = function (frame) {
5544 return frame.getHitTestResults(this._source);
5545 };
5546
5547 return HitTest;
5548}();
5549
5550/*
5551 * Copyright (c) 2020 NAVER Corp.
5552 * egjs projects are licensed under the MIT license
5553 */
5554/**
5555 * Fires for every animation frame when animation is active.
5556 * @type object
5557 * @property {object} event Event object.
5558 * @property {number} [event.progress] Current animation progress value.
5559 * Value is ranged from 0(start) to 1(end).
5560 * @property {number} [event.easedProgress] Eased progress value.
5561 * @event Animation#progress
5562 */
5563
5564/**
5565 * Fires for every animation loop except for the last loop
5566 * This will be triggered only when repeat > 0
5567 * @type object
5568 * @property {object} event Event object.
5569 * @property {number} [event.progress] Current animation progress value.
5570 * Value is ranged from 0(start) to 1(end).
5571 * @property {number} [event.easedProgress] Eased progress value.
5572 * @property {number} [event.loopIndex] Index of the current loop.
5573 * @event Animation#loop
5574 */
5575
5576/**
5577 * Fires when animation ends.
5578 * @type void
5579 * @event Animation#finish
5580 */
5581
5582/**
5583 * Self-running animation
5584 * @category Core
5585 */
5586
5587var Animation =
5588/*#__PURE__*/
5589function (_super) {
5590 __extends(Animation, _super);
5591 /**
5592 * Create new instance of the Animation
5593 * @param {object} [options={}] Options
5594 */
5595
5596
5597 function Animation(_a) {
5598 var _b = _a === void 0 ? {} : _a,
5599 _c = _b.context,
5600 context = _c === void 0 ? window : _c,
5601 _d = _b.repeat,
5602 repeat = _d === void 0 ? 0 : _d,
5603 _e = _b.duration,
5604 duration = _e === void 0 ? ANIMATION_DURATION : _e,
5605 _f = _b.easing,
5606 easing$1 = _f === void 0 ? EASE_OUT_CUBIC : _f;
5607
5608 var _this = _super.call(this) || this;
5609
5610 _this._loop = function () {
5611 var delta = _this._getDeltaTime();
5612
5613 var duration = _this._duration;
5614 _this._time += delta;
5615 var loopIncrease = Math.floor(_this._time / duration);
5616 _this._time = circulate(_this._time, 0, duration);
5617 var progress = _this._time / duration;
5618 var progressEvent = {
5619 progress: progress,
5620 easedProgress: _this._easing(progress)
5621 };
5622
5623 _this.emit("progress", progressEvent);
5624
5625 for (var loopIdx = 0; loopIdx < loopIncrease; loopIdx++) {
5626 _this._loopCount++;
5627
5628 if (_this._loopCount > _this._repeat) {
5629 _this.emit("finish");
5630
5631 _this.stop();
5632
5633 return;
5634 } else {
5635 _this.emit("loop", __assign(__assign({}, progressEvent), {
5636 loopIndex: _this._loopCount
5637 }));
5638 }
5639 }
5640
5641 _this._rafId = _this._ctx.requestAnimationFrame(_this._loop);
5642 }; // Options
5643
5644
5645 _this._repeat = repeat;
5646 _this._duration = duration;
5647 _this._easing = easing$1; // Internal States
5648
5649 _this._ctx = context;
5650 _this._rafId = -1;
5651 _this._time = 0;
5652 _this._clock = 0;
5653 _this._loopCount = 0;
5654 return _this;
5655 }
5656
5657 var __proto = Animation.prototype;
5658
5659 __proto.start = function () {
5660 if (this._rafId >= 0) return; // This guarantees "progress" event with progress = 0 on first start
5661
5662 this._updateClock();
5663
5664 this._loop();
5665 };
5666
5667 __proto.stop = function () {
5668 if (this._rafId < 0) return;
5669 this._time = 0;
5670 this._loopCount = 0;
5671
5672 this._stopLoop();
5673 };
5674
5675 __proto.pause = function () {
5676 if (this._rafId < 0) return;
5677
5678 this._stopLoop();
5679 };
5680
5681 __proto._stopLoop = function () {
5682 this._ctx.cancelAnimationFrame(this._rafId);
5683
5684 this._rafId = -1;
5685 };
5686
5687 __proto._getDeltaTime = function () {
5688 var lastTime = this._clock;
5689
5690 this._updateClock();
5691
5692 return this._clock - lastTime;
5693 };
5694
5695 __proto._updateClock = function () {
5696 this._clock = Date.now();
5697 };
5698
5699 return Animation;
5700}(EventEmitter);
5701
5702/*
5703 * Copyright (c) 2020 NAVER Corp.
5704 * egjs projects are licensed under the MIT license
5705 */
5706/**
5707 * Rotation indicator for ARHoverSession
5708 * @category Controls-AR
5709 */
5710
5711var RotationIndicator =
5712/*#__PURE__*/
5713function () {
5714 /**
5715 * Create new RotationIndicator
5716 * @param {RotationIndicatorOption} [options={}] Options
5717 */
5718 function RotationIndicator(_a) {
5719 var _b = _a === void 0 ? {} : _a,
5720 _c = _b.ringColor,
5721 ringColor = _c === void 0 ? 0xffffff : _c,
5722 _d = _b.axisColor,
5723 axisColor = _d === void 0 ? 0xffffff : _d;
5724
5725 var ringGeometry = new RingGeometry(0.99, 1, 150, 1, 0, Math.PI * 2);
5726 var ringMaterial = new MeshBasicMaterial({
5727 color: ringColor,
5728 side: DoubleSide
5729 });
5730 this._ring = new Mesh(ringGeometry, ringMaterial);
5731 var axisVertices = [new Vector3(0, 0, -1000), new Vector3(0, 0, +1000)];
5732 var axisGeometry = new BufferGeometry().setFromPoints(axisVertices);
5733 var axisMaterial = new LineBasicMaterial({
5734 color: axisColor
5735 });
5736 this._axis = new Line(axisGeometry, axisMaterial);
5737 this._obj = new Group();
5738
5739 this._obj.add(this._ring);
5740
5741 this._obj.add(this._axis);
5742
5743 this.hide();
5744 }
5745
5746 var __proto = RotationIndicator.prototype;
5747 Object.defineProperty(__proto, "object", {
5748 /**
5749 * {@link https://threejs.org/docs/index.html#api/en/objects/Group THREE.Group} object that contains ring & axis.
5750 */
5751 get: function () {
5752 return this._obj;
5753 },
5754 enumerable: false,
5755 configurable: true
5756 });
5757 /**
5758 * Show indicator
5759 */
5760
5761 __proto.show = function () {
5762 this._obj.visible = true;
5763 };
5764 /**
5765 * Hide indicator
5766 */
5767
5768
5769 __proto.hide = function () {
5770 this._obj.visible = false;
5771 };
5772 /**
5773 * Change the position of the indicator
5774 * @param position New position
5775 */
5776
5777
5778 __proto.updatePosition = function (position) {
5779 this._obj.position.copy(position);
5780 };
5781 /**
5782 * Update scale of the ring
5783 * @param scale New scale
5784 */
5785
5786
5787 __proto.updateScale = function (scale) {
5788 this._ring.scale.setScalar(scale);
5789 };
5790 /**
5791 * Update indicator's rotation
5792 * @param rotation Quaternion value set as new rotation.
5793 */
5794
5795
5796 __proto.updateRotation = function (rotation) {
5797 this._obj.quaternion.copy(rotation);
5798 };
5799
5800 return RotationIndicator;
5801}();
5802
5803/*
5804 * Copyright (c) 2020 NAVER Corp.
5805 * egjs projects are licensed under the MIT license
5806 */
5807/**
5808 * One finger swirl control on single axis
5809 * @category Controls-AR
5810 */
5811
5812var ARSwirlControl =
5813/*#__PURE__*/
5814function () {
5815 /**
5816 * Create new ARSwirlControl
5817 * @param {ARSwirlControlOption} [options={}] Options
5818 */
5819 function ARSwirlControl(_a) {
5820 var _b = _a === void 0 ? {} : _a,
5821 _c = _b.scale,
5822 scale = _c === void 0 ? 1 : _c,
5823 _d = _b.showIndicator,
5824 showIndicator = _d === void 0 ? true : _d;
5825 /**
5826 * Current rotation value
5827 */
5828
5829
5830 this.rotation = new Quaternion(); // Internal States
5831
5832 this._axis = new Vector3(0, 1, 0);
5833 this._enabled = true;
5834 this._active = false;
5835 this._prevPos = new Vector2();
5836 this._fromQuat = new Quaternion();
5837 this._toQuat = new Quaternion();
5838 this._motion = new Motion({
5839 range: INFINITE_RANGE
5840 });
5841 this._userScale = scale;
5842
5843 if (showIndicator) {
5844 this._rotationIndicator = new RotationIndicator();
5845 }
5846 }
5847
5848 var __proto = ARSwirlControl.prototype;
5849 Object.defineProperty(__proto, "enabled", {
5850 /**
5851 * Whether this control is enabled or not.
5852 * @readonly
5853 */
5854 get: function () {
5855 return this._enabled;
5856 },
5857 enumerable: false,
5858 configurable: true
5859 });
5860 Object.defineProperty(__proto, "scale", {
5861 /**
5862 * Scale(speed) factor of this control.
5863 */
5864 get: function () {
5865 return this._userScale;
5866 },
5867 set: function (val) {
5868 this._userScale = val;
5869 },
5870 enumerable: false,
5871 configurable: true
5872 });
5873
5874 __proto.init = function (_a) {
5875 var view3d = _a.view3d;
5876 var initialRotation = view3d.model.scene.quaternion;
5877 this.updateRotation(initialRotation);
5878
5879 if (this._rotationIndicator) {
5880 view3d.scene.add(this._rotationIndicator.object);
5881 }
5882 };
5883
5884 __proto.destroy = function (_a) {
5885 var view3d = _a.view3d;
5886
5887 if (this._rotationIndicator) {
5888 view3d.scene.remove(this._rotationIndicator.object);
5889 }
5890 };
5891
5892 __proto.updateRotation = function (rotation) {
5893 this.rotation.copy(rotation);
5894
5895 this._fromQuat.copy(rotation);
5896
5897 this._toQuat.copy(rotation);
5898 };
5899 /**
5900 * Enable this control
5901 */
5902
5903
5904 __proto.enable = function () {
5905 this._enabled = true;
5906 };
5907 /**
5908 * Disable this control
5909 */
5910
5911
5912 __proto.disable = function () {
5913 this._enabled = false;
5914 };
5915
5916 __proto.activate = function (_a, gesture) {
5917 var view3d = _a.view3d;
5918 if (!this._enabled) return;
5919 this._active = true;
5920 var model = view3d.model;
5921 var rotationIndicator = this._rotationIndicator;
5922
5923 if (rotationIndicator) {
5924 rotationIndicator.show();
5925 rotationIndicator.updatePosition(model.bbox.getCenter(new Vector3()));
5926 rotationIndicator.updateScale(model.size / 2);
5927 rotationIndicator.updateRotation(model.scene.quaternion);
5928 }
5929 };
5930
5931 __proto.deactivate = function () {
5932 this._active = false;
5933
5934 if (this._rotationIndicator) {
5935 this._rotationIndicator.hide();
5936 }
5937 };
5938
5939 __proto.updateAxis = function (axis) {
5940 this._axis.copy(axis);
5941 };
5942
5943 __proto.setInitialPos = function (coords) {
5944 this._prevPos.copy(coords[0]);
5945 };
5946
5947 __proto.process = function (_a, _b) {
5948 var view3d = _a.view3d,
5949 xrCam = _a.xrCam;
5950 var coords = _b.coords;
5951 if (!this._active || coords.length !== 1) return;
5952 var prevPos = this._prevPos;
5953 var motion = this._motion;
5954 var model = view3d.model;
5955 var coord = coords[0];
5956 var modelPos = model.scene.position.clone();
5957 var ndcModelPos = new Vector2().fromArray(modelPos.project(xrCam).toArray()); // Get the rotation angle with the model's NDC coordinates as the center.
5958
5959 var rotationAngle = getRotationAngle(ndcModelPos, prevPos, coord) * this._userScale;
5960
5961 var rotation = new Quaternion().setFromAxisAngle(this._axis, rotationAngle);
5962
5963 var interpolated = this._getInterpolatedQuaternion();
5964
5965 this._fromQuat.copy(interpolated);
5966
5967 this._toQuat.premultiply(rotation);
5968
5969 motion.reset(0);
5970 motion.setEndDelta(1);
5971 prevPos.copy(coord);
5972 };
5973
5974 __proto.update = function (_a, deltaTime) {
5975 var model = _a.model;
5976 if (!this._active) return;
5977 var motion = this._motion;
5978 motion.update(deltaTime);
5979
5980 var interpolated = this._getInterpolatedQuaternion();
5981
5982 this.rotation.copy(interpolated);
5983 model.scene.quaternion.copy(interpolated);
5984 };
5985
5986 __proto._getInterpolatedQuaternion = function () {
5987 var motion = this._motion;
5988 var toEuler = this._toQuat;
5989 var fromEuler = this._fromQuat;
5990 var progress = motion.val;
5991 return new Quaternion().copy(fromEuler).slerp(toEuler, progress);
5992 };
5993
5994 return ARSwirlControl;
5995}();
5996
5997/*
5998 * Copyright (c) 2020 NAVER Corp.
5999 * egjs projects are licensed under the MIT license
6000 */
6001var STATE;
6002
6003(function (STATE) {
6004 STATE[STATE["WAITING"] = 0] = "WAITING";
6005 STATE[STATE["TRANSLATING"] = 1] = "TRANSLATING";
6006 STATE[STATE["BOUNCING"] = 2] = "BOUNCING";
6007})(STATE || (STATE = {}));
6008/**
6009 * Model's translation(position) control for {@link ARFloorControl}
6010 * @category Controls-AR
6011 */
6012
6013
6014var ARFloorTranslateControl =
6015/*#__PURE__*/
6016function () {
6017 /**
6018 * Create new instance of ARTranslateControl
6019 * @param {ARFloorTranslateControlOption} [options={}] Options
6020 */
6021 function ARFloorTranslateControl(_a) {
6022 var _b = _a === void 0 ? {} : _a,
6023 _c = _b.hoverAmplitude,
6024 hoverAmplitude = _c === void 0 ? 0.01 : _c,
6025 _d = _b.hoverHeight,
6026 hoverHeight = _d === void 0 ? 0.1 : _d,
6027 _e = _b.hoverPeriod,
6028 hoverPeriod = _e === void 0 ? 1000 : _e,
6029 _f = _b.hoverEasing,
6030 hoverEasing = _f === void 0 ? SINE_WAVE : _f,
6031 _g = _b.bounceDuration,
6032 bounceDuration = _g === void 0 ? 1000 : _g,
6033 _h = _b.bounceEasing,
6034 bounceEasing = _h === void 0 ? EASE_OUT_BOUNCE : _h; // Internal states
6035
6036
6037 this._modelPosition = new Vector3();
6038 this._hoverPosition = new Vector3();
6039 this._floorPosition = new Vector3();
6040 this._dragPlane = new Plane();
6041 this._enabled = true;
6042 this._state = STATE.WAITING;
6043 this._initialPos = new Vector2();
6044 this._hoverAmplitude = hoverAmplitude;
6045 this._hoverHeight = hoverHeight;
6046 this._hoverMotion = new Motion({
6047 loop: true,
6048 duration: hoverPeriod,
6049 easing: hoverEasing
6050 });
6051 this._bounceMotion = new Motion({
6052 duration: bounceDuration,
6053 easing: bounceEasing,
6054 range: INFINITE_RANGE
6055 });
6056 }
6057
6058 var __proto = ARFloorTranslateControl.prototype;
6059 Object.defineProperty(__proto, "enabled", {
6060 /**
6061 * Whether this control is enabled or not
6062 * @readonly
6063 */
6064 get: function () {
6065 return this._enabled;
6066 },
6067 enumerable: false,
6068 configurable: true
6069 });
6070 Object.defineProperty(__proto, "modelPosition", {
6071 /**
6072 * Position including hover/bounce animation offset from the floor.
6073 * @readonly
6074 */
6075 get: function () {
6076 return this._modelPosition.clone();
6077 },
6078 enumerable: false,
6079 configurable: true
6080 });
6081 Object.defineProperty(__proto, "floorPosition", {
6082 /**
6083 * Last detected floor position
6084 * @readonly
6085 */
6086 get: function () {
6087 return this._floorPosition.clone();
6088 },
6089 enumerable: false,
6090 configurable: true
6091 });
6092 Object.defineProperty(__proto, "hoverAmplitude", {
6093 /**
6094 * How much model will hover up and down, in meter.
6095 */
6096 get: function () {
6097 return this._hoverAmplitude;
6098 },
6099 set: function (val) {
6100 this._hoverAmplitude = val;
6101 },
6102 enumerable: false,
6103 configurable: true
6104 });
6105 Object.defineProperty(__proto, "hoverHeight", {
6106 /**
6107 * How much model will float from the floor, in meter.
6108 */
6109 get: function () {
6110 return this._hoverHeight;
6111 },
6112 set: function (val) {
6113 this._hoverHeight = val;
6114 },
6115 enumerable: false,
6116 configurable: true
6117 });
6118
6119 __proto.initFloorPosition = function (position) {
6120 this._modelPosition.copy(position);
6121
6122 this._floorPosition.copy(position);
6123
6124 this._hoverPosition.copy(position);
6125
6126 this._hoverPosition.setY(position.y + this._hoverHeight);
6127 }; // tslint:disable-next-line no-empty
6128
6129
6130 __proto.init = function (ctx) {}; // tslint:disable-next-line no-empty
6131
6132
6133 __proto.destroy = function (ctx) {};
6134 /**
6135 * Enable this control
6136 */
6137
6138
6139 __proto.enable = function () {
6140 this._enabled = true;
6141 };
6142 /**
6143 * Disable this control
6144 */
6145
6146
6147 __proto.disable = function () {
6148 this._enabled = false;
6149 this.deactivate();
6150 };
6151
6152 __proto.activate = function (_a, gesture) {
6153 var model = _a.model;
6154 if (!this._enabled) return;
6155 var modelBbox = model.bbox;
6156 var modelBboxYOffset = modelBbox.getCenter(new Vector3()).y - modelBbox.min.y;
6157
6158 this._dragPlane.set(new Vector3(0, 1, 0), -(this._floorPosition.y + this._hoverHeight + modelBboxYOffset));
6159
6160 this._hoverMotion.reset(0);
6161
6162 this._hoverMotion.setEndDelta(1);
6163
6164 this._state = STATE.TRANSLATING;
6165 };
6166
6167 __proto.deactivate = function () {
6168 if (!this._enabled || this._state === STATE.WAITING) {
6169 this._state = STATE.WAITING;
6170 return;
6171 }
6172
6173 this._state = STATE.BOUNCING;
6174 var floorPosition = this._floorPosition;
6175 var modelPosition = this._modelPosition;
6176 var hoverPosition = this._hoverPosition;
6177 var bounceMotion = this._bounceMotion;
6178 var hoveringAmount = modelPosition.y - floorPosition.y;
6179 bounceMotion.reset(modelPosition.y);
6180 bounceMotion.setEndDelta(-hoveringAmount); // Restore hover pos
6181
6182 hoverPosition.copy(floorPosition);
6183 hoverPosition.setY(floorPosition.y + this._hoverHeight);
6184 };
6185
6186 __proto.setInitialPos = function (coords) {
6187 this._initialPos.copy(coords[0]);
6188 };
6189
6190 __proto.process = function (_a, _b) {
6191 var view3d = _a.view3d,
6192 model = _a.model,
6193 frame = _a.frame,
6194 referenceSpace = _a.referenceSpace,
6195 xrCam = _a.xrCam;
6196 var hitResults = _b.hitResults;
6197 var state = this._state;
6198 var notActive = state === STATE.WAITING || state === STATE.BOUNCING;
6199 if (!hitResults || hitResults.length !== 1 || notActive) return;
6200 var hitResult = hitResults[0];
6201
6202 var prevFloorPosition = this._floorPosition.clone();
6203
6204 var floorPosition = this._floorPosition;
6205 var hoverPosition = this._hoverPosition;
6206 var hoverHeight = this._hoverHeight;
6207 var dragPlane = this._dragPlane;
6208 var modelBbox = model.bbox;
6209 var modelBboxYOffset = modelBbox.getCenter(new Vector3()).y - modelBbox.min.y;
6210 var hitPose = hitResult.results[0] && hitResult.results[0].getPose(referenceSpace);
6211 var isFloorHit = hitPose && hitPose.transform.matrix[5] >= 0.75;
6212 var camPos = new Vector3().setFromMatrixPosition(xrCam.matrixWorld);
6213
6214 if (!hitPose || !isFloorHit) {
6215 // Use previous drag plane if no hit plane is found
6216 var targetRayPose = frame.getPose(hitResult.inputSource.targetRaySpace, view3d.renderer.threeRenderer.xr.getReferenceSpace());
6217 var fingerDir = new Vector3().copy(targetRayPose.transform.position).sub(camPos).normalize();
6218 var fingerRay = new Ray(camPos, fingerDir);
6219 var intersection = fingerRay.intersectPlane(dragPlane, new Vector3());
6220
6221 if (intersection) {
6222 floorPosition.copy(intersection);
6223 floorPosition.setY(prevFloorPosition.y);
6224 hoverPosition.copy(intersection);
6225 hoverPosition.setY(intersection.y - modelBboxYOffset);
6226 }
6227
6228 return;
6229 }
6230
6231 var hitMatrix = new Matrix4().fromArray(hitPose.transform.matrix);
6232 var hitPosition = new Vector3().setFromMatrixPosition(hitMatrix); // Set new floor level when it's increased at least 10cm
6233
6234 var currentDragPlaneHeight = -dragPlane.constant;
6235 var hitDragPlaneHeight = hitPosition.y + hoverHeight + modelBboxYOffset;
6236
6237 if (hitDragPlaneHeight - currentDragPlaneHeight > 0.1) {
6238 dragPlane.constant = -hitDragPlaneHeight;
6239 }
6240
6241 var camToHitDir = new Vector3().subVectors(hitPosition, camPos).normalize();
6242 var camToHitRay = new Ray(camPos, camToHitDir);
6243 var hitOnDragPlane = camToHitRay.intersectPlane(dragPlane, new Vector3());
6244 if (!hitOnDragPlane) return;
6245 floorPosition.copy(hitOnDragPlane);
6246 floorPosition.setY(hitPosition.y);
6247 hoverPosition.copy(hitOnDragPlane);
6248 hoverPosition.setY(hitOnDragPlane.y - modelBboxYOffset);
6249 };
6250
6251 __proto.update = function (_a, delta) {
6252 var model = _a.model;
6253 var state = this._state;
6254 var modelPosition = this._modelPosition;
6255 var hoverPosition = this._hoverPosition;
6256 if (state === STATE.WAITING) return;
6257
6258 if (state !== STATE.BOUNCING) {
6259 // Hover
6260 var hoverMotion = this._hoverMotion;
6261 hoverMotion.update(delta); // Change only x, y component of position
6262
6263 var hoverOffset = hoverMotion.val * this._hoverAmplitude;
6264 modelPosition.copy(hoverPosition);
6265 modelPosition.setY(hoverPosition.y + hoverOffset);
6266 } else {
6267 // Bounce
6268 var bounceMotion = this._bounceMotion;
6269 bounceMotion.update(delta);
6270 modelPosition.setY(bounceMotion.val);
6271
6272 if (bounceMotion.progress >= 1) {
6273 this._state = STATE.WAITING;
6274 }
6275 }
6276
6277 var modelBbox = model.bbox;
6278 var modelYOffset = modelBbox.getCenter(new Vector3()).y - modelBbox.min.y; // modelPosition = where model.bbox.min.y should be
6279
6280 model.scene.position.copy(modelPosition.clone().setY(modelPosition.y + modelYOffset));
6281 };
6282
6283 return ARFloorTranslateControl;
6284}();
6285
6286/*
6287 * Copyright (c) 2020 NAVER Corp.
6288 * egjs projects are licensed under the MIT license
6289 */
6290/**
6291 * UI element displaying model's scale percentage info when user chaning model's scale.
6292 * @category Controls-AR
6293 */
6294
6295var ScaleUI =
6296/*#__PURE__*/
6297function () {
6298 /**
6299 * Create new instance of ScaleUI
6300 * @param {ScaleUIOption} [options={}] Options
6301 */
6302 function ScaleUI(_a) {
6303 var _b = _a === void 0 ? {} : _a,
6304 _c = _b.width,
6305 width = _c === void 0 ? 0.1 : _c,
6306 _d = _b.padding,
6307 padding = _d === void 0 ? 20 : _d,
6308 _e = _b.offset,
6309 offset = _e === void 0 ? 0.05 : _e,
6310 _f = _b.font,
6311 font = _f === void 0 ? "64px sans-serif" : _f,
6312 _g = _b.color,
6313 color = _g === void 0 ? "white" : _g;
6314
6315 var canvas = document.createElement("canvas");
6316 var ctx = canvas.getContext("2d");
6317 ctx.font = font; // Maximum canvas width should be equal to this
6318
6319 var maxText = ctx.measureText("100%"); // Following APIs won't work on IE, but it's WebXR so I think it's okay
6320
6321 var maxWidth = maxText.actualBoundingBoxLeft + maxText.actualBoundingBoxRight;
6322 var maxHeight = maxText.actualBoundingBoxAscent + maxText.actualBoundingBoxDescent;
6323 var widthPowerOfTwo = toPowerOfTwo(maxWidth);
6324 canvas.width = widthPowerOfTwo;
6325 canvas.height = widthPowerOfTwo; // This considers increased amount by making width to power of two
6326
6327 var planeWidth = width * (widthPowerOfTwo / maxWidth);
6328 this._ctx = ctx;
6329 this._canvas = canvas;
6330 this._height = planeWidth * maxHeight / maxWidth; // Text height inside plane
6331
6332 this._texture = new CanvasTexture(canvas); // Plane is square
6333
6334 var uiGeometry = new PlaneGeometry(planeWidth, planeWidth);
6335 var mesh = new Mesh(uiGeometry, new MeshBasicMaterial({
6336 map: this._texture,
6337 transparent: true
6338 }));
6339 mesh.matrixAutoUpdate = false;
6340 this._mesh = mesh;
6341 this._font = font;
6342 this._color = color;
6343 this._padding = padding;
6344 this._offset = offset;
6345 }
6346
6347 var __proto = ScaleUI.prototype;
6348 Object.defineProperty(__proto, "mesh", {
6349 /**
6350 * Scale UI's plane mesh
6351 * @readonly
6352 */
6353 get: function () {
6354 return this._mesh;
6355 },
6356 enumerable: false,
6357 configurable: true
6358 });
6359 Object.defineProperty(__proto, "height", {
6360 /**
6361 * Scale UI's height value
6362 * @readonly
6363 */
6364 get: function () {
6365 return this._height;
6366 },
6367 enumerable: false,
6368 configurable: true
6369 });
6370 Object.defineProperty(__proto, "visible", {
6371 /**
6372 * Whether UI is visible or not.
6373 * @readonly
6374 */
6375 get: function () {
6376 return this._mesh.visible;
6377 },
6378 enumerable: false,
6379 configurable: true
6380 });
6381
6382 __proto.updatePosition = function (position, focus) {
6383 // Update mesh
6384 var mesh = this._mesh;
6385 mesh.lookAt(focus);
6386 mesh.position.copy(position);
6387 mesh.position.setY(position.y + this._height / 2 + this._offset);
6388 mesh.updateMatrix();
6389 };
6390
6391 __proto.updateScale = function (scale) {
6392 var ctx = this._ctx;
6393 var canvas = this._canvas;
6394 var padding = this._padding;
6395 var scalePercentage = (scale * 100).toFixed(0);
6396 ctx.clearRect(0, 0, canvas.width, canvas.height);
6397 var centerX = canvas.width / 2;
6398 var centerY = canvas.height / 2; // Draw round rect
6399
6400 var textSize = ctx.measureText(scalePercentage + "%");
6401 var halfWidth = (textSize.actualBoundingBoxLeft + textSize.actualBoundingBoxRight) / 2;
6402 var halfHeight = (textSize.actualBoundingBoxAscent + textSize.actualBoundingBoxDescent) / 2;
6403 ctx.beginPath();
6404 ctx.moveTo(centerX - halfWidth, centerY - halfHeight - padding);
6405 ctx.lineTo(centerX + halfWidth, centerY - halfHeight - padding);
6406 ctx.quadraticCurveTo(centerX + halfWidth + padding, centerY - halfHeight - padding, centerX + halfWidth + padding, centerY - halfHeight);
6407 ctx.lineTo(centerX + halfWidth + padding, centerY + halfHeight);
6408 ctx.quadraticCurveTo(centerX + halfWidth + padding, centerY + halfHeight + padding, centerX + halfWidth, centerY + halfHeight + padding);
6409 ctx.lineTo(centerX - halfWidth, centerY + halfHeight + padding);
6410 ctx.quadraticCurveTo(centerX - halfWidth - padding, centerY + halfHeight + padding, centerX - halfWidth - padding, centerY + halfHeight);
6411 ctx.lineTo(centerX - halfWidth - padding, centerY - halfHeight);
6412 ctx.quadraticCurveTo(centerX - halfWidth - padding, centerY - halfHeight - padding, centerX - halfWidth, centerY - halfHeight - padding);
6413 ctx.closePath();
6414 ctx.lineWidth = 5;
6415 ctx.fillStyle = "rgba(0, 0, 0, 0.3)";
6416 ctx.fill();
6417 ctx.stroke(); // Draw text
6418
6419 ctx.font = this._font;
6420 ctx.textAlign = "center";
6421 ctx.textBaseline = "middle";
6422 ctx.strokeStyle = this._color;
6423 ctx.fillStyle = this._color;
6424 ctx.fillText(scalePercentage + "%", centerX, centerY);
6425 this._texture.needsUpdate = true;
6426 };
6427 /**
6428 * Show UI
6429 */
6430
6431
6432 __proto.show = function () {
6433 this._mesh.visible = true;
6434 };
6435 /**
6436 * Hide UI
6437 */
6438
6439
6440 __proto.hide = function () {
6441 this._mesh.visible = false;
6442 };
6443
6444 return ScaleUI;
6445}();
6446
6447/*
6448 * Copyright (c) 2020 NAVER Corp.
6449 * egjs projects are licensed under the MIT license
6450 */
6451/**
6452 * Model's scale controller which works on AR(WebXR) mode.
6453 * @category Controls-AR
6454 */
6455
6456var ARScaleControl =
6457/*#__PURE__*/
6458function () {
6459 /**
6460 * Create new instance of ARScaleControl
6461 * @param {ARScaleControlOption} [options={}] Options
6462 */
6463 function ARScaleControl(_a) {
6464 var _b = _a === void 0 ? {} : _a,
6465 _c = _b.min,
6466 min = _c === void 0 ? 0.05 : _c,
6467 _d = _b.max,
6468 max = _d === void 0 ? 2 : _d;
6469
6470 this._enabled = true;
6471 this._active = false;
6472 this._prevCoordDistance = -1;
6473 this._scaleMultiplier = 1;
6474 this._initialScale = new Vector3();
6475 this._ui = new ScaleUI();
6476 this._motion = new Motion({
6477 duration: 0,
6478 range: {
6479 min: min,
6480 max: max
6481 }
6482 });
6483
6484 this._motion.reset(1); // default scale is 1(100%)
6485
6486
6487 this._ui = new ScaleUI();
6488 }
6489
6490 var __proto = ARScaleControl.prototype;
6491 Object.defineProperty(__proto, "enabled", {
6492 /**
6493 * Whether this control is enabled or not
6494 * @readonly
6495 */
6496 get: function () {
6497 return this._enabled;
6498 },
6499 enumerable: false,
6500 configurable: true
6501 });
6502 Object.defineProperty(__proto, "scale", {
6503 get: function () {
6504 return this._initialScale.clone().multiplyScalar(this._scaleMultiplier);
6505 },
6506 enumerable: false,
6507 configurable: true
6508 });
6509 Object.defineProperty(__proto, "scaleMultiplier", {
6510 get: function () {
6511 return this._scaleMultiplier;
6512 },
6513 enumerable: false,
6514 configurable: true
6515 });
6516 Object.defineProperty(__proto, "range", {
6517 /**
6518 * Range of the scale
6519 * @readonly
6520 */
6521 get: function () {
6522 return this._motion.range;
6523 },
6524 enumerable: false,
6525 configurable: true
6526 });
6527
6528 __proto.init = function (_a) {
6529 var view3d = _a.view3d;
6530
6531 this._initialScale.copy(view3d.model.scene.scale);
6532
6533 view3d.scene.add(this._ui.mesh);
6534 };
6535
6536 __proto.destroy = function (_a) {
6537 var view3d = _a.view3d;
6538 view3d.scene.remove(this._ui.mesh);
6539 };
6540
6541 __proto.setInitialPos = function (coords) {
6542 this._prevCoordDistance = new Vector2().subVectors(coords[0], coords[1]).length();
6543 };
6544 /**
6545 * Enable this control
6546 */
6547
6548
6549 __proto.enable = function () {
6550 this._enabled = true;
6551 };
6552 /**
6553 * Disable this control
6554 */
6555
6556
6557 __proto.disable = function () {
6558 this._enabled = false;
6559 this.deactivate();
6560 };
6561
6562 __proto.activate = function (ctx, gesture) {
6563 this._active = true;
6564
6565 this._ui.show();
6566
6567 this._updateUIPosition(ctx);
6568 };
6569
6570 __proto.deactivate = function () {
6571 this._active = false;
6572
6573 this._ui.hide();
6574
6575 this._prevCoordDistance = -1;
6576 };
6577 /**
6578 * Update scale range
6579 * @param min Minimum scale
6580 * @param max Maximum scale
6581 */
6582
6583
6584 __proto.setRange = function (min, max) {
6585 this._motion.range = {
6586 min: min,
6587 max: max
6588 };
6589 };
6590
6591 __proto.process = function (ctx, _a) {
6592 var coords = _a.coords;
6593 if (coords.length !== 2 || !this._enabled || !this._active) return;
6594 var motion = this._motion;
6595 var distance = new Vector2().subVectors(coords[0], coords[1]).length();
6596 var delta = distance - this._prevCoordDistance;
6597 motion.setEndDelta(delta);
6598 this._prevCoordDistance = distance;
6599
6600 this._updateUIPosition(ctx);
6601 };
6602
6603 __proto.update = function (_a, deltaTime) {
6604 var model = _a.model;
6605 if (!this._enabled || !this._active) return;
6606 var motion = this._motion;
6607 motion.update(deltaTime);
6608 this._scaleMultiplier = motion.val;
6609
6610 this._ui.updateScale(this._scaleMultiplier);
6611
6612 model.scene.scale.copy(this.scale);
6613 };
6614
6615 __proto._updateUIPosition = function (_a) {
6616 var view3d = _a.view3d,
6617 xrCam = _a.xrCam; // Update UI
6618
6619 var model = view3d.model;
6620 var camPos = new Vector3().setFromMatrixPosition(xrCam.matrixWorld);
6621 var uiPos = model.scene.position.clone().setY(model.bbox.max.y);
6622
6623 this._ui.updatePosition(uiPos, camPos);
6624 };
6625
6626 return ARScaleControl;
6627}();
6628
6629/*
6630 * Copyright (c) 2020 NAVER Corp.
6631 * egjs projects are licensed under the MIT license
6632 */
6633/**
6634 * Ring type indicator for showing where the model's at.
6635 * @category Controls-AR
6636 */
6637
6638var FloorIndicator =
6639/*#__PURE__*/
6640function () {
6641 /**
6642 * Create new instance of FloorIndicator
6643 * @param {FloorIndicatorOption} [options={}] Options
6644 */
6645 function FloorIndicator(_a) {
6646 var _b = _a === void 0 ? {} : _a,
6647 _c = _b.ringOpacity,
6648 ringOpacity = _c === void 0 ? 0.3 : _c,
6649 _d = _b.dirIndicatorOpacity,
6650 dirIndicatorOpacity = _d === void 0 ? 1 : _d,
6651 _e = _b.fadeoutDuration,
6652 fadeoutDuration = _e === void 0 ? 1000 : _e;
6653
6654 var deg10 = Math.PI / 18;
6655 var dimmedRingGeomtry = new RingGeometry(0.975, 1, 150, 1, -6 * deg10, 30 * deg10);
6656 var reticle = new CircleGeometry(0.1, 30, 0, Math.PI * 2);
6657 dimmedRingGeomtry.merge(reticle);
6658 var highlightedRingGeometry = new RingGeometry(0.96, 1.015, 30, 1, 25 * deg10, 4 * deg10); // Create little triangle in ring
6659
6660 var ringVertices = highlightedRingGeometry.vertices;
6661 var trianglePart = ringVertices.slice(Math.floor(11 * ringVertices.length / 16), Math.floor(13 * ringVertices.length / 16));
6662 var firstY = trianglePart[0].y;
6663 var midIndex = Math.floor(trianglePart.length / 2);
6664 trianglePart.forEach(function (vec, vecIdx) {
6665 var offsetAmount = 0.025 * (midIndex - Math.abs(vecIdx - midIndex));
6666 vec.setY(firstY - offsetAmount);
6667 });
6668 var indicatorMat = new Matrix4().makeRotationX(-Math.PI / 2);
6669 var mergedGeometry = new Geometry();
6670 mergedGeometry.merge(dimmedRingGeomtry, indicatorMat, 0);
6671 mergedGeometry.merge(highlightedRingGeometry, indicatorMat, 1);
6672 var dimmedMaterial = new MeshBasicMaterial({
6673 transparent: true,
6674 opacity: ringOpacity,
6675 color: 0xffffff
6676 });
6677 var highlightMaterial = new MeshBasicMaterial({
6678 transparent: true,
6679 opacity: dirIndicatorOpacity,
6680 color: 0xffffff
6681 });
6682 var materials = [dimmedMaterial, highlightMaterial];
6683 this._mesh = new Mesh(mergedGeometry, materials);
6684 this._mesh.matrixAutoUpdate = false;
6685 this._animator = new Motion({
6686 duration: fadeoutDuration
6687 });
6688 this._opacityRange = {
6689 min: ringOpacity,
6690 max: dirIndicatorOpacity
6691 };
6692 }
6693
6694 var __proto = FloorIndicator.prototype;
6695 Object.defineProperty(__proto, "mesh", {
6696 /**
6697 * Ring mesh
6698 */
6699 get: function () {
6700 return this._mesh;
6701 },
6702 enumerable: false,
6703 configurable: true
6704 });
6705
6706 __proto.update = function (_a) {
6707 var delta = _a.delta,
6708 scale = _a.scale,
6709 position = _a.position,
6710 rotation = _a.rotation;
6711 var mesh = this._mesh;
6712 var animator = this._animator;
6713 if (!this._mesh.visible) return;
6714 animator.update(delta);
6715 var materials = this._mesh.material;
6716 var minOpacityMat = materials[0];
6717 var maxOpacityMat = materials[1];
6718 var opacityRange = this._opacityRange;
6719 minOpacityMat.opacity = animator.val * opacityRange.min;
6720 maxOpacityMat.opacity = animator.val * opacityRange.max;
6721
6722 if (animator.val <= 0) {
6723 mesh.visible = false;
6724 } // Update mesh
6725
6726
6727 mesh.scale.setScalar(scale);
6728 mesh.position.copy(position);
6729 mesh.quaternion.copy(rotation);
6730 mesh.updateMatrix();
6731 };
6732
6733 __proto.show = function () {
6734 this._mesh.visible = true;
6735
6736 this._animator.reset(1);
6737 };
6738
6739 __proto.fadeout = function () {
6740 this._animator.setEndDelta(-1);
6741 };
6742
6743 return FloorIndicator;
6744}();
6745
6746/*
6747 * Copyright (c) 2020 NAVER Corp.
6748 * egjs projects are licensed under the MIT license
6749 */
6750var GESTURE;
6751
6752(function (GESTURE) {
6753 GESTURE[GESTURE["NONE"] = 0] = "NONE";
6754 GESTURE[GESTURE["ONE_FINGER_HORIZONTAL"] = 1] = "ONE_FINGER_HORIZONTAL";
6755 GESTURE[GESTURE["ONE_FINGER_VERTICAL"] = 2] = "ONE_FINGER_VERTICAL";
6756 GESTURE[GESTURE["ONE_FINGER"] = 3] = "ONE_FINGER";
6757 GESTURE[GESTURE["TWO_FINGER_HORIZONTAL"] = 4] = "TWO_FINGER_HORIZONTAL";
6758 GESTURE[GESTURE["TWO_FINGER_VERTICAL"] = 8] = "TWO_FINGER_VERTICAL";
6759 GESTURE[GESTURE["TWO_FINGER"] = 12] = "TWO_FINGER";
6760 GESTURE[GESTURE["PINCH"] = 16] = "PINCH";
6761})(GESTURE || (GESTURE = {}));
6762
6763/*
6764 * Copyright (c) 2020 NAVER Corp.
6765 * egjs projects are licensed under the MIT license
6766 */
6767var STATE$1;
6768
6769(function (STATE) {
6770 STATE[STATE["WAITING"] = 0] = "WAITING";
6771 STATE[STATE["IN_DEADZONE"] = 1] = "IN_DEADZONE";
6772 STATE[STATE["OUT_OF_DEADZONE"] = 2] = "OUT_OF_DEADZONE";
6773})(STATE$1 || (STATE$1 = {}));
6774/**
6775 * Deadzone checker for deadzone-based controls
6776 * @category Controls-AR
6777 */
6778
6779
6780var DeadzoneChecker =
6781/*#__PURE__*/
6782function () {
6783 /**
6784 * Create new DeadzoneChecker
6785 * @param {DeadzoneCheckerOption} [options={}] Options
6786 */
6787 function DeadzoneChecker(_a) {
6788 var _b = (_a === void 0 ? {} : _a).size,
6789 size = _b === void 0 ? 0.1 : _b; // Internal States
6790
6791 this._state = STATE$1.WAITING;
6792 this._detectedGesture = GESTURE.NONE;
6793 this._testingGestures = GESTURE.NONE;
6794 this._lastFingerCount = 0;
6795 this._aspect = 1; // Store two prev positions, as it should be maintained separately
6796
6797 this._prevOneFingerPos = new Vector2();
6798 this._prevTwoFingerPos = new Vector2();
6799 this._initialTwoFingerDistance = 0;
6800 this._prevOneFingerPosInitialized = false;
6801 this._prevTwoFingerPosInitialized = false;
6802 this._size = size;
6803 }
6804
6805 var __proto = DeadzoneChecker.prototype;
6806 Object.defineProperty(__proto, "size", {
6807 /**
6808 * Size of the deadzone.
6809 * @type number
6810 */
6811 get: function () {
6812 return this._size;
6813 },
6814 set: function (val) {
6815 this._size = val;
6816 },
6817 enumerable: false,
6818 configurable: true
6819 });
6820 Object.defineProperty(__proto, "inDeadzone", {
6821 get: function () {
6822 return this._state === STATE$1.IN_DEADZONE;
6823 },
6824 enumerable: false,
6825 configurable: true
6826 });
6827 /**
6828 * Set screen aspect(height / width)
6829 * @param aspect Screen aspect value
6830 */
6831
6832 __proto.setAspect = function (aspect) {
6833 this._aspect = aspect;
6834 };
6835
6836 __proto.setFirstInput = function (inputs) {
6837 var fingerCount = inputs.length;
6838
6839 if (fingerCount === 1 && !this._prevOneFingerPosInitialized) {
6840 this._prevOneFingerPos.copy(inputs[0]);
6841
6842 this._prevOneFingerPosInitialized = true;
6843 } else if (fingerCount === 2 && !this._prevTwoFingerPosInitialized) {
6844 this._prevTwoFingerPos.copy(new Vector2().addVectors(inputs[0], inputs[1]).multiplyScalar(0.5));
6845
6846 this._initialTwoFingerDistance = new Vector2().subVectors(inputs[0], inputs[1]).length();
6847 this._prevTwoFingerPosInitialized = true;
6848 }
6849
6850 this._lastFingerCount = fingerCount;
6851 this._state = STATE$1.IN_DEADZONE;
6852 };
6853
6854 __proto.addTestingGestures = function () {
6855 var gestures = [];
6856
6857 for (var _i = 0; _i < arguments.length; _i++) {
6858 gestures[_i] = arguments[_i];
6859 }
6860
6861 this._testingGestures = this._testingGestures | gestures.reduce(function (gesture, accumulated) {
6862 return gesture | accumulated;
6863 }, GESTURE.NONE);
6864 };
6865
6866 __proto.cleanup = function () {
6867 this._testingGestures = GESTURE.NONE;
6868 this._lastFingerCount = 0;
6869 this._prevOneFingerPosInitialized = false;
6870 this._prevTwoFingerPosInitialized = false;
6871 this._initialTwoFingerDistance = 0;
6872 this._detectedGesture = GESTURE.NONE;
6873 this._state = STATE$1.WAITING;
6874 };
6875
6876 __proto.applyScreenAspect = function (inputs) {
6877 var aspect = this._aspect;
6878 inputs.forEach(function (input) {
6879 if (aspect > 1) {
6880 input.setY(input.y * aspect);
6881 } else {
6882 input.setX(input.x / aspect);
6883 }
6884 });
6885 };
6886
6887 __proto.check = function (inputs) {
6888 var state = this._state;
6889 var deadzone = this._size;
6890 var testingGestures = this._testingGestures;
6891 var lastFingerCount = this._lastFingerCount;
6892 var fingerCount = inputs.length;
6893
6894 if (state === STATE$1.OUT_OF_DEADZONE) {
6895 return this._detectedGesture;
6896 }
6897
6898 this._lastFingerCount = fingerCount;
6899 this.applyScreenAspect(inputs);
6900
6901 if (fingerCount !== lastFingerCount) {
6902 this.setFirstInput(inputs);
6903 return GESTURE.NONE;
6904 }
6905
6906 if (fingerCount === 1) {
6907 var input = inputs[0];
6908
6909 var prevPos = this._prevOneFingerPos.clone();
6910
6911 var diff = new Vector2().subVectors(input, prevPos);
6912
6913 if (diff.length() > deadzone) {
6914 if (Math.abs(diff.x) > Math.abs(diff.y)) {
6915 if (GESTURE.ONE_FINGER_HORIZONTAL & testingGestures) {
6916 this._detectedGesture = GESTURE.ONE_FINGER_HORIZONTAL;
6917 }
6918 } else {
6919 if (GESTURE.ONE_FINGER_VERTICAL & testingGestures) {
6920 this._detectedGesture = GESTURE.ONE_FINGER_VERTICAL;
6921 }
6922 }
6923 }
6924 } else if (fingerCount === 2) {
6925 var middle = new Vector2().addVectors(inputs[1], inputs[0]).multiplyScalar(0.5);
6926
6927 var prevPos = this._prevTwoFingerPos.clone();
6928
6929 var diff = new Vector2().subVectors(middle, prevPos);
6930
6931 if (diff.length() > deadzone) {
6932 if (Math.abs(diff.x) > Math.abs(diff.y)) {
6933 if (GESTURE.TWO_FINGER_HORIZONTAL & testingGestures) {
6934 this._detectedGesture = GESTURE.TWO_FINGER_HORIZONTAL;
6935 }
6936 } else {
6937 if (GESTURE.TWO_FINGER_VERTICAL & testingGestures) {
6938 this._detectedGesture = GESTURE.TWO_FINGER_VERTICAL;
6939 }
6940 }
6941 }
6942
6943 var distance = new Vector2().subVectors(inputs[1], inputs[0]).length();
6944
6945 if (Math.abs(distance - this._initialTwoFingerDistance) > deadzone) {
6946 if (GESTURE.PINCH & testingGestures) {
6947 this._detectedGesture = GESTURE.PINCH;
6948 }
6949 }
6950 }
6951
6952 if (this._detectedGesture !== GESTURE.NONE) {
6953 this._state = STATE$1.OUT_OF_DEADZONE;
6954 }
6955
6956 return this._detectedGesture;
6957 };
6958
6959 return DeadzoneChecker;
6960}();
6961
6962/*
6963 * Copyright (c) 2020 NAVER Corp.
6964 * egjs projects are licensed under the MIT license
6965 */
6966/**
6967 * AR control for {@link FloorARSession}
6968 * @category Controls-AR
6969 */
6970
6971var ARFloorControl =
6972/*#__PURE__*/
6973function () {
6974 /**
6975 * Create new instance of ARFloorControl
6976 * @param {ARFloorControlOption} options Options
6977 */
6978 function ARFloorControl(options) {
6979 var _this = this;
6980
6981 if (options === void 0) {
6982 options = {};
6983 }
6984
6985 this._enabled = true;
6986 this._initialized = false;
6987 this._modelHit = false;
6988 this._hitTestSource = null;
6989
6990 this.onSelectStart = function (ctx) {
6991 var view3d = ctx.view3d,
6992 frame = ctx.frame,
6993 xrCam = ctx.xrCam,
6994 referenceSpace = ctx.referenceSpace;
6995 var hitTestSource = _this._hitTestSource;
6996 if (!hitTestSource || !_this._enabled) return;
6997 var deadzoneChecker = _this._deadzoneChecker;
6998 var rotateControl = _this._rotateControl;
6999 var translateControl = _this._translateControl;
7000 var scaleControl = _this._scaleControl; // Update deadzone testing gestures
7001
7002 if (rotateControl.enabled) {
7003 deadzoneChecker.addTestingGestures(GESTURE.ONE_FINGER);
7004 }
7005
7006 if (translateControl.enabled) {
7007 deadzoneChecker.addTestingGestures(GESTURE.ONE_FINGER);
7008 }
7009
7010 if (scaleControl.enabled) {
7011 deadzoneChecker.addTestingGestures(GESTURE.PINCH);
7012 }
7013
7014 var hitResults = frame.getHitTestResultsForTransientInput(hitTestSource);
7015
7016 var coords = _this._hitResultToVector(hitResults);
7017
7018 deadzoneChecker.applyScreenAspect(coords);
7019 deadzoneChecker.setFirstInput(coords);
7020
7021 if (coords.length === 1) {
7022 // Check finger is on the model
7023 var modelBbox = view3d.model.bbox;
7024 var targetRayPose = frame.getPose(hitResults[0].inputSource.targetRaySpace, referenceSpace);
7025 var camPos = new Vector3().setFromMatrixPosition(xrCam.matrixWorld);
7026 var fingerDir = new Vector3().copy(targetRayPose.transform.position).sub(camPos).normalize();
7027 var fingerRay = new Ray(camPos, fingerDir);
7028 var intersection = fingerRay.intersectBox(modelBbox, new Vector3());
7029
7030 if (intersection) {
7031 // Touch point intersected with model
7032 _this._modelHit = true;
7033 }
7034 }
7035
7036 _this._floorIndicator.show();
7037 };
7038
7039 this.onSelectEnd = function () {
7040 _this.deactivate();
7041
7042 _this._floorIndicator.fadeout();
7043 };
7044
7045 this._rotateControl = new ARSwirlControl(__assign({
7046 showIndicator: false
7047 }, options.rotate));
7048 this._translateControl = new ARFloorTranslateControl(options.translate);
7049 this._scaleControl = new ARScaleControl(options.scale);
7050 this._floorIndicator = new FloorIndicator(options.floorIndicator);
7051 this._deadzoneChecker = new DeadzoneChecker(options.deadzone);
7052 }
7053
7054 var __proto = ARFloorControl.prototype;
7055 Object.defineProperty(__proto, "enabled", {
7056 /**
7057 * Return whether this control is enabled or not
7058 */
7059 get: function () {
7060 return this._enabled;
7061 },
7062 enumerable: false,
7063 configurable: true
7064 });
7065 Object.defineProperty(__proto, "rotate", {
7066 /**
7067 * {@link ARSwirlControl} in this control
7068 */
7069 get: function () {
7070 return this._rotateControl;
7071 },
7072 enumerable: false,
7073 configurable: true
7074 });
7075 Object.defineProperty(__proto, "translate", {
7076 /**
7077 * {@link ARFloorTranslateControl} in this control
7078 */
7079 get: function () {
7080 return this._translateControl;
7081 },
7082 enumerable: false,
7083 configurable: true
7084 });
7085 Object.defineProperty(__proto, "scale", {
7086 /**
7087 * {@link ARScaleControl} in this control
7088 */
7089 get: function () {
7090 return this._scaleControl;
7091 },
7092 enumerable: false,
7093 configurable: true
7094 });
7095 Object.defineProperty(__proto, "controls", {
7096 get: function () {
7097 return [this._rotateControl, this._translateControl, this._scaleControl];
7098 },
7099 enumerable: false,
7100 configurable: true
7101 });
7102
7103 __proto.init = function (ctx, initialFloorPos) {
7104 var _this = this;
7105
7106 var session = ctx.session,
7107 view3d = ctx.view3d,
7108 size = ctx.size;
7109 this.controls.forEach(function (control) {
7110 return control.init(ctx);
7111 });
7112
7113 this._translateControl.initFloorPosition(initialFloorPos);
7114
7115 this._deadzoneChecker.setAspect(size.height / size.width);
7116
7117 view3d.scene.add(this._floorIndicator.mesh);
7118 this._initialized = true;
7119 session.requestHitTestSourceForTransientInput({
7120 profile: INPUT_PROFILE.TOUCH
7121 }).then(function (transientHitTestSource) {
7122 _this._hitTestSource = transientHitTestSource;
7123 });
7124 };
7125 /**
7126 * Destroy this control and deactivate it
7127 * @param view3d Instance of the {@link View3D}
7128 */
7129
7130
7131 __proto.destroy = function (ctx) {
7132 if (!this._initialized) return;
7133
7134 if (this._hitTestSource) {
7135 this._hitTestSource.cancel();
7136
7137 this._hitTestSource = null;
7138 }
7139
7140 ctx.view3d.scene.remove(this._floorIndicator.mesh);
7141 this.deactivate();
7142 this.controls.forEach(function (control) {
7143 return control.destroy(ctx);
7144 });
7145 this._initialized = false;
7146 };
7147
7148 __proto.deactivate = function () {
7149 this._modelHit = false;
7150
7151 this._deadzoneChecker.cleanup();
7152
7153 this.controls.forEach(function (control) {
7154 return control.deactivate();
7155 });
7156 };
7157 /**
7158 * Enable this control
7159 */
7160
7161
7162 __proto.enable = function () {
7163 this._enabled = true;
7164 };
7165 /**
7166 * Disable this control
7167 */
7168
7169
7170 __proto.disable = function () {
7171 this._enabled = false;
7172 this.deactivate();
7173 };
7174
7175 __proto.update = function (ctx) {
7176 var view3d = ctx.view3d,
7177 session = ctx.session,
7178 frame = ctx.frame;
7179 var hitTestSource = this._hitTestSource;
7180 if (!hitTestSource || !view3d.model) return;
7181 var deadzoneChecker = this._deadzoneChecker;
7182 var inputSources = session.inputSources;
7183 var hitResults = frame.getHitTestResultsForTransientInput(hitTestSource);
7184
7185 var coords = this._hitResultToVector(hitResults);
7186
7187 var xrInputs = {
7188 coords: coords,
7189 inputSources: inputSources,
7190 hitResults: hitResults
7191 };
7192
7193 if (deadzoneChecker.inDeadzone) {
7194 this._checkDeadzone(ctx, xrInputs);
7195 } else {
7196 this._processInput(ctx, xrInputs);
7197 }
7198
7199 this._updateControls(ctx);
7200 };
7201
7202 __proto._checkDeadzone = function (ctx, _a) {
7203 var coords = _a.coords;
7204
7205 var gesture = this._deadzoneChecker.check(coords.map(function (coord) {
7206 return coord.clone();
7207 }));
7208
7209 var rotateControl = this._rotateControl;
7210 var translateControl = this._translateControl;
7211 var scaleControl = this._scaleControl;
7212 if (gesture === GESTURE.NONE) return;
7213
7214 switch (gesture) {
7215 case GESTURE.ONE_FINGER_HORIZONTAL:
7216 case GESTURE.ONE_FINGER_VERTICAL:
7217 if (this._modelHit) {
7218 translateControl.activate(ctx, gesture);
7219 translateControl.setInitialPos(coords);
7220 } else {
7221 rotateControl.activate(ctx, gesture);
7222 rotateControl.setInitialPos(coords);
7223 }
7224
7225 break;
7226
7227 case GESTURE.PINCH:
7228 scaleControl.activate(ctx, gesture);
7229 scaleControl.setInitialPos(coords);
7230 break;
7231 }
7232 };
7233
7234 __proto._processInput = function (ctx, inputs) {
7235 this.controls.forEach(function (control) {
7236 return control.process(ctx, inputs);
7237 });
7238 };
7239
7240 __proto._updateControls = function (ctx) {
7241 var view3d = ctx.view3d,
7242 model = ctx.model,
7243 delta = ctx.delta;
7244 var deltaMilisec = delta * 1000;
7245 this.controls.forEach(function (control) {
7246 return control.update(ctx, deltaMilisec);
7247 });
7248 model.scene.updateMatrix();
7249 var modelRotation = this._rotateControl.rotation;
7250 var floorPosition = this._translateControl.floorPosition;
7251 view3d.scene.update(model, {
7252 floorPosition: floorPosition
7253 }); // Get a scaled bbox, which only has scale applied on it.
7254
7255 var scaleControl = this._scaleControl;
7256 var scaledBbox = model.initialBbox;
7257 scaledBbox.min.multiply(scaleControl.scale);
7258 scaledBbox.max.multiply(scaleControl.scale);
7259 var floorIndicator = this._floorIndicator;
7260 var boundingSphere = scaledBbox.getBoundingSphere(new Sphere());
7261 floorIndicator.update({
7262 delta: deltaMilisec,
7263 scale: boundingSphere.radius,
7264 position: floorPosition,
7265 rotation: modelRotation
7266 });
7267 };
7268
7269 __proto._hitResultToVector = function (hitResults) {
7270 return hitResults.map(function (input) {
7271 return new Vector2().set(input.inputSource.gamepad.axes[0], -input.inputSource.gamepad.axes[1]);
7272 });
7273 };
7274
7275 return ARFloorControl;
7276}();
7277
7278/*
7279 * Copyright (c) 2020 NAVER Corp.
7280 * egjs projects are licensed under the MIT license
7281 */
7282/**
7283 * WebXR based AR session which puts model on the detected floor.
7284 * @category XR
7285 * @fires WebARSession#start
7286 * @fires WebARSession#end
7287 * @fires WebARSession#canPlace
7288 * @fires WebARSession#modelPlaced
7289 */
7290
7291var FloorARSession =
7292/*#__PURE__*/
7293function (_super) {
7294 __extends(FloorARSession, _super);
7295 /**
7296 * Create new instance of FloorARSession
7297 * @param {FloorARSessionOption} options Options
7298 */
7299
7300
7301 function FloorARSession(options) {
7302 if (options === void 0) {
7303 options = {};
7304 }
7305
7306 var _this = _super.call(this, options) || this;
7307
7308 _this.onStart = function (ctx) {
7309 var view3d = ctx.view3d,
7310 session = ctx.session;
7311
7312 _super.prototype.onStart.call(_this, ctx);
7313
7314 _this._control = new ARFloorControl(_this._options);
7315 view3d.scene.hide();
7316
7317 _this._hitTest.init(session);
7318 };
7319
7320 _this.onEnd = function (ctx) {
7321 var view3d = ctx.view3d,
7322 session = ctx.session;
7323
7324 _super.prototype.onEnd.call(_this, ctx);
7325
7326 _this._renderContext = null;
7327 _this._modelPlaced = false;
7328 session.removeEventListener(EVENTS$1.SELECT_START, _this._onSelectStart);
7329 session.removeEventListener(EVENTS$1.SELECT_END, _this._onSelectEnd);
7330
7331 _this._hitTest.destroy();
7332
7333 _this._control.destroy(ctx);
7334
7335 _this._control = null;
7336 view3d.scene.show();
7337 };
7338
7339 _this._beforeRender = function (ctx) {
7340 _this._renderContext = ctx;
7341
7342 if (!_this._modelPlaced) {
7343 _this._initModelPosition(ctx);
7344 } else {
7345 _this._control.update(ctx);
7346 }
7347 };
7348
7349 _this._onSelectStart = function (e) {
7350 _this._control.onSelectStart(__assign(__assign({}, _this._renderContext), {
7351 frame: e.frame
7352 }));
7353 };
7354
7355 _this._onSelectEnd = function () {
7356 _this._control.onSelectEnd();
7357 };
7358
7359 _this._control = null;
7360 _this._renderContext = null;
7361 _this._modelPlaced = false;
7362 _this._hitTest = new HitTest();
7363 _this._features = merge(_this._features, _this._hitTest.features);
7364 _this._options = options;
7365 return _this;
7366 }
7367
7368 var __proto = FloorARSession.prototype;
7369 Object.defineProperty(__proto, "control", {
7370 /**
7371 * {@link ARControl} instance of this session
7372 * @type ARFloorControl | null
7373 */
7374 get: function () {
7375 return this._control;
7376 },
7377 enumerable: false,
7378 configurable: true
7379 });
7380
7381 __proto._initModelPosition = function (ctx) {
7382 var _a;
7383
7384 var view3d = ctx.view3d,
7385 frame = ctx.frame,
7386 session = ctx.session;
7387 var model = view3d.model;
7388 var hitTest = this._hitTest; // Make sure the model is loaded
7389
7390 if (!hitTest.ready || !model) return;
7391 var control = this._control;
7392 var referenceSpace = view3d.renderer.threeRenderer.xr.getReferenceSpace();
7393 var hitTestResults = hitTest.getResults(frame);
7394 if (hitTestResults.length <= 0) return;
7395 var hit = hitTestResults[0];
7396 var hitMatrix = new Matrix4().fromArray(hit.getPose(referenceSpace).transform.matrix); // If transformed coords space's y axis is not facing up, don't use it.
7397
7398 if (hitMatrix.elements[5] < 0.75) return;
7399 var modelRoot = model.scene;
7400 var hitPosition = new Vector3().setFromMatrixPosition(hitMatrix); // Reset rotation & update position
7401
7402 modelRoot.quaternion.set(0, 0, 0, 1);
7403 modelRoot.position.copy(hitPosition);
7404 modelRoot.position.setY(modelRoot.position.y - model.bbox.min.y);
7405 modelRoot.updateMatrix();
7406 view3d.scene.update(model);
7407 view3d.scene.show();
7408 this.emit("canPlace"); // Don't need it
7409
7410 hitTest.destroy();
7411 session.addEventListener(EVENTS$1.SELECT_START, this._onSelectStart);
7412 session.addEventListener(EVENTS$1.SELECT_END, this._onSelectEnd);
7413 (_a = this._domOverlay) === null || _a === void 0 ? void 0 : _a.hideLoading();
7414 this._modelPlaced = true;
7415 this.emit("modelPlaced"); // Show scale up animation
7416
7417 var originalModelScale = modelRoot.scale.clone();
7418 var scaleUpAnimation = new Animation({
7419 context: session
7420 });
7421 scaleUpAnimation.on("progress", function (evt) {
7422 var newScale = originalModelScale.clone().multiplyScalar(evt.easedProgress);
7423 modelRoot.scale.copy(newScale);
7424 });
7425 scaleUpAnimation.on("finish", function () {
7426 modelRoot.scale.copy(originalModelScale);
7427 control.init(ctx, hitPosition);
7428 });
7429 scaleUpAnimation.start();
7430 };
7431
7432 return FloorARSession;
7433}(WebARSession);
7434
7435/*
7436 * Copyright (c) 2020 NAVER Corp.
7437 * egjs projects are licensed under the MIT license
7438 */
7439/**
7440 * Arrow indicator for AR model translatioon.
7441 * @category Controls-AR
7442 */
7443
7444var ArrowIndicator =
7445/*#__PURE__*/
7446function () {
7447 /**
7448 * Create new ArrowIndicator
7449 * @param {ArrowIndicatorOption} [options={}] Options
7450 */
7451 function ArrowIndicator(_a) {
7452 var _this = this;
7453
7454 var _b = (_a === void 0 ? {} : _a).color,
7455 color = _b === void 0 ? 0xffffff : _b;
7456 var bodyGeometry = new CylinderBufferGeometry(0.1, 0.1, 1);
7457 var coneGeometry = new CylinderBufferGeometry(0, 0.5, 1, 30, 1);
7458 bodyGeometry.translate(0, 0.5, 0);
7459 coneGeometry.translate(0, 1.5, 0);
7460 var body = new Mesh(bodyGeometry, new MeshBasicMaterial({
7461 color: color
7462 }));
7463 var cone = new Mesh(coneGeometry, new MeshBasicMaterial({
7464 color: color
7465 }));
7466 var arrow = new Group();
7467 arrow.add(body);
7468 arrow.add(cone);
7469 this._arrows = [arrow];
7470 this._obj = new Group();
7471
7472 this._obj.add(arrow);
7473
7474 range(3).forEach(function (idx) {
7475 var copied = arrow.clone(true);
7476 copied.rotateZ(Math.PI / 2 * (idx + 1));
7477
7478 _this._obj.add(copied);
7479
7480 _this._arrows.push(copied);
7481 });
7482 this.hide();
7483 }
7484
7485 var __proto = ArrowIndicator.prototype;
7486 Object.defineProperty(__proto, "object", {
7487 /**
7488 * {@link https://threejs.org/docs/index.html#api/en/objects/Group THREE.Group} object that contains arrows.
7489 */
7490 get: function () {
7491 return this._obj;
7492 },
7493 enumerable: false,
7494 configurable: true
7495 });
7496 /**
7497 * Show indicator
7498 */
7499
7500 __proto.show = function () {
7501 this._obj.visible = true;
7502 };
7503 /**
7504 * Hide indicator
7505 */
7506
7507
7508 __proto.hide = function () {
7509 this._obj.visible = false;
7510 };
7511 /**
7512 * Change the center of the arrows to a given position
7513 * @param position Position to set as center of the arrows
7514 */
7515
7516
7517 __proto.updatePosition = function (position) {
7518 this._obj.position.copy(position);
7519 };
7520 /**
7521 * Update the arrow's offset from the center
7522 * @param offset Offset vector.
7523 */
7524
7525
7526 __proto.updateOffset = function (offset) {
7527 this._arrows.forEach(function (arrow, idx) {
7528 var facingDirection = new Vector3(0, 1, 0).applyQuaternion(arrow.quaternion);
7529 var facingOffset = facingDirection.multiply(offset);
7530 arrow.position.copy(facingOffset);
7531 });
7532 };
7533 /**
7534 * Update arrow's scale
7535 * @param scale Scale vector
7536 */
7537
7538
7539 __proto.updateScale = function (scale) {
7540 this._arrows.forEach(function (arrow) {
7541 return arrow.scale.setScalar(scale);
7542 });
7543 };
7544 /**
7545 * Update arrow's rotation.
7546 * @param rotation Quaternion value to rotate arrows.
7547 */
7548
7549
7550 __proto.updateRotation = function (rotation) {
7551 this._obj.quaternion.copy(rotation);
7552 };
7553
7554 return ArrowIndicator;
7555}();
7556
7557/*
7558 * Copyright (c) 2020 NAVER Corp.
7559 * egjs projects are licensed under the MIT license
7560 */
7561/**
7562 * Model's translation(position) control for {@link ARWallControl}
7563 * @category Controls-AR
7564 */
7565
7566var ARWallTranslateControl =
7567/*#__PURE__*/
7568function () {
7569 /**
7570 * Create new instance of ARTranslateControl
7571 * @param {ARWallTranslateControlOption} [options={}] Options
7572 */
7573 function ARWallTranslateControl(options) {
7574 if (options === void 0) {
7575 options = {};
7576 }
7577
7578 this.position = new Vector3();
7579 this.wallPosition = new Vector3();
7580 this.hitRotation = new Quaternion(); // Global Y guaranteed rotation matrix
7581
7582 this.wallRotation = new Quaternion(); // Options
7583 // Internal states
7584
7585 this._dragPlane = new Plane();
7586 this._enabled = true;
7587 this._active = false;
7588 this._initialPos = new Vector2();
7589 this._arrowIndicator = new ArrowIndicator(options.arrow);
7590 }
7591
7592 var __proto = ARWallTranslateControl.prototype;
7593 Object.defineProperty(__proto, "enabled", {
7594 /**
7595 * Whether this control is enabled or not
7596 * @readonly
7597 */
7598 get: function () {
7599 return this._enabled;
7600 },
7601 enumerable: false,
7602 configurable: true
7603 });
7604
7605 __proto.initWallTransform = function (_a) {
7606 var hitPosition = _a.hitPosition,
7607 hitRotation = _a.hitRotation,
7608 modelPosition = _a.modelPosition,
7609 wallRotation = _a.wallRotation;
7610 this.position.copy(modelPosition);
7611 this.hitRotation.copy(hitRotation);
7612 this.wallPosition.copy(hitPosition);
7613 this.wallRotation.copy(wallRotation);
7614 var wallNormal = new Vector3(0, 1, 0).applyQuaternion(wallRotation);
7615
7616 this._dragPlane.set(wallNormal, -wallNormal.dot(modelPosition));
7617 };
7618
7619 __proto.init = function (_a) {
7620 var view3d = _a.view3d;
7621 view3d.scene.add(this._arrowIndicator.object);
7622 };
7623
7624 __proto.destroy = function (_a) {
7625 var view3d = _a.view3d;
7626 view3d.scene.remove(this._arrowIndicator.object);
7627 };
7628 /**
7629 * Enable this control
7630 */
7631
7632
7633 __proto.enable = function () {
7634 this._enabled = true;
7635 };
7636 /**
7637 * Disable this control
7638 */
7639
7640
7641 __proto.disable = function () {
7642 this._enabled = false;
7643 this.deactivate();
7644 };
7645
7646 __proto.activate = function (_a, gesture) {
7647 var model = _a.model;
7648 if (!this._enabled) return;
7649 this._active = true; // Update arrows
7650
7651 var arrowIndicator = this._arrowIndicator;
7652 var modelBbox = model.initialBbox;
7653 modelBbox.min.multiply(model.scene.scale);
7654 modelBbox.max.multiply(model.scene.scale);
7655 modelBbox.translate(model.scene.position);
7656 arrowIndicator.show();
7657 arrowIndicator.updatePosition(modelBbox.getCenter(new Vector3()));
7658 arrowIndicator.updateScale(model.size / 16);
7659 var arrowPlaneRotation = model.scene.quaternion.clone();
7660 arrowIndicator.updateRotation(arrowPlaneRotation);
7661 arrowIndicator.updateOffset(new Vector3().subVectors(modelBbox.max, modelBbox.min).multiplyScalar(0.5));
7662 };
7663
7664 __proto.deactivate = function () {
7665 this._active = false;
7666
7667 this._arrowIndicator.hide();
7668 };
7669
7670 __proto.setInitialPos = function (coords) {
7671 this._initialPos.copy(coords[0]);
7672 };
7673
7674 __proto.process = function (_a, _b) {
7675 var view3d = _a.view3d,
7676 model = _a.model,
7677 frame = _a.frame,
7678 referenceSpace = _a.referenceSpace,
7679 xrCam = _a.xrCam;
7680 var hitResults = _b.hitResults;
7681 if (!hitResults || hitResults.length !== 1 || !this._active) return;
7682 var dragPlane = this._dragPlane;
7683 var modelRoot = model.scene;
7684 var modelZOffset = -model.initialBbox.min.z * modelRoot.scale.z;
7685 var camPos = new Vector3().setFromMatrixPosition(xrCam.matrixWorld);
7686 var hitResult = hitResults[0];
7687 var hitPose = hitResult.results[0] && hitResult.results[0].getPose(referenceSpace);
7688 var isWallHit = hitPose && hitPose.transform.matrix[5] < 0.25;
7689
7690 if (!hitPose || !isWallHit) {
7691 // Use previous drag plane if no hit plane is found
7692 var targetRayPose = frame.getPose(hitResult.inputSource.targetRaySpace, view3d.renderer.threeRenderer.xr.getReferenceSpace());
7693 var fingerDir = new Vector3().copy(targetRayPose.transform.position).sub(camPos).normalize();
7694 var fingerRay = new Ray(camPos, fingerDir);
7695 var intersection = fingerRay.intersectPlane(dragPlane, new Vector3());
7696
7697 if (intersection) {
7698 this.wallPosition.copy(intersection.clone().sub(dragPlane.normal.clone().multiplyScalar(modelZOffset)));
7699 this.position.copy(intersection);
7700 }
7701
7702 return;
7703 }
7704
7705 var hitMatrix = new Matrix4().fromArray(hitPose.transform.matrix);
7706 var hitOrientation = new Quaternion().copy(hitPose.transform.orientation);
7707 var hitPosition = new Vector3().setFromMatrixPosition(hitMatrix);
7708 var worldYAxis = new Vector3(0, 1, 0);
7709 /*
7710 * ^ wallU
7711 * |
7712 * ⨀---> wallV
7713 * wallNormal
7714 */
7715
7716 var wallNormal = new Vector3(0, 1, 0).applyQuaternion(hitOrientation).normalize();
7717 var wallU = new Vector3().crossVectors(worldYAxis, wallNormal);
7718 var wallV = wallNormal.clone().applyAxisAngle(wallU, -Math.PI / 2); // Reconstruct wall matrix with prev Y(normal) direction as Z axis
7719
7720 var wallMatrix = new Matrix4().makeBasis(wallU, wallV, wallNormal);
7721 var modelPosition = hitPosition.clone().add(wallNormal.clone().multiplyScalar(modelZOffset)); // Update position
7722
7723 this.position.copy(modelPosition);
7724 this.wallPosition.copy(hitPosition); // Update rotation if it differs more than 10deg
7725
7726 var prevWallNormal = new Vector3(0, 1, 0).applyQuaternion(this.hitRotation).normalize();
7727
7728 if (Math.acos(Math.abs(prevWallNormal.dot(wallNormal))) >= Math.PI / 18) {
7729 var prevWallRotation = this.wallRotation.clone();
7730 var wallRotation = new Quaternion().setFromRotationMatrix(wallMatrix);
7731 var rotationDiff = prevWallRotation.inverse().premultiply(wallRotation);
7732 modelRoot.quaternion.premultiply(rotationDiff);
7733 this.wallRotation.copy(wallRotation);
7734 this.hitRotation.copy(hitOrientation);
7735
7736 this._arrowIndicator.updateRotation(modelRoot.quaternion); // Update drag plane
7737
7738
7739 dragPlane.set(wallNormal, -modelPosition.dot(wallNormal));
7740 }
7741 };
7742
7743 __proto.update = function (_a, delta) {
7744 var model = _a.model;
7745 model.scene.position.copy(this.position);
7746
7747 this._arrowIndicator.updatePosition(this.position);
7748
7749 model.scene.updateMatrix();
7750 };
7751
7752 return ARWallTranslateControl;
7753}();
7754
7755/*
7756 * Copyright (c) 2020 NAVER Corp.
7757 * egjs projects are licensed under the MIT license
7758 */
7759/**
7760 * AR control for {@link WallARSession}.
7761 * @category Controls-AR
7762 */
7763
7764var ARWallControl =
7765/*#__PURE__*/
7766function () {
7767 /**
7768 * Create new instance of ARControl
7769 * @param {ARWallControlOption} options Options
7770 */
7771 function ARWallControl(options) {
7772 var _this = this;
7773
7774 if (options === void 0) {
7775 options = {};
7776 }
7777
7778 this._enabled = true;
7779 this._initialized = false;
7780 this._modelHit = false;
7781 this._hitTestSource = null;
7782
7783 this.onSelectStart = function (ctx) {
7784 var view3d = ctx.view3d,
7785 session = ctx.session,
7786 frame = ctx.frame,
7787 referenceSpace = ctx.referenceSpace,
7788 xrCam = ctx.xrCam;
7789 var hitTestSource = _this._hitTestSource;
7790 if (!hitTestSource || !_this._enabled) return;
7791 var deadzoneChecker = _this._deadzoneChecker;
7792 var rotateControl = _this._rotateControl;
7793 var translateControl = _this._translateControl;
7794 var scaleControl = _this._scaleControl; // Update deadzone testing gestures
7795
7796 if (rotateControl.enabled) {
7797 deadzoneChecker.addTestingGestures(GESTURE.ONE_FINGER);
7798 }
7799
7800 if (translateControl.enabled) {
7801 deadzoneChecker.addTestingGestures(GESTURE.ONE_FINGER);
7802 }
7803
7804 if (scaleControl.enabled) {
7805 deadzoneChecker.addTestingGestures(GESTURE.PINCH);
7806 }
7807
7808 var hitResults = frame.getHitTestResultsForTransientInput(hitTestSource);
7809
7810 var coords = _this._hitResultToVector(hitResults);
7811
7812 deadzoneChecker.applyScreenAspect(coords);
7813 deadzoneChecker.setFirstInput(coords);
7814
7815 if (coords.length === 1) {
7816 // Check finger is on the model
7817 var modelBbox = view3d.model.bbox;
7818 var targetRayPose = frame.getPose(session.inputSources[0].targetRaySpace, referenceSpace);
7819 var camPos = new Vector3().setFromMatrixPosition(xrCam.matrixWorld);
7820 var fingerDir = new Vector3().copy(targetRayPose.transform.position).sub(camPos).normalize();
7821 var fingerRay = new Ray(camPos, fingerDir);
7822 var intersection = fingerRay.intersectBox(modelBbox, new Vector3());
7823
7824 if (intersection) {
7825 // Touch point intersected with model
7826 _this._modelHit = true;
7827 }
7828 }
7829
7830 _this._floorIndicator.show();
7831 };
7832
7833 this.onSelectEnd = function () {
7834 _this.deactivate();
7835
7836 _this._floorIndicator.fadeout();
7837 }; // TODO: bind options
7838
7839
7840 this._rotateControl = new ARSwirlControl(__assign(__assign({}, options.rotate), {
7841 showIndicator: false
7842 }));
7843 this._translateControl = new ARWallTranslateControl(options.translate);
7844 this._scaleControl = new ARScaleControl(options.scale);
7845 this._floorIndicator = new FloorIndicator(options.floorIndicator);
7846 this._deadzoneChecker = new DeadzoneChecker(options.deadzone);
7847 }
7848
7849 var __proto = ARWallControl.prototype;
7850 Object.defineProperty(__proto, "enabled", {
7851 /**
7852 * Return whether this control is enabled or not
7853 */
7854 get: function () {
7855 return this._enabled;
7856 },
7857 enumerable: false,
7858 configurable: true
7859 });
7860 Object.defineProperty(__proto, "rotate", {
7861 /**
7862 * {@link ARSwirlControlOptions} in this control
7863 */
7864 get: function () {
7865 return this._rotateControl;
7866 },
7867 enumerable: false,
7868 configurable: true
7869 });
7870 Object.defineProperty(__proto, "translate", {
7871 /**
7872 * {@link ARTranslateControl} in this control
7873 */
7874 get: function () {
7875 return this._translateControl;
7876 },
7877 enumerable: false,
7878 configurable: true
7879 });
7880 Object.defineProperty(__proto, "scale", {
7881 /**
7882 * {@link ARScaleControl} in this control
7883 */
7884 get: function () {
7885 return this._scaleControl;
7886 },
7887 enumerable: false,
7888 configurable: true
7889 });
7890 Object.defineProperty(__proto, "controls", {
7891 get: function () {
7892 return [this._rotateControl, this._translateControl, this._scaleControl];
7893 },
7894 enumerable: false,
7895 configurable: true
7896 });
7897
7898 __proto.init = function (ctx, initialTransform) {
7899 var _this = this;
7900
7901 var session = ctx.session,
7902 view3d = ctx.view3d,
7903 size = ctx.size;
7904 this.controls.forEach(function (control) {
7905 return control.init(ctx);
7906 });
7907
7908 this._translateControl.initWallTransform(initialTransform);
7909
7910 this._deadzoneChecker.setAspect(size.height / size.width);
7911
7912 view3d.scene.add(this._floorIndicator.mesh);
7913 this._initialized = true;
7914 session.requestHitTestSourceForTransientInput({
7915 profile: INPUT_PROFILE.TOUCH
7916 }).then(function (transientHitTestSource) {
7917 _this._hitTestSource = transientHitTestSource;
7918 });
7919 };
7920 /**
7921 * Destroy this control and deactivate it
7922 * @param view3d Instance of the {@link View3D}
7923 */
7924
7925
7926 __proto.destroy = function (ctx) {
7927 if (!this._initialized) return;
7928
7929 if (this._hitTestSource) {
7930 this._hitTestSource.cancel();
7931
7932 this._hitTestSource = null;
7933 }
7934
7935 ctx.view3d.scene.remove(this._floorIndicator.mesh);
7936 this.deactivate();
7937 this.controls.forEach(function (control) {
7938 return control.destroy(ctx);
7939 });
7940 this._initialized = false;
7941 };
7942
7943 __proto.deactivate = function () {
7944 this._modelHit = false;
7945
7946 this._deadzoneChecker.cleanup();
7947
7948 this.controls.forEach(function (control) {
7949 return control.deactivate();
7950 });
7951 };
7952 /**
7953 * Enable this control
7954 */
7955
7956
7957 __proto.enable = function () {
7958 this._enabled = true;
7959 };
7960 /**
7961 * Disable this control
7962 */
7963
7964
7965 __proto.disable = function () {
7966 this._enabled = false;
7967 this.deactivate();
7968 };
7969
7970 __proto.update = function (ctx) {
7971 var view3d = ctx.view3d,
7972 session = ctx.session,
7973 frame = ctx.frame;
7974 var hitTestSource = this._hitTestSource;
7975 if (!hitTestSource || !view3d.model) return;
7976 var deadzoneChecker = this._deadzoneChecker;
7977 var inputSources = session.inputSources;
7978 var hitResults = frame.getHitTestResultsForTransientInput(hitTestSource);
7979
7980 var coords = this._hitResultToVector(hitResults);
7981
7982 var xrInputs = {
7983 coords: coords,
7984 inputSources: inputSources,
7985 hitResults: hitResults
7986 };
7987
7988 if (deadzoneChecker.inDeadzone) {
7989 this._checkDeadzone(ctx, xrInputs);
7990 } else {
7991 this._processInput(ctx, xrInputs);
7992 }
7993
7994 this._updateControls(ctx);
7995 };
7996
7997 __proto._checkDeadzone = function (ctx, _a) {
7998 var coords = _a.coords;
7999 var model = ctx.model;
8000
8001 var gesture = this._deadzoneChecker.check(coords.map(function (coord) {
8002 return coord.clone();
8003 }));
8004
8005 var rotateControl = this._rotateControl;
8006 var translateControl = this._translateControl;
8007 var scaleControl = this._scaleControl;
8008 if (gesture === GESTURE.NONE) return;
8009
8010 switch (gesture) {
8011 case GESTURE.ONE_FINGER_HORIZONTAL:
8012 case GESTURE.ONE_FINGER_VERTICAL:
8013 if (this._modelHit) {
8014 translateControl.activate(ctx, gesture);
8015 translateControl.setInitialPos(coords);
8016 } else {
8017 rotateControl.activate(ctx, gesture);
8018 rotateControl.updateAxis(new Vector3(0, 1, 0).applyQuaternion(translateControl.hitRotation));
8019 rotateControl.updateRotation(model.scene.quaternion);
8020 rotateControl.setInitialPos(coords);
8021 }
8022
8023 break;
8024
8025 case GESTURE.PINCH:
8026 scaleControl.activate(ctx, gesture);
8027 scaleControl.setInitialPos(coords);
8028 break;
8029 }
8030 };
8031
8032 __proto._processInput = function (ctx, inputs) {
8033 this.controls.forEach(function (control) {
8034 return control.process(ctx, inputs);
8035 });
8036 };
8037
8038 __proto._updateControls = function (ctx) {
8039 var view3d = ctx.view3d,
8040 model = ctx.model,
8041 delta = ctx.delta;
8042 var deltaMilisec = delta * 1000;
8043 this.controls.forEach(function (control) {
8044 return control.update(ctx, deltaMilisec);
8045 });
8046 model.scene.updateMatrix();
8047 var translateControl = this._translateControl;
8048 var floorPosition = translateControl.wallPosition;
8049 view3d.scene.update(model, {
8050 floorPosition: floorPosition,
8051 floorRotation: translateControl.hitRotation
8052 }); // Get a scaled bbox, which only has scale applied on it.
8053
8054 var scaleControl = this._scaleControl;
8055 var scaledBbox = model.initialBbox;
8056 scaledBbox.min.multiply(scaleControl.scale);
8057 scaledBbox.max.multiply(scaleControl.scale);
8058 var floorIndicator = this._floorIndicator;
8059 var boundingSphere = scaledBbox.getBoundingSphere(new Sphere());
8060 var rotX90 = new Quaternion().setFromEuler(new Euler(Math.PI / 2, 0, 0));
8061 var floorRotation = model.scene.quaternion.clone().multiply(rotX90);
8062 floorIndicator.update({
8063 delta: deltaMilisec,
8064 scale: boundingSphere.radius,
8065 position: floorPosition,
8066 rotation: floorRotation
8067 });
8068 };
8069
8070 __proto._hitResultToVector = function (hitResults) {
8071 return hitResults.map(function (input) {
8072 return new Vector2().set(input.inputSource.gamepad.axes[0], -input.inputSource.gamepad.axes[1]);
8073 });
8074 };
8075
8076 return ARWallControl;
8077}();
8078
8079/*
8080 * Copyright (c) 2020 NAVER Corp.
8081 * egjs projects are licensed under the MIT license
8082 */
8083/**
8084 * AR session which places model on the wall
8085 * @category XR
8086 * @fires WebARSession#start
8087 * @fires WebARSession#end
8088 * @fires WebARSession#canPlace
8089 * @fires WebARSession#modelPlaced
8090 */
8091
8092var WallARSession =
8093/*#__PURE__*/
8094function (_super) {
8095 __extends(WallARSession, _super);
8096 /**
8097 * Create new instance of WallARSession
8098 * @param {WallARSessionOption} [options={}] Options
8099 */
8100
8101
8102 function WallARSession(options) {
8103 if (options === void 0) {
8104 options = {};
8105 }
8106
8107 var _this = _super.call(this, options) || this;
8108
8109 _this.onStart = function (ctx) {
8110 var view3d = ctx.view3d,
8111 session = ctx.session;
8112
8113 _super.prototype.onStart.call(_this, ctx);
8114
8115 _this._control = new ARWallControl(_this._options);
8116 view3d.scene.hide();
8117
8118 _this._hitTest.init(session);
8119 };
8120
8121 _this.onEnd = function (ctx) {
8122 var view3d = ctx.view3d,
8123 session = ctx.session;
8124
8125 _super.prototype.onEnd.call(_this, ctx);
8126
8127 _this._renderContext = null;
8128 _this._modelPlaced = false;
8129 session.removeEventListener(EVENTS$1.SELECT_START, _this._onSelectStart);
8130 session.removeEventListener(EVENTS$1.SELECT_END, _this._onSelectEnd);
8131
8132 _this._hitTest.destroy();
8133
8134 _this._control.destroy(ctx);
8135
8136 _this._control = null;
8137 view3d.scene.show();
8138 };
8139
8140 _this._beforeRender = function (ctx) {
8141 _this._renderContext = ctx;
8142
8143 if (!_this._modelPlaced) {
8144 _this._initModelPosition(ctx);
8145 } else {
8146 _this._control.update(ctx);
8147 }
8148 };
8149
8150 _this._onSelectStart = function (e) {
8151 _this._control.onSelectStart(__assign(__assign({}, _this._renderContext), {
8152 frame: e.frame
8153 }));
8154 };
8155
8156 _this._onSelectEnd = function () {
8157 _this._control.onSelectEnd();
8158 };
8159
8160 _this._control = null;
8161 _this._renderContext = null;
8162 _this._modelPlaced = false;
8163 _this._hitTest = new HitTest();
8164 _this._features = merge(_this._features, _this._hitTest.features);
8165 _this._options = options;
8166 return _this;
8167 }
8168
8169 var __proto = WallARSession.prototype;
8170 Object.defineProperty(__proto, "control", {
8171 /**
8172 * {@link ARWallControl} instance of this session
8173 * @type ARWallControl | null
8174 */
8175 get: function () {
8176 return this._control;
8177 },
8178 enumerable: false,
8179 configurable: true
8180 });
8181
8182 __proto._initModelPosition = function (ctx) {
8183 var _a;
8184
8185 var view3d = ctx.view3d,
8186 frame = ctx.frame,
8187 session = ctx.session;
8188 var model = view3d.model;
8189 var hitTest = this._hitTest; // Make sure the model is loaded
8190
8191 if (!hitTest.ready || !model) return;
8192 var control = this._control;
8193 var referenceSpace = view3d.renderer.threeRenderer.xr.getReferenceSpace();
8194 var hitTestResults = hitTest.getResults(frame);
8195 if (hitTestResults.length <= 0) return;
8196 var hit = hitTestResults[0];
8197 var hitPose = hit.getPose(referenceSpace);
8198 var hitMatrix = new Matrix4().fromArray(hitPose.transform.matrix); // If transformed coord space's y axis is facing up or down, don't use it.
8199
8200 if (hitMatrix.elements[5] >= 0.25 || hitMatrix.elements[5] <= -0.25) return;
8201 var modelRoot = model.scene;
8202 var hitRotation = new Quaternion().copy(hitPose.transform.orientation);
8203 var hitPosition = new Vector3().setFromMatrixPosition(hitMatrix);
8204 var modelZOffset = -model.initialBbox.min.z * modelRoot.scale.z;
8205 var modelPosition = hitPosition.clone().setZ(hitPosition.z + modelZOffset);
8206 var worldYAxis = new Vector3(0, 1, 0);
8207 /*
8208 * ^ wallU
8209 * |
8210 * ⨀---> wallV
8211 * wallNormal
8212 */
8213
8214 var wallNormal = new Vector3(0, 1, 0).applyQuaternion(hitRotation).normalize();
8215 var wallU = new Vector3().crossVectors(worldYAxis, wallNormal);
8216 var wallV = wallNormal.clone().applyAxisAngle(wallU, -Math.PI / 2); // Reconstruct wall matrix with prev Y(normal) direction as Z axis
8217
8218 var wallMatrix = new Matrix4().makeBasis(wallU, wallV, wallNormal);
8219 var wallRotation = new Quaternion().setFromRotationMatrix(wallMatrix);
8220 var modelRotation = model.scene.quaternion.clone().premultiply(wallRotation); // Update rotation & position
8221
8222 modelRoot.quaternion.copy(modelRotation);
8223 modelRoot.position.copy(modelPosition);
8224 modelRoot.updateMatrix();
8225 view3d.scene.update(model);
8226 view3d.scene.show(); // Don't need it
8227
8228 hitTest.destroy();
8229 session.addEventListener(EVENTS$1.SELECT_START, this._onSelectStart);
8230 session.addEventListener(EVENTS$1.SELECT_END, this._onSelectEnd);
8231 (_a = this._domOverlay) === null || _a === void 0 ? void 0 : _a.hideLoading();
8232 this._modelPlaced = true; // Show scale up animation
8233
8234 var originalModelScale = model.scene.scale.clone();
8235 var scaleUpAnimation = new Animation({
8236 context: session
8237 });
8238 scaleUpAnimation.on("progress", function (evt) {
8239 var newScale = originalModelScale.clone().multiplyScalar(evt.easedProgress);
8240 model.scene.scale.copy(newScale);
8241 });
8242 scaleUpAnimation.on("finish", function () {
8243 model.scene.scale.copy(originalModelScale);
8244 control.init(ctx, {
8245 hitPosition: hitPosition,
8246 hitRotation: hitRotation,
8247 wallRotation: wallRotation,
8248 modelPosition: modelPosition
8249 });
8250 });
8251 scaleUpAnimation.start();
8252 };
8253
8254 return WallARSession;
8255}(WebARSession);
8256
8257/*
8258 * Copyright (c) 2020 NAVER Corp.
8259 * egjs projects are licensed under the MIT license
8260 */
8261var STATE$2;
8262
8263(function (STATE) {
8264 STATE[STATE["WAITING"] = 0] = "WAITING";
8265 STATE[STATE["ROTATE_HORIZONTAL"] = 1] = "ROTATE_HORIZONTAL";
8266 STATE[STATE["ROTATE_VERTICAL"] = 2] = "ROTATE_VERTICAL";
8267})(STATE$2 || (STATE$2 = {}));
8268/**
8269 * Two finger swipe control
8270 * @category Controls-AR
8271 */
8272
8273
8274var ARSwipeControl =
8275/*#__PURE__*/
8276function () {
8277 /**
8278 * Create new ARSwipeControl
8279 * @param {ARSwipeControlOption} [options={}] Options
8280 */
8281 function ARSwipeControl(_a) {
8282 var _b = (_a === void 0 ? {} : _a).scale,
8283 scale = _b === void 0 ? 1 : _b;
8284 /**
8285 * Current rotation value
8286 */
8287
8288 this.rotation = new Quaternion(); // Internal States
8289
8290 this._state = STATE$2.WAITING;
8291 this._enabled = true;
8292 this._active = false;
8293 this._prevPos = new Vector2();
8294 this._fromQuat = new Quaternion();
8295 this._toQuat = new Quaternion();
8296 this._horizontalAxis = new Vector3();
8297 this._verticalAxis = new Vector3();
8298 this._motion = new Motion({
8299 range: INFINITE_RANGE
8300 });
8301 this._rotationIndicator = new RotationIndicator();
8302 this._userScale = scale;
8303 }
8304
8305 var __proto = ARSwipeControl.prototype;
8306 Object.defineProperty(__proto, "enabled", {
8307 /**
8308 * Whether this control is enabled or not.
8309 * @readonly
8310 */
8311 get: function () {
8312 return this._enabled;
8313 },
8314 enumerable: false,
8315 configurable: true
8316 });
8317 Object.defineProperty(__proto, "scale", {
8318 /**
8319 * Scale(speed) factor of this control.
8320 */
8321 get: function () {
8322 return this._userScale;
8323 },
8324 set: function (val) {
8325 this._userScale = val;
8326 },
8327 enumerable: false,
8328 configurable: true
8329 });
8330 Object.defineProperty(__proto, "horizontalAxis", {
8331 get: function () {
8332 return this._horizontalAxis;
8333 },
8334 enumerable: false,
8335 configurable: true
8336 });
8337 Object.defineProperty(__proto, "verticalAxis", {
8338 get: function () {
8339 return this._verticalAxis;
8340 },
8341 enumerable: false,
8342 configurable: true
8343 });
8344
8345 __proto.init = function (_a) {
8346 var view3d = _a.view3d;
8347 var initialRotation = view3d.model.scene.quaternion;
8348 this.updateRotation(initialRotation);
8349 view3d.scene.add(this._rotationIndicator.object);
8350 };
8351
8352 __proto.destroy = function (_a) {
8353 var view3d = _a.view3d;
8354 view3d.scene.remove(this._rotationIndicator.object);
8355 };
8356
8357 __proto.updateRotation = function (rotation) {
8358 this.rotation.copy(rotation);
8359
8360 this._fromQuat.copy(rotation);
8361
8362 this._toQuat.copy(rotation);
8363 };
8364 /**
8365 * Enable this control
8366 */
8367
8368
8369 __proto.enable = function () {
8370 this._enabled = true;
8371 };
8372 /**
8373 * Disable this control
8374 */
8375
8376
8377 __proto.disable = function () {
8378 this._enabled = false;
8379 };
8380
8381 __proto.updateAxis = function (horizontal, vertical) {
8382 this._horizontalAxis.copy(horizontal);
8383
8384 this._verticalAxis.copy(vertical);
8385 };
8386
8387 __proto.activate = function (_a, gesture) {
8388 var view3d = _a.view3d;
8389 if (!this._enabled) return;
8390 var model = view3d.model;
8391 var rotationIndicator = this._rotationIndicator;
8392 this._active = true;
8393 rotationIndicator.show();
8394 rotationIndicator.updatePosition(model.bbox.getCenter(new Vector3()));
8395 rotationIndicator.updateScale(model.size / 2);
8396
8397 if (gesture === GESTURE.TWO_FINGER_HORIZONTAL) {
8398 rotationIndicator.updateRotation(model.scene.quaternion.clone().multiply(new Quaternion().setFromEuler(new Euler(-Math.PI / 2, 0, 0))));
8399 this._state = STATE$2.ROTATE_HORIZONTAL;
8400 } else if (gesture === GESTURE.TWO_FINGER_VERTICAL) {
8401 rotationIndicator.updateRotation(model.scene.quaternion.clone().multiply(new Quaternion().setFromEuler(new Euler(0, Math.PI / 2, 0))));
8402 this._state = STATE$2.ROTATE_VERTICAL;
8403 }
8404 };
8405
8406 __proto.deactivate = function () {
8407 this._active = false;
8408
8409 this._rotationIndicator.hide();
8410
8411 this._state = STATE$2.WAITING;
8412 };
8413
8414 __proto.setInitialPos = function (coords) {
8415 if (coords.length < 2) return;
8416
8417 this._prevPos.set((coords[0].x + coords[1].x) / 2, (coords[0].y + coords[1].y) / 2);
8418 };
8419
8420 __proto.process = function (ctx, _a) {
8421 var coords = _a.coords;
8422 if (!this._active || coords.length !== 2) return;
8423 var state = this._state;
8424 var prevPos = this._prevPos;
8425 var motion = this._motion;
8426 var scale = this._userScale;
8427 var middlePos = new Vector2((coords[0].x + coords[1].x) / 2, (coords[0].y + coords[1].y) / 2);
8428 var posDiff = new Vector2().subVectors(prevPos, middlePos);
8429 var rotationAxis = state === STATE$2.ROTATE_HORIZONTAL ? this._horizontalAxis : this._verticalAxis;
8430 var rotationAngle = state === STATE$2.ROTATE_HORIZONTAL ? posDiff.x * scale : -posDiff.y * scale;
8431 var rotation = new Quaternion().setFromAxisAngle(rotationAxis, rotationAngle);
8432
8433 var interpolated = this._getInterpolatedQuaternion();
8434
8435 this._fromQuat.copy(interpolated);
8436
8437 this._toQuat.premultiply(rotation);
8438
8439 motion.reset(0);
8440 motion.setEndDelta(1);
8441 prevPos.copy(middlePos);
8442 };
8443
8444 __proto.update = function (_a, deltaTime) {
8445 var model = _a.model;
8446 var motion = this._motion;
8447 motion.update(deltaTime);
8448
8449 var interpolated = this._getInterpolatedQuaternion();
8450
8451 this.rotation.copy(interpolated);
8452 model.scene.quaternion.copy(this.rotation);
8453 };
8454
8455 __proto._getInterpolatedQuaternion = function () {
8456 var motion = this._motion;
8457 var toEuler = this._toQuat;
8458 var fromEuler = this._fromQuat;
8459 var progress = motion.val;
8460 return new Quaternion().copy(fromEuler).slerp(toEuler, progress);
8461 };
8462
8463 return ARSwipeControl;
8464}();
8465
8466/*
8467 * Copyright (c) 2020 NAVER Corp.
8468 * egjs projects are licensed under the MIT license
8469 */
8470/**
8471 * Model's yaw(local y-axis rotation) controller which works on AR(WebXR) mode.
8472 * @category Controls-AR
8473 */
8474
8475var ARHoverRotateControl =
8476/*#__PURE__*/
8477function () {
8478 /**
8479 * Create new instance of ARRotateControl
8480 * @param {ARHoverRotateControlOption} options Options
8481 */
8482 function ARHoverRotateControl(options) {
8483 if (options === void 0) {
8484 options = {};
8485 }
8486 /**
8487 * Current rotation value
8488 */
8489
8490
8491 this.rotation = new Quaternion();
8492 this._zRotationControl = new ARSwirlControl(options.swirl);
8493 this._xyRotationControl = new ARSwipeControl(options.swipe);
8494 this._activatedControl = null;
8495 }
8496
8497 var __proto = ARHoverRotateControl.prototype;
8498 Object.defineProperty(__proto, "enabled", {
8499 /**
8500 * Whether this control is enabled or not.
8501 * This returns true when either one finger control or two finger control is enabled.
8502 * @readonly
8503 */
8504 get: function () {
8505 return this._zRotationControl.enabled || this._xyRotationControl.enabled;
8506 },
8507 enumerable: false,
8508 configurable: true
8509 });
8510 Object.defineProperty(__proto, "swirl", {
8511 /**
8512 * {@link ARSwirlControl} of this control.
8513 */
8514 get: function () {
8515 return this._zRotationControl;
8516 },
8517 enumerable: false,
8518 configurable: true
8519 });
8520 Object.defineProperty(__proto, "swipe", {
8521 /**
8522 * {@link ARSwipeControl} of this control.
8523 */
8524 get: function () {
8525 return this._xyRotationControl;
8526 },
8527 enumerable: false,
8528 configurable: true
8529 });
8530
8531 __proto.init = function (ctx) {
8532 var initialRotation = ctx.view3d.model.scene.quaternion;
8533 this.rotation.copy(initialRotation);
8534
8535 this._zRotationControl.init(ctx);
8536
8537 this._xyRotationControl.init(ctx);
8538 };
8539
8540 __proto.destroy = function (ctx) {
8541 this._zRotationControl.destroy(ctx);
8542
8543 this._xyRotationControl.destroy(ctx);
8544 };
8545 /**
8546 * Enable this control
8547 */
8548
8549
8550 __proto.enable = function () {
8551 this._zRotationControl.enable();
8552
8553 this._xyRotationControl.enable();
8554 };
8555 /**
8556 * Disable this control
8557 */
8558
8559
8560 __proto.disable = function () {
8561 this._zRotationControl.disable();
8562
8563 this._xyRotationControl.disable();
8564 };
8565
8566 __proto.activate = function (ctx, gesture) {
8567 var zRotationControl = this._zRotationControl;
8568 var xyRotationControl = this._xyRotationControl;
8569
8570 if (gesture & GESTURE.ONE_FINGER) {
8571 zRotationControl.activate(ctx, gesture);
8572 zRotationControl.updateRotation(this.rotation);
8573 this._activatedControl = zRotationControl;
8574 } else if (gesture & GESTURE.TWO_FINGER) {
8575 xyRotationControl.activate(ctx, gesture);
8576 xyRotationControl.updateRotation(this.rotation);
8577 this._activatedControl = xyRotationControl;
8578 }
8579 };
8580
8581 __proto.deactivate = function () {
8582 this._zRotationControl.deactivate();
8583
8584 this._xyRotationControl.deactivate();
8585 };
8586
8587 __proto.process = function (ctx, inputs) {
8588 this._zRotationControl.process(ctx, inputs);
8589
8590 this._xyRotationControl.process(ctx, inputs);
8591 };
8592
8593 __proto.setInitialPos = function (coords) {
8594 this._zRotationControl.setInitialPos(coords);
8595
8596 this._xyRotationControl.setInitialPos(coords);
8597 };
8598
8599 __proto.update = function (ctx, deltaTime) {
8600 if (this._activatedControl) {
8601 this._activatedControl.update(ctx, deltaTime);
8602
8603 this.rotation.copy(this._activatedControl.rotation);
8604 }
8605 };
8606
8607 __proto.updateRotateAxis = function (_a) {
8608 var view3d = _a.view3d,
8609 xrCam = _a.xrCam;
8610 var model = view3d.model;
8611 var zRotateAxis = new Vector3();
8612 var horizontalRotateAxis = new Vector3();
8613 var verticalRotateAxis = new Vector3();
8614 var cameraRotation = new Quaternion().setFromRotationMatrix(xrCam.matrixWorld);
8615 var cameraBasis = [new Vector3(1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1)].map(function (axis) {
8616 return axis.applyQuaternion(cameraRotation).normalize();
8617 });
8618 var modelBasis = [new Vector3(1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1)].map(function (axis) {
8619 return axis.applyQuaternion(model.scene.quaternion);
8620 }); // Always use z-rotation
8621
8622 zRotateAxis.copy(modelBasis[2]); // Use more appropriate one between x/y axis
8623
8624 horizontalRotateAxis.copy(modelBasis[1]);
8625 verticalRotateAxis.copy(modelBasis[0]); // If it's facing other direction, negate it to face correct direction
8626
8627 if (zRotateAxis.dot(cameraBasis[2]) < 0) {
8628 zRotateAxis.negate();
8629 }
8630
8631 if (horizontalRotateAxis.dot(cameraBasis[1]) > 0) {
8632 horizontalRotateAxis.negate();
8633 }
8634
8635 if (verticalRotateAxis.dot(cameraBasis[0]) > 0) {
8636 verticalRotateAxis.negate();
8637 }
8638
8639 this._zRotationControl.updateAxis(zRotateAxis);
8640
8641 this._xyRotationControl.updateAxis(horizontalRotateAxis, verticalRotateAxis);
8642 };
8643
8644 return ARHoverRotateControl;
8645}();
8646
8647/*
8648 * Copyright (c) 2020 NAVER Corp.
8649 * egjs projects are licensed under the MIT license
8650 */
8651/**
8652 * Model's translation(position) control for {@link ARHoverControl}
8653 * @category Controls-AR
8654 */
8655
8656var ARHoverTranslateControl =
8657/*#__PURE__*/
8658function () {
8659 /**
8660 * Create new instance of ARTranslateControl
8661 * @param {ARHoverTranslateControlOption} [options={}] Options
8662 */
8663 function ARHoverTranslateControl(options) {
8664 if (options === void 0) {
8665 options = {};
8666 } // Internal states
8667
8668
8669 this._position = new Vector3();
8670 this._dragPlane = new Plane();
8671 this._enabled = true;
8672 this._active = false;
8673 this._initialPos = new Vector2();
8674 this._arrowIndicator = new ArrowIndicator(options.arrow);
8675 }
8676
8677 var __proto = ARHoverTranslateControl.prototype;
8678 Object.defineProperty(__proto, "enabled", {
8679 get: function () {
8680 return this._enabled;
8681 },
8682 enumerable: false,
8683 configurable: true
8684 });
8685 Object.defineProperty(__proto, "position", {
8686 get: function () {
8687 return this._position.clone();
8688 },
8689 enumerable: false,
8690 configurable: true
8691 });
8692
8693 __proto.init = function (_a) {
8694 var view3d = _a.view3d;
8695
8696 this._position.copy(view3d.model.scene.position);
8697
8698 view3d.scene.add(this._arrowIndicator.object);
8699 };
8700
8701 __proto.destroy = function (_a) {
8702 var view3d = _a.view3d;
8703 view3d.scene.remove(this._arrowIndicator.object);
8704 };
8705 /**
8706 * Enable this control
8707 */
8708
8709
8710 __proto.enable = function () {
8711 this._enabled = true;
8712 };
8713 /**
8714 * Disable this control
8715 */
8716
8717
8718 __proto.disable = function () {
8719 this._enabled = false;
8720 this.deactivate();
8721 };
8722
8723 __proto.activate = function (_a, gesture) {
8724 var model = _a.model,
8725 xrCam = _a.xrCam;
8726 if (!this._enabled) return;
8727 var modelPos = model.scene.position;
8728 var camPos = new Vector3().setFromMatrixPosition(xrCam.matrixWorld);
8729 var modelBasis = [new Vector3(), new Vector3(), new Vector3()];
8730 model.scene.matrixWorld.extractBasis(modelBasis[0], modelBasis[1], modelBasis[2]);
8731 modelBasis.forEach(function (axes) {
8732 return axes.normalize();
8733 });
8734 var camToModelDir = new Vector3().subVectors(modelPos, camPos).clone().normalize();
8735 var primaryAxisIdx = getPrimaryAxisIndex(modelBasis, camToModelDir);
8736 var primaryAxis = modelBasis[primaryAxisIdx]; // If axes is facing the opposite of camera, negate it
8737
8738 if (primaryAxis.dot(camToModelDir) < 0) {
8739 primaryAxis.negate();
8740 }
8741
8742 var originToDragPlane = new Plane(primaryAxis, 0).distanceToPoint(modelPos);
8743
8744 this._dragPlane.set(primaryAxis, -originToDragPlane);
8745
8746 this._active = true; // Update arrows
8747
8748 var arrowIndicator = this._arrowIndicator;
8749 var modelBbox = model.initialBbox;
8750 modelBbox.min.multiply(model.scene.scale);
8751 modelBbox.max.multiply(model.scene.scale);
8752 modelBbox.translate(modelPos);
8753 arrowIndicator.show();
8754 arrowIndicator.updatePosition(modelBbox.getCenter(new Vector3()));
8755 arrowIndicator.updateScale(model.size / 16);
8756 var arrowPlaneRotation = model.scene.quaternion.clone();
8757
8758 if (primaryAxisIdx === 0) {
8759 arrowPlaneRotation.multiply(new Quaternion().setFromEuler(new Euler(0, Math.PI / 2, 0)));
8760 } else if (primaryAxisIdx === 1) {
8761 arrowPlaneRotation.multiply(new Quaternion().setFromEuler(new Euler(Math.PI / 2, 0, 0)));
8762 }
8763
8764 arrowIndicator.updateRotation(arrowPlaneRotation);
8765 arrowIndicator.updateOffset(new Vector3().subVectors(modelBbox.max, modelBbox.min).multiplyScalar(0.5));
8766 };
8767
8768 __proto.deactivate = function () {
8769 this._active = false;
8770
8771 this._arrowIndicator.hide();
8772 };
8773
8774 __proto.setInitialPos = function (coords) {
8775 this._initialPos.copy(coords[0]);
8776 };
8777
8778 __proto.process = function (_a, _b) {
8779 var view3d = _a.view3d,
8780 frame = _a.frame,
8781 referenceSpace = _a.referenceSpace,
8782 xrCam = _a.xrCam;
8783 var inputSources = _b.inputSources;
8784 if (inputSources.length !== 1 || !this._active) return;
8785 var inputSource = inputSources[0];
8786 var dragPlane = this._dragPlane;
8787 var targetRayPose = frame.getPose(inputSource.targetRaySpace, referenceSpace);
8788 var camPos = new Vector3().setFromMatrixPosition(xrCam.matrixWorld);
8789 var fingerDir = new Vector3().copy(targetRayPose.transform.position).sub(camPos).normalize();
8790 var fingerRay = new Ray(camPos, fingerDir);
8791 var intersection = fingerRay.intersectPlane(dragPlane, new Vector3());
8792
8793 if (intersection) {
8794 this._position.copy(intersection); // Update arrow position. As position is not a center of model, we should apply offset from it
8795
8796
8797 var model = view3d.model;
8798 var centerYOffset = model.initialBbox.getCenter(new Vector3()).multiply(model.scene.scale).y;
8799 var modelLocalYDir = new Vector3().applyQuaternion(model.scene.quaternion);
8800 var newCenter = intersection.add(modelLocalYDir.multiplyScalar(centerYOffset));
8801
8802 this._arrowIndicator.updatePosition(newCenter);
8803 }
8804 };
8805
8806 __proto.update = function (_a, delta) {
8807 var model = _a.model;
8808 model.scene.position.copy(this._position);
8809 };
8810
8811 return ARHoverTranslateControl;
8812}();
8813
8814/*
8815 * Copyright (c) 2020 NAVER Corp.
8816 * egjs projects are licensed under the MIT license
8817 */
8818/**
8819 * AR control for {@link HoverARSession}
8820 * @category Controls-AR
8821 */
8822
8823var ARHoverControl =
8824/*#__PURE__*/
8825function () {
8826 /**
8827 * Create new instance of ARHoverControl
8828 * @param {ARHoverControlOption} options Options
8829 */
8830 function ARHoverControl(options) {
8831 var _this = this;
8832
8833 if (options === void 0) {
8834 options = {};
8835 }
8836
8837 this._enabled = true;
8838 this._initialized = false;
8839 this._modelHit = false;
8840
8841 this.onSelectStart = function (ctx) {
8842 var view3d = ctx.view3d,
8843 session = ctx.session,
8844 frame = ctx.frame,
8845 referenceSpace = ctx.referenceSpace,
8846 xrCam = ctx.xrCam;
8847 if (!_this._enabled) return;
8848 var deadzoneChecker = _this._deadzoneChecker;
8849 var rotateControl = _this._rotateControl;
8850 var translateControl = _this._translateControl;
8851 var scaleControl = _this._scaleControl; // Update rotation axis
8852
8853 if (rotateControl.enabled) {
8854 rotateControl.updateRotateAxis(ctx);
8855 } // Update deadzone testing gestures
8856
8857
8858 if (rotateControl.swirl.enabled) {
8859 deadzoneChecker.addTestingGestures(GESTURE.ONE_FINGER);
8860 }
8861
8862 if (rotateControl.swipe.enabled) {
8863 deadzoneChecker.addTestingGestures(GESTURE.TWO_FINGER);
8864 }
8865
8866 if (translateControl.enabled) {
8867 deadzoneChecker.addTestingGestures(GESTURE.ONE_FINGER);
8868 }
8869
8870 if (scaleControl.enabled) {
8871 deadzoneChecker.addTestingGestures(GESTURE.PINCH);
8872 }
8873
8874 var coords = _this._inputSourceToVector(session.inputSources);
8875
8876 deadzoneChecker.applyScreenAspect(coords);
8877 deadzoneChecker.setFirstInput(coords);
8878
8879 if (coords.length === 1) {
8880 // Check finger is on the model
8881 var modelBbox = view3d.model.bbox;
8882 var targetRayPose = frame.getPose(session.inputSources[0].targetRaySpace, referenceSpace);
8883 var camPos = new Vector3().setFromMatrixPosition(xrCam.matrixWorld);
8884 var fingerDir = new Vector3().copy(targetRayPose.transform.position).sub(camPos).normalize();
8885 var fingerRay = new Ray(camPos, fingerDir);
8886 var intersection = fingerRay.intersectBox(modelBbox, new Vector3());
8887
8888 if (intersection) {
8889 // Touch point intersected with model
8890 _this._modelHit = true;
8891 }
8892 }
8893 };
8894
8895 this.onSelectEnd = function () {
8896 _this.deactivate();
8897 };
8898
8899 this._rotateControl = new ARHoverRotateControl(options.rotate);
8900 this._translateControl = new ARHoverTranslateControl(options.translate);
8901 this._scaleControl = new ARScaleControl(options.scale);
8902 this._deadzoneChecker = new DeadzoneChecker();
8903 }
8904
8905 var __proto = ARHoverControl.prototype;
8906 Object.defineProperty(__proto, "enabled", {
8907 /**
8908 * Return whether this control is enabled or not
8909 */
8910 get: function () {
8911 return this._enabled;
8912 },
8913 enumerable: false,
8914 configurable: true
8915 });
8916 Object.defineProperty(__proto, "rotate", {
8917 /**
8918 * {@link ARHoverRotateControlOption} in this control
8919 */
8920 get: function () {
8921 return this._rotateControl;
8922 },
8923 enumerable: false,
8924 configurable: true
8925 });
8926 Object.defineProperty(__proto, "translate", {
8927 /**
8928 * {@link ARHoverTranslateControlOption} in this control
8929 */
8930 get: function () {
8931 return this._translateControl;
8932 },
8933 enumerable: false,
8934 configurable: true
8935 });
8936 Object.defineProperty(__proto, "scale", {
8937 /**
8938 * {@link ARScaleControl} in this control
8939 */
8940 get: function () {
8941 return this._scaleControl;
8942 },
8943 enumerable: false,
8944 configurable: true
8945 });
8946 Object.defineProperty(__proto, "controls", {
8947 get: function () {
8948 return [this._rotateControl, this._translateControl, this._scaleControl];
8949 },
8950 enumerable: false,
8951 configurable: true
8952 });
8953
8954 __proto.init = function (ctx) {
8955 var size = ctx.size;
8956 this.controls.forEach(function (control) {
8957 return control.init(ctx);
8958 });
8959
8960 this._deadzoneChecker.setAspect(size.height / size.width);
8961
8962 this._initialized = true;
8963 };
8964 /**
8965 * Destroy this control and deactivate it
8966 * @param view3d Instance of the {@link View3D}
8967 */
8968
8969
8970 __proto.destroy = function (ctx) {
8971 if (!this._initialized) return;
8972 this.deactivate();
8973 this.controls.forEach(function (control) {
8974 return control.destroy(ctx);
8975 });
8976 this._initialized = false;
8977 };
8978
8979 __proto.deactivate = function () {
8980 this._modelHit = false;
8981
8982 this._deadzoneChecker.cleanup();
8983
8984 this.controls.forEach(function (control) {
8985 return control.deactivate();
8986 });
8987 };
8988 /**
8989 * Enable this control
8990 */
8991
8992
8993 __proto.enable = function () {
8994 this._enabled = true;
8995 };
8996 /**
8997 * Disable this control
8998 */
8999
9000
9001 __proto.disable = function () {
9002 this._enabled = false;
9003 this.deactivate();
9004 };
9005
9006 __proto.update = function (ctx) {
9007 var session = ctx.session;
9008 if (!this._initialized) return;
9009 var deadzoneChecker = this._deadzoneChecker;
9010 var inputSources = session.inputSources;
9011
9012 if (deadzoneChecker.inDeadzone) {
9013 this._checkDeadzone(ctx, inputSources);
9014 } else {
9015 this._processInput(ctx, inputSources);
9016 }
9017
9018 this._updateControls(ctx);
9019 };
9020
9021 __proto._checkDeadzone = function (ctx, inputSources) {
9022 var coords = this._inputSourceToVector(inputSources);
9023
9024 var gesture = this._deadzoneChecker.check(coords.map(function (coord) {
9025 return coord.clone();
9026 }));
9027
9028 var rotateControl = this._rotateControl;
9029 var translateControl = this._translateControl;
9030 var scaleControl = this._scaleControl;
9031 if (gesture === GESTURE.NONE) return;
9032
9033 switch (gesture) {
9034 case GESTURE.ONE_FINGER_HORIZONTAL:
9035 case GESTURE.ONE_FINGER_VERTICAL:
9036 if (this._modelHit) {
9037 translateControl.activate(ctx, gesture);
9038 translateControl.setInitialPos(coords);
9039 } else {
9040 rotateControl.activate(ctx, gesture);
9041 rotateControl.setInitialPos(coords);
9042 }
9043
9044 break;
9045
9046 case GESTURE.TWO_FINGER_HORIZONTAL:
9047 case GESTURE.TWO_FINGER_VERTICAL:
9048 rotateControl.activate(ctx, gesture);
9049 rotateControl.setInitialPos(coords);
9050 break;
9051
9052 case GESTURE.PINCH:
9053 scaleControl.activate(ctx, gesture);
9054 scaleControl.setInitialPos(coords);
9055 break;
9056 }
9057 };
9058
9059 __proto._processInput = function (ctx, inputSources) {
9060 var coords = this._inputSourceToVector(inputSources);
9061
9062 this.controls.forEach(function (control) {
9063 return control.process(ctx, {
9064 coords: coords,
9065 inputSources: inputSources
9066 });
9067 });
9068 };
9069
9070 __proto._updateControls = function (ctx) {
9071 var view3d = ctx.view3d,
9072 model = ctx.model,
9073 delta = ctx.delta;
9074 var deltaMilisec = delta * 1000;
9075 this.controls.forEach(function (control) {
9076 return control.update(ctx, deltaMilisec);
9077 });
9078 model.scene.updateMatrix();
9079 view3d.scene.update(model);
9080 };
9081
9082 __proto._inputSourceToVector = function (inputSources) {
9083 return Array.from(inputSources).map(function (inputSource) {
9084 var axes = inputSource.gamepad.axes;
9085 return new Vector2(axes[0], -axes[1]);
9086 });
9087 };
9088
9089 return ARHoverControl;
9090}();
9091
9092/*
9093 * Copyright (c) 2020 NAVER Corp.
9094 * egjs projects are licensed under the MIT license
9095 */
9096/**
9097 * WebXR based AR session which puts model at the space front of camera.
9098 * @category XR
9099 * @fires WebARSession#start
9100 * @fires WebARSession#end
9101 * @fires WebARSession#canPlace
9102 * @fires WebARSession#modelPlaced
9103 */
9104
9105var HoverARSession =
9106/*#__PURE__*/
9107function (_super) {
9108 __extends(HoverARSession, _super);
9109 /**
9110 * Create new instance of HoverARSession
9111 * @param {HoverARSessionOption} options Options
9112 */
9113
9114
9115 function HoverARSession(options) {
9116 if (options === void 0) {
9117 options = {};
9118 }
9119
9120 var _this = _super.call(this, options) || this;
9121
9122 _this.onStart = function (ctx) {
9123 var view3d = ctx.view3d;
9124
9125 _super.prototype.onStart.call(_this, ctx);
9126
9127 _this._control = new ARHoverControl(_this._options);
9128 view3d.scene.hide();
9129 };
9130
9131 _this.onEnd = function (ctx) {
9132 var view3d = ctx.view3d,
9133 session = ctx.session;
9134
9135 _super.prototype.onEnd.call(_this, ctx);
9136
9137 _this._renderContext = null;
9138 _this._modelPlaced = false;
9139 session.removeEventListener(EVENTS$1.SELECT_START, _this._onSelectStart);
9140 session.removeEventListener(EVENTS$1.SELECT_END, _this._onSelectEnd);
9141
9142 _this._control.destroy(ctx);
9143
9144 _this._control = null;
9145 view3d.scene.show();
9146 };
9147
9148 _this._beforeRender = function (ctx) {
9149 _this._renderContext = ctx;
9150
9151 if (!_this._modelPlaced) {
9152 _this._initModelPosition(ctx);
9153 } else {
9154 _this._control.update(ctx);
9155 }
9156 };
9157
9158 _this._onSelectStart = function (e) {
9159 _this._control.onSelectStart(__assign(__assign({}, _this._renderContext), {
9160 frame: e.frame
9161 }));
9162 };
9163
9164 _this._onSelectEnd = function () {
9165 _this._control.onSelectEnd();
9166 };
9167
9168 _this._control = null;
9169 _this._renderContext = null;
9170 _this._modelPlaced = false;
9171 _this._options = options;
9172 return _this;
9173 }
9174
9175 var __proto = HoverARSession.prototype;
9176 Object.defineProperty(__proto, "control", {
9177 /**
9178 * {@link ARControl} instance of this session
9179 * @type ARHoverControl | null
9180 */
9181 get: function () {
9182 return this._control;
9183 },
9184 enumerable: false,
9185 configurable: true
9186 });
9187 /**
9188 * Place model on the current position
9189 */
9190
9191 __proto.placeModel = function () {
9192 var ctx = this._renderContext; // Not ready to place model yet
9193
9194 if (!ctx || !ctx.view3d.scene.visible || this._modelPlaced) return;
9195 var session = ctx.session,
9196 view3d = ctx.view3d;
9197 var modelRoot = view3d.model.scene;
9198 var control = this._control;
9199 session.addEventListener(EVENTS$1.SELECT_START, this._onSelectStart);
9200 session.addEventListener(EVENTS$1.SELECT_END, this._onSelectEnd);
9201 this._modelPlaced = true;
9202 this.emit("modelPlaced"); // Show scale up animation
9203
9204 var originalModelScale = modelRoot.scale.clone();
9205 var scaleUpAnimation = new Animation({
9206 context: session
9207 });
9208 scaleUpAnimation.on("progress", function (evt) {
9209 var newScale = originalModelScale.clone().multiplyScalar(evt.easedProgress);
9210 modelRoot.scale.copy(newScale);
9211 });
9212 scaleUpAnimation.on("finish", function () {
9213 modelRoot.scale.copy(originalModelScale);
9214 control.init(ctx);
9215 });
9216 scaleUpAnimation.start();
9217 };
9218
9219 __proto._initModelPosition = function (ctx) {
9220 var view3d = ctx.view3d,
9221 xrCam = ctx.xrCam;
9222 var model = view3d.model; // Make sure the model exist
9223
9224 if (!model) return;
9225 var modelRoot = model.scene;
9226 var camPos = new Vector3().setFromMatrixPosition(xrCam.matrixWorld);
9227 var camQuat = new Quaternion().setFromRotationMatrix(xrCam.matrixWorld);
9228 var viewDir = new Vector3(0, 0, -1).applyQuaternion(camQuat);
9229 var modelBbox = model.bbox;
9230 var bboxDiff = new Vector3().subVectors(modelBbox.max, modelBbox.min);
9231 var maxComponent = Math.max(bboxDiff.x, bboxDiff.y, bboxDiff.z); // Reset rotation & update position
9232
9233 modelRoot.position.copy(camPos);
9234 modelRoot.position.add(viewDir.multiplyScalar(clamp(maxComponent, 0.5, 3))); // Place at 1m from camera
9235
9236 modelRoot.lookAt(camPos.setY(modelRoot.position.y));
9237 modelRoot.updateMatrix();
9238 view3d.scene.update(model);
9239
9240 if (!view3d.scene.visible) {
9241 view3d.scene.show();
9242 this.emit("canPlace");
9243 }
9244 };
9245
9246 return HoverARSession;
9247}(WebARSession);
9248
9249/*
9250 * Copyright (c) 2020 NAVER Corp.
9251 * egjs projects are licensed under the MIT license
9252 */
9253// Browser related constants
9254var IS_IOS = /iPad|iPhone|iPod/.test(navigator.userAgent) || navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1;
9255var IS_ANDROID = /android/i.test(navigator.userAgent);
9256var IS_SAFARI = /safari/i.test(navigator.userAgent);
9257
9258/*
9259 * Copyright (c) 2020 NAVER Corp.
9260 * egjs projects are licensed under the MIT license
9261 */
9262/**
9263 * AR session using Google's scene-viewer
9264 * @category XR
9265 * @see https://developers.google.com/ar/develop/java/scene-viewer
9266 */
9267
9268var SceneViewerSession =
9269/*#__PURE__*/
9270function () {
9271 /**
9272 * Create new instance of SceneViewerSession
9273 * @see https://developers.google.com/ar/develop/java/scene-viewer
9274 * @param params Session params
9275 * @param {string} [params.file] This URL specifies the glTF or glb file that should be loaded into Scene Viewer. This should be URL-escaped.
9276 * @param {string} [params.browser_fallback_url] This is a Google Chrome feature supported only for web-based implementations. When the Google app com.google.android.googlequicksearchbox is not present on the device, this is the URL that Google Chrome navigates to.
9277 * @param {string} [params.mode="ar_only"] See {@link https://developers.google.com/ar/develop/java/scene-viewer} for available modes.
9278 * @param {string} [params.title] A name for the model. If present, it will be displayed in the UI. The name will be truncated with ellipses after 60 characters.
9279 * @param {string} [params.link] A URL for an external webpage. If present, a button will be surfaced in the UI that intents to this URL when clicked.
9280 * @param {string} [params.sound] A URL to a looping audio track that is synchronized with the first animation embedded in a glTF file. It should be provided alongside a glTF with an animation of matching length. If present, the sound is looped after the model is loaded. This should be URL-escaped.
9281 * @param {string} [params.resizable=true] When set to false, users will not be able to scale the model in the AR experience. Scaling works normally in the 3D experience.
9282 */
9283 function SceneViewerSession(params) {
9284 this.params = params;
9285 this.isWebXRSession = false;
9286
9287 if (!this.params.mode) {
9288 // Default mode is "ar_only", which should use com.google.ar.core package
9289 this.params.mode = "ar_only";
9290 }
9291 }
9292 /**
9293 * Return the availability of SceneViewerSession.
9294 * Scene-viewer is available on all android devices with google ARCore installed.
9295 * @returns {Promise} A Promise that resolves availability of this session(boolean).
9296 */
9297
9298
9299 var __proto = SceneViewerSession.prototype;
9300
9301 __proto.isAvailable = function () {
9302 return Promise.resolve(IS_ANDROID);
9303 };
9304 /**
9305 * Enter Scene-viewer AR session
9306 */
9307
9308
9309 __proto.enter = function () {
9310 var params = Object.assign({}, this.params);
9311 var fallback = params.browser_fallback_url;
9312 delete params.browser_fallback_url;
9313 var resizable = params.resizable;
9314 delete params.resizable;
9315
9316 if (resizable === true) {
9317 params.resizable = "true";
9318 } else if (resizable === false) {
9319 params.resizable = "false";
9320 } else if (resizable) {
9321 params.resizable = resizable;
9322 }
9323
9324 var queryString = Object.keys(params).filter(function (key) {
9325 return params[key] != null;
9326 }).map(function (key) {
9327 return key + "=" + params[key];
9328 }).join("&");
9329 var intentURL = params.mode === "ar_only" ? SCENE_VIEWER.INTENT_AR_CORE(queryString, fallback) : SCENE_VIEWER.INTENT_SEARCHBOX(queryString, fallback || SCENE_VIEWER.FALLBACK_DEFAULT(queryString));
9330 var anchor = document.createElement("a");
9331 anchor.href = intentURL;
9332 anchor.click();
9333 return Promise.resolve();
9334 };
9335
9336 __proto.exit = function () {// DO NOTHING
9337 };
9338
9339 return SceneViewerSession;
9340}();
9341
9342/*
9343 * Copyright (c) 2020 NAVER Corp.
9344 * egjs projects are licensed under the MIT license
9345 */
9346/**
9347 * AR Session using Apple AR Quick Look Viewer
9348 * @category XR
9349 * @see https://developer.apple.com/augmented-reality/quick-look/
9350 */
9351
9352var QuickLookSession =
9353/*#__PURE__*/
9354function () {
9355 /**
9356 * Create new instance of QuickLookSession
9357 * @param {object} [options] Quick Look options
9358 * @param {string} [options.file] USDZ file's location URL.
9359 * @param {boolean} [options.allowsContentScaling=true] Whether to allow content scaling.
9360 */
9361 function QuickLookSession(_a) {
9362 var file = _a.file,
9363 _b = _a.allowsContentScaling,
9364 allowsContentScaling = _b === void 0 ? true : _b;
9365 /**
9366 * Whether it's webxr-based session or not
9367 * @type false
9368 */
9369
9370 this.isWebXRSession = false;
9371 this._file = file;
9372 this._allowsContentScaling = allowsContentScaling;
9373 }
9374 /**
9375 * Return the availability of QuickLookSession.
9376 * QuickLook AR is available on iOS12+ on Safari & Chrome browser
9377 * Note that iOS Chrome won't show up QuickLook AR when it's local dev environment
9378 * @returns {Promise} A Promise that resolves availability of this session(boolean).
9379 */
9380
9381
9382 var __proto = QuickLookSession.prototype;
9383
9384 __proto.isAvailable = function () {
9385 // This can handle all WebKit based browsers including iOS Safari & iOS Chrome
9386 return Promise.resolve(QUICKLOOK_SUPPORTED && IS_IOS && IS_SAFARI);
9387 };
9388 /**
9389 * Enter QuickLook AR Session
9390 */
9391
9392
9393 __proto.enter = function () {
9394 var anchor = document.createElement("a");
9395 anchor.setAttribute("rel", "ar");
9396 anchor.appendChild(document.createElement("img"));
9397 var usdzURL = new URL(this._file, window.location.toString());
9398
9399 if (!this._allowsContentScaling) {
9400 usdzURL.hash = "allowsContentScaling=0";
9401 }
9402
9403 anchor.setAttribute("href", usdzURL.toString());
9404 anchor.click();
9405 return Promise.resolve();
9406 };
9407
9408 __proto.exit = function () {// DO NOTHING
9409 };
9410
9411 return QuickLookSession;
9412}();
9413
9414/*
9415 * Copyright (c) 2020 NAVER Corp.
9416 * egjs projects are licensed under the MIT license
9417 */
9418/**
9419 * Texture(image) model
9420 * @category Extra
9421 */
9422
9423var TextureModel =
9424/*#__PURE__*/
9425function (_super) {
9426 __extends(TextureModel, _super);
9427 /**
9428 * Create new TextureModel
9429 * @param {object} options Options
9430 * @param {number} [options.width] Width of the model.
9431 * @param {number} [options.height] Height of the model.
9432 * @param {boolean} [options.billboard=false] When set to true, model will keep rotate to show its front face to camera. Only Y-axis rotation is considered.
9433 * @throws {View3DError} `CODES.PROVIDE_WIDTH_OR_HEIGHT` When both width and height are not given.
9434 */
9435
9436
9437 function TextureModel(_a) {
9438 var image = _a.image,
9439 width = _a.width,
9440 height = _a.height,
9441 _b = _a.billboard,
9442 billboard = _b === void 0 ? false : _b;
9443
9444 var _this = this;
9445
9446 var texture = image.isTexture ? image : new Texture(image);
9447 var aspect = texture.image.width / texture.image.height;
9448
9449 if (width == null && height == null) {
9450 throw new View3DError(MESSAGES.PROVIDE_WIDTH_OR_HEIGHT, CODES.PROVIDE_WIDTH_OR_HEIGHT);
9451 }
9452
9453 if (width == null) {
9454 width = height * aspect;
9455 } else if (height == null) {
9456 height = width / aspect;
9457 }
9458
9459 texture.encoding = sRGBEncoding;
9460 var geometry = new PlaneGeometry(width, height);
9461 var material = new MeshBasicMaterial({
9462 map: texture,
9463 side: DoubleSide
9464 });
9465 var mesh = new Mesh(geometry, material);
9466 _this = _super.call(this, {
9467 scenes: [mesh]
9468 }) || this;
9469 _this._texture = texture;
9470 _this._mesh = mesh;
9471
9472 if (billboard) {
9473 var root_1 = mesh;
9474
9475 root_1.onBeforeRender = function (renderer, scene, camera) {
9476 var pos = root_1.getWorldPosition(new Vector3());
9477 var camPos = new Vector3().setFromMatrixPosition(camera.matrixWorld);
9478 root_1.lookAt(camPos.setY(pos.y));
9479 mesh.updateMatrix();
9480 };
9481 }
9482
9483 return _this;
9484 }
9485
9486 var __proto = TextureModel.prototype;
9487 Object.defineProperty(__proto, "texture", {
9488 /**
9489 * Texture that used for this model
9490 * @see https://threejs.org/docs/index.html#api/en/textures/Texture
9491 * @type THREE.Texture
9492 */
9493 get: function () {
9494 return this._texture;
9495 },
9496 enumerable: false,
9497 configurable: true
9498 });
9499 Object.defineProperty(__proto, "mesh", {
9500 /**
9501 * Model's mesh object
9502 * @see https://threejs.org/docs/index.html#api/en/objects/Mesh
9503 * @type THREE.Mesh
9504 */
9505 get: function () {
9506 return this._mesh;
9507 },
9508 enumerable: false,
9509 configurable: true
9510 });
9511 return TextureModel;
9512}(Model);
9513
9514export default View3D;
9515export { AnimationControl, AutoControl, AutoDirectionalLight, Camera, Controller, DistanceControl, DracoLoader, easing as EASING, CODES as ERROR_CODES, FloorARSession, GLTFLoader, HoverARSession, Model, ModelAnimator, Motion, OrbitControl, Pose, QuickLookSession, Renderer, RotateControl, Scene, SceneViewerSession, ShadowPlane, TextureLoader, TextureModel, TranslateControl, View3DError, WallARSession, WebARSession, XRManager };
9516//# sourceMappingURL=view3d.esm.js.map