This commit is contained in:
commit
5b83a72513
1 changed files with 139 additions and 0 deletions
139
pot3d.py
Normal file
139
pot3d.py
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
# hi this is super hacky and only converts a tiny segment of display lists.
|
||||||
|
# it's really just to get a feel for how the formats work.
|
||||||
|
# after extracting/decompressing OoT, run this script on object_tsubo.
|
||||||
|
# the top of the pot is missing, i know. see the comment above.
|
||||||
|
|
||||||
|
import array
|
||||||
|
import io
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
|
||||||
|
name = "pot"
|
||||||
|
tw, th = 32, 64 # hardcoded texture width and height
|
||||||
|
viscale = 256 # inverted scale, this is arbitrary
|
||||||
|
v_base = 0x1838 # offset to first G_VTX command
|
||||||
|
|
||||||
|
with open(sys.argv[1], "rb") as f:
|
||||||
|
data = f.read()
|
||||||
|
|
||||||
|
f = io.BytesIO(data)
|
||||||
|
|
||||||
|
# hardcoded for a rga5a1 texture at the start of the file:
|
||||||
|
pix = array.array('H', f.read(tw * th * 2))
|
||||||
|
pix.byteswap()
|
||||||
|
rgbs = []
|
||||||
|
for p in pix:
|
||||||
|
r = (p & 0xF800) >> 11
|
||||||
|
g = (p & 0x07C0) >> 6
|
||||||
|
b = (p & 0x003E) >> 1
|
||||||
|
a = p & 1
|
||||||
|
# TODO: round color calculations. or not? i don't imagine the N64 bothers.
|
||||||
|
rgb = (r * 255 // 31, g * 255 // 31, b * 255 // 31)
|
||||||
|
rgbs.append(rgb)
|
||||||
|
|
||||||
|
verts = []
|
||||||
|
texes = []
|
||||||
|
norms = [] # unimplemented
|
||||||
|
polys = []
|
||||||
|
|
||||||
|
vi = 0 # vertex index to offset by (incremented after each chunk)
|
||||||
|
|
||||||
|
f.seek(v_base)
|
||||||
|
opcode = ord(f.read(1))
|
||||||
|
|
||||||
|
while opcode == 0x01: # G_VTX
|
||||||
|
counts = f.read(3)
|
||||||
|
numv = ((counts[0] & 0xF) << 4) | ((counts[1] & 0xF0) >> 4)
|
||||||
|
vbidx = counts[2] // 2 - numv
|
||||||
|
vaddr = struct.unpack(">i", f.read(4))[0]
|
||||||
|
|
||||||
|
back = f.tell()
|
||||||
|
f.seek(vaddr & 0xFFFFFF)
|
||||||
|
|
||||||
|
for i in range(numv):
|
||||||
|
if 0:
|
||||||
|
# colored vertices
|
||||||
|
vertex = struct.unpack(">hhhHhhBBBB", f.read(16))
|
||||||
|
x, y, z, w, tx, ty, r, g, b, a = vertex
|
||||||
|
else:
|
||||||
|
# lit vertices
|
||||||
|
vertex = struct.unpack(">hhhHhhbbbB", f.read(16))
|
||||||
|
x, y, z, w, tx, ty, n, p, q, a = vertex
|
||||||
|
|
||||||
|
pos = (x / viscale, y / viscale, z / viscale)
|
||||||
|
# FIXME: texture coordinates are slightly off
|
||||||
|
tpos = ((tx / 32 / tw), 1 - (ty / 32 / th))
|
||||||
|
verts.append(pos)
|
||||||
|
texes.append(tpos)
|
||||||
|
|
||||||
|
f.seek(back)
|
||||||
|
|
||||||
|
while 1:
|
||||||
|
opcode = ord(f.read(1))
|
||||||
|
if opcode not in (6, 5):
|
||||||
|
break
|
||||||
|
if opcode == 5:
|
||||||
|
indices = struct.unpack('>bbbbbbb', f.read(7))
|
||||||
|
a0, a1, a2, _, _, _, _ = indices
|
||||||
|
atri = a0 // 2 + 1 + vi, a1 // 2 + 1 + vi, a2 // 2 + 1 + vi
|
||||||
|
polys.append(atri)
|
||||||
|
elif opcode == 6:
|
||||||
|
indices = struct.unpack('>bbbbbbb', f.read(7))
|
||||||
|
a0, a1, a2, _, b0, b1, b2 = indices
|
||||||
|
# TODO: assert all indices are in range(32)
|
||||||
|
atri = a0 // 2 + 1 + vi, a1 // 2 + 1 + vi, a2 // 2 + 1 + vi
|
||||||
|
btri = b0 // 2 + 1 + vi, b1 // 2 + 1 + vi, b2 // 2 + 1 + vi
|
||||||
|
polys.append(atri)
|
||||||
|
polys.append(btri)
|
||||||
|
|
||||||
|
vi = len(verts)
|
||||||
|
|
||||||
|
# write the model file
|
||||||
|
with open("{}.obj".format(name), "w") as f:
|
||||||
|
fprint = lambda *args, **kwargs: print(*args, file=f, **kwargs)
|
||||||
|
|
||||||
|
fprint("mtllib {}.mtl".format(name))
|
||||||
|
|
||||||
|
fprint("o {}".format(name))
|
||||||
|
|
||||||
|
for vert in verts:
|
||||||
|
fprint("v", *("{:.8f}".format(v) for v in vert))
|
||||||
|
|
||||||
|
for tex in texes:
|
||||||
|
fprint("vt", *("{:.8f}".format(v) for v in tex))
|
||||||
|
|
||||||
|
#fprint("g {}".format(name))
|
||||||
|
fprint("usemtl {}".format(name))
|
||||||
|
fprint("s off")
|
||||||
|
|
||||||
|
for poly in polys:
|
||||||
|
fprint("f", *("{}/{}".format(i, i) for i in poly))
|
||||||
|
|
||||||
|
# write the material file
|
||||||
|
with open("{}.mtl".format(name), "w") as f:
|
||||||
|
fprint = lambda *args, **kwargs: print(*args, file=f, **kwargs)
|
||||||
|
|
||||||
|
fprint("newmtl {}".format(name))
|
||||||
|
fprint("Ns 0.0")
|
||||||
|
# i don't know what any of these do but they all look terrible
|
||||||
|
fprint("Ka 1.0 1.0 1.0")
|
||||||
|
fprint("Kd 0.8 0.8 0.8")
|
||||||
|
fprint("Ks 0.0 0.0 0.0")
|
||||||
|
fprint("Ke 0.0 0.0 0.0")
|
||||||
|
fprint("d 1.0")
|
||||||
|
fprint("illum 0")
|
||||||
|
|
||||||
|
fprint("map_Kd {}.bmp".format(name))
|
||||||
|
#fprint("map_Ka {}.bmp".format(name))
|
||||||
|
|
||||||
|
# write the texture file
|
||||||
|
with open("{}.bmp".format(name), "wb") as f:
|
||||||
|
f.write(b'BM')
|
||||||
|
# format: 32-bit BGRA
|
||||||
|
# everything else: sane default
|
||||||
|
f.write(struct.pack("<ihhi", len(rgbs) * 4 + 14, 0, 0, 14 + 40))
|
||||||
|
f.write(struct.pack("<iiihhiiiiii", 40, tw, th, 1, 32, 0, 0, 0, 0, 0, 0))
|
||||||
|
for rgb in reversed(rgbs):
|
||||||
|
r, g, b = rgb
|
||||||
|
a = 0xFF
|
||||||
|
f.write(struct.pack("<BBBB", b, g, r, a))
|
Loading…
Reference in a new issue