UV unwrapping pipeline with pybind11 bindings and preprocessing utilities
Project description
PartUV: Part-Based UV Unwrapping of 3D Meshes
SIGGRAPH Asia 2025
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
.objfile - 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 (VandF), 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 topipeline_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 usesbpyfor 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
-
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
-
Install UVPackMaster: Follow the instructions on the UVPackMaster website to obtain the Linux distribution. Download the ZIP file and place it in the
extern/uvpackmasterfolder. -
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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
- Download URL: partuv-0.1.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 3.5 MB
- Tags: CPython 3.13, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.18
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9d343725101b68fb4ad631e598a6c3cb5c673ae0ed76123a2daec8b8d08d87e6
|
|
| MD5 |
0f92b1387b8159e7ac9341d3f898ab0c
|
|
| BLAKE2b-256 |
4d88b48b55afee7630b2274a97df36f8f46d466daf1ee85be5155842b285b5d3
|
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
- Download URL: partuv-0.1.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 3.5 MB
- Tags: CPython 3.12, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.18
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dcffaecde98c1e4a0e97ddf99ec9b229079d79cb172a17817a7416f3df2519f5
|
|
| MD5 |
b265a2c390c1c307686b090016809a2f
|
|
| BLAKE2b-256 |
1c8a56814ae7112710299ba4c0bd444ccb0df07e2a0416ae089cccad17b4c42b
|
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
- Download URL: partuv-0.1.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 3.5 MB
- Tags: CPython 3.11, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.18
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
27f496d6152d2d78454de3b9f1b8cf1fb3cf6921bc5a2f85960e7663dc97c985
|
|
| MD5 |
3252768171c4d92535d068c117ae9928
|
|
| BLAKE2b-256 |
36f196a207661ba13008198031d0a45f985186a44b2c2ef8522addcc86de0f9d
|
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
- Download URL: partuv-0.1.2-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 3.4 MB
- Tags: CPython 3.10, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.18
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9bc821846cc7c136d4cb2adecdcb5d8b4172e8a072514830a03f2b60f9b67928
|
|
| MD5 |
52923eba34ce054845e22249845db219
|
|
| BLAKE2b-256 |
96329199cbd9c85b03f637b42e71e478d841bb4fccdecb72bf813b57fae1698c
|
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
- Download URL: partuv-0.1.2-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 3.4 MB
- Tags: CPython 3.9, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.18
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5df6dc91c394435d708e3605938c99fccfcf07281c2e43198e7ae64fba1b13af
|
|
| MD5 |
ec47fe27c73deaf5e22a1ee43eceec76
|
|
| BLAKE2b-256 |
fe977474104c5bd86aacc5844b4bde4aff6f8aba61394b2de15b70027ed1fac9
|