UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

476 lines (475 loc) 14.1 kB
"use strict"; import { createRaycaster } from "./../../core/RaycastHelper"; import { ViewerCamerasController } from "./utils/ViewerCamerasController"; import { ViewerControlsController } from "./utils/ViewerControlsController"; import { ViewerEventsController } from "./utils/ViewerEventsController"; import { ViewerWebGLController } from "./utils/ViewerWebglController"; import { ViewerAudioController } from "./utils/ViewerAudioController"; import { Vector2 } from "three"; import { Poly } from "../Poly"; import { ViewerRaycastersController } from "./utils/ViewerRaycastersController"; import { ViewerPerformanceMonitor } from "./utils/ViewerPerformanceMonitor"; import { MouseHelper } from "../../core/event/MouseHelper"; const HOVERED_CLASS_NAME = "hovered"; const _TypedViewer = class { constructor(options) { this._active = false; this._doRender = true; this._mounted = false; this._controlsController = new ViewerControlsController(this); this.performanceMonitor = new ViewerPerformanceMonitor(this); this._size = new Vector2(); this._onBeforeTickCallbacks = []; this._onAfterTickCallbacks = []; this._onBeforeRenderCallbacks = []; this._onAfterRenderCallbacks = []; this._id = _TypedViewer._nextId(); this._camera = options.camera; this._scene = options.scene; this._canvas = options.canvas; if (options.autoRender != null) { this._doRender = options.autoRender; } this.updateCameraAspect = options.updateCameraAspect; this.scene().viewersRegister.registerViewer(this); this.raycastersController = new ViewerRaycastersController(this); } static _nextId() { return `${_TypedViewer._nextViewerId++}`; } createRaycaster() { return createRaycaster(); } /** * mounts the viewer onto an element * * */ mount(element, options) { let updateAutoRenderOnIntersectionChange = true; if (options && options.updateAutoRenderOnIntersectionChange != null) { updateAutoRenderOnIntersectionChange = options.updateAutoRenderOnIntersectionChange; } this._domElement = element; this._domElement.viewer = this; this._domElement.scene = this._scene; this._domElement.Poly = Poly; if (updateAutoRenderOnIntersectionChange) { this.updateAutoRenderOnIntersectionChange({ playPauseScene: true }); } this.controlsController().mount(); this._mounted = true; } /** * unmounts the viewer * * */ unmount() { var _a, _b; if (!this._domElement) { return; } (_a = this._audioController) == null ? void 0 : _a.unmount(); const canvas = this.canvas(); (_b = canvas.parentElement) == null ? void 0 : _b.removeChild(canvas); this.eventsController().unmount(); this.controlsController().unmount(); this._mounted = false; } static _canvasIdPrefix() { return "TypedViewer"; } static createCanvas(id) { id = id || _TypedViewer._nextId(); const canvas = Poly.canvasRegister.findOrCreateCanvas(); canvas.id = `${this._canvasIdPrefix()}_${id}`; canvas.style.display = "block"; canvas.style.outline = "none"; canvas.style.width = "100%"; canvas.style.height = "100%"; canvas.addEventListener("contextmenu", (e) => { e.preventDefault(); e.stopPropagation(); }); return canvas; } controlsNode() { return this._controlsNode; } /** * return the canvas and create one if none yet * * */ canvas() { return this._canvas = this._canvas || _TypedViewer.createCanvas(this._id); } _tickAndRender(delta) { this._tick(delta); this.render(delta); this._postRender(delta); } _tick(delta) { this.controlsController().update(delta); this.raycastersController.updateRaycasters(); this._runOnBeforeTickCallbacks(delta); this.scene().update(delta); this._runOnAfterTickCallbacks(delta); } render(delta) { if (!this._doRender) { return; } const renderer = this._renderer; if (!renderer) { console.error("render: no renderer"); return; } this._runOnBeforeRenderCallbacks(delta, renderer); if (this._renderFunc) { this._renderFunc(delta); } if (this._renderCSSFunc) { this._renderCSSFunc(); } this._runOnAfterRenderCallbacks(delta, renderer); } _postRender(delta) { this._scene.viewersRegister.markViewerAsRendered(this); if (this.scene().timeController.playing()) { this.performanceMonitor.measurePerformance(delta); } } setRenderObjectOverride(object) { if (object) { this._renderObjectOverride = object; } else { this._renderObjectOverride = void 0; } } active() { return this._active; } activate() { this._active = true; } deactivate() { this._active = false; } camerasController() { return this._camerasController = this._camerasController || new ViewerCamerasController(this); } controlsController() { return this._controlsController; } eventsController() { return this._eventsController = this._eventsController || new ViewerEventsController(this); } webglController() { return this._webGLController = this._webGLController || new ViewerWebGLController(this); } audioController() { return this._audioController = this._audioController || new ViewerAudioController(this); } domElement() { return this._domElement; } scene() { return this._scene; } /** * return the camera the viewer was created with * * */ camera() { return this._camera; } // cameraControlsController(): ThreejsCameraControlsController | undefined { // return undefined; // } id() { return this._id; } size(target) { target.copy(this._size); } updateSize() { } /** * disposes the viewer * * */ dispose() { this._scene.viewersRegister.unregisterViewer(this); this.controlsController().unmount(); if (!this._domElement) { return; } let child; while (child = this._domElement.children[0]) { this._domElement.removeChild(child); } } // html container class resetContainerClass() { var _a; (_a = this.domElement()) == null ? void 0 : _a.classList.remove(HOVERED_CLASS_NAME); } setContainerClassHovered() { var _a; (_a = this.domElement()) == null ? void 0 : _a.classList.add(HOVERED_CLASS_NAME); } markAsReady() { } /** * sets auto render state. If falls, the viewer will not render. * * */ setAutoRender(state = true) { this._doRender = state; } autoRenderState() { return this._doRender; } /** * This sets the viewer to detect if it is visible, and to pause/unpause itself when its visibility changes. * This can be very useful to improve performance. * */ updateAutoRenderOnIntersectionChange(_options) { if (!this._domElement) { console.warn("cannot apply"); return; } let { threshold, root, rootMargin, playPauseScene, onChange } = _options; if (threshold == null) { threshold = 0.01; } const observerOptions = { root, rootMargin, threshold }; const onObserverChange = (entries, observer) => { if (!this._scene.loadingController.loaded()) { return; } entries.forEach((entry) => { const isVisible = entry.isIntersecting; this.setAutoRender(isVisible); if (playPauseScene) { if (isVisible) { this._scene.play(); } else { this._scene.pause(); } } if (onChange) { onChange(isVisible); } }); }; this.disableUpdateAutoRenderOnIntersectionChange(); this._observer = new IntersectionObserver(onObserverChange, observerOptions); this._observer.observe(this._domElement); } /** * Cancels the listening of the viewer's visibility. * */ disableUpdateAutoRenderOnIntersectionChange() { if (!this._domElement) { return; } if (!this._observer) { return; } this._observer.unobserve(this._domElement); } registerOnResizeCallback(callback) { this._onResizeCallbacks = this._onResizeCallbacks || []; this._onResizeCallbacks.push(callback); } unregisterOnResizeCallback(callback) { if (!this._onResizeCallbacks) { return; } const index = this._onResizeCallbacks.indexOf(callback); if (index >= 0) { this._onResizeCallbacks.splice(index, 1); } if (this._onResizeCallbacks.length == 0) { this._onResizeCallbacks = void 0; } } _runOnResizeCallbacks() { const canvas = this.canvas(); if (canvas) { MouseHelper.resetCacheForCanvas(canvas); } if (!this._onResizeCallbacks) { return; } for (const callback of this._onResizeCallbacks) { callback(this._size.x, this._size.y); } } // onBeforeTick /** * registers a BeforeTick callback. BeforeTick callbacks are run before updating the frame (and therefore before any time dependent node has changed) * */ registerOnBeforeTick(callbackName, callback, options = {}) { this._registerCallback(callbackName, callback, this.registeredBeforeTickCallbacks(), options); } /** * unregisters BeforeTick callback * */ unRegisterOnBeforeTick(callbackName) { this._unregisterCallback(callbackName, this._onBeforeTickCallbacksMap); } /** * Returns the list registered BeforeTick callback names * */ registeredBeforeTickCallbacks() { return this._onBeforeTickCallbacksMap = this._onBeforeTickCallbacksMap || /* @__PURE__ */ new Map(); } // onAfterTick /** * registers AfterTick callback. AfterTick callbacks are run after updating the frame (and therefore after any time dependent node has changed) * */ registerOnAfterTick(callbackName, callback, options = {}) { this._registerCallback(callbackName, callback, this.registeredAfterTickCallbacks(), options); } /** * unregisters AfterTick callback * */ unRegisterOnAfterTick(callbackName) { this._unregisterCallback(callbackName, this._onAfterTickCallbacksMap); } /** * Returns the list registered AfterTick callback names * */ registeredAfterTickCallbacks() { return this._onAfterTickCallbacksMap = this._onAfterTickCallbacksMap || /* @__PURE__ */ new Map(); } // onBeforeRender /** * registers a BeforeRender callback. BeforeRender callbacks are run before the frame is rendered * */ registerOnBeforeRender(callbackName, callback, options = {}) { this._registerCallback(callbackName, callback, this.registeredBeforeRenderCallbacks(), options); } /** * unregisters BeforeRender callback * */ unRegisterOnBeforeRender(callbackName) { this._unregisterCallback(callbackName, this._onBeforeRenderCallbacksMap); } /** * Returns the list registered BeforeRender callback names * */ registeredBeforeRenderCallbacks() { return this._onBeforeRenderCallbacksMap = this._onBeforeRenderCallbacksMap || /* @__PURE__ */ new Map(); } // onAfterRender /** * registers a AfterRender callback. AfterRender callbacks are run after the frame is rendered * */ registerOnAfterRender(callbackName, callback, options = {}) { this._registerCallback(callbackName, callback, this.registeredAfterRenderCallbacks(), options); } /** * unregisters AfterRender callback * */ unRegisterOnAfterRender(callbackName) { this._unregisterCallback(callbackName, this._onAfterRenderCallbacksMap); } /** * Returns the list AfterRender BeforeRender callback names * */ registeredAfterRenderCallbacks() { return this._onAfterRenderCallbacksMap = this._onAfterRenderCallbacksMap || /* @__PURE__ */ new Map(); } _registerCallback(callbackName, callback, map, options = {}) { if (map.has(callbackName)) { console.warn(`callback ${callbackName} already registered`); return; } map.set(callbackName, { callback, options }); this._updateCallbacks(); } _unregisterCallback(callbackName, map) { if (!map) { return; } const callbackContainer = map.get(callbackName); if (!callbackContainer) { return; } const options = callbackContainer.options; if (options.persistent == true) { return; } map.delete(callbackName); this._updateCallbacks(); } _updateCallbacks() { var _a, _b, _c, _d; this._onBeforeTickCallbacks = []; (_a = this._onBeforeTickCallbacksMap) == null ? void 0 : _a.forEach((callbackContainer) => { this._onBeforeTickCallbacks.push(callbackContainer.callback); }); this._onAfterTickCallbacks = []; (_b = this._onAfterTickCallbacksMap) == null ? void 0 : _b.forEach((callbackContainer) => { this._onAfterTickCallbacks.push(callbackContainer.callback); }); this._onBeforeRenderCallbacks = []; (_c = this._onBeforeRenderCallbacksMap) == null ? void 0 : _c.forEach((callbackContainer) => { this._onBeforeRenderCallbacks.push(callbackContainer.callback); }); this._onAfterRenderCallbacks = []; (_d = this._onAfterRenderCallbacksMap) == null ? void 0 : _d.forEach((callbackContainer) => { this._onAfterRenderCallbacks.push(callbackContainer.callback); }); } _runTickCallbacks(callbacks, delta) { for (const callback of callbacks) { callback(delta); } } _runRenderCallbacks(callbacks, delta, renderer) { for (const callback of callbacks) { callback(delta, renderer); } } _runOnBeforeTickCallbacks(delta) { this._runTickCallbacks(this._onBeforeTickCallbacks, delta); } _runOnAfterTickCallbacks(delta) { this._runTickCallbacks(this._onAfterTickCallbacks, delta); } _runOnBeforeRenderCallbacks(delta, renderer) { this._runRenderCallbacks(this._onBeforeRenderCallbacks, delta, renderer); } _runOnAfterRenderCallbacks(delta, renderer) { this._runRenderCallbacks(this._onAfterRenderCallbacks, delta, renderer); } }; export let TypedViewer = _TypedViewer; TypedViewer._nextViewerId = 0;