UNPKG

@polygonjs/polygonjs

Version:

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

156 lines (155 loc) 6.35 kB
"use strict"; import { BaseSopOperation } from "./_Base"; import { Mesh, BufferGeometry, BufferAttribute, Vector2, Vector3, Vector4 } from "three"; import { ObjectType } from "../../../core/geometry/Constant"; import { InputCloneMode } from "../../../engine/poly/InputCloneMode"; import { rangeWithEnd } from "../../../core/ArrayUtils"; import { isBooleanTrue } from "../../../core/BooleanValue"; import { Attribute } from "../../../core/geometry/Attribute"; import { MeshSurfaceSampler } from "../../../modules/core/math/MeshSurfaceSampler"; import { CoreMath } from "../../../core/math/_Module"; const tmpV2 = new Vector2(); const tmpV4 = new Vector4(); export class ScatterSopOperation extends BaseSopOperation { constructor() { super(...arguments); this._position = new Vector3(); this._normal = new Vector3(); } static type() { return "scatter"; } cook(inputContents, params) { const coreGroup = inputContents[0]; let inputMesh = coreGroup.threejsObjectsWithGeo()[0]; if (inputMesh) { return this._createPoints(inputMesh, coreGroup, params); } else { return this.createCoreGroupFromObjects([]); } } _createPoints(inputMesh, coreGroup, params) { const originalMesh = inputMesh; let inputGeometry = inputMesh.geometry; if (inputGeometry.index) { inputGeometry = inputGeometry.toNonIndexed(); const newMesh = new Mesh(inputGeometry); inputMesh = newMesh; } const transferAttributes = params.transferAttributes; let attribNames = []; if (isBooleanTrue(transferAttributes)) { attribNames = coreGroup.pointAttribNamesMatchingMask(params.attributesToTransfer); } const sampler = new MeshSurfaceSampler(inputMesh, attribNames); if (isBooleanTrue(params.useWeightAttribute)) { const weightAttributeName = params.weightAttribute.trim(); if (weightAttributeName != "") { sampler.setWeightAttribute(weightAttributeName); } } const baseSeed = 2454 * params.seed % Number.MAX_SAFE_INTEGER; sampler.setRandomGenerator((index) => { return CoreMath.randFloat(baseSeed, index); }); sampler.build(); const pointsCount = params.pointsCount; const positions = new Array(pointsCount * 3); const normals = new Array(pointsCount * 3); const additionalVectors = new Array(attribNames.length); const additionalAttribSizes = new Array(attribNames.length); const additionalAttribSizeByName = /* @__PURE__ */ new Map(); const additionalAttribBuffers = new Array(attribNames.length); const additionalAttribBuffersByName = /* @__PURE__ */ new Map(); for (let i = 0; i < attribNames.length; i++) { const attribName = attribNames[i]; additionalVectors[i] = new Vector3(); const attribSize = inputMesh.geometry.getAttribute(attribNames[i]).itemSize; additionalAttribSizeByName.set(attribName, attribSize); additionalAttribSizes[i] = attribSize; additionalAttribBuffers[i] = new Array(pointsCount * attribSize); additionalAttribBuffersByName.set(attribName, additionalAttribBuffers[i]); } const processAdditionalAttributes = attribNames.length > 0; const _position = this._position; const _normal = this._normal; let i3 = 0; for (let i = 0; i < pointsCount; i++) { sampler.sample(i, _position, _normal, additionalVectors); i3 = i * 3; _position.toArray(positions, i3); _normal.toArray(normals, i3); if (processAdditionalAttributes) { let j = 0; for (let additionalVector of additionalVectors) { const array = additionalAttribBuffers[j]; const attribSize = additionalAttribSizes[j]; const arrayIndex = i * attribSize; if (i < pointsCount - 1) { additionalVector.toArray(array, arrayIndex); } else { if (attribSize == 3) { additionalVector.toArray(array, arrayIndex); } else { switch (attribSize) { case 1: { array[arrayIndex] = additionalVector.x; break; } case 2: { tmpV2.set(additionalVector.x, additionalVector.y); tmpV2.toArray(array, arrayIndex); break; } case 4: { tmpV4.set(additionalVector.x, additionalVector.y, additionalVector.z, 0); tmpV4.toArray(array, arrayIndex); break; } } } } j++; } } } const geometry = new BufferGeometry(); geometry.setAttribute(Attribute.POSITION, new BufferAttribute(new Float32Array(positions), 3)); geometry.setAttribute(Attribute.NORMAL, new BufferAttribute(new Float32Array(normals), 3)); for (let attribName of attribNames) { const attribSize = additionalAttribSizeByName.get(attribName); const buffer = additionalAttribBuffersByName.get(attribName); if (attribSize != null && buffer != null) { geometry.setAttribute(attribName, new BufferAttribute(new Float32Array(buffer), attribSize)); } } if (isBooleanTrue(params.addIdAttribute) || isBooleanTrue(params.addIdnAttribute)) { const pointsCount2 = params.pointsCount; const ids = rangeWithEnd(pointsCount2); if (isBooleanTrue(params.addIdAttribute)) { geometry.setAttribute("id", new BufferAttribute(new Float32Array(ids), 1)); } const idns = ids.map((id) => id / (pointsCount2 - 1)); if (isBooleanTrue(params.addIdnAttribute)) { geometry.setAttribute("idn", new BufferAttribute(new Float32Array(idns), 1)); } } const object = this.createObject(geometry, ObjectType.POINTS); object.position.copy(originalMesh.position); object.rotation.copy(originalMesh.rotation); object.scale.copy(originalMesh.scale); object.matrix.copy(originalMesh.matrix); return this.createCoreGroupFromObjects([object]); } } ScatterSopOperation.DEFAULT_PARAMS = { pointsCount: 100, seed: 0, useWeightAttribute: false, weightAttribute: "", transferAttributes: false, attributesToTransfer: "", addIdAttribute: true, addIdnAttribute: true }; ScatterSopOperation.INPUT_CLONED_STATE = InputCloneMode.FROM_NODE;