UNPKG

@polygonjs/polygonjs

Version:

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

141 lines (140 loc) 5.2 kB
"use strict"; import { BaseSopOperation } from "./_Base"; import { InputCloneMode } from "../../../engine/poly/InputCloneMode"; import { rotateGeometry } from "../../../core/Transform"; import { isBooleanTrue } from "../../../core/Type"; import { Attribute } from "../../../core/geometry/Attribute"; import { Vector3, Triangle, BufferGeometry, Float32BufferAttribute } from "three"; const DEFAULT_UP = new Vector3(0, 0, 1); const capPositionsCenter0 = new Vector3(); const capCenter0 = new Vector3(); const tmpPos0 = new Vector3(); const tmpPos1 = new Vector3(); const tmpN = new Vector3(); const triangle = new Triangle(); const center = new Vector3(); const position = new Vector3(); const normal = new Vector3(); export class TorusSopOperation extends BaseSopOperation { static type() { return "torus"; } cook(input_contents, params) { const arc = isBooleanTrue(params.open) ? params.arc : Math.PI * 2; const cap = isBooleanTrue(params.open) ? params.cap : false; const radialSegments = Math.floor(params.segmentsRadial); const tubularSegments = Math.floor(params.segmentsTube); const { radius, radiusTube } = params; const indices = []; const positions = []; const normals = []; const uvs = []; const capIndices0 = []; const capIndices1 = []; function setPosition(i, j, position2) { const u = i / tubularSegments * arc; const v = j / radialSegments * Math.PI * 2; position2.x = (radius + radiusTube * Math.cos(v)) * Math.cos(u); position2.y = (radius + radiusTube * Math.cos(v)) * Math.sin(u); position2.z = radiusTube * Math.sin(v); return { u, v }; } for (let j = 0; j <= radialSegments; j++) { for (let i = 0; i <= tubularSegments; i++) { const { u } = setPosition(i, j, position); positions.push(position.x, position.y, position.z); center.x = radius * Math.cos(u); center.y = radius * Math.sin(u); normal.subVectors(position, center).normalize(); normals.push(normal.x, normal.y, normal.z); uvs.push(i / tubularSegments); uvs.push(j / radialSegments); } } if (cap) { let i = 0; for (let j = 0; j <= radialSegments; j++) { setPosition(i, j, position); positions.push(position.x, position.y, position.z); normals.push(-1, -1, -1); capIndices0.push(positions.length / 3 - 1); } i = tubularSegments; for (let j = 0; j <= radialSegments; j++) { setPosition(i, j, position); positions.push(position.x, position.y, position.z); normals.push(-1, -1, -1); capIndices1.push(positions.length / 3 - 1); } } for (let j = 1; j <= radialSegments; j++) { for (let i = 1; i <= tubularSegments; i++) { const a = (tubularSegments + 1) * j + i - 1; const b = (tubularSegments + 1) * (j - 1) + i - 1; const c = (tubularSegments + 1) * (j - 1) + i; const d = (tubularSegments + 1) * j + i; indices.push(a, b, d); indices.push(b, c, d); } } if (cap) { this._addCap(capIndices0, positions, normals, indices, false); this._addCap(capIndices1, positions, normals, indices, true); } const geometry = new BufferGeometry(); geometry.setIndex(indices); geometry.setAttribute(Attribute.POSITION, new Float32BufferAttribute(positions, 3)); geometry.setAttribute(Attribute.NORMAL, new Float32BufferAttribute(normals, 3)); geometry.setAttribute(Attribute.UV, new Float32BufferAttribute(uvs, 2)); rotateGeometry(geometry, DEFAULT_UP, params.direction); geometry.translate(params.center.x, params.center.y, params.center.z); return this.createCoreGroupFromGeometry(geometry); } _addCap(capIndices, positions, normals, indices, invert) { const capPointsCount = capIndices.length; if (capPointsCount <= 2) { return; } capCenter0.set(0, 0, 0); for (let i = 0; i < capPointsCount; i++) { capPositionsCenter0.fromArray(positions, capIndices[i] * 3); capCenter0.add(capPositionsCenter0); } capCenter0.divideScalar(capIndices.length); positions.push(capCenter0.x, capCenter0.y, capCenter0.z); const centerIndex = positions.length / 3 - 1; tmpPos0.fromArray(positions, capIndices[0] * 3); tmpPos1.fromArray(positions, capIndices[1] * 3); triangle.a.copy(tmpPos0); triangle.b.copy(tmpPos1); triangle.c.copy(capCenter0); triangle.getNormal(tmpN); if (invert) { tmpN.multiplyScalar(-1); } normals.push(tmpN.x, tmpN.y, tmpN.z); for (let i = 0; i < capIndices.length - 1; i++) { const a = centerIndex; const b = capIndices[i]; const c = capIndices[i + 1]; if (invert) { indices.push(c, b, a); } else { indices.push(a, b, c); } tmpN.toArray(normals, 3 * b); } } } TorusSopOperation.DEFAULT_PARAMS = { radius: 1, radiusTube: 0.25, segmentsRadial: 20, segmentsTube: 50, open: false, arc: Math.PI * 2, cap: true, direction: new Vector3(0, 1, 0), center: new Vector3(0, 0, 0) }; TorusSopOperation.INPUT_CLONED_STATE = InputCloneMode.FROM_NODE;