Files
1960-utils/Texturing/ConvertOGLDX/convertogldx.py
2025-10-13 21:58:44 +02:00

166 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# normalmap_to_directx_filtered.py
# Drag & drop image files or folders onto the EXE.
# Default: FORCE convert to DirectX (Y) by flipping green.
# Suffix filter: only process files whose *basename* ends with one of: _n, _nrm, _normal (case-insensitive).
# Flags:
# --detect -> flip only if image looks OpenGL (+Y)
# --all -> ignore suffix filter; process all supported images
# --suffixes "a,b,c" -> comma-separated list of suffixes (without extensions)
import sys, os
from PIL import Image
import numpy as np
SUPPORTED_EXTS = {".png", ".tga", ".jpg", ".jpeg", ".tif", ".tiff", ".bmp"}
OUT_SUBFOLDER = "DirectX_Converted"
DEFAULT_SUFFIXES = ["_n", "_nrm", "_normal"] # case-insensitive
def is_image(p):
return os.path.splitext(p)[1].lower() in SUPPORTED_EXTS
def iter_inputs(paths):
for p in paths:
if os.path.isdir(p):
for root, _, files in os.walk(p):
for f in files:
fp = os.path.join(root, f)
if is_image(fp):
yield fp
else:
if is_image(p):
yield p
def has_normal_suffix(path, suffixes):
stem = os.path.splitext(os.path.basename(path))[0].lower()
return any(stem.endswith(suf.lower()) for suf in suffixes)
def flip_green(img_rgba):
r, g, b, a = img_rgba.split()
g = g.point(lambda i: 255 - i)
return Image.merge("RGBA", (r, g, b, a))
def analyze_mean_y(img_rgba):
_, g, b, _ = img_rgba.split()
g_np = np.array(g, dtype=np.float32)
b_np = np.array(b, dtype=np.float32)
y = (g_np / 255.0) * 2.0 - 1.0
flat_mask = b_np > 240
y_use = y[~flat_mask] if (~flat_mask).any() else y
return float(y_use.mean())
def ensure_directx(img_rgba, detect_mode: bool):
if not detect_mode:
return flip_green(img_rgba), True, None # forced
mean_y = analyze_mean_y(img_rgba)
if mean_y >= 0.0:
return flip_green(img_rgba), True, mean_y
else:
return img_rgba, False, mean_y
def output_path(src_path):
folder, base = os.path.split(src_path)
out_dir = os.path.join(folder, OUT_SUBFOLDER)
os.makedirs(out_dir, exist_ok=True)
return os.path.join(out_dir, base)
def save_preserving_format(out_img_rgba, src_path, had_alpha):
_, ext = os.path.splitext(src_path)
ext = ext.lower()
if not had_alpha or ext in {".jpg", ".jpeg", ".bmp"}:
out_img = out_img_rgba.convert("RGB")
else:
out_img = out_img_rgba
dst = output_path(src_path)
save_kwargs, fmt = {}, None
if ext in {".jpg", ".jpeg"}:
save_kwargs["quality"] = 95
fmt = "JPEG"
elif ext == ".png":
fmt = "PNG"
elif ext == ".tga":
fmt = "TGA"
elif ext in {".tif", ".tiff"}:
fmt = "TIFF"
elif ext == ".bmp":
fmt = "BMP"
else:
dst = os.path.splitext(dst)[0] + ".png"
fmt = "PNG"
out_img.save(dst, format=fmt, **save_kwargs)
return dst
def process_one(path, detect_mode: bool):
try:
src = Image.open(path)
had_alpha = src.mode in ("LA", "RGBA", "PA")
img = src.convert("RGBA")
out_img, flipped, mean_y = ensure_directx(img, detect_mode)
dst = save_preserving_format(out_img, path, had_alpha)
if detect_mode:
status = "flipped to DirectX (Y)" if flipped else "already DirectX (Y)"
extra = f" meanY={mean_y:+.4f}"
else:
status = "FORCED flip -> DirectX (Y)"
extra = ""
print(f"[OK] {path}\n {status}{extra}\n -> {dst}")
except Exception as e:
print(f"[ERR] {path} :: {e}")
def parse_args(argv):
detect_mode = False
process_all = False
suffixes = DEFAULT_SUFFIXES[:]
paths = []
it = iter(argv)
for a in it:
if a == "--detect":
detect_mode = True
elif a == "--all":
process_all = True
elif a == "--suffixes":
try:
raw = next(it)
suffixes = [s.strip() for s in raw.split(",") if s.strip()]
except StopIteration:
print("[WARN] --suffixes expects a quoted comma-separated list; using defaults.")
else:
paths.append(a)
return detect_mode, process_all, suffixes, paths
def main():
detect_mode, process_all, suffixes, args = parse_args(sys.argv[1:])
if not args:
print("Drag and drop image files or folders onto this EXE.")
print("Options: --detect --all --suffixes \"_n,_nrm,_normal\"")
return # auto-exit
files = list(iter_inputs(args))
if not files:
print("No supported images found.")
return
mode_desc = "DETECT mode (flip only if +Y detected)" if detect_mode else "FORCE mode (flip everything)"
filt_desc = "NO suffix filter (--all)" if process_all else f"Suffix filter: {', '.join(suffixes)}"
print(f"{mode_desc}\n{filt_desc}\nFound {len(files)} file(s) before filtering.\n")
count_total = 0
count_skipped = 0
for p in files:
if not process_all and not has_normal_suffix(p, suffixes):
print(f"[SKIP] {p} (name lacks normal-map suffix)")
count_skipped += 1
continue
count_total += 1
process_one(p, detect_mode)
print(f"\nProcessed: {count_total}, Skipped: {count_skipped}")
if __name__ == "__main__":
main()