-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Fix textToModel face normals for extruded text #8091
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev-2.0
Are you sure you want to change the base?
Conversation
|
@davepagurek Could you please take a look at this PR and provide a review? Thanks! |
|
Hi! So far this change just reverses the order of the vertices in front faces; I don't think we need to change that. I left a comment here #8074 (comment) explaining the approach I had in mind, which likely involves changing this part of the code that constructs the geometry: Lines 547 to 588 in fc8ca8a
Currently there's an if/else that uses a different approach if extruding vs if creating a flat model. Instead, we'd probably always start with the flat model, but if we need extrusion, you could read the |
|
@davepagurek can you review this now its working perfectly i add your recommendation of making a new geometry |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking good! I left a few little comments below. I think the big thing left to figure out is the normals. The problem currently is that each side face has its own normal, so the sides will look faceted instead of smooth.
Ideally, I'd like us to be able to just call extruded.computeNormals() and have it generate all the normals for us. It works per vertex by averaging the normals of the faces sharing that vertex. In order to do that, this means:
- the side faces need to share vertices with adjacent side faces. This means adding all the vertices on each side to
extruded.verticesonce, and then adding faces that reference the vertex indices for each of those rather than creating brand new vertices in each face. - the side faces cannot share any vertices with the front and back faces. This is because if they do, the edge between the front/back and the sides will be smoothed, but we want a crisp edge there.
Let me know if you need any pointers on how to accomplish that!
src/type/p5.Font.js
Outdated
| }); | ||
|
|
||
| if (extrude === 0) { | ||
| console.log('No extrusion'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should take this out before merging so we don't clutter users' console.
|
@davepagurek The extrusion is still not rendering correctly on the top and bottom faces. In my earlier changes, the top and bottom rendered perfectly, but in this version they only work if I comment out extruded.computeNormals(). It seems that computeNormals() is overriding the normals in a way that breaks the top/bottom faces I think keeping of eeping the front and back faces with their own independent vertices and make the side faces share vertices with each other, so their normals average smoothly.. this is the code |
|
@davepagurek I think the problem could be duplication of normals for the top and bottom faces when using separate side vertices. When we create new vertices for the sides, each side face has its own independent vertices, so computeNormals() cannot average normals across adjacent side faces. This causes the sides to look faceted and can even distort the normals for the top and bottom faces.I also noticed that the extrusion renders correctly if we reuse the existing front and back vertices for the side faces instead of creating new ones. Am I on the right track? Which approach aligns better? |
|
The first approach you mentioned looks right -- we intentionally don't want the front/back faces to share vertices with the sides so that there is a crisp edge. Can you send a screenshot of what the shading look like with that so I get a clearer idea of the problem? Some thoughts though:
|
|
@davepagurek it looks same as original issue have top and bottom dont render normals properly its really getting way complex triying to figure out now |
|
@davepagurek ok so I Initialized vertexNormals array manually and assigned exact normals of [0,0,1] to all front vertices and [0,0,-1] to all back vertices when creating them. This eliminates any numerical issues since we know these faces should be perfectly flat.Reordered the face creation process. Side faces are now added first, then computeNormals() is called which only affects the side vertices since front and back vertices already have their normals set. After that, front and back faces are added to the faces array. This optimization means computeNormals() doesn't waste computation on faces where we already know the exact normals.Reversed the winding order of back faces by swapping face[i] and face[i+1] in the back face loop. This ensures that when viewed from outside the geometry, the back faces have vertices ordered counterclockwise (opposite to the front faces), which is necessary for normals to point in opposite directions.I think that allign to your thoughts still it giving same rendering dont able to figure it now |
|
@davepagurek I tried your suggestion of generating side faces from the triangulation instead of geom.edges, but the overlapping issue was still there and, on top of that, the sides became very messy because internal triangulation edges also got extruded. So unfortunately that approach didn’t fix the problem |
|
@VANSH3104 do you have a build of that you could put into a p5 web editor sketch? I can take a look and see if I can debug a bit to figure out what a next step should be. |
|
Hi @davepagurek, I messaged you on Discord because I'm having some issues with the build. Could you please check? |
…and normal calculation
|
@davepagurek here is editor for visualizing this https://editor.p5js.org/vanshkabra05/sketches/Qc--JMEgU |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome work on this, it's working great! I think there's just some minor cleanup left.
| const contours = this.textToContours(str, x, y, width, height, options); | ||
|
|
||
| let contours = this.textToContours(str, x, y, width, height, options); | ||
| if (!Array.isArray(contours[0][0])) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like right now this is being used to create multiple begin/endShape calls. Do we need that? I assumed it would be ok to just get a list of contours and put them all in one shape.
Also I believe textToContours should always be returning an array of array of points (a list of contours, each contour a list of points), so I also wonder if we always go down the same branch here anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked textToContours() it returns a flat array of contours like [{x,y}, {x,y}, ...] for a single character, so contours[0][0] is an object, not an array. The check wraps it into [[contours]] so the loop works correctly and i simplify it to one beginShape/endShape call with all contours inside its good idea
src/type/p5.Font.js
Outdated
| const prevValidateFaces = this._pInst._renderer._validateFaces; | ||
| this._pInst._renderer._validateFaces = true; | ||
| this._pInst.push(); | ||
| this._pInst.stroke(0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this do the same thing as noStroke()?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, you're right I've removed the push/pop and stroke settings entirely since they're not needed for the geometry building.
src/type/p5.Font.js
Outdated
| } | ||
| return geom; | ||
|
|
||
| console.log(`Found ${validEdges.length} outer edges from ${Object.keys(seen).length} total edges`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a reminder to take this out before merging to not clog up the console
| } | ||
|
|
||
| const vertexIndices = {}; | ||
| const vertexId = v => `${v.x.toFixed(6)}-${v.y.toFixed(6)}-${v.z.toFixed(6)}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had to remind myself that this was necessary because tessellation produces triangles with no shared vertices, so we need to deduplicate them here to find connected faces. Would you mind leaving a comment explaining this for future contributors' context?
src/type/p5.Font.js
Outdated
| const vA = newVertices[a]; | ||
| const vB = newVertices[b]; | ||
| // Skip if vertices are too close (degenerate edge) | ||
| const dist = Math.sqrt( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this still passes if we have a triangle that has collinear edges. I think in computeNormals, we check basically that the magnitude of the cross product is over a certain size. maybe we should match that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup you are right updated it to check the cross product magnitude instead, matching the logic in computeNormals
…and normal calculation with recomended chnages
…and normal calculation with recomended chnages
…and normal calculation with recomended chnages
|
@davepagurek could you please review this? If everything looks good, I’ll clean up my commit history. |





Resolves #8074
Changes:
Screenshots of the change:
Screencast.From.2025-09-15.15-30-14.mp4
PR Checklist
npm run lintpasses