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