# 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 bpy from bpy_extras.object_utils import object_data_add from mathutils import Vector,Matrix from .quakepal import palette from .mdl import MDL def make_verts(mdl, framenum, subframenum=0): frame = mdl.frames[framenum] if frame.type: frame = frame.frames[subframenum] verts = [] s = Vector(mdl.scale) o = Vector(mdl.scale_origin) m = Matrix(((s.x, 0, 0,o.x), ( 0,s.y, 0,o.y), ( 0, 0,s.z,o.z), ( 0, 0, 0, 1))) for v in frame.verts: verts.append(m * Vector(v.r)) return verts def make_faces(mdl): faces = [] uvs = [] for tri in mdl.tris: tv = list(tri.verts) sts = [] for v in tri.verts: stv = mdl.stverts[v] s = stv.s t = stv.t if stv.onseam and not tri.facesfront: s += mdl.skinwidth / 2 # quake textures are top to bottom, but blender images # are bottom to top sts.append((s * 1.0 / mdl.skinwidth, 1 - t * 1.0 / mdl.skinheight)) # blender's and quake's vertex order seem to be opposed tv.reverse() sts.reverse() # annoyingly, blender can't have 0 in the final vertex, so rotate the # face vertices and uvs if not tv[2]: tv = [tv[2]] + tv[:2] sts = [sts[2]] + sts[:2] faces.append (tv) uvs.append(sts) return faces, uvs def load_skins(mdl): def load_skin(skin, name): img = bpy.data.images.new(name, mdl.skinwidth, mdl.skinheight) mdl.images.append(img) p = [0.0] * mdl.skinwidth * mdl.skinheight * 4 d = skin.pixels for j in range(mdl.skinheight): for k in range(mdl.skinwidth): c = palette[d[j * mdl.skinwidth + k]] # quake textures are top to bottom, but blender images # are bottom to top l = ((mdl.skinheight - 1 - j) * mdl.skinwidth + k) * 4 p[l + 0] = c[0] / 255.0 p[l + 1] = c[1] / 255.0 p[l + 2] = c[2] / 255.0 p[l + 3] = 1.0 img.pixels[:] = p[:] if hasattr(img, "pack"): img.pack(True) mdl.images=[] for i, skin in enumerate(mdl.skins): if skin.type: for j, subskin in enumerate(skin.skins): load_skin (subskin, "%s_%d_%d" % (mdl.name, i, j)) else: load_skin (skin, "%s_%d" % (mdl.name, i)) def setup_skins (mdl, uvs): load_skins (mdl) img = mdl.images[0] # use the first skin for now uvlay = mdl.mesh.uv_textures.new(mdl.name) uvloop = mdl.mesh.uv_loop_layers[0] for i, texpoly in enumerate(uvlay.data): poly = mdl.mesh.polygons[i] mdl_uv = uvs[i] texpoly.image = img for j,k in enumerate(poly.loop_indices): uvloop.data[k].uv = mdl_uv[j] mat = bpy.data.materials.new(mdl.name) mat.diffuse_color = (1,1,1) mat.use_raytrace = False tex = bpy.data.textures.new(mdl.name, 'IMAGE') tex.extension = 'CLIP' tex.use_preview_alpha = True tex.image = img mat.texture_slots.add() ts = mat.texture_slots[0] ts.texture = tex ts.use_map_alpha = True ts.texture_coords = 'UV' mdl.mesh.materials.append(mat) def make_shape_key(mdl, framenum, subframenum=0): frame = mdl.frames[framenum] name = "%s_%d" % (mdl.name, framenum) if frame.type: frame = frame.frames[subframenum] name = "%s_%d_%d" % (mdl.name, framenum, subframenum) if frame.name: name = frame.name else: frame.name = name frame.key = mdl.obj.shape_key_add(name) frame.key.value = 0.0 mdl.keys.append (frame.key) s = Vector(mdl.scale) o = Vector(mdl.scale_origin) m = Matrix(((s.x, 0, 0,o.x), ( 0,s.y, 0,o.y), ( 0, 0,s.z,o.z), ( 0, 0, 0, 1))) for i, v in enumerate(frame.verts): frame.key.data[i].co = m * Vector(v.r) def build_shape_keys(mdl): mdl.keys = [] mdl.obj.shape_key_add("Basis") mdl.mesh.shape_keys.name = mdl.name mdl.obj.active_shape_key_index = 0 for i, frame in enumerate(mdl.frames): frame = mdl.frames[i] if frame.type: for j in range(len(frame.frames)): make_shape_key(mdl, i, j) else: make_shape_key(mdl, i) def set_keys(act, data): for d in data: key, co = d dp = """key_blocks["%s"].value""" % key.name fc = act.fcurves.new(data_path = dp) fc.keyframe_points.add(len(co)) for i in range(len(co)): fc.keyframe_points[i].co = co[i] def build_actions(mdl): sk = mdl.mesh.shape_keys ad = sk.animation_data_create() track = ad.nla_tracks.new (); track.name = mdl.name start_frame = 1 for frame in mdl.frames: act = bpy.data.actions.new(frame.name) data = [] other_keys = mdl.keys[:] if frame.type: for j, subframe in enumerate(frame.frames): co = [] if j > 1: co.append ((1.0, 0.0)) if j > 0: co.append ((j * 1.0, 0.0)) co.append (((j + 1) * 1.0, 1.0)) if j < len(frame.frames) - 2: co.append (((j + 2) * 1.0, 0.0)) if j < len(frame.frames) - 1: co.append ((len(frame.frames) * 1.0, 0.0)) data.append((subframe.key, co)) if subframe.key in other_keys: del(other_keys[other_keys.index(subframe.key)]) co = [(1.0, 0.0), (len(frame.frames) * 1.0, 0.0)] for k in other_keys: data.append((k, co)) else: data.append((frame.key, [(1.0, 1.0)])) if frame.key in other_keys: del(other_keys[other_keys.index(frame.key)]) co = [(1.0, 0.0)] for k in other_keys: data.append((k, co)) set_keys (act, data) track.strips.new (act.name, start_frame, act) start_frame += act.frame_range[1] def merge_frames(mdl): def get_base(name): i = 0 while i < len(name) and name[i] not in "0123456789": i += 1 return name[:i] i = 0 while i < len(mdl.frames): if mdl.frames[i].type: i += 1 continue base = get_base(mdl.frames[i].name) j = i + 1 while j < len(mdl.frames): if mdl.frames[j].type: break if get_base(mdl.frames[j].name) != base: break j += 1 f = MDL.Frame() f.name = base f.type = 1 f.frames = mdl.frames[i:j] mdl.frames[i:j] = [f] i += 1 def write_text(mdl): string = "$eyeposition %g %g %g\n" % mdl.eyeposition string += "$flags %d\n" % mdl.flags if mdl.synctype: string += "$sync\n" txt = bpy.data.texts.new(mdl.name) txt.from_string(string) return txt.name def parse_flags(flags): #NOTE these are in QuakeForge priority order; a little different to id. # id has rocket and grenate between tracer2 and tracer3 if flags & MDL.EF_ROCKET: return 'EF_ROCKET' elif flags & MDL.EF_GRENADE: return 'EF_GRENADE' elif flags & MDL.EF_GIB: return 'EF_GIB' elif flags & MDL.EF_ZOMGIB: return 'EF_ZOMGIB' elif flags & MDL.EF_TRACER: return 'EF_TRACER' elif flags & MDL.EF_TRACER2: return 'EF_TRACER2' elif flags & MDL.EF_TRACER3: return 'EF_TRACER3' else: return 'EF_NONE' def set_properties(mdl): mdl.obj.qfmdl.eyeposition = mdl.eyeposition try: mdl.obj.qfmdl.synctype = MDL.SYNCTYPE[mdl.synctype] except IndexError: mdl.obj.qfmdl.synctype = 'ST_SYNC' mdl.obj.qfmdl.rotate = (mdl.flags & MDL.EF_ROTATE) and True or False mdl.obj.qfmdl.effects = parse_flags(mdl.flags) def import_mdl(operator, context, filepath): bpy.context.user_preferences.edit.use_global_undo = False for obj in bpy.context.scene.objects: obj.select = False mdl = MDL() if not mdl.read(filepath): operator.report({'ERROR'}, "Unrecognized format: %s %d" % (mdl.ident, mdl.version)) return {'CANCELLED'} faces, uvs = make_faces (mdl) verts = make_verts (mdl, 0) mdl.mesh = bpy.data.meshes.new(mdl.name) mdl.mesh.from_pydata(verts, [], faces) mdl.obj = bpy.data.objects.new(mdl.name, mdl.mesh) bpy.context.scene.objects.link(mdl.obj) bpy.context.scene.objects.active = mdl.obj mdl.obj.select = True setup_skins (mdl, uvs) if mdl.images and not hasattr(mdl.images[0], "pack"): operator.report({'WARNING'}, "Unable to pack skins. They must be packed by hand." +" Some may have been lost") if len(mdl.frames) > 1 or mdl.frames[0].type: build_shape_keys(mdl) merge_frames(mdl) build_actions(mdl) #operator.report({'INFO'}, # "Extra settings saved in the %s text block." % write_text(mdl)) set_properties(mdl) mdl.mesh.update() bpy.context.user_preferences.edit.use_global_undo = True return {'FINISHED'}