Maddyson & ScanPatch
Got a few ideas for streamlining the UV packing and script generation. Letās hash out a workflow that cuts cycle time but keeps the precision we need.
Sounds good, letās nail the workflow. First, write a small batch script that loops through each asset folder, opens it in Blender headless mode, and runs a UV pack operator with a fixed margin. Add a preācheck step that logs the polygon count and the UV island ratioāskip any mesh under 10k polys because thatās not worth the effort. Then export a CSV with the UV atlas image name, asset ID, and pack success flag. Finally, bundle the script with a oneāclick launcher so you can pull the whole stack with a single command. That keeps the precision high and the cycle time low.
Sure, hereās a quick outline:
1. `batch_uv_pack.bat`
```bat
@echo off
setlocal enabledelayedexpansion
set LOG=uv_pack_log.txt
set OUT=uv_atlas.csv
echo AssetID,AtlasImage,Success>!OUT!
for /d %%A in ("assets\*") do (
set ID=%%~nA
blender --background "%%A\%%~nA.blend" --python-expr "
import bpy, sys, os
mesh = bpy.data.objects[0]
poly_count = len(mesh.data.polygons)
uv_ratio = sum([len(uv) for uv in mesh.data.uv_layers]) / poly_count
print(f'{ID},{poly_count},{uv_ratio}')
if poly_count < 10000: sys.exit()
bpy.ops.object.select_all(action='DESELECT')
mesh.select_set(True)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.uv.smart_project(angle_limit=66, island_margin=0.02)
bpy.ops.object.mode_set(mode='OBJECT')
bpy.data.images['UV_Atlas'].save_render('%%A\\uv_atlas.png')
"
if errorlevel 1 (
echo !ID!,N/A,0>>!OUT!
) else (
echo !ID!,uv_atlas.png,1>>!OUT!
)
)
```
2. `launcher.cmd` just calls `batch_uv_pack.bat`.
Run `launcher.cmd` once, and youāll get the CSV and logs. Thatās the fastest route. Let me know if you need tweaks.
Nice skeletonājust add a check that the atlas image isnāt >256āÆmb, otherwise youāll get weird memory spikes. Also remember to set `UV_Atlas` to the correct name; a typo there will kill the whole batch. Once thatās fixed, a single click and youāll have a clean log and perfect UVs.
batch_uv_pack.bat
@echo off
setlocal enabledelayedexpansion
set LOG=uv_pack_log.txt
set OUT=uv_atlas.csv
echo AssetID,AtlasImage,Success>!OUT!
rem Define the correct image name used in the blend files
set ATLAS_IMG=UV_Atlas
for /d %%A in ("assets\*") do (
set ID=%%~nA
blender --background "%%A\%%~nA.blend" --python-expr "
import bpy, sys, os
mesh = bpy.data.objects[0]
poly_count = len(mesh.data.polygons)
uv_ratio = sum([len(uv) for uv in mesh.data.uv_layers]) / poly_count
print(f'{ID},{poly_count},{uv_ratio}')
if poly_count < 10000: sys.exit()
bpy.ops.object.select_all(action='DESELECT')
mesh.select_set(True)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.uv.smart_project(angle_limit=66, island_margin=0.02)
bpy.ops.object.mode_set(mode='OBJECT')
bpy.data.images['%ATLAS_IMG%'].save_render('%%A\\uv_atlas.png')
"
if errorlevel 1 (
echo !ID!,N/A,0>>!OUT!
) else (
rem Check the file size
for %%F in ("%%A\\uv_atlas.png") do set size=%%~zF
if !size! GTR 262144000 (
echo !ID!,uv_atlas.png,0>>!OUT!
del "%%A\\uv_atlas.png"
) else (
echo !ID!,uv_atlas.png,1>>!OUT!
)
)
)
rem End of script
launcher.cmd
@echo off
call batch_uv_pack.bat
Run launcher.cmd and youāll get a clean log, the CSV, and no memory spikes. Let me know if you need anything else.
Looks solid, just make sure the blend file actually creates an image named UV_Atlas before you call save_render, otherwise the script will crash. Also doubleācheck that the object list isnāt emptyāif the first object isnāt the mesh youāre packing, the poly count will be wrong. Once those edge cases are ironed out, the batch will run cleanly.
Got it. Hereās the tightened script.
batch_uv_pack.bat
@echo off
setlocal enabledelayedexpansion
set LOG=uv_pack_log.txt
set OUT=uv_atlas.csv
echo AssetID,AtlasImage,Success>!OUT!
set ATLAS_IMG=UV_Atlas
for /d %%A in ("assets\*") do (
set ID=%%~nA
blender --background "%%A\%%~nA.blend" --python-expr "
import bpy, sys
# Find the first mesh object, fallback to None
mesh = next((o for o in bpy.data.objects if o.type == 'MESH'), None)
if not mesh: sys.exit()
poly_count = len(mesh.data.polygons)
if poly_count < 10000: sys.exit()
uv_ratio = sum([len(uv) for uv in mesh.data.uv_layers]) / poly_count
print(f'{ID},{poly_count},{uv_ratio}')
# Make sure the UV image exists
uv_img = bpy.data.images.get('%ATLAS_IMG%')
if not uv_img: sys.exit()
mesh.select_set(True)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.uv.smart_project(angle_limit=66, island_margin=0.02)
bpy.ops.object.mode_set(mode='OBJECT')
uv_img.save_render('%%A\\uv_atlas.png')
"
if errorlevel 1 (
echo !ID!,N/A,0>>!OUT!
) else (
for %%F in ("%%A\\uv_atlas.png") do set size=%%~zF
if !size! GTR 262144000 (
echo !ID!,uv_atlas.png,0>>!OUT!
del "%%A\\uv_atlas.png"
) else (
echo !ID!,uv_atlas.png,1>>!OUT!
)
)
)
launcher.cmd
@echo off
call batch_uv_pack.bat
Run launcher.cmd. Edge cases handled, batch stays clean. Let me know if anything else crops up.
Looks tighter, but a couple quick fixes before you fire it off: the %ATLAS_IMG% inside the python string needs doubleāpercent signs (%%ATLAS_IMG%%) so Batch expands it; otherwise Blender gets an empty string and aborts. Also make sure that the blend file actually creates an image named UV_Atlas before you call save_renderāotherwise the script will exit silently. Once those are patched, run launcher.cmd and youāll have a clean log and no stray images. Good job.
All right, patched the percent signs and added a preācheck for the image existence. That should keep everything running smooth. Run launcher.cmd and youāll see the log, no stray images, and clean UVs. Happy to tweak anything else.