PRE-ALPHA NO RELEASE
Open-source mapping tools for Source SDK-family games, and eventually a level editor to replace Hammer++.
Built in the order that makes each step independently useful:
- Standalone tools: texture browser, VMT editor, RAD generator, compile GUI. These replace nem's unmaintained tools.
- Clean libraries underneath them: VMF, VTF/VMT, FGD, MDL, filesystem.
- A viewport.
- An editor.
Each step is usable on its own.
Early. What works today:
| Piece | State |
|---|---|
smithy-browser |
Texture browser. Mounts VPKs, indexes ~70k materials from a stock CS:S install, decodes VTF thumbnails, shows VMT source and which mount each material came from. |
smithy |
Editor shell. Game detection, VMF load/save with lossless round-trip, wireframe viewport, compile-plan preview. Not an editor yet. |
| Game detection | Reads gameinfo.txt, resolves the real mount order, matches a profile by AppID, and shows every inference it made. |
| Compile driver | Plans vbsp/vvis/vrad invocations from profile data. Does not run them yet. |
| Brush geometry | Plane clipping to real polygons, primitives (block, wedge, cylinder, spike, sphere, arch, torus, stairs), split, convex hull, ray picking, lossless VMF round-trip. |
| Scripting | Embedded Python 3 with a smithy module, plus a plugin system: a plugin is a folder with a plugin.json, and it can register menu commands. |
Not started: VMT editor, RAD generator, compile GUI, selection, displacements.
Every game's own vbsp/vvis/vrad does that. This is what Hammer, Hammer++,
and CompilePal all do, and it is correct: BSP v19 vs v20 vs v21 vs Strata's v25
is not Smithy's problem. The deliverable is a correct VMF plus a correct compile
invocation.
Five interfaces separate everything branch-specific from everything else:
| Seam | Header | Backed by |
|---|---|---|
| Filesystem | game/ifilesystem.h |
loose dirs + vpkpp |
| Entity schema (FGD) | game/ientityschema.h |
toolpp |
| Shader schema (VMT) | game/ishaderschema.h |
per-profile data (not yet written) |
| Model loader | game/imodelloader.h |
mdlpp (not yet wired) |
| Compile driver | game/icompiledriver.h |
SourceCompileDriver |
A game is a JSON profile: FGD list, asset version ranges, shader schema, compile steps, engine limits. Someone who mains TF2 will write and test a TF2 profile if the price is a file instead of a fork.
Scope: Source SDK 2006/2007/2013: HL2, CS:S, TF2, L4D1/2, Portal 1/2, Black Mesa, Garry's Mod. Dark Messiah, Bloodlines, and Titanfall diverged into separate engines wearing a Source badge. Strata is a second engine: reachable through the seams, not on the roadmap.
sourcepp already maintains the
binary formats that are expensive per branch: vtfpp (VTF 7.0–7.6), vpkpp
(VPK/BSP-pak/ZIP/WAD mounts), mdlpp (MDL 44–49 with VVD/VTX), toolpp (FGD),
steampp (Steam library discovery). Smithy adapts them to its seams.
Smithy keeps its own KeyValues and VMF implementation. It has to round-trip
unknown blocks, duplicate sibling keys and file order losslessly so that maps
can be shared with Hammer, which is a stricter contract than a general-purpose
KV1 reader offers. A dispinfo block Smithy does not model yet still survives
a load/save cycle byte-for-byte.
Hammer++ and its ++ compile tools are closed source and Windows-only. Smithy
invokes whatever compilers a user already has, but it bundles none of them, and
it ships no extracted art: its icons are drawn in code. Reading a closed tool
closely enough to write an open one is fine; redistributing its files is not.
See assets/icons/NOTICE.md and the roadmap.
gameinfo.txt already declares the mount order and the Steam AppID. Point Smithy
at a game folder and it reads what the engine already knows, then shows its work:
every resolved path, every missing one, which profile matched, which compile
tools it found. Hammer's game-config dialog asks for all of this by hand.
Smithy embeds Python 3, in the spirit of Blender's API: the editor exposes its own operations, so repetitive work can be scripted.
import smithy
smithy.add_block((0, 0, 0), (512, 512, 16), "DEV/DEV_MEASUREGENERIC01B")
smithy.add_primitive("arch", (-256, -256, 0), (256, 256, 128), sides=12, arc=180.0)
smithy.add_entity("info_player_terrorist", (64, 64, 32))
def build():
smithy.log(f"{len(smithy.entities())} entities")
smithy.register_operator("mine.build", "Build Thing", build, menu="Plugins/Mine")A plugin is a directory with a plugin.json and an entry point; drop it in the
plugin folder and enable it in Plugins → Plugin Manager. plugins/surf_ramp_helper
is a worked example. There is a Python console under the same menu.
Requires Qt 6.11, CMake 3.24+, a C++20 compiler, and Python 3.10+ development
headers (python3-devel). sourcepp is fetched at configure time.
cmake --preset release
cmake --build build/release
ctest --test-dir build/releaseBinaries land in build/release/bin/.
# What does Smithy infer about an install?
./build/release/bin/smithy --detect ~/.steam/steam/steamapps/common/Counter-Strike\ Source
# Mount it and index every material.
./build/release/bin/smithy-browser --index --game <same path>src/core math, Result/Error, logging
src/io KeyValues (KV1) and VMF: lossless round-trip
src/geom brush geometry: half-spaces in, polygons out
src/script embedded Python and the plugin system
src/game the five seams, game profiles, gameinfo detection, mounting
src/assets Qt boundary over sourcepp's asset loaders
src/render OpenGL 4.6 viewport
src/tools standalone tools (shared toolkit + browser)
src/editor the editor shell
profiles/ game profiles, one per branch
