Skip to content
TexmexdexPublic

About

Turns AI-generated mesh output into a watertight, manifold, correctly-scaled, multicolour-print-ready model, without losing the colour, and then into an articulated, poseable, printable figure

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

MeshDeX Print Prep

Turns AI-generated mesh output (Meshy, Luma, photogrammetry scans, and similar) into a watertight, manifold, correctly-scaled, multicolour-print-ready model — without losing the colour — and then into an articulated, poseable, printable figure.

Runs entirely on your PC. Nothing is uploaded anywhere, and nothing is fetched from the internet: three.js is vendored in static/vendor/, so the 3D view works with the network cable out.


Install & run

The standalone app (no Python needed)

Grab the build for your machine, unzip it anywhere, and run it. Nothing to install; everything still stays on your machine.

Platform You get Notes
Windows 10/11 x64 MeshDeX.exe one file, ~57 MB
macOS 11+ Apple Silicon MeshDeX.app arm64
macOS 11+ Intel MeshDeX.app x86_64

It opens http://127.0.0.1:8730 in your browser. input/ and output/ are created next to the app, so an unzipped-to-a-USB-stick copy keeps its own work with it. If the app lives somewhere read-only — /Applications, or Program Files — those folders go to ~/Documents/MeshDeX on macOS and %LOCALAPPDATA%\MeshDeX on Windows instead. The path is printed in the console window at startup.

Two first-launch warnings to expect, both because the builds are unsigned:

  • Windows SmartScreen — "Windows protected your PC" → More info → Run anyway.
  • macOS Gatekeeper — right-click the app → Open the first time. If it was downloaded rather than built locally, macOS quarantines it and a plain double-click reports it as damaged; xattr -dr com.apple.quarantine /Applications/MeshDeX.app clears that.

Being a onefile build, Windows unpacks ~200 MB to temp on each launch, so the first window takes a few seconds to appear. Set ONEFILE = False in MeshDeX.spec for a folder build that starts instantly.

Running from source

  1. Install Python 3.10+ from python.org. Tick "Add python.exe to PATH" in the installer.
  2. Double-click Launch_Windows.bat (or ./Launch_Mac_Linux.sh). First run builds a private environment (a few minutes, once). After that it starts in seconds and opens http://127.0.0.1:8730 in your browser.

Building the standalone app yourself

Windows   powershell -ExecutionPolicy Bypass -File build_windows.ps1
macOS     ./build_macos.sh

PyInstaller cannot cross-compile. The Mac app has to be built on a Mac and the Windows exe on Windows — there is no way around this, because manifold3d and fast_simplification are native extensions that must be linked for the target platform. If you do not own a Mac, push a v* tag and .github/workflows/release.yml builds all three on GitHub's runners and attaches them to a draft release.


Meshmixer's "Make Solid", Netfabb, Microsoft's 3D repair service and most other auto-repair tools work by rebuilding the mesh from a voxel or signed-distance field. That representation stores occupancy only — it has no colour channel. What comes out is a brand-new mesh whose vertices never existed in your original, so there is nothing left to attach colour to. Default grey, every time.

This tool never does that. It:

  1. repairs topology in place, so original vertices survive carrying their colour;
  2. when new geometry is genuinely unavoidable (decimation, hole filling), it re-samples colour from the original surface by nearest-point lookup.

That's the whole trick.


What it does

Diagnose — non-manifold edges, holes, winding defects, bowtie vertices, coincident vertices, separate shells, Euler characteristic. Before and after, side by side.

Repair, colour-safe — separates coincident vertices, deletes faces on non-manifold edges, fills the resulting holes, splits bowtie vertices into per-fan copies (each copy inherits the colour), makes winding globally consistent, forces normals outward. Verified by re-checking the result.

Decimate — quadric edge collapse to a triangle budget your slicer can actually handle, then colour transfer, then a second repair pass (decimation routinely creates non-manifold edges — this catches them).

