@@ -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