polygonjs-engine
Version:
node-based webgl 3D engine https://polygonjs.com
149 lines (134 loc) • 4.63 kB
text/typescript
import {BaseObjNodeClass} from '../_Base';
import {Object3D} from 'three/src/core/Object3D';
import {DisplayNodeController, DisplayNodeControllerCallbacks} from '../../utils/DisplayNodeController';
import {Group} from 'three/src/objects/Group';
import {PolyDictionary} from '../../../../types/GlobalTypes';
const DISPLAY_PARAM_NAME = 'display';
interface BaseObjNodeClassWithDisplayNode extends BaseObjNodeClass {
display_node_controller: DisplayNodeController;
}
export class ChildrenDisplayController {
_children_uuids_dict: PolyDictionary<boolean> = {};
_children_length: number = 0;
private _sop_group = this._create_sop_group();
constructor(private node: BaseObjNodeClassWithDisplayNode) {}
private _create_sop_group() {
// This may need to be a Mesh for the rivet to update correctly
// But when it is not used for a rivet, there is a place where a MeshBasicMaterial
// is added to it, making it an additional webgl program for the renderer.
// const mesh = new Mesh();
const group = new Group();
group.matrixAutoUpdate = false;
return group;
}
get sop_group() {
return this._sop_group;
}
set_sop_group_name() {
this._sop_group.name = `${this.node.name()}:sop_group`;
}
display_node_controller_callbacks(): DisplayNodeControllerCallbacks {
return {
on_display_node_remove: () => {
this.remove_children();
},
on_display_node_set: () => {
// use a timeout here, so that the node isn't cooked too early when being copy/pasted, if it had the display flag on.
// This would make nodes error
setTimeout(() => {
this.request_display_node_container();
}, 0);
},
on_display_node_update: () => {
this.request_display_node_container();
},
};
}
initializeNode() {
this.node.object.add(this.sop_group);
this.node.nameController.add_post_set_fullPath_hook(this.set_sop_group_name.bind(this));
this._create_sop_group();
const display_flag = this.node.flags?.display;
if (display_flag) {
display_flag.add_hook(() => {
this._updateSopGroupHierarchy();
if (display_flag.active()) {
this.request_display_node_container();
}
});
}
}
private _updateSopGroupHierarchy() {
const display_flag = this.node.flags?.display;
if (display_flag) {
if (this.usedInScene()) {
this.sop_group.visible = true;
this.node.object.add(this.sop_group);
} else {
this.sop_group.visible = false;
this.node.object.remove(this.sop_group);
}
}
}
usedInScene(): boolean {
const has_active_param = this.node.params.has(DISPLAY_PARAM_NAME);
const is_active_param_on = this.node.params.boolean(DISPLAY_PARAM_NAME);
const used_in_scene = this.node.usedInScene();
const display_flag_on = this.node.flags?.display?.active() || false;
const param_active_on = !has_active_param || is_active_param_on;
return used_in_scene && display_flag_on && param_active_on;
}
async request_display_node_container() {
if (!this.node.scene().loadingController.loaded()) {
return;
}
if (this.usedInScene()) {
await this._set_content_under_sop_group();
}
}
remove_children() {
if (this._sop_group.children.length == 0) {
return;
}
let child: Object3D | undefined;
while ((child = this._sop_group.children[0])) {
this._sop_group.remove(child);
}
this._children_uuids_dict = {};
this._children_length = 0;
}
async _set_content_under_sop_group() {
// we also check that the parent are the same, in case the node has been deleted
// TODO: there should be a wider refactor where deleted node cannot raise callbacks such as flags update
const display_node = this.node.display_node_controller.display_node;
if (display_node && display_node.parent()?.graphNodeId() == this.node.graphNodeId()) {
const container = await display_node.requestContainer();
const core_group = container.coreContent();
if (core_group) {
// check if the new objects are different
const new_objects = core_group.objects();
let new_objects_are_different = new_objects.length != this._children_length;
if (!new_objects_are_different) {
for (let object of new_objects) {
if (!(object.uuid in this._children_uuids_dict)) {
new_objects_are_different = true;
}
}
}
// update hierarchy if different
if (new_objects_are_different) {
this.remove_children();
for (let object of new_objects) {
this._sop_group.add(object);
this._children_uuids_dict[object.uuid] = true;
}
this._children_length = new_objects.length;
}
} else {
this.remove_children();
}
} else {
this.remove_children();
}
}
}