gaoypeng/3dcodeverse/c3d-threejs-forms
Claude Code and agent skill by gaoypeng/3dcodeverse
- community
- ext:[{"type":"plugin","description":"Custom plugin integration"}]
Checking that the SKILL.md file is still in the repository.
Use when writing three.js object geometry in src/object.js or src/parts/*.js on the static_object track, in any session kind. Maps each FORM word in your manifest to the three.js geometry maker that produces it, names the traps that make a threejs part silently ship nothing, and states that this language has no boolean/CSG at all, so cavities and shells are built into the profile instead of subtracted. Options, not a recipe: what the part is made of is the brief's.
Install
npx vskill@latest install gaoypeng/3dcodeverse/c3d-threejs-formsSemi-extensibleMentions customization, but needs skill-specific knowledge. Read the extensible skills standard
SKILL.md
FORM to three.js
When to use
You have a manifest (or a plan part row) and you are about to write src/parts/<snake>.js. Look up each FORM word, pick the maker that produces it, move on. Nothing here says what a chair or a lamp is made of; the brief and the plan do.
The table
The FORM words in the left column key the blender and cadquery tables too, so only this column changes when the language does.
| form | three.js | notes | |
|---|---|---|---|
| revolved profile | LatheGeometry(points, seg) | points are Vector2(radius, y), spun about Y. Normals point out when the radius goes 0 → R → 0 bottom-up | |
| extruded outline | ExtrudeGeometry(shape, {depth, bevelEnabled:true, bevelSize, bevelSegments}) | extrudes along +Z; .rotateX(-Math.PI/2) to lay it flat with thickness along Y | |
| tube swept along a path | TubeGeometry(new CatmullRomCurve3(pts), tubular, radius, radial, closed) | the clean way to a handle, cable, rail or hoop | |
| slab | BoxGeometry(w,h,d), or RoundedBoxGeometry(w,h,d,seg,r) from three/addons/geometries/RoundedBoxGeometry.js | the rounded one is the cheap way to a soft edge where the brief or the judged view wants one | |
| shaft | CylinderGeometry(r, r, h, radial) | axis is Y; rotate the group, not the geometry, when you need another axis | |
| tapered solid | CylinderGeometry(rTop, rBottom, h, radial) or ConeGeometry(r,h,seg) | two different radii give a taper at no triangle cost | |
| ring or band | TorusGeometry(R, r, radial, tubular) | lies in the XY plane; .rotateX(Math.PI/2) to lay it flat | |
| carved cavity | no boolean exists here — put the recess in the profile: a Shape with a Path pushed into shape.holes, a LatheGeometry profile that climbs the outside and comes back down the inside, or slabs around the opening | see "There is no CSG" below | |
| shell with wall thickness | a LatheGeometry whose profile traces outer wall up, across the rim, inner wall down | one geometry, watertight, with a visible open rim | |
| mirror pair | build once, add twice with mesh.position.x = ±d | never scale.set(-1,1,1): a negative scale inverts the winding and exports reversed normals | |
| radial array | one geometry, InstancedMesh(geo, mat, N) with M.makeRotationY(i/N * 2π) | derive the angle from N; see the instancing trap below | |
| layered or inset | a thin second slab slightly proud of or sunk into the face, in its own material | the cheapest panel break there is | |
| faceted solid | raw BufferGeometry: Float32BufferAttribute positions + setIndex + `computeVertexNormals()` | without the normals call it shades black | |
| lofted transition | LatheGeometry when it is round-to-round; otherwise a raw BufferGeometry ring-to-ring strip | three r182 has no loft primitive |
mergeGeometries from three/addons/utils/BufferGeometryUtils.js collapses many static pieces of one part into one mesh. Merge inputs must share the same attribute set (geo.deleteAttribute('uv') or toNonIndexed() everything) or it returns null, and call computeVertexNormals() after.
Segments and triangles
Under-segmenting is the most common reason a lathe or a tube reads as a prism in the render sheet. Segment counts are not measured; the triangle total is — the depth gate WARNs below 350 triangles per built object and ERRORs above 20 000 (the ceiling for the whole object is 600 000). A SphereGeometry(r, 64, 64) repeated a hundred times is 800 k triangles and over that ceiling; the detail belongs on the shapes that fill the silhouette.
There is no CSG
runtime_js/package.json ships three and puppeteer only — there is no three-bvh-csg, no Brush, no Evaluator, and the lint allows imports of three, three/addons/* and relative files under src/ and nothing else. So a boolean operation is not available to you at all, in either direction.
That changes how you plan a cavity. A cup is not "a cylinder minus a smaller cylinder"; it is one lathe profile that goes up the outer wall, across the rim and back down the inner wall. A bracket with a bolt hole is not a plate minus a cylinder; it is a Shape with an absarc Path in shape.holes. A slot in a face is slabs around the opening, or a hole in the extrude outline. Each of those is one geometry, stays watertight, and exports cleanly — which subtraction in this language would not, because it does not exist.
The traps that actually bite
- The file name is the export name.
src/parts/seat_cushion.jsmust
export function buildSeatCushion(THREE) — the lint derives the expected name from the file stem (codeverse3d/languages/threejs/__init__.py). A misspelt name is a WARN; no build* export in the file at all is an ERROR.
- A part file nobody imports ships nothing.
src/object.jsmust import each
part builder and add its Group to the root. An unimported src/parts/*.js is a WARN ("dead part file?"), the build succeeds, and the part is simply absent from the GLB — this is a real, recorded failure mode in the blender track and the same lint rule exists here.
- An `InstancedMesh` reads as N separate pieces. The harness replaces an
InstancedMesh named Bolts with sibling meshes Bolts_0 … Bolts_{n-1} (matrices baked), so the connectivity gate measures every copy on its own. In runs/e2e_bench_threejs a FastenerBolts part produced 39 tiny disconnected island warnings: copies that touch nothing are islands or floaters, copies within the gate's 2 mm of the surface they fasten are contacts.
- Never resize with `root.scale`. Writing your dimensions in one unit and
then setting root.scale.set(0.01, ...) to convert makes the bounds and the contract gate disagree, because they read the scaled and unscaled geometry inconsistently. Build the geometry at the size the contract asks for.
- `ShaderMaterial` and every texture are lost. GLB export keeps
MeshStandardMaterial / MeshPhysicalMaterial and vertex colours only. The module is imported in node with no DOM, so TextureLoader / ImageLoader / FileLoader and any document or window reference are hard lint ERRORs — colour, roughness, metalness and vertex colours are the whole palette.
- Faceted or black shading after any manual edit → you skipped
computeVertexNormals().
- `mesh.position` versus `geometry.translate()`. Position moves the pivot
and is what you want for a part placed at its world pose; .translate() bakes the offset into the vertices and is what you want before a merge. Doing both by accident doubles the offset.
- Names that no longer exist in r182 (
mergeBufferGeometries,three/examples/js/*,
Geometry, Face3, sRGBEncoding, outputEncoding) are listed in the contract's Imports section; each is an undefined import, not a deprecation.
What the part must return
One THREE.Group per plan part, returned at its world pose and added to the root in src/object.js. Everything inside it — merged detail, instanced repeats, raw BufferGeometry — is invisible to the part-level gates, which is exactly why the detail belongs in there. The object's naming and frame rules are in the contract; dimensions and tolerances belong to c3d-bbox-contract; contact and overlap belong to c3d-part-contact.
Strong options (none of them required)
Merging without islands. mergeGeometries keeps every input's vertices, so two pieces that merely overlap stay two shells inside one mesh and the connectivity gate counts the small one as stray geometry. Merge only what is genuinely one piece, make seams share vertices where they can, and normalise the inputs first (toNonIndexed(), deleteAttribute('uv')) so the merge does not return null.
Contact by construction. The gate counts a surface gap of 2 mm or less as contact (flush included) and penetration beyond 2 mm as a WARN, so a part whose face is computed from its neighbour's bounds lands inside that band without nudging.
Where triangles pay. The depth gate measures the total only, so spend it on the silhouette and on the faces the judged views see — the front, eye height, where two materials meet — inside the plan part, not on the underside, the back or interior volumes. A radius on the edges hands touch is what reads as manufactured; a subdivided flat panel reads as nothing.
Depth
references/form_recipes.md — each form word under its r182 API name with the trap that bites it, including the cavity and shell profiles that replace the boolean you do not have.
Security
- Security scanPassed, 90 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingharness/codeverse3d/skills/c3d-threejs-forms/SKILL.md
Popularity
- Repository stars
- 176
- Repository forks
- 12
- 7-day trend score
- 0
Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.
Extension points
pluginCustom plugin integration
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/gaoypeng/3dcodeverse/c3d-threejs-forms)