Scale & orient — Y-up to Z-up, scale to a target height in mm, centre in X/Y, drop onto the bed. Loads into the slicer ready to go.

Print report — size, volume, filament weight, bed-contact area, overhang percentage, with warnings when adhesion or supports will be a problem.

Full Spectrum palette — enter your whole filament shelf; it searches every 4-filament combination, picks the best set for that specific model, and gives you layer-Pattern strings to paste into Snapmaker Orca's Color Mixing panel. Every recipe reports its resulting hex, so you can set the mixes up before the model is anywhere near the plate.

Pre-order recommendations — tick "also consider filaments I could order" and it searches the Panchroma catalogue too, then tells you which spools to buy. Because the shelf can now run to dozens of spools, an exhaustive search is hopeless past ~400 combinations, so it switches to greedy selection with swap refinement.

Calibration swatch — a plate of tiles, one per recipe, plus a reference sheet mapping tile position to pattern and predicted hex. Tile 1 has a corner tab so you know which way round it is. Print it, compare, then correct the shelf hex values so future predictions match your actual spools.

Articulate (print in place) — the articulated-dragon workflow: slices the model into a chain along its centreline and links each pair with a captured ball joint. One file, one print job, comes off the bed already flexible with nothing to assemble. Full description below.

Auto-rig — for the other kind of articulation, where parts print separately and you assemble them. Press Find limbs & joints and it works out where the joints go by itself. Full description below; the short version is that it finds each limb, measures where it narrows, and proposes a cut plane already perpendicular to the bone.

Paint & recolour — click any region of the model and repaint it, or shift its hue, saturation and lightness. Selection floods across the surface, not through space, so it cannot leak from an arm onto the torso it is resting against.

Plate & export — every part is turned onto its best printing face, packed onto your bed, and written as one 3MF with per-triangle colour and named objects.

Cut into parts, with joints — open the 3D preview and place a plane:

  • click the model to anchor the plane at that point
  • Tilt / Spin sliders orient it freely — cuts are not restricted to X/Y/Z
  • Slide along normal moves it perpendicular to itself
  • flat Z / X / Y buttons snap back to an axis
  • Guide size scales the translucent square in the viewport. It is a visual aid only — the plane itself is mathematically infinite, and what limits the cut is confinement (below), not the size of the guide.
  • Drag the coloured arrows (or the ±X/Y/Z buttons) to slide the plane in world axes.

Print-in-place articulation

This is the articulated-dragon workflow, and it is not the same thing as cutting a model into parts:

Cut into parts Print in place
files one per part one
print jobs one per part, or a plate one
assembly caps, pins, your hands none
result a kit comes off the bed already flexible

Open a model, go to Articulate, press Find it automatically, choose how many segments, and build. The output is a single OBJ containing many disjoint shells that interlock without touching.

Why it is one file with many shells

Every instinct at the end of this process says to union the segments together. Unioning is exactly what turns a dragon into a paperweight. A print-in-place model is one file containing many separate solids, and the air between them is the joint. So the segments are concatenated, never unioned.

The chain

  1. Find the spine. The longest medial path through the model - nose to tail-tip on a dragon. It reuses the voxel skeleton: thin voxels are expensive to travel through, so the path rides the core of the body instead of skimming the skin. You can also click the two ends yourself and get the medial path between them, which is how you articulate only a tail or only a neck.

    The cost the path finder minimises is a reluctance, not a distance, so asking it for the "farthest" point returns the hardest place to reach - an ear tip. Taking two of those gave a spine running ear to ear across the top of a dog's skull. The route still comes from the weighted graph, but its length is measured afterwards in real millimetres, so farthest now means longest.

  2. Station the spine at equal arc length, each with a plane perpendicular to the local tangent. Because the tangent follows the curve, slices stay square to the body even where it bends.

  3. Slice, confined by connectivity. A spine curves - a dragon's tail curls, a dog's tail arches back over its own body - and a plane perpendicular to the tail, extended forever, also cuts straight through the torso. So only the piece connected to that stretch of spine is taken, and everything else the plane grazed is handed straight back.

  4. Link each pair with a captured ball. The socket's mouth is deliberately narrower than the ball. On a joint you assemble that is a defect - you could never push it together - but printed in place it never has to be assembled, and the narrow mouth is the only thing stopping the chain pulling apart. Default grip is 80%: the mouth is four fifths of the ball diameter.

