UNPKG

@polygonjs/polygonjs

Version:

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

189 lines (173 loc) 6.68 kB
import {ParamEvent} from './../../../../poly/ParamEvent'; import {TypedSopNode} from './../../_Base'; import {BaseNodeType} from '../../../_Base'; import {NodeParamsConfig, ParamConfig} from '../../../utils/params/ParamsConfig'; import {CoreGroup} from '../../../../../core/geometry/Group'; import {InputCloneMode} from '../../../../poly/InputCloneMode'; import {Object3D} from 'three'; import {Group} from 'three'; import {CoreInstancer} from '../../../../../core/geometry/Instancer'; import {Matrix4} from 'three'; import {Poly} from '../../../../Poly'; import type {BaseGeoLoaderOutput} from '../../../../../core/loader/geometry/Common'; import {BaseObject3DLoaderHandler} from '../../../../../core/loader/geometry/_BaseLoaderHandler'; import {BaseCorePoint, CorePoint} from '../../../../../core/geometry/entities/point/CorePoint'; import {CoreObjectType} from '../../../../../core/geometry/ObjectContent'; // import { Constructor } from 'vue/types/options'; // interface FileMultSopNodeParamConfigOptions { // extensions: string[]; // defaultUrlExpression: string; // } // interface FileMultSopNodeOptions<O extends BaseGeoLoaderOutput> { // type: string; // extensions: string[]; // defaultUrlExpression: string; // createLoader: (url: string, node: BaseNodeType) => BaseGeoLoaderHandler<O>; // } const _points: CorePoint<CoreObjectType>[] = []; class BaseFileMultiParamsConfigResult extends NodeParamsConfig { url = ParamConfig.STRING(''); matrixAutoUpdate = ParamConfig.BOOLEAN(0); reload = ParamConfig.BUTTON(null); } export class BaseFileMultiSopNodeFactoryResult extends TypedSopNode<BaseFileMultiParamsConfigResult> {} // export function fileMultiSopNodeParamConfigFactory( // options: FileMultSopNodeParamConfigOptions // ): typeof NodeParamsConfig { export class BaseFileMultiParamsConfig extends NodeParamsConfig { /** @param url to load the geometry from */ url = ParamConfig.STRING('', { fileBrowse: {extensions: []}, expression: {forEntities: true}, }); /** @param sets the matrixAutoUpdate attribute for the objects loaded */ matrixAutoUpdate = ParamConfig.BOOLEAN(false); /** @param reload the geometry */ reload = ParamConfig.BUTTON(null, { callback: (node: BaseNodeType) => { BaseFileMultiSopNode.PARAM_CALLBACK_reload( node as BaseFileMultiSopNode<BaseGeoLoaderOutput, BaseFileMultiParamsConfig> ); }, }); } // return BaseFileMultiParamsConfig // } // const BaseFileMultiParamsConfig = fileMultiSopNodeParamConfigFactory({defaultUrlExpression: '', extensions:[]}); export abstract class BaseFileMultiSopNode< O extends BaseGeoLoaderOutput, K extends BaseFileMultiParamsConfig > extends TypedSopNode<K> { // override paramsConfig = ParamsConfig; // static override type() { // return options.type; // } protected abstract _createLoader(url: string): BaseObject3DLoaderHandler<O>; override dispose(): void { super.dispose(); Poly.blobs.clearBlobsForNode(this); } override initializeNode() { this.io.inputs.setCount(1); this.io.inputs.initInputsClonedState(InputCloneMode.NEVER); } private _instancer = new CoreInstancer(); private _instanceMatrix = new Matrix4(); override async cook(inputCoreGroups: CoreGroup[]) { const inputCoreGroup = inputCoreGroups[0]; inputCoreGroup.points(_points); // const urls: string[] = new Array(points.length); const urls: string[] = []; const urlByIndex: Map<number, string> = new Map(); const loadedResultByUrl: Map<string, Object3D> = new Map(); const urlUsageCount: Map<string, number> = new Map(); const param = this.p.url; // gather all unique urls if (param.hasExpression() && param.expressionController) { const uniqueUrls: Set<string> = new Set(); const _applyUrlToPoint = (point: BaseCorePoint, url: string) => { // check that this index was not already set const index = point.index(); if (urlByIndex.has(index)) { this.states.error.set(`input points have duplicate indices. Make sure to merge inputs together.`); } else { urlByIndex.set(index, url); uniqueUrls.add(url); BaseFileMultiSopNode._incrementUrlUsageCount(url, urlUsageCount); } }; if (param.expressionController.entitiesDependent()) { await param.expressionController.computeExpressionForPoints(_points, _applyUrlToPoint); } else { for (const point of _points) { _applyUrlToPoint(point, param.value); } } uniqueUrls.forEach((url) => { urls.push(url); }); } else { const url = this.pv.url; urls.push(url); BaseFileMultiSopNode._incrementUrlUsageCount(url, urlUsageCount); } // load each url and place the result under a parent const loadedObjects: Object3D[] = []; const promises = urls.map((url) => this._loadFromUrlPromises(url, loadedResultByUrl)); await Promise.all(promises); // move each loaded result and transform it according to its template point this._instancer.setCoreGroup(inputCoreGroup); for (const point of _points) { const index = point.index(); const url = urlByIndex.get(index) || this.pv.url; this._instancer.matrixFromPoint(point, this._instanceMatrix); const usageCount = urlUsageCount.get(url) || 1; let parent = loadedResultByUrl.get(url); if (parent) { // if this url is used more than 1x, we clone the loaded result if (usageCount > 1) { parent = parent.clone(); } parent.applyMatrix4(this._instanceMatrix); loadedObjects.push(parent); } } this.setObjects(loadedObjects); } private static _incrementUrlUsageCount(url: string, map: Map<string, number>) { const currentUsage = map.get(url); if (currentUsage != null) { map.set(url, currentUsage + 1); } else { map.set(url, 1); } } private async _loadFromUrlPromises(url: string, loadedResultByUrl: Map<string, Object3D>) { const objects = await this._loadObject(url); if (objects) { const parent = new Group(); parent.matrixAutoUpdate = false; parent.name = url; for (const object of objects) { parent.add(object); } loadedResultByUrl.set(url, parent); } } private _loadObject(url: string) { const loader = this._createLoader(url); return this._loadWithLoader(loader); } protected _loadWithLoader(loader: BaseObject3DLoaderHandler<O>) { return loader.load({node: this}); } static PARAM_CALLBACK_reload(node: BaseFileMultiSopNode<BaseGeoLoaderOutput, BaseFileMultiParamsConfig>) { node._paramCallbackReload(); } private _paramCallbackReload() { // set the param dirty is preferable to just the successors, in case the expression result needs to be updated this.p.url.setDirty(); this.p.url.emit(ParamEvent.ASSET_RELOAD_REQUEST); // this.setDirty() } }