@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
141 lines (140 loc) • 5.2 kB
JavaScript
"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;