UNPKG

@polygonjs/polygonjs

Version:

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

113 lines (112 loc) 4.38 kB
"use strict"; import { Triangle } from "three"; const _face = new Triangle(); export class MeshSurfaceSampler { constructor(mesh, additionalAttributeNames) { this.additionalAttributeNames = additionalAttributeNames; this.randomFunction = Math.random; this.additionalAttributes = /* @__PURE__ */ new Map(); this.weightAttribute = null; this.distribution = null; let geometry = mesh.geometry; if (!geometry.isBufferGeometry || geometry.attributes.position.itemSize !== 3) { throw new Error("THREE.MeshSurfaceSampler: Requires BufferGeometry triangle mesh."); } if (geometry.index) { console.warn("THREE.MeshSurfaceSampler: Converting geometry to non-indexed BufferGeometry."); geometry = geometry.toNonIndexed(); } this.geometry = geometry; this.positionAttribute = this.geometry.getAttribute("position"); for (let attribName of additionalAttributeNames) { const attribute = this.geometry.getAttribute(attribName); if (attribute) { this.additionalAttributes.set(attribName, attribute); } } } setWeightAttribute(name) { this.weightAttribute = name ? this.geometry.getAttribute(name) : null; return this; } build() { const positionAttribute = this.positionAttribute; const weightAttribute = this.weightAttribute; const faceWeights = new Float32Array(positionAttribute.count / 3); for (let i = 0; i < positionAttribute.count; i += 3) { let faceWeight = 1; if (weightAttribute) { faceWeight = weightAttribute.getX(i) + weightAttribute.getX(i + 1) + weightAttribute.getX(i + 2); } _face.a.fromBufferAttribute(positionAttribute, i); _face.b.fromBufferAttribute(positionAttribute, i + 1); _face.c.fromBufferAttribute(positionAttribute, i + 2); faceWeight *= _face.getArea(); faceWeights[i / 3] = faceWeight; } this.distribution = new Float32Array(positionAttribute.count / 3); let cumulativeTotal = 0; for (let i = 0; i < faceWeights.length; i++) { cumulativeTotal += faceWeights[i]; this.distribution[i] = cumulativeTotal; } return this; } setRandomGenerator(randomFunction) { this.randomFunction = randomFunction; return this; } sample(index, targetPosition, targetNormal, targetAdditionalVectors) { const cumulativeTotal = this.distribution[this.distribution.length - 1]; const faceIndex = this.binarySearch(this.randomFunction(index) * cumulativeTotal); return this.sampleFace(index, faceIndex, targetPosition, targetNormal, targetAdditionalVectors); } binarySearch(x) { const dist = this.distribution; let start = 0; let end = dist.length - 1; let index = -1; while (start <= end) { const mid = Math.ceil((start + end) / 2); if (mid === 0 || dist[mid - 1] <= x && dist[mid] > x) { index = mid; break; } else if (x < dist[mid]) { end = mid - 1; } else { start = mid + 1; } } return index; } sampleFace(i, faceIndex, targetPosition, targetNormal, targetAdditionalVectors) { let u = this.randomFunction(i * (faceIndex + 1456)); let v = this.randomFunction((i + 9851) * (faceIndex + 7646)); if (u + v > 1) { u = 1 - u; v = 1 - v; } _face.a.fromBufferAttribute(this.positionAttribute, faceIndex * 3); _face.b.fromBufferAttribute(this.positionAttribute, faceIndex * 3 + 1); _face.c.fromBufferAttribute(this.positionAttribute, faceIndex * 3 + 2); targetPosition.set(0, 0, 0).addScaledVector(_face.a, u).addScaledVector(_face.b, v).addScaledVector(_face.c, 1 - (u + v)); if (targetNormal !== void 0) { _face.getNormal(targetNormal); } if (targetAdditionalVectors) { let i2 = 0; for (let attribName of this.additionalAttributeNames) { const attrib = this.additionalAttributes.get(attribName); if (attrib) { _face.a.fromBufferAttribute(attrib, faceIndex * 3); _face.b.fromBufferAttribute(attrib, faceIndex * 3 + 1); _face.c.fromBufferAttribute(attrib, faceIndex * 3 + 2); const targetVector = targetAdditionalVectors[i2]; targetVector.set(0, 0, 0).addScaledVector(_face.a, u).addScaledVector(_face.b, v).addScaledVector(_face.c, 1 - (u + v)); } i2++; } } return this; } }