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