Repository navigation
Planar texgen maps a face that faces one way as one plane, and its normal map takes the planar frame - #561
Open
RyeMutt wants to merge 3 commits into
Open
Planar texgen maps a face that faces one way as one plane, and its normal map takes the planar frame#561RyeMutt wants to merge 3 commits into
RyeMutt wants to merge 3 commits into
Conversation
Contributor
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 59 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This was referenced Oct 23, 2026
RyeMutt
added this pull request to stack #563
October 23, 2026 09:37
RyeMutt
force-pushed
the
rye/planar-texgen
branch
from
October 23, 2026 10:56
8f6c8ca to
224d279
Compare
Planar texgen projected each vertex along its own normal, switching the projection's axis at |n.x| >= 0.5, with the tangent b x n left unnormalized. A round prim puts columns on that switch at every LOD, and the tangent scales V by the normal: a seam and a jump in scale across every curved face. LLVolumeFace::getPlanarNormal makes, on a face's first planar use, the vertices' one normal where they all share it, and otherwise the sum of its triangles' normals weighted by area, normalized; the face is one plane where every vertex normal lies within 60 degrees of it. Such a face is projected along that normal at every point, by the formula as it was, so a flat face maps as it did and a curved one has no seam. Any other face, a full cylinder, sphere or torus, keeps per-vertex mapping, where an x within 1e-4 below the switch takes the y binormal, so that a column maps one way on every row and at every LOD. The planar normal lasts until a full build; a flexible prim's frames keep their first build's, as they keep its planar coordinates. alsimd::planar_texcoords maps a face in one pass with the texture entry's transform folded in. MSVC's /fp:fast had rewritten LLFace's per-vertex loops: 1 + (2d - 0.5) folded to 2d + 0.5, one product of each turned sum fused, the offset added before the 0.5. The kernel rounds exactly so, fenced by float_control(precise) against fusing on its own, and over the 9,194 sweep faces with one normal, 734,028 coordinates, gives what those loops gave bit for bit. Of the sweep's 26,152 faces, 45.3% are one plane: 9,194 with one normal, 1,051 flat to rounding and 1,596 curved. surfaceToTexture maps a touch through the same kernel and transform, with its normal normalized to full precision: the fast one moved vertices across the switch. getPlanarProjectedParams aligns a face projected as one plane by its planar normal, without its tangents. Six prism sides flat to rounding change, a triangle profile with a square hole at 0.85 and a cut: their normals straddle the switch, and half their vertices took each axis. A box face maps in 5.3 ns where it took 7.6 (6.3 for 9.2 through a transform), a cylinder cap 2.6-3.3x faster, spheres, tori and sculpts 2.5-5.7x; the planar normal costs about one old pass, once. A tapered round side's normals still turn about half a degree from row to row, more than the tie; its case is marked [!shouldfail]. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A face with planar texgen samples its normal map at the planar coordinates, but its vertex tangents came from the default UVs. Box and cylinder faces happen to agree; angled faces, sculpts and meshes shaded with the normal map turned or mirrored, legacy and PBR alike. alsimd::planar_tangents gives the tangent the generators would give for UVs that are the planar coordinates, from the normals alone: with s's gradient gs = scale * b and t's gt = -(scale * t), w is the sign of n . (gs x gt) and the tangent is w (gt x n), normalized. getGeometryVolume writes those for planar faces, turned by the vertex matrix as before; the PBR shaders' texture-rotation fix-up applies to them as it does to default ones. The legacy emboss offsets take them too, and getPlanarProjectedParams builds its frame from them on every face, so planar texgen no longer makes the face's default-UV tangents. Against each face's own generator run on its planar coordinates, over the sweep and the mesh corpus, 297,310 tangents: within 0.02 degrees on faces with one normal, within 0.73 of the bound the generator's sum allows on curved ones (0.5 degrees plus twice the vertex normal's lean from its triangles), with the same handedness except at 30 vertices of folded strips whose normals lie 86-89 degrees from a triangle. A planar face's tangents cost about 1.5 ns a vertex on each position rebuild, 12.8 ns for a box face. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RyeMutt
force-pushed
the
rye/planar-texgen
branch
from
October 23, 2026 13:10
224d279 to
b003070
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Planar texgen projected each vertex along that vertex's own normal. It switched the projection axis at |n.x| ≥ 0.5, and left the tangent
b × nunnormalized. A round prim puts columns right on that switch at every LOD, and the unnormalized tangent scales V by the normal. The result is a seam and a jump in scale across every curved face.A planar face's normal map also sampled at the planar coordinates while taking its tangent frame from the default UVs.
This is part 6 of a stack of 7. Merge them in order: each PR is based on the one before it and shows only its own commits.
Changes
One plane per face
LLVolumeFace::getPlanarNormal: on a face's first planar use, gives the face one normal. Where all the vertices share a normal, it's that one. Otherwise it's the area-weighted sum of the face's triangle normals.alsimd::planar_texcoords: maps a face in one pass, with the texture entry's transform folded in. It reproduces exactly what MSVC's/fp:fasthad made ofLLFace's per-vertex loops, bit for bit over 734,028 coordinates on the sweep's faces that have one normal.surfaceToTexturemaps a touch through the same kernel. Touch ST, which is sent to the simulator, therefore agrees with the rendered texture.Normal maps
alsimd::planar_tangents: gives the tangent that the generators would give for UVs equal to the planar coordinates, from the normals alone.getGeometryVolumewrites these tangents for planar faces.getPlanarProjectedParamsbuilds its frame from them.Planar texgen no longer makes a face's default-UV tangents. Before, normal maps on angled faces, sculpts and meshes shaded turned or mirrored, legacy and PBR alike.
Results
A tapered round side's normals turn about half a degree from row to row, which is more than the tie-break absorbs. Its case is marked
[!shouldfail].Testing
llmath_tests, shuffled: 432 cases. 430 pass, 1 is skipped, and 1 fails as expected. The expected failure is the tapered round side described above.llprimitive_testspasses all 48 cases.llcommon_testshas 723 cases: 720 pass and 3 are skipped.🤖 Generated with Claude Code