---
name: hi3d-mesh-refinement
description: Refine an existing 3D mesh with Hi3D 1.0 and a matching reference image, using tested GPU setup, geometry checks and a game-export handoff.
---

# Hi3D 1.0: tested mesh refinement for game assets

Use this process to refine a coarse character, building or prop while keeping an inspectable original. This original operational guide records actual October 25, 2026 King Conquest tests. Hi3D requires **a triangle mesh plus a reference image of the same object**. Its Python package is `sparc3d`, and its API is `Sparc3DPipeline`. Output is static geometry; texture, materials, skin weights, animation, original position and absolute scale do not survive.

Official source: https://github.com/hi3d-official/Hi3Dv1.0
Model: https://huggingface.co/hi3d-official/Hi3Dv1.0
Installation: https://github.com/hi3d-official/Hi3Dv1.0/blob/main/docs/installation.md

The guide's MIT license covers this original text only. Code, model weights, image encoders, coarse generators, source images and motion assets retain their respective upstream terms. Keep their notices with downstream deliveries.

## 1. Prepare a trustworthy input

Reuse an existing mesh when available. Image-only generation is a separate upstream step: our knight started with Hunyuan3D 2.1, but an authored Blender mesh can also work. Hi3D does not require Hunyuan. Keep original mesh/image hashes and export into a new derivative directory.

For a character, use an unobstructed pose with separated limbs. Render or photograph the SAME object in the SAME pose as the coarse mesh. Do not use a multi-view contact sheet as the single conditioning image. Inspect alpha and silhouette; near-transparent background contamination once produced a giant sheet in our coarse Hunyuan mesh. A threshold of127 fixed that particular input; choose a mask appropriate to the subject and inspect fine edges before reuse.

For rigged GLBs, bake the intended pose into a copy of the actual render meshes. Restrict the export to original source mesh-node names or an explicit allowlist. Our first herald bake accidentally included Blender's bone-display Icosphere. That contaminated run is excluded from model-quality evidence. Exclude cameras, armature display shapes, collision helpers and unrelated hidden meshes.

If cleaning disconnected debris, weld duplicate positions for connectivity analysis while preserving per-loop UVs. UV seams can appear to be isolated islands; deleting them tears valid surfaces. Preserve architectural flags, trim and finials until visual inspection establishes they are debris.

## 2. Establish GPU readiness and install

Use an authorized existing NVIDIA worker where possible. Confirm current provider state separately from cached installation records. Run one heavy job at a time. A listed GPU or successful `nvidia-smi` does not prove CUDA works: one rented host failed raw CUDA initialization with error999 and `/dev/nvidia-uvm` EIO.

In the intended environment, require a real allocation before downloading models:

```bash
python -c 'import torch; assert torch.cuda.is_available(); print(torch.ones(8, device="cuda").sum().item())'
```

Expected sum:8.0. If Torch is not installed yet, probe the worker's existing Torch environment first, then repeat in the new environment. Persistent host initialization failure belongs to the provider; do not spend the run rebuilding dependencies around it.

Measured Linux stack: Python3.10.22, Torch2.5.0+cu124, torchvision0.20.0+cu124, flash-attn2.7.4.post1, spconv-cu1202.3.6 and Transformers4.46.3. Install into an isolated Python3.10 environment on retained storage, with a matching CUDA toolkit for source builds:

```bash
git clone https://github.com/hi3d-official/Hi3Dv1.0.git
cd Hi3Dv1.0
git checkout 13657ad09afaa92e350b461a1cd66399e44a59c4
python -m pip install torch==2.5.0+cu124 torchvision==0.20.0+cu124 --extra-index-url https://download.pytorch.org/whl/cu124
python -m pip install packaging ninja psutil setuptools wheel
python -m pip install --no-build-isolation flash-attn==2.7.4.post1
python -m pip install -c docker/constraints.txt -e '.[inference]'
python -m pip freeze > environment-freeze.txt
```

