2007-07-26 13:53:20 -07:00
|
|
|
__author__ = "Kevin Gillette"
|
2007-08-20 08:09:13 -07:00
|
|
|
__version__ = "1.0"
|
2007-07-26 13:53:20 -07:00
|
|
|
|
|
|
|
#
|
|
|
|
# --------------------------------------------------------------------------
|
|
|
|
# pie_common v0.1 by Kevin Gillette (kage)
|
2007-08-20 08:09:13 -07:00
|
|
|
# v1.0 --
|
2007-07-26 13:53:20 -07:00
|
|
|
# --------------------------------------------------------------------------
|
|
|
|
# ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
#
|
|
|
|
# ***** END GPL LICENCE BLOCK *****
|
|
|
|
# --------------------------------------------------------------------------
|
|
|
|
|
2007-08-20 08:09:13 -07:00
|
|
|
from Blender import *
|
2007-07-26 13:53:20 -07:00
|
|
|
|
2007-08-20 08:09:13 -07:00
|
|
|
verbose = (Registry.GetKey('General') or dict()).get('verbose', True)
|
2007-07-26 13:53:20 -07:00
|
|
|
|
|
|
|
class FatalError(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def scalar_value(val, type=None):
|
|
|
|
if hasattr(val, 'val'):
|
|
|
|
val = val.val
|
|
|
|
if type is 'bool': val = bool(val)
|
|
|
|
return val
|
|
|
|
|
2007-08-20 08:09:13 -07:00
|
|
|
def default_value(ui, key):
|
|
|
|
return ui.getData('defaults/user').get(key, ui.getData('defaults/script').get(key))
|
2007-07-26 13:53:20 -07:00
|
|
|
|
2007-08-20 08:09:13 -07:00
|
|
|
def save_defaults(ui):
|
|
|
|
defaults = ui.getData('defaults/script')
|
|
|
|
defaults.update(ui.getData('defaults/user'))
|
|
|
|
for opt in ui.getData('optlist'):
|
2007-07-26 13:53:20 -07:00
|
|
|
name = opt['name']
|
|
|
|
defaults[name] = scalar_value(opt['val'], opt['opts'])
|
|
|
|
if 'tooltips' not in defaults: defaults['tooltips'] = dict()
|
|
|
|
if 'limits' not in defaults: defaults['limits'] = dict()
|
2007-08-20 08:09:13 -07:00
|
|
|
defaults['tooltips'].update(ui.getData('tooltips'))
|
|
|
|
defaults['limits'].update(ui.getData('limits'))
|
|
|
|
Registry.SetKey('warzone_pie', defaults, True)
|
|
|
|
Draw.PupMenu("Defaults saved")
|
|
|
|
|
|
|
|
tc_unit_size = 100
|
|
|
|
|
|
|
|
def get_teamcolor_meta(uvs):
|
|
|
|
for coord in uvs[:3]:
|
|
|
|
if coord[0] not in (0.0, 1.0) or coord[1] not in (0.0, 1.0): break
|
|
|
|
else: return None
|
|
|
|
width, height = uvs[0]
|
|
|
|
frames = round((uvs[1][0] - width) * tc_unit_size)
|
|
|
|
delay = round((uvs[2][1] - height) * tc_unit_size)
|
|
|
|
return [frames, delay, width, height]
|
|
|
|
|
|
|
|
def create_teamcolor_meta(nbPoints, width, height, frames, delay=10):
|
|
|
|
offsetU = width + frames / tc_unit_size
|
|
|
|
offsetV = height + delay / tc_unit_size
|
|
|
|
offsets = range(nbPoints - 3)
|
|
|
|
offsets.reverse()
|
|
|
|
uv = [Mathutils.Vector(width, height),
|
|
|
|
Mathutils.Vector(offsetU, height),
|
|
|
|
Mathutils.Vector(offsetU, offsetV)]
|
|
|
|
uv.extend([Mathutils.Vector(width, offsetV + 0.01 * i) for i in offsets])
|
|
|
|
return uv
|
|
|
|
|
|
|
|
class BeltFedUI:
|
|
|
|
def __init__(self, debug_gui=False):
|
|
|
|
self._persistent_data = dict()
|
|
|
|
self._volatile_data = dict()
|
|
|
|
self._beltlinks = [[None, None, lambda self, val: True, None]] # used for bootstrapping
|
|
|
|
self._linknames = dict()
|
|
|
|
self._index = -1
|
|
|
|
self._skipui = False
|
|
|
|
self._debug_gui = debug_gui
|
|
|
|
|
|
|
|
self._debug_messages = {
|
|
|
|
'fatal-error': ["FATAL ERROR: ", 0],
|
|
|
|
'error': ["ERROR: ", 0],
|
|
|
|
'warning': ["Warning: ", 0],
|
|
|
|
'notice': ["", 0]
|
|
|
|
}
|
|
|
|
|
|
|
|
self._debug_buffer = "" # also includes notices
|
|
|
|
self._error_list = list()
|
|
|
|
|
|
|
|
def debug(self, message, type="notice", lineno=None):
|
|
|
|
if type not in self._debug_messages:
|
|
|
|
raise ValueError("not a correct message type")
|
|
|
|
if lineno: message += " line: %i" % lineno
|
|
|
|
tobj = self._debug_messages[type]
|
|
|
|
tobj[1] += 1
|
|
|
|
lbuf = tobj[0] + message
|
|
|
|
if verbose: print lbuf
|
|
|
|
self._debug_buffer += lbuf + "\n"
|
|
|
|
if type != "notice": self._error_list.append(lbuf)
|
|
|
|
if type == 'fatal-error':
|
|
|
|
raise FatalError()
|
|
|
|
|
|
|
|
def append(self, process, draw=None, event=None, scrl_range=None, name=None):
|
|
|
|
if self._index >= 0:
|
|
|
|
raise ValueError("cannot append a beltlink after process has started")
|
|
|
|
if (draw or event) and not (draw and event):
|
|
|
|
raise TypeError("Both draw and event should be callable or both should be None")
|
|
|
|
self._beltlinks.append([process, draw, event, scrl_range])
|
|
|
|
if name: self._linknames[name] = len(self._beltlinks)
|
|
|
|
|
|
|
|
def setScrollRange(self, min, max):
|
|
|
|
if self._index < 0:
|
|
|
|
raise ValueError("cannot set the scroll range before the process has started.")
|
|
|
|
if min == max:
|
|
|
|
self._beltlinks[self._index][3] = None
|
|
|
|
return
|
|
|
|
if min > max:
|
|
|
|
raise ValueError("min must be less than max")
|
|
|
|
self._beltlinks[self._index][3] = (min, max)
|
|
|
|
|
|
|
|
def skipUI(self):
|
|
|
|
self._skipui = True
|
|
|
|
|
|
|
|
def jumpToNamedLink(name):
|
|
|
|
if self._index < 0: raise ValueError("must first start the ui process")
|
|
|
|
if name in self._linknames:
|
|
|
|
self._index = self._linknames[name]
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
def Run(self):
|
|
|
|
if self._index < 0:
|
|
|
|
if self._debug_gui:
|
|
|
|
self.append(self._error_process, self._error_draw, self._error_evt,
|
|
|
|
[None, None], "debug")
|
|
|
|
self._index = 0
|
|
|
|
self._evt()
|
|
|
|
else:
|
|
|
|
raise ValueError("process has already started")
|
|
|
|
|
|
|
|
def _process(self):
|
|
|
|
func = self._beltlinks[self._index][0]
|
|
|
|
if callable(func):
|
|
|
|
return func(self)
|
|
|
|
return None
|
|
|
|
|
|
|
|
def _draw(self):
|
|
|
|
self._beltlinks[self._index][1](self)
|
|
|
|
|
|
|
|
def _evt(self, val=None):
|
|
|
|
retval = self._beltlinks[self._index][2](self, val)
|
|
|
|
if retval is False:
|
|
|
|
self.debug("Import aborted")
|
|
|
|
Draw.Exit()
|
|
|
|
elif retval is True:
|
|
|
|
Draw.Exit()
|
|
|
|
beltlen = len(self._beltlinks)
|
|
|
|
while retval is True:
|
|
|
|
self._volatile_data = dict()
|
|
|
|
self._index += 1
|
|
|
|
self._skipui = False
|
|
|
|
if self._index >= beltlen: return
|
|
|
|
try:
|
|
|
|
retval = self._process()
|
|
|
|
except FatalError:
|
|
|
|
if not self.jumpToNamedLink('debug'): self._index = beltlen
|
|
|
|
continue
|
|
|
|
if retval is None and self._beltlinks[self._index][1] and \
|
|
|
|
not self._skipui:
|
|
|
|
if self._beltlinks[self._index][3]:
|
|
|
|
input_handler = self._handle_mousewheel
|
|
|
|
else:
|
|
|
|
input_handler = None
|
|
|
|
Draw.Register(self._draw, input_handler, self._evt)
|
|
|
|
break
|
|
|
|
|
|
|
|
def _handle_mousewheel(self, evt, val):
|
|
|
|
offset = 0
|
|
|
|
if evt is Draw.WHEELDOWNMOUSE: offset = 1
|
|
|
|
elif evt is Draw.WHEELUPMOUSE: offset = -1
|
|
|
|
if offset:
|
|
|
|
scroll = self.getData('scroll-position', 0)
|
|
|
|
scroll += offset
|
|
|
|
min, max = self._beltlinks[self._index][3]
|
|
|
|
if (min is None or scroll >= min) and (max is None or scroll <= max):
|
|
|
|
self.setData('scroll-position', scroll)
|
|
|
|
Draw.Redraw()
|
|
|
|
|
|
|
|
def setData(self, key, value, persist=False):
|
|
|
|
if persist: self._persistent_data[key] = value
|
|
|
|
else: self._volatile_data[key] = value
|
|
|
|
|
|
|
|
def getData(self, key, default=None):
|
|
|
|
if key in self._volatile_data:
|
|
|
|
return self._volatile_data[key]
|
|
|
|
if key in self._persistent_data:
|
|
|
|
return self._persistent_data[key]
|
|
|
|
return default
|
|
|
|
|
|
|
|
def __contains__(key):
|
|
|
|
return key in self._volatile_data or key in self._persistent_data
|
|
|
|
|
|
|
|
def _error_process(self, shim):
|
|
|
|
if self._error_list:
|
|
|
|
numerrors = len(self._error_list)
|
|
|
|
rows = min(numerrors, 20)
|
|
|
|
self.setData('num-errors', numerrors)
|
|
|
|
self.setData('rows', rows)
|
|
|
|
self.setScrollRange(0, numerrors - rows)
|
|
|
|
else:
|
|
|
|
self.skipUI()
|
|
|
|
|
|
|
|
def _error_draw(self, shim):
|
|
|
|
Draw.PushButton("Save log", 1, 31, 10, 140, 30, "Save the log to a file, and then continue with loading")
|
|
|
|
Draw.PushButton("Finish", 0, 289, 10, 140, 30, "Do not save the errors, and continue with loading")
|
|
|
|
BGL.glClearColor(0.7, 0.7, 0.7, 1)
|
|
|
|
BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)
|
|
|
|
BGL.glColor3f(0.8, 0.8, 0.8)
|
|
|
|
BGL.glRecti(5, 5, 455, 405)
|
|
|
|
BGL.glColor3f(0.7, 0.7, 0.7)
|
|
|
|
BGL.glRecti(10, 45, 450, 365)
|
|
|
|
BGL.glRecti(175, 15, 285, 35)
|
|
|
|
BGL.glRecti(15, 370, 440, 400)
|
|
|
|
rows, scroll = self.getData('rows'), self.getData('scroll-position', 0)
|
|
|
|
numerrors = self.getData('num-errors')
|
|
|
|
Draw.Label("%i-%i of %i" % (scroll, scroll + rows, numerrors),
|
|
|
|
176, 16, 108, 18)
|
|
|
|
start = scroll
|
|
|
|
end = start + rows
|
|
|
|
BGL.glColor3i(0, 0, 0)
|
|
|
|
counter = 0
|
|
|
|
while start + counter < end:
|
|
|
|
BGL.glRasterPos2i(11, 355 - counter * 16)
|
|
|
|
Draw.Text(self._error_list[start + counter])
|
|
|
|
counter += 1
|
|
|
|
BGL.glColor3i(0, 0, 0)
|
|
|
|
text = ("Processing Errors", "large")
|
|
|
|
BGL.glRasterPos2i(int((426 - Draw.GetStringWidth(*text)) / 2 + 15), 381)
|
|
|
|
Draw.Text(*text)
|
|
|
|
|
|
|
|
def _error_evt(self, shim, val):
|
|
|
|
if val == 0:
|
|
|
|
return True
|
|
|
|
if val == 1:
|
|
|
|
filename = self.getData('filename').rsplit('.', 1)[0] + ".log"
|
|
|
|
Window.FileSelector(self._dump_log, 'Save import log', filename)
|
|
|
|
return
|
|
|
|
errpage = self.getData('error-page')
|
|
|
|
if val == 2: self.setData('error-page', errpage - 1)
|
|
|
|
elif val == 3: self.setData('error-page', errpage + 1)
|
|
|
|
Draw.Redraw()
|
|
|
|
|
|
|
|
def _dump_log(self, filename):
|
|
|
|
f = open(filename, "w")
|
|
|
|
f.write(self._debug_buffer)
|
|
|
|
f.close()
|
|
|
|
|
|
|
|
def normalizeObjectName(name):
|
|
|
|
pos = name.rfind('.')
|
|
|
|
if name[pos + 1:].isdigit(): name = name[:pos]
|
|
|
|
return name
|
2007-07-26 13:53:20 -07:00
|
|
|
|
2007-08-20 08:09:13 -07:00
|
|
|
def validate(levels):
|
|
|
|
""" expects levels pre-sorted by object name, and assumes that all passed
|
|
|
|
objects have names starting with LEVEL_ and are children of a PIE_ object
|
|
|
|
"""
|
|
|
|
errors = list()
|
|
|
|
if not levels:
|
|
|
|
errors.append(('error', "no levels were found"))
|
|
|
|
i = 0
|
|
|
|
for level in levels:
|
|
|
|
i += 1
|
|
|
|
name = level.getName()
|
|
|
|
invalid = True
|
|
|
|
try:
|
|
|
|
listed_num = int(normalizeObjectName(name)[6:])
|
|
|
|
if listed_num is i: invalid = False
|
|
|
|
except ValueError:
|
|
|
|
pass
|
|
|
|
if invalid:
|
|
|
|
errors.append(('warning', "expected level %i" % i))
|
|
|
|
i += 1
|
|
|
|
if level.getType() != "Mesh":
|
|
|
|
errors.append(('error', "%s must be a mesh object" % name))
|
|
|
|
continue
|
|
|
|
mesh = level.getData(mesh=True)
|
|
|
|
num = len(mesh.faces)
|
|
|
|
if num > 512:
|
|
|
|
errors.append(('error', "more than 512 faces found in %s (%i found)" %
|
|
|
|
(name, num)))
|
|
|
|
num = len(mesh.verts)
|
|
|
|
if num > 768:
|
|
|
|
errors.append(('error', "more than 768 verts found in %s (%i found)" %
|
|
|
|
(name, num)))
|
|
|
|
for j, face in enumerate(mesh.faces):
|
|
|
|
num = len(face.verts)
|
|
|
|
if num > 6:
|
|
|
|
errors.append(('error', "more than 6 verts found in face %i of %s (%i found)" %
|
|
|
|
(j, name, num)))
|
|
|
|
return errors
|