Skip to main content

UV unwrapping pipeline with pybind11 bindings and preprocessing utilities

Project description

PartUV: Part-Based UV Unwrapping of 3D Meshes

SIGGRAPH Asia 2025

arXiv Project Page

Official implementation of PartUV: Part-Based UV Unwrapping of 3D Meshes.

---
Table of Contents

TODO List

  • [✅] Resolve the handling of non-2-manifold meshes, see Known Issues
  • [✅] Release benchmark code and data
  • [✅] Multi-atlas packing with uvpackmaster

Installation

PartUV (for UV Unwrapping)

# 1) Create and activate environment
conda create -y --name partuv python=3.11
conda activate partuv

# 2) Install PyTorch 2.7.1 (CUDA 12.8 wheels) and torch-scatter
# It should work with other PyTorch/CUDA versions, but those are not tested.
# conda install nvidia/label/cuda-12.8.1::cuda-toolkit
pip install torch==2.7.1 --index-url https://download.pytorch.org/whl/cu128
pip install torch-scatter -f https://data.pyg.org/whl/torch-2.7.1+cu128.html

# 3) Install project requirements and local wheel
pip install -r requirements.txt
pip install partuv

Download the PartField checkpoint from PartField:

wget https://huggingface.co/mikaelaangel/partfield-ckpt/resolve/main/model_objaverse.ckpt ./

Packing with bpy (optional)

# For Python 3.11+
pip install bpy
# For Python 3.10
pip install bpy==4.0.0 --extra-index-url https://download.blender.org/pypi/

Demo

TL;DR

python demo/partuv_demo.py --mesh_path {input_mesh_path} --save_visuals

Step 1: UV Unwrapping

The demo takes a 3D mesh (e.g., .obj or .glb) as input and outputs the mesh with unwrapped UVs in a non-packed format.

Input Requirements

We recommend using meshes without 3D self-intersections and non-2-manifold edges, as they may result in highly fragmented UVs.

Preprocessing

The input mesh is first preprocessed, including:

  • Mesh repair (e.g., merging nearby vertices, fixing non–2-manifold meshes, etc.)
  • (Optional) Exporting the mesh to a .obj file
  • Running PartField to obtain the hierarchical part tree

Unwrapping

We then run the unwrapping pipeline via our prebuilt pip wheels. Two main API versions are provided:

  • pipeline_numpy: The default version. It takes mesh NumPy arrays (V and F), the PartField dictionary (a hierarchical tree), a configuration file path, and a distortion threshold as input. Note that the distortion threshold specified here will override the value defined in the configuration file.
  • pipeline: Similar to pipeline_numpy, but it takes file paths as input and performs I/O operations directly from disk.

Output Results

Both APIs save the results to the output folder. The final mesh with unwrapped UVs is saved as final_components.obj. Each chart is flattened to a unit square, but inter-chart arrangement is not yet solved.

Individual parts are also saved as part_{i}.obj, which can be used with UVPackMaster to produce part-based UV packing (where charts belonging to the same part are grouped nearby). See the later section for more details.

The saving behavior can be configured in the save_results function.

If you specify the --pack_method flag, the code will pack the UVs and save the final mesh in final_packed.obj.

Hyperparameters

By default, the API reads all hyperparameters from config/config.yaml. See config.md for more details on hyperparameters and usage examples for customizing them to suit your needs.


Step 2: Packing

The unwrapping API outputs UVs in a non-packed format. You can pack all UV charts together to create a UV map for the input mesh. Two packing methods are supported:

  • blender: The default packing method. We provide a script (pack/pack_blender.py) that uses bpy for packing, which is called by default in the demo file.
  • uvpackmaster: A paid Blender add-on. We use this to achieve part-based packing (charts from the same part are packed close together) or automatic multi-atlas packing. Please see more details below.

Part-Based Packing with UVPackMaster

In our results, we include both part-based packing (where charts from the same part are packed close together) and automatic multi-atlas packing (given N desired tiles, parts are assigned to tiles according to the hierarchical part tree).

These results are packed using UVPackMaster, which unfortunately is a paid tool. We provide scripts to pack the UVs with UVPackMaster.

Installation

  1. Install BlenderProc: We use BlenderProc to run this add-on within Blender. Please follow the instructions in the BlenderProc repository to install it.

    pip install blenderproc
    
  2. Install UVPackMaster: Follow the instructions on the UVPackMaster website to obtain the Linux distribution. Download the ZIP file and place it in the extern/uvpackmaster folder.

  3. Install the add-on: We provide a script to install the add-on:

    blenderproc run pack/install_uvp.py
    

Usage

To pack UVs with UVPackMaster, use the same command as the default packing method, changing the --pack_method flag to uvpackmaster:

python demo/partuv_demo.py --mesh_path {input_mesh_path} --pack_method uvpackmaster --save_visuals

Multi-Atlas Packing

We also implement multi-atlas packing with UVPackMaster (which requires UVPackMaster to be installed and licensed like above). To use it, specify the --num_atlas flag:

