UNPKG

@polygonjs/polygonjs

Version:

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

134 lines (133 loc) 4.25 kB
"use strict"; import { LOD } from "three"; import { TypedSopNode } from "./_Base"; import { InputCloneMode } from "../../poly/InputCloneMode"; import { CAMERA_TYPES, NodeContext } from "../../poly/NodeContext"; import { NodeParamsConfig, ParamConfig } from "../utils/params/ParamsConfig"; import { CoreTransform } from "../../../core/Transform"; import { isBooleanTrue } from "../../../core/BooleanValue"; import { ObjectType, registerObjectType } from "../../../core/geometry/Constant"; import { SopType } from "../../poly/registers/nodes/types/Sop"; class LODSopParamsConfig extends NodeParamsConfig { constructor() { super(...arguments); /** @param distance when switching between high res and mid res (first input and second input) */ this.distance0 = ParamConfig.FLOAT(10, { range: [0, 100], rangeLocked: [true, false] }); /** @param distance when switching between mid res and low res (second input and third input) */ this.distance1 = ParamConfig.FLOAT(20, { range: [0, 100], rangeLocked: [true, false] }); /** @param Threshold used to avoid flickering at LOD boundaries, as a fraction of distance */ this.hysteresis = ParamConfig.FLOAT(0, { range: [0, 1], rangeLocked: [true, true] }); /** @param sets if the switch is done automatically */ this.autoUpdate = ParamConfig.BOOLEAN(1); /** @param updates which object is displayed manually */ this.update = ParamConfig.BUTTON(null, { callback: (node) => { LodSopNode.PARAM_CALLBACK_update(node); } }); /** @param sets which camera will be used when the switch is to be done manually */ this.camera = ParamConfig.NODE_PATH("", { nodeSelection: { context: NodeContext.OBJ, types: CAMERA_TYPES }, visibleIf: { autoUpdate: 0 }, dependentOnFoundNode: false }); } } const ParamsConfig = new LODSopParamsConfig(); export class LodSopNode extends TypedSopNode { constructor() { super(...arguments); this.paramsConfig = ParamsConfig; this._lod = this._createLOD(); } static type() { return SopType.LOD; } initializeNode() { this.io.inputs.setCount(1, 3); this.io.inputs.initInputsClonedState(InputCloneMode.FROM_NODE); } _createLOD() { registerObjectType({ type: ObjectType.LOD, checkFunc: (o) => { if (o.isLOD) { return ObjectType.LOD; } }, ctor: LOD, humanName: "LOD" }); const lod = new LOD(); lod.matrixAutoUpdate = false; return lod; } cook(inputCoreGroups) { this._clearLOD(); this._addLevel(inputCoreGroups[0], 0); this._addLevel(inputCoreGroups[1], this.pv.distance0); this._addLevel(inputCoreGroups[2], this.pv.distance1); this._lod.autoUpdate = isBooleanTrue(this.pv.autoUpdate); this.setObject(this._lod); } _addLevel(coreGroup, level) { if (coreGroup) { const objects = coreGroup.threejsObjects(); let object; for (let i = 0; i < objects.length; i++) { object = objects[i]; object.visible = true; this._lod.addLevel(object, level, this.pv.hysteresis); if (level == 0) { if (i == 0) { this._lod.matrix.copy(object.matrix); CoreTransform.decomposeMatrix(this._lod); } } object.matrix.identity(); CoreTransform.decomposeMatrix(object); } } } _clearLOD() { let child; while (child = this._lod.children[0]) { this._lod.remove(child); child.matrix.multiply(this._lod.matrix); CoreTransform.decomposeMatrix(child); } while (this._lod.levels.pop()) { } } static PARAM_CALLBACK_update(node) { node._updateLOD(); } async _updateLOD() { if (isBooleanTrue(this.pv.autoUpdate)) { return; } const cameraNode = this.pv.camera.nodeWithContext(NodeContext.OBJ, this.states.error); if (!cameraNode) { this.states.error.set("no camera node found"); return; } if (!CAMERA_TYPES.includes(cameraNode.type())) { this.states.error.set("node is not a camera node"); return; } const object = cameraNode.object; this._lod.update(object); } }