Add project files.

This commit is contained in:
alejo committed 2026-10-25 03:29:04 -03:00
commit a9ae435321
52 files changed
+65378

No files matched your search

+183
View File
@@ -0,0 +1,183 @@
# Hammer++
A level editor in the spirit of Valve Hammer, written in **C++17 with raylib, Dear ImGui (via rlImGui) and CMake**.
It edits two kinds of geometry side by side and compiles both into one renderable, collidable, exportable map.
| | **Brushes** (BSP) | **Meshes** |
|---|---|---|
| Representation | convex solid = intersection of planes | free-form polygon mesh |
| Best for | rooms, walls, floors, collision | curves, terrain, organic detail, imported OBJ |
| Compile | CSG (hidden faces removed) + BSP collision tree | straight to render batches, smooth normals |
| Tools | block, clip, hollow, resize handles, tie-to-entity | vertex mode, extrude, noise terrain, flip, convert from brush |
Plus entities and props (models loaded through raylib), UV/texture tools, baked vertex lighting with BSP-traced
shadows, **FBX / OBJ export**, the **`.hmap` format** and a small **C++ / C runtime API** to load it in other apps.
> ### Status: v0.1 foundation - read this first
> * **`libhmap`** (geometry, CSG, BSP, lighting, file format, FBX/OBJ, C API) is covered by 2,939 automated checks and is
> clean under AddressSanitizer + UBSan, including a randomised BSP-vs-brute-force test and a file-corruption fuzz test.
> * **The editor** compiles and its logic is tested headless (1,515 checks: commands, undo, picking, camera maths,
> scripted mouse drags for move / resize / box-select / gizmo / paint / vertex edit). **The GUI itself has not been run
> against real raylib/ImGui yet** - the authoring environment had no network or GPU. Your first build is the real
> integration test; if the dependency versions drift, see *Pinning dependencies*.
> * **FBX** output follows the binary 7.4 layout and passes two independent structural parsers, but has **not** been
> opened in Blender / Unity / Unreal yet. Try `samples/demo.fbx` first.
> * Hammer parity is a **goal, not a fact**: see the checklist at the bottom for what exists and what does not.
## Build
```bash
cmake -B build
cmake --build build -j
./build/editor/hammerpp # opens the demo map (File > New for an empty room)
./build/examples/hmap_viewer samples/demo_runtime.hmap # tiny raylib-only viewer (example of integration)
./build/tools/hmap_info samples/demo.hmap --fbx out.fbx # inspect / convert from the command line
ctest --test-dir build # library tests
```
Needs CMake >= 3.20, a C++17 compiler and OpenGL 3.3. raylib, Dear ImGui and rlImGui are fetched automatically.
`libhmap` itself has **no** third-party dependencies (only `std::thread` for light baking).
| CMake option | Default | |
|---|---|---|
| `HPP_BUILD_EDITOR` | ON | the editor (fetches raylib + ImGui + rlImGui) |
| `HPP_BUILD_VIEWER` | ON | minimal raylib viewer example |
| `HPP_BUILD_TOOLS` | ON | `hmap_info`, `hmap_sample` |
| `HPP_BUILD_TESTS` | ON | `test_hmap`; add `-DHPP_BUILD_EDITOR_LOGIC_TESTS=ON` for the headless editor tests (Linux) |
| `HPP_USE_SYSTEM_RAYLIB` | OFF | use an installed raylib |
**Pinning dependencies.** Defaults: raylib `5.5` (the API the editor is written against), Dear ImGui `docking` branch,
rlImGui `main`. They are cache variables, so a mismatch is a one-line fix:
```bash
cmake -B build -DHPP_RAYLIB_TAG=5.5 -DHPP_IMGUI_TAG=v1.91.9b-docking -DHPP_RLIMGUI_TAG=<commit>
```
If rlImGui fails to compile, pin ImGui and rlImGui to a matching pair (ImGui 1.92 changed texture/font handling, so
rlImGui and ImGui must be from the same era). Without the docking branch the editor still builds; windows just float.
## Using the editor
Default layout: Top / Front / Side / 3D views, Outliner, Inspector, Assets (materials, models, entities) and Log.
Everything is dockable; *View > Reset window layout* restores it. **F1** shows all controls in-app.
| | |
|---|---|
| 3D view | hold **right mouse** + `W A S D`, `Q`/`E` down/up, `Shift` fast, wheel = speed. Wheel = dolly, middle drag = pan |
| 2D views | wheel zooms to cursor, right/middle drag pans, `F` frames the selection |
| Tools | `1` Select, `2` Block, `3` Entity, `4` Clip |
| Select | click, `Shift`/`Ctrl` adds, drag empty space = box select, drag = move, orange handles = resize, gizmo arrows in 3D |
| Block | drag a rectangle in a 2D view (height from *Create > Default height*) or click a surface in 3D. Shapes: box, wedge, cylinder, cone, stairs, sphere, torus, terrain patch; "as editable mesh" toggle |
| Entity | pick a class in *Assets > Entities* (props: pick a model in *Assets > Models*), click to place |
| Clip | two clicks in a 2D view, `Tab` = which side to keep, `Enter` = cut |
| Surfaces | `Alt+click` paints the current material (`+Shift` = whole object). *Face* mode + Inspector: material, offset, scale, rotation, flip, **Fit** |
| Meshes | *Vertex* mode: click / box-select vertices, drag them. Inspector > Mesh tools: extrude faces, noise terrain, flip normals |
| Brush entities | select brushes, `Ctrl+T` (or *Tie to entity*) -> `func_door`, `trigger_once`, `func_detail`... the Inspector then shows the entity's properties |
| Misc | `Ctrl+Z/Y` undo/redo, `Ctrl+D` duplicate, `Del`, `H` hide / `Alt+H` unhide, `G` snap, `[` `]` grid, **`F9` build** |
**Build (F9)** runs CSG, builds the BSP and (optionally) bakes lighting. Switch the view to *Baked* to preview it;
*Lit* is the live preview (ambient + sun + up to 16 point lights per pixel, no build needed); *Flat* and *Wireframe* too.
**Entities** are defined in a tiny FGD-style text format (`assets/entities.def`, built-ins in `libhmap/src/entdefs.cpp`)
and the Inspector builds its forms from it:
```
@point npc_spawner "NPC spawner" color(255,110,110) size(12,32,12) dir
npc_type choice "NPC type" "grunt" "grunt=Grunt,sniper=Sniper,boss=Boss"
count int "Count" "3"
```
Types: `string int float bool color255 vec3 choice model material target`. `@brush` classes become brush entities.
## Conventions
Right-handed, **+Y up**, forward **-Z**, `unitsPerMeter` (default 64) converts to metres. CCW triangles. UV `v` grows
downward (image space). Euler angles are `(pitch, yaw, roll)` degrees. Texture mapping is planar per face
(`Material::tileSize` world units per repeat; per-face offset / scale / rotation).
## Integrating the format in another app
C++ (`#include <hmap/hmap.h>`, link `hmap::hmap`):
```cpp
hmap::CompiledMap map;
if (hmap::Status s = hmap::loadCompiled("level.hmap", map); !s) { /* s.message */ }
for (const hmap::Batch& b : map.batches) { // one draw call each: model + material + (brush|mesh)
const hmap::Material& m = map.materials[b.material]; // texture path, tint, tile size
// upload b.vertices (pos, normal, uv, color = baked light) and b.indices
}
hmap::TraceResult hit = map.trace(from, to); // BSP brushes + solid meshes
bool underwater = map.pointContents(eye) & hmap::CONTENTS_WATER;
for (const hmap::Entity& e : map.entities) // lights, spawns, props, triggers...
if (e.classname == "prop_static") spawn(e.get("model"), e.transform());
```
`loadCompiled` accepts runtime files, editor files with a compiled chunk, and plain source files (it compiles on the
fly). `hmap::loadMap` / `saveMap` give you the editable `hmap::Map`; the same library can create maps in code
(`makeBox`, `hollowBrush`, `meshSphere`, ... then `compile`). See `examples/viewer.cpp` for a complete raylib consumer
and `tools/` for the command line tools.
Plain C (`#include <hmap/hmap_c.h>`) for engines, Python/C#/Rust FFI:
```c
hmap_map* m = hmap_load("level.hmap", err, sizeof err);
for (uint32_t i = 0; i < hmap_batch_count(m); ++i) { hmap_batch b; hmap_get_batch(m, i, &b); /* draw */ }
hmap_trace t; hmap_trace_segment(m, from, to, HMAP_CONTENTS_SOLID, &t);
hmap_free(m);
```
The byte layout is in [`docs/FORMAT.md`](docs/FORMAT.md) if you would rather write your own reader.
Editor saves are source-only unless *Embed compiled data* is ticked; *File > Export > Runtime map* writes a compact
compiled-only file.
## Export
* **FBX** (binary 7.4): one Model + Geometry per world / brush-entity with every material as a sub-mesh, per-vertex normals,
UVs (V flipped to FBX convention), baked lighting as a colour layer, diffuse colour + texture path per material,
entities and props as Null nodes carrying their key-values as custom properties. Y-up right-handed, `UnitScaleFactor`
derived from *Units per metre* (and the export *Scale* option). No skeletons or animation.
* **OBJ + MTL**, and the **runtime `.hmap`**.
* Import: **OBJ** as an editable mesh. Props use raylib's loaders: obj, gltf/glb, iqm, vox, m3d.
## Project layout
```
libhmap/ dependency-free library: model, geometry, CSG, BSP, lighting, .hmap IO, FBX/OBJ, entity defs, C API
editor/ raylib + ImGui front-end: editor.cpp (commands), viewport.cpp (cameras, tools, gizmo), render.cpp
(one lit shader for brush + mesh batches, caches), ui.cpp (docking UI, inspector, file dialog)
examples/ viewer.cpp - minimal raylib consumer tools/ hmap_info, hmap_sample
tests/ libhmap tests + optional headless editor tests samples/ demo.hmap / demo_runtime.hmap / demo.fbx / demo.obj
assets/ entities.def, sample props (crate.obj, pyramid.obj); put textures in assets/materials, models in assets/models
```
How the pieces fit: brushes -> `buildBrushGeometry` (plane clipping) -> CSG against other solids -> triangle batches
**and** collision brushes -> BSP tree (nodes split on brush planes, leaves hold brush lists; used for `trace`,
`pointContents` and light shadows). Meshes skip the BSP: they become batches (smooth/flat normals) and, if they collide,
collision triangles. The editor viewport draws the *same* shader for per-object edit batches and for compiled batches.
## Hammer parity checklist
| Feature | Status |
|---|---|
| Brush editing (block, clip, hollow, resize, move, rotate, scale, mirror) | yes (no vertex editing of brushes - convert to mesh) |
| 2D + 3D views, grid snapping, undo/redo, hide, duplicate | yes |
| Texture application, offset/scale/rotation/fit, texture lock on move | yes (lock for translation only) |
| Entities with auto-generated property forms, brush entities, models/props | yes (own definition format; no FGD import) |
| Displacements | partial: terrain meshes + noise + vertex edit, no sculpt brush |
| Curves / arches / stairs primitives | stairs, cylinder, cone, sphere, torus yes; arch no |
| Compile: CSG + BSP + lighting | yes (vertex lighting with shadows; **no** lightmaps, PVS/portals or game-specific BSP output) |
| Visgroups, prefabs/instances, groups | no (hide only) |
| Entity I/O connections, overlays/decals, cordon, measure tool, skybox | no |
| Export | FBX, OBJ, own format (more than stock Hammer) |
Ideas for next steps: prefab/instance support, sculpt brush for terrain, lightmap atlas, portal/PVS, FGD import,
glTF export, multi-mesh vertex editing, material browser thumbnails with search.
## Troubleshooting
* *Black / grey viewports* - needs OpenGL 3.3. Try `Shading = Flat` to rule out the lit shader.
* *Textures missing* - put images in `assets/materials`, *Assets > Materials > Add texture*. Without a file a tinted
checker is generated from the material colour.
* *Props show as boxes* - the model path is relative to `assets/`; check *Assets > Models* and the Log panel.
* *Layout broken after an update* - delete `hammerpp.ini` or *View > Reset window layout*.