The number that decides whether it works

Clearance. Too small and the two shells fuse into one solid in the print; too large and the chain is sloppy and droops. It cannot go below roughly one extrusion width, because a slicer cannot resolve a thinner gap - a 0.1 mm gap through a 0.4 mm nozzle is not a gap, it is a solid. The panel refuses to let that pass quietly: set it under half your nozzle and it says so.

Default is 0.55 x nozzle, which is 0.22 mm on a 0.4 mm machine.

Verification, not hope

Every build is checked before you download it:

  • every shell watertight;
  • no two adjacent shells share any volume - a boolean intersection answers this exactly, no sampling and no tolerance, and any overlap at all means those two segments print as one lump;
  • an interface too thin for a ball is merged into its neighbour rather than left as two coincident shells. Touching shells fuse on the bed, so reporting a joint there would be a lie.

Measured on a 150 mm figure, 600k triangles:

segments asked segments built joints watertight fused total bend time
16 14 13 14/14 0 111° 15 s
24 21 20 21/21 0 137° 19 s

Honest limits

  • Bend costs material. Each joint needs its faces relieved or they grind and the chain comes off the bed rigid. The relief has to be capped against the segment length, not just the cross-section: an angle times a 25 mm torso radius asked for 8 mm per joint, and across twelve joints that quietly removed nearly 40% of the animal. It is capped now, and the panel shows the total bend you are buying.
  • A chain through a quadruped still crosses its legs. Those pieces are handed back rather than cut, so the chain is intact - but a limb the chain passes through does not itself articulate. Articulate a tail or a neck on its own if that matters.
  • Very thin extremities cannot hold a ball and are merged into the segment before them. A tail that tapers to a point loses its last joint or two.
  • Print it as one object, and do not put supports through the joints - the gaps are meant to be bridged.

Auto-rig: finding the joints for you

Placing every joint by hand is fine for one cut and miserable for a twelve-part figure. This finds them.

