Skip to content

Add procedural elevator system with floorplan, runtime, and first-person support#305

Merged
wass08 merged 12 commits into
pascalorg:mainfrom
sudhir9297:feat/elevator-system
May 13, 2026
Merged

Add procedural elevator system with floorplan, runtime, and first-person support#305
wass08 merged 12 commits into
pascalorg:mainfrom
sudhir9297:feat/elevator-system

Conversation

@sudhir9297
Copy link
Copy Markdown
Contributor

What does this PR do?

This PR introduces a complete procedural elevator system across core, editor, viewer, floorplan, and first-person workflows.

It enables:

  • Fully configurable procedural elevators
  • Physical 3D shaft/cab rendering with animated runtime behavior
  • Integrated floorplan rendering and editing
  • Automatic slab/ceiling cutout generation
  • Elevator dispatching, floor servicing, and queue handling
  • First-person elevator interaction and movement support
  • Improved editor architecture and live-preview editing workflows

Elevator Core System

  • Added new ElevatorNode schema with support for:
    • Cab size
    • Shaft size
    • Wall thickness
    • Shaft style
    • Door style
    • Door panel style
    • Service range
    • Disabled floors
    • Service-only floors
    • Elevator speed
    • Door timing
    • Dwell timing

Elevator Tools & Editor Integration

  • Added elevator creation and move tools.

  • Integrated elevators into:

    • Structure tools
    • Command palette
    • Contextual tools
    • Selection manager
    • Tree panel
    • Side panel
  • Added full elevator side panel controls:

    • Position / rotation
    • Cab dimensions
    • Shaft dimensions
    • Shaft style
    • Door styles
    • Door panel types
    • Door size
    • Service floors
    • Default floor
    • Disabled/service-only floors
    • Destinations
    • Speed
    • Door timing
    • Dwell timing

Live Editing & Performance

  • Added live preview editing:
    • Position
    • Rotation
    • Dimensions
  • Updates render immediately while editing.
  • Scene store commits only on final commit/release.
  • Added live transform/live override store support for improved editing performance.

Elevator Rendering

  • Added physical 3D elevator rendering:
    • Shaft
    • Cab
    • Solid/glass shaft styles
    • Cab walls
    • Landing door frames
    • Cab doors
    • Call panels
    • Physical mesh buttons
    • Floor indicators

Door Styles & Cab Controls

  • Added door opening styles:

    • Center-opening
    • Single-left
    • Single-right
  • Added door panel styles:

    • Glass-frame
    • Solid-panel
    • Segmented-panel
  • Added inside-cab controls:

    • Destination buttons
    • Door-open button
  • Added landing call buttons outside elevator doors.


Runtime & Dispatch System

  • Added elevator runtime state:

    • Current floor
    • Target floor
    • Queue
    • Car Y position
    • Door openness
    • Animation phase
  • Added elevator movement system:

    • Speed handling
    • Door timing
    • Dwell timing
    • Queued destinations
    • Arrival/open-close behavior
    • Active/queued button states
  • Added shared dispatch system for multiple elevators:

    • Chooses best eligible elevator for landing calls

Floor Restrictions & Indicators

  • Added support for:

    • Disabled floors
    • Service-only floors
  • Applied restrictions across:

    • Dispatch
    • Side panel
    • Cab buttons
    • Landing calls
    • Floorplan markers
  • Added floor indicators:

    • Above landing doors
    • Inside cab
    • Shows:
      • Current floor
      • Target floor
      • Travel direction
  • Fixed mirrored floor numbers and inverted direction arrows.


Slab & Ceiling Cutouts

  • Added automatic slab and ceiling cutouts for elevator shafts.

  • Cutouts generated from:

    • Shaft footprint
    • Elevator service range
  • Added:

    • Polygon union helpers
    • Shared surface hole merging
  • Allows multiple cutouts to merge cleanly.


First-Person Support

  • Added first-person elevator interaction:

    • Button targeting
    • Cab and landing colliders
    • Closed-door blocking
    • Door-open entry behavior
  • Added player locking:

    • Player remains attached to moving elevator cab

Floorplan Support

  • Added elevator rendering in floorplan:

    • Shaft footprint
    • Cab footprint
    • Door opening side
    • Call station marker
    • Current/target/queued markers
    • Selection/hover/delete states
    • Glass/solid shaft styling
  • Added floorplan interactions:

    • Drag support
    • Resize handles
    • Hit testing
    • Marquee selection
  • Added served-range visualization:

    • Ordered stop markers
    • Disabled/service-only floors
    • Current floor
    • Target floor
    • Queued floors

Architecture Improvements

  • Refactored elevator architecture:
    • Opening sync logic moved into core
    • Renderer kept presentation-only
    • Viewer interactions moved into dedicated system
    • Editor tools no longer import viewer state directly

Related Structural & Viewer Updates

  • Expanded procedural column support:

    • Support styles
    • Presets/defaults
    • Richer rendering
    • Better placement/hit testing
  • Updated related systems for:

    • Ceilings
    • Slabs
    • Fences
    • Ground occluder
    • Node renderer registration
    • Post-processing
  • Ensured compatibility with elevator cutouts and rendering behavior.


