Generate deterministic blocky mesh assets with Model Foundry
verifiedAgainst 6f58cea · verifiedOn 2026-09-10.
What you will build
Section titled “What you will build”In Voxamine, technical art follows strict project ownership and licensing disciplines (MASTERPLAN.md §14.5 / §14.6). Instead of importing unlicensed third-party 3D meshes or hand-sculpting opaque assets with unknown origins, passive creatures, handheld tools, and laboratory glassware are procedurally synthesized by a family of pure, standard-library Python 3 tools under Tools/BlockyModel/ (blockymodel.py).
You will author a procedural 3D box model on the project’s standard 16-unit grid, configure material colors and translucent alpha channels so chemistry apparatus reads as true glass, enforce strict coordinate conventions (fauna feet grounded on $y=0$ facing $+Z$, items centered at local origin), verify that committed OBJ/MTL files have not drifted from their recipes using headless --check gates, and audit the entire 50-asset library inside the Model Foundry Voxel Workshop module (ModelFoundryModule.cs).
# Generate and verify the entire blocky asset library headlessly:python3 Tools/FaunaGen/voxel_fauna_gen.py --check# Auditing 16 fauna species: all in sync.python3 Tools/ItemGen/voxel_item_gen.py --check# Auditing 34 item models: all in sync.Where this sits
Section titled “Where this sits”The blocky model toolchain bridges offline deterministic Python generators and the Unity Editor asset database:
- Geometry Core (
Tools/BlockyModel/blockymodel.py) provides the pure math primitives (Box,Model,write_obj,validate_obj,check_drift,write_contact_sheet). - Recipe Scripts (
Tools/FaunaGen/&Tools/ItemGen/) define the box layouts, symmetric parts, and palette assignments for 16 passive animal species and 34 tools/glassware/drops. - Committed Assets (
Assets/_Game/Art/Models/) stores the emitted.objand.mtlfiles versioned in git. - Voxel Workshop (
ModelFoundryModule.cs) orchestrates headless script execution viaModelScriptRunner, monitoring live counts and drift status directly inside Unity.
flowchart TD
subgraph TOOLS["Tools/ (Pure Python 3, zero deps)"]
CORE["Tools/BlockyModel/blockymodel.py\n(Box math, OBJ writer, drift checker)"]
FAUNA_GEN["Tools/FaunaGen/voxel_fauna_gen.py\n(16 passive species: chicken, cow, fox...)"]
ITEM_GEN["Tools/ItemGen/voxel_item_gen.py\n(34 tools, glassware, drops)"]
end
subgraph ASSETS["Assets/_Game/Art/Models/"]
FAUNA_OBJ["Assets/_Game/Art/Models/Fauna/\n(16 species .obj + .mtl)"]
ITEM_OBJ["Assets/_Game/Art/Models/Items/\n(34 item .obj + .mtl)"]
end
subgraph WORKSHOP["Voxel Workshop (Editor)"]
CATALOG["ModelGeneratorCatalog.cs\n(Shell paths and output asset folders)"]
RUNNER["ModelScriptRunner.cs\n(Runs --list, --check, regenerate headlessly)"]
MODULE["ModelFoundryModule.cs\n(Live count, drift status, Regenerate All)"]
end
CORE --> FAUNA_GEN
CORE --> ITEM_GEN
FAUNA_GEN -->|writes| FAUNA_OBJ
ITEM_GEN -->|writes| ITEM_OBJ
CATALOG --> MODULE
MODULE --> RUNNER
RUNNER -->|queries --check| FAUNA_GEN
RUNNER -->|queries --check| ITEM_GEN
Before you start
Section titled “Before you start”Read Tools/BlockyModel/README.md and MASTERPLAN.md §14.5 (“Asset provenance and ground rules”), then review:
blockymodel.py, the pure Python geometry and Wavefront OBJ/MTL exporter.voxel_fauna_gen.py, fauna species recipes and bounding conventions.voxel_item_gen.py, tools, laboratory apparatus, and drop recipes.ModelGeneratorCatalog.cs, registering generator paths with Unity.ModelFoundryModule.cs, the interactive management dashboard.
Review the mandatory coordinate and orientation standards:
| Asset Type | Primary Anchor | Facing Direction | Metric Scale | Example |
|---|---|---|---|---|
| Fauna Species | Feet at $y=0$ (ground plane) | Forward along $+Z$ | $1/16\text{ m}$ per unit ($16\text{ px} = 1\text{ m}$) | Chicken, Fox, Cow, Deer |
| Tools & Glassware | Centered at local origin $(0, 0, 0)$ | Flat or upright along $+Y$ | $1/16\text{ m}$ per unit | Beaker, Retort, Pickaxe |
| Chemistry Glass | Centered at origin | Upright | Translucent ($\alpha < 1$) with illum 4 |
Condenser, RoundFlask |
The build, step by step
Section titled “The build, step by step”1. The 16-Unit Pixel Grid and Box Primitives
Section titled “1. The 16-Unit Pixel Grid and Box Primitives”In blockymodel.py, every model is composed of integer Box primitives on a 16-unit grid where $16\text{ units} = 1\text{ block} = 1\text{ meter}$:
class Box: def __init__(self, x, y, z, w, h, d, mat, rot=None): self.x, self.y, self.z = x, y, z # min corner (integer units) self.w, self.h, self.d = w, h, d # size in width, height, depth self.mat = mat # material key from PALETTE self.rot = rot # optional (degrees, axis, pivot)Symmetric features (such as animal legs, ears, or wings) are added via model.add_symmetric(x, y, z, w, h, d, mat) which automatically mirrors the box across the $X=0$ plane.
2. Translucent Glassware in Wavefront MTL
Section titled “2. Translucent Glassware in Wavefront MTL”Chemistry laboratory vessels (such as beakers, retorts, condensers, and alembics) require visible interior liquid levels. In blockymodel.py, palette values can define an optional alpha channel (r, g, b, alpha):
# Laboratory glassware palette definition:PALETTE = { "glass": (0.85, 0.92, 0.95, 0.35), # translucent soft cyan "glass_frosted": (0.90, 0.95, 0.98, 0.55), "iron": (0.35, 0.35, 0.38), # opaque metals "copper": (0.72, 0.45, 0.30),}When exporting the material file (.mtl), write_obj checks the alpha channel:
if len(color) == 4 and color[3] < 1.0: alpha = color[3] f.write(f"d {alpha:.2f}\n") f.write(f"Tr {1.0 - alpha:.2f}\n") f.write("illum 4\n") # glass / transparency illumination modelelse: f.write("illum 2\n") # standard diffuse/specularThis guarantees Unity’s model importer imports glassware with transparent blend modes rather than rendering a solid grey box.
3. Authoring a Model Recipe
Section titled “3. Authoring a Model Recipe”In voxel_item_gen.py, author a laboratory Round-Bottom Flask:
def make_round_flask(): m = Model("RoundFlask", mats("glass", "glass_frosted", "cork")) # Spherical body approximated by stepped boxes centered at origin: m.add(-4, -6, -4, 8, 8, 8, "glass") m.add(-5, -5, -3, 10, 6, 6, "glass") m.add(-3, -5, -5, 6, 6, 10, "glass") # Cylindrical neck extending upwards: m.add(-2, 2, -2, 4, 6, 4, "glass") # Flanged lip: m.add(-3, 8, -3, 6, 1, 6, "glass_frosted") # Optional cork stopper: m.add(-2, 8, -2, 4, 3, 4, "cork") return mEvery model function returns a Model instance whose bounding box is automatically calculated and logged.
4. Headless Drift Checking and Contact Sheets
Section titled “4. Headless Drift Checking and Contact Sheets”To prevent recipes and committed assets from falling out of sync, blockymodel.py implements in-memory regeneration and diffing:
# In blockymodel.py:def check_drift(builders, committed_root): drifted = [] for name, builder in builders.items(): obj_path = os.path.join(committed_root, f"{name}.obj") if not os.path.exists(obj_path): drifted.append(name) continue # Compare regenerated geometry excluding header timestamp comments if not geometry_matches(builder(), obj_path): drifted.append(name) return driftedThe toolchain also generates a combined Contact Sheet (--contact-sheet), arranging all models in an inspection grid with a companion .txt cell map for quick visual review.
5. Interactive Orchestration in Model Foundry
Section titled “5. Interactive Orchestration in Model Foundry”Open ModelFoundryModule.cs. The module displays each generator registered in ModelGeneratorCatalog.cs:
- Live Model Count: Queries
--liston each Python script. - In-Sync Badge: Runs
--checkheadlessly and displays green (“All in sync”) or amber (“Drift detected”). - Regenerate All: Runs
generate-fauna-models.shandgenerate-item-models.shand triggers an immediate UnityAssetDatabase.Refresh().
Verify
Section titled “Verify”1. Execute Headless Self-Tests and Drift Audits
Section titled “1. Execute Headless Self-Tests and Drift Audits”Run the command-line suite from the repository root:
# Verify fauna generation:python3 Tools/FaunaGen/voxel_fauna_gen.py --selftestpython3 Tools/FaunaGen/voxel_fauna_gen.py --check
# Verify item generation:python3 Tools/ItemGen/voxel_item_gen.py --selftestpython3 Tools/ItemGen/voxel_item_gen.py --checkConfirm that:
--selftestreports zero index-out-of-bounds or corrupt quad errors.--checkconfirms zero drifted models between Python recipes and committed files.
2. Inspect in Model Foundry
Section titled “2. Inspect in Model Foundry”- Open Tools → Voxel Sandbox → Voxel Workshop → Model Foundry.
- Confirm the header reads:
All 2 generators are in sync (50 models total). - Click Rescan: verify the status indicators refresh cleanly.
- Select any generated model in
Assets/_Game/Art/Models/Items/(e.g.RoundFlask.obj) and verify in Unity’s Model Previewer:- Neck and lip are centered cleanly.
- Material transparency displays correctly.
Now do your own
Section titled “Now do your own”To author a new blocky model (e.g. a Wolf fauna species or a Büchner Funnel apparatus):
- Pick the Target Generator: Add species to
voxel_fauna_gen.pyor item tovoxel_item_gen.py. - Select Materials from
PALETTE: Use existing palette keys or declare new stable colors inblockymodel.py. - Sketch Boxes on the 16-Unit Grid:
- For fauna: ensure feet are placed at $y=0$ and the snout points toward $+Z$.
- For items: center geometry around $(0, 0, 0)$.
- Register in Catalog: Add
make_<name>to the generator’sBUILDERSdictionary. - Regenerate and Commit: Run
./generate-item-models.shto emit.objand.mtlfiles underAssets/_Game/Art/Models/. - Verify in Model Foundry: Confirm Model Foundry detects the new model and reports green in-sync status.
Pitfalls
Section titled “Pitfalls”| Symptom | Cause | Fix |
|---|---|---|
| Animal floats in the air or sinks into the terrain when placed. | Box coordinates were authored with min $Y \ne 0$ or without centering on $X=0$. | Enforce grounding: the lowest box (hooves/feet) must touch $y=0$, and body symmetry must center across $X=0$. |
| Chemistry glassware renders as a solid, dark opaque box in Unity. | Palette entry had only 3 color channels (r, g, b). |
Add alpha channel (r, g, b, alpha) where $\alpha < 1.0$. The exporter will automatically write d, Tr, and illum 4. |
CI build fails with --check mismatch after changing Python code. |
The Python recipe was updated but the .obj asset was not regenerated and committed. |
Run ./generate-fauna-models.sh or ./generate-item-models.sh and stage the resulting .obj / .mtl files. |
| Model faces render with inverted or black lighting. | Quad winding order was inverted or box had negative dimensions ($w < 0$). | Always define positive dimensions ($w, h, d > 0$). Box guarantees outward-facing counter-clockwise quad normals. |
| Non-deterministic diffs appear in git across different machines. | Generator relied on dictionary iteration order or floating-point formatting drift. | Use pure integer grid math and standard Model structures. blockymodel.py formats floats to fixed decimal precision. |
User-contributed notes
Corrections, clarifications, and practical tips for this page. Anonymous is fine — a name is optional. Basic Markdown works:
**bold**,*italic*,`code`, and links.Notes policy
Notes are lightly filtered for spam and may be edited or removed. Keep them about this page — no support requests, no personal data, nothing you would not publish. Links are limited and marked
nofollow.