#!/usr/bin/env python3 """ Rewrite glTF materials that hide their albedo in the emissive slot. Anime models are very often exported "unlit": `KHR_materials_unlit`, a BLACK `baseColorFactor`, and the actual texture wired to `emissiveTexture`. Renderers that honour the unlit extension are supposed to use base colour and ignore emission — so Blender reads black, never references the images at all, and imports the model with `bpy.data.images` empty. The textures are not lost on export; they are never loaded. Taila goes through the whole pipeline and comes out a silhouette. Patching Blender's node graph afterwards cannot fix this, because by then there is nothing to patch — so the file is normalised BEFORE it is imported: emissive becomes base colour, and the unlit flag is dropped. The game shades these characters with its own toon material off ALBEDO anyway. Pure stdlib, so it runs inside Blender's Python or out of it. """ import json import os import struct _MAGIC = 0x46546C67 _JSON = 0x4E4F534A _BIN = 0x004E4942 def _read_glb(path): with open(path, "rb") as f: magic, version, _total = struct.unpack(" 3 else 1.0 pbr["baseColorFactor"] = [emissive_factor[0], emissive_factor[1], emissive_factor[2], alpha] mat["emissiveFactor"] = [0.0, 0.0, 0.0] # Unlit would tell the importer to ignore everything but base colour; # the game lights these with its own toon shader. ext = mat.get("extensions", {}) ext.pop("KHR_materials_unlit", None) if ext: mat["extensions"] = ext else: mat.pop("extensions", None) changed += 1 if changed: used = doc.get("extensionsUsed", []) still = any("KHR_materials_unlit" in m.get("extensions", {}) for m in doc.get("materials", [])) if not still and "KHR_materials_unlit" in used: used.remove("KHR_materials_unlit") if used: doc["extensionsUsed"] = used else: doc.pop("extensionsUsed", None) _write_glb(out_path, version, doc, chunks) return changed def prepare(in_path, work_dir): """Return a path safe to import: the original, or a normalised copy.""" if os.path.splitext(in_path)[1].lower() != ".glb": return in_path candidate = os.path.join( work_dir, os.path.splitext(os.path.basename(in_path))[0] + ".albedo.glb") try: changed = normalize_unlit(in_path, candidate) except (OSError, ValueError, KeyError, IndexError) as e: print(f"WARNING: could not normalise materials in '{in_path}' ({e})") return in_path if changed: print(f"Moved emissive albedo into base colour on {changed} materials " "(model was exported unlit)") return candidate return in_path if __name__ == "__main__": import sys if len(sys.argv) < 3: print("Usage: python tools/gltf_fix.py ") sys.exit(1) n = normalize_unlit(sys.argv[1], sys.argv[2]) print(f"{n} materials rewritten")