Pascal 3D Building Editor

repository·main·Indexed 12 days ago

https://github.com/pascalorg/editor

A 3D building editor built with React Three Fiber and WebGPU, utilizing a node-based data model for complex architectural scenes. The ecosystem includes @pascal-app/core for scene state management, @pascal-app/nodes for geometry builders, a CLI for editor and MCP service management, and an IFC to Pascal converter for transforming Industry Foundation Classes (IFC 2x3 and IFC4) models into scene-graph JSON.

Tokens
102K
Snippets
279
Records
491
Agent score
95%

What's inside Pascal

  1. Overview of Pascal Editor Architecture

    main

    The Pascal Editor architecture is organized into several specialized domains. When working with core packages like @pascal-app/core, @pascal-app/viewer, @pascal-app/editor, or @pascal-app/mcp, developers should follow the canonical rules defined in the architecture documentation.

    Key architectural domains include:

    • Rendering & Layers: Three.js layer constants and rendering separation.
    • Systems & Renderers: Core/viewer systems and the node renderer pattern.
    • Node Management: Node definitions (geometry/renderer/system composition), node schemas (Zod-based), and the Scene Registry.
    • Interaction & Tools: The InteractionScope state machine, editor tools, and spatial queries (placement validation).
    • Selection: Two-layer selection (viewer + editor) and selection groups.
    • Content Authoring: Contracts for GLB catalog items and plugin authoring.
  2. Overview of IFC → Pascal Converter

    main

    The IFC → Pascal Converter is a web application designed to convert IFC (Industry Foundation Classes) building models into Pascal scene-graph JSON. The application allows users to drop in .ifc files, inspect the extracted elements, preview the resulting scene in the @pascal-app/viewer, and download the generated JSON for use in the Pascal editor.

    Note: This tool is in early alpha. Users should expect rough edges such as misplaced elements, missing geometry, or unmapped element types. It is intended for previewing and iteration rather than production workflows.

  3. Use @pascal-app/ifc-converter for IFC to Pascal scene graph conversion

    main

    @pascal-app/ifc-converter provides pure conversion logic to transform IFC data into Pascal scene graphs.

    Key Characteristics:

    • Input: A Uint8Array containing the raw IFC bytes.
    • Output: An object containing { nodes, rootNodeIds, stats }, which follows the schemas defined in @pascal-app/core.
    • Environment: This is a headless package. It contains no DOM or React dependencies. It is designed for use in non-UI environments (like workers or CLI tools) or as the logic engine for a UI (like the ifc-converter-app).
  4. Explore the @pascal-app/mcp package layout

    main

    The @pascal-app/mcp package implements a Model Context Protocol (MCP) server for the Pascal 3D editor. The package is organized into several functional areas:

    • src/bridge/: Contains the SceneBridge class and node shims used to connect the MCP server to the editor's scene state.
    • src/tools/: Contains the core tool implementations (e.g., get_scene, apply_patch, create_wall) and their Zod schemas.
    • src/resources/: Provides read-only access to scene data, catalogs, and constraints.
    • src/prompts/: Contains pre-defined agent prompts for tasks like generating designs from briefs or renovating from photos.
    • src/transports/: Supports stdio and http communication methods.
    • bin/pascal-mcp.ts: The CLI entry point for running the server.
  5. Understand the Pascal Editor package architecture

    main

    The project is a Turborepo monorepo with the following package responsibilities:

    • @pascal-app/core: Node schemas, scene state (Zustand), registry contracts, spatial queries, and event bus.
    • @pascal-app/viewer: 3D rendering via React Three Fiber, shared render systems, default camera/controls, and post-processing.
    • @pascal-app/editor: Editing tools, panels, selection, and direct-manipulation UI.
    • @pascal-app/nodes: Built-in registry plugin with node definitions, renderers, geometry, and systems.
    • @pascal-app/cli: Installs and manages a versioned standalone editor runtime and persistent local data.
    • @pascal-app/mcp: Exposes scene tools, resources, prompts, and local storage to MCP-compatible AI hosts.
    • @repo/ui: Shared UI components.
    • apps/editor: Standalone Next.js host for the editor packages.
  6. What is the Scene Registry?

    main

    The Scene Registry is a global, mutable map that links node IDs to their live THREE.Object3D instances. It is designed to provide $O(1)$ lookups for systems and selection managers, avoiding the need for expensive tree traversals through the Three.js scene graph.

    It consists of two main parts:

    1. nodes: A primary Map<string, THREE.Object3D> for direct ID-to-object lookups.
    2. byType: A collection of Set<string> objects, where each set contains the IDs of nodes belonging to a specific type (e.g., wall, slab, item), allowing systems to iterate over specific categories of objects efficiently.
  7. Use the three-checkbox model for NodeDefinitions

    main

    When defining a node kind, select from these three independent fields to match its requirements:

    FieldPurposeWhen to use
    geometryA pure builder function: (node, ctx, shading, textures, colorPreset, sceneTheme) => Object3D.When the kind has parametric meshes that must rebuild when updateNode is called.
    rendererAn optional React component loader: () => Promise<{ default: ComponentType<{ node }> }>.When the kind needs JSX features like <Html>, useGLTF, drei helpers, or TSL shader materials.
    systemAn optional per-frame component loader: () => Promise<{ default: ComponentType }>.When the kind needs imperative per-frame work like animations, opacity transitions, or material poking.

    Note: These fields are independent. If you provide a renderer, it overrides the generic <ParametricNodeRenderer>, but the GeometrySystem will still run if geometry is also provided.

  8. How the Scene Registry and Renderers work together

    main

    The Scene Registry maps node IDs to Three.js Object3D instances for fast lookup, allowing systems to access 3D objects without traversing the scene graph.

    Node Renderers are React components that create these Three.js objects. They follow a specific pattern:

    1. Create a placeholder mesh/group.
    2. Register the reference with the registry using the useRegistry hook.
    3. Systems then update the geometry based on the node's data.

    Using useRegistry

    const ref = useRef<Mesh>(null!)
    useRegistry(node.id, 'wall', ref)
  9. Understand the `useLiveTransforms` coordinate frames

    main

    The useLiveTransforms store does not use a uniform coordinate frame; the meaning of position and rotation depends on the node kind. Consumers (like 2D renderers) must handle these frames explicitly:

    Writerposition framerotation frame
    usePlacementCoordinator (item floor / wall / ceiling)world plan (level-local)world Y
    door / window move toolswall-localwall-local (0 or π)
    slab / ceiling / fence / polygon-based moversposition delta ([Δx, 0, Δz])unused / 0
    column / roof / elevator / spawn / single-position kindsworld planworld Y

    Note: For polygon-based movers (e.g., slab, ceiling), position is a delta applied to the polygon vertices, not an absolute position.

  10. Understand @pascal-app/mcp Limitations

    main

    When using the MCP package, be aware of the following constraints:

    • GLB Export: export_glb is currently not_implemented because it requires a browser-based Three.js renderer.
    • Vision Tools: Require the MCP host to support the createMessage sampling capability (e.g., Claude Desktop supports this; some other clients may not).
    • Catalog Size: The built-in catalog is intentionally small. Host applications can extend this by providing additional tools.
    • Headless Geometry: Systems like wall mitering or slab triangulation run in React hooks within the editor. In headless mode, derived geometry is not regenerated, though all node data remains manipulable. For rendered geometry, use @pascal-app/viewer in a browser.
    • Asset Resolution: loadAssetUrl and saveAsset are browser-only. If using items with asset://<id> URLs in a Node environment, you must provide absolute or data: URLs.
    • Dirty Nodes: In headless mode, dirtyNodes accumulates because there is no renderer to consume them. Call bridge.flushDirty() if you need to observe these changes.
  11. Manage vertical reactivity and dependencies

    main

    Reactivity in the vertical model is explicit. Changes to certain properties trigger a cascade of updates to dependent nodes:

    • level.height changes: Dirties walls, stairs, ceilings, and fences for that level.
    • Slab changes: Dirties overlapping same-level supports, the level below's walls/ceilings, and deck-attached stairs.
    • Terrain changes: Every live terrain dab dirties ground-hosted structures, fillToTerrain walls/slabs, every floorPlaced node at grade, and any kind that used ctx.levelBaseAt in its builder (via the useLiveTerrain subscription).

    Important Lifecycle Notes:

    • Stroke-based updates: While live dabs update via useLiveTerrain, the actual scene graph and undo history are only written once when the stroke commits.
    • Auto-room re-derivation: This is per-stroke, not per-dab. Mid-drag, ground-hosted walls follow the brush, but floors wait until the stroke is released to prevent jitter and excessive scene writes.
    • Stair Rises: Straight stairs build from stored segment heights. To change a rise, you must use syncStairRises (applied by StairOpeningSystem). This runs one microtask after store updates to ensure the spatial grid has settled.
  12. Understand measurement snapping and magnetic behavior

    main

    Measurement snapping is unconditionally magnetic for walls, semantic features, and axes, regardless of the construction snapping-mode (grid, lines, angles, or off).

    Key behaviors:

    • Bypassing Magnetism: Hold Alt to temporarily release axis pull, wall magnetism, and 2D projected-geometry pull. In this mode, semantic features only bind at a strict contact tolerance of 0.012 m.
    • Grid Snapping: Free measurement points do not quantize to the construction grid. However, the volume extrusion height does quantize to the grid step when in grid mode.
    • 2D Priority: In 2D, a discrete wall snap (endpoint, midpoint, or crossing) takes precedence over a locked axis pull.
    • Axis Assistance: Axis snapping (X, Y, or Z) starts from the previous vertex and only triggers if the projected candidate is verified on the hit surface within a screen-space threshold. A magnetic lock engages at 16 screen pixels and releases at 24 pixels.