From 4fc93163209f9dadcc136ca43f2b2d69022d0f22 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Sun, 16 Sep 2012 10:59:47 +0900 Subject: [PATCH] Add very preliminary support for map exporting. There is no error checking, texture alignment or support for non-point lights, but it's a start. Also, it's assumed all brushes are convex and manifold. --- tools/io_qfmap/__init__.py | 5 +- tools/io_qfmap/export_map.py | 166 +++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 tools/io_qfmap/export_map.py diff --git a/tools/io_qfmap/__init__.py b/tools/io_qfmap/__init__.py index 3df110277..7b369e100 100644 --- a/tools/io_qfmap/__init__.py +++ b/tools/io_qfmap/__init__.py @@ -54,7 +54,7 @@ from bpy.app.handlers import persistent from .entityclass import EntityClassDict, EntityClassError from . import entity from . import import_map -#from . import export_map +from . import export_map def ecm_draw(self, context): layout = self.layout @@ -267,8 +267,7 @@ class ExportMap(bpy.types.Operator, ExportHelper): @classmethod def poll(cls, context): - return (context.active_object != None - and type(context.active_object.data) == bpy.types.Mesh) + return True def execute(self, context): keywords = self.as_keywords (ignore=("check_existing", "filter_glob")) diff --git a/tools/io_qfmap/export_map.py b/tools/io_qfmap/export_map.py new file mode 100644 index 000000000..4093af9e9 --- /dev/null +++ b/tools/io_qfmap/export_map.py @@ -0,0 +1,166 @@ +# vim:ts=4:et +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# + +import os +import sys + +import bpy +import bmesh +from mathutils import Vector,Matrix +from math import pi + +def active_uv(mesh): + for uvt in mesh.uv_textures: + if uvt.active: + return uvt + return None + +def make_face(bmface, mesh): + uvtex = active_uv(mesh) + uvfaces = mesh.uv_layers[uvtex.name].data + face = mesh.polygons[bmface.index] + mat = mesh.materials[face.material_index] + uvs = uvfaces[face.loop_start:face.loop_start + face.loop_total] + uvs = list(map(lambda a: a.uv, uvs)) + v = list(face.vertices[-3:]) + v.reverse() + return (mesh.vertices[v[0]].co, mesh.vertices[v[1]].co, + mesh.vertices[v[2]].co, mat.name, 0, 0, 0, 1, 1) + +def make_brushes(obj): + act = bpy.context.scene.objects.active + bpy.context.scene.objects.active = obj + bpy.ops.object.editmode_toggle() + brushmesh = bmesh.from_edit_mesh(obj.data).copy() + bpy.ops.object.editmode_toggle() + bpy.context.scene.objects.active = act + brushes = [] + face_set = set(brushmesh.faces) + while face_set: + face_queue = [face_set.pop()] + brush = [] + while face_queue: + face = face_queue.pop() + brush.append(face) + for edge in face.edges: + for link_face in edge.link_faces: + if link_face in face_set: + face_set.remove(link_face) + face_queue.append(link_face) + brushes.append(brush) + mesh = obj.to_mesh(bpy.context.scene, True, 'PREVIEW') + mesh.transform(obj.matrix_world) + for b in brushes: + for i in range(len(b)): + b[i] = make_face(b[i], mesh) + return brushes + +def export_mesh(obj): + qfe = obj.qfentity + edict = entity_dict(qfe) + if qfe.classname: + edict["classname"] = qfe.classname + ec = bpy.context.scene.qfmap.entity_classes.get(edict["classname"]) + if not ec or ec.size: + edict["origin"] = "{l.x:.0f} {l.y:.0f} {l.z:.0f}".format(l=obj.location) + return edict, [] + return edict, make_brushes(obj) + +def export_lamp(obj): + qfe = obj.qfentity + edict = entity_dict(qfe) + if qfe.classname: + edict["classname"] = qfe.classname + if not edict.get("classname"): + edict["classname"] = "light" + edict["origin"] = "{l.x:.0f} {l.y:.0f} {l.z:.0f}".format(l=obj.location) + light = obj.data + edict["light"] = light.distance + return edict + +def vector_str(vect): + return "{v.x:.0f} {v.y:.0f} {v.z:.0f}".format(v=vect) + +def export_empty(obj): + qfe = obj.qfentity + edict = entity_dict(qfe) + if qfe.classname: + edict["classname"] = qfe.classname + edict["origin"] = vector_str(obj.location) + return edict + +def write_brush(outfile, brush): + outfile.write("{\n") + for f in brush: + outfile.write("( {} ) ( {} ) ( {} ) {} {} {} {} {} {}\n".format( + vector_str(f[0]), vector_str(f[1]), vector_str(f[2]), + f[3], f[4], f[5], f[6], f[7], f[8])) + outfile.write("}\n") + +def write_entity(outfile, edict, brushes): + outfile.write("{\n") + for item in edict.items(): + outfile.write("\"{item[0]}\" \"{item[1]}\"\n".format(item=item)) + for brush in brushes: + write_brush(outfile, brush) + outfile.write("}\n") + +def entity_dict(ent): + edict = {} + for field in ent.fields: + edict[field.name] = field.value + return edict + +def export_map(operator, context, filepath): + bpy.context.user_preferences.edit.use_global_undo = False + + world_objects = [] + entities = [] + for obj in bpy.context.scene.objects: + qfe = obj.qfentity + edict = entity_dict(qfe) + if (obj.type == 'MESH' + and (qfe.classname == 'worldspawn' + or (not qfe.classname and "classname" not in edict))): + world_objects.append(obj) + elif (obj.type in {'MESH', 'LAMP', 'EMPTY'} + and (qfe.classname or ("classname" in edict))): + entities.append(obj) + world_brushes = [] + world_dict = {"classname":"worldspawn"} + for obj in world_objects: + world_dict.update(entity_dict(obj.qfentity)) + world_brushes.extend(make_brushes(obj)) + outfile = open(filepath, "wt") + write_entity(outfile, world_dict, world_brushes) + for obj in entities: + if obj.type == 'MESH': + edict, brushes = export_mesh(obj) + elif obj.type == 'LAMP': + brushes = [] + edict = export_lamp(obj) + elif obj.type == 'EMPTY': + brushes = [] + edict = export_empty(obj) + write_entity(outfile, edict, brushes) + outfile.close() + bpy.context.user_preferences.edit.use_global_undo = True + return {'FINISHED'}