Skip to content

Commit c7fd306

Browse files
committed
Fix textToModel extrusion and degenerate face issues
1 parent 652dc02 commit c7fd306

File tree

1 file changed

+112
-45
lines changed

1 file changed

+112
-45
lines changed

src/type/p5.Font.js

Lines changed: 112 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -542,63 +542,130 @@ export class Font {
542542
textToModel(str, x, y, width, height, options) {
543543
({ width, height, options } = this._parseArgs(width, height, options));
544544
const extrude = options?.extrude || 0;
545-
const contours = this.textToContours(str, x, y, width, height, options);
545+
// Step 1: generate glyph contours
546+
let contours = this.textToContours(str, x, y, width, height, options);
547+
if (!Array.isArray(contours[0][0])) {
548+
contours = [contours];
549+
}
546550

551+
// Step 2: build base flat geometry
547552
const geom = this._pInst.buildGeometry(() => {
548-
if (extrude === 0) {
549-
const prevValidateFaces = this._pInst._renderer._validateFaces;
550-
this._pInst._renderer._validateFaces = true;
553+
const prevValidateFaces = this._pInst._renderer._validateFaces;
554+
this._pInst._renderer._validateFaces = true;
555+
556+
contours.forEach(glyphContours => {
551557
this._pInst.beginShape();
552-
this._pInst.normal(0, 0, 1);
553-
for (const contour of contours) {
558+
const outer = glyphContours[0];
559+
outer.forEach(({ x, y }) => this._pInst.vertex(x, y, 0));
560+
561+
for (let i = 1; i < glyphContours.length; i++) {
554562
this._pInst.beginContour();
555-
for (const { x, y } of contour) {
556-
this._pInst.vertex(x, y);
557-
}
563+
glyphContours[i].forEach(({ x, y }) => this._pInst.vertex(x, y, 0));
558564
this._pInst.endContour(this._pInst.CLOSE);
559565
}
560-
this._pInst.endShape();
561-
this._pInst._renderer._validateFaces = prevValidateFaces;
566+
567+
this._pInst.endShape(this._pInst.CLOSE);
568+
});
569+
570+
this._pInst._renderer._validateFaces = prevValidateFaces;
571+
});
572+
573+
if (extrude === 0) {
574+
console.log('No extrusion');
575+
return geom;
576+
}
577+
578+
// Step 3: Create extruded geometry with UNSHARED vertices for flat shading
579+
const extruded = this._pInst.buildGeometry(() => {});
580+
const half = extrude * 0.5;
581+
582+
extruded.vertices = [];
583+
extruded.vertexNormals = [];
584+
extruded.faces = [];
585+
586+
let vertexIndex = 0;
587+
588+
// Helper to add a triangle with flat normal
589+
const addTriangle = (v0, v1, v2) => {
590+
const edge1 = p5.Vector.sub(v1, v0);
591+
const edge2 = p5.Vector.sub(v2, v0);
592+
const normal = p5.Vector.cross(edge1, edge2);
593+
if (normal.magSq() > 0.0001) {
594+
normal.normalize();
562595
} else {
563-
const prevValidateFaces = this._pInst._renderer._validateFaces;
564-
this._pInst._renderer._validateFaces = true;
565-
566-
// Draw front faces
567-
for (const side of [1, -1]) {
568-
this._pInst.beginShape();
569-
for (const contour of contours) {
570-
this._pInst.beginContour();
571-
for (const { x, y } of contour) {
572-
this._pInst.vertex(x, y, side * extrude * 0.5);
573-
}
574-
this._pInst.endContour(this._pInst.CLOSE);
575-
}
576-
this._pInst.endShape();
577-
}
578-
this._pInst._renderer._validateFaces = prevValidateFaces;
579-
580-
// Draw sides
581-
for (const contour of contours) {
582-
this._pInst.beginShape(this._pInst.QUAD_STRIP);
583-
for (const v of contour) {
584-
for (const side of [-1, 1]) {
585-
this._pInst.vertex(v.x, v.y, side * extrude * 0.5);
586-
}
587-
}
588-
this._pInst.endShape();
589-
}
596+
normal.set(0, 0, 1);
590597
}
591-
});
592-
if (extrude !== 0) {
593-
geom.computeNormals();
598+
599+
// Add vertices (unshared - each triangle gets its own copies)
600+
extruded.vertices.push(v0.copy(), v1.copy(), v2.copy());
601+
extruded.vertexNormals.push(normal.copy(), normal.copy(), normal.copy());
602+
extruded.faces.push([vertexIndex, vertexIndex + 1, vertexIndex + 2]);
603+
vertexIndex += 3;
604+
};
605+
606+
for (const face of geom.faces) {
607+
if (face.length < 3) continue;
608+
const v0 = geom.vertices[face[0]];
609+
for (let i = 1; i < face.length - 1; i++) {
610+
const v1 = geom.vertices[face[i]];
611+
const v2 = geom.vertices[face[i + 1]];
612+
addTriangle(
613+
new p5.Vector(v0.x, v0.y, v0.z + half),
614+
new p5.Vector(v1.x, v1.y, v1.z + half),
615+
new p5.Vector(v2.x, v2.y, v2.z + half)
616+
);
617+
}
618+
}
619+
620+
for (const face of geom.faces) {
621+
if (face.length < 3) continue;
622+
const v0 = geom.vertices[face[0]];
623+
for (let i = 1; i < face.length - 1; i++) {
624+
const v1 = geom.vertices[face[i]];
625+
const v2 = geom.vertices[face[i + 1]];
626+
addTriangle(
627+
new p5.Vector(v0.x, v0.y, v0.z - half),
628+
new p5.Vector(v2.x, v2.y, v2.z - half),
629+
new p5.Vector(v1.x, v1.y, v1.z - half)
630+
);
631+
}
632+
}
633+
634+
// Side faces from edges
635+
let edges = geom.edges;
636+
if (!edges || !Array.isArray(edges)) {
637+
edges = [];
638+
const edgeSet = new Set();
594639
for (const face of geom.faces) {
595-
if (face.every(idx => geom.vertices[idx].z <= -extrude * 0.5 + 0.1 || geom.vertices[idx].z >= extrude * 0.5 - 0.1)) {
596-
for (const idx of face) geom.vertexNormals[idx].set(0, 0, -1);
597-
face.reverse();
640+
for (let i = 0; i < face.length; i++) {
641+
const a = face[i];
642+
const b = face[(i + 1) % face.length];
643+
if (a === b) continue;
644+
const key = a < b ? `${a},${b}` : `${b},${a}`;
645+
if (!edgeSet.has(key)) {
646+
edgeSet.add(key);
647+
edges.push([a, b]);
648+
}
598649
}
599650
}
600651
}
601-
return geom;
652+
653+
const validEdges = edges.filter(([a, b]) => a !== b);
654+
655+
for (const [a, b] of validEdges) {
656+
const v0 = geom.vertices[a];
657+
const v1 = geom.vertices[b];
658+
659+
const vFront0 = new p5.Vector(v0.x, v0.y, v0.z + half);
660+
const vFront1 = new p5.Vector(v1.x, v1.y, v1.z + half);
661+
const vBack0 = new p5.Vector(v0.x, v0.y, v0.z - half);
662+
const vBack1 = new p5.Vector(v1.x, v1.y, v1.z - half);
663+
664+
// Two triangles forming the side quad
665+
addTriangle(vFront0, vFront1, vBack1);
666+
addTriangle(vFront0, vBack1, vBack0);
667+
}
668+
return extruded;
602669
}
603670

604671
variations() {

0 commit comments

Comments
 (0)