Last updated: March 27, 2026
This guide is for contributors implementing or extending the MVP.
- 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.
HammerForge uses a coordinator + subsystems pattern:
plugin.gdhandles editor input and routes toLevelRoot. Uses stickyactive_rootwith deep recursive tree search.level_root.gdis 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) areRefCountedclasses that do the real work. Each receives aLevelRootreference in its constructor. input_state.gdis a state machine managing drag/paint modes.dock.gduses 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.
- DraftBrush nodes represent all authored geometry.
HFDragSystemmanages the two-stage draw lifecycle (base drag -> height click) and owns theHFInputStateinstance.HFExtrudeToolhandles face extrusion: picks a face viaFaceSelector, shows a preview, and commits a new DraftBrush on release. Supports Up (along face normal) and Down (opposite).HFBrushSystemhandles brush CRUD, pending/committed cuts, materials, picking, hollow, clip, tie/untie, move floor/ceiling, and UV justify. Failable operations (hollow, clip, delete) returnHFOpResultwith actionable fix hints.HFSnapSystemprovides centralized snapping with Grid, Vertex (brush corners), and Center modes._snap_point()delegates to it.HFInputStateexposesget_drag_dimensions()for live W x H x D display during drag gestures.- PendingCuts allow staging subtract operations before applying.
- 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.
- 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. HFPrototypeTexturesprovides 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.
HFUndoHelper.commit()wraps all editor actions with state snapshot restore on undo.- Command collation: pass a
collation_tagfor rapid operations (nudge, resize, paint). Consecutive actions with the same tag and samefull_statescope within 1 second merge into one undo entry viaMERGE_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 types and brush entity classes are data-driven via
hf_entity_def.gd. - Loaded from
entities.jsonor built-in defaults (func_detail, func_wall, trigger_once, trigger_multiple). - Dock brush entity class dropdown populated from definitions, not hardcoded.
- 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
HFInputStateenum.
- 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 live under LevelRoot/Entities or
is_entitymeta. - 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_outputsmeta. Fields: output_name, target_name, input_name, parameter, delay, fire_once. Managed viaadd_entity_output(),remove_entity_output(),get_entity_outputs(). Connections serialize with entity info in.hflevel.
RefCountedsubsystem showing wireframe AABB intersection overlays between additive and subtractive brushes.- Uses
ImmediateMeshwithPRIMITIVE_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_changedsignals for automatic updates. - Toggle via
show_subtract_previewexport on LevelRoot. Persisted in state settings.
HFPrefabcaptures 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 withHFLevelIOencoding for Godot types.HFPrefabLibrary(dock section) scansres://prefabs/and provides drag-and-drop.- Plugin handles
"hammerforge_prefab"drop type with raycast + snap + undo/redo.
- 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_stepin user prefs; resumes on editor restart. - Dock
highlight_tab()flashes the relevant tab on each step.
- 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.
HFStateSystemcaptures and restores brush/entity/paint/settings state for undo/redo.HFFileSystemhandles .hflevel save/load, .map import/export, and glTF export with threaded I/O.
- Input is handled by
plugin.gd(EditorPlugin) with typed references toLevelRootand the dock. LevelRootdelegates 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)
- Draw tool ->
- State changes go through
HFStateSystemfor undo/redo capture.
- 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
.hfrfiles 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.
Run gdformat --check addons/hammerforge/ and gdlint addons/hammerforge/ locally. These same checks run automatically on push/PR via .github/workflows/ci.yml.
- 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" `
-NoNewWindowViewport 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.
- Numeric input during draw
- Material atlasing and compression
- Additional import/export pipelines