python demo/partuv_demo.py --mesh_path {input_mesh_path} --pack_method uvpackmaster --save_visuals --num_atlas {num_atlas}

The script will pack the UVs into the specified number of atlases automatically based on the hierarchical part tree.


Benchmarking


We provide 4 datasets for benchmarking:

  • PartObjaverse-Tiny
  • common-meshes
  • Trellis
  • ABC

You can run the benchmark script by:

bash demo/benchmark/benchmark.sh

The script will run the benchmark on the 4 datasets and save the results to the output_meshes_python folder.

One example file structure is as follows:

ABC-benchmark/
├── ABC-benchmark/
│   └── mesh_id/
        ├── final_packed.obj
        ├── Other Final Results...
    ...
├── output/
    ├── mesh_id/
    |   └── Intermediate Results...
    ...

The script then evaluates the metrics on the final results, and generate a report in the html and json folders.

Building from Source

Please refer to build.md for detailed build instructions.


Known Issues

Handling non-2-manifold meshes

ABF expects 2-manifold meshes. The previous preprocessing strategy (vertex-splitting at non-manifold edges) could sometimes yield undesirable UV charts.

Update: Non-manifold edges are now split as UV seams (similar to Blender), which typically improves results on non-2-manifold inputs. Meshes with severe non-manifold structure may still require cleanup.

Common Problems

Below are common issues and their solutions:

1. Problem with cuda crt/math_functions.h

Modify math_functions.h according to the fix described at: https://forums.developer.nvidia.com/t/error-exception-specification-is-incompatible-for-cospi-sinpi-cospif-sinpif-with-glibc-2-41/323591/3

2. Floating-Point Error

Disable PAMO when running the pipeline on CPU machines.

3. ImportError: GLIBCXX_3.4.32 not found

export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libstdc++.so.6
python demo/partuv_demo.py

4. (Build) nvcc fatal: Unsupported gpu architecture 'compute_120'

Remove compute_120 from the CMAKE_CUDA_ARCHITECTURES in CMakeLists.txt.



🍀 Acknowledgments

We acknowledge the following repositories for their contributions and code:

and all the libraries in the extern/ folder.


BibTeX

If this repository helps your research or project, please consider citing our work:

@inproceedings{wang2025partuv,
  title     = {PartUV: Part-Based UV Unwrapping of 3D Meshes},
  author    = {Wang, Zhaoning and Wei, Xinyue and Shi, Ruoxi and Zhang, Xiaoshuai and Su, Hao and Liu, Minghua},
  booktitle = {ACM SIGGRAPH Asia Conference and Exhibition on Computer Graphics and Interactive Techniques},
  year      = {2025}
}

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

partuv-0.1.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (3.5 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

partuv-0.1.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (3.5 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

partuv-0.1.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (3.5 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

partuv-0.1.2-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (3.4 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

partuv-0.1.2-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (3.4 MB view details)

Uploaded CPython 3.9manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

File details

Details for the file partuv-0.1.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for partuv-0.1.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 9d343725101b68fb4ad631e598a6c3cb5c673ae0ed76123a2daec8b8d08d87e6
MD5 0f92b1387b8159e7ac9341d3f898ab0c
BLAKE2b-256 4d88b48b55afee7630b2274a97df36f8f46d466daf1ee85be5155842b285b5d3

See more details on using hashes here.

File details

Details for the file partuv-0.1.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for partuv-0.1.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 dcffaecde98c1e4a0e97ddf99ec9b229079d79cb172a17817a7416f3df2519f5
MD5 b265a2c390c1c307686b090016809a2f
BLAKE2b-256 1c8a56814ae7112710299ba4c0bd444ccb0df07e2a0416ae089cccad17b4c42b

See more details on using hashes here.

File details

Details for the file partuv-0.1.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for partuv-0.1.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 27f496d6152d2d78454de3b9f1b8cf1fb3cf6921bc5a2f85960e7663dc97c985
MD5 3252768171c4d92535d068c117ae9928
BLAKE2b-256 36f196a207661ba13008198031d0a45f985186a44b2c2ef8522addcc86de0f9d

See more details on using hashes here.

File details

Details for the file partuv-0.1.2-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for partuv-0.1.2-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 9bc821846cc7c136d4cb2adecdcb5d8b4172e8a072514830a03f2b60f9b67928
MD5 52923eba34ce054845e22249845db219
BLAKE2b-256 96329199cbd9c85b03f637b42e71e478d841bb4fccdecb72bf813b57fae1698c

See more details on using hashes here.

File details

Details for the file partuv-0.1.2-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for partuv-0.1.2-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 5df6dc91c394435d708e3605938c99fccfcf07281c2e43198e7ae64fba1b13af
MD5 ec47fe27c73deaf5e22a1ee43eceec76
BLAKE2b-256 fe977474104c5bd86aacc5844b4bde4aff6f8aba61394b2de15b70027ed1fac9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page