threejs-procedural-geometry skill (Threejs-Awesome-Graphics-Agent-Skills)

From Public Agent Wiki

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

  1. Define units, local/world frames, dimensions, bounds, primary profiles, topology intent, shell thickness, apertures, material slots, and triangle band.
  2. Declare every required contact, support, clearance, penetration, reveal, moving envelope, and interaction datum.
  3. Choose a modeling operation for each visible form: profile extrusion, inset, loft, revolve, sweep, direct-topology aperture, solidify, subdivision, bevel, or fillet.
  4. Keep quads and n-gons through design. Preserve one named mesh per semantic part; do not weld unrelated parts or merge material slots yet.
  5. Apply modifiers in a deliberate order, then clean inside each part, repair winding, assign a part-specific smooth angle, and emit triangles.
  6. Run the polygon topology gate, then audit the named world assembly for coplanar overlap, defects, and substantial solid clashes.
  7. Run builder-owned semantic checks, including support, openings, shell thickness samples, ergonomic clearances, and moving extrema.
  8. 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 Object3D hierarchy 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

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 * 1024 hull atlas stores window cut-out, gold ornament, and soft grime channels;
  • a 512 * 512 Sobel-derived normal map stores the six-cell diamond quilt;
  • a 128 * 128 gauge 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 the 56 * 128 output 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:

  1. One continuous polygon mesh.
  2. A declared structural penetration with a narrow named allowance.
  3. A proud part with a readable offset.
  4. 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:

  1. Sort by minimum world X and sweep until inflated X bounds no longer meet.
  2. Reject Y/Z AABBs separated by more than 0.0015 m.
  3. Require abs(dot(ni, nj)) >= cos(0.0025).
  4. Require a vertex-to-plane distance no greater than 0.0015 m.
  5. Project both triangles into one in-plane basis.
  6. Clip one convex triangle against the other.
  7. 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:

  1. Compute the shared AABB and reject empty overlap.
  2. Reject a narrowly named intentional pair.
  3. Compute each mesh's minimum bound thickness.
  4. Set the required shared depth to min(0.03, max(0.004, 0.34 * min(thicknessA, thicknessB))) metres.
  5. Spatially hash triangles from one mesh inside the shared region. Triangles spanning more than 256 cells use a complete fallback candidate pass rather than disappearing from collision coverage.
  6. Test whether edges from either triangle pierce the other using a segment-restricted Möller–Trumbore test.
  7. 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; truncated is 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,809 triangles 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.