UNPKG

@polygonjs/polygonjs

Version:

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

186 lines (164 loc) 6.47 kB
import {BaseSopOperation} from './_Base'; import {CoreGroup} from '../../../core/geometry/Group'; import {Vector3, Matrix4, LineSegments, BufferGeometry, Object3D} from 'three'; import {ObjectType} from '../../../core/geometry/Constant'; import {CircleCreateOptions, CoreGeometryUtilCircle} from '../../../core/geometry/util/Circle'; import {CoreGeometryUtilCurve} from '../../../core/geometry/util/Curve'; import {CoreGeometryOperationSkin} from '../../../core/geometry/operation/Skin'; import {BaseCorePoint, CorePoint} from '../../../core/geometry/entities/point/CorePoint'; import {isBooleanTrue} from '../../../core/Type'; import {DefaultOperationParams} from '../../../core/operations/_Base'; import {CoreGeometryBuilderMerge} from '../../../core/geometry/modules/three/builders/Merge'; import {addAttributesFromPoint} from '../../../core/geometry/util/addAttributesFromPoint'; import {pointsFromObject} from '../../../core/geometry/entities/point/CorePointUtils'; import {corePointClassFactory} from '../../../core/geometry/CoreObjectFactory'; import {copyObject3DProperties} from '../../../core/geometry/modules/three/ThreejsObjectUtils'; import {CoreObjectType} from '../../../core/geometry/ObjectContent'; import {SopType} from '../../poly/registers/nodes/types/Sop'; interface PolywireSopParams extends DefaultOperationParams { radius: number; segmentsRadial: number; closed: boolean; attributesToCopy: string; } const ORIGIN = new Vector3(0, 0, 0); const UP = new Vector3(0, 1, 0); const lookAtMat = new Matrix4(); const translateMat = new Matrix4(); const currentPos = new Vector3(); const prevPos = new Vector3(); const nextPos = new Vector3(); const delta = new Vector3(); const deltaNext = new Vector3(); const deltaPrev = new Vector3(); const _points: CorePoint<CoreObjectType>[] = []; export class PolywireSopOperation extends BaseSopOperation { static override readonly DEFAULT_PARAMS: PolywireSopParams = { radius: 1, segmentsRadial: 8, closed: true, attributesToCopy: '*', }; static override type(): Readonly<SopType.POLYWIRE> { return SopType.POLYWIRE; } override cook(inputCoreGroups: CoreGroup[], params: PolywireSopParams) { const coreGroup = inputCoreGroups[0]; const inputObjects = coreGroup.threejsObjects(); const newObjects: Object3D[] = []; for (const inputObject of inputObjects) { const geometries: BufferGeometry[] = []; if ((inputObject as LineSegments).isLineSegments) { this._createTube(inputObject as LineSegments, params, geometries); } const mergedGeometry = CoreGeometryBuilderMerge.merge(geometries); if (mergedGeometry) { const newObject = this.createObject(mergedGeometry, ObjectType.MESH); copyObject3DProperties(inputObject, newObject); newObjects.push(newObject); } } // const mergedGeometry = CoreGeometryBuilderMerge.merge(geometries); // for (const geometry of geometries) { // geometry.dispose(); // } // if (mergedGeometry) { // const object = this.createObject(mergedGeometry, ObjectType.MESH); return this.createCoreGroupFromObjects(newObjects); // } else { // return this.createCoreGroupFromObjects([]); // } } private _createTube(lineSegment: LineSegments, params: PolywireSopParams, geometries: BufferGeometry[]) { const geometry = lineSegment.geometry as BufferGeometry; const corePointClass = corePointClassFactory(lineSegment); const attributeNames = corePointClass.attributeNamesMatchingMask(lineSegment, params.attributesToCopy); pointsFromObject(lineSegment, _points); const indices = geometry.getIndex()?.array; if (!indices) { return; } const accumulatedCurvePointIndices = CoreGeometryUtilCurve.accumulatedCurvePointIndices(indices); for (let curvePointIndices of accumulatedCurvePointIndices) { const currentPoints = curvePointIndices.map((index) => _points[index]); this._createTubeFromPoints(currentPoints, attributeNames, params, geometries); } } private _createTubeFromPoints( points: BaseCorePoint[], attributeNames: string[], params: PolywireSopParams, geometries: BufferGeometry[] ) { if (points.length <= 1) { return; } const options: CircleCreateOptions = { radius: params.radius, segments: params.segmentsRadial, arcAngle: 360, connectLastPoint: true, }; const circleTemplate = CoreGeometryUtilCircle.create(options); const circles: BufferGeometry[] = []; // const scale = 1; let i = 0; for (let point of points) { point.position(currentPos); let prevPoint = points[i - 1]; let nextPoint = points[i + 1]; if (prevPoint && nextPoint) { // if we have both prev and next points, the dir is a blend between the dir to the prev and to the next nextPoint.position(nextPos); deltaNext.copy(nextPos).sub(currentPos); prevPoint.position(prevPos); deltaPrev.copy(prevPos).sub(currentPos).multiplyScalar(-1); delta.lerpVectors(deltaNext, deltaPrev, 0.5); } else { if (nextPoint) { nextPoint.position(nextPos); delta.copy(nextPos).sub(currentPos); } else { nextPoint = points[i - 1]; nextPoint.position(nextPos); delta.copy(nextPos).sub(currentPos).multiplyScalar(-1); } } lookAtMat.identity(); lookAtMat.lookAt(ORIGIN, delta.multiplyScalar(-1), UP); translateMat.identity(); translateMat.makeTranslation(currentPos.x, currentPos.y, currentPos.z); const newCircle = circleTemplate.clone(); newCircle.applyMatrix4(lookAtMat); newCircle.applyMatrix4(translateMat); // remove position before transfering the attribute const positionIndex = attributeNames.indexOf('position'); if (positionIndex >= 0) { attributeNames.splice(positionIndex, 1); } addAttributesFromPoint(newCircle, point, attributeNames); circles.push(newCircle); i++; } for (let i = 0; i < circles.length; i++) { if (i > 0) { const circle = circles[i]; const prevCircle = circles[i - 1]; const geometry = this._skin(prevCircle, circle); geometries.push(geometry); } } if (isBooleanTrue(params.closed)) { const circle = circles[0]; const prevCircle = circles[circles.length - 1]; const geometry = this._skin(prevCircle, circle); geometries.push(geometry); } } _skin(geometry1: BufferGeometry, geometry0: BufferGeometry) { const geometry = new BufferGeometry(); const operation = new CoreGeometryOperationSkin(geometry, geometry1, geometry0); operation.process(); return geometry; } }