Replace per-tool rule trees (.cursor/rules, .claude/rules, .codex/rules) with a single wiki/architecture/ source — 11 pages + README — readable as plain markdown by any agent. Canonical skills live in .agents/skills/; .claude/skills, .cursor/skills, .codex/skills are directory symlinks. AGENTS.md is the entrypoint (rewritten as a lean overview, no per-tool path lists). CLAUDE.md, GEMINI.md, and .github/copilot-instructions.md all point to it. Add open-pr skill (uses .github/pull_request_template.md as the source of truth for the PR body) and remove the dangling .claude/CLAUDE.md relative symlink. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
56 lines
2.6 KiB
Markdown
56 lines
2.6 KiB
Markdown
# Three.js Layers
|
|
|
|
*Three.js layer conventions — which layer each object type lives on and why.*
|
|
|
|
Applies to: `packages/viewer/**`, `apps/editor/**`.
|
|
|
|
Three.js `Layers` control which objects each camera and render pass sees. We use them to separate scene geometry, editor helpers, and zone overlays into distinct rendering buckets without duplicating scene structure.
|
|
|
|
## Layer Map
|
|
|
|
| Constant | Value | Package | Purpose |
|
|
|---|---|---|---|
|
|
| `SCENE_LAYER` | `0` | `@pascal-app/viewer` | Default Three.js layer — all regular scene geometry |
|
|
| `EDITOR_LAYER` | `1` | `apps/editor` | Editor-only helpers: grid, tool previews, cursor meshes, snap guides |
|
|
| `ZONE_LAYER` | `2` | `@pascal-app/viewer` | Zone floor fills and wall borders — composited in a separate post-processing pass |
|
|
|
|
Import the constants from their owning packages:
|
|
|
|
```ts
|
|
// In viewer code
|
|
import { SCENE_LAYER, ZONE_LAYER } from '@pascal-app/viewer'
|
|
|
|
// In editor code
|
|
import { EDITOR_LAYER } from '@/lib/constants'
|
|
```
|
|
|
|
## Why Separate Zones onto Layer 2
|
|
|
|
Zones use semi-transparent, `depthTest: false` materials that must be composited *on top of* the scene without being fed into SSGI or TRAA. The post-processing pipeline in `post-processing.tsx` renders a dedicated `zonePass` with a `Layers` mask that enables only `ZONE_LAYER` (and disables `SCENE_LAYER`), then blends its output into the final composite manually:
|
|
|
|
```ts
|
|
const zoneLayers = useMemo(() => {
|
|
const l = new Layers()
|
|
l.enable(ZONE_LAYER)
|
|
l.disable(SCENE_LAYER)
|
|
return l
|
|
}, [])
|
|
|
|
zonePass.setLayers(zoneLayers)
|
|
```
|
|
|
|
This keeps zones out of the SSGI depth/normal buffers (which would produce incorrect AO on transparent surfaces) while still letting them appear correctly over the scene.
|
|
|
|
## Why Separate Editor Helpers onto Layer 1
|
|
|
|
The editor camera enables `EDITOR_LAYER` so tools and helpers are visible during editing. The thumbnail generator disables `EDITOR_LAYER` so exports show clean geometry without snap lines or cursor spheres.
|
|
|
|
## Rules
|
|
|
|
- **Never hardcode layer numbers.** Always use the named constants.
|
|
- **`SCENE_LAYER` and `ZONE_LAYER` belong in `@pascal-app/viewer`** — they are renderer concerns, not editor concerns.
|
|
- **`EDITOR_LAYER` belongs in `apps/editor`** — the viewer must never import it; editor behaviour is injected via props/children.
|
|
- **Zone meshes must set `layers={ZONE_LAYER}`** so they are picked up by `zonePass` and excluded from `scenePass` depth buffers.
|
|
- **Editor helper meshes must set `layers={EDITOR_LAYER}`** so they are invisible to the thumbnail camera and the viewer's render passes.
|
|
- **Do not add new layers without updating this page** and the post-processing pipeline accordingly.
|