English · Français
Catch broken 3D assets before they reach your game. easyuv-lint checks OBJ, glTF, GLB and FBX files for UV overlaps, flipped faces, texel density, polycount, scale mistakes and naming, on your machine or on every pull request.
It is free, open source and powered by the EasyUV engine, so the UV measurements are exact, not estimated on a low-resolution grid.
- Desktop app for Windows: drop a folder, see every problem and the UV layout of each object. Download
EasyUV-Lint-Setup-x64.exefrom the releases. - GitHub Action: annotations on the faulty files, a summary in the job page, and an HTML report with UV previews.
- CLI for Windows, Linux and macOS: one binary, no dependencies.
- Presets for game, mobile, web and film, and a small
easyuv-lint.tomlto tune every rule. - English and French messages and reports.
- Download
EasyUV-Lint-Setup-x64.exefrom the latest release and run it. It installs in your user profile, no admin rights needed. - The installer is not signed yet: if Windows SmartScreen shows "Windows protected your PC", click More info, then Run anyway.
- Drop a folder or 3D files on the window, or click New scan.
The app has a dashboard, the detail of every file with its UV preview, a rules editor (presets, levels, thresholds, export to easyuv-lint.toml), the history of your scans, and the HTML report export. English and French, dark and light themes.
The
easyuv-lint-x86_64-pc-windows-msvc.ziparchive contains the command-line tool, not the app: run it from a terminal.
# .github/workflows/assets.yml
name: 3D assets
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: lburdier/easyuv-lint@v0
with:
paths: Assets/Models
preset: gameInputs:
| Input | Default | Description |
|---|---|---|
paths |
. |
Files or folders to check, separated by spaces or new lines |
preset |
game, mobile, web or film |
|
config |
Path to an easyuv-lint.toml (default: the one at the root, if present) |
|
texture-size |
Texture size in px used for texel density | |
strict |
false |
Fail on warnings too |
lang |
en |
en or fr |
report |
easyuv-lint-report.html |
HTML report path, empty to skip it |
upload-report |
true |
Upload the HTML report as an artifact |
version |
latest |
A release tag, latest, or source |
Outputs: errors, warnings, passed.
Download the binary for your system from the releases, or build it:
cargo install --git https://github.com/lburdier/easyuv-linteasyuv-lint Assets/Models # check a folder
easyuv-lint hero.fbx --preset mobile # one file, mobile budget
easyuv-lint . --html report.html # with the HTML report
easyuv-lint . --format json # for scripts
easyuv-lint . --lang fr # messages in French
easyuv-lint --list-rules # every rule and its defaultExit codes: 0 no errors, 1 errors found (or warnings with --strict), 2 invalid usage.
| Rule | Default | What it catches |
|---|---|---|
uv.missing |
error | The object has no UVs |
uv.overlap |
warning, max 1 % | UV area covered twice (mirrored or stacked islands count too) |
uv.flipped |
warning, max 0 | Faces mirrored relative to their island |
uv.outside |
warning, max 1 % | UVs outside the 0-1 tile (turn off for UDIMs) |
uv.distortion |
warning, max 0.15 | Stretched angles and areas |
uv.density_spread |
warning, max 30 % | Texel density that changes between islands |
uv.density |
off | Texel density outside a px/m range |
uv.score |
warning, min 60 | Overall EasyUV score out of 100 |
mesh.tris |
error, max 100,000 | Triangle budget |
mesh.degenerate |
warning, max 0 | Collapsed or zero-area faces |
mesh.nonmanifold |
warning, max 0 | Edges shared by more than two faces |
mesh.ngons |
off | Faces with more than four sides |
mesh.scale |
warning, 0.01 to 100 m | Unit mistakes (a crate exported in centimetres) |
name.pattern |
off | Object names that break your convention |
| Preset | Triangles | Texture | Also |
|---|---|---|---|
game |
100,000 | 2048 px | The defaults above |
mobile |
15,000 | 1024 px | Texel density 256 to 1024 px/m, n-gons warned |
web |
50,000 | 2048 px | For three.js and other glTF viewers |
film |
no limit | 2048 px | More tolerant on distortion and score |
Put an easyuv-lint.toml at the root of your repository:
preset = "game"
texture_size = 2048
ignore = ["ThirdParty/"]
[rules]
"uv.overlap" = { level = "error", max = 0.5 }
"uv.outside" = "off"
"uv.density" = { min = 512, max = 1024 }
"mesh.tris" = { max = 25000 }
"name.pattern" = { pattern = "^(SM|SK)_[A-Za-z0-9_]+$" }A rule takes a level ("off", "info", "warning", "error") or a table with level, min, max or pattern. Giving parameters to a rule that is off turns it on as a warning. See examples/easyuv-lint.toml.
- OBJ and glTF are read as metres, FBX is converted to metres.
- glTF UVs are flipped to the OBJ convention (V up) so every format is measured the same way.
- Overlaps are measured per object. Mirrored or stacked islands are often intentional: lower
uv.overlaptoinfoor raise itsmaxif your pipeline relies on them.
Issues and pull requests are welcome, in English or French. Good first contributions: new presets (Unreal, Unity, Godot), new rules, more formats, translations.
cargo test --release
cargo run --release -- examples/assets --html report.htmlGPL-3.0-or-later, like the EasyUV engine it is built on.
Made by Lucas Burdier.

