threejs-procedural-geometry skill (Threejs-Awesome-Graphics-Agent-Skills)
- Install
- SKILL.md (verbatim)
- Craft loop
- Modeling bar
- Portable JavaScript kit
- Mechanism references and implementations
- Failure conditions
- Routing boundary
- Other files in this skill
- references/complete-submarine-assembly.md (verbatim)
- Contents
- Design contract and coordinate frame
- Geometry kernel
- Tilted-collar hull and apertures
- Semantic assembly
- Material fit
- Observed limitations
- Diagnostics
- Failure diagnosis
- references/geometry-craft-workflow.md (verbatim)
- Contents
- Semantic geometry contract
- Polygon-first design model
- Modeling operation selection
- Modifier and emission order
- Join and assembly discipline
- Detail and geometry budgets
- Visual inspection contract
- Observed limitations and defects
- Diagnostics
- Failure diagnosis
- references/geometry-quality-gates.md (verbatim)
- Contents
- Gate order and ownership
- Pre-emission topology audit
- World-space collection contract
- Coplanar overlap audit
- Solid-clash audit
- Semantic geometry contracts
- Support, clearance, and motion
- Visual gate
- Observed limitations and defects
- Diagnostics
- Failure diagnosis
- references/procedural-optimus-humanoid-assembly.md (verbatim)
- Contents
- Coordinate frame and assembly evidence
- Polygon mesh and modifier contract
- Loft, spine, and pillow generators
- Semantic humanoid construction
- Material identity system
- Geometry emission and ownership
- Observed limits and defects
- Diagnostics
- Failure diagnosis
What it does. Build well-crafted production procedural meshes in Three.js. Use for complete hard-surface assemblies and humanoid robots, profile extrusion, parameter-curve and spine lofts, pillow panels, exact polygon cuts, inset, revolve, sweep, solidify, bevels and fillets, shell thickness, direct-topology apertures, semantic mesh writers, or diagnosing primitive-built forms, coplanar flicker, loose/non-manifold geometry, detached parts, interpenetration, support, clearance, and swept-envelope defects. Part of scottstts/Threejs-Awesome-Graphics-Agent-Skills (scottstts/Threejs-Awesome-Graphics-Agent-Skills).
| Upstream | scottstts/Threejs-Awesome-Graphics-Agent-Skills |
| Skill file | skills/threejs-procedural-geometry/SKILL.md |
| License | MIT |
| Author | Scott Sun (scottstts) |
| Fetched | 2026-09-10 |
Install
npx skills add scottstts/Threejs-Awesome-Graphics-Agent-Skills --skill threejs-procedural-geometry, or copy the skill folder into~/.claude/skills/threejs-procedural-geometry/.- Raw file:
curl -sL https://raw.githubusercontent.com/scottstts/Threejs-Awesome-Graphics-Agent-Skills/HEAD/skills/threejs-procedural-geometry/SKILL.md
SKILL.md (verbatim)
name: threejs-procedural-geometry
description: Build well-crafted production procedural meshes in Three.js. Use for complete hard-surface assemblies and humanoid robots, profile extrusion, parameter-curve and spine lofts, pillow panels, exact polygon cuts, inset, revolve, sweep, solidify, bevels and fillets, shell thickness, direct-topology apertures, semantic mesh writers, or diagnosing primitive-built forms, coplanar flicker, loose/non-manifold geometry, detached parts, interpenetration, support, clearance, and swept-envelope defects.
Procedural Geometry
Generate geometry from a semantic contract and explicit coordinate frame. Treat polygon operations as the design model and triangle emission as the final compilation step. Do not declare an object complete until its topology, assembly relationships, semantic measurements, and fixed views all pass.
This skill contains exemplary examples and assets beyond descriptive guidance, they're worth studying, referencing, or even copying. Use them sufficiently when relevant and do NOT blindly skip them.
Craft loop
- Define units, local/world frames, dimensions, bounds, primary profiles, topology intent, shell thickness, apertures, material slots, and triangle band.
- Declare every required contact, support, clearance, penetration, reveal, moving envelope, and interaction datum.
- Choose a modeling operation for each visible form: profile extrusion, inset, loft, revolve, sweep, direct-topology aperture, solidify, subdivision, bevel, or fillet.
- Keep quads and n-gons through design. Preserve one named mesh per semantic part; do not weld unrelated parts or merge material slots yet.
- Apply modifiers in a deliberate order, then clean inside each part, repair winding, assign a part-specific smooth angle, and emit triangles.
- Run the polygon topology gate, then audit the named world assembly for coplanar overlap, defects, and substantial solid clashes.
- Run builder-owned semantic checks, including support, openings, shell thickness samples, ergonomic clearances, and moving extrema.
- Merge by material slot, audit again, and inspect fixed silhouettes, hidden sides, joints, interaction views, wireframe, normals, and grazing light.
Modeling bar
- Use primitives only for genuinely primitive or hidden structural parts.
- Give every visible manufactured edge a scale-appropriate bevel or fillet.
- Build openings into one closed shell; do not stack a dark plane over a solid.
- Construct thick shells with paired profiles or
solidify; never rely on a single surface where an exposed rim reveals thickness. - Drive mating parts from the same datum. A late visual nudge is a failed dimension contract.
- Choose a continuous mesh for a continuous manufactured form. An arrangement of intersecting primitives is not a substitute for modeling.
- Keep narrow, named allowances only for intersections that are structurally intentional.
Read references/geometry-craft-workflow.md for the polygon-first modeling grammar, modifier order, join floors, detail budgets, fixed visual-review contract, and geometry-craft failure diagnosis.
Read references/geometry-quality-gates.md for exact topology, coplanar, solid-clash, semantic, support, clearance, motion-envelope, and visual gates with metre-scale tolerances.
Portable JavaScript kit
Place the complete assets/geometry-quality-kit/ directory into a Three.js
project when the project lacks an equivalent modeling and quality layer.
Adjust import paths and scene plumbing as needed; preserve the geometry and
audit contracts.
- procedural-mesh.js provides
MeshData, polygon cleanup, winding reconstruction, profiles, offsets, extrusion, loft, revolve, sweeps, solidify, subdivision, bevels, apertures, smooth-angle normals, material-slot builds, and Three.js emission. - mesh-topology-audit.js checks loose/duplicate vertices, degenerates, open/non-manifold edges, detached components, signed volume, and emitted positions/normals.
- geometry-audit.js audits a
built
Object3Dhierarchy for true clipped coplanar overlap, invalid mesh data, missing materials, and substantial triangle-crossing clashes. - geometry-contract.js runs object-specific measurements without imposing project-specific shape semantics.
- assembly.js preserves named parts for auditing, then builds one draw mesh per material slot after gates pass.
- selftest.js plants known defects and verifies the modeling, topology, contract, assembly, z-fight, and clash paths; run it after placing or adapting the kit.
Mechanism references and implementations
Read references/profile-sweeps-and-mesh-writers.md for the exact sculpted-frame profile, rail emission, tree rings, semantic mesh writer, and their observed scaling limits.
Read the sculpted gallery frame geometry for profile sweeps, miter-like rail mapping, deliberate cap ownership, PBR surface bundles, grazing highlights, selective bloom, and geometry diagnostics.
Read references/complete-submarine-assembly.md for the exact dimensioned object contract, shared loft/sweep kernel, UV-owned apertures, semantic subassemblies, generated fittings, and model diagnostics.
Read the porcelain-and-brass submarine model for a complete assembly with a tilted-collar hull loft, parallel-transport trim, furnished glass cabin, shrouded propeller, lens-section fins, and per-part triangle evidence.
Read references/vehicle-loft-and-projector-contract.md for parameter-curve section tracks, recess-opening sections, superellipse volumes, spanwise airfoil lofts, warped plates, projector ownership, load-deflected tyres, and measured limits.
Read the Formula One race car model for one continuous body loft, section-owned cockpit recess, real inlet aperture, spanwise wing lofts, livery projection, and contact-deflected tyres.
Read the sport motorcycle model for slot-tagged emission, revolve and upright-frame sweeps, offset panel shells, spoked wheels, a hanging chain path, and a volume-audited assembly.
Read references/procedural-optimus-humanoid-assembly.md for the exact coordinate, polygon/modifier, curve, loft, spine, pillow, CSG, bevel, semantic assembly, filtered material, emission, limitation, and diagnostic contracts of a complete human-scale robot.
Read the procedural Optimus humanoid entry and its complete geometry and material system for a 176-object torso/head/arm/hand/hip/leg/foot assembly with five-finger hands, 891,809 emitted triangles, fourteen PBR identities, exact polygon cuts, angle-limited bevels, split corner normals, and derivative-filtered object-space roughness and bump.
Read the procedural financial tower compiler for semantic placement compilation and material-slot instancing at building scale.
Failure conditions
- same-facing coplanar triangles survive at a visible scale;
- a loose vertex, degenerate face, open solid, non-manifold edge, detached component, invalid normal, or inward closed volume reaches emission;
- substantial unrelated solids intersect, or a placed part lacks a declared support/contact relationship;
- an exposed shell is paper-thin or an aperture is a visual overlay;
- a visible primitive keeps razor edges or joins another primitive without a designed transition;
- profile frames flip, caps share smooth side normals, or UV density changes with segment count;
- material merging happens before named-part auditing;
- triangle count is the only complexity evidence;
- a script passes but fixed-view inspection still finds implausible modeling.
- a complete humanoid is reduced to intersecting capsules and boxes;
- mirrored hands or limbs keep inward winding;
- high-frequency object-space material noise is emitted without footprint filtering.
Routing boundary
This skill owns reusable mesh construction and geometry quality. Use
$threejs-procedural-materials when surface identity is primary,
$threejs-procedural-architecture for a building grammar, and
$threejs-procedural-vegetation for a growth hierarchy; those subject skills
may then apply these geometry mechanisms.
Other files in this skill
- agents/openai.yaml
- assets/geometry-quality-kit/assembly.js
- assets/geometry-quality-kit/geometry-audit.js
- assets/geometry-quality-kit/geometry-contract.js
- assets/geometry-quality-kit/mesh-topology-audit.js
- assets/geometry-quality-kit/procedural-mesh.js
- assets/geometry-quality-kit/selftest.js
- assets/sculpted-gallery-frame/aged-walnut-frame.webp
- assets/sculpted-gallery-frame/antique-gold-frame.webp
- assets/sculpted-gallery-frame/dark-ebony-frame.webp
- assets/sculpted-gallery-frame/gallery-mat-board.webp
- examples/formula-one-race-car/race-car-model.js
- examples/formula-one-race-car/source/design-contract.js
- examples/formula-one-race-car/source/mesh-kit.js
- examples/formula-one-race-car/source/race-car-materials.js
- examples/formula-one-race-car/source/race-car-model.js
- examples/formula-one-race-car/source/race-car-textures.js
- examples/porcelain-brass-submarine/source/design-contract.js
- examples/porcelain-brass-submarine/source/mesh-kit.js
- examples/porcelain-brass-submarine/source/submarine-materials.js
- examples/porcelain-brass-submarine/source/submarine-model.js
- examples/porcelain-brass-submarine/submarine-model.js
- examples/procedural-optimus-humanoid/procedural-optimus-humanoid.js
- examples/procedural-optimus-humanoid/source/optimus-humanoid-system.js
- examples/sculpted-gallery-frame/frame-geometry.js
- examples/sport-motorcycle/motorcycle-model.js
- examples/sport-motorcycle/source/design-contract.js
- examples/sport-motorcycle/source/mesh-kit.js
- examples/sport-motorcycle/source/motorcycle-materials.js
- examples/sport-motorcycle/source/motorcycle-model.js
- examples/sport-motorcycle/source/motorcycle-parts.js
- references/complete-submarine-assembly.md
- references/geometry-craft-workflow.md
- references/geometry-quality-gates.md
- references/procedural-optimus-humanoid-assembly.md
- references/profile-sweeps-and-mesh-writers.md
- references/vehicle-loft-and-projector-contract.md
references/complete-submarine-assembly.md (verbatim)
Complete procedural hard-surface assembly
Use this reference for dimensioned object plans, reusable loft and sweep primitives, UV-owned apertures, semantic subassemblies, generated fittings, and complete-model topology diagnostics.
Contents
- Design contract and coordinate frame
- Geometry kernel
- Tilted-collar hull and apertures
- Semantic assembly
- Material fit
- Observed limitations
- Diagnostics
- Failure diagnosis
Design contract and coordinate frame
The porcelain-brass-submarine example uses metres, with +Z forward and
+Y upward. The design contract keeps shape decisions separate from triangle
emission:
dome center = (0, 0.02, 0.92) m
dome radius = 1.00 m
collar tilt = 24 degrees
collar plane offset = 0.14 m
hull tail Z = -1.30 m
hull tail radius = 0.30 m
hull maximum radius = 1.015 m
hull output = 56 rings * 128 radial segments
shroud center Z = -1.76 m
shroud radius = 0.80 m
shroud section width = 0.145 m
shroud section depth = 0.26 m
propeller blade count = 8
propeller radius = 0.62 m
The collar plane normal and circular intersection are exact:
nColl = (0, sin(24 degrees), cos(24 degrees))
rimCenter = domeCenter - nColl * 0.14 m
rimRadius = sqrt(1.00^2 - 0.14^2) m
rimU = (1, 0, 0)
rimV = (0, cos(24 degrees), -sin(24 degrees))
Every subassembly is positioned from this contract or from a hull sample. There is no late global scale used to repair mismatched parts.
Geometry kernel
gridGeometry compiles sampled Vector3[row][column] plans. Closed rows emit
one explicit seam column. The geometric normal is:
dU = P(row, column + 1) - P(row, column - 1)
dV = P(row + 1, column) - P(row - 1, column)
normal = normalize(cross(dU, dV))
At a pole, the fallback normal uses the vector between adjacent row centroids.
The flip contract reverses both normal direction and index winding.
latheZ revolves an (radius, Z) profile around +Z. Its longitudinal UV
coordinate is normalized accumulated profile distance:
distance[i] = distance[i - 1]
+ hypot(radius[i] - radius[i - 1], Z[i] - Z[i - 1])
v[i] = distance[i] / distance[last]
sweepTube uses parallel-transport frames. Each tangent is sampled from its
two neighboring path points. The previous normal rotates around the cross
product of consecutive tangents by their clamped dot-product angle. Rounded
ends add exactly 5 tapered rings at each end.
The fin section is a closed lens profile. For section angle a:
chord = 0.5 - 0.5 * cos(a)
side = sin(a)
width = thickness
* max(sin(pi * chord^0.85), 0)^0.62
point = lerp(leadingEdge, trailingEdge, chord)
+ up * side * width * 0.5
These four emitters are shared by the hull, collar, window frames, rails, fittings, cage, furniture, shroud, propeller, fins, and pods.
Tilted-collar hull and apertures
The hull is planned at 240 longitudinal samples before the 56 output rings
are emitted. Radius control points are:
(0.995, 0.00), (1.014, 0.07), (1.006, 0.25), (0.958, 0.42),
(0.845, 0.60), (0.645, 0.78), (0.430, 0.92), (0.300, 1.00)
Center-Y control points are:
(rimCenterY, 0.00), (0.012, 0.18), (-0.012, 0.50),
(0.020, 0.80), (0.075, 1.00)
The collar tilt decays with:
ringTilt(t) = 24 degrees * (1 - smooth01(min(t / 0.34, 1)))
The hull v coordinate uses accumulated
hypot(deltaZ, deltaRadius) + abs(deltaCenterY). This keeps ornament placement
stable when the output ring count changes.
Window ownership remains in hull UV space. The center is (0.345, 0.205), the
half extents are (0.052, 0.135), and the other side mirrors u with 1 - u.
The hull material cuts the aperture from the atlas red channel. Two swept
brass outlines use scales 1.06 and 1.22. Nine glazing rows contract toward
the window center by 0.985, with outward bulge:
bulge = sqrt(max(1 - contraction^2, 0))
offset = 0.004 m + 0.028 m * bulge
The cut-out, frame, and glazing therefore share one parameterization instead of three independently placed approximations.
Semantic assembly
The submarine is a hierarchy of role-specific meshes:
- the hull owns its skin, aperture mask, collar, bead, and rivets;
- flank spears sample hull position and normal, then align a revolved profile to the local hull tangent;
- the step, handle, vent, lamps, and flanges are independent fittings;
- the dome owns glazing, latitude rings, meridian ribs, headlight, and crown lantern;
- the cabin owns the deck, chair, helm, gauges, and telegraph lever;
- the stern owns the tail cone, collars, superelliptic shroud, struts, propeller, halo, lamp, and spike;
- each fin owns its lens loft, gilt perimeter, inset line, and lamp pod.
The eight propeller blades share one geometry because their topology and material are identical. The thirty collar rivets use one instanced sphere. Parts with different topology retain separate meshes and semantic names.
Material fit
The model generates its effect-owned texture inputs at construction time:
- a
2048 * 1024hull atlas stores window cut-out, gold ornament, and soft grime channels; - a
512 * 512Sobel-derived normal map stores the six-cell diamond quilt; - a
128 * 128gauge texture stores twelve ticks and the fixed needle pose.
Porcelain, brass, glass, leather, walnut, lamp, and gauge materials remain
separate because their shading and transparency contracts differ. The hull is
double-sided: front faces mix porcelain, gold, and grime; back faces use the
interior dark color. Glass uses transmission 1.0, IOR 1.52, thickness
0.05 m, roughness 0.035, and attenuation distance 2.5 m.
Observed limitations
- The hull aperture is an alpha-tested material cut-out rather than a boolean topology opening. The separate glazing and frame make the visible assembly complete, but the hull boundary has no emitted interior wall.
- The model targets close hero framing. The
128-segment hull,96-segment dome, repeated sweep sections, and furnished interior are not a distant LOD. - Triangle evidence counts one copy of shared geometry. Instanced rivets and repeated propeller-blade draws need separate instance accounting when a renderer budget is the question.
- Generated canvas textures require a browser canvas implementation.
- Motion animates the propeller, helm, and whole-object float; it is presentation motion, not a physical vehicle simulation.
Diagnostics
The example records part and tris whenever a defining geometry is emitted.
Report that list together with total draw calls and renderer triangles; no one
number represents both unique geometry storage and repeated draws.
Use the gallery modes as follows:
final: complete assembly with generated materials;topology: wireframe across every material role;hull-loft: isolated wireframe hull skin showing the56 * 128output grid.no-glass: complete opaque assembly with transmissive glazing hidden.
Also inspect the alpha-tested window boundary, mirrored fin winding, glass depth order, shared propeller blade, and the transition from tilted collar rings to the untilted stern frame.
Failure diagnosis
| Symptom | Check |
|---|---|
| hull turns inside out | gridGeometry normal flip and index winding must change together |
| sweep twists abruptly | parallel-transport normal rotation or a duplicate interior path point is invalid |
| ornament drifts when tessellation changes | hull UV v must use accumulated plan distance, not output-ring index alone |
| window frame misses the cut-out | cut-out, frame, and glazing must share the same hull UV outline |
| mirrored fins shade inward | mirror transform, loft winding, and material side ownership are inconsistent |
| propeller memory multiplies by eight | all eight blade meshes must share one blade geometry |
| cabin disappears through the hull | hull back-face material or alpha aperture ownership has been removed |
| topology mode changes silhouette | the debug path is changing visibility or transforms instead of material wireframe only |
references/geometry-craft-workflow.md (verbatim)
Geometry craft workflow
Use this reference to replace primitive assembly with a polygon-first modeling process, explicit part relationships, deliberate edge treatment, and a staged definition of done. Use metres for the numeric defaults below or scale all dimensions and audit tolerances together.
Contents
- Semantic geometry contract
- Polygon-first design model
- Modeling operation selection
- Modifier and emission order
- Join and assembly discipline
- Detail and geometry budgets
- Visual inspection contract
- Observed limitations and defects
- Diagnostics
- Failure diagnosis
Semantic geometry contract
Write a contract before vertices. Keep it beside the builder so the same dimensions drive geometry, placement, interaction, and audit measurements.
const contract = {
frame: "+X length, +Y back, +Z up; ground at Z = 0",
unit: "metre",
bounds: { width: 1.8, depth: 0.52, height: 0.98 },
parts: {
frame: { topology: "closed-solid", material: "cast" },
slat: { topology: "closed-solid", material: "metal" },
},
joins: [
{ a: "slat:*", b: "rail:*", relation: "clearance", min: 0.012 },
{ a: "foot:*", b: "ground", relation: "support", gap: 0 },
],
triangleBand: [2500, 10000],
};
Record at least:
- authoring frame, world frame, origin, and unit;
- overall bounds and the primary silhouette/profile dimensions;
- whether every part is a closed solid, thick shell, or intentional surface;
- shell thickness, aperture, reveal, and clearance requirements;
- required contacts, supports, intended penetrations, and forbidden overlaps;
- moving-part extrema and swept-volume clearance;
- stable names, material slots, and expected part/triangle bands;
- the views needed to verify silhouette, hidden sides, joins, and interaction.
Do not re-derive a mating dimension in two builders. Compute both parts from one datum, endpoint, profile, or layout constant.
Polygon-first design model
Keep quads and n-gons during design. Triangles are a rendering representation,
not the modeling language. Use MeshData from
assets/geometry-quality-kit/procedural-mesh.js to retain polygon ownership
through transforms and modifiers.
import {
cleanMesh,
loft,
recalcNormals,
smoothShade,
toGeometry,
} from "./geometry-quality-kit/procedural-mesh.js";
const part = loft(sectionRings, {
closeV: true,
capStart: true,
capEnd: true,
});
cleanMesh(part);
recalcNormals(part);
smoothShade(part, 34);
const geometry = toGeometry(part);
The module authors Z-up and emits Three.js Y-up once. The conversion
(x, y, z) -> (x, z, y) has negative determinant, so emission reverses face
winding while converting positions and normals.
Use smooth-by-angle per-corner normals. A corner averages only adjacent faces
whose face normals remain within the part's crease threshold. Do not replace
this with an unrestricted computeVertexNormals() pass.
cleanMesh() uses an actual Euclidean weld test across neighboring spatial
cells; a quantized cell key alone is insufficient because two vertices within
the weld distance can straddle a cell boundary. toGeometry() preserves
per-vertex color attributes and compiles consecutive faceMat values into
Three.js geometry groups.
Modeling operation selection
Choose the operation that owns the visible form:
| Form | Primary operation |
|---|---|
| constant section | profile extrusion / prism* |
| changing section | loft over authored rings |
| axial body, fastener, vessel | revolve with welded poles |
| rail, pipe, cable, frame | tubeAlong, sweepPlanarLoop, or runMolding |
| opening through a solid | panelWithHoles, wallRun, or aperturedPrism |
| hollow rim or casing | hollowPrism or annularPrism |
| rounded manufactured block | roundedBoxMesh or provenance-aware bevel |
| thin designed skin | profile pairs or solidify with explicit thickness |
Treat primitives as acceptable only when the intended form is genuinely a primitive or the part is hidden structure. A visible housing assembled from a box, cylinder, and sphere still owes a unified profile, edge hierarchy, and plausible junctions.
Prefer direct topology for apertures. A frame built from matched outer/inner rings owns its opening, jambs, end faces, and bevels in one closed shell. Reach for general boolean machinery only when a direct ring or segmented-wall construction cannot express the cut reliably.
Modifier and emission order
Use this order unless the object's construction demands a stated exception:
semantic profiles and rings
→ extrusion / inset / loft / revolve / sweep
→ solidify
→ subdivision where the design needs it
→ bevel or fillet
→ cleanup inside each part
→ winding repair and smooth-angle assignment
→ topology audit
→ triangle emission
Modifier order changes the object. Solidifying after beveling creates a different rim from beveling a thick shell. Cleaning before a deliberate modifier can remove topology it expects; cleaning after joining unrelated parts can weld a butt joint into accidental geometry.
Use the metre-scale bevel bands as starting classes, not one universal radius:
hardware / hinges / casings 0.002 m
panel / shelf / slat edges 0.004 m
carcass / plinth / cast stone 0.007 m
frame / apron / machine body 0.013 m
soft upholstered form 0.045 m
Common smooth-angle starting points are 40° for turned parts, 34° for
moulded sections, 45° for shells, 38° for cast forms, 50° for tight
rolls, and 32° for broad tops. Inspect the actual highlight before accepting
the value.
Join and assembly discipline
Keep one MeshData per semantic part. Do not join separate components before
their relationship audit: cleanup can weld intended butt joints and material
merging can erase the names needed to diagnose an overlap.
Use these readable join floors:
applied detail proud of host >= 0.0008 m
intentional reveal 0.0015–0.006 m
gap meant to read at 2 m >= 0.004 m
Flush but unrelated faces are forbidden. Choose one of four explicit states:
- One continuous polygon mesh.
- A declared structural penetration with a narrow named allowance.
- A proud part with a readable offset.
- A reveal or open gap.
Build named audit meshes first with buildNamedAuditGroup(). After the named
assembly passes, build one draw mesh per material slot with
buildMergedAssembly(), then run the scene audit again because same-slot
merging can contain coplanar triangles inside one mesh. Named audit parts must
have unique stable names; duplicate names fail assembly construction rather
than merging unrelated evidence under one label.
Detail and geometry budgets
Allocate segments by visible radius and camera distance:
radius <= 0.025 m 10–14 segments
radius <= 0.100 m 16–24 segments
radius >= 0.150 m 28–48 segments for player-adjacent forms
Report both part count and triangle count. Triangle count alone cannot expose an object made from hundreds of independent primitives, and part count alone cannot expose an over-tessellated hero surface.
Spend topology on silhouette, aperture shape, edge highlights, and contact points before hidden planar faces. Add the small parts that explain assembly— fasteners, collars, gussets, pads, seams—only after the primary form and joins are correct.
Visual inspection contract
Mechanical checks cannot decide whether a form is plausible or well modeled. Inspect at least:
- front, rear, both sides, top, underside, and opposing three-quarter views;
- orthographic silhouette views;
- close views of every join, aperture, shell edge, and cap;
- interaction-height views such as seated eye, standing eye, or boarding view;
- both extrema and intermediate poses of moving geometry;
- grazing light, clay material, material-slot colors, wireframe, and normal or face-orientation diagnostics.
Look specifically for primitive seams, implausible load paths, unsupported parts, hidden-side defects, paper-thin openings, unrounded manufactured edges, and trim that floats when viewed from behind.
Observed limitations and defects
- A closed manifold shell can still be the wrong shape.
- Bounding-box overlap is not proof of triangle collision.
- A generic support ray cannot understand suspended, wall-mounted, or hanging parts without declared support targets.
- Shell thickness cannot be inferred reliably for every arbitrary mesh; assert
generator-owned thickness samples or construct the shell with
solidify. - Cleaning a combined assembly can weld parts that must remain independent.
- A global bevel radius destroys scale hierarchy.
- Smooth shading across caps and side walls rounds edges that should remain exact.
- A broad collision allowance hides defects; every allowed pair needs stable names and a construction reason.
Diagnostics
Keep a build report with:
part count / triangle count / material slots
per-part topology reports
named-assembly z-fight and clash reports
post-merge z-fight report
semantic contract measurements
fixed-view capture list
Print actual measured values beside expected ranges. A bare pass/fail hides whether a clearance is comfortably valid or one floating-point step from the boundary.
Failure diagnosis
- Faceted curve: raise profile/ring resolution before adding subdivision.
- Soft hard edge: lower the smooth angle or split cap/side ownership.
- Inside-out solid: repair winding from signed volume before emission.
- Floating component: fix the shared datum or declared support relationship; do not visually nudge the part downward.
- Z-fight: remove one face, join topology, or create a proud/revealed relation.
- Collision false positive at a butt joint: verify true penetration depth before adding a narrow named allowance.
- Defect appears only after merging: inspect same-slot parts that lost their individual object boundary.
references/geometry-quality-gates.md (verbatim)
Geometry quality gates
Use this reference to run deterministic topology, coplanar, collision,
semantic, and visual gates against procedural Three.js geometry. The scripts in
assets/geometry-quality-kit/ are plain JavaScript and may be placed directly
into a project, with import paths and scene plumbing adjusted as needed.
Contents
- Gate order and ownership
- Pre-emission topology audit
- World-space collection contract
- Coplanar overlap audit
- Solid-clash audit
- Semantic geometry contracts
- Support, clearance, and motion
- Visual gate
- Observed limitations and defects
- Diagnostics
- Failure diagnosis
Gate order and ownership
Run gates in this order:
constructor self-test
→ per-part polygon topology
→ named-part world assembly
→ object-specific geometry contract
→ material-slot merged assembly
→ fixed-view visual inspection
Do not skip named-part auditing. A merged mesh can reveal same-slot coplanar triangles, but its material-slot name cannot identify the two semantic parts that caused them. Do not skip post-merge auditing either; same-mesh overlaps must remain comparable.
Use selftest.js as the smoke test after placing the kit in a project:
node path/to/geometry-quality-kit/selftest.js
The test plants a 0.5 mm coplanar offset, a clean 10 mm separation, a
60 mm solid interpenetration, an exact butt joint, duplicate/loose vertices,
and passing/failing semantic measurements.
Pre-emission topology audit
Run auditMeshData() after modifiers and cleanup but before triangle emission:
import { cleanMesh, toTriangles } from "./procedural-mesh.js";
import {
assertMeshAudit,
auditMeshData,
auditTriangleSoup,
} from "./mesh-topology-audit.js";
const part = cleanMesh(buildPart());
assertMeshAudit(auditMeshData(part), "housing");
assertMeshAudit(auditTriangleSoup(toTriangles(part)), "housing emission");
The polygon audit reports:
- non-finite vertices and invalid face indices;
- faces with fewer than three usable corners or zero area;
- unused vertices and vertex pairs within
0.00002 m; - boundary edges, edges used by more than two faces, and same-direction shared edges that expose inconsistent local winding;
- edge-connected face components, so shells that only touch at one vertex remain separate;
- non-positive signed volume for each closed component rather than only the summed mesh volume;
- mismatched or invalid per-face material indices, per-corner UVs, and per-vertex color arrays.
auditTriangleSoup() separately rejects incomplete triangle arrays,
non-finite positions, non-unit normals, normals opposed to triangle winding,
and zero-area emitted triangles.
The default contract is one outward-wound closed solid. An intentional open surface must state that exception:
auditMeshData(surface, {
closed: false,
maxComponents: Infinity,
checkOrientation: false,
});
Do not use that configuration to silence an accidentally open shell.
World-space collection contract
auditGeometry(root, options) traverses visible meshes by the isMesh flag,
not instanceof, so it remains valid when two Three.js module instances exist.
It transforms every triangle by matrixWorld, validates local position and
normal attributes, and records world bounds per mesh.
const report = auditGeometry(root, {
bounds: null,
includeInstanced: false,
clash: true,
angleRadians: 0.0025,
planeDistance: 0.0015,
overlapArea: 0.0002,
maxTriangles: 600000,
top: 25,
clashAllow: [["bolt:", "housing:"]],
skip: (mesh, name) => name.startsWith("debug:"),
});
mesh.userData.auditSkip = true skips one mesh. Keep this for non-production
diagnostics, never as a general defect escape. Invisible ancestors suppress
their descendants. Local normal attributes must remain unit length within
0.001. Instanced scatter is excluded by default because organic or aggregate
instances often interpenetrate by design. When enabled, every available
instance is audited as name[index] with the composed
matrixWorld * instanceMatrix transform; the collector never reads beyond the
instance buffer's capacity. Diagnostic names retain the full named-ancestor
path. clashAllow prefixes may match that full path or an individual path
segment.
The metre-scale mechanical constants are:
normal angular tolerance 0.0025 rad (~0.143 degrees)
plane separation 0.0015 m
minimum clipped overlap area 0.0002 m² (2 cm²)
normal plane-grid cell 0.02
distance plane-grid cell 0.02 m
maximum clash depth threshold 0.03 m
normal-length tolerance 0.001
Scale every one of these coherently if the project's world unit is not a metre.
The neighboring-cell lookup requires planeCellNormal to be at least
2 * sin(angleRadians / 2) and planeCellDistance to be at least
planeDistance; invalid combinations throw instead of silently losing
comparisons.
Coplanar overlap audit
For each triangle, compute a unit normal n, plane distance d = dot(n, p0),
world AABB, and area. Canonicalize plane direction so opposed normals share a
plane bucket. Quantize (nx, ny, nz, d) and register the triangle in all
2^4 = 16 neighboring cells.
Inside each cell:
- Sort by minimum world X and sweep until inflated X bounds no longer meet.
- Reject Y/Z AABBs separated by more than
0.0015 m. - Require
abs(dot(ni, nj)) >= cos(0.0025). - Require a vertex-to-plane distance no greater than
0.0015 m. - Project both triangles into one in-plane basis.
- Clip one convex triangle against the other.
- Report only clipped overlap at least
0.0002 m².
The AABB test must be inflated by the plane tolerance. Without inflation, two
faces separated by 0.5 mm have disjoint boxes and the exact defect disappears
before the plane test.
Same-facing overlap is a z-fight. Opposed overlap is counted separately as back-to-back information because a closed solid may legitimately butt against another closed solid. Back-to-back status is not proof that the construction is correct; the semantic contract still owns that decision.
Solid-clash audit
Bounding-box intersection is only a broad phase. For each named mesh pair:
- Compute the shared AABB and reject empty overlap.
- Reject a narrowly named intentional pair.
- Compute each mesh's minimum bound thickness.
- Set the required shared depth to
min(0.03, max(0.004, 0.34 * min(thicknessA, thicknessB)))metres. - Spatially hash triangles from one mesh inside the shared region.
Triangles spanning more than
256cells use a complete fallback candidate pass rather than disappearing from collision coverage. - Test whether edges from either triangle pierce the other using a segment-restricted Möller–Trumbore test.
- Report the pair after more than two crossings.
This deliberately ignores touching faces and shallow construction seams while
finding a substantial solid running through another solid. clashDepth,
clashCellSize, maxCellsPerTriangle, and minimumClashCrossings are explicit
options; their defaults are 0.03 m, 0.4 m, 256, and 3. Use stable
semantic prefixes in clashAllow; never allow entire material classes.
Semantic geometry contracts
Generic triangles cannot know that a window must remain open or a wheel must
clear a fender. Keep the measurement beside the builder and use
geometry-contract.js for consistent results:
const report = runGeometryContract({
name: "service hatch",
checks: [
rangeCheck("opening-width", (g) => g.opening.width, 0.82, 0.86),
minimumCheck("hinge-clearance", (g) => g.hingeClearance, 0.004),
nearCheck("closed-seat", (g) => g.closedGap, 0, 0.0005),
predicateCheck(
"sight-line",
(g) => g.eyeRayBlocked,
(blocked) => blocked === false,
"false",
),
],
}, measurements);
assertGeometryContract(report);
Useful contract measurements include:
- expected bounds, height, width, and placement datum;
- contact depth, reveal, minimum gap, and forbidden overlap;
- aperture width/height and visibility rays through it;
- shell-thickness samples owned by the generator;
- seating, handrail, doorway, step, and control clearances;
- expected material slots, part count, and triangle band;
- curve continuity, loop seam position/twist, curvature, and self-distance.
Always print the measured value and accepted interval. A boolean alone makes a
near-boundary result impossible to judge.
Contract check identifiers must be unique and severity is exactly "error" or
"warning"; misspellings fail at contract construction time.
Support, clearance, and motion
A support check needs declared targets. Test the lowest/downward-facing sample points of the part against the surfaces it is permitted to touch, or compare analytic datums when both builders share them. A generic downward ray is not authoritative for wall-mounted, suspended, or hanging geometry.
For moving geometry, audit the whole schedule rather than only its endpoints:
for (let i = 0; i <= 64; i++) {
const t = i / 64;
const pose = mechanismPose(t);
const clearance = measureClearance(pose, environment);
minimum = Math.min(minimum, clearance);
}
Raise the sample count where rotation or curvature changes fastest. For a critical thin clearance, supplement samples with analytic extrema or a swept silhouette/voxel test. Record the pose of the minimum clearance in diagnostics.
Visual gate
An agent or human must inspect fixed views after all scripts pass:
- orthographic front/rear/side/top/underside silhouettes;
- opposing three-quarter views;
- grazing-light close views of bevels, caps, seams, and apertures;
- clay, wireframe, material-slot, and normal-orientation views;
- human-height or interaction-height views;
- every extreme pose and at least one intermediate pose.
Reject geometry that looks implausible, primitive-assembled, unsupported, paper-thin, or poorly finished even when no numeric gate fires.
Observed limitations and defects
- Triangle truncation makes a report incomplete;
truncatedis a failure for a release gate, not a performance success. - Tiny triangles below the overlap-area threshold cannot create a reportable overlap, but they still require topology and visual checks.
- The clash pass detects triangle crossings, not complete containment without boundary crossings; add an object-specific containment test where relevant.
- Skinned, morph-targeted, shader-displaced, and procedurally animated vertices are audited in their stored buffer pose. Audit their required poses through a posed CPU representation or an object-specific contract.
- The collector audits every stored triangle rather than camera layers,
material groups, or
drawRange; use a dedicated audit hierarchy when a geometry buffer intentionally stores inactive regions. - Same-material merging can obscure names; retain the named audit hierarchy.
- An open surface must opt out of closed-solid checks explicitly.
- A named allowance can become stale after a rename or design change.
- Fixed views do not replace free orbit inspection when a suspicious junction remains ambiguous.
Diagnostics
Log both summary and evidence:
const report = logAuditReport(auditGeometry(root));
if (
report.truncated ||
report.zfight.length ||
report.defects.length ||
report.noMaterial.length ||
report.clash.length
) {
throw new Error("geometry quality gate failed");
}
Retain pair names, clipped area in cm², representative world position,
triangle crossings, topology issue codes, semantic measurements, and the
camera/pose that exposed a visual defect.
Failure diagnosis
- Missing known z-fight: confirm bounds include it, triangles were not
truncated, the overlap exceeds
2 cm², and all sixteen plane cells remain. - Many false z-fights: separate same-facing from opposed faces and inspect true clipped area rather than bounding boxes.
- Missed collision: add containment or swept-volume logic when no triangle edge crosses the other mesh.
- Many duplicate vertices: run cleanup inside one part, not across the assembly.
- Open boundary after solidify: inspect rim orientation and cap ownership.
- Inside-out report: reverse polygon winding or run winding reconstruction; never negate normals alone.
- Clean named assembly but dirty merged assembly: isolate the material slot and compare its contributing part names before merge.
references/procedural-optimus-humanoid-assembly.md (verbatim)
Procedural Optimus humanoid assembly
Use this reference for a complete human-scale hard-surface robot built from parameter curves, semantic polygon generators, exact cut operations, authored bevels, per-corner normals, and material identities that remain readable across white shells, black polymers, metals, rubber, glass, and emissive details.
Contents
- Coordinate frame and assembly evidence
- Polygon mesh and modifier contract
- Loft, spine, and pillow generators
- Semantic humanoid construction
- Material identity system
- Geometry emission and ownership
- Observed limits and defects
- Diagnostics
- Failure diagnosis
Coordinate frame and assembly evidence
The procedural-optimus-humanoid example uses metres, keeps +Z upward, faces
the figure toward -Y, and places the origin on the floor midway between the
ankles. The completed bounds are:
minimum = (-0.2822209001, -0.1870000064, -0.0016486322) m
maximum = ( 0.2822209001, 0.1903314888, 1.7320995331) m
The emitted assembly contains 176 named mesh objects and 891,809 triangles
across seven semantic groups:
TORSO 25 HEAD 2 ARM 22 HAND 56
HIP 27 LEG 38 FOOT 6
These counts are deterministic evidence. Do not merge groups or delete small fittings before recording them; the object hierarchy is how the construction stays inspectable.
Polygon mesh and modifier contract
The internal mesh holds polygon faces rather than pre-triangulated buffers:
v vertex positions
f polygon index loops
fm material slot per face
fg generated-face class
mats material names by slot
mods pending bevel and boolean operations
Welding uses a spatial hash and a default 2e-5 m distance. It removes
duplicate and degenerate polygons, then reconstructs consistent winding across
each connected shell. Whole shells with negative signed volume are flipped.
Boolean difference uses a BSP polygon split with CSG_EPS = 1e-9. Faces of
the minuend are first separated by cutter bounds so distant polygons bypass
the BSP. The rebuilt fragments are welded at 1e-6 m before winding repair.
Bevel ownership is angle-limited. The modifier identifies shared edges whose
face-normal dot product falls below cos(angle), constructs inset face sectors,
emits the requested profile arcs, and closes bevel-vertex patches. Bevel faces
carry a generated-face class so the corner-normal stage can keep them hard
where required.
Loft, spine, and pillow generators
Curve1D provides two interpolation modes. Shape-preserving cubic Hermite
slopes prevent overshoot in authored dimension tracks; a natural cubic solve
provides C2-continuous tracks where smooth curvature is required.
The section profile is a four-quadrant superellipse with independent positive
and negative half-axes and upper/lower exponents. profile() first samples
512 dense points, accumulates perimeter distance, and blends arc-length and
uniform-angle spacing with the even parameter.
loft() samples station curves for axes, offsets, exponent, rotation, and
scale. Rounded caps use five rings whose radius follows
cos(πf/2)^0.85 and whose axial distance follows sin(πf/2).
loftSpine() fits a cubic path, computes centered tangents, and propagates its
normal by projection onto each new tangent plane. This parallel-transport frame
prevents the torsional flips produced by a fixed world-up cross product.
pillow() emits paired front and back surfaces from one closed outline. The
front bulge falls from a pole to the rim through pillowBulge; the back surface
retains explicit thickness. Panels therefore expose a real rim rather than a
single displaced sheet.
Semantic humanoid construction
The torso owns a continuous black core plus fitted chest, back, strap, box, recess, and wordmark panels. Front and back panel surfaces query the same core curves, so their offsets remain tied to the body rather than hand-positioned.
The head owns a closed shell split into a glossy visor and matte hood, a crown transition, seam curves, and an emissive sensor detail. Arms contain shoulder caps, upper-arm shells, ring transitions, elbow housings, ribbed pads, forearm frames, actuators, and wrists.
Each hand is a complete five-finger assembly. A wrist-local frame drives the palm, back plate, four finger chains, thumb chain, joints, pads, and structural members. Mirroring is performed in mesh data, after which winding is repaired.
The lower body owns a central pelvis, paired hip drums and covers, thigh cores and shells, knees, shin shells, ankle actuators, and moulded feet. The semantic groups remain separate even when they share a material; assembly identity is not sacrificed to draw-call reduction in this example.
Material identity system
The example defines fourteen named identities:
M_SHELL M_SHELL_LEG M_BLACK M_GLOSSBLACK
M_VISOR M_HELMET M_LED M_DARKMECH
M_ALU M_STEEL M_RUBBER M_FOOT
M_LOGO M_DARKGREY
Every identity is a MeshPhysicalNodeMaterial or MeshStandardNodeMaterial.
Base colour, metalness, roughness, IOR, specular intensity, clearcoat, sheen,
and emission are authored as one bundle.
The object-space noise is a signed Perlin field built from a Jenkins lookup3
integer hash, quintic fade, a 16-way gradient selector, and 0.9820 amplitude
scale. Fractional Brownian accumulation removes octaves after their period
falls below roughly two pixels:
band = 1 - smoothstep(0.25, 0.5, footprint × scale × octaveFrequency)
This derivative filter is part of the material. Without it, scales from 520
to 1400 m^-1 alias into slowly drifting blotches.
Roughness variation maps normalized noise from [0.25, 0.75] into each
material's declared base ± amount. Bump distance is exactly
strength × 0.0006 m. White shell, helmet composite, cast mechanism,
aluminium, rubber, foot polymer, and dark covers use different scale/detail/
roughness tuples; one generic noise node is not substituted across them.
Geometry emission and ownership
Before emission, pending booleans and bevels are applied. computeCornerNormals
groups face corners into smooth fans bounded by authored sharp edges, then
angle-weights the contributing face normals.
toGeometry() triangulates each polygon as a fan only at the final step. A
position is reused only when its corner normal matches within 1e-6; sharp
fans therefore receive separate vertices. Indices are grouped by material slot
and retain the mesh's ordered material-name table.
The returned root owns the seven semantic groups. Each emitted mesh owns one
BufferGeometry and references one or more of the fourteen shared materials.
Disposal releases every geometry and material without touching gallery lights,
camera, or floor.
Observed limits and defects
- The assembly is a fixed neutral pose. It does not include a skeleton, skin weights, joint limits, or collision envelopes for animation.
891,809triangles are appropriate for a close inspection model, not an unrestricted crowd population. Build a deliberate LOD rather than deleting small parts from the canonical assembly.- The BSP difference is exact for the declared polygon inputs but is not a general robust-solid kernel for arbitrary dirty meshes.
- Polygon fan triangulation assumes each emitted face is simple and convex after modifiers. Concave faces must be decomposed by their generator.
- Shared materials make global wireframe diagnostics inexpensive, but per-part destructive material mutation would affect every mesh using that identity.
- The geometry system repairs winding and normals; it does not replace a full inter-part clash, support, or articulation-clearance audit.
Diagnostics
The gallery exposes four deterministic views:
Complete assembly all seven semantic groups, final materials
Wireframe topology all final triangles with shared material identities
Upper body torso, head, arms, and hands only
Lower body hips, legs, and feet only
Always record the object count, triangle count, bounds, group counts, and material-name set. A count drift localizes lost or duplicated subassemblies before a screenshot could explain why the silhouette changed.
Failure diagnosis
If a panel floats, compare its surface query and offset against the owning core curve. If a mirrored part shades inside-out, run winding repair after the negative-axis transform. If a bevel pinches, inspect the selected edge angle, sector inset, and local edge length before reducing segment count.
If procedural materials crawl with camera motion, verify object-space position and derivative footprint filtering. If edges look melted, inspect corner-normal fan boundaries rather than lowering global smoothness. If a body region is missing, inspect the seven collection counts before geometry merging or scene staging.
Back to scottstts/Threejs-Awesome-Graphics-Agent-Skills or Agent skills.