|
@@ -1,5 +1,5 @@
|
|
|
/**
|
|
|
- * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
|
|
+ * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
|
|
*
|
|
|
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
|
|
* @author David Sehnal <david.sehnal@gmail.com>
|
|
@@ -30,9 +30,10 @@ function add3AndScale2(out: Vec3, a: Vec3, b: Vec3, c: Vec3, sa: number, sb: num
|
|
|
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
|
|
|
const v3fromArray = Vec3.fromArray;
|
|
|
const v3normalize = Vec3.normalize;
|
|
|
-const v3negate = Vec3.negate;
|
|
|
-const v3copy = Vec3.copy;
|
|
|
+const v3scaleAndAdd = Vec3.scaleAndAdd;
|
|
|
const v3cross = Vec3.cross;
|
|
|
+const v3dot = Vec3.dot;
|
|
|
+const v3unitX = Vec3.unitX;
|
|
|
const caAdd3 = ChunkedArray.add3;
|
|
|
|
|
|
const CosSinCache = new Map<number, { cos: number[], sin: number[] }>();
|
|
@@ -50,13 +51,16 @@ function getCosSin(radialSegments: number) {
|
|
|
return CosSinCache.get(radialSegments)!;
|
|
|
}
|
|
|
|
|
|
-export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, radialSegments: number, widthValues: ArrayLike<number>, heightValues: ArrayLike<number>, startCap: boolean, endCap: boolean) {
|
|
|
+export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, radialSegments: number, widthValues: ArrayLike<number>, heightValues: ArrayLike<number>, startCap: boolean, endCap: boolean, crossSection: 'elliptical' | 'rounded') {
|
|
|
const { currentGroup, vertices, normals, indices, groups } = state;
|
|
|
|
|
|
let vertexCount = vertices.elementCount;
|
|
|
|
|
|
const { cos, sin } = getCosSin(radialSegments);
|
|
|
|
|
|
+ const q1 = radialSegments / 4;
|
|
|
+ const q3 = q1 * 3;
|
|
|
+
|
|
|
for (let i = 0; i <= linearSegments; ++i) {
|
|
|
const i3 = i * 3;
|
|
|
v3fromArray(u, normalVectors, i3);
|
|
@@ -65,14 +69,18 @@ export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<numbe
|
|
|
|
|
|
const width = widthValues[i];
|
|
|
const height = heightValues[i];
|
|
|
+ const rounded = crossSection === 'rounded' && height > width;
|
|
|
|
|
|
for (let j = 0; j < radialSegments; ++j) {
|
|
|
- add3AndScale2(surfacePoint, u, v, controlPoint, height * cos[j], width * sin[j]);
|
|
|
- if (radialSegments === 2) {
|
|
|
- v3copy(normalVector, v);
|
|
|
- v3normalize(normalVector, normalVector);
|
|
|
- if (j !== 0 || i % 2 === 0) v3negate(normalVector, normalVector);
|
|
|
+ if (rounded) {
|
|
|
+ add3AndScale2(surfacePoint, u, v, controlPoint, width * cos[j], width * sin[j]);
|
|
|
+ const h = v3dot(v, v3unitX) < 0
|
|
|
+ ? (j < q1 || j >= q3) ? height - width : -height + width
|
|
|
+ : (j >= q1 && j < q3) ? -height + width : height - width;
|
|
|
+ v3scaleAndAdd(surfacePoint, surfacePoint, u, h);
|
|
|
+ add2AndScale2(normalVector, u, v, cos[j], sin[j]);
|
|
|
} else {
|
|
|
+ add3AndScale2(surfacePoint, u, v, controlPoint, height * cos[j], width * sin[j]);
|
|
|
add2AndScale2(normalVector, u, v, width * cos[j], height * sin[j]);
|
|
|
}
|
|
|
v3normalize(normalVector, normalVector);
|
|
@@ -82,19 +90,37 @@ export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<numbe
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ const radialSegmentsHalf = Math.round(radialSegments / 2);
|
|
|
+
|
|
|
for (let i = 0; i < linearSegments; ++i) {
|
|
|
- for (let j = 0; j < radialSegments; ++j) {
|
|
|
+ // the triangles are arranged such that opposing triangles of the sheet align
|
|
|
+ // which prevents triangle intersection within tight curves
|
|
|
+ for (let j = 0; j < radialSegmentsHalf; ++j) {
|
|
|
+ caAdd3(
|
|
|
+ indices,
|
|
|
+ vertexCount + i * radialSegments + (j + 1) % radialSegments, // a
|
|
|
+ vertexCount + (i + 1) * radialSegments + (j + 1) % radialSegments, // c
|
|
|
+ vertexCount + i * radialSegments + j // b
|
|
|
+ );
|
|
|
+ caAdd3(
|
|
|
+ indices,
|
|
|
+ vertexCount + (i + 1) * radialSegments + (j + 1) % radialSegments, // c
|
|
|
+ vertexCount + (i + 1) * radialSegments + j, // d
|
|
|
+ vertexCount + i * radialSegments + j // b
|
|
|
+ );
|
|
|
+ }
|
|
|
+ for (let j = radialSegmentsHalf; j < radialSegments; ++j) {
|
|
|
caAdd3(
|
|
|
indices,
|
|
|
- vertexCount + i * radialSegments + (j + 1) % radialSegments,
|
|
|
- vertexCount + (i + 1) * radialSegments + (j + 1) % radialSegments,
|
|
|
- vertexCount + i * radialSegments + j
|
|
|
+ vertexCount + i * radialSegments + (j + 1) % radialSegments, // a
|
|
|
+ vertexCount + (i + 1) * radialSegments + j, // d
|
|
|
+ vertexCount + i * radialSegments + j // b
|
|
|
);
|
|
|
caAdd3(
|
|
|
indices,
|
|
|
- vertexCount + (i + 1) * radialSegments + (j + 1) % radialSegments,
|
|
|
- vertexCount + (i + 1) * radialSegments + j,
|
|
|
- vertexCount + i * radialSegments + j
|
|
|
+ vertexCount + (i + 1) * radialSegments + (j + 1) % radialSegments, // c
|
|
|
+ vertexCount + (i + 1) * radialSegments + j, // d
|
|
|
+ vertexCount + i * radialSegments + (j + 1) % radialSegments, // a
|
|
|
);
|
|
|
}
|
|
|
}
|
|
@@ -111,11 +137,18 @@ export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<numbe
|
|
|
caAdd3(normals, normalVector[0], normalVector[1], normalVector[2]);
|
|
|
|
|
|
const width = widthValues[0];
|
|
|
- const height = heightValues[0];
|
|
|
+ let height = heightValues[0];
|
|
|
+ const rounded = crossSection === 'rounded' && height > width;
|
|
|
+ if (rounded) height -= width;
|
|
|
|
|
|
vertexCount = vertices.elementCount;
|
|
|
for (let i = 0; i < radialSegments; ++i) {
|
|
|
- add3AndScale2(surfacePoint, u, v, controlPoint, height * cos[i], width * sin[i]);
|
|
|
+ if (rounded) {
|
|
|
+ add3AndScale2(surfacePoint, u, v, controlPoint, width * cos[i], width * sin[i]);
|
|
|
+ v3scaleAndAdd(surfacePoint, surfacePoint, u, (i < q1 || i >= q3) ? height : -height);
|
|
|
+ } else {
|
|
|
+ add3AndScale2(surfacePoint, u, v, controlPoint, height * cos[i], width * sin[i]);
|
|
|
+ }
|
|
|
|
|
|
caAdd3(vertices, surfacePoint[0], surfacePoint[1], surfacePoint[2]);
|
|
|
caAdd3(normals, normalVector[0], normalVector[1], normalVector[2]);
|
|
@@ -141,11 +174,18 @@ export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<numbe
|
|
|
caAdd3(normals, normalVector[0], normalVector[1], normalVector[2]);
|
|
|
|
|
|
const width = widthValues[linearSegments];
|
|
|
- const height = heightValues[linearSegments];
|
|
|
+ let height = heightValues[linearSegments];
|
|
|
+ const rounded = crossSection === 'rounded' && height > width;
|
|
|
+ if (rounded) height -= width;
|
|
|
|
|
|
vertexCount = vertices.elementCount;
|
|
|
for (let i = 0; i < radialSegments; ++i) {
|
|
|
- add3AndScale2(surfacePoint, u, v, controlPoint, height * cos[i], width * sin[i]);
|
|
|
+ if (rounded) {
|
|
|
+ add3AndScale2(surfacePoint, u, v, controlPoint, width * cos[i], width * sin[i]);
|
|
|
+ v3scaleAndAdd(surfacePoint, surfacePoint, u, (i < q1 || i >= q3) ? height : -height);
|
|
|
+ } else {
|
|
|
+ add3AndScale2(surfacePoint, u, v, controlPoint, height * cos[i], width * sin[i]);
|
|
|
+ }
|
|
|
|
|
|
caAdd3(vertices, surfacePoint[0], surfacePoint[1], surfacePoint[2]);
|
|
|
caAdd3(normals, normalVector[0], normalVector[1], normalVector[2]);
|