Our remote install used the matching upstream prebuilt flash-attn wheel instead of compiling. Wheel compatibility must match Python, Torch, CUDA and C++ ABI. Local installation initially failed with a misleading Triton error because `setuptools` was missing; keep that runtime dependency.

Retain source, environment, model cache and Python interpreter together. A venv pointing to a transient interpreter will not survive restart. Plan storage for about10.66GB of model/encoder weights plus the environment, cache and outputs; this weight total is not the total disk requirement.

## 3. Pin and stage models

Tested revisions:

| Artifact | Revision |
| --- | --- |
| Hi3D source | `13657ad09afaa92e350b461a1cd66399e44a59c4` |
| Hi3D weights | `d502e938cbedc6e87aba8884614c5ca6d177a4b6` |
| facebook/dinov2-large | `47b73eefe95e8d44ec3623f8890bd894b6ea2d6c` |
| openai/clip-vit-large-patch14 | `32bd64288804d66eefd0ccbe215aa642df71cc41` |

Use `huggingface_hub.snapshot_download(repo_id, revision=...)` for each repository. Keep DINO and CLIP snapshots separate from the Hi3D runtime root. Stage these eight Hi3D files as regular files with the relative layout preserved:

```text
model_index.json
scheduler/scheduler_config.json
vae/config.json
vae/model.safetensors
transformer_512/config.json
transformer_512/model.safetensors
transformer_1024/config.json
transformer_1024/model.safetensors
```

The tested local loader rejected extra cache symlinks. Copy the eight files into a clean task-owned runtime directory using `shutil.copy2`; preserve LICENSE/NOTICE and third-party terms separately. Do not relocate blobs out of a shared Hub cache: that can break other jobs. Our volume also rejected hardlinks, so do not assume hardlink support. Offline repeat runs should set `HF_HUB_OFFLINE=1` after all artifacts have been verified.

## 4. Run one 512 refinement first

Set `HI3D_MODELS` to the runtime directory, `DINO_MODELS` and `CLIP_MODELS` to their encoder snapshot paths, `INPUT_MESH`/`INPUT_IMAGE` to matching inputs and `OUTPUT_MESH` to a new GLB path. Save this as `refine.py` in the prepared environment:

```python
import os, time, json, hashlib
from pathlib import Path
import numpy as np
import torch, trimesh
from PIL import Image
from sparc3d import Sparc3DPipeline
from sparc3d.sparcubes.postprocess import validate_mesh

out = Path(os.environ['OUTPUT_MESH'])
receipt = out.with_suffix('.json')
assert not out.exists() and not receipt.exists(), 'Inspect existing output before retrying'
assert float(torch.ones(8, device='cuda').sum()) == 8.0
pipe = Sparc3DPipeline.from_pretrained(
    Path(os.environ['HI3D_MODELS']), local_files_only=True,
    dino_model_id=Path(os.environ['DINO_MODELS']),
    clip_model_id=Path(os.environ['CLIP_MODELS']))
# Choose ONE placement mode before inference:
pipe.to('cuda')
# For an offload retry, replace the line above with:
# pipe.enable_sequential_cpu_offload('cuda')
mesh_path = Path(os.environ['INPUT_MESH'])
image_path = Path(os.environ['INPUT_IMAGE'])
coarse = validate_mesh(trimesh.load(mesh_path, process=False))
torch.cuda.reset_peak_memory_stats()
t0 = time.monotonic()
result = pipe(Image.open(image_path), coarse,
              resolution=512, num_inference_steps=25, seed=42)
torch.cuda.synchronize()
seconds = time.monotonic() - t0
assert len(result.mesh.faces) > 0 and np.isfinite(result.mesh.vertices).all()
out.parent.mkdir(parents=True, exist_ok=True)
result.mesh.export(out)
sha = lambda p: hashlib.sha256(p.read_bytes()).hexdigest()
receipt.write_text(json.dumps(dict(
    resolution=result.resolution, steps_per_pass=25, seed=42,
    inference_seconds=seconds, gpu=torch.cuda.get_device_name(),
    torch=torch.__version__, triangles=len(result.mesh.faces),
    watertight=bool(result.mesh.is_watertight),
    bounds=result.mesh.bounds.tolist(), geometry_only=True,
    peak_allocated_bytes=torch.cuda.max_memory_allocated(),
    peak_reserved_bytes=torch.cuda.max_memory_reserved(),
    input_mesh_sha256=sha(mesh_path), input_image_sha256=sha(image_path),
    output_sha256=sha(out)), indent=2))
```

