- OBJ read/write (multi-object, ngon fan-triangulation, negative indices), STL binary+ascii with auto-detect and vertex welding - info: bbox/size/area/volume + watertightness via edge manifold stats - view: z-buffered orthographic ASCII renders (front/side/top/iso/back) - transform: center/mirror/scale/rotate/translate/fit, per-object filter, winding auto-flip on negative determinant - create: box/sphere/cylinder/cone/plane/torus; merge; convert - go tests: primitive volumes vs analytic values, roundtrips, topology Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmdG9GqfSWCzts7AkDwRDh
102 lines
3.8 KiB
Markdown
102 lines
3.8 KiB
Markdown
# mesh-tool (`mesht`)
|
|
|
|
Create, inspect and edit **3D models** from the command line — built so an
|
|
agent can *see* a model (ASCII multi-view rendering + measurements),
|
|
reason about it, and edit it step by step. Written in Go, zero
|
|
dependencies, single static binary.
|
|
|
|
## Formats
|
|
|
|
| Format | Read | Write | Notes |
|
|
|--------|------|-------|-------|
|
|
| OBJ | ✔ | ✔ | multiple named objects, quads/ngons triangulated |
|
|
| STL | ✔ | ✔ | binary + ASCII, auto-detected; vertices re-welded on load |
|
|
|
|
OBJ and STL cover the vast majority of simple editing/printing/game
|
|
pipelines and are plain enough to survive round-trips. glTF/GLB is out of
|
|
scope for now (a full JSON+buffer+material model; use Blender for that).
|
|
|
|
Normals, UVs and materials are ignored on read — this tool edits
|
|
*geometry*. Viewers recompute normals; STL export writes correct face
|
|
normals from the winding.
|
|
|
|
## Build
|
|
|
|
```bash
|
|
# Arch/Garuda: sudo pacman -S go
|
|
cd mesh-tool
|
|
go build -o build/mesht . # or the VS Code task "build mesh-tool"
|
|
go test ./...
|
|
```
|
|
|
|
## Commands
|
|
|
|
```bash
|
|
mesht info model.obj # verts/tris, bbox, size, area, volume, watertight?
|
|
mesht view model.obj # ASCII render: front, side, top (default)
|
|
mesht view model.obj --views iso --width 100 --object wheel
|
|
|
|
# creation — box|sphere|cylinder|cone|plane|torus
|
|
mesht create box --size 2,1,1 --name crate -o crate.obj
|
|
mesht create sphere --radius 1 --segments 32 --rings 16 -o ball.stl
|
|
|
|
# editing (applied in the listed order)
|
|
mesht transform m.obj --center # bbox center -> origin
|
|
mesht transform m.obj --mirror x # winding auto-corrected
|
|
mesht transform m.obj --scale 2 # or --scale 1,2,1
|
|
mesht transform m.obj --rotate 0,45,0 # degrees, X then Y then Z
|
|
mesht transform m.obj --translate 0,2,0
|
|
mesht transform m.obj --fit 10 # largest dimension -> 10 units
|
|
mesht transform m.obj --object wheel --scale 1.2 -o out.obj # edit one part
|
|
|
|
mesht merge body.obj wheels.obj -o car.obj # keeps objects, renames dups
|
|
mesht merge a.stl b.stl --flatten -o one.obj # single combined object
|
|
mesht convert model.obj -o model.stl # --ascii for text STL
|
|
```
|
|
|
|
`-o` picks the output format from the extension. `transform` overwrites
|
|
the input when `-o` is omitted.
|
|
|
|
## Conventions
|
|
|
|
- **Axes:** right-handed, **+Y up**, +X right, +Z toward the "front" viewer.
|
|
- Primitives are centered on the origin.
|
|
- Mirroring / negative scaling automatically flips triangle winding so
|
|
surfaces keep facing outward.
|
|
- `info` reports **volume only for watertight meshes** — otherwise it
|
|
tells you how many boundary/non-manifold edges the mesh has.
|
|
|
|
## How an agent should edit a model
|
|
|
|
1. `mesht info m.obj` — learn size, orientation and object names.
|
|
2. `mesht view m.obj` — see the shape (front/side/top; add `iso` for depth).
|
|
3. Apply **one small transform**, write to a new file.
|
|
4. `mesht view` again and compare — verify before continuing.
|
|
|
|
The ASCII views are z-buffered orthographic renders; brightness = how
|
|
much the surface faces the light over the viewer's shoulder, so shape
|
|
and curvature read directly from the text. Each view header repeats the
|
|
axis legend and model dimensions.
|
|
|
|
Example (a 32-segment sphere, front view, width 48):
|
|
|
|
```
|
|
front view — X→right Y↑up (seen from +Z)
|
|
model 2 x 2 x 2 (XYZ)
|
|
==++**####%%%%%%%%%%%%###*
|
|
-==++**######%%%%%%%%%%%%%%####*
|
|
:-=+++**#######%%%%%%%%%%%%%%%%%%#**
|
|
-==++***#######%%%%%%%%%%%%%%%%%%%%##*
|
|
.:--+++***########%%%%%%%%%%%%%%%%%%%%%%##*+
|
|
```
|
|
|
|
## Viewing the results as a human
|
|
|
|
Any of these (Arch/Garuda):
|
|
|
|
```bash
|
|
sudo pacman -S f3d # fast minimal viewer: f3d model.obj
|
|
sudo pacman -S blender # full editor
|
|
flatpak install org.prusa3d.PrusaSlicer # if you also want to print
|
|
```
|