Skip to content

feat: multi-region ceilings (tray ceilings, soffits, stepped ceilings)#247

Open
b9llach wants to merge 1 commit intopascalorg:mainfrom
b9llach:feat/multi-region-ceilings
Open

feat: multi-region ceilings (tray ceilings, soffits, stepped ceilings)#247
b9llach wants to merge 1 commit intopascalorg:mainfrom
b9llach:feat/multi-region-ceilings

Conversation

@b9llach
Copy link
Copy Markdown
Contributor

@b9llach b9llach commented Apr 16, 2026

What does this PR do?

Adds ceiling regions — sub-areas of a ceiling at a different height from the main plane — to model tray ceilings, soffits, and multi-height rooms. Each region is a polygon with its own height, rendered as a connected 3D shape with vertical skirt walls and vertex-color shading. The feature includes the full stack: schema, rendering system, level-stacking fix, wall-cavity fill, and editor UI.

Schema

New `CeilingRegion` type (`{ polygon, height, holes }`) and `CeilingNode.regions` field (`z.array(CeilingRegion).default([])`). Backward compatible — existing scenes without regions are unchanged.

Rendering

Each region polygon is subtracted from the main ceiling as a hole, then drawn as its own flat `ShapeGeometry` at the region's height. A vertical skirt strip connects the region plane to the main ceiling plane so tray wells and soffit drops render as connected 3D shapes instead of disconnected floating planes. Skirt vertices are darkened (~62% brightness) via a `color` vertex attribute + TSL `colorNode` multiplication, giving a visual depth cue under the unlit `MeshBasicNodeMaterial` shader.

Hole winding auto-correction ensures the triangulator cuts correctly regardless of whether the region polygon winds the same as or opposite to the outer ceiling contour.

Level stacking

`getLevelHeight` now scans `ceiling.regions` and picks the tallest surface (main ceiling or any region). When a tray region extends above the main ceiling, the upper level's floor automatically clears the tray instead of sitting at the main ceiling height and z-fighting with the region.

Wall cavity fill

Walls on a level extend to fill the structural cavity between the ceiling and the next floor's slab. The wall system reads the effective level height (including region heights) and inflates the wall's rendered height to match, closing the visual gap that would otherwise appear in orbit view between floors.

Editor UI

A "Regions" section in the ceiling panel mirrors the existing "Holes" section: Add / Edit / Delete / Done buttons + a per-region height slider. `CeilingRegionEditor` uses the generic `PolygonEditor` component with amber handles positioned at the region's own height. `editingRegion` state in `useEditor` is parallel to `editingHole` — a ceiling can't be in both edit states at once.

Utility

`packages/core/src/lib/polygon-uv.ts` — `dominantPolygonAngle` and `setAxisAlignedPlanarUVs` for axis-aligned UV mapping on slab and ceiling polygons. Used by both the ceiling system and the slab system.

How to test

  1. `bun dev`, open any scene with a ceiling.
  2. Select the ceiling → scroll to the "Regions" section → click "Add Region".
  3. A 1m default square appears at the ceiling centroid, 0.3m above the main ceiling. Drag the amber handles to reshape; use the height slider to adjust.
  4. From walkthrough mode, look up — the tray well should be visible with darkened skirt walls connecting the flat region to the main ceiling.
  5. In a multi-story scene, add a tray region on the ground floor ceiling. The upper level's floor should shift up to clear the tray. Walls should extend through the gap.
  6. `bun check`, `bun check-types`, `bun run build` all clean.

Checklist

  • I've tested this locally with `bun dev`
  • My code follows the existing code style (`bun check` passes on the touched files)
  • I've updated relevant documentation (N/A — no docs affected)
  • This PR targets the `main` branch

Adds support for ceiling regions — sub-areas of a ceiling at a
different height from the main plane. Used to model tray ceilings
(inner region raised above the main ceiling, creating a recessed
well), soffits (inner region dropped below, creating a lowered
panel), and multi-height rooms (e.g. a vaulted wing).

Schema:
- New `CeilingRegion` type: `{ polygon, height, holes }`.
- `CeilingNode.regions` field: `z.array(CeilingRegion).default([])`.
- Backward compatible — existing scenes without regions are unchanged.

Rendering (ceiling-system.tsx):
- Each region polygon is subtracted from the main ceiling shape as a
  hole, then drawn as its own flat `ShapeGeometry` at the region's
  height. A vertical skirt strip connects the region to the main
  ceiling plane so tray wells and soffit drops render as connected 3D
  shapes instead of disconnected floating planes. All sub-geometries
  (main plane + region planes + skirts) are merged into one
  BufferGeometry so the mesh stays single-draw-call.
- Vertex color attribute darkens skirt vertices (~62% brightness) so
  tray wells read as shaded under the unlit MeshBasicNodeMaterial
  shader. Ceiling renderer wires this via TSL colorNode
  (`vec3(baseColor).mul(vec3(attribute('color')))`).
- Hole winding auto-correction: holes whose signed area matches the
  outer contour are reversed before triangulation.

Level stacking (level-utils.ts):
- `getLevelHeight` now scans `ceiling.regions` and picks the tallest
  surface. An upper level's floor clears the tray automatically.

Wall cavity fill (wall-system.tsx):
- Walls on a level extend to fill the structural cavity if a ceiling
  region pushes the effective level height above the main ceiling.

Editor UI (ceiling-panel.tsx + ceiling-region-editor.tsx):
- "Regions" panel section with Add / Edit / Delete / Done + per-region
  height slider. Mirrors the existing "Holes" section pattern.
- `CeilingRegionEditor` tool using the generic `PolygonEditor` with
  amber handles at the region's own height.
- `editingRegion` state in `useEditor`, parallel to `editingHole`.

Utility:
- `packages/core/src/lib/polygon-uv.ts` — `dominantPolygonAngle` and
  `setAxisAlignedPlanarUVs` for axis-aligned UV mapping on slab and
  ceiling polygons.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant