Self-hosted photo / 3D-model → Minecraft gallery appliance (repo codename
mc-pipeline). Drop in a photo (or a 3D model) through the web UI; it becomes a
voxel build, colored to Minecraft blocks and placed into a live Creative world,
viewable on a BlueMap web map. Everything runs in Docker on your own hardware — no
external SaaS.
A demo model voxelized by the pipeline (procedurally generated, no external assets):
photo ──▶ TripoSR (GPU) ──▶ mesh ─┐
├─▶ voxelize + color→block ─▶ .nbt / .schem ─▶ place via RCON ─▶ live Creative server ─▶ BlueMap
3D model (.glb/.obj/.stl/…) ──────┘ │
web upload UI + job queue
The individual pieces are off-the-shelf, but nobody has bottled the full photo → GPU mesh → voxelize → auto-inject into a live server loop as a turnkey self-hosted thing. The genuinely hard parts — solved here, and mostly undocumented anywhere else:
- Headless placement. WorldEdit's
//pasteneeds a player position; RCON has none. This routes around it with Minecraft's native/place template(+ the/forceload, async-load, and/reload-drops-RCON gotchas) for models ≤48³, and a tiny bundled FastAsyncWorldEdit plugin (/pasteschem) for larger builds. Seedocs/PLACEMENT.md. - Version reality. Structure
DataVersion, the modernworld/dimensions/...layout, FAWE build matching, mcschematic's version cap — this rots fast and is exactly what breaks copycats. Seedocs/COMPATIBILITY.md. - The glue. A job queue that serializes the GPU, a gallery slot allocator, a build-height ceiling clamp, memory safeguards against runaway voxelizations, and per-source orientation fixes.
- Photo → 3D via TripoSR (GPU),
or upload a 3D model directly (
.glb .gltf .obj .stl .ply .3mf .off) — great for CAD / SketchUp / 3D-print models. No GPU? PointMESH_BACKEND=externalat any hosted image-to-3D API (or skip photos entirely and only upload 3D models). - Fast surface-sampling voxelizer — colors → block palette via CIELAB, scales with mesh surface area rather than triangle count. A ~290k-triangle CAD model (a full house, interior included) voxelizes in seconds, not minutes.
- Single-block builds: pick a block (stone bricks, oak planks, …) in the UI to build colorless CAD/STL models in one material instead of a default gray.
- Model height up to 256 (native
/place template≤48³, FAWE/pasteschemabove), with an automatic ceiling clamp so tall builds are never cut off. - Gallery of processed models with thumbnails and one-click re-placement (no reprocessing).
- Jobs manager in the UI — live queued/running/finished/failed with stages + errors.
- In-game editing: every model is also a WorldEdit schematic, so an op player can
//schem load <name>and fine-tune placement in-game (see below). - Orientation fixes per source (TripoSR vs Z-up CAD), env-tunable + per-upload override.
- Safeguards: single GPU worker (serialized), hard worker memory limit, voxel-grid resolution cap.
- BlueMap web map of the gallery world.
- Docker + Docker Compose.
- For the photo → 3D path: an NVIDIA GPU (~10 GB VRAM card is plenty; TripoSR uses
~6 GB) + the NVIDIA Container Toolkit. No GPU? Set
MESH_BACKEND=externaland pointMESH_EXTERNAL_URLat any hosted image-to-3D API — seemesh-service/backends/external.py. - 3D-model uploads need no GPU at all — CAD/SketchUp/STL files voxelize on CPU.
- ~5 GB disk for images + model weights, plus room for worlds.
git clone <your-fork> mc-pipeline && cd mc-pipeline
cp .env.example .env # set RCON_PASSWORD, DATA_ROOT, MC_VERSION, WHITELIST…
docker compose build # builds the TripoSR image (CUDA + torchmcubes) + API
docker compose up -d
# one-time BlueMap accept-download, then restart the creative server:
# edit $DATA_ROOT/creative/plugins/BlueMap/core.conf -> accept-download: true
docker compose restart mcp-creative
# (optional, for models > 48³) build + install the FAWE paster plugin:
# see docs/PLACEMENT.mdOpen the upload UI at http://<host>:8142/, join the creative server at
<host>:25566, and view the map at http://<host>:8165/.
| Service | Role |
|---|---|
mcp-mesh |
GPU: image → mesh (TripoSR; pluggable backend — see mesh-service/backends/) |
mcp-redis |
job queue |
mcp-api |
FastAPI orchestrator; serves the upload UI + gallery + jobs manager |
mcp-worker |
single RQ worker (GPU serialized); voxelize → write .nbt/.schem → place |
mcp-creative |
bundled Paper server (Creative + FAWE + BlueMap) — the pipeline's target |
Pipeline stages: queued → generating_mesh (photos only) → voxelizing → placing → done.
Everything is one compose.yaml; the creative server is bundled (the pipeline does
not depend on a game panel). Service hostnames are mcp--prefixed on purpose so they
don't collide with other containers on a shared reverse-proxy network.
compose.yaml the whole appliance
.env.example config template
mesh-service/ GPU image→mesh (TripoSR, pluggable backend) + Dockerfile
voxel-worker/ mesh→colored voxels→.nbt/.schem, palette, thumbnails, CLI, demo model
injector/ RCON placement: /place template + FAWE /pasteschem + gallery
fawe-plugin/ McPipelinePaster — console-safe /pasteschem for models >48³ (Gradle)
pipeline-api/ FastAPI + RQ worker + Dockerfile
frontend/ single-page upload UI + gallery + jobs manager
deploy/ example Caddy reverse-proxy + service-catalog snippets
docs/ PLACEMENT.md (the hard-won headless-placement knowledge) + COMPATIBILITY.md
All via .env (see .env.example). Highlights:
| Var | What |
|---|---|
RCON_PASSWORD |
required |
DATA_ROOT |
where worlds / uploads / weights live (put it on a disk with room) |
MC_VERSION |
pin exactly (see docs/COMPATIBILITY.md) |
MAX_HEIGHT |
max model height (default 256) |
VOXEL_ROTATE / MESH_ROTATE |
orientation fix for photos / 3D models (e.g. x-90) |
WORKER_MEM_LIMIT |
hard cap so a runaway voxelization OOMs the container, not the host |
MESH_BACKEND |
triposr (default, GPU) or external (proxy to a hosted API — no GPU needed) |
MAX_VOXEL_FACES |
optional mesh decimation cap; 0 (default) disables it |
Every processed model is saved as a Sponge .schem under <world>/mcpipeline-schem/.
Point FAWE's schematic folder at it and all models show up in WorldEdit in-game:
# data/creative/plugins/FastAsyncWorldEdit/worldedit-config.yml
saving:
dir: /data/world/mcpipeline-schemRestart the creative server once, then as an op player:
//schem list # every pipeline model
//schem load anna1
//paste -a # paste at your position (-a skips air)
//rotate 90 # …fine-tune
//undo
For hologram-preview placement (position a ghost of the model before committing),
client mods like Litematica or Axiom can import the same .schem files.
- Voxelizing is fast even for complex meshes. The voxelizer surface-samples the
mesh (scales with surface area, not triangle count), so a ~290k-triangle house STL —
interior geometry included — voxelizes in seconds. Optional decimation is still
available (
MAX_VOXEL_FACES) as an escape hatch for pathological meshes, but it's off by default since it no longer helps. - STL/3MF carry no color → those build monochrome by default. Either accept the
gray/stone fallback, or pick a single block in the UI (stone bricks, oak planks,
…) for a deliberate one-material build.
.glbwith materials keeps real color. - Orientation: photos default to
VOXEL_ROTATE; 3D models toMESH_ROTATE(CAD is usually Z-up →x-90). Override per-upload in the UI's Advanced field.
deploy/Caddyfile.snippet is an example for fronting the upload UI + map with
Caddy behind Cloudflare Access. Swap in your domains. The
game protocol is TCP/UDP and can't go through an HTTP-only tunnel — expose the game port
on your LAN/VPN.
MIT — see LICENSE.

