Skip to content

Commit 3e567bf

Browse files
committed
Fix textToModel extrusion and degenerate face issues
1 parent 9a7e956 commit 3e567bf

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
@@ -541,63 +541,130 @@ export class Font {
541541
textToModel(str, x, y, width, height, options) {
542542
({ width, height, options } = this._parseArgs(width, height, options));
543543
const extrude = options?.extrude || 0;
544-
const contours = this.textToContours(str, x, y, width, height, options);
544+
// Step 1: generate glyph contours
545+
let contours = this.textToContours(str, x, y, width, height, options);
546+
if (!Array.isArray(contours[0][0])) {
547+
contours = [contours];
548+
}
545549

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

603670
variations() {

0 commit comments

Comments
 (0)