2011-09-14 10:33:51 +00:00
|
|
|
# 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>
|
|
|
|
|
|
|
|
from struct import unpack
|
|
|
|
from pprint import pprint
|
|
|
|
|
|
|
|
import bpy
|
|
|
|
from bpy_extras.object_utils import object_data_add
|
|
|
|
from mathutils import Vector,Matrix
|
|
|
|
|
2011-09-15 02:29:54 +00:00
|
|
|
from . import quakepal
|
|
|
|
|
2011-09-14 10:33:51 +00:00
|
|
|
class MDL:
|
2011-09-20 03:44:37 +00:00
|
|
|
class Skin:
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
def read(self, mdl, sub=0):
|
|
|
|
self.width, self.height = mdl.skinwidth, mdl.skinheight
|
|
|
|
if sub:
|
|
|
|
self.type = 0
|
|
|
|
self.read_pixels(mdl)
|
|
|
|
return self
|
|
|
|
self.type = mdl.read_int()
|
|
|
|
if self.type:
|
|
|
|
# skin group
|
|
|
|
num = mdl.read_int()
|
|
|
|
self.times = mdl.read_float(num)
|
|
|
|
self.skins = []
|
|
|
|
for i in range(num):
|
|
|
|
self.skins.append(MDL.Skin().read(mdl, 1))
|
|
|
|
num -= 1
|
|
|
|
return self
|
|
|
|
self.read_pixels(mdl)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def read_pixels(self, mdl):
|
|
|
|
size = self.width * self.height
|
2011-09-20 04:33:41 +00:00
|
|
|
self.pixels = mdl.read_bytes(size)
|
2011-09-20 03:44:37 +00:00
|
|
|
|
|
|
|
class STVert:
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
def read(self, mdl):
|
|
|
|
self.onseam = mdl.read_int()
|
|
|
|
self.s, self.t = mdl.read_int(2)
|
|
|
|
return self
|
|
|
|
|
|
|
|
class Tri:
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
def read(self, mdl):
|
|
|
|
self.facesfront = mdl.read_int()
|
|
|
|
self.verts = mdl.read_int(3)
|
|
|
|
return self
|
|
|
|
|
|
|
|
class Frame:
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
def read(self, mdl, numverts, sub=0):
|
|
|
|
if sub:
|
|
|
|
self.type = 0
|
|
|
|
else:
|
|
|
|
self.type = mdl.read_int()
|
|
|
|
if self.type:
|
|
|
|
num = mdl.read_int()
|
|
|
|
self.read_bounds(mdl)
|
|
|
|
self.times = mdl.read_float(num)
|
|
|
|
self.frames = []
|
|
|
|
for i in range(num):
|
|
|
|
self.frames.append(MDL.Frame().read(mdl, numverts, 1))
|
|
|
|
return self
|
|
|
|
self.read_bounds(mdl)
|
|
|
|
self.read_name(mdl)
|
|
|
|
self.read_verts(mdl, numverts)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def read_name(self, mdl):
|
2011-09-17 00:05:07 +00:00
|
|
|
if mdl.version == 6:
|
2011-09-20 03:44:37 +00:00
|
|
|
name = mdl.read_string(16)
|
2011-09-17 00:05:07 +00:00
|
|
|
else:
|
2011-09-17 14:13:21 +00:00
|
|
|
name = ""
|
|
|
|
if "\0" in name:
|
|
|
|
name = name[:name.index("\0")]
|
2011-09-20 03:44:37 +00:00
|
|
|
self.name = name
|
|
|
|
|
|
|
|
def read_bounds(self, mdl):
|
|
|
|
self.mins = mdl.read_byte(4)[:3] #discard normal index
|
|
|
|
self.maxs = mdl.read_byte(4)[:3] #discard normal index
|
|
|
|
|
|
|
|
def read_verts(self, mdl, num):
|
|
|
|
self.verts = []
|
|
|
|
for i in range(num):
|
|
|
|
self.verts.append(MDL.Vert().read(mdl))
|
|
|
|
|
|
|
|
class Vert:
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
def read(self, mdl):
|
|
|
|
self.r = mdl.read_byte(3)
|
|
|
|
self.ni = mdl.read_byte()
|
|
|
|
return self
|
|
|
|
|
|
|
|
def read_byte(self, count=1):
|
|
|
|
size = 1 * count
|
|
|
|
data = self.file.read(size)
|
|
|
|
data = unpack("<%dB" % count, data)
|
|
|
|
if count == 1:
|
|
|
|
return data[0]
|
|
|
|
return data
|
|
|
|
|
|
|
|
def read_int(self, count=1):
|
|
|
|
size = 4 * count
|
|
|
|
data = self.file.read(size)
|
|
|
|
data = unpack("<%di" % count, data)
|
|
|
|
if count == 1:
|
|
|
|
return data[0]
|
|
|
|
return data
|
|
|
|
|
|
|
|
def read_float(self, count=1):
|
|
|
|
size = 4 * count
|
|
|
|
data = self.file.read(size)
|
|
|
|
data = unpack("<%df" % count, data)
|
|
|
|
if count == 1:
|
|
|
|
return data[0]
|
|
|
|
return data
|
|
|
|
|
2011-09-20 04:33:41 +00:00
|
|
|
def read_bytes(self, size):
|
2011-09-20 03:44:37 +00:00
|
|
|
return self.file.read(size)
|
|
|
|
|
2011-09-20 04:33:41 +00:00
|
|
|
def read_string(self, size):
|
|
|
|
data = self.file.read(size)
|
|
|
|
s = ""
|
|
|
|
for c in data:
|
|
|
|
s = s + chr(c)
|
|
|
|
return s
|
|
|
|
|
2011-09-20 03:44:37 +00:00
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
def read(self, filepath):
|
|
|
|
self.file = open(filepath, "rb")
|
|
|
|
self.name = filepath.split('/')[-1]
|
|
|
|
self.name = self.name.split('.')[0]
|
|
|
|
self.ident = self.read_string(4)
|
|
|
|
self.version = self.read_int()
|
2011-09-20 04:33:41 +00:00
|
|
|
if self.ident not in ["IDPO", "MD16"] or self.version not in [3, 6]:
|
2011-09-20 03:44:37 +00:00
|
|
|
return None
|
|
|
|
self.scale = Vector(self.read_float(3))
|
|
|
|
self.scale_origin = Vector(self.read_float(3))
|
|
|
|
self.boundingradius = self.read_float()
|
|
|
|
self.eyeposition = Vector(self.read_float(3))
|
|
|
|
numskins = self.read_int()
|
|
|
|
self.skinwidth, self.skinheight = self.read_int(2)
|
|
|
|
numverts, numtris, numframes = self.read_int(3)
|
|
|
|
self.synctype = self.read_int()
|
|
|
|
if self.version == 6:
|
|
|
|
self.flags = self.read_int()
|
|
|
|
self.size = self.read_float()
|
|
|
|
# read in the skin data
|
|
|
|
self.skins = []
|
|
|
|
for i in range(numskins):
|
|
|
|
self.skins.append(MDL.Skin().read(self))
|
|
|
|
#read in the st verts (uv map)
|
|
|
|
self.stverts = []
|
|
|
|
for i in range(numverts):
|
|
|
|
self.stverts.append (MDL.STVert().read(self))
|
|
|
|
#read in the tris
|
|
|
|
self.tris = []
|
|
|
|
for i in range(numtris):
|
|
|
|
self.tris.append(MDL.Tri().read(self))
|
|
|
|
#read in the frames
|
|
|
|
self.frames = []
|
|
|
|
for i in range(numframes):
|
|
|
|
self.frames.append(MDL.Frame().read(self, numverts))
|
|
|
|
return self
|
2011-09-14 10:33:51 +00:00
|
|
|
|
2011-09-15 04:21:08 +00:00
|
|
|
def make_verts(mdl, framenum, subframenum=0):
|
2011-09-14 10:33:51 +00:00
|
|
|
frame = mdl.frames[framenum]
|
|
|
|
if frame.type:
|
|
|
|
frame = frame.frames[subframenum]
|
|
|
|
verts = []
|
|
|
|
s = mdl.scale
|
|
|
|
o = mdl.scale_origin
|
|
|
|
m = Matrix(((s.x, 0, 0, 0),
|
|
|
|
( 0,s.y, 0, 0),
|
|
|
|
( 0, 0,s.z, 0),
|
|
|
|
(o.x,o.y,o.z, 1)))
|
|
|
|
for v in frame.verts:
|
|
|
|
verts.append(Vector(v.r) * m)
|
2011-09-20 03:44:37 +00:00
|
|
|
#verts.append(m * Vector(v.r))
|
2011-09-15 04:21:08 +00:00
|
|
|
return verts
|
|
|
|
|
|
|
|
def make_faces(mdl):
|
|
|
|
faces = []
|
|
|
|
uvs = []
|
|
|
|
for tri in mdl.tris:
|
2011-09-15 08:23:40 +00:00
|
|
|
tv = tri.verts
|
|
|
|
tv = tv[2], tv[1], tv[0] # flip the normal by reversing the winding
|
|
|
|
faces.append (tv)
|
2011-09-15 04:21:08 +00:00
|
|
|
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))
|
2011-09-15 08:23:40 +00:00
|
|
|
sts = sts[2], sts[1], sts[0] # to match face vert reversal
|
2011-09-15 04:21:08 +00:00
|
|
|
uvs.append(sts)
|
|
|
|
return faces, uvs
|
2011-09-14 10:33:51 +00:00
|
|
|
|
2011-09-15 02:29:54 +00:00
|
|
|
def load_skins(mdl):
|
2011-09-15 10:19:21 +00:00
|
|
|
def load_skin(skin, name):
|
|
|
|
img = bpy.data.images.new(name, mdl.skinwidth, mdl.skinheight)
|
|
|
|
mdl.images.append(img)
|
2011-09-15 02:29:54 +00:00
|
|
|
p = [0.0] * mdl.skinwidth * mdl.skinheight * 4
|
2011-09-15 10:19:21 +00:00
|
|
|
d = skin.pixels
|
2011-09-15 02:29:54 +00:00
|
|
|
for j in range(mdl.skinheight):
|
|
|
|
for k in range(mdl.skinwidth):
|
|
|
|
c = quakepal.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[:]
|
2011-09-20 03:44:37 +00:00
|
|
|
#img.pack(True)
|
2011-09-15 02:29:54 +00:00
|
|
|
|
2011-09-15 10:19:21 +00:00
|
|
|
mdl.images=[]
|
2011-09-20 03:44:37 +00:00
|
|
|
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))
|
2011-09-15 10:19:21 +00:00
|
|
|
else:
|
2011-09-20 03:44:37 +00:00
|
|
|
load_skin (skin, "%s_%d" % (mdl.name, i))
|
2011-09-15 10:19:21 +00:00
|
|
|
|
2011-09-16 11:59:39 +00:00
|
|
|
def setup_skins (mdl, uvs):
|
2011-09-15 02:29:54 +00:00
|
|
|
load_skins (mdl)
|
2011-09-15 13:45:23 +00:00
|
|
|
img = mdl.images[0] # use the first skin for now
|
2011-09-16 11:59:39 +00:00
|
|
|
uvlay = mdl.mesh.uv_textures.new(mdl.name)
|
2011-09-15 04:21:08 +00:00
|
|
|
for i, f in enumerate(uvlay.data):
|
|
|
|
mdl_uv = uvs[i]
|
|
|
|
for j, uv in enumerate(f.uv):
|
|
|
|
uv[0], uv[1] = mdl_uv[j]
|
2011-09-15 13:45:23 +00:00
|
|
|
f.image = img
|
|
|
|
f.use_image = True
|
2011-09-15 10:52:18 +00:00
|
|
|
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
|
2011-09-15 13:45:23 +00:00
|
|
|
tex.image = img
|
2011-09-15 10:52:18 +00:00
|
|
|
mat.texture_slots.add()
|
|
|
|
ts = mat.texture_slots[0]
|
|
|
|
ts.texture = tex
|
|
|
|
ts.use_map_alpha = True
|
|
|
|
ts.texture_coords = 'UV'
|
2011-09-16 11:59:39 +00:00
|
|
|
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:
|
2011-09-17 14:13:21 +00:00
|
|
|
name = frame.name
|
2011-09-18 00:18:49 +00:00
|
|
|
else:
|
|
|
|
frame.name = name
|
2011-09-16 11:59:39 +00:00
|
|
|
frame.key = mdl.obj.shape_key_add(name)
|
2011-09-17 14:13:21 +00:00
|
|
|
frame.key.value = 0.0
|
|
|
|
mdl.keys.append (frame.key)
|
2011-09-16 11:59:39 +00:00
|
|
|
s = mdl.scale
|
|
|
|
o = mdl.scale_origin
|
|
|
|
m = Matrix(((s.x, 0, 0, 0),
|
|
|
|
( 0,s.y, 0, 0),
|
|
|
|
( 0, 0,s.z, 0),
|
|
|
|
(o.x,o.y,o.z, 1)))
|
|
|
|
for i, v in enumerate(frame.verts):
|
|
|
|
frame.key.data[i].co = Vector(v.r) * m
|
2011-09-20 03:44:37 +00:00
|
|
|
#frame.key.data[i].co = m * Vector(v.r)
|
2011-09-15 04:21:08 +00:00
|
|
|
|
2011-09-17 14:13:21 +00:00
|
|
|
def build_shape_keys(mdl):
|
|
|
|
mdl.keys = []
|
|
|
|
mdl.obj.shape_key_add("Basis") # FIXME do I want this?
|
2011-09-20 03:44:37 +00:00
|
|
|
for i, frame in enumerate(mdl.frames):
|
2011-09-17 14:13:21 +00:00
|
|
|
frame = mdl.frames[i]
|
|
|
|
if frame.type:
|
2011-09-20 03:44:37 +00:00
|
|
|
for j in range(len(frame.frames)):
|
2011-09-17 14:13:21 +00:00
|
|
|
make_shape_key(mdl, i, j)
|
|
|
|
else:
|
|
|
|
make_shape_key(mdl, i)
|
|
|
|
|
2011-09-18 00:58:59 +00:00
|
|
|
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]
|
2011-09-17 14:13:21 +00:00
|
|
|
|
|
|
|
def build_actions(mdl):
|
|
|
|
sk = mdl.mesh.shape_keys
|
2011-09-20 03:44:37 +00:00
|
|
|
for frame in mdl.frames:
|
2011-09-17 14:13:21 +00:00
|
|
|
sk.animation_data_create()
|
|
|
|
sk.animation_data.action = bpy.data.actions.new(frame.name)
|
|
|
|
act=sk.animation_data.action
|
2011-09-18 00:58:59 +00:00
|
|
|
data = []
|
2011-09-18 06:37:17 +00:00
|
|
|
other_keys = mdl.keys[:]
|
2011-09-17 14:13:21 +00:00
|
|
|
if frame.type:
|
2011-09-20 03:44:37 +00:00
|
|
|
for j, subframe in enumerate(frame.frames):
|
2011-09-18 00:58:59 +00:00
|
|
|
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))
|
2011-09-20 03:44:37 +00:00
|
|
|
if j < len(frame.frames) - 2:
|
2011-09-18 00:58:59 +00:00
|
|
|
co.append (((j + 2) * 1.0, 0.0))
|
2011-09-20 03:44:37 +00:00
|
|
|
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)]
|
2011-09-18 06:37:17 +00:00
|
|
|
for k in other_keys:
|
|
|
|
data.append((k, co))
|
2011-09-17 14:13:21 +00:00
|
|
|
else:
|
2011-09-18 00:58:59 +00:00
|
|
|
data.append((frame.key, [(1.0, 1.0)]))
|
2011-09-18 06:37:17 +00:00
|
|
|
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))
|
2011-09-18 00:58:59 +00:00
|
|
|
set_keys (act, data)
|
2011-09-17 14:13:21 +00:00
|
|
|
|
2011-09-17 14:41:35 +00:00
|
|
|
def get_base(name):
|
|
|
|
i = 0
|
2011-09-18 00:18:49 +00:00
|
|
|
while i < len(name) and name[i] not in "0123456789":
|
2011-09-17 14:41:35 +00:00
|
|
|
i += 1
|
|
|
|
return name[:i]
|
|
|
|
|
|
|
|
def merge_frames(mdl):
|
|
|
|
i = 0
|
2011-09-20 03:44:37 +00:00
|
|
|
while i < len(mdl.frames):
|
2011-09-17 14:41:35 +00:00
|
|
|
if mdl.frames[i].type:
|
|
|
|
i += 1
|
|
|
|
continue
|
|
|
|
base = get_base(mdl.frames[i].name)
|
|
|
|
j = i + 1
|
2011-09-20 03:44:37 +00:00
|
|
|
while j < len(mdl.frames):
|
2011-09-17 14:41:35 +00:00
|
|
|
if mdl.frames[j].type:
|
|
|
|
break
|
|
|
|
if get_base(mdl.frames[j].name) != base:
|
|
|
|
break
|
|
|
|
j += 1
|
2011-09-20 03:44:37 +00:00
|
|
|
f = MDL.Frame()
|
2011-09-17 14:41:35 +00:00
|
|
|
f.name = base
|
|
|
|
f.type = 1
|
|
|
|
f.frames = mdl.frames[i:j]
|
|
|
|
mdl.frames[i:j] = [f]
|
|
|
|
i += 1
|
|
|
|
|
2011-09-16 08:57:07 +00:00
|
|
|
def import_mdl(operator, context, filepath):
|
|
|
|
bpy.context.user_preferences.edit.use_global_undo = False
|
|
|
|
|
2011-09-16 10:14:31 +00:00
|
|
|
for obj in bpy.context.scene.objects:
|
|
|
|
obj.select = False
|
|
|
|
|
2011-09-20 03:44:37 +00:00
|
|
|
mdl = MDL()
|
|
|
|
if not mdl.read(filepath):
|
2011-09-20 04:34:12 +00:00
|
|
|
operator.report({'ERROR'},
|
|
|
|
"Unrecognized format: %s %d" % (mdl.ident, mdl.version))
|
|
|
|
return {'CANCELLED'}
|
2011-09-16 08:57:07 +00:00
|
|
|
faces, uvs = make_faces (mdl)
|
|
|
|
verts = make_verts (mdl, 0)
|
2011-09-16 11:59:39 +00:00
|
|
|
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)
|
2011-09-20 03:44:37 +00:00
|
|
|
if len(mdl.frames) > 1 or mdl.frames[0].type:
|
2011-09-17 14:13:21 +00:00
|
|
|
build_shape_keys(mdl)
|
2011-09-18 00:18:49 +00:00
|
|
|
merge_frames(mdl)
|
2011-09-17 14:13:21 +00:00
|
|
|
build_actions(mdl)
|
2011-09-16 11:59:39 +00:00
|
|
|
|
|
|
|
mdl.mesh.update()
|
2011-09-16 08:57:07 +00:00
|
|
|
|
2011-09-14 10:33:51 +00:00
|
|
|
bpy.context.user_preferences.edit.use_global_undo = True
|
2011-09-18 09:49:16 +00:00
|
|
|
return {'FINISHED'}
|