I am trying to do programmatic mesh generation, where i provide a parallel source and target list of points and then mesh between them (like to make a wall, where floor perimeter is source, and target is ceiling perimeter, equal number of points for both).
For example, I am testing with less than 50 verts such as:
source = [(0,0), (1,0), (2,0)]
target = [(0,1), (1,1), (2,1)]
I can do this via βtriangle soupβ - where I make each triangle completely separately:
static getGeometryFilledQuadsSharp(
originV3: Vector3[],
targetV3: Vector3[],
originUV: Vector2[], // full ref uv at source points
targetUV: Vector2[], // full ref uv at target points
ccw = true) {
let geometry = new BufferGeometry();
//βββββββββββββββββββββββββββ
// DEFINE BUFFERS
//βββββββββββββββββββββββββββ
let posArray = new Float32Array(originV3.length * 18); // 6 verts x3 (x,y,z)
let uvArray = new Float32Array(originV3.length * 12); // 6 verts Γ 2 (u,v)
//βββββββββββββββββββββββββββ
// MAIN LOOP
//βββββββββββββββββββββββββββ
for (let i = 0; i < originV3.length; i++) {
let iPlusOne = i + 1;
if (iPlusOne > originV3.length - 1) iPlusOne = 0;
let v1Index = iPlusOne;
let k = i * 18;
//βββββββββββββββββββββββββββ
// TRIANGLE POSITIONS
//βββββββββββββββββββββββββββ
// triangle A: (origin[i], target[v1Index], origin[v1Index])
posArray[k] = originV3[i].x; k++;
posArray[k] = originV3[i].y; k++;
posArray[k] = originV3[i].z; k++;
posArray[k] = targetV3[v1Index].x; k++;
posArray[k] = targetV3[v1Index].y; k++;
posArray[k] = targetV3[v1Index].z; k++;
posArray[k] = originV3[v1Index].x; k++;
posArray[k] = originV3[v1Index].y; k++;
posArray[k] = originV3[v1Index].z; k++;
// triangle B: (origin[i], target[v1Index], origin[v1Index])
posArray[k] = targetV3[i].x; k++;
posArray[k] = targetV3[i].y; k++;
posArray[k] = targetV3[i].z; k++;
posArray[k] = targetV3[v1Index].x; k++;
posArray[k] = targetV3[v1Index].y; k++;
posArray[k] = targetV3[v1Index].z; k++;
posArray[k] = originV3[i].x; k++;
posArray[k] = originV3[i].y; k++;
posArray[k] = originV3[i].z; k++;
//βββββββββββββββββββββββββββ
// UVs
//βββββββββββββββββββββββββββ
let u = i * 12; // 6 points * 2
// allocate uv across 6 vertices
// Triangle A: (origin[i], target[v1Index], origin[v1Index])
uvArray[u++] = originUV[i].x;
uvArray[u++] = originUV[i].y;
uvArray[u++] = targetUV[v1Index].x;
uvArray[u++] = targetUV[v1Index].y;
uvArray[u++] = originUV[v1Index].x;
uvArray[u++] = originUV[v1Index].y;
// Triangle B: (target[i], target[v1Index], origin[i])
uvArray[u++] = targetUV[i].x;
uvArray[u++] = targetUV[i].y;
uvArray[u++] = targetUV[v1Index].x;
uvArray[u++] = targetUV[v1Index].y;
uvArray[u++] = originUV[i].x;
uvArray[u++] = originUV[i].y;
}
//βββββββββββββββββββββββββββ
// SET RESULTS
//βββββββββββββββββββββββββββ
geometry.setAttribute('position', new BufferAttribute(posArray, 3));
geometry.setAttribute('uv', new BufferAttribute(uvArray, 2));
// merge it manually then
//geometry = BufferGeometryUtils.mergeVertices(geometry);
// return
geometry.computeVertexNormals();
return geometry;
}
This works fine. Because every triangle is distinct, the wall is fully visible. But as soon as I merge the vertexes (so I can get true smoothing) it destroys half the triangles. So they are not merging properly.
I have tried also drawing them from scratch via indexed verts but this doesnβt work either - only half the triangles at most are visible (even with two sided material, or mesh material):
static getGeometryFilledQuadsSmooth(
originV3: Vector3[],
targetV3: Vector3[],
originUV: Vector2[],
targetUV: Vector2[],
ccw = true
): BufferGeometry {
const g = new BufferGeometry();
const N = originV3.length;
const pos = new Float32Array(N * 2 * 3);
const uv = new Float32Array(N * 2 * 2);
const idx = new Uint16Array(N * 6);
// vertices: [origin ring, target ring, origin ...],
for (let i = 0; i < N; i++) {
// interleave, so order of points is:
// => origin_0x3, target_0x3, origin_1x3, target_1x3, etc..
const oVert = 2 * i;
const tVert = 2 * i + 1;
// origin pos
let o3 = oVert * 3;
pos[o3 + 0] = originV3[i].x;
pos[o3 + 1] = originV3[i].y;
pos[o3 + 2] = originV3[i].z;
// target pos
let t3 = tVert * 3;
pos[t3 + 0] = targetV3[i].x;
pos[t3 + 1] = targetV3[i].y;
pos[t3 + 2] = targetV3[i].z;
// origin uv
let o2 = oVert * 2;
uv[o2 + 0] = originUV[i].x;
uv[o2 + 1] = originUV[i].y;
// target uv
let t2 = tVert * 2;
uv[t2 + 0] = targetUV[i].x;
uv[t2 + 1] = targetUV[i].y;
}
// DRAW FROM INDICES: connect i -> j (next) between rings
for (let i = 0; i < N; i++) {
const j = (i + 1) % N;
const origin_i = 2 * i;
const target_i = 2 * i + 1;
const origin_j = 2 * j;
const target_j = 2 * j + 1;
const k = i * 6;
if (ccw) {
// tri A: (origin_i, origin_j, target_j)
idx[k + 0] = origin_i;
idx[k + 1] = origin_j;
idx[k + 2] = target_j;
// tri B: (origin_i, target_j, target_i)
idx[k + 3] = origin_i;
idx[k + 4] = target_j;
idx[k + 5] = target_i;
} else {
// flipped winding
idx[k + 0] = origin_i;
idx[k + 1] = target_j;
idx[k + 2] = origin_j;
idx[k + 3] = origin_i;
idx[k + 4] = target_i;
idx[k + 5] = target_j;
}
}
g.setAttribute("position", new BufferAttribute(pos, 3));
g.setAttribute("uv", new BufferAttribute(uv, 2));
g.setIndex(new Uint16BufferAttribute(idx, 1));
g.computeVertexNormals();
g.computeBoundingSphere();
g.computeBoundingBox();
return g;
}
I cannot figure out what the trick is. I asked ChatGPT and it cannot tell me what is wrong here.
I assume thereβs some magic way to draw a wall or fill these quads in ThreeJs but I canβt find it.
Any help? Thanks.