# vim:ts=4:et from mathutils import Vector, Quaternion from math import pi from .script import Script class Entity: def __init__(self): self.d = {} self.b = [] pass texdefs=[] class Texinfo: def __init__(self, name, s_vec, t_vec, s_offs, t_offs, rotate, scale): self.name = name norm = s_vec.cross(t_vec) q = Quaternion(norm, rotate * pi / 180) self.vecs = [None] * 2 self.vecs[0] = (q * s_vec / scale[0], s_offs) self.vecs[1] = (q * t_vec / scale[1], t_offs) def __cmp__(self, other): return self.name == other.name and self.vecs == other.vecs @classmethod def unique(cls, script, plane): name = script.getToken() script.getToken() if script.token == "[": hldef = True s_vec = Vector(parse_vector(script)) s_offs = float(script.getToken()) if script.getToken() != "]": map_error(script, "Missing ]") if script.getToken() != "[": map_error(script, "Missing [") t_vec = Vector(parse_vector(script)) t_offs = float(script.getToken()) if script.getToken() != "]": map_error(script, "Missing ]") else: hldef = False s_vec, t_vec = texture_axis_from_plane(plane) s_offs = float(script.token) t_offs = float(script.getToken()) rotate = float(script.getToken()) scale = [0, 0] scale[0] = float(script.getToken()) scale[1] = float(script.getToken()) tx = cls(name, s_vec, t_vec, s_offs, t_offs, rotate, scale) for t in texdefs: if t == tx: return t return tx baseaxis = ( (Vector((0,0, 1)), (Vector((1,0,0)), Vector((0,-1,0)))), #floor (Vector((0,0,-1)), (Vector((1,0,0)), Vector((0,-1,0)))), #ceiling (Vector(( 1,0,0)), (Vector((0,1,0)), Vector((0,0,-1)))), #west wall (Vector((-1,0,0)), (Vector((0,1,0)), Vector((0,0,-1)))), #east wall (Vector((0, 1,0)), (Vector((1,0,0)), Vector((0,0,-1)))), #south wall (Vector((0,-1,0)), (Vector((1,0,0)), Vector((0,0,-1)))) #north wall ) def texture_axis_from_plane(plane): best = 0 bestaxis = 0 for i in range(6): dot = plane[0].dot(baseaxis[i][0]) if dot > best: best = dot bestaxis = i return baseaxis[bestaxis][1] def clip_poly(poly, plane, keepon): new_poly = [] last_dist = poly[-1].dot(plane[0]) - plane[1] last_point = poly[-1] for point in poly: dist = point.dot(plane[0]) - plane[1] if dist * last_dist < -1e-6: #crossed the plane frac = last_dist / (last_dist - dist) new_poly.append(last_point + frac * (point - last_point)) if dist < -1e-6 or (dist < 1e-6 and keepon): new_poly.append(point) last_point = point last_dist = dist return new_poly def clip_plane(plane, clip_planes): s, t = texture_axis_from_plane(plane) t = plane[0].cross(s) t.normalize() s = t.cross(plane[0]) s *= 1e4 t *= 1e4 o = plane[0] * plane[1] poly = [o + s + t, o + s - t, o - s - t, o - s + t] #CW for p in clip_planes: poly = clip_poly(poly, p, True) return poly EPSILON = 0.5**5 # 1/32 plenty for quake maps def rnd(x): return int(x / EPSILON) * EPSILON def convert_planes(planes): verts = [] faces = [] for i in range(len(planes)): poly = clip_plane(planes[i], planes[:i] + planes[i + 1:]) face = [] for v in poly: v = Vector((rnd(v.x), rnd(v.y), rnd(v.z))) ind = len(verts) for i in range(len(verts)): d = verts[i] - v if d.dot(d) < 1e-6: ind = i break if ind == len(verts): verts.append(v) face.append(ind) faces.append(face) return verts, faces def parse_vector(script): v = (float(script.getToken()), float(script.getToken()), float(script.getToken())) return v def parse_verts(script): if script.token != ":": map_error(script, "Missing :") script.getToken() numverts = int(script.token) verts = [] for i in range(numverts): script.tokenAvailable(True) verts.append(parse_vector(script)) return verts def parse_brush(script, mapent): verts = [] faces = [] planes = [] texdefs = [] planepts = [None] * 3 if script.getToken(True) != "(": verts = parse_verts(script) else: script.ungetToken() while True: if script.getToken(True) in [None, "}"]: break if verts: n_v = int(script.token) face = [None] * n_v if script.getToken() != "(": map_error(script, "Missing (") for i in range(n_v): script.getToken() face[i] = int(script.token) if i < 3: planepts[i] = Vector(verts[face[i]]) if script.getToken() != ")": map_error(script, "Missing )") faces.append(face) else: for i in range(3): if i != 0: script.getToken(True) if script.token != "(": map_error(script, "Missing (") planepts[i] = Vector(parse_vector(script)) script.getToken() if script.token != ")": map_error(script, "Missing )") t1 = planepts[0] - planepts[1] t2 = planepts[2] - planepts[1] norm = t1.cross(t2) norm.normalize() plane = (norm, planepts[1].dot(norm)) planes.append(plane) tx = Texinfo.unique(script, plane) texdefs.append(tx) detail = False while script.tokenAvailable(): script.getToken() if script.token == "detail": detail = True else: map_error(script, "invalid flag") if not verts: verts, faces = convert_planes(planes) mapent.b.append((verts,faces,texdefs)) def parse_epair(script, mapent): key = script.token script.getToken() value = script.token mapent.d[key] = value def parse_entity(script): if script.getToken(True) == None: return False if script.token != "{": map_error(script, "Missing {") mapent = Entity() while True: if script.getToken(True) == None: map_error(script, "EOF without closing brace") if script.token == "}": break if script.token == "{": parse_brush(script, mapent) else: parse_epair(script, mapent) return mapent class MapError(Exception): def __init__(self, fname, line, message): Exception.__init__(self, "%s:%d: %s" % (fname, line, message)) self.line = line def map_error(self, msg): raise MapError(self.filename, self.line, msg) def parse_map(filename): text = open(filename, "rt").read() script = Script(filename, text) script.error = map_error entities = [] global texdefs texdefs = [] while True: ent = parse_entity(script) if not ent: break entities.append(ent) return entities