@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
141 lines (131 loc) • 4.77 kB
text/typescript
/**
* Reads an attribute from a geometry and fills a texture with the values.
*
* @remarks
*
* This can be used to lookup geometry attributes inside a shader.
*
*
*/
import {TypedCopNode} from './_Base';
import {DataTexture, Mesh, Object3D, Texture} from 'three';
import {NodeParamsConfig, ParamConfig} from '../utils/params/ParamsConfig';
import {NodeContext} from '../../poly/NodeContext';
import {CoreMask} from '../../../core/geometry/Mask';
import {
textureFromAttributes,
// textureFromAttributesMissingAttributes,
textureFromAttributesTotalAttribSizes,
} from '../../../core/geometry/operation/TextureFromAttribute';
import {CoreAttribute} from '../../../core/geometry/Attribute';
import {corePointClassFactory} from '../../../core/geometry/CoreObjectFactory';
class GeometryAttributeCopParamsConfig extends NodeParamsConfig {
/** @param sop node */
node = ParamConfig.NODE_PATH('', {
nodeSelection: {
context: NodeContext.SOP,
},
});
/** @param group to read the material from */
group = ParamConfig.STRING('', {
objectMask: true,
});
/** @param attribute name */
attribute = ParamConfig.STRING('P');
}
const ParamsConfig = new GeometryAttributeCopParamsConfig();
export class GeometryAttributeCopNode extends TypedCopNode<GeometryAttributeCopParamsConfig> {
override paramsConfig = ParamsConfig;
static override type(): 'geometryAttribute' {
return 'geometryAttribute';
}
override async cook() {
const geometryNode = this.pv.node.nodeWithContext(NodeContext.SOP, this.states.error);
if (!geometryNode) {
this.states.error.set(`node not found at path '${this.pv.node.path()}'`);
return;
}
const container = await geometryNode.compute();
const coreGroup = container.coreContent();
if (!coreGroup) {
this.states.error.set(`geometry invalid`);
return;
}
const selectedObjects = CoreMask.filterThreejsObjects(coreGroup, this.pv);
if (selectedObjects.length == 0) {
this.states.error.set(`no object matching group`);
return;
}
let texture: Texture | undefined;
for (const selectedObject of selectedObjects) {
const geometry = (selectedObject as Mesh).geometry;
if (geometry) {
texture = texture || this._textureFromGeometry(selectedObject);
}
}
if (!texture) {
this.cookController.endCook();
return;
}
this.setTexture(texture);
}
private _textureFromGeometry(object: Object3D): DataTexture | undefined {
const corePointClass = corePointClassFactory(object);
const geoAttribNames = corePointClass.attributeNames(object);
const attribNames = CoreAttribute.attribNamesMatchingMask(this.pv.attribute, geoAttribNames);
const geometry = (object as Mesh).geometry;
if (!geometry) {
return;
}
// the missing attributes is currently not working, since we use a mask check first
// const missingAttributes = textureFromAttributesMissingAttributes(geometry, attribNames);
// if (missingAttributes.length > 0) {
// this.states.error.set(`missing attributes: ${missingAttributes.join(',')}`);
// return;
// }
const totalSize = textureFromAttributesTotalAttribSizes(geometry, attribNames);
if (totalSize > 4) {
this.states.error.set(`total size of attributes is ${totalSize}, but maximum is 4`);
return;
}
return textureFromAttributes(geometry, attribNames);
// const position = geometry.attributes.position;
// if (!(position instanceof BufferAttribute)) {
// this.states.error.set('position is not a BufferAttribute');
// return;
// }
// const pointsCount = position.count;
// const attributeName = CoreAttribute.remapName(this.pv.attribute);
// const attribute = geometry.getAttribute(attributeName) as BufferAttribute;
// if (!attribute) {
// this.states.error.set(`attribute '${attributeName}' not found`);
// return;
// }
// const attribSize = attribute.itemSize;
// textureFromAttributeSize(geometry, textureSize);
// const width = textureSize.x;
// const height = textureSize.y;
// const size = width * height * 4;
// const pixelBuffer = new Float32Array(size);
// const attribArray = attribute.array as number[];
// if (attribSize == 1) {
// for (let i = 0; i < pointsCount; i++) {
// pixelBuffer[i * 4] = attribArray[i];
// }
// } else {
// const vector = _vectorFromAttribSize(attribSize);
// if (!vector) {
// return;
// }
// for (let i = 0; i < pointsCount; i++) {
// vector.fromArray(attribArray, i * attribSize);
// vector.toArray(pixelBuffer, i * 4);
// }
// }
// const texture = new DataTexture(pixelBuffer, width, height, RGBAFormat, FloatType);
// texture.minFilter = texture.magFilter = NearestFilter;
// texture.wrapS = texture.wrapT = RepeatWrapping;
// texture.needsUpdate = true;
// return texture;
}
}