@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
391 lines (390 loc) • 11.4 kB
JavaScript
"use strict";
import { ActorsManager } from "./utils/ActorsManager";
import { SceneCamerasController } from "./utils/SceneCamerasController";
import { Cooker } from "./utils/Cooker";
import { SceneCookController } from "./utils/CookController";
import { CoreGraph } from "../../core/graph/CoreGraph";
import { CorePerformance } from "../../core/performance/CorePerformance";
import { DispatchController } from "./utils/DispatchController";
import { SceneExpressionsController } from "./utils/SceneExpressionsController";
import { SceneLifeCycleController } from "./utils/LifeCycleController";
import { LoadingController } from "./utils/LoadingController";
import { MissingReferencesController } from "./utils/missingReferences/MissingReferencesController";
import { GraphNodesController } from "./utils/GraphNodesController";
import { NodesController } from "./utils/NodesController";
import { SceneEventsDispatcher } from "./utils/events/EventsDispatcher";
import { ObjectsController } from "./utils/ObjectsController";
import { ScenePerformanceMonitor } from "./utils/ScenePerformanceMonitor";
import { ReferencesController } from "./utils/ReferencesController";
import {
TimeController,
TIME_CONTROLLER_UPDATE_TIME_OPTIONS_DEFAULT
} from "./utils/TimeController";
import { UniformsController } from "./utils/UniformsController";
import { ViewersRegister } from "./utils/ViewersRegister";
import { SceneWebGLController } from "./utils/WebGLController";
import { WindowController } from "./utils/WindowController";
import { SceneAssetsController } from "./utils/AssetsController";
import { SceneTraverserController } from "./utils/SceneTraverser";
import { CoreString } from "../../core/String";
import { SceneRenderersRegister } from "./utils/SceneRenderersRegister";
import { Poly } from "../Poly";
import { SceneWebXRController } from "./utils/WebXREventsController";
export class PolyScene {
//
//
// constructor
//
//
constructor(options) {
this._cooker = new Cooker(this);
this.cookController = new SceneCookController();
this._graph = new CoreGraph();
this.lifecycleController = new SceneLifeCycleController(this);
this.loadingController = new LoadingController(this);
this.missingExpressionReferencesController = new MissingReferencesController(this);
this.expressionsController = new SceneExpressionsController();
this.nodesController = new NodesController(this);
this.graphNodesController = new GraphNodesController(this);
this.renderersRegister = new SceneRenderersRegister(this);
this._objectsController = new ObjectsController(this);
this._referencesController = new ReferencesController(this);
this.perfMonitor = new ScenePerformanceMonitor(this);
this.sceneTraverser = new SceneTraverserController(this);
//
//
// time
//
//
this.timeController = new TimeController(this);
this._disposed = false;
this._graph.setScene(this);
this._paramSerializerClass = options == null ? void 0 : options.paramsSerializerClass;
this.nodesController.createRoot(options == null ? void 0 : options.root);
Poly.scenesRegister.registerScene(this);
}
/**
* Returns the THREE.Scene.
*
* @remarks
*
* Read more about how to use a THREE.Scene on [THREE's documentation](https://threejs.org/docs/?q=scene#api/en/scenes/Scene)
*
*/
threejsScene() {
return this.root().object;
}
setUuid(uuid) {
return this._uuid = uuid;
}
get uuid() {
return this._uuid;
}
setName(newName) {
return this._name = PolyScene.sanitizeName(newName);
}
static sanitizeName(newName) {
newName = CoreString.sanitizeName(newName);
newName = newName.toLowerCase();
return newName;
}
name() {
return this._name;
}
get camerasController() {
return this._camerasController = this._camerasController || new SceneCamerasController(this);
}
/**
* Returns the camera object that has been set as main
*
*/
mainCamera() {
return this.camerasController.mainCamera();
}
get cooker() {
return this._cooker;
}
get actorsManager() {
return this._actorsManager = this._actorsManager || new ActorsManager(this);
}
get assets() {
return this._assetsController = this._assetsController || new SceneAssetsController();
}
/**
* Returns a promise to wait for all nodes to have cooked when loading a scene.
*
*/
async waitForCooksCompleted() {
return await this.cookController.waitForCooksCompleted();
}
get dispatchController() {
return this._dispatchController = this._dispatchController || new DispatchController(this);
}
get eventsDispatcher() {
return this._eventsDispatcher = this._eventsDispatcher || new SceneEventsDispatcher(this);
}
get webXR() {
return this._webXRController = this._webXRController || new SceneWebXRController(this);
}
/**
* When using Polygonjs viewers, a raycaster is created to use mouse events and detect if there are any object under the cursor.
* But if no viewer is created, such as when [importing a scene in react three fiber](/docs/integrations/react_three_fiber),
* It is then useful to give a raycaster.
*
*/
setRaycaster(raycaster) {
this.eventsDispatcher.setRaycaster(raycaster);
}
get graph() {
return this._graph;
}
createNode(nodeClass, options) {
return this.root().createNode(nodeClass, options);
}
/**
* returns all nodes with a given type
*
* - polyScene.nodesByType('box'): returns all BoxSopNodes
*/
nodesByType(type) {
return this.nodesController.nodesByType(type);
}
get objectsController() {
return this._objectsController;
}
/**
* returns a THREE.Object3D whose name matches the mask
*
*/
findObjectByMask(mask) {
return this._objectsController.findObjectByMask(mask);
}
/**
* returns a list THREE.Object3Ds whose names matche the mask
*
*/
objectsByMask(mask, parent) {
return this._objectsController.objectsByMask(mask, parent);
}
get referencesController() {
return this._referencesController;
}
get performance() {
return this._performance = this._performance || new CorePerformance();
}
get viewersRegister() {
return this._viewersRegister = this._viewersRegister || new ViewersRegister(this);
}
/**
* updates Polygonjs scene internals. This is called automatically when using Polygonjs viewers,
* but you would need to call it yourself in the render loop when adding your scene to threejs or react-three-fiber.
* See [https://polygonjs.com/docs/integrations](https://polygonjs.com/docs/integrations)
*
*/
update(delta, state) {
this.timeController.setDelta(delta);
this.timeController.incrementTimeIfPlaying(TIME_CONTROLLER_UPDATE_TIME_OPTIONS_DEFAULT);
this.sceneTraverser.traverseScene(state == null ? void 0 : state.scene);
}
/**
* sets the current frame
*
*/
setFrame(frame) {
this.timeController.setFrame(frame);
}
setFrameToStart() {
this.timeController.setFrameToStart();
}
/**
* returns the current frame
*
*/
frame() {
return this.timeController.frame();
}
/**
* returns the current time
*
*/
time() {
return this.timeController.time();
}
maxFrame() {
return this.timeController.maxFrame();
}
/**
* starts playing the scene
*
*/
play() {
this.timeController.play();
}
/**
* pauses the scene
*
*/
pause() {
this.timeController.pause();
}
/**
* increments the time
*
*/
incrementTime(options) {
this.timeController.incrementTime(options);
}
/**
* increments the time if the scene is playing()
*
*/
incrementTimeIfPlaying(options) {
this.timeController.incrementTimeIfPlaying(options);
}
/**
* registers a renderer
*
*/
registerRenderer(renderer, options) {
return this.renderersRegister.registerRenderer(renderer, options);
}
get uniformsController() {
return this._uniformsController = this._uniformsController || new UniformsController(this);
}
get webglController() {
return this._webglController = this._webglController || new SceneWebGLController();
}
get windowController() {
return this._windowController = this._windowController || new WindowController(this);
}
dispose() {
if (this._disposed == true) {
return;
}
this._disposed = true;
this.batchUpdates(() => {
this.nodesController.traverseNodes((node) => {
var _a;
(_a = node.parent()) == null ? void 0 : _a.removeNode(node);
});
});
if (this._windowController) {
this._windowController.dispose();
this._windowController = void 0;
}
this.timeController.dispose();
this.renderersRegister.dispose();
this.camerasController.dispose();
this.root().dispose();
Poly.scenesRegister.deregisterScene(this);
}
disposed() {
return this._disposed;
}
paramSerializerClass() {
return this._paramSerializerClass;
}
//
//
// cooker
//
//
/**
* batchUpdates can be useful to set multiple parameter values without triggering a recook for each update.
*
*/
async batchUpdates(callback) {
this._cooker.block();
await callback();
this._cooker.unblock();
}
//
//
// nodes
//
//
/**
* returns a node based on its path
*
* - polyScene.node('/geo1')
*
*/
node(path) {
return this.nodesController.node(path);
}
/**
* returns the root node
*
*/
root() {
return this.nodesController.root();
}
/**
* traverse all nodes and runs a callback for each
*
*/
traverseNodes(callback) {
this.nodesController.traverseNodes(callback);
}
//
//
// CALLBACKS
//
//
/**
* registers a BeforeTick callback. BeforeTick callbacks are run before updating the frame (and therefore before any time dependent node has changed)
*
*/
registerOnBeforeTick(callbackName, callback) {
this.timeController.registerOnBeforeTick(callbackName, callback);
}
/**
* unregisters BeforeTick callback
*
*/
unRegisterOnBeforeTick(callbackName) {
this.timeController.unRegisterOnBeforeTick(callbackName);
}
/**
* Returns the list registered BeforeTick callback names
*
*/
registeredBeforeTickCallbacks() {
return this.timeController.registeredBeforeTickCallbacks();
}
/**
* return true if a callback is registered with that name
*
*/
hasBeforeTickCallback(callbackName) {
return this.timeController.hasBeforeTickCallback(callbackName);
}
/**
* registers AfterTick callback. AfterTick callbacks are run after updating the frame (and therefore after any time dependent node has changed)
*
*/
registerOnAfterTick(callbackName, callback) {
this.timeController.registerOnAfterTick(callbackName, callback);
}
/**
* unregisters AfterTick callback
*
*/
unRegisterOnAfterTick(callbackName) {
this.timeController.unRegisterOnAfterTick(callbackName);
}
/**
* Returns the list registered AfterTick callback names
*
*/
registeredAfterTickCallbacks() {
return this.timeController.registeredAfterTickCallbacks();
}
/**
* return true if a callback is registered with that name
*
*/
hasAfterTickCallback(callbackName) {
return this.timeController.hasAfterTickCallback(callbackName);
}
}