# How to Convert Textured Meshes to O-Voxel Format for TRELLIS.2 Training Data

> Convert textured meshes to O-Voxel format for TRELLIS.2 training data. Use the dump_mesh.py script for CUDA-accelerated voxelization and serialization into compact binary files.

- Repository: [Microsoft/TRELLIS.2](https://github.com/microsoft/TRELLIS.2)
- Tags: how-to-guide
- Published: 2026-08-04

---

**To convert textured meshes to O-Voxel format for TRELLIS.2 training data, use the [`data_toolkit/dump_mesh.py`](https://github.com/microsoft/TRELLIS.2/blob/main/data_toolkit/dump_mesh.py) script which loads your mesh, rasterizes it via CUDA-accelerated voxelization in [`o_voxel/rasterize.py`](https://github.com/microsoft/TRELLIS.2/blob/main/o_voxel/rasterize.py), and serializes the result to a compact `.o_voxel` binary file.**

TRELLIS.2 trains on a proprietary sparse-voxel representation called **O‑Voxel** that efficiently stores fully textured 3D assets with their PBR attributes. Before you can train the model, you must preprocess your source meshes—whether OBJ, GLTF, or PLY—into this format. The Microsoft TRELLIS.2 repository provides a complete conversion pipeline that runs without rendering and completes in under 10 seconds per mesh on a single CPU core.

## Prerequisites: Install the O-Voxel Library

The conversion tools depend on compiled CUDA kernels. Initialize the submodule before your first conversion:

```bash
. ./setup.sh --o-voxel

```

This pulls and builds the `o-voxel` package located at `o-voxel/o_voxel/` in the repository root.

## Method 1: Command-Line Conversion (Recommended)

The fastest way to convert textured meshes to O-Voxel format is through [`data_toolkit/dump_mesh.py`](https://github.com/microsoft/TRELLIS.2/blob/main/data_toolkit/dump_mesh.py). This script orchestrates the full pipeline: loading, rasterization, serialization, and metadata generation.

```bash
python data_toolkit/dump_mesh.py \
    --mesh_path /path/to/your_mesh.obj \
    --output_path /path/to/output_dir/your_mesh.o_voxel \
    --resolution 256

```

### Key Arguments

| Argument | Description |
|----------|-------------|
| `--mesh_path` | Path to source mesh with texture coordinates and PBR materials |
| `--output_path` | Destination `.o_voxel` file path |
| `--resolution` | Voxel grid resolution per axis (default: 256) |
| `--voxel_size` | Physical voxel size in world units (default: 1/resolution) |
| `--attributes` | PBR channels to retain: `color,roughness,metallic,opacity` |

The script automatically extracts vertex positions, face indices, and per-vertex texture attributes during loading. It then invokes the rasterizer and writes a JSON sidecar ([`metadata.json`](https://github.com/microsoft/TRELLIS.2/blob/main/metadata.json)) recording the source name, resolution, and preprocessing flags.

## Method 2: Python API for Batch Processing

For dataset-scale conversion, use the `o_voxel` module directly in Python. This approach gives you full control over batching, parallelization, and custom preprocessing.

```python
import os
from trellis2.utils.vis_utils import load_mesh
import o_voxel

def mesh_to_ovoxel(mesh_path, out_path, resolution=256):
    # Step 1: Load mesh with PBR attributes

    mesh = load_mesh(mesh_path)
    
    # Step 2: CUDA-accelerated rasterization

    voxel_grid = o_voxel.rasterize(
        vertices=mesh.vertices,
        faces=mesh.faces,
        attributes=mesh.attributes,  # dict of PBR channels

        resolution=resolution,
        voxel_size=1.0 / resolution,
    )
    
    # Step 3: Serialize to O-Voxel binary format

    o_voxel.serialize(
        voxel_grid,
        out_path,
        aabb=[[-0.5, -0.5, -0.5], [0.5, 0.5, 0.5]],
        attr_layout=mesh.attr_layout,
    )
    
    # Step 4: Write metadata JSON

    metadata = {
        "source_mesh": os.path.basename(mesh_path),
        "resolution": resolution,
        "voxel_size": 1.0 / resolution,
    }
    with open(out_path + ".json", "w") as f:
        import json
        json.dump(metadata, f, indent=2)

```

### Parallel Batch Conversion

The rasterizer creates isolated CUDA contexts per process, making it thread-safe for parallel execution:

```python
from concurrent.futures import ProcessPoolExecutor

mesh_files = [f for f in os.listdir("my_meshes") if f.endswith(".obj")]

with ProcessPoolExecutor(max_workers=8) as executor:
    executor.map(
        lambda f: mesh_to_ovoxel(
            os.path.join("my_meshes", f),
            os.path.join("ovoxels", f.replace(".obj", ".o_voxel")),
            256
        ),
        mesh_files
    )

```

## Verifying Your O-Voxel Output

Confirm successful conversion by deserializing and inspecting the voxel grid:

```python
import o_voxel

voxel = o_voxel.deserialize("my_asset.o_voxel")
print("Voxel shape:", voxel.shape)           # (256, 256, 256, 4)

print("Attribute layout:", voxel.layout)      # e.g., ['color', 'roughness', 'metallic', 'opacity']

```

If the grid appears empty or distorted, verify that your source mesh contains UV coordinates in the `[0, 1]` range and valid PBR material channels. The rasterizer in [`o-voxel/o_voxel/rasterize.py`](https://github.com/microsoft/TRELLIS.2/blob/main/o-voxel/o_voxel/rasterize.py) requires properly unwrapped textures to interpolate attributes onto the voxel grid.

## Core Pipeline Components

Understanding the underlying implementation helps debug conversion failures and optimize for your data.

### Mesh Loading: [`data_toolkit/dump_mesh.py`](https://github.com/microsoft/TRELLIS.2/blob/main/data_toolkit/dump_mesh.py)

The CLI entry point at [`data_toolkit/dump_mesh.py`](https://github.com/microsoft/TRELLIS.2/blob/main/data_toolkit/dump_mesh.py) handles:
- Triangle mesh ingestion via `trimesh.load` or internal loaders
- Extraction of per-vertex PBR attributes (base color, roughness, metallic, opacity)
- Coordination with the rasterization and serialization modules

### Voxel Rasterization: [`o-voxel/o_voxel/rasterize.py`](https://github.com/microsoft/TRELLIS.2/blob/main/o-voxel/o_voxel/rasterize.py)

The CUDA kernel implemented in [`o_voxel/rasterize.py`](https://github.com/microsoft/TRELLIS.2/blob/main/o_voxel/rasterize.py) projects mesh triangles onto a 3D grid. For each voxel intersecting a triangle, it barycentrically interpolates the four PBR channel values. This operation is **render-free**—it operates directly on geometry without GPU rendering pipelines.

### Binary Serialization: [`o-voxel/o_voxel/serialize.py`](https://github.com/microsoft/TRELLIS.2/blob/main/o-voxel/o_voxel/serialize.py)

The `o_voxel.serialize()` function in [`o-voxel/o_voxel/serialize.py`](https://github.com/microsoft/TRELLIS.2/blob/main/o-voxel/o_voxel/serialize.py) compresses the dense voxel grid into the `.o_voxel` format. The binary structure stores:
- Voxel dimensions and axis-aligned bounding box
- Attribute layout descriptor
- Run-length encoded voxel data for sparse regions

## Performance Characteristics

| Aspect | Specification |
|--------|-------------|
| Runtime | < 10 seconds per mesh (256³ resolution, single CPU core) |
| Memory | Proportional to resolution³ × 4 channels × 4 bytes |
| Parallelism | Multi-process safe; each worker requires separate CUDA context |
| Input requirements | Watertight mesh preferred; requires UVs and PBR materials |

## Connecting to Training

Once converted, reference your O-Voxel files in the training configuration. The [`train.py`](https://github.com/microsoft/TRELLIS.2/blob/main/train.py) script accepts a `--data_dir` argument pointing to a JSON manifest that lists your `.o_voxel` paths. See [`data_toolkit/README.md`](https://github.com/microsoft/TRELLIS.2/blob/main/data_toolkit/README.md) for the expected directory layout and manifest format.

## Summary

- **Install dependencies**: Run `. ./setup.sh --o-voxel` to build CUDA kernels
- **Single conversion**: Use `python data_toolkit/dump_mesh.py` with `--mesh_path` and `--output_path`
- **Batch processing**: Import `o_voxel` directly and parallelize with `ProcessPoolExecutor`
- **Verify output**: Call `o_voxel.deserialize()` to inspect grid shape and attribute layout
- **Troubleshoot**: Ensure source meshes have valid UVs in `[0, 1]` and complete PBR channels

## Frequently Asked Questions

### What mesh formats does the O-Voxel converter support?

The converter supports OBJ, GLTF, GLB, and PLY files through the `trellis2.utils.vis_utils.load_mesh()` utility. All formats must include texture coordinates and material definitions for the PBR channels you intend to rasterize. Untextured meshes will produce voxel grids with default attribute values.

### Why is my output voxel grid empty?

Empty grids typically indicate missing or malformed UV coordinates. The rasterizer in [`o-voxel/o_voxel/rasterize.py`](https://github.com/microsoft/TRELLIS.2/blob/main/o-voxel/o_voxel/rasterize.py) requires texture coordinates to map PBR attributes onto voxel positions. Verify your mesh has UVs in the `[0, 1]` range and that faces reference valid UV indices. Non-manifold geometry or zero-area triangles can also cause rasterization failures.

### Can I convert meshes without CUDA?

No—the voxel rasterization depends on CUDA kernels compiled during `setup.sh --o-voxel`. CPU-only rasterization is not implemented in the current TRELLIS.2 codebase. You need an NVIDIA GPU with CUDA toolkit installed to run the conversion pipeline.

### How do I choose the right resolution value?

The `--resolution` parameter controls the voxel grid size per axis. Use 128 for rapid prototyping, 256 for standard training data (recommended default), or 512+ for high-fidelity assets. Each doubling of resolution increases memory usage 8× and runtime approximately 4×. Match your training configuration's expected input resolution.