Skip to content

Latest commit

 

History

History
185 lines (157 loc) · 12.3 KB

File metadata and controls

185 lines (157 loc) · 12.3 KB

HammerForge MVP Guide

Last updated: March 27, 2026

This guide is for contributors implementing or extending the MVP.

Goals

  • Fast in-editor greyboxing with draft brushes.
  • Bake to optimized meshes only when needed.
  • Keep editor responsiveness high (no live CSG while editing).
  • Modular architecture with clear subsystem boundaries.

Architecture Overview

HammerForge uses a coordinator + subsystems pattern:

  • plugin.gd handles editor input and routes to LevelRoot. Uses sticky active_root with deep recursive tree search.
  • level_root.gd is a thin coordinator (~1,100 lines) that owns containers, exports, and signals. All public methods delegate to one of 13 subsystem classes.
  • Subsystems (systems/*.gd) are RefCounted classes that do the real work. Each receives a LevelRoot reference in its constructor.
  • input_state.gd is a state machine managing drag/paint modes.
  • dock.gd uses 4 tabs (Brush, Paint, Entities, Manage) with collapsible sections (persisted state, separators, indented content) built programmatically. Selection tools appear contextually in Brush tab when brushes are selected. Compact toolbar with single-char labels.

See DEVELOPMENT.md for the full file tree and architecture conventions.

Core Systems

Brush Workflow (HFBrushSystem + HFDragSystem + HFExtrudeTool)

  • DraftBrush nodes represent all authored geometry.
  • HFDragSystem manages the two-stage draw lifecycle (base drag -> height click) and owns the HFInputState instance.
  • HFExtrudeTool handles face extrusion: picks a face via FaceSelector, shows a preview, and commits a new DraftBrush on release. Supports Up (along face normal) and Down (opposite).
  • HFBrushSystem handles brush CRUD, pending/committed cuts, materials, picking, hollow, clip, tie/untie, move floor/ceiling, and UV justify. Failable operations (hollow, clip, delete) return HFOpResult with actionable fix hints.
  • HFSnapSystem provides centralized snapping with Grid, Vertex (brush corners), and Center modes. _snap_point() delegates to it.
  • HFInputState exposes get_drag_dimensions() for live W x H x D display during drag gestures.
  • PendingCuts allow staging subtract operations before applying.

Floor Paint (HFPaintSystem + paint/*.gd)

  • Paint layers store grid occupancy in chunked bitsets with per-cell material_ids and blend weights (blend_weights, blend_weights_2, blend_weights_3).
  • Layers optionally have heightmaps (Image FORMAT_RF) for vertex displacement.
  • Geometry synthesis runs per dirty chunk:
    • Flat layers: greedy rectangles for floors, boundary edges + merged segments for walls.
    • Heightmap layers: per-cell displaced quads via HFHeightmapSynth, walls still flat.
  • Per-chunk blend images drive a four-slot shader (hf_blend.gdshader).
  • Blend tool paints material blend weights (slots B/C/D) on already-filled cells.
  • Auto-connectors (HFConnectorTool) generate ramp/stair meshes between layers.
  • Foliage populator (HFFoliagePopulator) scatters MultiMeshInstance3D with height/slope filtering.
  • Stable IDs are used to reconcile generated nodes without churn.

Face Materials + Surface Paint (HFPaintSystem)

  • DraftBrush faces store material indices, UVs, and paint layers.
  • Materials are managed by a shared palette (MaterialManager) with library persistence (save/load JSON) and usage tracking.
  • HFPrototypeTextures provides 150 built-in SVG textures (15 patterns x 10 colors) loadable via the "Refresh Prototypes" button for rapid material testing.
  • Surface paint writes per-face weight images and updates previews.

Undo/Redo (HFUndoHelper + HFStateSystem)

  • HFUndoHelper.commit() wraps all editor actions with state snapshot restore on undo.
  • Command collation: pass a collation_tag for rapid operations (nudge, resize, paint). Consecutive actions with the same tag and same full_state scope within 1 second merge into one undo entry via MERGE_ENDS.
  • Transactions: state_system.begin_transaction() / commit_transaction() / rollback_transaction() for atomic multi-step operations.
  • State-tracked scaffolding: capture_state() / restore_state() covers brushes, entities, paint layers, TempFloor, and DefaultSun. All scaffolding created by "New HammerForge Level" round-trips through undo/redo.

Entity Definitions (HFEntityDef)

  • Entity types and brush entity classes are data-driven via hf_entity_def.gd.
  • Loaded from entities.json or built-in defaults (func_detail, func_wall, trigger_once, trigger_multiple).
  • Dock brush entity class dropdown populated from definitions, not hardcoded.

Gesture Tracker (HFGesture)

  • Base class for encapsulated input gestures (hf_gesture.gd).
  • Holds root, camera, positions, numeric buffer. Subclasses override update(), commit(), cancel().
  • New tools should subclass this instead of adding modes to the HFInputState enum.

Bake (HFBakeSystem)

  • Assembles DraftBrushes (including generated flat paint geometry) into mesh output via CSG.
  • Heightmap floor meshes are duplicated directly into baked output (bypass CSG) with trimesh collision.
  • Supports single and chunked baking modes.
  • Optional: mesh merging, LODs, lightmap UV2, navmesh, collision.

Entities (HFEntitySystem)

  • Entities live under LevelRoot/Entities or is_entity meta.
  • Entities are excluded from bake.
  • Definitions are loaded from addons/hammerforge/entities.json.
  • Entity I/O: Source-style input/output connections stored as entity_io_outputs meta. Fields: output_name, target_name, input_name, parameter, delay, fire_once. Managed via add_entity_output(), remove_entity_output(), get_entity_outputs(). Connections serialize with entity info in .hflevel.

Subtract Preview (HFSubtractPreview)

  • RefCounted subsystem showing wireframe AABB intersection overlays between additive and subtractive brushes.
  • Uses ImmediateMesh with PRIMITIVE_LINES (same 12-edge box pattern as cordon wireframe).
  • Debounced rebuild (0.15s), MeshInstance3D pool (max 50 entries).
  • Connects to brush_added, brush_removed, brush_changed signals for automatic updates.
  • Toggle via show_subtract_preview export on LevelRoot. Persisted in state settings.

Prefabs (HFPrefab + HFPrefabLibrary)

  • HFPrefab captures brush + entity selections with transforms relative to the group centroid.
  • capture_from_selection() computes centroid, strips brush_id/group_id, stores infos as dictionaries.
  • instantiate() assigns new IDs, offsets transforms by placement position, remaps entity I/O connections.
  • save_to_file() / load_from_file() use JSON with HFLevelIO encoding for Godot types.
  • HFPrefabLibrary (dock section) scans res://prefabs/ and provides drag-and-drop.
  • Plugin handles "hammerforge_prefab" drop type with raycast + snap + undo/redo.

Tutorial Wizard (HFTutorialWizard)

  • 5-step interactive tutorial replacing the static welcome panel.
  • Each step listens for a specific LevelRoot signal (brush_added, paint_layer_changed, entity_added, bake_finished).
  • Optional validation (e.g. step 2 checks that the brush operation is SUBTRACTION).
  • Progress persisted via tutorial_step in user prefs; resumes on editor restart.
  • Dock highlight_tab() flashes the relevant tab on each step.

Brush Operations (HFBrushSystem extended)

  • Hollow (Ctrl+H): creates 6 wall brushes, deletes original. Configurable wall thickness.
  • Clip (Shift+X): splits a brush along an axis-aligned plane. Preserves material, entity class, visgroups, group ID.
  • Merge (Ctrl+Shift+M): combines 2+ selected brushes into a single brush. Applies full transform (rotation/scale) to face vertices, preserves per-brush materials via per-face material indices.
  • Tie/Untie: tag brushes as brush entity classes (func_detail, func_wall, trigger_once, trigger_multiple). Tagged brushes get color-coded overlays and may be excluded from structural bake.
  • Move to Floor/Ceiling (Ctrl+Shift+F/C): raycasts against other brush AABBs to snap vertically.
  • UV Justify: fit/center/left/right/top/bottom/stretch/tile alignment for selected faces.

Persistence (HFFileSystem + HFStateSystem)

  • HFStateSystem captures and restores brush/entity/paint/settings state for undo/redo.
  • HFFileSystem handles .hflevel save/load, .map import/export, and glTF export with threaded I/O.

High-Level Flow

  1. Input is handled by plugin.gd (EditorPlugin) with typed references to LevelRoot and the dock.
  2. LevelRoot delegates to the appropriate subsystem:
    • Draw tool -> HFDragSystem (drag lifecycle + preview)
    • Extrude Up/Down -> HFExtrudeTool (face pick + drag + commit)
    • Select tool -> HFBrushSystem (picking + selection)
    • Paint (floor) -> HFPaintSystem -> HFPaintTool (layers + synth + reconcile)
    • Paint (surface) -> HFPaintSystem -> SurfacePaint (per-face weight images)
    • Face selection -> HFBrushSystem -> FaceSelector
    • Bake -> HFBakeSystem (CSG assembly + mesh output)
    • Save/load -> HFFileSystem (threaded I/O)
  3. State changes go through HFStateSystem for undo/redo capture.

Testing Checklist

  • Create and resize draft brushes (Draw tool).
  • Extrude Up (U) and Extrude Down (J) on brush faces; confirm new brushes appear with correct orientation.
  • Right-click during extrude to cancel; confirm preview is removed.
  • Apply/clear/commit subtract cuts.
  • Verify pending cuts appear orange-red, applied cuts turn standard red.
  • Bake (with and without chunking).
  • Enable Paint Mode and test Paint tab → Floor Paint section Brush/Erase/Line/Rect/Bucket.
  • Switch brush shape (Square/Circle) and verify radius fills correctly for each.
  • Test paint tool shortcuts: B/E/R/L/K in Paint Mode.
  • Verify live paint preview while dragging.
  • Switch paint layers and ensure isolation.
  • Enable Region Streaming, paint across regions, and verify .hfr files are created on save.
  • Import a heightmap (PNG/EXR) or generate procedural noise on a paint layer.
  • Verify displaced mesh appears under Generated/HeightmapFloors.
  • Adjust Height Scale and Layer Y spinboxes; confirm mesh updates.
  • Select Blend tool, choose Blend Slot B/C/D, paint blend weights on filled cells; verify four-slot shader blending.
  • Bake with heightmap floors and confirm baked output includes heightmap meshes with trimesh collision.
  • Save and load .hflevel with heightmap data; verify heightmap, material_ids, blend_weights, and terrain slot settings persist.
  • Click Refresh Prototypes in Paint tab → Materials section; confirm 150 prototype textures appear in the palette.
  • Assign a prototype texture to faces and verify preview updates.
  • Create a material resource (.tres/.material) in materials/ and add it to the palette.
  • Enable Face Select Mode and assign materials to faces.
  • Edit UVs and ensure preview updates.
  • Surface paint on a face with two layers and verify blending (Paint Target = Surface).
  • Toggle Use Face Materials and compare bake output.
  • Drag entities from the palette and check selection/exclusion from bake.
  • Verify shortcut HUD updates when switching tools/modes.
  • Verify tooltips appear on all dock controls.
  • Verify selection count appears in status bar.
  • Trigger a bake failure and confirm red error message with auto-clear.
  • Click New HammerForge Level in Manage tab; confirm floor, sun, and spawn appear. Undo/redo and verify all three round-trip.
  • Run Quick Play after New Level; confirm playtest lighting matches editor sun angle.

CI

Run gdformat --check addons/hammerforge/ and gdlint addons/hammerforge/ locally. These same checks run automatically on push/PR via .github/workflows/ci.yml.

Diagnostics

  • Enable Debug Logs in the Manage tab → Settings section for runtime tracing.
  • Capture exit-time errors with:
Start-Process -FilePath "C:\Godot\Godot_v4.6-stable_win64.exe" `
  -ArgumentList '--editor','--path','C:\hammerforge' `
  -RedirectStandardOutput "C:\Godot\godot_stdout.log" `
  -RedirectStandardError "C:\Godot\godot_stderr.log" `
  -NoNewWindow

Resolved Issues

  • Viewport drag-marquee selection is disabled. Marquee selection for brushes, entities, and faces was added in the Improved Selection & Multi-Select wave.
  • Multi-select can cap at 2 items in the viewport. Multi-select works for arbitrary counts via marquee or Ctrl+Click.

Next Steps

  • Numeric input during draw
  • Material atlasing and compression
  • Additional import/export pipelines