Explore the path from triangle meshes to texture space.
An experimental Rust workspace for mesh processing, UV unwrapping, and island packing. Build on the individual crates or run the complete unfolding pipeline.
Quick start Β· Features Β· Workspace Β· Development Β· Roadmap
Nine crates Β· inspectable algorithms Β· reproducible mesh fixtures
Important
Experimental software. ResearchUV is an active research prototype. APIs and numerical behavior may change; validate your meshes and UV output before using them in a production workflow.
Table of contents
Use a current stable Rust toolchain with Cargo.
git clone https://github.com/ahnafnafee/researchuv.git
cd researchuv
cargo test --workspace --locked --release
cargo run --locked --release -p researchuv-unwrap --example unwrap_cube -- cube-uv.svgThe example creates a subdivided cube, runs the unfolding pipeline, and writes its packed UV triangles to cube-uv.svg. Open the SVG in a browser to inspect the six islands.
A browser-served atlas editor runs the whole pipeline live:
cargo run --locked --release -p researchuv-editor -- --openPick a fixture, tune the cut angle, packer, seam cuts, distortion-driven re-cutting, thread count, and the CUDA solve; every run repaints the packed islands with per-chart distortion metrics.
The researchuv CLI unwraps OBJ/STL files or built-in fixtures and writes the packed atlas back out:
cargo run --locked --release -p researchuv-cli -- unwrap --fixture cube 6 --svg cube-atlas.svg
cargo run --locked --release -p researchuv-cli -- unwrap model.obj -o model-uv.obj
cargo run --locked --release -p researchuv-cli -- info model.stlunwrap prints a per-chart distortion table, uses the island packer by default (--packer shelf restores the reference packer), can seam-cut closed surfaces (--seam-cut), and exits non-zero when validation finds errors.
The same pipeline accepts vertex positions and triangle indices from your own mesh loader. This minimal example uses a built-in fixture:
use researchuv_unwrap::{meshgen, run, PipelineOptions};
let (positions, triangles) = meshgen::cube(6);
let result = run(positions, triangles, &PipelineOptions::default())
.expect("the input was validated");
assert_eq!(result.charts.len(), 6);
assert!(result.placed.iter().all(Option::is_some));
assert!(!result.atlas_report.has_errors());
for island in &result.multi.islands {
println!("{} vertices, {} triangles", island.uv.len(), island.tris.len());
}Add researchuv-unwrap as a path dependency when integrating it into another local project:
[dependencies]
researchuv-unwrap = { path = "../researchuv/crates/researchuv-unwrap" }Note
Everything is available from the Rust API: CLI, editor, I/O, API catalog, host integration, multi-core and CUDA execution.
Construct half-edge surfaces from triangles, traverse face adjacency, and keep source vertex indices alongside UV islands. The core crate also provides typed parameter values, a binary codec, configurable tasks, and island snapshots for undo and redo.
Weld coincident vertices, split the mesh into charts at sharp edges (optional geodesic seam trees for closed surfaces and distortion-driven re-cutting for charts that fold), and solve for UV coordinates with a least-squares conformal mapping (LSCM) driver β on the CPU's direct sparse LU, on worker threads, or on the GPU. Per-chart diagnostics report angular distortion, area ratios, flipped triangles, and winding folds before the results are normalized and packed.
The unfolding pipeline offers two final packers: the historic greedy shelf packer (the reference CFinalPack behavior recorded by the regression baselines) and the separate researchuv-pack engine β a CPU clone of UVPackmaster 4.1.2's island packer with rotations, flips, margins (relative or pixel), scale modes, target boxes, tiling, grouping, similarity stacking, texel density, pixel-perfect alignment, a time-budgeted heuristic search, and polygon-level overlap validation on island outlines with holes. Select it with PipelineOptions.packer = Packer::Islands (the CLI uses it by default).
| Area | Available building blocks |
|---|---|
| Mesh preparation | Vertex welding, degenerate-face filtering, adjacency, and boundary loops |
| Unwrapping | Sharp-edge charting, geodesic seam trees for closed charts, least-squares solving, border constraints, and chart normalization |
| Packing | Shelf packing or the configurable UVPackmaster-grade island packer, connected to the pipeline; optional near-square tilt candidates from the optimal equal-square packings |
| Diagnostics | Conformal and area distortion, winding-consistency folds, overlap checks, topology invariants, and packing quality (atlas utilization plus a certified optimality gap for equal-square island sets) |
| Validation | Malformed-mesh reports (indices, NaNs, non-manifold edges, isolated vertices) and atlas reports (unplaced islands, UVs outside [0,1]Β², overlaps) |
| Fixtures | Subdivided cubes, UV spheres, closed tori, torus annuli, grid planes, and open cylinders |
| Execution | Deterministic multi-core unfold stage, a CUDA conjugate-gradient solver (120Γ+ on large charts, CPU fallback) with Jacobi, level-scheduled IC(0), multi-color IC(0), aggregation-AMG, and smoothed-aggregation-AMG preconditioning (the latter with an exact sparse coarse solve on the device), plus a GPU multi-restart packing heuristic |
| I/O & tooling | OBJ/STL import, OBJ/STL/SVG export, the researchuv CLI, a catalog API with host dispatch, a browser atlas editor, and stage benchmarks |
The packer ships with reference data from the mathematical square-packing problem: the side s(n) of the smallest square holding n unit squares. Curated certified bounds (register T-xxx ids from the squares project, data under CC BY 4.0; classical proven values; the 2026 Lean 4 optimality proof for s(11)) live in researchuv-pack's sqbounds module and power three things:
- Optimality-gap benchmarks (
researchuv-pack'ssquares_gaptests and thesquaresbench): packnunit-square islands at a binary-searched uniform scale and compare the achieved container side against the proven floor β a soundness tripwire (an achieved side below a certified bound means an overlap escaped validation) and an honest quality dial. - A quality report on every pack result (
PackResult::quality): atlas utilization, and for equal-square island sets the certified utilization ceilingn / s(n)Β²plus the gap to it, surfaced in the atlas report. - Replayable packing certificates (
researchuv-pack'scertmodule): per-island transforms plus a parameter digest, JSON-serializable, replayed through the exact polygon validator β with mutant tests that deliberately corrupt certificates to prove the validator is not vacuous (the squares project's verification-rung discipline).
flowchart LR
A[Triangle mesh] --> B[Weld vertices]
B --> C[Segment charts]
C --> D[Unfold UVs]
D --> E[Measure and normalize]
E --> F[Pack islands]
F --> G[UV coordinates + diagnostics]
PipelineOptions exposes welding tolerance, the sharp-edge angle, the seam-cut policy, unfolding settings, packer selection (Shelf or Islands with the full PackParams surface), padding, and retry limits. PipelineResult returns the welded mesh, chart results with distortion metrics, placement status, scale, final islands, the island packer's full result contract, and validation reports β so callers can inspect intermediate results as well as the packed output.
Important
Inspect placement status, distortion, and the validation reports before using an atlas. Strongly curved charts can fold under the rectangle-pinned least-squares solve; the validator reports winding-inconsistent triangles and the --recut / SplitCut option re-cuts folding charts automatically.
| Crate | Role | Status |
|---|---|---|
researchuv-math |
Vectors, matrices, geometry factors, and bounding boxes | Implemented |
researchuv-core |
Mesh topology, values, tasks, configuration, undo/redo, and OBJ/STL/SVG I/O | Implemented |
researchuv-unwrap |
End-to-end unfolding, seam cutting, validation, and packing | Implemented |
researchuv-pack |
UVPackmaster-4.1.2-grade CPU island packing | Implemented |
researchuv-api |
Structured API catalog | Implemented |
researchuv-link |
Host integration (catalog dispatch + wire framing) | Implemented |
researchuv-cli |
Command-line entry point | Implemented |
researchuv-gpu |
CUDA conjugate-gradient solver (runtime-loaded driver API) | Implemented |
researchuv-editor |
Browser-served atlas editor | Implemented |
The current dependency graph contains only workspace crates. Numerical kernels and geometry utilities use the Rust standard library.
# Build and run the unit and integration suites.
cargo test --workspace --locked --release
# Exercise the mesh pipeline regression fixtures.
cargo test --locked --release -p researchuv-unwrap --test parity
# Time the pipeline stages over the fixture set (incl. large grids/cylinders
# and a CUDA-solve column when a device is present).
cargo bench --locked -p researchuv-unwrap
# Measure the island packer against certified square-packing bounds: the
# achieved container side for n unit-square islands vs. the proven floors
# and best-known packings of the equal-square problem (exact planner, tilt
# candidates, and the GPU raster path when a device is present).
cargo bench --locked -p researchuv-pack
# Generate local API documentation.
cargo doc --workspace --no-deps --openRelease-mode tests make the larger sphere and torus fixtures practical to run. The suite checks numerical primitives, topology, serialization, task behavior, packing constraints, I/O round-trips, the CLI end to end, and the four original mesh fixtures. These tests establish regression behavior for the included cases; they are not a benchmark of arbitrary production meshes.
- Connect the configurable island packer to the unfolding pipeline.
- Add mesh import/export and a working CLI.
- Implement the API catalog and host integration layer.
- Improve seam selection and distortion on closed surfaces.
- Expand validation for malformed meshes and unsuccessful packing.
- Add performance benchmarks and larger mesh fixtures.
- GPU execution (CUDA conjugate-gradient unfold solve) and an interactive atlas editor (browser-served).
- Distortion-driven chart splitting (folding charts re-cut automatically).
- Preconditioned GPU solver (Jacobi and level-scheduled IC0) and a GPU multi-restart packing heuristic.
- GPU-friendly orderings to level IC(0) on elongated charts (multi-color) and an AMG-class preconditioner (pairwise aggregation, Galerkin, V(1,1)).
- Free-space rasterizer packer (dilate/merge GPU pipeline).
- Smoothed aggregation (CSR transfer operators, size-4 aggregates) and sparse coarse-solve pipelines (exact nested-dissection Cholesky applied on the device).
- Certified packing-quality ground truth: equal-square optimality-gap benchmarks and quality-gap reporting against proven
s(n)bounds (the squares project, CC BY 4.0), near-square tilt rotation candidates, and replayable packing certificates with mutant checks.
Keep changes focused and include a small reproducible mesh when reporting a geometry problem. For solver and packing changes, describe the expected result, relevant tolerances, and any effect on determinism. Run the workspace tests before opening a pull request, and use Conventional Commits for commit subjects and PR titles.
Licensed under the Apache License 2.0.