A joint belongs where a limb is narrow — wrists, ankles, necks, the base of a tail. That is a statement about the model's interior, so none of it happens on the triangles:

  1. Rasterise the mesh into a voxel shell, seal it, flood the outside, keep what is left. That gives a solid. (The fill is checked against the mesh's own volume; a leaking surface says so instead of quietly rigging a brick.)
  2. Distance transform the solid. Every interior voxel now knows how far it is from the surface — which is exactly the local limb radius.
  3. Root the skeleton at the thickest voxel. On any figure that lands in the torso, which is the correct trunk.
  4. Grow a shortest-path tree outward, weighting each step by how thin the voxel is. Paths then refuse to cut corners through skin and hug the medial axis instead — a curve skeleton, with no thinning algorithm and no mesh contraction solve.
  5. Repeatedly take the farthest unclaimed voxel and trace back until the path meets skeleton already found. Each trace is one limb.
  6. Read the radius along each branch. Local minima are constrictions, and a constriction is where a joint wants to be. The branch tangent gives the cut normal for free, so the plane arrives already perpendicular to the limb.

Why the thickness weighting matters. With uniform step cost the shortest path from a hand to the torso cuts straight through the armpit and hugs the inside of the elbow, because that genuinely is the shortest route through the solid. The skeleton then runs along the skin and every joint it proposes is off-centre. Making thin voxels expensive makes the detour through the middle of the limb cheaper than the shortcut.

Where the joint goes along the branch. Not at a fixed fraction. Every branch starts at a junction inside the body, so its first samples carry the trunk's radius rather than the limb's — put the hip at "16% along" and on a tall figure it lands in the pelvis. Instead it finds where the radius has actually fallen away from the junction value. That point is the shoulder or the hip whatever the branch's length. If the radius never falls, the branch never separated from the body and is not a limb at all.

A limb, or a detail on a limb? A branch joining the tree near the trunk is a new limb. One joining near the far END of an existing branch is a finger, a toe or an ear - a feature of that limb rather than a peer of it. Details are indented under their parent, drawn faintly in the viewport, and left unticked.

Mirror twins are levelled up. A bilaterally symmetric model that comes back with the left arm ticked and the right arm not has failed, whatever the individual scores say - the two sides are the same shape and the difference is measurement noise. So each limb is matched to its mirror partner and the weaker one is promoted to match.

It proposes, it does not cut. Every suggestion arrives with the radius it measured and a confidence, and lands in the same editable cut list you would have built by hand.

When two limbs touch

This is the one failure mode worth recognising on sight. If a model's hands rest against its hips, then at a coarse voxel size the hand and the hip merge, and that arm's skeleton takes a short cut through the contact instead of going round through the shoulder. The symptom is one full-length arm and one stunted one.

The tool detects this rather than shipping it quietly. Two limbs that end in mirrored places but start in different ones is the signature, and it produces a named warning telling you to raise Detail. The default is 150.

Mirroring, and why cut order matters

Mirror across symmetry reflects every cut through the detected plane and flips _L/_R in the name. Cuts sitting on the plane — a head, a tail — are skipped, as are ones whose twin already exists, so pressing it twice does not double the rig. The roll angle flips with the plane; without that a mirrored hinge bends the opposite way to its twin.

Cut distal joints first. This one is not cosmetic. Each cut takes its part off the body and carries on with what is left, so cutting a leg off at the hip and then asking for a knee finds nothing — the leg left with the hip. The symptom is a silent wrong cut somewhere else on the model. It is on by default; the checkbox exists so you can see what it is doing.

Saved rigs. A joint layout that suits one biped suits the next one, so rigs save to output/rigs/ as JSON and reload onto a different model — positions are remapped through the bounding boxes, while clearances are left alone, because those are printer numbers and shrinking them with the model is how you get a joint that fuses.


Painting and recolouring

Group & assign already answers "print this cluster in that filament". It does not answer "the model gave the character a brown collar and I want a red one".

Four selection modes, because the right one depends on why the region is a region:

mode use it when
similar, connected most of the time — follows the collar round the neck and stops at the fur
same colour anywhere all four paws in one go
brush the colour has no boundary at all — a shading ramp
whole connected piece models that arrived in parts

Flood fill runs over mesh edges, never over a distance threshold in space. Two surfaces that pass close to each other — an arm against a torso, a tail lying on a back — are near in space and far apart across the surface, and a radius-based fill leaks straight from one to the other. Tolerance is in ΔE, not RGB: 10/255 is a visible step between two near-blacks and invisible between two near-whites, so an RGB threshold grabs half the model in shadow and nothing in highlight.

Softening the edge blends across the boundary, and every blended vertex is a new colour — lovely on screen, expensive on a printer. The panel says how many colours you ended up with.


Plating and export

Orientation is not tidiness. FDM parts split along layer lines, so a peg printed standing up is a stack of discs glued end to end and snaps at the first layer boundary; the same peg lying down has its layers running along it.

Candidate orientations come from the convex hull's faces — a part will only sit stably on a face that is already on its hull, so that is the complete set of stable positions rather than an approximation. Each is scored on support needed (dominant), height, and bed contact. Parts are then shelf-packed onto your bed, each laid down in whichever of its two axis-aligned rotations is shorter.

3MF rather than OBJ for the final file. v x y z r g b is not in any OBJ spec — it is a de-facto extension, and whether a slicer reads it is luck. 3MF has an actual materials extension, so colour travels as data, and each part arrives as a separate named object instead of one merged blob. STL is also offered, and carries no colour at all.

Clicking centres on the limb, not the skin

A click lands on the outside of a limb, so using it directly as the anchor puts the joint against the surface instead of down the middle. With snap to the centre of the limb on, the click is sent to the server, which takes the mesh vertices in a band around the plane, keeps only the connected patch nearest your click, and returns that patch's centroid.

Which way does a hinge bend?

A plane has only two rotational degrees of freedom — its normal — and tilt and spin already cover those. The third rotation you need is the axle's angle within the plane, which is what the roll slider sets. The axle is drawn in the viewport as a yellow line so you can see the bend axis before committing.

roll axle runs along the part swings
0° Y (left–right) forward / back — a knee or a walking leg
90° X (front–back) left / right — a hip doing the splits

Reading the gizmo

yellow line the axle — what the part pivots about
orange double arrow the direction the part actually swings
cyan arrow the side that DETACHES, and gets the ball / axle / pins / tongue
magenta arrow the side that STAYS, and gets the socket cavity or fork slot
red / green / blue arrows drag to slide the plane in X / Y / Z

Confinement. A plane is infinite, so it slices everything it passes through. With "confine to the clicked piece" on (the default) only the connected component nearest your click is detached, and the rest of the model is left completely untouched. This is exact, not a bounding-box approximation.

Can you actually put it together?

So joints split into two families:

Assemble after printing — parts print separately, in any orientation:

Joint Motion How it goes together
Ball + cap 3-axis Ball drops into an open cup, then a separate printed cap presses in and traps it. Nothing flexes.
Friction ball ★ 3-axis Identical geometry, deliberately undersized clearance. The cap squeezes the ball, and the pose stays where you put it. Grip is adjustable per cut.
Clevis + pin 1 axis Tongue slides into a two-eared fork, a separate printed pin pushes through. Strongest and most forgiving.
Ratchet clevis ★ 1 axis A clevis with radial face teeth. Clicks into 6–24 fixed angles and holds. Tooth count comes from what the nozzle can resolve at that radius.
Peg + bore 1 axis Push-fit peg, free spin. Fits where a clevis is too wide for the limb.
Dowel pins ★ static Faces mate flush on 2+ alignment pins, then glue.

★ = holds a pose rather than swinging free.

Print already assembled — the joint is printed captured, with a clearance gap, and cannot be taken apart or put together later:

Joint Motion Constraint
Ball & socket (PIP) ~16° swing Both parts must print in the same job and orientation
Hinge (PIP) 1 axis Same

The extra parts appear in the results list as <part>_pin and <part>_cap.

Sizing is scale-independent

Joint dimensions come from the cross-section at the cut, so the same cut works on a 40 mm figurine and a 250 mm one. The only absolute number in the joint maths is the smallest feature your nozzle prints reliably — and that is a property of your printer, not of the model.

It comes from the Printer tab: pick your machine, and minimum feature (3x nozzle), joint clearance (0.5x nozzle) and dowel clearance (0.375x nozzle) all follow. Change to a 0.6 mm nozzle and every joint on every model resizes with it.

The machine also decides how much colour costs

mechanism example colour regions are…
toolchange Snapmaker U1 (4 heads), Prusa XL nearly free — push the count up
idex Snapmaker J1, Artisan cheap for two colours, a swap beyond that
purge Bambu + AMS, Prusa + MMU, Creality CFS genuinely expensive — fewer, larger areas
manual Ender 3, CORE One, K1 you are standing at the machine

Measured across scales, same cut, same leg:

Model height ball radius neck relief retention swing
80 mm 1.26 mm 0.63 mm 1.39 mm 88% 16°
150 mm 2.45 mm 1.22 mm 2.34 mm 88% 16°
250 mm 4.06 mm 2.03 mm 3.74 mm 88% 16°

Clearance defaults to 0.20 mm (0.15 mm for dowels) and is adjustable per cut.

About

Turns AI-generated mesh output into a watertight, manifold, correctly-scaled, multicolour-print-ready model, without losing the colour, and then into an articulated, poseable, printable figure

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages