warzone2100/tools/blender/pie_common.py

310 lines
9.7 KiB
Python

__author__ = "Kevin Gillette"
__version__ = "1.0"
#
# --------------------------------------------------------------------------
# pie_common v0.1 by Kevin Gillette (kage)
# v1.0 --
# --------------------------------------------------------------------------
# ***** 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 *****
# --------------------------------------------------------------------------
from Blender import *
verbose = (Registry.GetKey('General') or dict()).get('verbose', True)
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
def default_value(ui, key):
return ui.getData('defaults/user').get(key, ui.getData('defaults/script').get(key))
def save_defaults(ui):
defaults = ui.getData('defaults/script')
defaults.update(ui.getData('defaults/user'))
for opt in ui.getData('optlist'):
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()
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
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