Converts Skyrim body skin textures authored for the 3BA (3BBB Amazing) body so they line up
with the BHUNP body's UV layout. Point it at a folder of .dds textures and it writes a
matching folder of BC7-compressed .dds textures, preserving subfolder structure.
The transfer is done by baking: the 3BA mesh is rendered with the source texture as pure emission, and Cycles projects that onto the BHUNP mesh's UVs. This means the result is correct wherever the two bodies occupy the same space, rather than being a naive UV remap.
| Tool | Purpose | Notes |
|---|---|---|
| Blender | runs the bake | 2.9x or newer; developed against 5.1 |
ImageMagick (magick) |
DDS↔PNG, alpha merge | v7 (the magick command, not convert) |
| texconv | final BC7 compression | from DirectXTex |
All three must be on PATH, or passed explicitly via -Blender, -Texconv, and -BlendFile.
A GPU is strongly recommended. The bake is a single-sample texel transfer, but at 4K texture sizes CPU baking is slow.
.\batch_bake.ps1 -InputDir "C:\path\to\3BA textures" -OutputDir "C:\path\to\output"Every .dds found recursively under -InputDir is converted, and the relative path is mirrored
into -OutputDir. Existing output files are overwritten.
To go the other way — BHUNP → 3BA — add -Reverse:
.\batch_bake.ps1 -InputDir "C:\path\to\BHUNP textures" -OutputDir "C:\path\to\output" -Reverse| Parameter | Default | |
|---|---|---|
-InputDir |
(required) | folder to scan recursively for .dds |
-OutputDir |
(required) | destination; also holds the scratch temp\ folder during the run |
-BlendFile |
.\conversion.blend |
scene holding both bodies and the bake materials |
-Script |
.\Conversion_Script.py |
the bake script Blender runs |
-Blender |
blender |
path to the Blender executable |
-Texconv |
texconv |
path to texconv |
-Reverse |
off | convert BHUNP --> 3BA instead of 3BA → BHUNP |
-DryRun |
off | list each source --> output (flagging overwrites) without converting or creating anything |
Skip the batch script and drive Blender directly. It takes a PNG in and writes the colour and alpha passes as two separate PNGs:
magick input.dds src.png
blender --background --factory-startup .\conversion.blend --python .\Conversion_Script.py -- src.png diff.png alpha.png
magick diff.png ( alpha.png -colorspace gray -alpha off ) -compose CopyOpacity -composite merged.pngAppend --reverse after the three paths for BHUNP → 3BA.
For each texture:
magickconverts the source.ddsto PNG. Blender cannot load every Skyrim DDS variant reliably, so this normalises the input.- Blender bakes twice — once with the source image's Colour output wired to the emission surface, once with its Alpha output. Two passes are needed because the bake path cannot carry an alpha channel through in one go, so the passes land in separate files.
magickrecombines them, using the alpha bake as the merged image's alpha channel.texconvcompresses toBC7_UNORMwith no mipmaps (-m 1) and writes the final.dds.
The scratch temp\ folder inside -OutputDir is deleted when the run finishes.
Conversion_Script.py probes for a working Cycles backend at startup, trying OPTIX → CUDA →
HIP → ONEAPI → METAL and taking the first one that both exists in the Blender build and reports
an actual device. It prints what it picked:
Device: OPTIX available but no devices found, skipping
Device: CUDA available but no devices found, skipping
Device: HIP -> AMD Radeon RX 7900 XT
If nothing usable is found it falls back to CPU and says so. To override the probe, set
BAKE_DEVICE to one of the backend names, or to CPU:
$env:BAKE_DEVICE = "CPU"; .\batch_bake.ps1 -InputDir ... -OutputDir ...Only the chosen backend's GPUs are enabled for the bake; the CPU is not mixed in.
conversion.blend is not a scratch scene — it is the configuration. Conversion_Script.py
builds nothing and instead looks up objects, materials, nodes, and images by hardcoded name, so
renaming anything inside the scene breaks the script with a KeyError. The expected names are
listed in CLAUDE.md.
The two bodies must also stay spatially aligned, since the bake projects from the source surface onto the destination surface by proximity.
KeyError: 'bpy_prop_collection[key]: key "..." not found' -- a datablock in conversion.blend
was renamed or deleted. Compare against the name table in CLAUDE.md.
Output is fully transparent -- the alpha pass baked black. Check that the Source Image node's
second output really is Alpha; the pass index in the script is the socket index.
magick not recognised -- ImageMagick v6 installs as convert, not magick. Install v7.
Bake is extremely slow -- check the Device: line in the output. If it says it fell back to
CPU, the GPU driver isn't visible to Blender.
It doesn't work on my operating system -- I'm working on that, give me a bit. Right now it's dependent on a powershell script but I'm adding bash script functionality with the next tag.
batch_bake.ps1— the driver you wantConversion_Script.py— headless Blender bake scriptconversion.blend— scene with both bodies, materials, and bake targetsBATCH_CONVERSION.ps1— earlier driver, superseded. Hardcodes absolute paths and writes PNG data under a.ddsfilename with no compression step. Kept for reference only.
MIT — see LICENSE. Use it, modify it, ship it, sell it; the only requirement is that the copyright notice and licence text travel with any substantial portion of the code.
One exception: Conversion_Script.py is GPL-2.0-or-later. It runs inside Blender and uses the
bpy API, which the Blender Foundation treats as making a script a derivative work of Blender, so
it carries a GPL-compatible licence to match. Everything else — batch_bake.ps1, the .blend, the
docs — is MIT. Blender, ImageMagick and texconv are only ever invoked as separate processes, so
their licences place no requirements on this repository.
The body meshes inside conversion.blend were provided by their author under the WTFPL. Textures
you run through the pipeline stay under whatever terms their original author set — converting a
texture does not change who owns it, so check the source mod's permissions before redistributing
output.