Add project files.
This commit is contained in:
commit
a9ae435321
52 files changed
+65378
No files matched your search
@@ -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*.
|
||||
Reference in new issue
Block a user