mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-23 21:02:50 +00:00
4fc9316320
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.
166 lines
5.4 KiB
Python
166 lines
5.4 KiB
Python
# 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 #####
|
|
|
|
# <pep8 compliant>
|
|
|
|
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'}
|