QuakeForge 0.7.0 released.
Moderator: InsideQC Admins
28 posts
• Page 1 of 2 • 1, 2
QuakeForge 0.7.0 released.
QuakeForge 0.7.0 has been released.
Highlights:
Highlights:
- OpenGL alias model shadow fixes.
- MDL import/export addon for blender has been properly packaged.
- GLSL fog fixes.
- Instant console raise/lower.
- noclip physics changes.
- Intermission and level change fixes.
- Automatic quick save backups.
- QuakeC-VM improvements.
- Up to 128 dlights.
Leave others their otherness.
http://quakeforge.net/
http://quakeforge.net/
- taniwha
- Posts: 399
- Joined: Thu Jan 14, 2010 7:11 am
Re: QuakeForge 0.7.0 released.
Now I want to learn blender. And not just for painting wings3d models. The only other option for direct MDL production including animation is MS3D, which has not been updated in a while.
Somewhere there's a Chithon replacement model and tutorial for blender 2.4. Anything newer out there for noobs?
Somewhere there's a Chithon replacement model and tutorial for blender 2.4. Anything newer out there for noobs?
-
qbism - Posts: 1236
- Joined: Thu Nov 04, 2004 5:51 am
Re: QuakeForge 0.7.0 released.
No, because I didn't write one yet. I have finally gotten used to the 2.6x interface though, chances are I could, in the same condescending way I did before.
This also depends on how well this new MDL exporter works (i.e. not being annoying and anal about selecting, transformations/rot/scale of objects). I will test by exporting the angelyss/dark character.
EDIT: exporter crashes on 2.61.
This also depends on how well this new MDL exporter works (i.e. not being annoying and anal about selecting, transformations/rot/scale of objects). I will test by exporting the angelyss/dark character.
EDIT: exporter crashes on 2.61.
i should not be here
- leileilol
- Posts: 2783
- Joined: Fri Oct 15, 2004 3:23 am
Re: QuakeForge 0.7.0 released.
- Code: Select all
"blender": (2, 6, 3),
-
qbism - Posts: 1236
- Joined: Thu Nov 04, 2004 5:51 am
Re: QuakeForge 0.7.0 released.
ARGH
that means i have to install yet another regressing version of Blender for one format
EDIT: yeah, i'd say this script is too immature, especially compared to ajmdl export for 2.4x
- demands pre-triangulation
- no animations (instead it's the current frame)
- produces a very broken mesh similar to the bad md2 exporter

same mesh, old exporter, which does some comfortable things:
- exports what is selected
- triangulates on the fly
- can work from any origin, rotation, scale (no need to apply)
- bakes image textures in the mdl
- exports animations to the current frame

that means i have to install yet another regressing version of Blender for one format
EDIT: yeah, i'd say this script is too immature, especially compared to ajmdl export for 2.4x
- demands pre-triangulation
- no animations (instead it's the current frame)
- produces a very broken mesh similar to the bad md2 exporter

same mesh, old exporter, which does some comfortable things:
- exports what is selected
- triangulates on the fly
- can work from any origin, rotation, scale (no need to apply)
- bakes image textures in the mdl
- exports animations to the current frame

i should not be here
- leileilol
- Posts: 2783
- Joined: Fri Oct 15, 2004 3:23 am
Re: QuakeForge 0.7.0 released.
re blender 2.63: I seem to remember some matrix changes between 2.62- and 2.63 (or was that an earlier version?). Anyway, working with bmesh was definitely an issue (the path to the face data changed).
leileilol: yes, it's immature, but this is the first feedback I've received on it.
On to your feature list:
Anyway, I very much appreciate your feedback.
And in anticipation of some questions:
I hope that helps (well, minus the mangled mesh bug: I'll see what I can do).
leileilol: yes, it's immature, but this is the first feedback I've received on it.
- demanding triangulation: this was actually a deliberate decision as I thought the user might want explicit control over how quads (or, I guess ngons these days) triangulate (what with our previous discussion on tris vs quads, this one surprised me).
- no animations: not true
Even frame groups, multiple skins and skin groups are supported. However, it's scripted. I you either look at write_text in import_mdl.py or import an animated model at look at the generated text file, you'll see the documentation for the format. - producing a broken mesh: that's a bug somewhere, definitely a bug. I'll check it out, but I suspect I might need to get a model off you for testing.
On to your feature list:
- exports what is selected: QF's script exports the currently active mesh. For clarity, just what do you want? All selected meshes into the one object?
- triangulates on the fly: I can certainly do that. The only reason I didn't is mentioned above. Should I make it optional? If so, what should be the default?
- can work from any origin, rotation, scale (no need to apply): I'll have to double check, but I believe that's the case for QF's. Also, depends on just what you mean about rotation: do you want a rotated mesh to be exported in a rotated state? I can see all sorts of possibilities, especially in conjunction with the selection item.
- bakes image textures in the mdl: something on my unwritten TODO list.
- exports animations to the current frame: I'm not sure what you mean by "to the current frame". Step from frame 1 to current, exporting an mdl frame for each blender frame?
Anyway, I very much appreciate your feedback.
And in anticipation of some questions:
- All mdl options are specifiable: see the "QF MDL" panel in the Object properties tab (box icon).
- The above mentioned panel is where you specify the name of the script (blender text object). Unfortunately, you have to type it or cut and paste it: there's currently no way to do a suitable list box thingy (like materials etc) for custom properties.
- Unless you're planning on QF-only models (yeah, right), don't bother with the 16-bit check-box.
I hope that helps (well, minus the mangled mesh bug: I'll see what I can do).
Leave others their otherness.
http://quakeforge.net/
http://quakeforge.net/
- taniwha
- Posts: 399
- Joined: Thu Jan 14, 2010 7:11 am
Re: QuakeForge 0.7.0 released.
leileilol: I've found the ajmdl script. That should help get some good functionality going 
Leave others their otherness.
http://quakeforge.net/
http://quakeforge.net/
- taniwha
- Posts: 399
- Joined: Thu Jan 14, 2010 7:11 am
Re: QuakeForge 0.7.0 released.
There's a newer script than the one in the original package. May or may not be the absolute latest, but posted for posterity:taniwha wrote:leileilol: I've found the ajmdl script. That should help get some good functionality going
- Code: Select all
#!BPY
"""
Name: 'MDL (.mdl)'
Blender: 243
Group: 'Export'
Tooltip: 'Export to QuakeI file format (.mdl)'
"""
__author__ = "Andrew Apted"
__url__ = ("http://openarena.ws")
__version__ = "0.62 2011-03-29"
__bpydoc__ = """\
This script exports a QuakeI file (MDL).
"""
import math
import struct
import Blender
import BPyMesh
#q_shared
MAX_QPATH = 64
MDL_IDENT = "IDPO"
MDL_VERSION = 6
MDL_MAX_SKINS = 64
MDL_MAX_VERTICES = 1024
MDL_MAX_TRIANGLES = 2048
MDL_MAX_FRAMES = 256
MDL_XYZ_SCALE = (1.0 / 64.0)
MDL_BLENDER_SCALE = (1.0 / 1.0)
# strips file type extension
def StripExtension(name):
if name.find('.') != -1:
name = name[:name.find('.')]
return name
#q_math
def VectorLength(v):
return math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2])
def RadiusFromBounds(mins, maxs):
corner = [0, 0, 0]
a = 0
b = 0
for i in range(0, 3):
a = abs(mins[i])
b = abs(maxs[i])
if a > b:
corner[i] = a
else:
corner[i] = b
return VectorLength(corner)
# our own logger class. it works just the same as a normal logger except
# all info messages get show.
class Logger:
def __init__(self, name):
self.has_warnings = False
self.has_errors = False
self.has_critical = False
self.message = ""
self.name = name
self.start = 0
if name not in ( text.getName() for text in Blender.Text.Get() ):
self.outtext = Blender.Text.New(name)
else:
self.outtext = Blender.Text.Get(name)
self.outtext.clear()
self.outtext.write("\n___START___\n\n")
def log(self, type, msg, *args):
self.message = type.ljust(10) + ":" + msg % args
self.outtext.write(self.message+'\n')
print self.message
def info(self, msg, *args):
self.log("info", msg, *args)
def warning(self, msg, *args):
self.log("warning", msg, *args)
self.has_warnings = True
def error(self, msg, *args):
self.log("error", msg, *args)
self.has_errors = True
def critical(self, msg, *args):
self.log("critical", msg, *args)
self.has_errors = True
class BlenderGui:
def __init__(self, log):
text = ["A log has been written to a blender text window.",
"Change this window type to a text window.",
"You will be able to select the file %s." % log.name ]
text+=["Parsed in %i seconds"%(Blender.sys.time() - log.start)]
if log.has_critical:
text += ["There were critical errors!!!!"]
elif log.has_errors:
text += ["There were errors!"]
elif log.has_warnings:
text += ["There were warnings"]
text.reverse()
self.msg = text
Blender.Draw.Register(self.gui, self.event, self.button_event)
def gui(self,):
quitbutton = Blender.Draw.Button("Exit", 1, 0, 0, 100, 20, "Close Window")
y = 35
for line in self.msg:
Blender.BGL.glRasterPos2i(10,y)
Blender.Draw.Text(line)
y+=15
def event(self,evt, val):
if evt == Blender.Draw.ESCKEY:
Blender.Draw.Exit()
return
def button_event(self,evt):
if evt == 1:
Blender.Draw.Exit()
return
log = Logger("mdl_export_log")
#------------------------------------------------------------------------#
class q1ColorCache:
def __init__(self, gamma, fullbright):
self.gamma = gamma
self.fullbright = fullbright
self.cache = {}
self.MakeGammaTab()
if fullbright:
self.color_range = range(0,256)
else:
self.color_range = range(0,224)
self.palette = (
( 0, 0, 0), ( 15, 15, 15), ( 31, 31, 31), ( 47, 47, 47),
( 63, 63, 63), ( 75, 75, 75), ( 91, 91, 91), (107, 107, 107),
(123, 123, 123), (139, 139, 139), (155, 155, 155), (171, 171, 171),
(187, 187, 187), (203, 203, 203), (219, 219, 219), (235, 235, 235),
( 15, 11, 7), ( 23, 15, 11), ( 31, 23, 11), ( 39, 27, 15),
( 47, 35, 19), ( 55, 43, 23), ( 63, 47, 23), ( 75, 55, 27),
( 83, 59, 27), ( 91, 67, 31), ( 99, 75, 31), (107, 83, 31),
(115, 87, 31), (123, 95, 35), (131, 103, 35), (143, 111, 35),
( 11, 11, 15), ( 19, 19, 27), ( 27, 27, 39), ( 39, 39, 51),
( 47, 47, 63), ( 55, 55, 75), ( 63, 63, 87), ( 71, 71, 103),
( 79, 79, 115), ( 91, 91, 127), ( 99, 99, 139), (107, 107, 151),
(115, 115, 163), (123, 123, 175), (131, 131, 187), (139, 139, 203),
( 0, 0, 0), ( 7, 7, 0), ( 11, 11, 0), ( 19, 19, 0),
( 27, 27, 0), ( 35, 35, 0), ( 43, 43, 7), ( 47, 47, 7),
( 55, 55, 7), ( 63, 63, 7), ( 71, 71, 7), ( 75, 75, 11),
( 83, 83, 11), ( 91, 91, 11), ( 99, 99, 11), (107, 107, 15),
( 7, 0, 0), ( 15, 0, 0), ( 23, 0, 0), ( 31, 0, 0),
( 39, 0, 0), ( 47, 0, 0), ( 55, 0, 0), ( 63, 0, 0),
( 71, 0, 0), ( 79, 0, 0), ( 87, 0, 0), ( 95, 0, 0),
(103, 0, 0), (111, 0, 0), (119, 0, 0), (127, 0, 0),
( 19, 19, 0), ( 27, 27, 0), ( 35, 35, 0), ( 47, 43, 0),
( 55, 47, 0), ( 67, 55, 0), ( 75, 59, 7), ( 87, 67, 7),
( 95, 71, 7), (107, 75, 11), (119, 83, 15), (131, 87, 19),
(139, 91, 19), (151, 95, 27), (163, 99, 31), (175, 103, 35),
( 35, 19, 7), ( 47, 23, 11), ( 59, 31, 15), ( 75, 35, 19),
( 87, 43, 23), ( 99, 47, 31), (115, 55, 35), (127, 59, 43),
(143, 67, 51), (159, 79, 51), (175, 99, 47), (191, 119, 47),
(207, 143, 43), (223, 171, 39), (239, 203, 31), (255, 243, 27),
( 11, 7, 0), ( 27, 19, 0), ( 43, 35, 15), ( 55, 43, 19),
( 71, 51, 27), ( 83, 55, 35), ( 99, 63, 43), (111, 71, 51),
(127, 83, 63), (139, 95, 71), (155, 107, 83), (167, 123, 95),
(183, 135, 107), (195, 147, 123), (211, 163, 139), (227, 179, 151),
(171, 139, 163), (159, 127, 151), (147, 115, 135), (139, 103, 123),
(127, 91, 111), (119, 83, 99), (107, 75, 87), ( 95, 63, 75),
( 87, 55, 67), ( 75, 47, 55), ( 67, 39, 47), ( 55, 31, 35),
( 43, 23, 27), ( 35, 19, 19), ( 23, 11, 11), ( 15, 7, 7),
(187, 115, 159), (175, 107, 143), (163, 95, 131), (151, 87, 119),
(139, 79, 107), (127, 75, 95), (115, 67, 83), (107, 59, 75),
( 95, 51, 63), ( 83, 43, 55), ( 71, 35, 43), ( 59, 31, 35),
( 47, 23, 27), ( 35, 19, 19), ( 23, 11, 11), ( 15, 7, 7),
(219, 195, 187), (203, 179, 167), (191, 163, 155), (175, 151, 139),
(163, 135, 123), (151, 123, 111), (135, 111, 95), (123, 99, 83),
(107, 87, 71), ( 95, 75, 59), ( 83, 63, 51), ( 67, 51, 39),
( 55, 43, 31), ( 39, 31, 23), ( 27, 19, 15), ( 15, 11, 7),
(111, 131, 123), (103, 123, 111), ( 95, 115, 103), ( 87, 107, 95),
( 79, 99, 87), ( 71, 91, 79), ( 63, 83, 71), ( 55, 75, 63),
( 47, 67, 55), ( 43, 59, 47), ( 35, 51, 39), ( 31, 43, 31),
( 23, 35, 23), ( 15, 27, 19), ( 11, 19, 11), ( 7, 11, 7),
(255, 243, 27), (239, 223, 23), (219, 203, 19), (203, 183, 15),
(187, 167, 15), (171, 151, 11), (155, 131, 7), (139, 115, 7),
(123, 99, 7), (107, 83, 0), ( 91, 71, 0), ( 75, 55, 0),
( 59, 43, 0), ( 43, 31, 0), ( 27, 15, 0), ( 11, 7, 0),
( 0, 0, 255), ( 11, 11, 239), ( 19, 19, 223), ( 27, 27, 207),
( 35, 35, 191), ( 43, 43, 175), ( 47, 47, 159), ( 47, 47, 143),
( 47, 47, 127), ( 47, 47, 111), ( 47, 47, 95), ( 43, 43, 79),
( 35, 35, 63), ( 27, 27, 47), ( 19, 19, 31), ( 11, 11, 15),
( 43, 0, 0), ( 59, 0, 0), ( 75, 7, 0), ( 95, 7, 0),
(111, 15, 0), (127, 23, 7), (147, 31, 7), (163, 39, 11),
(183, 51, 15), (195, 75, 27), (207, 99, 43), (219, 127, 59),
(227, 151, 79), (231, 171, 95), (239, 191, 119), (247, 211, 139),
(167, 123, 59), (183, 155, 55), (199, 195, 55), (231, 227, 87),
(127, 191, 255), (171, 231, 255), (215, 255, 255), (103, 0, 0),
(139, 0, 0), (179, 0, 0), (215, 0, 0), (255, 0, 0),
(255, 243, 147), (255, 247, 199), (255, 255, 255), (159, 91, 83))
#
def MakeGammaTab(self):
self.gamma_map = {}
for r in range(0,256):
k = r / 255.0
k = k ** (1.0 / self.gamma)
self.gamma_map[r] = int(k * 255.0)
#
def LookupColor(self, r, g, b):
# AJA: find the closest color
# (this might be better done in HSV space, giving more weight
# to the HUE and less weight to the VALUE).
r = self.gamma_map[r]
g = self.gamma_map[g]
b = self.gamma_map[b]
#
best = 0
best_dist = 99999999
#
for i in self.color_range:
dr = self.palette[i][0] - r
dg = self.palette[i][1] - g
db = self.palette[i][2] - b
dist = dr*dr + dg*dg + db*db
if dist == 0: # exact match
return i
if dist < best_dist:
best = i
best_dist = dist
return best
def MapColor(self, r, g, b):
rgb = r*65536 + g*256 + b
if self.cache.has_key(rgb):
return self.cache[rgb]
#
# don't let the cache grow without limit
if len(self.cache) > 8000:
self.cache.clear()
pixel = self.LookupColor(r, g, b)
self.cache[rgb] = pixel
return pixel
class q1Normalizer:
def __init__(self):
self.group_range = range(0,11)
self.x_group = (
(1.0000, 0.0000, 0.0000), (52,52,52,52,143,143,143,143),
(0.9554, 0.2952, 0.0000), (51,51,55,55,141,141,145,145),
(0.9511, 0.1625, 0.2629), (53,63,57,70,142,148,146,151),
(0.8642, 0.4429, 0.2389), (46,61,56,69,19,147,123,150),
(0.8507, 0.5257, 0.0000), (41,41,54,54,18,18,116,116),
(0.8507, 0.0000, 0.5257), (60,67,60,67,144,155,144,155),
(0.8090, 0.3090, 0.5000), (48,62,58,68,16,149,124,152),
(0.7166, 0.6817, 0.1476), (42,43,111,100,20,25,118,117),
(0.6882, 0.5878, 0.4253), (47,76,140,101,21,156,125,161),
(0.6817, 0.1476, 0.7166), (49,65,59,66,15,153,126,154),
(0.5878, 0.4253, 0.6882), (50,75,139,102,17,157,128,160) )
#
self.y_group = (
(0.0000, 1.0000, 0.0000), (32,32,104,104,32,32,104,104),
(0.0000, 0.9554, 0.2952), (33,30,107,103,33,30,107,103),
(0.2629, 0.9511, 0.1625), (36,39,109,105,34,31,122,115),
(0.2389, 0.8642, 0.4429), (35,38,108,97,23,29,121,113),
(0.5257, 0.8507, 0.0000), (44,44,112,112,27,27,119,119),
(0.0000, 0.8507, 0.5257), (6,28,106,90,6,28,106,90),
(0.5000, 0.8090, 0.3090), (37,40,110,98,22,26,120,114),
(0.1476, 0.7166, 0.6817), (8,71,136,92,7,77,130,91),
(0.4253, 0.6882, 0.5878), (45,73,138,99,24,158,131,159),
(0.7166, 0.6817, 0.1476), (42,43,111,100,20,25,118,117),
(0.6882, 0.5878, 0.4253), (47,76,140,101,21,156,125,161) )
#
self.z_group = (
(0.0000, 0.0000, 1.0000), (5,84,5,84,5,84,5,84),
(0.2952, 0.0000, 0.9554), (12,85,12,85,2,82,2,82),
(0.1625, 0.2629, 0.9511), (14,86,134,96,4,83,132,89),
(0.4429, 0.2389, 0.8642), (13,74,133,95,1,81,127,87),
(0.5257, 0.0000, 0.8507), (11,64,11,64,0,80,0,80),
(0.0000, 0.5257, 0.8507), (9,79,137,93,9,79,137,93),
(0.3090, 0.5000, 0.8090), (10,72,135,94,3,78,129,88),
(0.6817, 0.1476, 0.7166), (49,65,59,66,15,153,126,154),
(0.5878, 0.4253, 0.6882), (50,75,139,102,17,157,128,160),
(0.1476, 0.7166, 0.6817), (8,71,136,92,7,77,130,91),
(0.4253, 0.6882, 0.5878), (45,73,138,99,24,158,131,159) )
#
def MapNormal(self, x, y, z):
# AJA: I use the following shortcuts to speed up normal lookup:
#
# Firstly, a preliminary match only uses the first quadrant
# (where all coords are >= 0). Then we use the appropriate
# normal index for the actual quadrant. We can do this because
# the 162 MDL/MD2 normals are not arbitrary but are mirrored in
# every quadrant. The eight numbers in the lists above are the
# indices for each quadrant (+++ ++- +-+ +-- -++ -+- --+ ---).
#
# Secondly we use the axis with the greatest magnitude (of the
# incoming normal) to search an axis-specific group, which means
# we only need to check about 1/3rd of the normals.
#
fx = abs(x)
fy = abs(y)
fz = abs(z)
#
group = self.x_group
if (fy > fx) and (fy > fz):
group = self.y_group
elif (fz > fx) and (fz > fy):
group = self.z_group
#
best = 0
best_dot = -1
#
for i in self.group_range:
dot = group[i*2][0] * fx + group[i*2][1] * fy + group[i*2][2] * fz
#
if dot > best_dot:
best = i
best_dot = dot
#
quadrant = 0
if x < 0:
quadrant += 4
if y < 0:
quadrant += 2
if z < 0:
quadrant += 1
#
return group[best*2+1][quadrant]
col_cache = q1ColorCache(1.0, False)
normalizer = q1Normalizer()
#------------------------------------------------------------------------#
class mdlFrame:
__slots__ = 'name', 'vlist', 'mins', 'maxs'
def __init__(self, name, vlist):
self.name = name
self.vlist = vlist
# compute bbox of this frame
mins = [ +9e9, +9e9, +9e9 ]
maxs = [ -9e9, -9e9, -9e9 ]
for i in range(0, 3):
for V in vlist:
if V[i] < mins[i]: mins[i] = V[i]
if V[i] > maxs[i]: maxs[i] = V[i]
self.mins = mins
self.maxs = maxs
class mdlObject:
def __init__(self):
self.skins = []
self.verts = []
self.tris = []
self.frames = []
#
self.sync_type = 0
self.flags = 0
#
self.skin_w = 32 # temp
self.skin_h = 32
self.avg_tri_size = 20.0
#
self.scale = [1.0] * 3 # temp stuff
self.origin = [0.0] * 3
self.bound_radius = 100.0
self.eye = [0.5, 0.5, 0.5]
#
self.mins = [ +9e9, +9e9, +9e9 ]
self.maxs = [ -9e9, -9e9, -9e9 ]
def AddSkin(self, image):
size = image.getSize()
if len(self.skins) == 0:
self.skin_w = size[0]
self.skin_h = size[1]
## FIXME: elif size[0] != self.skin_w ERROR
self.skins.append(image)
def AddVertex(self, u, v): # returns new vertex index
result = len(self.verts)
# invert vertically
v = 1 - v
# map onto the skin
if u < 0: u = 0
if u > 1: u = 1
if v < 0: v = 0
if v > 1: v = 1
u = int(u * 0.999 * self.skin_w + 0.99)
v = int(v * 0.999 * self.skin_h + 0.99)
on_seam = 0 ## FIXME
self.verts.append( (on_seam, u, v) )
return result
def AddTriangle(self, vlist):
on_front = 0
self.tris.append( (on_front, vlist[0], vlist[2], vlist[1]) )
def AddFrame(self, vlist):
name = "frame_%d" % len(self.frames)
f = mdlFrame(name, vlist)
# update bbox of whole model
for i in range(0, 3):
if f.mins[i] < self.mins[i]: self.mins[i] = f.mins[i]
if f.maxs[i] > self.maxs[i]: self.maxs[i] = f.maxs[i]
self.frames.append(f)
def CalcScaleOrigin(self):
for i in range(0, 3):
size = self.maxs[i] - self.mins[i]
self.scale[i] = size / 253.0
self.origin[i] = self.mins[i] - 1.0 * self.scale[i]
log.info("MDL Scale: (%1.3f, %1.3f, %1.3f)",
self.scale[0], self.scale[1], self.scale[2]);
log.info("MDL Origin: (%1.3f, %1.3f, %1.3f)",
self.origin[0], self.origin[1], self.origin[2]);
def MapCoord(self, x, y, z):
x = int((x - self.origin[0]) / self.scale[0])
y = int((y - self.origin[1]) / self.scale[1])
z = int((z - self.origin[2]) / self.scale[2])
return (x, y, z)
def Write(self, file):
self.CalcScaleOrigin()
#
# Header
#
file.write(struct.pack("<4si", MDL_IDENT, MDL_VERSION))
#
file.write(struct.pack("<3f3f4f",
self.scale[0], self.scale[1], self.scale[2],
self.origin[0], self.origin[1], self.origin[2],
self.bound_radius,
self.eye[0], self.eye[1], self.eye[2]))
#
file.write(struct.pack("<3i",
1, ##!!!!!!!!! FIXME
self.skin_w,
self.skin_h))
#
file.write(struct.pack("<5if",
len(self.verts),
len(self.tris),
len(self.frames),
self.sync_type,
self.flags,
self.avg_tri_size))
#
# Skins
#
if len(self.skins) == 0:
self.WriteDummySkin(file)
else:
for S in self.skins:
self.WriteSkin(file, S)
#
# Vertices (ST coords)
self.WriteVertices(file)
#
# Triangles
self.WriteTriangles(file)
#
# Frames
self.WriteFrames(file)
def WriteSkin(self, file, image):
file.write(struct.pack("<i", 0)) # type = SINGLE
x_range = range(0, self.skin_w)
y_range = reversed(range(0, self.skin_h))
for y in y_range:
for x in x_range:
rgb = image.getPixelI(x, y)
pixel = col_cache.MapColor(rgb[0], rgb[1], rgb[2])
file.write(struct.pack("B", pixel))
def WriteDummySkin(self, file):
file.write(struct.pack("<i", 0)) # type = SINGLE
x_range = range(0, 16)
y_range = range(0, 32)
for y in y_range:
for x in x_range:
pix = 3 + 5 * ((x + int(y/2)) % 2)
file.write(struct.pack("BB", pix, pix))
def WriteVertices(self, file):
for st in self.verts:
file.write(
struct.pack("<3i", st[0], st[1], st[2]))
def WriteTriangles(self, file):
for t in self.tris:
file.write(
struct.pack("<4i", t[0], t[1], t[2], t[3]))
def WriteFrames(self, file):
for f in self.frames:
file.write(struct.pack("<i", 0)) # type = SINGLE
#
mins = self.MapCoord(f.mins[0], f.mins[1], f.mins[2])
maxs = self.MapCoord(f.maxs[0], f.maxs[1], f.maxs[2])
#
file.write(struct.pack("<4B4B",
mins[0], mins[1], mins[2], 0,
maxs[0], maxs[1], maxs[2], 0))
#
file.write(struct.pack("16s", f.name))
#
# all vertices for this frame
for V in f.vlist:
xyz = self.MapCoord(V[0], V[1], V[2])
file.write(struct.pack("<4B",
xyz[0], xyz[1], xyz[2], V[3]))
#------------------------------------------------------------------------#
def Export(fileName):
#log starts here
log.start = Blender.sys.time()
log.info("Quake MDL Export script v0.62")
if (fileName.find('.mdl', -4) <= 0):
fileName += '.mdl'
log.info("Exporting to: %s", fileName)
# create MDL header object (contains everything else)
mdl = mdlObject()
# get the scene
scene = Blender.Scene.GetCurrent()
scene.makeCurrent()
# find the mesh to process
meshOBJ = None
for obj in Blender.Object.GetSelected():
# check if it's a mesh object
if obj.getType() == "Mesh":
meshOBJ = obj
break
if not meshOBJ:
print "Error: Must select a mesh to output as MDL"
Blender.Draw.PupMenu("Selected Object must be a mesh to output as MDL%t|OK")
return
scene.makeCurrent()
total_frames = Blender.Get("curframe")
Blender.Set("curframe", 1)
Blender.Window.Redraw()
# get the object (not just name) and the Mesh, not NMesh
mesh = meshOBJ.getData(False, True)
matrix = meshOBJ.getMatrix('worldspace')
log.info("Materials: %s", mesh.materials)
#if not mesh.materials:
## surf.shaders[0].name = pathName + meshOBJ.name
#else:
## surf.shaders[0].name = pathName + mesh.materials[0].name
# find skin texture
mesh_image = mesh.faces[0].image
if mesh_image == "":
mesh_image = None
if mesh_image:
mdl.AddSkin(mesh_image)
# Process the Mesh....
# because MDL doesnt suppoort faceUVs like blender, we need to duplicate
# any vertex that has multiple uv coords
SeenVerts = {} # maps vertex id + UV coords to the new vertex id
OldToNew = {} # maps old vertex id to a list of new ids after duplicating to account for UV
# process each face in the mesh
for face in mesh.faces:
# this makes a list for each tri in this face. a quad will be [[0,1,2],[0,2,3]]
tris_in_this_face = []
for vi in range(1, len(face)-1):
tris_in_this_face.append([0, vi, vi + 1])
# loop across each tri in the face, then each vertex in the tri
for cur_tri in tris_in_this_face:
tri_verts = []
for vi in cur_tri:
# get the old vertex index and uv coords
old_vert = face.v[vi].index
if mesh.faceUV == True:
uv = tuple(face.uv[vi])
elif mesh.vertexUV:
uv = (face.v[vi].uvco[0], face.v[vi].uvco[1])
else:
uv = (0.0, 0.0) # handle case with no tex coords
if SeenVerts.has_key((old_vert, uv)):
new_vert = SeenVerts[(old_vert, uv)]
else:
# haven't seen this vertex/uv combo before
new_vert = mdl.AddVertex(uv[0], uv[1])
SeenVerts[(old_vert, uv)] = new_vert
# now because we have created a new index,
# we need a way to link it to the index that
# blender returns for NMVert.index
if not OldToNew.has_key(old_vert):
OldToNew[old_vert] = []
OldToNew[old_vert].append(new_vert)
tri_verts.append(new_vert)
mdl.AddTriangle(tri_verts)
# we're done with faces and uv coords
# now vertices are stored as frames :-
# all vertices for frame 1, all vertices for frame 2...., all vertices for frame n
# so we need to iterate across blender's frames, and copy out each vertex
for frame_idx in range(0, total_frames):
# print "Doing frame ", frame_idx, total_frames
Blender.Set("curframe", frame_idx + 1)
Blender.Window.Redraw()
m = BPyMesh.getMeshFromObject(meshOBJ)
m.transform(matrix)
frame_verts = [ (0,0,0,0) ] * len(mdl.verts)
no_face_verts = 0
for V in m.verts:
try:
vlist = OldToNew[V.index]
except:
no_face_verts += 1
continue
norm = normalizer.MapNormal(V.no[0], V.no[1], V.no[2])
vert_info = ( V.co[0], V.co[1], V.co[2], norm )
# apply the position to all the duplicated vertices
for new_vert in vlist:
frame_verts[new_vert] = vert_info
mdl.AddFrame(frame_verts)
if frame_idx == 1 and no_face_verts > 0:
log.warning("Found %d vertices not part of a face",
no_face_verts)
# export!
file = open(fileName, "wb")
mdl.Write(file)
file.close()
log.info("Finished.")
def FileSelectorCallback(fileName):
Export(fileName)
BlenderGui(log)
Blender.Window.FileSelector(FileSelectorCallback, "Export Quake1 MDL", Blender.sys.makename(ext='.mdl'))
#--- editor settings ------------
# vi:ts=4:sw=4:noexpandtab
-
qbism - Posts: 1236
- Joined: Thu Nov 04, 2004 5:51 am
Re: QuakeForge 0.7.0 released.
Ok, I've taken a brief look at ajmdl (ajmdl_export_055.py found on quaddicted). Here are some thoughts.
I need to think more about the idea of exporting multiple selected meshes to the one mdl file. The main issue is what to do about the mdl options as they're currently tied to the object (I could use the active object's settings).
[ninjaed]Ok, I've also looked at the version posted by qbism. The changes between 0.55 and 0.62 are limited to what looks like bug-fixes: inverting uv coords and the image, and changing something in the color cache. And then a few logging tweaks.
- ajmdl processes only the one mesh (but does scan through all selected objects, stopping at the first mesh object).
- Opps, I'd forgotten I hadn't yet implemented vertex normals. However, I doubt this is the source of your breakage.
- For textures, ajmdl doesn't seem to do anything smarter than converting RGB to the quake palette. No multiple skins, no skin groups, and certainly nothing I'd call baking (my idea of baking being to take the uv islands from the various selected images and merging into the one image (would require careful uv layout by the artist, though).
- I now understand what you mean by working with any origin/rotation/scale: ajmdl uses the vertices' world coordinates.
- I now see also what you meant by "to the current frame" ('tis as I thought). Fair enough. I like my scheme, but in the absence of a control script, that makes for reasonable behavior.
I need to think more about the idea of exporting multiple selected meshes to the one mdl file. The main issue is what to do about the mdl options as they're currently tied to the object (I could use the active object's settings).
[ninjaed]Ok, I've also looked at the version posted by qbism. The changes between 0.55 and 0.62 are limited to what looks like bug-fixes: inverting uv coords and the image, and changing something in the color cache. And then a few logging tweaks.
Leave others their otherness.
http://quakeforge.net/
http://quakeforge.net/
- taniwha
- Posts: 399
- Joined: Thu Jan 14, 2010 7:11 am
Re: QuakeForge 0.7.0 released.
Ok, I've done up a quick-and-dirty bugfix release (only the mdl addon). Look for "bugfix" in the QF downloads page.
Changes:
Changes:
- An exception when exporting a model with no image textures has been fixed
- Vertex normals are now exported
- Multiple vertices sharing the one UV coordinate now work as expected (leileilol: I believe this is your bug)
- Quads and n-gons automatically get converted to triangles.
- The model's location, rotation and scale are now non-destructively applied to the mesh (optional, default) before exporting.
- If no frame information is available via the export script, Blender's frames 1 to the current frame (inclusive) will be exported as individual frames.
Leave others their otherness.
http://quakeforge.net/
http://quakeforge.net/
- taniwha
- Posts: 399
- Joined: Thu Jan 14, 2010 7:11 am
Re: QuakeForge 0.7.0 released.
taniwha wrote:Unless you're planning on QF-only models (yeah, right), don't bother with the 16-bit check-box.
Hexen II MDL?
taniwha wrote:I need to think more about the idea of exporting multiple selected meshes to the one mdl file. The main issue is what to do about the mdl options as they're currently tied to the object (I could use the active object's settings).
Noesis pulled it off somehow. Blender 2.4x had a "consolidate images into one" feature that I can't seem to find anymore in 2.6x.
i should not be here
- leileilol
- Posts: 2783
- Joined: Fri Oct 15, 2004 3:23 am
Re: QuakeForge 0.7.0 released.
I asked Campbell about image consolidation and I was told I'd have to write it myself (I may have asked the wrong question, though, and he didn't seem 100% certain). That said, it can't be that hard (though I imagine it will be quite slow written in python).
Did my changes fix most of your issues with the exporter?
I don't know anything about Hexen II MDL
. The MD16 format was (I believe) designed by Serplord (Seth Galbraith).
Did my changes fix most of your issues with the exporter?
I don't know anything about Hexen II MDL
Leave others their otherness.
http://quakeforge.net/
http://quakeforge.net/
- taniwha
- Posts: 399
- Joined: Thu Jan 14, 2010 7:11 am
Re: QuakeForge 0.7.0 released.
I've done multiple meshes in Wings3D and MS3D, but they were all mapped onto the same texture. Guessing that doesn't help for exporting a Q3 model + vwep.leileilol wrote:taniwha wrote:I need to think more about the idea of exporting multiple selected meshes to the one mdl file. The main issue is what to do about the mdl options as they're currently tied to the object (I could use the active object's settings).
Noesis pulled it off somehow. Blender 2.4x had a "consolidate images into one" feature that I can't seem to find anymore in 2.6x.
Regarding export of multiple meshes would CTRL-J work in Blender (join meshes)? So far I can't tell if it "really" combines them or just groups them somehow. My attempt to export this way errored-out, forgot to do texture assignment instead of material assignment I think, but out of time for now...
-
qbism - Posts: 1236
- Joined: Thu Nov 04, 2004 5:51 am
Re: QuakeForge 0.7.0 released.
ctrl-j does indeed truely join the meshes into the one object. However, I have no idea how it handles uvmaps and textures assigned via the uvmaps.
Leave others their otherness.
http://quakeforge.net/
http://quakeforge.net/
- taniwha
- Posts: 399
- Joined: Thu Jan 14, 2010 7:11 am
28 posts
• Page 1 of 2 • 1, 2
Who is online
Users browsing this forum: No registered users and 4 guests