Commit Highlights

  • Add procedural support column variants
  • Add column hit testing and align column defaults
  • Optimize elevator editing performance with live previews
  • Add cab door-open control and align elevator buttons
  • Add elevator floorplan overlay
  • Improve elevator floorplan resize and drag controls
  • Refactor elevator systems across core, viewer, and editor
  • Fix mirrored elevator indicators and arrows

sudhir9297 added 8 commits May 9, 2026 19:09
# Conflicts:
#	packages/editor/src/components/tools/item/move-tool.tsx
#	packages/editor/src/components/tools/tool-manager.tsx
#	packages/editor/src/components/ui/panels/panel-manager.tsx
#	packages/editor/src/store/use-editor.tsx
#	packages/viewer/src/components/renderers/site/site-renderer.tsx
#	packages/viewer/src/components/viewer/ground-occluder.tsx
#	packages/viewer/src/components/viewer/index.tsx
#	packages/viewer/src/components/viewer/post-processing.tsx
Copy link
Copy Markdown
Collaborator

@wass08 wass08 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Architectural review — feat/elevator-system

67 files / +7,017 / -139. Reviewed against the Pascal architecture rules (layer boundaries, hook hygiene, renderer/system separation, selector performance). Overall: clean architecture — one blocker, a few suggestions.


Blocker

Duplicate SurfaceHoleMetadata type definitionpackages/core/src/systems/elevator/elevator-opening-sync.ts:7-11 and packages/core/src/systems/stair/stair-opening-sync.ts:14-18 each redeclare the type already exported from packages/core/src/schema/nodes/surface-hole-metadata.ts. Two parallel sources of truth — when the schema gains a field (e.g. another hole source), the sync files will silently drift.

Fix: Import SurfaceHoleMetadata from ../../schema in both *-opening-sync.ts files; delete the local type.


Suggestions

  1. Asymmetric elevator lifecycle in use-interactive.tsinitElevator and setElevatorState exist, but there's no removeElevator to mirror removeDoorOpenState / removeWindowOpenState. Elevator entries leak when nodes are deleted.

  2. move-elevator-tool.tsx:59-60 temporal pause/resume isn't StrictMode-safe. The tool calls useScene.temporal.getState().pause() at mount and .resume() at unmount with no guard. Under StrictMode double-invoke (or if temporal is already paused upstream), you'll resume more times than you paused. Track locally: const wasPausedRef = useRef(false).

  3. Elevator level-bypass in selection-manager.tsx:59isNodeInCurrentLevel returns true for node.type === 'elevator' regardless of level. This is correct (elevators are building-scoped, not level-scoped), but the behaviour is non-obvious next to the neighbouring level checks. Worth a one-line comment.

  4. resolveElevatorSupportLevelId in packages/editor/src/lib/elevator-support.ts accepts preferredLevelId but ignores it when the node is undefined. Either honour the preferred ID in all branches or split placement-vs-resolution into two functions.

  5. surface-hole-metadata.ts enum gained 'elevator' alongside 'manual' / 'stair' — no comment explains that elevator sources are tracked via elevatorId (parallel to stairId) and consumed by syncAutoElevatorOpenings. Worth a one-liner since the coupling is invisible from the schema alone.


Nits

  • post-processing.tsx:134void projectId; looks like a leftover. Either drop the line or wire it into the effect's deps properly.
  • elevator-tool.tsx:34-47resolveCurrentBuildingId reads useScene.getState() at module scope. Works, but it's a pure function in disguise — move into lib/elevator-support.ts taking (buildingId, levelId, nodes) as args.
  • elevator-renderer.tsx at 1,310 lines is large but stays renderer-shaped: Three.js primitives + useNodeEvents + useRegistry, with geometry delegated to core. No rule violation. Flag for future: if accessibility/emergency panels push past ~1,500, split into elevator-cab + elevator-shaft sub-renderers.

What's clean (notable)

  • Zero editor imports in viewer code — no apps/editor, packages/editor, useEditor, or phase/mode references.
  • Core stays editor-agnostic — no Floorplan*/Paint*/Draft* types, no tool/phase vocabulary leaked into systems.
  • elevator-runtime-system.tsx / elevator-opening-system.tsx are .tsx in core but only use useFrame from @react-three/fiber — the documented pattern. No Three.js objects.
  • ElevatorNode schema uses objectId('elevator') + nodeType('elevator') and is registered in AnyNode.
  • ElevatorEvent typed in bus.ts and added to useNodeEvents config — renderers use useNodeEvents(node, 'elevator'), never emitter.emit directly.
  • ElevatorInteractionSystem (viewer system) does raycast + dispatch only — actual dispatch logic lives in core (resolveElevatorDispatchTarget, requestElevatorLevel, openElevatorDoor). Correct layering.
  • polygon-union.ts in viewer is correctly placed — pure rendering-time geometry, no scene state.
  • move-elevator-tool.tsx follows the live-drag exception correctly: mirrors writes into useLiveTransforms and sceneRegistry, cleans up on unmount.

Verdict

1 blocker, 5 suggestions, 3 nits — merge-ready once the duplicate type is removed.

Start with elevator-opening-sync.ts and stair-opening-sync.ts — same fix in both.

@sudhir9297 sudhir9297 requested a review from wass08 May 13, 2026 17:17
@wass08 wass08 merged commit 29fd673 into pascalorg:main May 13, 2026
1 check passed
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.

2 participants