| Crates.io | mshmash |
| lib.rs | mshmash |
| version | 0.4.2 |
| created_at | 2025-10-15 22:22:24.319971+00 |
| updated_at | 2025-10-20 21:35:34.276658+00 |
| description | A CLI tool for 3D mesh analysis, repair, and visualization |
| homepage | |
| repository | https://github.com/bobisme/msh |
| max_upload_size | |
| id | 1885057 |
| size | 390,373 |
A command-line tool for 3D mesh analysis, repair, and visualization. Supports
.obj and .glb file formats.

cargo install mshmash
Full features:
cargo install mshmash --features remote,renderdoc
Interactively view a mesh file in a 3D viewer:
msh view <INPUT>
msh view model.obj
msh view scene.glb --mesh name # For GLB files with multiple meshes
Options:
-m, --mesh <MESH>: Mesh name (required if GLB contains multiple meshes)Remesh a file with automatic fixing and incremental remeshing:
msh remesh <INPUT> --out <OUTPUT>
msh remesh model.obj --out fixed.obj
msh remesh scene.glb --out output.obj --mesh meshname
Options:
-o, --out <OUT>: Output mesh file (.obj) - required-m, --mesh <MESH>: Mesh name (required if GLB contains multiple meshes)-i, --iterations <ITERATIONS>: Number of incremental remeshing iterations
(default: 10)-t, --target-edge-length <TARGET_EDGE_LENGTH>: Target edge length for
incremental remeshing (default: 0.01)-v, --voxel-size <VOXEL_SIZE>: Voxel size for fix step (default: 0.01)--tolerance <TOLERANCE>: Vertex merge tolerance for fix step (default:
0.0001)--no-fix: Skip the fix step (just do incremental remesh)Remeshing Methods:
Incremental (default): Edge-based operations that progressively improve mesh quality
msh remesh model.obj --out output.obj incremental
Voxel: Converts mesh to signed distance field (SDF) then remeshes for clean topology
msh remesh model.obj --out output.obj voxel
Display detailed information about a mesh:
msh stats <INPUT>
msh stats model.obj
msh stats scene.glb --mesh meshname
Options:
-m, --mesh <MESH>: Mesh name (required if GLB contains multiple meshes)Verify if a mesh is manifold (watertight):
msh check <INPUT>
msh check model.obj
Options:
-m, --mesh <MESH>: Mesh name (required if GLB contains multiple meshes)Automatically fix holes and issues in a damaged mesh:
msh fix <INPUT> --out <OUTPUT>
msh fix damaged.obj --out fixed.obj
Options:
-o, --out <OUT>: Output mesh file (.obj) - required-m, --mesh <MESH>: Mesh name (required if GLB contains multiple meshes)-v, --voxel-size <VOXEL_SIZE>: Voxel size for remeshing (default: 0.01)-t, --tolerance <TOLERANCE>: Merge vertices closer than this distance before
fixing (default: 0.0001)--no-merge: Skip vertex merging stepDisplay the structure and contents of a GLB or glTF file:
msh inspect-glb <INPUT>
msh inspect-glb scene.glb
msh inspect-glb model.glb --json # Output as JSON
This command shows the scene hierarchy, including:
Options:
--json: Output as JSON instead of tree format.obj, .glb.objRepair a damaged mesh:
msh fix broken.obj --out repaired.obj
High-quality remesh with custom edge length:
msh remesh model.obj --out output.obj --target-edge-length 0.005 --iterations 20
Voxel-based remeshing for topology cleanup:
msh remesh model.obj --out clean.obj voxel --voxel-size 0.02
Inspect mesh before and after:
msh view original.obj
msh remesh original.obj --out fixed.obj
msh view fixed.obj
remote)Control the mesh viewer remotely via JSON-RPC. Perfect for automated workflows, scripting, or external tool integration.
Start the viewer with remote control enabled:
# Build with remote feature
cargo build --features remote
# Start viewer with RPC server
msh view model.obj --remote
The viewer will start a JSON-RPC server on http://127.0.0.1:9001 and display:
✓ RPC server ready at http://127.0.0.1:9001
Available methods:
- load_model(path, mesh_name?)
- set_rotation(x, y, z)
- rotate_around_axis(axis, angle)
...
Control the running viewer from another terminal:
Load a Model
msh remote load path/to/model.obj
msh remote load scene.glb --mesh "Body"
Rotate the Model
# Set absolute rotation (Euler angles in radians)
msh remote rotate 0 1.57 0
# Rotate around axis with angle notation
msh remote rotate-axis 0,1,0 90d # 90 degrees around Y axis
msh remote rotate-axis 1,0,0 1.57r # 1.57 radians around X axis
Control Camera
# Set camera position
msh remote camera-pos 5.0 3.0 5.0
# Set camera target (look-at point)
msh remote camera-target 0 0 0
Toggle Display Options
# Wireframe
msh remote enable-wireframe
msh remote disable-wireframe
msh remote toggle-wireframe
# Backface visualization (shows red reversed faces)
msh remote enable-backfaces
msh remote disable-backfaces
msh remote toggle-backfaces
# UI overlay
msh remote enable-ui
msh remote disable-ui
msh remote toggle-ui
Get Mesh Statistics
msh remote stats
Output:
=== Mesh Statistics ===
Vertices: 1234
Edges: 3702
Faces: 2468
Manifold: Yes
Take Screenshot
msh remote screenshot output.png
msh remote screenshot captures/view.png # Creates 'captures' dir if needed
msh remote screenshot deep/nested/dirs/shot.png # Creates all parent dirs
Capture Frame (RenderDoc)
msh remote capture
msh remote capture /path/to/save.rdc
Quit the Viewer
msh remote quit
renderdoc)Capture GPU frames for debugging and analysis using RenderDoc.
cargo build --features renderdoc
# or with both features
cargo build --features remote,renderdoc
Launch with RenderDoc:
# Build with RenderDoc support
cargo build --features renderdoc --release
# Launch through RenderDoc
renderdoccmd ./target/release/msh view model.obj
# or
qrenderdoc # Then use GUI: File -> Launch Application
When launched through RenderDoc, F12 will capture frames (injected by RenderDoc).
Via Remote Control:
# Terminal 1: Launch with both features
cargo build --features remote,renderdoc --release
renderdoccmd ./target/release/msh view model.obj --remote
# Terminal 2: Trigger capture remotely
msh remote capture # Default RenderDoc location
msh remote capture "captures/my_mesh" # Relative to your current directory
msh remote capture "/tmp/debug/mesh_analysis" # Absolute path
The path parameter sets RenderDoc's capture file path template:
msh remote).rdc extension to the final fileNote: RenderDoc works by injecting itself into your application process. The app must be launched through RenderDoc (via renderdoccmd or the RenderDoc GUI) for frame capture to work.
The remote control system supports flexible angle notation:
90d, 180d, 45d, -90d1.57r, 3.14r, 0.785r1.57, 3.14Examples:
msh remote rotate-axis 0,1,0 90d # Quarter turn
msh remote rotate-axis 1,0,0 180d # Half turn
msh remote rotate-axis 0,0,1 1.57r # ~90° in radians
# Terminal 1: Start viewer with remote control
msh view model.obj --remote
# Terminal 2: Automate inspection with screenshots
msh remote rotate 0 0 0 # Reset rotation
msh remote camera-pos 10 5 10 # Position camera
msh remote enable-wireframe # Show wireframe
msh remote rotate-axis 0,1,0 45d # Rotate 45° around Y
msh remote screenshot "shots/front_view.png" # Take screenshot
msh remote rotate-axis 0,1,0 90d # Rotate another 90°
msh remote screenshot "shots/side_view.png" # Another screenshot
msh remote disable-wireframe # Toggle for comparison
msh remote screenshot "shots/side_shaded.png"
msh remote stats # Get mesh info
With RenderDoc for GPU debugging:
# Terminal 1: Start with RenderDoc
renderdoccmd msh view model.obj --remote
# Terminal 2: Mix screenshots and RenderDoc captures
msh remote screenshot "analysis/mesh.png" # High-level view (PNG)
msh remote capture "analysis/mesh" # GPU-level capture (.rdc)
Note:
renderdoccmd to enable frame capture. Without it, msh remote capture will report that RenderDoc is not available./home/user/projects/models/, the path "captures/front_view.png" becomes /home/user/projects/models/captures/front_view.png."output/shots/view.png" will create the output/shots/ directory structure if it doesn't exist.The RPC server implements JSON-RPC 2.0 over HTTP. You can also call methods directly:
curl -X POST http://127.0.0.1:9001 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"set_rotation","params":[0,1.57,0],"id":1}'
Available methods:
load_model(path: String, mesh_name: Option<String>)set_rotation(x: f32, y: f32, z: f32)rotate_around_axis(axis: Vec<f32>, angle: String)set_camera_position(x: f32, y: f32, z: f32)set_camera_target(x: f32, y: f32, z: f32)enable_wireframe() / disable_wireframe() / toggle_wireframe()enable_backfaces() / disable_backfaces() / toggle_backfaces()enable_ui() / disable_ui() / toggle_ui()get_stats() → {vertices, edges, faces, is_manifold, holes}screenshot(path: String) - Save current view as PNGcapture_frame(path: Option<String>) (requires renderdoc feature)quit() - Exit the viewerremote: Enables JSON-RPC server and remote control CLI
cargo build --features remoterenderdoc: Enables RenderDoc frame capture
cargo build --features renderdocBoth: cargo build --features remote,renderdoc