Run `python refine.py`. Record setup/load time separately from inference. Keep stderr and exact artifact revisions with the receipt. Use a distinct output name for changed inputs, resolutions or placement modes. After an uncertain run, inspect the output, process and receipt before retrying. This example is adapted from our exercised runners; it was syntax-checked for publication, not rerun on GPU during publication.

512 is an extraction-grid label, not a polygon budget. Our raw results were roughly1million triangles. Escalate to1024 only after the512 result is useful. CPU offload still uses a CUDA GPU and substantial host RAM; enable it instead of `.to('cuda')`, never together. 1536 was not tested here.

## 5. Compare actual geometry and prepare for the game

Inspect front, rear, sides, silhouette and fine details in clay renders at matched cameras. Include the original as a comparison. Finite vertices and export success establish structural validity, not visual quality. We observed lost roof shingles, flags/finials and a missing central roof even at1024. Several outputs were not watertight; do not call them printable/manifold without additional repair.

Keep the high-resolution master. Make a separately named decimated derivative; our review target was50,000 triangles. That is a review convenience, not a proven mobile or army-scale budget. Recheck thin geometry after simplification. Restore metre scale, ground pivot and forward direction explicitly because upstream normalizes and recenters geometry.

Texture or rebake materials in a separate stage. Re-rig changed character topology and verify weight sums, joints and deformation. Motion must be retargeted/baked separately and inspected at multiple times per clip. Related tested guides:
- https://promoteindex.com/skills/unirig-rig-custom-3d-characters-without-replacin
- https://promoteindex.com/skills/custom-3d-rts-asset-pipeline-a-reusable-skill-pa

Validate the exported glTF, then test real engine lighting, culling, grounding, scale, multiple animated instances and frame cost. A static refinement does not inherit the source's animation readiness. Preserve the existing accepted game asset until the derivative passes its intended use.

## Tested results and limits

Actual remote RTX4090 24GB results,25steps/pass,seed42, October25:

| Input | Mode | Seconds | Peak allocated GiB | Result |
| --- | --- | ---: | ---: | --- |
| Priest512 | GPU | 36.9 | 7.16 | success |
| Guild town center512 | GPU | 71.5 | 9.41 | success |
| Citadel512 | GPU | 84.6 | 10.17 | success |
| Smithy512 | GPU | 94.8 | 11.20 | success |
| Priest1024 | GPU | 169.8 | 14.61 | success |
| Citadel1024 | GPU | failed | 21.22 | CUDA OOM |
| Citadel1024 | CPU offload | 524.6 | 15.82 | success |

Nine valid remote refinements succeeded, with seven50k derivatives,16 result GLBs and47 review PNGs. The separate contaminated herald attempt was excluded. Delivery QA verified199 manifest hashes, GLB headers/counts and PNG integrity. Local RTX3090Ti24GB knight512 refinement also completed:40.23s inference,29.35s load,7.24GB allocated/7.78GB reserved memory;654,700 output triangles. Local1024 was not qualified. Timings depend on geometry and exclude installation/downloads.

These tests establish geometry-refinement feasibility and documented visual tradeoffs. They do not establish faithful hidden geometry, universally improved artwork, preserved paint/rigs,1536 viability on24GB, or production game acceptance. No new GPU run was performed to publish this guide.

For paid workers, reuse authorized retained infrastructure and a bounded budget. Stop compute after retrieving outputs, verify hashes and independently confirm provider stopped state. A background guard that exited143 was not accepted as shutdown evidence in our assessment; explicit provider EXITED was verified. Retained storage can still cost money while compute is stopped.
