UNPKG

@polygonjs/polygonjs

Version:

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

184 lines (167 loc) 5.88 kB
import {BaseSopOperation} from './_Base'; import {CoreGroup} from '../../../core/geometry/Group'; import { BufferGeometry, CatmullRomCurve3, Float32BufferAttribute, Object3D, Vector3, Vector2, BufferAttribute, } from 'three'; import {InputCloneMode} from '../../poly/InputCloneMode'; import {DefaultOperationParams} from '../../../core/operations/_Base'; import {ObjectType} from '../../../core/geometry/Constant'; import {SPLINE_CURVE_TYPES} from '../../../core/geometry/Curve'; import {ObjectUserData} from '../../../core/UserData'; import {CoreObjectType, ObjectContent} from '../../../core/geometry/ObjectContent'; import {pointsFromObject} from '../../../core/geometry/entities/point/CorePointUtils'; import {corePointClassFactory} from '../../../core/geometry/CoreObjectFactory'; import {CorePoint} from '../../../core/geometry/entities/point/CorePoint'; interface CurveFromPointsSopParams extends DefaultOperationParams { pointsCount: number; closed: boolean; curveType: number; tension: number; tTangent: boolean; tangentName: string; // attributesToInterpolate: string; } const tmpV2 = new Vector2(); const current = new Vector3(); const next = new Vector3(); const _points: CorePoint<CoreObjectType>[] = []; export class CurveFromPointsSopOperation extends BaseSopOperation { static override readonly DEFAULT_PARAMS: CurveFromPointsSopParams = { pointsCount: 100, closed: false, curveType: 0, tension: 0.5, tTangent: false, tangentName: 'tangent', // attributesToInterpolate: '', }; static override readonly INPUT_CLONED_STATE = InputCloneMode.NEVER; static override type(): Readonly<'curveFromPoint'> { return 'curveFromPoint'; } override cook(inputCoreGroups: CoreGroup[], params: CurveFromPointsSopParams) { const inputCoreGroup = inputCoreGroups[0]; const objects = inputCoreGroup.allObjects(); const newObjects: Object3D[] = []; for (let object of objects) { const newObject = this._createCurveFromCoreObject(object, params); if (newObject) { newObjects.push(newObject); } } return this.createCoreGroupFromObjects(newObjects); } private _createCurveFromCoreObject<T extends CoreObjectType>( object: ObjectContent<T>, params: CurveFromPointsSopParams ) { const {pointsCount, closed, curveType, tension, tTangent} = params; // const coreGeo = coreObject.coreGeometry(); // if (!coreGeo) { // return; // } const corePointClass = corePointClassFactory(object); pointsFromObject(object, _points); const pointPositions = _points.map((p) => p.position(new Vector3())); if (pointPositions.length < 2) { return; } const curveTypeName = SPLINE_CURVE_TYPES[curveType]; const curve = new CatmullRomCurve3(pointPositions, closed, curveTypeName, tension); // const positions: number[] = new Array(pointsCount * 3); // create indices const indices: number[] = new Array(pointsCount); for (let i = 0; i < pointsCount; i++) { // const t = i / (pointsCount - 1); // curve.getPoint(t, this._current); // this._current.toArray(positions, i * 3); if (i > 0) { indices[(i - 1) * 2] = i - 1; indices[(i - 1) * 2 + 1] = i; } } // create geo const geometry = new BufferGeometry(); // geometry.setAttribute('position', new Float32BufferAttribute(positions, 3)); geometry.setIndex(indices); // attributes (position + attributesToInterpolate) const attribNamesToInterpolate = ['position']; //.concat( // coreGeo.attribNamesMatchingMask(params.attributesToInterpolate) // ); for (const attribName of attribNamesToInterpolate) { const attribSize = corePointClass.attribSize(object, attribName); let attribPositions: Vector3[] = []; switch (attribSize) { case 1: { attribPositions = _points.map((p) => new Vector3(p.attribValue(attribName) as number, 0, 0)); break; } case 2: { attribPositions = _points.map((p) => { p.attribValue(attribName, tmpV2); return new Vector3(tmpV2.x, tmpV2.y, 0); }); break; } case 3: { attribPositions = _points.map((p) => { p.attribValue(attribName, current); return current.clone(); }); break; } } const curveTypeName = SPLINE_CURVE_TYPES[curveType]; const curve = new CatmullRomCurve3(attribPositions, closed, curveTypeName, tension); const attribValues: number[] = new Array(pointsCount * attribSize); for (let i = 0; i < pointsCount; i++) { const t = i / (pointsCount - 1); curve.getPoint(t, current); switch (attribSize) { case 1: { attribValues[i] = current.x; break; } case 2: { attribValues[2 * i] = current.x; attribValues[2 * i + 1] = current.y; break; } case 3: { current.toArray(attribValues, i * 3); break; } } } geometry.setAttribute(attribName, new Float32BufferAttribute(attribValues, attribSize)); } // compute tangent if (tTangent) { const positions = (geometry.getAttribute('position') as BufferAttribute).array; const tangentName = params.tangentName; const tangents: number[] = new Array(pointsCount * 3); for (let i = 0; i < pointsCount - 1; i++) { current.fromArray(positions, i * 3); next.fromArray(positions, (i + 1) * 3); next.sub(current).normalize(); next.toArray(tangents, i * 3); } // and last current.fromArray(positions, (pointsCount - 1) * 3); next.fromArray(positions, (pointsCount - 2) * 3); current.sub(next).normalize(); next.toArray(tangents, (pointsCount - 1) * 3); geometry.setAttribute(tangentName, new Float32BufferAttribute(tangents, 3)); } // add curve as userData to the object, to make it possible to use the curve from actor nodes const newObject = BaseSopOperation.createObject(geometry, ObjectType.LINE_SEGMENTS); newObject.userData[ObjectUserData.PATH] = curve; return newObject; } }