@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
476 lines (475 loc) • 14.1 kB
JavaScript
"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;