142 lines
4.0 KiB
Python
142 lines
4.0 KiB
Python
from PIL import Image
|
|
import sys
|
|
from glob import glob
|
|
from generate import renderer
|
|
from dataclasses import dataclass
|
|
from os import path
|
|
|
|
def read_texture_names():
|
|
buf = sys.stdin.read()
|
|
lines = buf.strip().split('\n')
|
|
return lines
|
|
|
|
def glob_and_filter(name):
|
|
filenames = glob(f"*/{name}.tga") + glob(f"*/{name}.jpg")
|
|
assert len(filenames) in {0, 1}, filenames
|
|
filename = filenames[0] if filenames else None
|
|
return filename
|
|
|
|
def image_size(filename):
|
|
if filename is None:
|
|
return (0, 0)
|
|
with Image.open(filename) as im:
|
|
return im.size
|
|
|
|
def npot(v):
|
|
v -= 1
|
|
v |= v >> 1
|
|
v |= v >> 2
|
|
v |= v >> 4
|
|
v |= v >> 8
|
|
v |= v >> 16
|
|
v += 1
|
|
return v
|
|
|
|
@dataclass
|
|
class Size:
|
|
w: int
|
|
h: int
|
|
|
|
@dataclass
|
|
class Texture:
|
|
filename: str
|
|
real_size: Size
|
|
npot_size: Size
|
|
offset: int
|
|
|
|
def mip_size(n, bytes_per_pixel):
|
|
assert bytes_per_pixel == 2 # fixme VQ codebook size
|
|
if n == 0:
|
|
return 0
|
|
size = 0 if bytes_per_pixel < 1 else 6
|
|
while n > 0:
|
|
size += int(n * n * bytes_per_pixel)
|
|
n >>= 1
|
|
return size
|
|
|
|
assert mip_size(256, 2) == 0x2aab0, hex(mip_size(256))
|
|
|
|
def texture_metadata(filenames, mipmapped, bytes_per_pixel):
|
|
acc = 0
|
|
for filename in filenames:
|
|
w, h = image_size(filename)
|
|
nw, nh = npot(w), npot(h)
|
|
|
|
if w > 512:
|
|
name = None
|
|
filename = None
|
|
w, h, nw, nh = 0, 0, 0, 0
|
|
elif filename:
|
|
print(filename)
|
|
yield Texture(
|
|
filename,
|
|
Size(w, h),
|
|
Size(nw, nh),
|
|
acc
|
|
)
|
|
|
|
if mipmapped:
|
|
assert w == h and nw == w, (w, h)
|
|
acc += mip_size(w, bytes_per_pixel)
|
|
assert acc % 32 == 0, (filename, acc)
|
|
else:
|
|
if w != h:
|
|
h = w
|
|
assert w == h, (w, h)
|
|
extra = 0 if bytes_per_pixel == 2 else 256 * 4 * 2 + 16
|
|
size = int(w * h * bytes_per_pixel) + extra
|
|
size = (size + 31) & (~31)
|
|
acc += size
|
|
assert acc % 32 == 0, (filename, acc)
|
|
assert acc <= (0x80_0000 - 0x38_4040), acc
|
|
|
|
def name_to_bin(prefix, suffix, filename):
|
|
if filename is None:
|
|
return None
|
|
else:
|
|
name, ext = path.splitext(filename)
|
|
return f"_binary_" + prefix + name.replace('/', '_').replace('.', '_').replace('-', '_') + suffix
|
|
|
|
def uv_mul(texture):
|
|
u = 0 if texture.npot_size.w == 0 else texture.real_size.w / texture.npot_size.w
|
|
v = 0 if texture.npot_size.h == 0 else texture.real_size.h / texture.npot_size.h
|
|
return u, v
|
|
|
|
def render_texture_metadata(prefix, suffix, texture):
|
|
name = name_to_bin(prefix, suffix, texture.filename)
|
|
u, v = uv_mul(texture)
|
|
assert u == 1.0 or u == 0.0
|
|
start = "0" if name is None else f"&{name}_start"
|
|
size = "0" if name is None else f"&{name}_size"
|
|
yield "{"
|
|
yield f".start = (void *){start},"
|
|
yield f".size = (uint32_t){size},"
|
|
yield f".offset = {texture.offset},"
|
|
yield f".width = {texture.npot_size.w},"
|
|
yield f".height = {texture.npot_size.h},"
|
|
#yield f".u_mul = {u}, // {texture.real_size.w}"
|
|
yield f".v_mul = {v}, // {texture.real_size.h}"
|
|
yield "},"
|
|
|
|
def render_texture_metadatas(names, mipmapped, bytes_per_pixel, prefix, suffix):
|
|
for texture in texture_metadata(names, mipmapped, bytes_per_pixel):
|
|
yield from render_texture_metadata(prefix, suffix, texture)
|
|
|
|
def main():
|
|
global mipmapped
|
|
out_filename = sys.argv[1]
|
|
is_mipmapped = sys.argv[2]
|
|
bytes_per_pixel = float(sys.argv[3])
|
|
assert is_mipmapped in {"mipmapped", "non_mipmapped"}
|
|
mipmapped = is_mipmapped == "mipmapped"
|
|
names = map(glob_and_filter, read_texture_names())
|
|
render, out = renderer()
|
|
prefix = "bsp_"
|
|
suffix = "_data"
|
|
render(render_texture_metadatas(names, mipmapped, bytes_per_pixel, prefix, suffix))
|
|
with open(out_filename, "w") as f:
|
|
f.write(out.getvalue())
|
|
|
|
if __name__ == "__main__":
|
|
main()
|