UNPKG

@polygonjs/polygonjs

Version:

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

212 lines (195 loc) 7.62 kB
/** * Loads multiple geometries from a url, using attributes from the input points. This can be more convenient than the File SOP if you want to load many geometries. * * @remarks * Note that this node will automatically use a specific loader depending on the extension of the url. * */ import {BaseNodeType} from '../_Base'; import {Object3D} from 'three'; import {BaseFileMultiSopNode} from './utils/file/_BaseSopFileMulti'; import {SopTypeFileMulti} from '../../poly/registers/nodes/types/Sop'; import {OBJLoaderHandler} from '../../../core/loader/geometry/OBJ'; import {NodeParamsConfig, ParamConfig} from '../utils/params/ParamsConfig'; import {ASSETS_ROOT} from '../../../core/loader/AssetsUtils'; import {EXTENSIONS_BY_NODE_TYPE_BY_CONTEXT} from '../../../core/loader/FileExtensionRegister'; import {NodeContext} from '../../poly/NodeContext'; // export class FileMultiOBJSopNode extends fileMultiSopNodeFactory<Object3D>({ // type: SopTypeFileMulti.FILE_OBJ, // extensions: [GeometryExtension.OBJ], // defaultUrlExpression: `${ASSETS_ROOT}/models/\`@name\`.obj`, // createLoader: (url: string, node: BaseNodeType) => new OBJLoaderHandler(url, node), // }) {} class FileMultiOBJParamsConfig extends NodeParamsConfig { /** @param url to load the geometry from */ url = ParamConfig.STRING(`${ASSETS_ROOT}/models/\`@name\`.obj`, { fileBrowse: {extensions: EXTENSIONS_BY_NODE_TYPE_BY_CONTEXT[NodeContext.SOP][SopTypeFileMulti.FILE_OBJ]}, 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 FileMultiOBJSopNode); }, }); } const ParamsConfig = new FileMultiOBJParamsConfig(); export class FileMultiOBJSopNode extends BaseFileMultiSopNode<Object3D, FileMultiOBJParamsConfig> { override paramsConfig = ParamsConfig; static override type() { return SopTypeFileMulti.FILE_OBJ; } protected _createLoader(url: string) { return new OBJLoaderHandler(url, this); } } // class FileMultiSopParamsConfig extends NodeParamsConfig { // /** @param url to load the geometry from */ // url = ParamConfig.STRING(`${ASSETS_ROOT}/models/\`@name\`.obj`, { // fileBrowse: {extensions: [FileType.GEOMETRY]}, // expression: {forEntities: true}, // }); // /** @param format */ // format = ParamConfig.STRING(GeometryFormat.AUTO, { // menuString: { // entries: GEOMETRY_FORMATS.map((name) => { // return {name, value: name}; // }), // }, // }); // /** @param reload the geometry */ // reload = ParamConfig.BUTTON(null, { // callback: (node: BaseNodeType) => { // FileMultiSopNode.PARAM_CALLBACK_reload(node as FileMultiSopNode); // }, // }); // } // const ParamsConfig = new FileMultiSopParamsConfig(); // export class FileMultiSopNode extends TypedSopNode<FileMultiSopParamsConfig> { // override paramsConfig = ParamsConfig; // static override type() { // return 'fileMultiOBJ'; // } // 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]; // const points = inputCoreGroup.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(); // await param.expressionController.computeExpressionForPoints(points, (point, url) => { // // 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); // FileMultiSopNode._incrementUrlUsageCount(url, urlUsageCount); // } // }); // uniqueUrls.forEach((url) => { // urls.push(url); // }); // } else { // const url = this.pv.url; // urls.push(url); // FileMultiSopNode._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 (let 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); // const parent = new Group(); // parent.matrixAutoUpdate = false; // parent.name = url; // for (let object of objects) { // parent.add(object); // } // loadedResultByUrl.set(url, parent); // } // private _loadObject(url: string): Promise<Object3D[]> { // const loader = new CoreLoaderGeometry({url: url, format: this.pv.format as GeometryFormat}, this.scene(), this); // return new Promise((resolve) => { // loader.load( // (objects: Object3D[]) => { // const new_objects = this._onLoad(objects); // resolve(new_objects); // }, // (message: string) => { // this._onError(message, url); // } // ); // }); // } // private _onLoad(objects: Object3D[]) { // objects = objects.flat(); // for (let object of objects) { // object.traverse((child) => { // this._ensureGeometryHasIndex(child); // child.matrixAutoUpdate = false; // }); // } // return objects; // } // private _onError(message: string, url: string) { // this.states?.error.set(`could not load geometry from ${url} (${message})`); // } // private _ensureGeometryHasIndex(object: Object3D) { // const mesh = object as Mesh; // const geometry = mesh.geometry; // if (geometry) { // CoreGeometryIndexBuilder.createIndexIfNone(geometry as BufferGeometry); // } // } // static PARAM_CALLBACK_reload(node: FileMultiSopNode) { // 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.setDirty() // } // }