@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
343 lines (342 loc) • 11.1 kB
JavaScript
"use strict";
import { WebGLRenderer } from "three";
import { TypedViewer } from "./_Base";
import { CoreCameraRendererController } from "../../core/camera/CoreCameraRendererController";
import { CoreCameraPostProcessController } from "../../core/camera/CoreCameraPostProcessController";
import { CoreCameraCSSRendererController } from "../../core/camera/CoreCameraCSSRendererController";
import { CoreCameraViewerCodeController } from "../../core/camera/CoreCameraViewerCodeController";
import { CoreCameraControlsController } from "../../core/camera/CoreCameraControlsController";
import { CoreCameraRenderSceneController } from "../../core/camera/CoreCameraRenderSceneController";
import { CoreCameraWebXRController } from "../../core/camera/webXR/CoreCameraWebXR";
import { CoreCameraMarkerTrackingController } from "../../core/camera/webXR/CoreCameraMarkerTracking";
import { CoreCameraViewerFPSController, isDeltaValid } from "../../core/camera/CoreCameraFPS";
const CSS_CLASS = "CoreThreejsViewer";
export class ThreejsViewer extends TypedViewer {
constructor(options) {
super(options);
this._animateWebBound = this._animateWeb.bind(this);
this._accumulatedDelta = 0;
//
//
// resize
//
//
this._onResizeBound = this.onResize.bind(this);
this._setupFunctions(options);
}
static _canvasIdPrefix() {
return "ThreejsViewer";
}
rendererConfig() {
return this._rendererConfig;
}
_setupFunctions(options) {
var _a;
const camera = this.camera();
const scene = this.scene();
const canvas = this.canvas();
const threejsScene = scene.threejsScene();
this._errorMessage = void 0;
this._renderer = options.renderer;
if (!this._renderer) {
this._rendererConfig = CoreCameraRendererController.rendererConfig({
camera,
scene,
canvas
});
if (this._rendererConfig) {
this._renderer = this._rendererConfig.renderer;
}
}
const renderer = this._renderer;
if (!renderer) {
console.error("no renderer");
}
if (renderer) {
const rendererScene = CoreCameraRenderSceneController.renderScene({ camera, scene });
const renderScene = rendererScene || threejsScene;
this._effectComposer = CoreCameraPostProcessController.createComposer({
camera,
scene,
renderScene,
renderer,
viewer: this
});
const effectComposer = this._effectComposer;
if (renderer instanceof WebGLRenderer) {
this._webXRConfig = CoreCameraWebXRController.process({
camera,
scene,
renderer,
canvas: this.canvas()
});
this._markerTrackingConfig = CoreCameraMarkerTrackingController.process({
canvas,
camera,
scene,
onError: (errorMessage) => {
this._errorMessage = errorMessage;
}
});
}
this._codeConfig = CoreCameraViewerCodeController.viewerCodeConfig({ camera });
this._cssRendererConfig = CoreCameraCSSRendererController.cssRendererConfig({ scene, camera, canvas });
const cssRenderer = (_a = this._cssRendererConfig) == null ? void 0 : _a.cssRenderer;
this._renderCSSFunc = cssRenderer ? () => cssRenderer.render(renderScene, camera) : void 0;
this._controlsNode = CoreCameraControlsController.controlsNode({ camera, scene });
this._FPSConfig = CoreCameraViewerFPSController.viewerFPSConfig({ camera });
if (effectComposer) {
this._renderFunc = (delta) => effectComposer.render(delta);
} else {
this._renderFunc = () => renderer.render(renderScene, camera);
}
return { renderer, renderScene, camera };
}
}
/**
* mounts the viewer onto an element
*
*
*/
mount(element, options) {
var _a, _b, _c;
super.mount(element, options);
const canvas = this.canvas();
const _appendCanvasWithViewerCodeConfig = (config) => {
if (!this._domElement) {
return;
}
return config.createViewerElement({ domElement: this._domElement, canvas, CSSClass: CSS_CLASS });
};
const _appendCanvasWithoutViewerCodeConfig = () => {
var _a2, _b2;
(_a2 = this._domElement) == null ? void 0 : _a2.appendChild(canvas);
(_b2 = this._domElement) == null ? void 0 : _b2.classList.add(CSS_CLASS);
return this._domElement;
};
const viewerContainerElement = this._codeConfig ? _appendCanvasWithViewerCodeConfig(this._codeConfig) : _appendCanvasWithoutViewerCodeConfig();
const cssRendererNode = (_a = this._cssRendererConfig) == null ? void 0 : _a.cssRendererNode;
if (cssRendererNode) {
cssRendererNode.mountRenderer(canvas);
}
(_b = this._webXRConfig) == null ? void 0 : _b.mountFunction();
(_c = this._markerTrackingConfig) == null ? void 0 : _c.mountFunction();
this._build();
this._setEvents();
const canvasParent = canvas.parentElement;
if (canvasParent) {
this._createResizeObserver(canvasParent);
}
this.onResize();
if (this._errorMessage) {
const errorElement = document.createElement("div");
errorElement.style.position = "absolute";
errorElement.style.top = "0px";
errorElement.style.width = "100%";
errorElement.style.color = "red";
errorElement.style.backgroundColor = "white";
errorElement.style.padding = "20px";
errorElement.style.textAlign = "center";
errorElement.style.opacity = "90%";
errorElement.innerText = this._errorMessage;
viewerContainerElement == null ? void 0 : viewerContainerElement.append(errorElement);
}
}
_build() {
this._initDisplay();
this.activate();
}
/**
* disposes the viewer
*
*
*/
dispose() {
var _a, _b, _c;
const canvas = this.canvas();
const cssRendererNode = (_a = this._cssRendererConfig) == null ? void 0 : _a.cssRendererNode;
if (cssRendererNode) {
cssRendererNode.unmountRenderer(canvas);
}
this._cssRendererConfig = void 0;
(_b = this._webXRConfig) == null ? void 0 : _b.unmountFunction();
(_c = this._markerTrackingConfig) == null ? void 0 : _c.unmountFunction();
this._effectComposer = void 0;
this.setAutoRender(false);
this._cancelAnimate();
this._unlistenToWindowResize();
this._disposeResizeObserver();
super.dispose();
}
_setEvents() {
this.eventsController().init();
this.webglController().init();
this._unlistenToWindowResize();
this._listenToWindowResize();
}
_initDisplay() {
if (!this._canvas) {
console.warn("no canvas found for viewer");
return;
}
if (!this._renderer) {
return;
}
const pixelRatio = this._renderer.getPixelRatio();
this.camerasController().computeSizeAndAspect(pixelRatio);
this.audioController().update();
this._startAnimate();
}
/**
* setAutoRender to false will stop the rendering. This can be useful if you know that nothing has changed in the scene, or if the renderer is currently not visible.
*
*
*/
setAutoRender(state = true, options) {
super.setAutoRender(state);
if (this._doRender && this._requestAnimationFrameId == null) {
this._startAnimate();
}
if (!this._doRender) {
if (options && options.cancelAnimate == true) {
this._cancelAnimate();
}
}
}
isXR() {
if (!this._renderer) {
return false;
}
return this._renderer instanceof WebGLRenderer && this._renderer.xr.enabled;
}
_startAnimate() {
if (this.isXR()) {
const renderer = this._renderer;
if (!renderer) {
return;
}
const webXRController = this.scene().webXR;
const xrCallback = (timestamp, frame) => {
var _a;
(_a = webXRController.activeXRController()) == null ? void 0 : _a.process(frame);
this._animateWebXR();
};
renderer.setAnimationLoop(xrCallback);
} else {
this._animateWeb();
}
}
_cancelAnimate() {
var _a;
if (this.isXR()) {
(_a = this._renderer) == null ? void 0 : _a.setAnimationLoop(null);
} else {
this._cancelAnimateCommon();
}
}
_animateWeb() {
this._requestAnimationFrameId = requestAnimationFrame(this._animateWebBound);
this.__animateCommon__();
}
_animateWebXR() {
this.__animateCommon__();
}
__animateCommon__() {
const delta = this._scene.timeController.updateClockDelta();
if (this._FPSConfig) {
this._accumulatedDelta += delta;
if (!isDeltaValid(this._accumulatedDelta, this._FPSConfig)) {
return;
}
this._accumulatedDelta = 0;
}
this._tickAndRender(delta);
}
_cancelAnimateCommon() {
this._doRender = false;
if (this._requestAnimationFrameId != null) {
cancelAnimationFrame(this._requestAnimationFrameId);
this._requestAnimationFrameId = void 0;
}
if (this._canvas) {
}
}
_tick(delta) {
var _a;
super._tick(delta);
(_a = this._markerTrackingConfig) == null ? void 0 : _a.renderFunction();
}
/**
* returns the current renderer
*
*
*/
renderer() {
return this._renderer;
}
effectComposer() {
return this._effectComposer;
}
preCompile() {
if (!this._renderer) {
return;
}
this._renderer.compile(this._scene.threejsScene(), this._camera);
}
markAsReady() {
this.preCompile();
this.setAutoRender(true);
}
onResize() {
const renderUpdateRequired = this.updateSize();
if (!renderUpdateRequired) {
return;
}
this._updateRendererSize();
if (this._renderFunc) {
this._renderFunc(this.scene().timeController.delta());
}
this._runOnResizeCallbacks();
}
updateSize() {
const renderer = this._renderer;
if (!renderer) {
return false;
}
const pixelRatio = renderer.getPixelRatio();
this.camerasController().computeSizeAndAspect(pixelRatio);
const size = this.camerasController().size;
if (this._size.equals(size)) {
return false;
}
this._size.copy(size);
this._scene.viewersRegister.markViewerAsSizeUpdated(this);
return true;
}
_updateRendererSize() {
var _a, _b;
const canvas = this._canvas;
if (!canvas) {
return;
}
CoreCameraRendererController.setRendererSize(canvas, this._size);
(_a = this._cssRendererConfig) == null ? void 0 : _a.cssRenderer.setSize(this._size.x, this._size.y);
(_b = this._effectComposer) == null ? void 0 : _b.setSize(this._size.x, this._size.y);
this.camerasController().updateCameraAspect();
}
_listenToWindowResize() {
}
_unlistenToWindowResize() {
}
_createResizeObserver(canvasElementParent) {
this._disposeResizeObserver();
this._resizeObserver = new ResizeObserver(this._onResizeBound);
this._resizeObserver.observe(canvasElementParent, { box: "border-box" });
}
_disposeResizeObserver() {
if (this._resizeObserver) {
this._resizeObserver.disconnect();
this._resizeObserver = void 0;
}
}
}