115 lines
4.9 KiB
Python
115 lines
4.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Erzeugt ein Beispiel-OBJ mit komplett neuer Geometrie fuer geom_import.py.
|
|
|
|
geom_demo.py basis.wad demo.obj [--plaza]
|
|
|
|
Baut aus Kachel-Vierecken (je 1x1, Textur = ganze Texturkachel, so wie die Engine es will):
|
|
- (--plaza) eine Bodenplatte y=0 ueber allen Rasterzellen, die im Basislevel belegt sind
|
|
(damit Strats/Startpunkt irgendwo Boden finden)
|
|
- einige Bloecke und eine Rampe in der Mitte des belegten Bereichs
|
|
Texturen: haeufigste Textur auf nach oben zeigenden Faces (Boden) bzw. auf Seitenflaechen (Wand).
|
|
"""
|
|
import argparse
|
|
import collections
|
|
import math
|
|
import os
|
|
import sys
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
import geomlib as G # noqa: E402
|
|
|
|
|
|
class ObjW:
|
|
def __init__(self, texs):
|
|
self.v, self.vt, self.f, self.texs = [], [], [], texs
|
|
|
|
def quad(self, p, tex, mat=0):
|
|
"""p: 4 Punkte, Reihenfolge so dass (p1-p0)x(p2-p0) nach aussen zeigt."""
|
|
U0, U1, V0, V1 = G.tex_uv(self.texs[tex])
|
|
uv = [(U1, V1), (U0, V1), (U0, V0), (U1, V0)]
|
|
base = len(self.v)
|
|
self.v += p
|
|
self.vt += uv
|
|
self.f.append((tex, mat, [base + k for k in range(4)]))
|
|
|
|
def box(self, x0, z0, x1, z1, y0, y1, top, side):
|
|
# Koordinaten: x0<x1, z0>z1 (z negativ = weiter im Raster)
|
|
self.quad([(x0, y1, z0), (x1, y1, z0), (x1, y1, z1), (x0, y1, z1)], top)
|
|
for (ax, az), (bx, bz) in [((x0, z0), (x0, z1)), ((x0, z1), (x1, z1)), ((x1, z1), (x1, z0)), ((x1, z0), (x0, z0))]:
|
|
# Wand von a nach b, aussen = rechts herum
|
|
n = math.ceil(y1 - y0)
|
|
for k in range(n):
|
|
ya, yb = y0 + k * (y1 - y0) / n, y0 + (k + 1) * (y1 - y0) / n
|
|
self.quad([(ax, ya, az), (bx, ya, bz), (bx, yb, bz), (ax, yb, az)][::-1], side)
|
|
|
|
def save(self, path):
|
|
with open(path, 'w') as fo:
|
|
for p in self.v:
|
|
fo.write('v %.4f %.4f %.4f 1 1 1\n' % p)
|
|
for u, v in self.vt:
|
|
fo.write('vt %.6f %.6f\n' % (u, 1 - v))
|
|
for tex, mat, ids in self.f:
|
|
fo.write(f'g demo_mat{mat:02d}\nusemtl tex_{tex}\n')
|
|
fo.write('f ' + ' '.join(f'{i + 1}/{i + 1}' for i in ids) + '\n')
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument('base'); ap.add_argument('out'); ap.add_argument('--plaza', action='store_true')
|
|
a = ap.parse_args()
|
|
ch, wf, trak, mp = G.load_level(a.base)
|
|
pages, texs = G.parse_text(G.wad_get(ch, 'TEXT'))
|
|
up, side = collections.Counter(), collections.Counter()
|
|
for m in trak:
|
|
for f in m.faces:
|
|
t = texs[f['tex']]
|
|
if t['flags'] & 1 or t['page'] < 0:
|
|
continue
|
|
(up if f['plane'][1] > 0.9 else side if abs(f['plane'][1]) < 0.2 else collections.Counter())[f['tex']] += 1
|
|
grass, wall = up.most_common(1)[0][0], side.most_common(1)[0][0]
|
|
o = ObjW(texs)
|
|
cells = sorted(set((math.floor(t[0]), math.floor(-t[2])) for t in mp['A']))
|
|
if a.plaza:
|
|
for cx, cz in cells:
|
|
o.quad([(cx, 0, -cz), (cx + 1, 0, -cz), (cx + 1, 0, -cz - 1), (cx, 0, -cz - 1)], grass)
|
|
mx = sorted(c[0] for c in cells)[len(cells) // 2]; mz = sorted(c[1] for c in cells)[len(cells) // 2]
|
|
if not a.plaza:
|
|
# Stelle suchen, an der das 9x4-Fenster moeglichst flach und voll belegt ist
|
|
top = collections.defaultdict(lambda: -99.0)
|
|
for t in mp['A']:
|
|
if trak[t[4]].faces:
|
|
f, _ = G.tile_xform(*t[:4])
|
|
k = (math.floor(t[0]), math.floor(-t[2]))
|
|
top[k] = max(top[k], max(f(*v[:3])[1] for v in trak[t[4]].verts))
|
|
best = None
|
|
for (cx, cz) in list(top):
|
|
win = [top.get((x, z)) for x in range(cx - 4, cx + 5) for z in range(cz - 1, cz + 3)]
|
|
if None in win:
|
|
continue
|
|
spread = max(win) - min(win)
|
|
if best is None or spread < best[0]:
|
|
best = (spread, cx, cz)
|
|
if best:
|
|
mx, mz = best[1], best[2]
|
|
# Bloecke + Rampe um die Mitte; ohne --plaza auf die vorhandene Geometrie gestellt
|
|
b = 0.0
|
|
if not a.plaza:
|
|
ys = []
|
|
for t in mp['A']:
|
|
if mx - 4 <= t[0] <= mx + 5 and mz - 1 <= -t[2] <= mz + 3 and trak[t[4]].faces:
|
|
f, _ = G.tile_xform(*t[:4])
|
|
ys += [f(*v[:3])[1] for v in trak[t[4]].verts]
|
|
b = float(round(sorted(ys)[len(ys) * 3 // 4] * 4) / 4) if ys else 0.0
|
|
o.box(mx, -mz, mx + 2, -mz - 2, b, b + 1, grass, wall)
|
|
o.box(mx + 3, -mz, mx + 4, -mz - 1, b, b + 3, grass, wall)
|
|
for k in range(4): # Rampe in x-Richtung, 4 Zellen lang, steigt auf 1
|
|
y0, y1 = b + k / 4, b + (k + 1) / 4
|
|
x = mx - 4 + k
|
|
o.quad([(x, y0, -mz), (x + 1, y1, -mz), (x + 1, y1, -mz - 2), (x, y0, -mz - 2)], grass)
|
|
o.save(a.out)
|
|
print(f'{a.out}: {len(o.f)} Vierecke, Boden-Textur {grass}, Wand-Textur {wall}, Mitte Zelle ({mx},{mz})')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|