source: OGRE/trunk/ogrenew/Tools/BlenderExport/ogreexport.py @ 690

Revision 690, 176.3 KB checked in by mattausch, 18 years ago (diff)

added ogre 1.07 main

Line 
1#!BPY
2
3"""
4Name: 'Ogre XML'
5Blender: 234
6Group: 'Export'
7Tooltip: 'Exports selected meshs with armature animations to Ogre3D'
8"""
9
10__author__ = ['Michael Reimpell', 'Jens Hoffmann', 'et al.']
11__version__ = '0.15.0'
12__url__ = ['OGRE website, http://www.ogre3d.org',
13        'Script manual, http://www.ogre3d.org/docs/Tutorials/blender/index.html',
14        'OGRE forum, http://www.ogre3d.org/phpBB2/']
15__bpydoc__ = """\
16Exports selected meshs with armature animations to Ogre3D.
17"""
18
19# Blender to Ogre Mesh and Skeleton Exporter v0.15.0
20# url: http://www.ogre3d.org
21
22# Ogre exporter written by Jens Hoffmann and Michael Reimpell
23# based on the Cal3D exporter v0.5 written by Jean-Baptiste LAMY
24
25# Copyright (C) 2004 Michael Reimpell -- <M.Reimpell@tu-bs.de>
26# Copyright (C) 2003 Jens Hoffmann -- <hoffmajs@gmx.de>
27# Copyright (C) 2003 Jean-Baptiste LAMY -- jiba@tuxfamily.org
28#
29# ChangeLog:
30#   0.7 :  released by Jens Hoffman
31#   0.8 :  * Mon Feb 02 2004 Michael Reimpell <M.Reimpell@tu-bs.de>
32#          - added GUI
33#   0.9 :  * Tue Feb 03 2004 Michael Reimpell <M.Reimpell@tu-bs.de>
34#          - added special header to be registered in blenders export menu
35#   0.10:  * Wed Feb 04 2004 Michael Reimpell <M.Reimpell@tu-bs.de>
36#          - blenders broken Draw.Scrollbar replaced with own class
37#          - texture origin changed to top-left (Ogre v0.13.0)
38#          - export log is shown in message window
39#          - dirty hack for blender 2.32 (does not implement IpoCurve.getName()
40#            for action Ipos)
41#   0.11:  * Mon Feb 09 2004 Michael Reimpell <M.Reimpell@tu-bs.de>
42#          - strip path from texture filenames in material file
43#          - back button for doneMessageBox
44#          - changed scrollbar focus behaviour
45#          - log text position offset
46#          - Ogre v0.13.0 material script support
47#            Material specific:
48#             amb * rgbCol          -> ambient <r> <g> <b>
49#             rgbCol                -> diffuse <r> <g> <b>
50#             spec * specCol, hard  -> specular <r> <g> <b> <hard>
51#             emit*rgbCol           -> emissive <r> <g> <b>
52#             Material.mode
53#              ZINVERT              -> depth_func greater_equal
54#              ENV                  -> depth_func always_fail
55#              SHADELESS            -> lighting off
56#              NOMIST               -> fog_override true
57#            Face specific:
58#             NMFace.mode
59#              INVISIBLE            -> no export
60#              TEX                  -> texture_unit
61#             NMFace.transp
62#              SOLID                -> Default: scene_blend one zero
63#              ADD                  -> scene_blend add
64#              ALPHA                -> scene_blend alpha_blend
65#            Texture specific:
66#             NMFace.image.filename -> texture <name without path>
67#   0.12:  * Mon Feb 16 2004 Michael Reimpell <M.Reimpell@tu-bs.de>
68#          - black border flashing removed
69#          - added material script support for
70#            Material.mode
71#              TEXFACE -> disable ambient, diffuse, specular and emissive
72#          - exit on ESCKEY or QKEY pressed (not released)
73#          - added frame based animation export
74#   0.12.1: * Wed Feb 18 2004 Michael Reimpell <M.Reimpell@tu-bs.de>
75#          - changed two user interface strings to avoid confusion
76#   0.13:  * Mon Feb 23 2004 Michael Reimpell <M.Reimpell@tu-bs.de>
77#          - scrollbar marker moves on focus click without MOUSEY event
78#          - scrollbar marker focus light
79#          - show version number in GUI
80#          - transparent load and save of export settings
81#   0.13.1: * Wed Feb 25 2004 Michael Reimpell <M.Reimpell@tu-bs.de>
82#          - added support for vertices with different uv coordinates but same normal
83#          - improved button handling
84#          - added support for sticky uv coordinates
85#   0.13.2: * Thu Jun 03 2004 Michael Reimpell <M.Reimpell@tu-bs.de>
86#          - added warning if no materials or textures are defined
87#          - added warning if mesh has no visible faces
88#          - displays a message while exporting
89#          - added material script support for
90#            Material.mode
91#              SHADOW -> receive_shadows
92#   0.13.3: * Sun Jun 06 2004 Michael Reimpell <M.Reimpell@tu-bs.de>
93#          - changed GUI positions to ints to avoid DeprecationWarnings (Blender 2.33)
94#          - get frames per second setting from the render buttons (Blender 2.33)
95#          - respect new Armature.getBones() behaviour (Blender 2.33)
96#          - added missing argument in SkeletonMaterial creation
97#          - added option to rotate the coordinate system on export
98#   0.14.0: * Sun Jul 04 2004 Michael Reimpell <M.Reimpell@tu-bs.de>
99#          - script loadable via command line
100#          - Ogre logo added
101#          - changed material mapping:
102#             amb * World.getAmb() -> ambient <r> <g> <b>
103#            where World is the first world returned by Blender.World.Get()
104#          - selected objects menu visible even if "Export Armature" option disabled
105#          - use Blender.Armature.NLA submodule to access Action objects (Blender 2.33)
106#   0.14.1: * Fri Aug 13 2004 Michael Reimpell <M.Reimpell@tu-bs.de>
107#          - additional changes due to new Armature.getBones() behaviour (Blender 2.33)
108#          - ordering of the additional rotation on export changed
109#          - allow actions with less channels than bones
110#          - support for dotted parenting in armature edit mode
111#          - workaround for quaternion naming bug in Blender 2.34
112#          - support for single loc ipo curves
113#          - use Blender.World.GetActive() to get the ambient colour of the current world (Blender 2.34)
114#          - added mousewheel support to the scrollbar (Blender 2.34)
115#          - change keyframes and name of animation according to selected action
116#          - use Object.getMatrix("worldspace") (Blender 2.34)
117#          - smoothed scrollbar movement and removed flicker
118#   0.14.2: * Fri Aug 13 2004 Michael Reimpell <M.Reimpell@tu-bs.de>
119#          - location key frame values fixed
120#          - fixed redraw if action is changed
121#   0.15.0: * Sun Oct 24 2004 Michael Reimpell <M.Reimpell@tu-bs.de>
122#          - scalar product range correction in calc_rootaxis
123#          - made ArmatureAction.createArmatureActionDict a static method
124#          - renamed private methods to begin with an underscore
125#          - switched to javadoc comments
126#          - changed vertex buffer layout to allow software skinning
127#          - settings are now stored inside the .blend file
128#          - importing pickle module now optional
129#          - support for non-uniform keyframe scaling
130#          - export vertex colours
131#   0.15.1: * Michael Reimpell <M.Reimpell@tu-bs.de>
132#          - use Blender.World.GetCurrent() for Blender > 2.34
133#          - fixed calculation of initial bone rotation
134#          - preliminary normal map support
135#          - option to export in objects local coordinates
136#          - changed material file default name to the current scene name
137#          - files are now named after their datablock name
138#          - path selection starts with current export path
139#          - material ambient colour is scaled white
140#          - option to use scaled diffuse colour as ambient
141#          - BPy documentation added
142#          - coloured log
143#          - crossplatform path handling
144#          - allow empty material list entries
145#          - material export distinguishs between rendering and game engine materials
146#   0.15.1: * Sun Nov 27 2004 John Bartholomew <johnb213@users.sourceforge.net>
147#          - option to run OgreXMLConverter automatically on the exported files
148#
149# TODO:
150#          - vertex colours
151#          - code cleanup
152#          - noninteractive mode when called from command line
153#          - TWOSIDE face mode, TWOSIDED mesh mode
154#          - SUBSURF mesh mode
155#          - assign unskinned vertices to a static bone
156#
157# This program is free software; you can redistribute it and/or modify
158# it under the terms of the GNU General Public License as published by
159# the Free Software Foundation; either version 2 of the License, or
160# (at your option) any later version.
161#
162# This program is distributed in the hope that it will be useful,
163# but WITHOUT ANY WARRANTY; without even the implied warranty of
164# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
165# GNU General Public License for more details.
166#
167# You should have received a copy of the GNU General Public License
168# along with this program; if not, write to the Free Software
169# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
170
171# this export script is assumed to be used with the latest blender version.
172#
173# Usage:
174# select the meshes you want to export and run this script (alt-p)
175
176# KEEP_SETTINGS (enable = 1, disable = 0)
177#  transparently load and save settings to the text 'ogreexport.cfg'
178#  inside the current .blend file.
179KEEP_SETTINGS = 1
180
181# OGRE_XML_CONVERTER
182#  the command line used to run the OgreXMLConverter tool.
183#  Set to '' to disable automatical conversion of XML files.
184OGRE_XML_CONVERTER = ''
185
186# OGRE_VERTEXCOLOUR_BGRA
187#  workaround for Ogre's vertex colour conversion bug.
188#  Set to 0 for RGBA, 1 for BGRA.
189OGRE_OPENGL_VERTEXCOLOUR = 0
190
191#######################################################################################
192## Code starts here.
193
194# epydoc doc format
195__docformat__ = "javadoc en"
196
197######
198# imports
199######
200import Blender, sys, os, math, string
201if KEEP_SETTINGS:
202        try:
203                import pickle
204        except ImportError:
205                Blender.Draw.PupMenu("Can't import pickle module!%t|Permanent settings disabled.")
206                KEEP_SETTINGS = 0
207
208######
209# namespaces
210######
211from Blender import Draw
212from Blender import Mathutils
213from Blender.BGL import *
214
215######
216# Classes
217######
218class ReplacementScrollbar:
219        """Scrollbar replacement for Draw.Scrollbar
220           <ul>
221           <li> import Blender
222           <li> call eventFilter and buttonFilter in registered callbacks
223           </ul>
224           
225           @author Michael Reimpell
226        """
227        def __init__(self, initialValue, minValue, maxValue, buttonUpEvent, buttonDownEvent):
228                """Constructor   
229                   
230                   @param initialValue    inital value
231                   @param minValue        minimum value
232                   @param maxValue        maxium value
233                   @param buttonUpEvent   unique event number
234                   @param buttonDownEvent unique event number
235                """
236                self.currentValue = initialValue
237                self.minValue = minValue
238                if maxValue > minValue:
239                        self.maxValue = maxValue
240                else:
241                        self.maxValue = self.minValue
242                        self.minValue = maxValue
243                self.buttonUpEvent = buttonUpEvent
244                self.buttonDownEvent = buttonDownEvent
245                # private
246                self.guiRect = [0,0,0,0]
247                self.positionRect = [0,0,0,0]
248                self.markerRect = [0,0,0,0]
249                self.mousePressed = 0
250                self.mouseFocusX = 0
251                self.mouseFocusY = 0
252                self.markerFocusY = 0
253                self.mousePositionY = 0
254                return
255       
256        def getCurrentValue(self):
257                """current marker position
258                """
259                return self.currentValue
260               
261        def up(self, steps=1):
262                """move scrollbar up
263                """
264                if (steps > 0):
265                        if ((self.currentValue - steps) > self.minValue):
266                                self.currentValue -= steps
267                        else:
268                                self.currentValue = self.minValue
269                return
270       
271        def down(self, steps=1):
272                """move scrollbar down
273                """
274                if (steps > 0):
275                        if ((self.currentValue + steps) < self.maxValue):
276                                self.currentValue += steps
277                        else:
278                                self.currentValue = self.maxValue
279                return
280       
281        def draw(self, x, y, width, height):
282                """draw scrollbar
283                """
284                # get size of the GUI window to translate MOUSEX and MOUSEY events
285                guiRectBuffer = Blender.BGL.Buffer(GL_FLOAT, 4)
286                Blender.BGL.glGetFloatv(Blender.BGL.GL_SCISSOR_BOX, guiRectBuffer)
287                self.guiRect = [int(guiRectBuffer.list[0]), int(guiRectBuffer.list[1]), \
288                                int(guiRectBuffer.list[2]), int(guiRectBuffer.list[3])]
289                # relative position
290                self.positionRect = [ x, y, x + width, y + height]
291                # check minimal size:
292                # 2 square buttons,4 pixel borders and 1 pixel inside for inner and marker rectangles
293                if ((height > (2*(width+5))) and (width > 2*5)):
294                        # keep track of remaining area
295                        remainRect = self.positionRect[:]
296                        # draw square buttons
297                        Blender.Draw.Button("/\\", self.buttonUpEvent, x, y + (height-width), width, width, "scroll up")
298                        remainRect[3] -=  width + 2
299                        Blender.Draw.Button("\\/", self.buttonDownEvent, x, y, width, width, "scroll down")
300                        remainRect[1] +=  width + 1
301                        # draw inner rectangle
302                        Blender.BGL.glColor3f(0.13,0.13,0.13) # dark grey
303                        Blender.BGL.glRectf(remainRect[0], remainRect[1], remainRect[2], remainRect[3])
304                        remainRect[0] += 1
305                        remainRect[3] -= 1
306                        Blender.BGL.glColor3f(0.78,0.78,0.78) # light grey
307                        Blender.BGL.glRectf(remainRect[0], remainRect[1], remainRect[2], remainRect[3])
308                        remainRect[1] += 1
309                        remainRect[2] -= 1
310                        Blender.BGL.glColor3f(0.48,0.48,0.48) # grey
311                        Blender.BGL.glRectf(remainRect[0], remainRect[1], remainRect[2], remainRect[3])
312                        # draw marker rectangle
313                        # calculate marker rectangle
314                        innerHeight = remainRect[3]-remainRect[1]
315                        markerHeight = innerHeight/(self.maxValue-self.minValue+1.0)
316                        # markerRect
317                        self.markerRect[0] = remainRect[0]
318                        self.markerRect[1] = remainRect[1] + (self.maxValue - self.currentValue)*markerHeight
319                        self.markerRect[2] = remainRect[2]
320                        self.markerRect[3] = self.markerRect[1] + markerHeight
321                        # clip markerRect to innerRect (catch all missed by one errors)
322                        if self.markerRect[1] > remainRect[3]:
323                                self.markerRect[1] = remainRect[3]
324                        if self.markerRect[3] > remainRect[3]:
325                                self.markerRect[3] = remainRect[3]
326                        # draw markerRect
327                        remainRect = self.markerRect
328                        Blender.BGL.glColor3f(0.78,0.78,0.78) # light grey
329                        Blender.BGL.glRectf(remainRect[0], remainRect[1], remainRect[2], remainRect[3])
330                        remainRect[0] += 1
331                        remainRect[3] -= 1
332                        Blender.BGL.glColor3f(0.13,0.13,0.13) # dark grey
333                        Blender.BGL.glRectf(remainRect[0], remainRect[1], remainRect[2], remainRect[3])
334                        remainRect[1] += 1
335                        remainRect[2] -= 1
336                        # check if marker has foucs
337                        if (self.mouseFocusX and self.mouseFocusY and (self.mousePositionY > self.markerRect[1]) and (self.mousePositionY < self.markerRect[3])):
338                                Blender.BGL.glColor3f(0.64,0.64,0.64) # marker focus grey
339                        else:
340                                Blender.BGL.glColor3f(0.60,0.60,0.60) # marker grey
341                        Blender.BGL.glRectf(remainRect[0], remainRect[1], remainRect[2], remainRect[3])
342                else:
343                        print "scrollbar draw size too small!"
344                return
345               
346        def eventFilter(self, event, value):
347                """event filter for keyboard and mouse input events
348                   call it inside the registered event function
349                """
350                if (value != 0):
351                        # Buttons
352                        if (event == Blender.Draw.PAGEUPKEY):
353                                self.up(3)
354                                Blender.Draw.Redraw(1)
355                        elif (event == Blender.Draw.PAGEDOWNKEY):
356                                self.down(3)
357                                Blender.Draw.Redraw(1)
358                        elif (event == Blender.Draw.UPARROWKEY):
359                                self.up(1)
360                                Blender.Draw.Redraw(1)
361                        elif (event == Blender.Draw.DOWNARROWKEY):
362                                self.down(1)
363                                Blender.Draw.Redraw(1)
364                        # Mouse
365                        elif (event == Blender.Draw.MOUSEX):
366                                # check if mouse is inside positionRect
367                                if (value >= (self.guiRect[0] + self.positionRect[0])) and (value <= (self.guiRect[0] + self.positionRect[2])):
368                                        # redraw if marker got focus
369                                        if (not self.mouseFocusX) and self.mouseFocusY:
370                                                Blender.Draw.Redraw(1)
371                                        self.mouseFocusX = 1
372                                else:
373                                        # redraw if marker lost focus
374                                        if self.mouseFocusX and self.mouseFocusY:
375                                                Blender.Draw.Redraw(1)
376                                        self.mouseFocusX = 0
377                        elif (event == Blender.Draw.MOUSEY):
378                                # check if mouse is inside positionRect
379                                if (value >= (self.guiRect[1] + self.positionRect[1])) and (value <= (self.guiRect[1] + self.positionRect[3])):
380                                        self.mouseFocusY = 1
381                                        # relative mouse position
382                                        self.mousePositionY = value - self.guiRect[1]
383                                        if ((self.mousePositionY > self.markerRect[1]) and (self.mousePositionY < self.markerRect[3])):
384                                                # redraw if marker got focus
385                                                if self.mouseFocusX and (not self.markerFocusY):
386                                                        Blender.Draw.Redraw(1)
387                                                self.markerFocusY = 1
388                                        else:
389                                                # redraw if marker lost focus
390                                                if self.mouseFocusX and self.markerFocusY:
391                                                        Blender.Draw.Redraw(1)
392                                                self.markerFocusY = 0
393                                        # move marker
394                                        if (self.mousePressed == 1):
395                                                # calculate step from distance to marker
396                                                if (self.mousePositionY > self.markerRect[3]):
397                                                        # up
398                                                        self.up(1)
399                                                        Blender.Draw.Draw()
400                                                elif (self.mousePositionY < self.markerRect[1]):
401                                                        # down
402                                                        self.down(1)
403                                                        Blender.Draw.Draw()
404                                                # redraw if marker lost focus
405                                                if self.mouseFocusX and self.mouseFocusY:
406                                                        Blender.Draw.Redraw(1)
407                                else:
408                                        # redraw if marker lost focus
409                                        if self.mouseFocusX and self.markerFocusY:
410                                                Blender.Draw.Redraw(1)
411                                        self.markerFocusY = 0
412                                        self.mouseFocusY = 0
413                        elif ((event == Blender.Draw.LEFTMOUSE) and (self.mouseFocusX == 1) and (self.mouseFocusY == 1)):
414                                self.mousePressed = 1
415                                # move marker
416                                if (self.mousePositionY > self.markerRect[3]):
417                                        # up
418                                        self.up(1)
419                                        Blender.Draw.Redraw(1)
420                                elif (self.mousePositionY < self.markerRect[1]):
421                                        # down
422                                        self.down(1)
423                                        Blender.Draw.Redraw(1)
424                        elif (Blender.Get("version") >= 234):
425                                if (event == Blender.Draw.WHEELUPMOUSE):
426                                        self.up(1)
427                                        Blender.Draw.Redraw(1)
428                                elif (event == Blender.Draw.WHEELDOWNMOUSE):
429                                        self.down(1)
430                                        Blender.Draw.Redraw(1)
431                else: # released keys and buttons
432                        if (event == Blender.Draw.LEFTMOUSE):
433                                self.mousePressed = 0
434                               
435                return
436               
437        def buttonFilter(self, event):
438                """button filter for Draw Button events
439                   call it inside the registered button function
440                """
441                if (event  == self.buttonUpEvent):
442                        self.up()
443                        Blender.Draw.Redraw(1)
444                elif (event == self.buttonDownEvent):
445                        self.down()
446                        Blender.Draw.Redraw(1)
447                return
448
449class ArmatureAction:
450        """Resembles Blender's actions
451           <ul>
452           <li> import Blender, string
453           </ul>
454           
455           @author Michael Reimpell
456        """
457        def __init__(self, name="", ipoDict=None):
458                """Constructor
459               
460                   @param name    the action name
461                   @param ipoDict a dictionary with bone names as keys and action Ipos as values
462                """
463                self.firstKeyFrame = None
464                self.lastKeyFrame = None
465                self.name = name
466                # ipoDict[boneName] = Blender.Ipo
467                if ipoDict is None:
468                        self.ipoDict = {}
469                else:
470                        self.ipoDict = ipoDict
471                        self._updateKeyFrameRange()
472                return
473       
474        # private method       
475        def _updateKeyFrameRange(self):
476                """Updates firstKeyFrame and lastKeyFrame considering the current IpoCurves.
477                """
478                self.firstKeyFrame = None
479                self.lastKeyFrame = None
480                if self.ipoDict is not None:
481                        # check all bone Ipos
482                        for ipo in self.ipoDict.values():
483                                # check all IpoCurves
484                                for ipoCurve in ipo.getCurves():
485                                        # check first and last keyframe
486                                        for bezTriple in ipoCurve.getPoints():
487                                                iFrame = bezTriple.getPoints()[0]
488                                                if ((iFrame < self.firstKeyFrame) or (self.firstKeyFrame is None)):
489                                                        self.firstKeyFrame = iFrame
490                                                if ((iFrame > self.lastKeyFrame) or (self.lastKeyFrame is None)):
491                                                        self.lastKeyFrame = iFrame
492                return
493       
494        # static method
495        def createArmatureActionDict(object):
496                """Creates a dictionary of possible actions belonging to an armature.
497                   Static call with: ArmatureAction.createArmatureActionDict(object)
498                   
499                   @param object a Blender.Object of type Armature
500                   @return a dictionary of ArmatureAction objects with name as key and ArmatureAction as value
501                """
502                # create bone dict
503                boneQueue = object.getData().getBones()
504                boneDict = {}
505                while (len(boneQueue) > 0):
506                        # get all bones of the armature
507                        currentBone = boneQueue.pop(0)
508                        boneDict[currentBone.getName()] = currentBone
509                        children = currentBone.getChildren()
510                        if (len(children) > 0):
511                                for child in children:
512                                        boneQueue.append(child)
513                boneNameList = boneDict.keys()
514                # check for available actions
515                armatureActionDict = {}
516                ## get linked action first
517                linkedAction = object.getAction()
518                if linkedAction is not None:
519                        # check all bones
520                        linkedActionIpoDict = linkedAction.getAllChannelIpos()
521                        hasValidChannel = 0 # false
522                        iBone = 0
523                        while ((not hasValidChannel) and (iBone < len(boneNameList))):
524                                if (linkedActionIpoDict.keys().count(boneNameList[iBone]) == 1):
525                                        hasValidChannel = 1 # true
526                                else:
527                                        iBone += 1
528                        if hasValidChannel:
529                                # add action
530                                armatureActionDict[linkedAction.getName()] = ArmatureAction(linkedAction.getName(), linkedActionIpoDict)
531                ## get other actions != linked action
532                actionDict = Blender.Armature.NLA.GetActions()
533                for actionName in actionDict.keys():
534                        # check if action is not linked action
535                        if actionDict[actionName] is not linkedAction:
536                                # check all bones
537                                actionIpoDict = actionDict[actionName].getAllChannelIpos()
538                                hasValidChannel = 0 # false
539                                iBone = 0
540                                while ((not hasValidChannel) and (iBone < len(boneNameList))):
541                                        if (actionIpoDict.keys().count(boneNameList[iBone]) == 1):
542                                                hasValidChannel = 1 # true
543                                        else:
544                                                iBone += 1
545                                if hasValidChannel:
546                                        # add action
547                                        armatureActionDict[actionName] = ArmatureAction(actionName, actionIpoDict)
548                return armatureActionDict
549        createArmatureActionDict = staticmethod(createArmatureActionDict)
550
551class ArmatureActionActuator:
552        """Resembles Blender's action actuators.
553       
554           @author Michael Reimpell
555        """
556        def __init__(self, name, startFrame, endFrame, armatureAction):
557                """Constructor
558                   
559                   @param name           Animation name
560                   @param startFrame     first frame of the animation
561                   @param endFrame       last frame of the animation
562                   @param armatureAction ArmatureAction object of the animation
563                """
564                self.name = name
565                self.startFrame = startFrame
566                self.endFrame = endFrame
567                self.armatureAction = armatureAction
568                return
569
570class ArmatureActionActuatorListView:
571        """Mangages a list of ArmatureActionActuators.
572           <ul>
573           <li> import Blender
574           <li> call eventFilter and buttonFilter in registered callbacks
575           </ul>
576           
577           @author Michael Reimpell
578        """
579        def __init__(self, armatureActionDict, maxActuators, buttonEventRangeStart, armatureAnimationDictList=None):
580                """Constructor.
581                   
582                   @param armatureActionDict        possible armature actuator actions
583                   @param maxActuators              maximal number of actuator list elements
584                   @param buttonEventRangeStart     first button event number.
585                                                    The number of used event numbers is (3 + maxActuators*5)
586                   @param armatureAnimationDictList list of armature animations (see getArmatureAnimationDictList())
587                """
588                self.armatureActionDict = armatureActionDict
589                self.maxActuators = maxActuators
590                self.buttonEventRangeStart = buttonEventRangeStart
591                self.armatureActionActuatorList = []
592                self.armatureActionMenuList = []
593                self.startFrameNumberButtonList = []
594                self.endFrameNumberButtonList = []
595                self.animationNameStringButtonList = []
596                # scrollbar values:
597                #   0:(len(self.armatureActionActuatorList)-1) = listIndex
598                #   len(self.armatureActionActuatorList) = addbuttonline
599                self.scrollbar = ReplacementScrollbar(0,0,0, self.buttonEventRangeStart+1, self.buttonEventRangeStart+2)
600                if armatureAnimationDictList is not None:
601                        # rebuild ArmatureActionActuators for animationList animations
602                        for animationDict in armatureAnimationDictList:
603                                # check if Action is available
604                                if self.armatureActionDict.has_key(animationDict['actionKey']):
605                                        armatureActionActuator = ArmatureActionActuator(animationDict['name'], \
606                                                                                        animationDict['startFrame'], \
607                                                                                        animationDict['endFrame'], \
608                                                                                        self.armatureActionDict[animationDict['actionKey']])
609                                        self._addArmatureActionActuator(armatureActionActuator)
610                else:
611                        # create default ArmatureActionActuators
612                        for armatureAction in self.armatureActionDict.values():
613                                # add default action
614                                armatureActionActuator = ArmatureActionActuator(armatureAction.name, armatureAction.firstKeyFrame, armatureAction.lastKeyFrame, armatureAction)
615                                self._addArmatureActionActuator(armatureActionActuator)
616                return
617               
618        def refresh(self, armatureActionDict):
619                """Delete ArmatureActionActuators for removed Actions.
620                   
621                   @param armatureActionDict possible ArmatureActuator actions
622                """
623                self.armatureActionDict = armatureActionDict
624                # delete ArmatureActionActuators for removed Actions
625                for armatureActionActuator in self.armatureActionActuatorList[:]:
626                        # check if action is still available
627                        if not self.armatureActionDict.has_key(armatureActionActuator.armatureAction.name):
628                                # remove armatureActionActuator from lists
629                                listIndex = self.armatureActionActuatorList.index(armatureActionActuator)
630                                self._deleteArmatureActionActuator(listIndex)
631                Blender.Draw.Redraw(1)
632                return
633               
634        def draw(self, x, y, width, height):
635                """draw actuatorList
636                   use scrollbar if needed
637                """
638                # black border
639                minX = x
640                minY = y
641                maxX = x + width
642                maxY = y + height
643                minWidth = 441
644                if ((width - 5) > minWidth):
645                        glColor3f(0.0,0.0,0.0)
646                        glRectf(minX, minY, maxX - 22, maxY)
647                        glColor3f(0.6,0.6,0.6) # Background: grey
648                        glRectf(minX + 1, minY + 1, maxX - 23, maxY - 1)
649                        x += 3
650                        y += 3
651                        width -= 5
652                        height -= 6
653                else:
654                        print "ArmatureActionActuatorListView draw size to small!"
655                        glColor3f(0.0,0.0,0.0)
656                        glRectf(minX, minY, maxX, maxY)
657                        glColor3f(0.6,0.6,0.6) # Background: grey
658                        glRectf(minX + 1, minY + 1, maxX, maxY - 1)
659                        x += 3
660                        y += 3
661                        width -= 5
662                        height -= 6
663                # Layout:
664                # |---- 105 ---|2|----80---|2|---80---|2|---- >80 ----|2|---60---|2|----20---|
665                # actionName   | startFrame | endFrame | animationName | [delete] | scrollbar
666                # [ add ]                                                         | scrollbar
667                if (len(self.armatureActionDict.keys()) > 0):
668                        # construct actionMenu name
669                        menuValue = 0
670                        menuName = ""
671                        for key in self.armatureActionDict.keys():
672                                menuName += key + " %x" + ("%d" % menuValue) + "|"
673                                menuValue +=1
674                        # first line
675                        lineY = y + height - 20
676                        lineX = x
677                        listIndex = self.scrollbar.getCurrentValue()
678                        while ((listIndex < len(self.armatureActionActuatorList)) and (lineY >= y)):
679                                # still armatureActionActuators left to draw
680                                lineX = x
681                                armatureActionActuator = self.armatureActionActuatorList[listIndex]
682                                # draw actionMenu
683                                event = self.buttonEventRangeStart + 3 + listIndex
684                                menuValue = self.armatureActionDict.keys().index(armatureActionActuator.armatureAction.name)
685                                self.armatureActionMenuList[listIndex] = Blender.Draw.Menu(menuName,event, x, lineY, 105, 20, menuValue, "Action name")
686                                lineX += 107
687                                # draw startFrameNumberButton
688                                event = self.buttonEventRangeStart + 3 + self.maxActuators + listIndex
689                                self.startFrameNumberButtonList[listIndex] = Blender.Draw.Number("Sta: ", event, lineX, lineY, 80, 20, \
690                                                                         armatureActionActuator.startFrame, -18000, 18000, "Start frame")
691                                lineX += 82
692                                # draw endFrameNumberButton
693                                event = self.buttonEventRangeStart + 3 + 2*self.maxActuators + listIndex
694                                self.endFrameNumberButtonList[listIndex] = Blender.Draw.Number("End: ", event, lineX, lineY, 80, 20, \
695                                                                         armatureActionActuator.endFrame, -18000, 18000, "End frame")
696                                lineX += 82
697                                # compute animationNameWidht
698                                animationNameWidth = width - 271 - 85
699                                if (animationNameWidth < 80):
700                                        animationNameWidth = 80
701                                # draw animationNameStringButton
702                                event = self.buttonEventRangeStart + 3 + 3*self.maxActuators + listIndex
703                                self.animationNameStringButtonList[listIndex] = Blender.Draw.String("",event, lineX, lineY, animationNameWidth, 20, \
704                                                                                armatureActionActuator.name, 1000, "Animation export name")
705                                lineX += animationNameWidth + 2
706                                # draw deleteButton
707                                event = self.buttonEventRangeStart + 3 + 4*self.maxActuators + listIndex
708                                Draw.Button("Delete", event, lineX, lineY, 60, 20, "Delete export animation")
709                                lineX += 62
710                                # inc line
711                                lineY -= 22
712                                listIndex += 1
713                        # draw add button
714                        if (lineY >= y):
715                                Draw.Button("Add", self.buttonEventRangeStart, x, lineY, 60, 20, "Add new export animation")
716                # draw scrollbar
717                if (width > minWidth):
718                        # align left
719                        self.scrollbar.draw(maxX - 20, minY, 20, (maxY - minY))
720                return
721       
722        def eventFilter(self, event, value):
723                """event filter for keyboard and mouse input events
724                   call it inside the registered event function
725                """
726                self.scrollbar.eventFilter(event,value)
727                return
728               
729        def buttonFilter(self, event):
730                """button filter for Draw Button events
731                   call it inside the registered button function
732                """
733                # button numbers = self.buttonEventRangeStart + buttonNumberOffset
734                # buttonNumberOffsets:
735                # addButton: 0
736                # scrollbar: 1 and 2
737                # actionMenu range: 3 <= event < 3 + maxActuators
738                # startFrameNumberButton range:  3 + maxActuators <= event < 3 + 2*maxActuators
739                # endFrameNumberButton range: 3 + 2*maxActuators <= event < 3 + 3*maxActuators
740                # animationNameStringButton range: 3 + 3*maxActuators <= event < 3 + 4*maxActuators
741                # deleteButton range: 3 + 4*maxActuators <= event < 3 + 5*maxActuators
742                self.scrollbar.buttonFilter(event)
743                relativeEvent = event - self.buttonEventRangeStart
744                if (relativeEvent == 0):
745                        # add button pressed
746                        if (len(self.armatureActionDict.keys()) > 0):
747                                # add default ArmatureActionActuator
748                                armatureAction = self.armatureActionDict[self.armatureActionDict.keys()[0]]
749                                armatureActionActuator = ArmatureActionActuator(armatureAction.name, armatureAction.firstKeyFrame, armatureAction.lastKeyFrame, armatureAction)
750                                self._addArmatureActionActuator(armatureActionActuator)
751                                Blender.Draw.Redraw(1)
752                elif ((3 <= relativeEvent) and (relativeEvent < (3 + self.maxActuators))):
753                        # actionMenu
754                        listIndex = relativeEvent - 3
755                        armatureActionActuator = self.armatureActionActuatorList[listIndex]
756                        # button value is self.actionDict.keys().index
757                        keyIndex = self.armatureActionMenuList[listIndex].val
758                        key = self.armatureActionDict.keys()[keyIndex]
759                        armatureActionActuator.armatureAction = self.armatureActionDict[key]
760                        armatureActionActuator.startFrame = self.armatureActionDict[key].firstKeyFrame
761                        armatureActionActuator.endFrame = self.armatureActionDict[key].lastKeyFrame
762                        armatureActionActuator.name = self.armatureActionDict[key].name
763                        self.armatureActionActuatorList[listIndex] = armatureActionActuator
764                        Blender.Draw.Redraw(1)
765                elif (((3 + self.maxActuators) <= relativeEvent) and (relativeEvent < (3 + 2*self.maxActuators))):
766                        # startFrameNumberButton
767                        listIndex = relativeEvent - (3 + self.maxActuators)
768                        armatureActionActuator = self.armatureActionActuatorList[listIndex]
769                        armatureActionActuator.startFrame = self.startFrameNumberButtonList[listIndex].val
770                        self.armatureActionActuatorList[listIndex] = armatureActionActuator
771                elif (((3 + 2*self.maxActuators) <= relativeEvent) and (relativeEvent < (3 + 3*self.maxActuators))):
772                        # endFrameNumberButton
773                        listIndex = relativeEvent - (3 + 2*self.maxActuators)
774                        armatureActionActuator = self.armatureActionActuatorList[listIndex]
775                        armatureActionActuator.endFrame = self.endFrameNumberButtonList[listIndex].val
776                        self.armatureActionActuatorList[listIndex] = armatureActionActuator
777                elif (((3 + 3*self.maxActuators) <= relativeEvent) and (relativeEvent < (3 + 4*self.maxActuators))):
778                        # animationNameStringButton
779                        listIndex = relativeEvent - (3 + 3*self.maxActuators)
780                        armatureActionActuator = self.armatureActionActuatorList[listIndex]
781                        armatureActionActuator.name = self.animationNameStringButtonList[listIndex].val
782                        self.armatureActionActuatorList[listIndex] = armatureActionActuator
783                elif (((3 + 4*self.maxActuators) <= relativeEvent) and (relativeEvent < (3 + 5*self.maxActuators))):
784                        # deleteButton
785                        listIndex = relativeEvent - (3 + 4*self.maxActuators)
786                        self._deleteArmatureActionActuator(listIndex)
787                        Blender.Draw.Redraw(1)
788                return
789               
790        def getArmatureAnimationDictList(self):
791                """serialize the armatureActionActuatorList into a pickle storable list
792                   Each item of the returned list is a dictionary with key-value pairs:
793                   <ul>
794                   <li> name - ArmatureActionActuator.name
795                   <li> startFrame - ArmatureActionActuator.startFrame
796                   <li> endFrame - ArmatureActionActuator.endFrame
797                   <li> armatureActionKey - ArmatureActionActuator.armatureAction.name
798                   </ul>
799                   
800                   @return serialized actionActuatorList
801                """
802                animationDictList = []
803                for armatureActionActuator in self.armatureActionActuatorList:
804                        # create animationDict
805                        animationDict = {}
806                        animationDict['name'] = armatureActionActuator.name
807                        animationDict['startFrame'] = armatureActionActuator.startFrame
808                        animationDict['endFrame'] = armatureActionActuator.endFrame
809                        animationDict['actionKey'] = armatureActionActuator.armatureAction.name
810                        animationDictList.append(animationDict)
811                return animationDictList
812               
813        def setAnimationDictList(self, animationDictList):
814                """loads old AnimationDictList with actionKey = (ipoPrefix, ipoPostfix)
815                   
816                   @see #getArmatureAnimationDictList()
817                """
818                # rebuild ArmatureActionActuators for animationList animations
819                for animationDict in animationDictList:
820                        # check if Action is available
821                        prefix, postfix = animationDict['actionKey']
822                        armatureActionName = prefix+postfix
823                        if self.armatureActionDict.has_key(armatureActionName):
824                                armatureActionActuator = ArmatureActionActuator(animationDict['name'], \
825                                                                                animationDict['startFrame'], \
826                                                                                animationDict['endFrame'], \
827                                                                                self.armatureActionDict[armatureActionName])
828                                self._addArmatureActionActuator(armatureActionActuator)
829                return
830               
831        # private methods
832        def _addArmatureActionActuator(self, armatureActionActuator):
833                """adds an ArmatureActionActuator to the list
834                   <ul>
835                   <li> call Blender.Draw.Redraw(1) afterwards
836                   </ul>
837                """
838                if (len(self.armatureActionActuatorList) < self.maxActuators):
839                        # check if armatureActionActuator.action is available
840                        if armatureActionActuator.armatureAction.name in self.armatureActionDict.keys():
841                                # create armatureActionMenu
842                                # get ArmatureAction index in armatureActionDict.keys() list
843                                armatureActionMenu = Draw.Create(self.armatureActionDict.keys().index(armatureActionActuator.armatureAction.name))
844                                self.armatureActionMenuList.append(armatureActionMenu)
845                                # create startFrameNumberButton
846                                startFrameNumberButton = Draw.Create(int(armatureActionActuator.startFrame))
847                                self.startFrameNumberButtonList.append(startFrameNumberButton)
848                                # create endFrameNumberButton
849                                endFrameNumberButton = Draw.Create(int(armatureActionActuator.endFrame))
850                                self.endFrameNumberButtonList.append(endFrameNumberButton)
851                                # create animationNameStringButton
852                                animationNameStringButton = Draw.Create(armatureActionActuator.name)
853                                self.animationNameStringButtonList.append(animationNameStringButton)
854                                # append to armatureActionActuatorList
855                                self.armatureActionActuatorList.append(armatureActionActuator)
856                                # adjust scrollbar
857                                scrollbarPosition = self.scrollbar.getCurrentValue()
858                                self.scrollbar = ReplacementScrollbar(scrollbarPosition,0,len(self.armatureActionActuatorList), self.buttonEventRangeStart+1, self.buttonEventRangeStart+2)
859                                # TODO: change scrollbarPosition in a way, such that the new actuator is visible
860                        else:
861                                print "Error: Could not add ArmatureActionActuator because ArmatureAction is not available!"
862                return
863               
864        def _deleteArmatureActionActuator(self, listIndex):
865                """removes an ArmatureActionActuator from the list
866                   <ul>
867                   <li> call Blender.Draw.Redraw(1) afterwards
868                   </ul>
869                """
870                # check listIndex
871                if ((len(self.armatureActionActuatorList) > 0) and (listIndex >= 0) and (listIndex < len(self.armatureActionActuatorList))):
872                        # remove armatureActionMenu
873                        self.armatureActionMenuList.pop(listIndex)
874                        # remove startFrameNumberButton
875                        self.startFrameNumberButtonList.pop(listIndex)
876                        # remove endFrameNumberButton
877                        self.endFrameNumberButtonList.pop(listIndex)
878                        # remove animationNameStringButton
879                        self.animationNameStringButtonList.pop(listIndex)
880                        # remove armatureActionActuator
881                        self.armatureActionActuatorList.pop(listIndex)
882                        # adjust scrollbar
883                        scrollbarPosition = self.scrollbar.getCurrentValue()
884                        if (scrollbarPosition > len(self.armatureActionActuatorList)):
885                                scrollbarPosition = len(self.armatureActionActuatorList)
886                        self.scrollbar = ReplacementScrollbar(scrollbarPosition,0,len(self.armatureActionActuatorList), self.buttonEventRangeStart+1, self.buttonEventRangeStart+2)
887                        return
888
889class Logger:
890        """Logs messages and status.
891       
892           Logs messages as a list of strings and keeps track of the status.
893           Possible status values are info, warning and error.
894           
895           @cvar INFO info status
896           @cvar WARNING warning status
897           @cvar ERROR error status
898        """
899        INFO, WARNING, ERROR = range(3)
900        def __init__(self):
901                """Constructor.
902                """
903                self.messageList = []
904                self.status = Logger.INFO
905                return
906        def logInfo(self, message):
907                """Logs an info message.
908               
909                   @param message message string
910                """
911                self.messageList.append((Logger.INFO, message))
912                return         
913        def logWarning(self, message):
914                """Logs a warning message.
915               
916                   The status is set to <code>Logger.WARNING</code> if it is not already <code>Logger.ERROR</code>.
917                   
918                   @param message message string
919                """
920                self.messageList.append((Logger.WARNING, "Warning: "+message))
921                if not self.status == Logger.ERROR:
922                        self.status = Logger.WARNING
923                return
924        def logError(self, message):
925                """Logs an error message.
926               
927                   The status is set to <code>Logger.ERROR</code>.
928                   
929                   @param message message string
930                """
931                self.messageList.append((Logger.ERROR, "Error: "+message))
932                self.status = Logger.ERROR
933                return
934        def getStatus(self):
935                """Gets the current status.
936               
937                   The status can be
938                   <ul>
939                   <li><code>Logger.INFO</code>
940                   <li><code>Logger.WARNING</code>
941                   <li><code>Logger.ERROR</code>
942                   </ul>
943                   
944                   @return status
945                """
946                return self.status
947        def getMessageList(self):
948                """Returns the list of log messages.
949               
950                   @return list of tuples (status, message)
951                """
952                return self.messageList
953
954class LogInterface:
955        def __init__(self):
956                self.loggerList = []
957        def addLogger(self, logger):
958                self.loggerList.append(logger)
959                return
960        def removeLogger(self, logger):
961                self.loggerList.remove(logger)
962                return
963        # protected
964        def _logInfo(self, message):
965                for logger in self.loggerList:
966                        logger.logInfo(message)
967                return
968        def _logWarning(self, message):
969                for logger in self.loggerList:
970                        logger.logWarning(message)
971                return
972        def _logError(self, message):
973                for logger in self.loggerList:
974                        logger.logWarning(message)
975                return
976
977class PathName(LogInterface):
978        """Splits a pathname independent of the underlying os.
979       
980           Blender saves pathnames in the os specific manner. Using os.path may result in problems
981           when the export is done on a different os than the creation of the .blend file.         
982        """
983        def __init__(self, pathName):
984                self.pathName = pathName
985                LogInterface.__init__(self)
986                return
987        def dirname(self):
988                return os.path.dirname(self.pathName)
989        def basename(self):
990                baseName = os.path.basename(self.pathName)
991                # split from non-os directories
992                # \\
993                baseName = baseName.split('\\').pop()
994                # /
995                baseName = baseName.split('/').pop()
996                if (baseName != baseName.replace(' ','_')):
997                        # replace whitespace with underscore
998                        self._logWarning("Whitespaces in filename \"%s\" replaced with underscores." % baseName)
999                        baseName = baseName.replace(' ','_')
1000                return baseName
1001        def path(self):
1002                return self.pathName
1003
1004class ExportOptions:
1005        """Encapsulates export options common to all objects.
1006        """
1007        # TODO: Model for GUI
1008        def __init__(self, rotXAngle, rotYAngle, rotZAngle, scale, useWorldCoordinates, colouredAmbient, exportPath, materialFilename):
1009                """Constructor.
1010                """
1011                # floating point accuracy
1012                self.accuracy = 1e-6
1013                # export transformation
1014                self.rotXAngle = rotXAngle
1015                self.rotYAngle = rotYAngle
1016                self.rotZAngle = rotZAngle
1017                self.scale = scale
1018                self.useWorldCoordinates = useWorldCoordinates
1019                self.colouredAmbient = colouredAmbient
1020                # file settings
1021                self.exportPath = exportPath
1022                self.materialFilename = materialFilename
1023                return
1024       
1025        def transformationMatrix(self):
1026                """Returns the matrix representation for the additional transformation on export.
1027                """
1028                rotationMatrix = Mathutils.RotationMatrix(self.rotXAngle,4,'x')
1029                rotationMatrix *= Mathutils.RotationMatrix(self.rotYAngle,4,'y')
1030                rotationMatrix *= Mathutils.RotationMatrix(self.rotZAngle,4,'z')
1031                scaleMatrix = Mathutils.Matrix([self.scale,0,0],[0,self.scale,0],[0,0,self.scale])
1032                scaleMatrix.resize4x4()
1033                return rotationMatrix*scaleMatrix
1034
1035class ObjectExporter:
1036        """Interface. Exports a Blender object to Ogre.
1037        """
1038        def __init__(self, object):
1039                """Constructor.
1040                   
1041                   @param object Blender object to export.
1042                """
1043                self.object = object
1044                return
1045       
1046        def getName(self):
1047                """Returns the name of the object.
1048                """
1049                return self.object.getName()
1050       
1051        def getObjectMatrix(self):
1052                """Returns the object matrix in worldspace.
1053                """
1054                return self.object.getMatrix('worldspace')
1055       
1056class MeshExporter(ObjectExporter):
1057        """
1058        """
1059        def getName(self):
1060                return self.object.getData().name
1061               
1062class ArmatureExporter:
1063        """Exports an armature of a mesh.
1064        """
1065        # TODO: Provide bone ids for vertex influences.
1066        def __init__(self, meshObject, armatureObject):
1067                """Constructor.
1068               
1069                  @param meshObject ObjectExporter.
1070                  @param armatureObject Blender armature object.
1071                """
1072                self.meshObject = meshObject
1073                self.armatureObject = armatureObject
1074                self.skeleton = None
1075                return
1076       
1077        def export(self, actionActuatorList, exportOptions, logger):
1078                """Exports the armature.
1079               
1080                   @param actionActuatorList list of animations to export.
1081                   @param exportOptions global export options.
1082                   @param logger Logger Logger for log messages.                   
1083                """
1084                # convert Armature into Skeleton
1085                name = None
1086                if exportOptions.useWorldCoordinates:
1087                        name = self.armatureObject.getData().getName()
1088                else:
1089                        name = self.meshObject.getName() + "-" + self.armatureObject.getData().getName()
1090                skeleton = Skeleton(name)
1091                skeleton = self._convertRestpose(skeleton, exportOptions, logger)
1092               
1093                # convert ActionActuators into Animations
1094                self._convertAnimations(skeleton, actionActuatorList, exportOptions, exportLogger)
1095               
1096                # write to file
1097                self._toFile(skeleton, exportOptions, exportLogger)
1098               
1099                self.skeleton = skeleton
1100                return
1101       
1102        def _convertAnimations(self, skeleton, armatureActionActuatorList, exportOptions, exportLogger):
1103                """Converts ActionActuators to Ogre animations.
1104                """
1105                # frames per second
1106                fps = Blender.Scene.GetCurrent().getRenderingContext().framesPerSec()
1107                # map armatureActionActuatorList to skeleton.animationsDict
1108                for armatureActionActuator in armatureActionActuatorList:
1109                        # map armatureActionActuator to animation
1110                        if (not skeleton.animationsDict.has_key(armatureActionActuator.name)):
1111                                # create animation
1112                                animation = Animation(armatureActionActuator.name)
1113                                # map bones to tracks
1114                                for boneName in armatureActionActuator.armatureAction.ipoDict.keys():
1115                                        if (not(animation.tracksDict.has_key(boneName))):
1116                                                # get bone object
1117                                                if skeleton.bonesDict.has_key(boneName):
1118                                                        # create track
1119                                                        track = Track(animation, skeleton.bonesDict[boneName])
1120                                                        # map ipocurves to keyframes
1121                                                        # get ipo for that bone
1122                                                        ipo = armatureActionActuator.armatureAction.ipoDict[boneName]
1123                                                        # map curve names to curvepos
1124                                                        curveId = {}
1125                                                        index = 0
1126                                                        have_quat = 0
1127                                                        for curve in ipo.getCurves():
1128                                                                try:
1129                                                                        name = curve.getName()
1130                                                                        if (name == "LocX" or name == "LocY" or name == "LocZ" or \
1131                                                                        name == "SizeX" or name == "SizeY" or name == "SizeZ" or \
1132                                                                        name == "QuatX" or name == "QuatY" or name == "QuatZ" or name == "QuatW"):
1133                                                                                curveId[name] = index
1134                                                                                index += 1
1135                                                                        else:
1136                                                                        # bug: 2.28 does not return "Quat*"...
1137                                                                                if not have_quat:
1138                                                                                        curveId["QuatX"] = index
1139                                                                                        curveId["QuatY"] = index+1
1140                                                                                        curveId["QuatZ"] = index+2
1141                                                                                        curveId["QuatW"] = index+3
1142                                                                                        index += 4
1143                                                                                        have_quat = 1
1144                                                                except TypeError:
1145                                                                        # blender 2.32 does not implement IpoCurve.getName() for action Ipos
1146                                                                        if not have_quat:
1147                                                                                # no automatic assignments so far
1148                                                                                # guess Ipo Names       
1149                                                                                nIpoCurves = ipo.getNcurves()
1150                                                                                if nIpoCurves in [4,7,10]:
1151                                                                                        exportLogger.logWarning("IpoCurve.getName() not available!")
1152                                                                                        exportLogger.logWarning("The exporter tries to guess the IpoCurve names.")
1153                                                                                        if (nIpoCurves >= 7):
1154                                                                                                # not only Quats
1155                                                                                                # guess: Quats and Locs
1156                                                                                                curveId["LocX"] = index
1157                                                                                                curveId["LocY"] = index+1
1158                                                                                                curveId["LocZ"] = index+2
1159                                                                                                index += 3     
1160                                                                                        if (nIpoCurves == 10):
1161                                                                                                # all possible Action IpoCurves
1162                                                                                                curveId["SizeX"] = index
1163                                                                                                curveId["SizeY"] = index+1
1164                                                                                                curveId["SizeZ"] = index+2
1165                                                                                                index += 3
1166                                                                                        if (nIpoCurves >= 4):
1167                                                                                                # at least 4 IpoCurves
1168                                                                                                # guess: 4 Quats
1169                                                                                                curveId["QuatX"] = index
1170                                                                                                curveId["QuatY"] = index+1
1171                                                                                                curveId["QuatZ"] = index+2
1172                                                                                                curveId["QuatW"] = index+3
1173                                                                                                index += 4
1174                                                                                        have_quat = 1
1175                                                                                else:
1176                                                                                        exportLogger.logError("IpoCurve.getName() not available!")
1177                                                                                        exportLogger.logError("Could not guess the IpoCurve names. Other Blender versions may work.")
1178                                                        # get all frame numbers between startFrame and endFrame where this ipo has a point in one of its curves
1179                                                        frameNumberDict = {}
1180                                                        for curveIndex in range(ipo.getNcurves()):
1181                                                                for bez in range(ipo.getNBezPoints(curveIndex)):
1182                                                                        frame = int(ipo.getCurveBeztriple(curveIndex, bez)[3])
1183                                                                        frameNumberDict[frame] = frame
1184                                                        frameNumberDict[armatureActionActuator.startFrame] = armatureActionActuator.startFrame
1185                                                        frameNumberDict[armatureActionActuator.endFrame] = armatureActionActuator.endFrame
1186                                                        # remove frame numbers not in the startFrame endFrame range
1187                                                        if (armatureActionActuator.startFrame > armatureActionActuator.endFrame):
1188                                                                minFrame = armatureActionActuator.endFrame
1189                                                                maxFrame = armatureActionActuator.startFrame
1190                                                        else:
1191                                                                minFrame = armatureActionActuator.startFrame
1192                                                                maxFrame = armatureActionActuator.endFrame
1193                                                        for frameNumber in frameNumberDict.keys()[:]:
1194                                                                if ((frameNumber < minFrame) or (frameNumber > maxFrame)):
1195                                                                        del frameNumberDict[frameNumber]
1196                                                        frameNumberList = frameNumberDict.keys()
1197                                                        # convert frame numbers to seconds
1198                                                        # frameNumberDict: key = export time, value = frame number
1199                                                        frameNumberDict = {}
1200                                                        for frameNumber in frameNumberList:
1201                                                                if  (armatureActionActuator.startFrame <= armatureActionActuator.endFrame):
1202                                                                        # forward animation
1203                                                                        time = float(frameNumber-armatureActionActuator.startFrame)/fps
1204                                                                else:
1205                                                                        # backward animation
1206                                                                        time = float(armatureActionActuator.endFrame-frameNumber)/fps
1207                                                                # update animation duration
1208                                                                if animation.duration < time:
1209                                                                        animation.duration = time
1210                                                                frameNumberDict[time] = frameNumber
1211                                                        # create key frames
1212                                                        timeList = frameNumberDict.keys()
1213                                                        timeList.sort()
1214                                                        for time in timeList:
1215                                                                # Blender's ordering of transformation is deltaR*deltaS*deltaT
1216                                                                # in the bones coordinate system.
1217                                                                frame = frameNumberDict[time]
1218                                                                loc = ( 0.0, 0.0, 0.0 )
1219                                                                rotQuat = Mathutils.Quaternion([1.0, 0.0, 0.0, 0.0])
1220                                                                sizeX = 1.0
1221                                                                sizeY = 1.0
1222                                                                sizeZ = 1.0
1223                                                                blenderLoc = [0, 0, 0]
1224                                                                hasLocKey = 0 #false
1225                                                                if curveId.has_key("LocX"):
1226                                                                        blenderLoc[0] = ipo.EvaluateCurveOn(curveId["LocX"], frame)
1227                                                                        hasLocKey = 1 #true
1228                                                                if curveId.has_key("LocY"):
1229                                                                        blenderLoc[1] = ipo.EvaluateCurveOn(curveId["LocY"], frame)
1230                                                                        hasLocKey = 1 #true
1231                                                                if curveId.has_key("LocZ"):
1232                                                                        blenderLoc[2] = ipo.EvaluateCurveOn(curveId["LocZ"], frame)
1233                                                                        hasLocKey = 1 #true
1234                                                                if hasLocKey:
1235                                                                        # Ogre's deltaT is in the bone's parent coordinate system
1236                                                                        loc = point_by_matrix(blenderLoc, skeleton.bonesDict[boneName].conversionMatrix)
1237                                                                if curveId.has_key("QuatX") and curveId.has_key("QuatY") and curveId.has_key("QuatZ") and curveId.has_key("QuatW"):
1238                                                                        if not (Blender.Get("version") == 234):
1239                                                                                rot = [ ipo.EvaluateCurveOn(curveId["QuatW"], frame), \
1240                                                                                                ipo.EvaluateCurveOn(curveId["QuatX"], frame), \
1241                                                                                                ipo.EvaluateCurveOn(curveId["QuatY"], frame), \
1242                                                                                                ipo.EvaluateCurveOn(curveId["QuatZ"], frame) ]
1243                                                                                rotQuat = Mathutils.Quaternion(rot)
1244                                                                        else:
1245                                                                                # Blender 2.34 quaternion naming bug
1246                                                                                rot = [ ipo.EvaluateCurveOn(curveId["QuatX"], frame), \
1247                                                                                                ipo.EvaluateCurveOn(curveId["QuatY"], frame), \
1248                                                                                                ipo.EvaluateCurveOn(curveId["QuatZ"], frame), \
1249                                                                                                ipo.EvaluateCurveOn(curveId["QuatW"], frame) ]
1250                                                                                rotQuat = Mathutils.Quaternion(rot)
1251                                                                if curveId.has_key("SizeX"):
1252                                                                        sizeX = ipo.EvaluateCurveOn(curveId["SizeX"], frame)
1253                                                                if curveId.has_key("SizeY"):
1254                                                                        sizeY = ipo.EvaluateCurveOn(curveId["SizeY"], frame)
1255                                                                if curveId.has_key("SizeZ"):
1256                                                                        sizeZ = ipo.EvaluateCurveOn(curveId["SizeZ"], frame)
1257                                                                size = (sizeX, sizeY, sizeZ)
1258                                                                KeyFrame(track, time, loc, rotQuat, size)
1259                                                        # append track
1260                                                        animation.tracksDict[boneName] = track
1261                                                else:
1262                                                        # ipo name contains bone but armature doesn't
1263                                                        exportLogger.logWarning("Unused action channel \"%s\" in action \"%s\" for skeleton \"%s\"." \
1264                                                                                         % (boneName, armatureActionActuator.armatureAction.name, skeleton.name))
1265                                        else:
1266                                                # track for that bone already exists
1267                                                exportLogger.logError("Ambiguous bone name \"%s\", track already exists." % boneName)
1268                                # append animation
1269                                skeleton.animationsDict[armatureActionActuator.name] = animation
1270                        else:
1271                                # animation export name already exists
1272                                exportLogger.logError("Ambiguous animation name \"%s\"." % armatureActionActuator.name)
1273                return
1274       
1275        def _convertRestpose(self, skeleton, exportOptions, logger):
1276                """Calculate inital bone positions and rotations.
1277                """
1278                obj = self.armatureObject
1279                stack = []
1280                matrix = None
1281                if exportOptions.useWorldCoordinates:
1282                        # world coordinates
1283                        matrix = obj.getMatrix("worldspace")
1284                else:
1285                        # local mesh coordinates
1286                        armatureMatrix = obj.getMatrix("worldspace")
1287                        inverseMeshMatrix = self.meshObject.getObjectMatrix()
1288                        inverseMeshMatrix.invert()
1289                        matrix = armatureMatrix*inverseMeshMatrix
1290                # apply additional export transformation
1291                matrix = matrix*exportOptions.transformationMatrix()
1292                loc = [ 0.0, 0, 0 ]
1293                matrix_one = Mathutils.Matrix([1.0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1])
1294                parent = None
1295               
1296                # get parent bones
1297                boneList = obj.getData().getBones()
1298                boneDict = {}
1299                for bone in boneList:
1300                        if not bone.hasParent():
1301                                boneDict[bone.getName()] = bone
1302                for bbone in boneDict.values(): 
1303                        stack.append([bbone, parent, matrix, loc, 0, matrix_one])
1304               
1305                while len(stack):
1306                        bbone, parent, accu_mat, parent_pos, parent_ds, invertedOgreTransformation = stack.pop()
1307                        # preconditions: (R : rotation, T : translation, S : scale, M: general transformation matrix)
1308                        #   accu_mat
1309                        #     points to the tail of the parents bone, i.e. for root bones
1310                        #     accu_mat = M_{object}*R_{additional on export}
1311                        #     and for child bones
1312                        #     accu_mat = T_{length of parent}*R_{parent}*T_{to head of parent}*M_{parent's parent}
1313                        #  invertedOgreTransformation
1314                        #    inverse of transformation done in Ogre so far, i.e. identity for root bones,
1315                        #    M^{-1}_{Ogre, parent's parent}*T^{-1}_{Ogre, parent}*R^{-1}_{Ogre, parent} for child bones.
1316                       
1317                        head = bbone.getHead()
1318                        tail = bbone.getTail()
1319                       
1320                        # get the restmat
1321                        R_bmat = bbone.getRestMatrix('bonespace').rotationPart()
1322                        R_bmat.resize4x4()
1323                       
1324                        # get the bone's root offset (in the parent's coordinate system)
1325                        T_root = [ [       1,       0,       0,      0 ],
1326                        [       0,       1,       0,      0 ],
1327                        [       0,       0,       1,      0 ],
1328                        [ head[0], head[1], head[2],      1 ] ]
1329                       
1330                        # get the bone length translation (length along y axis)
1331                        dx, dy, dz = tail[0] - head[0], tail[1] - head[1], tail[2] - head[2]
1332                        ds = math.sqrt(dx*dx + dy*dy + dz*dz)
1333                        T_len = [ [ 1,  0,  0,  0 ],
1334                                [ 0,  1,  0,  0 ],
1335                                [ 0,  0,  1,  0 ],
1336                                [ 0, ds,  0,  1 ] ]
1337                       
1338                        # calculate bone points in world coordinates
1339                        accu_mat = matrix_multiply(accu_mat, T_root)
1340                        pos = point_by_matrix([ 0, 0, 0 ], accu_mat)
1341                       
1342                        accu_mat = tmp_mat = matrix_multiply(accu_mat, R_bmat)
1343                        # tmp_mat = R_{bone}*T_{to head}*M_{parent}
1344                        accu_mat = matrix_multiply(accu_mat, T_len)
1345                       
1346                        # local rotation and distance from parent bone
1347                        if parent:
1348                                rotQuat = bbone.getRestMatrix('bonespace').toQuat()
1349                       
1350                        else:
1351                                rotQuat = (bbone.getRestMatrix('bonespace')*matrix).toQuat()
1352                       
1353                        x, y, z = pos
1354                        # pos = loc * M_{Ogre}
1355                        loc = point_by_matrix([x, y, z], invertedOgreTransformation)
1356                        x, y, z = loc
1357                        ogreTranslationMatrix = [[ 1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [x, y, z, 1]]
1358                       
1359                        # R_{Ogre} is either
1360                        # the rotation part of R_{bone}*T_{to_head}*M_{parent} for root bones or
1361                        # the rotation part of R_{bone}*T_{to_head} of child bones
1362                        ogreRotationMatrix = rotQuat.toMatrix()
1363                        ogreRotationMatrix.resize4x4()
1364                        invertedOgreTransformation = matrix_multiply(matrix_invert(ogreTranslationMatrix), invertedOgreTransformation)
1365                        parent = Bone(skeleton, parent, bbone.getName(), loc, rotQuat, matrix_multiply(invertedOgreTransformation, tmp_mat))
1366                        # matrix_multiply(invertedOgreTransformation, tmp_mat) is R*T*M_{parent} M^{-1}_{Ogre}T^{-1}_{Ogre}.
1367                        # Necessary, since Ogre's delta location is in the Bone's parent coordinate system, i.e.
1368                        # delatT_{Blender}*R*T*M = deltaT_{Ogre}*T_{Ogre}*M_{Ogre}
1369                        invertedOgreTransformation = matrix_multiply(matrix_invert(ogreRotationMatrix), invertedOgreTransformation)
1370                        for child in bbone.getChildren():
1371                                stack.append([child, parent, accu_mat, pos, ds, invertedOgreTransformation])
1372                return skeleton
1373               
1374        def _toFile(self, skeleton, exportOptions, exportLogger):
1375                """Writes converted skeleton to file.
1376                """
1377                file = skeleton.name+".skeleton.xml"
1378                exportLogger.logInfo("Skeleton \"%s\"" % file)
1379                f = open(os.path.join(exportOptions.exportPath, file), "w")
1380                f.write(tab(0)+"<skeleton>\n")
1381                f.write(tab(1)+"<bones>\n")
1382                for bone in skeleton.bones:
1383                        f.write(tab(2)+"<bone id=\"%d\" name=\"%s\">\n" % (bone.id, bone.name))
1384
1385                        x, y, z = bone.loc
1386                        f.write(tab(3)+"<position x=\"%.6f\" y=\"%.6f\" z=\"%.6f\"/>\n" % (x, y, z))
1387
1388                        f.write(tab(3)+"<rotation angle=\"%.6f\">\n" % (bone.rotQuat.angle/360*2*math.pi))
1389                        f.write(tab(4)+"<axis x=\"%.6f\" y=\"%.6f\" z=\"%.6f\"/>\n" % tuple(bone.rotQuat.axis))
1390                        f.write(tab(3)+"</rotation>\n")
1391                        f.write(tab(2)+"</bone>\n")
1392                f.write(tab(1)+"</bones>\n")
1393               
1394                f.write(tab(1)+"<bonehierarchy>\n")
1395                for bone in skeleton.bones:
1396                        parent = bone.parent
1397                        if parent:
1398                                f.write(tab(2)+"<boneparent bone=\"%s\" parent=\"%s\"/>\n" % (bone.name, parent.name))
1399                f.write(tab(1)+"</bonehierarchy>\n")
1400
1401                f.write(tab(1)+"<animations>\n")
1402
1403                for animation in skeleton.animationsDict.values():
1404                        name = animation.name
1405                       
1406                        f.write(tab(2)+"<animation")
1407                        f.write(" name=\"%s\"" % name)
1408                        f.write(" length=\"%f\">\n" % animation.duration )
1409                       
1410                        f.write(tab(3)+"<tracks>\n")
1411                        for track in animation.tracksDict.values():
1412                                f.write(tab(4)+"<track bone=\"%s\">\n" % track.bone.name)
1413                                f.write(tab(5)+"<keyframes>\n")
1414                               
1415                                for keyframe in track.keyframes:
1416                                        f.write(tab(6)+"<keyframe time=\"%f\">\n" % keyframe.time)
1417                                        x, y, z = keyframe.loc
1418                                        f.write(tab(7)+"<translate x=\"%.6f\" y=\"%.6f\" z=\"%.6f\"/>\n" % (x, y, z))
1419                                       
1420                                        f.write(tab(7)+"<rotate angle=\"%.6f\">\n" % (keyframe.rotQuat.angle/360*2*math.pi))
1421                                        f.write(tab(8)+"<axis x=\"%.6f\" y=\"%.6f\" z=\"%.6f\"/>\n" % tuple(keyframe.rotQuat.axis))
1422                                        f.write(tab(7)+"</rotate>\n")
1423                                       
1424                                        f.write(tab(7)+"<scale x=\"%f\" y=\"%f\" z=\"%f\"/>\n" % keyframe.scale)
1425                                       
1426                                        f.write(tab(6)+"</keyframe>\n")
1427                               
1428                                f.write(tab(5)+"</keyframes>\n")
1429                                f.write(tab(4)+"</track>\n");
1430                       
1431                        f.write(tab(3)+"</tracks>\n");
1432                        f.write(tab(2)+"</animation>\n")
1433                       
1434                f.write(tab(1)+"</animations>\n")
1435                f.write(tab(0)+"</skeleton>\n")
1436                f.close()
1437                convertXMLFile(os.path.join(exportOptions.exportPath, file))
1438                return
1439               
1440class ArmatureMeshExporter(ObjectExporter):
1441        """Exports an armature object as mesh.
1442       
1443           Converts a Blender armature into an animated Ogre mesh.
1444        """
1445        # TODO: Use observer pattern for progress bar.
1446        # TODO: Get bone ids from skeletonExporter class.
1447        def __init__(self, armatureObject):
1448                """Constructor.
1449               
1450                   @param armatureObject armature object to export.
1451                """
1452                # call base class constructor
1453                ObjectExporter.__init__(self, armatureObject)
1454                self.skeleton = None
1455                return
1456       
1457        def export(self, materialsDict, actionActuatorList, exportOptions, logger):
1458                """Exports the mesh object.
1459                   
1460                   @param materialsDict dictionary that contains already existing materials.
1461                   @param actionActuatorList list of animations to export.
1462                   @param exportOptions global export options.
1463                   @param logger Logger for log messages.
1464                   @return materialsDict with the new materials added.
1465                """
1466                # export skeleton
1467                armatureExporter = ArmatureExporter(self, self.object)
1468                armatureExporter.export(actionActuatorList, exportOptions, logger)
1469                self.skeleton = armatureExporter.skeleton
1470                self._convertToMesh(materialsDict, exportOptions, logger)
1471                return materialsDict
1472        def getName(self):
1473                return self.object.getData().getName()
1474       
1475        def _convertToMesh(self, materialsDict, exportOptions, logger):
1476                """Creates meshes in form of the armature bones.
1477                """
1478                obj = self.object
1479                stack = []
1480                # list of bone data (boneName, startPosition, endPosition)
1481                boneMeshList = []               
1482                matrix = None
1483                if exportOptions.useWorldCoordinates:
1484                        # world coordinates
1485                        matrix = obj.getMatrix("worldspace")
1486                else:
1487                        # local mesh coordinates
1488                        armatureMatrix = obj.getMatrix("worldspace")
1489                        inverseMeshMatrix = self.getObjectMatrix()
1490                        inverseMeshMatrix.invert()
1491                        matrix = armatureMatrix*inverseMeshMatrix
1492                # apply additional export transformation
1493                matrix = matrix*exportOptions.transformationMatrix()
1494                loc = [ 0.0, 0, 0 ]
1495                parent = None
1496               
1497                # get parent bones
1498                boneList = obj.getData().getBones()
1499                boneDict = {}
1500                for bone in boneList:
1501                        if not bone.hasParent():
1502                                boneDict[bone.getName()] = bone
1503                for bbone in boneDict.values(): 
1504                        stack.append([bbone, parent, matrix, loc, 0])
1505               
1506                while len(stack):
1507                        bbone, parent, accu_mat, parent_pos, parent_ds = stack.pop()
1508                        # preconditions: (R : rotation, T : translation, S : scale, M: general transformation matrix)
1509                        #   accu_mat
1510                        #     points to the tail of the parents bone, i.e. for root bones
1511                        #     accu_mat = M_{object}*R_{additional on export}
1512                        #     and for child bones
1513                        #     accu_mat = T_{length of parent}*R_{parent}*T_{to head of parent}*M_{parent's parent}
1514                        #  invertedOgreTransformation
1515                        #    inverse of transformation done in Ogre so far, i.e. identity for root bones,
1516                        #    M^{-1}_{Ogre, parent's parent}*T^{-1}_{Ogre, parent}*R^{-1}_{Ogre, parent} for child bones.
1517                       
1518                        head = bbone.getHead()
1519                        tail = bbone.getTail()
1520                       
1521                        # get the restmat
1522                        R_bmat = bbone.getRestMatrix('bonespace').rotationPart()
1523                        R_bmat.resize4x4()
1524                       
1525                        # get the bone's root offset (in the parent's coordinate system)
1526                        T_root = [ [       1,       0,       0,      0 ],
1527                        [       0,       1,       0,      0 ],
1528                        [       0,       0,       1,      0 ],
1529                        [ head[0], head[1], head[2],      1 ] ]
1530                       
1531                        # get the bone length translation (length along y axis)
1532                        dx, dy, dz = tail[0] - head[0], tail[1] - head[1], tail[2] - head[2]
1533                        ds = math.sqrt(dx*dx + dy*dy + dz*dz)
1534                        T_len = [ [ 1,  0,  0,  0 ],
1535                                [ 0,  1,  0,  0 ],
1536                                [ 0,  0,  1,  0 ],
1537                                [ 0, ds,  0,  1 ] ]
1538                       
1539                        # calculate bone points in world coordinates
1540                        accu_mat = matrix_multiply(accu_mat, T_root)
1541                        pos = point_by_matrix([ 0, 0, 0 ], accu_mat)
1542                       
1543                        accu_mat = tmp_mat = matrix_multiply(accu_mat, R_bmat)
1544                        # tmp_mat = R_{bone}*T_{to head}*M_{parent}
1545                        accu_mat = matrix_multiply(accu_mat, T_len)
1546                        pos2 = point_by_matrix([ 0, 0, 0 ], accu_mat)
1547                        boneMeshList.append([bbone.getName(), pos, pos2])
1548                        for child in bbone.getChildren():
1549                                stack.append([child, parent, accu_mat, pos, ds])
1550                self._createMeshFromBoneList(materialsDict, boneMeshList)
1551                return
1552
1553        def _makeFace(self, submesh, name, p1, p2, p3):
1554                normal = vector_normalize(vector_crossproduct(
1555                                [ p3[0] - p2[0], p3[1] - p2[1], p3[2] - p2[2] ],
1556                                [ p1[0] - p2[0], p1[1] - p2[1], p1[2] - p2[2] ]))
1557                v1 = Vertex(submesh, XMLVertex(p1, normal))
1558                v2 = Vertex(submesh, XMLVertex(p2, normal))
1559                v3 = Vertex(submesh, XMLVertex(p3, normal))
1560
1561                id = self.skeleton.bonesDict[name]
1562                v1.influences.append(Influence(id, 1.0))
1563                v2.influences.append(Influence(id, 1.0))
1564                v3.influences.append(Influence(id, 1.0))
1565
1566                Face(submesh, v1, v2, v3)
1567                return
1568       
1569        def _createMeshFromBoneList(self, materialsDict, boneMeshList):
1570                matName = "SkeletonMaterial"
1571                material = materialsDict.get(matName)
1572                if not material:
1573                        material = DefaultMaterial(matName)
1574                        materialsDict[matName] = material
1575
1576                submesh = SubMesh(material)
1577                for name, p1, p2 in boneMeshList:
1578                        axis = blender_bone2matrix(p1, p2, 0)
1579                        axis = matrix_translate(axis, p1)
1580                        dx, dy, dz = p1[0] - p2[0], p1[1] - p2[1], p1[2] - p2[2]
1581                        ds = math.sqrt(dx*dx + dy*dy + dz*dz)
1582                        d = 0.1 + 0.2 * (ds / 10.0)
1583                        c1 = point_by_matrix([-d, 0,-d], axis)
1584                        c2 = point_by_matrix([-d, 0, d], axis)
1585                        c3 = point_by_matrix([ d, 0, d], axis)
1586                        c4 = point_by_matrix([ d, 0,-d], axis)
1587                       
1588                        self._makeFace(submesh, name, p2, c1, c2)
1589                        self._makeFace(submesh, name, p2, c2, c3)
1590                        self._makeFace(submesh, name, p2, c3, c4)
1591                        self._makeFace(submesh, name, p2, c4, c1)
1592                        self._makeFace(submesh, name, c3, c2, c1)
1593                        self._makeFace(submesh, name, c1, c4, c3)
1594                mesh = Mesh([submesh], self.skeleton)
1595                mesh.name = self.getName()
1596                mesh.write()
1597                return
1598       
1599######
1600# global variables
1601######
1602gameEngineMaterialsToggle = Draw.Create(0)
1603armatureToggle = Draw.Create(1)
1604worldCoordinatesToggle = Draw.Create(0)
1605ambientToggle = Draw.Create(0)
1606pathString = Draw.Create(os.path.dirname(Blender.Get('filename')))
1607materialString = Draw.Create(Blender.Scene.GetCurrent().getName()+".material")
1608scaleNumber = Draw.Create(1.0)
1609fpsNumber = Draw.Create(25)
1610# first rotation, around X-axis
1611rotXNumber = Draw.Create(-90.0)
1612# second rotation, around Y-axis
1613rotYNumber = Draw.Create(0.0)
1614# third rotation, around Z-axis
1615rotZNumber = Draw.Create(0.0)
1616selectedObjectsList = Blender.Object.GetSelected()
1617selectedObjectsMenu = Draw.Create(0)
1618scrollbar = ReplacementScrollbar(0,0,0,0,0)
1619# key: objectName, value: armatureName
1620armatureDict = {}
1621# key: armatureName, value: armatureActionActuatorListView
1622# does only contain keys for the current selected objects
1623armatureActionActuatorListViewDict = {}
1624# key: armatureName, value: animationDictList
1625armatureAnimationDictListDict = {}
1626MAXACTUATORS = 100
1627
1628# button event numbers:
1629BUTTON_EVENT_OK = 101
1630BUTTON_EVENT_QUIT = 102
1631BUTTON_EVENT_EXPORT = 103
1632BUTTON_EVENT_GAMEENGINEMATERIALSTOGGLE = 104
1633BUTTON_EVENT_ARMATURETOGGLE = 105
1634BUTTON_EVENT_WORLDCOORDINATESTOGGLE = 106
1635BUTTON_EVENT_AMBIENTTOGGLE = 107
1636BUTTON_EVENT_PATHSTRING = 108
1637BUTTON_EVENT_PATHBUTTON = 109
1638BUTTON_EVENT_MATERIALSTRING = 1010
1639BUTTON_EVENT_SCALENUMBER = 1011
1640BUTTON_EVENT_ROTXNUMBER = 1012
1641BUTTON_EVENT_ROTYNUMBER = 1013
1642BUTTON_EVENT_ROTZNUMBER = 1014
1643BUTTON_EVENT_FPSNUMBER = 1015
1644BUTTON_EVENT_SCROLLBAR = 1016
1645BUTTON_EVENT_SCROLLBARUP = 1017
1646BUTTON_EVENT_SRCROLLBARDOWN = 1018
1647BUTTON_EVENT_UPDATEBUTTON = 1019
1648BUTTON_EVENT_SELECTEDOBJECTSMENU = 1020
1649BUTTON_EVENT_ACTUATOR_RANGESTART = 1021
1650
1651exportLogger = Logger()
1652
1653OGRE_LOGO = Buffer(GL_BYTE, [48,4*77],[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,11,25,4,2,2,8,74,1,1,5,114,1,1,4,123,1,1,4,124,1,1,4,126,0,0,4,124,0,0,5,109,2,2,9,66,11,10,26,7,56,56,56,0,9,8,28,13,8,8,19,31,2,1,12,46,5,5,16,34,43,43,43,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
1654[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,4,14,25,3,3,6,119,4,4,2,127,5,5,3,127,5,5,3,127,4,4,2,127,4,4,2,127,2,2,1,127,0,0,0,127,0,0,0,127,1,1,3,127,3,3,5,127,3,3,3,127,2,2,1,127,1,2,1,127,0,0,0,127,0,0,3,125,0,0,4,109,2,1,7,79,6,5,16,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
1655[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,8,22,25,2,2,9,60,2,2,7,82,2,1,8,71,6,6,19,36,6,6,6,125,10,11,6,127,12,13,8,127,11,12,7,127,9,10,6,127,7,8,4,127,5,6,3,127,3,3,2,127,1,2,1,127,7,8,4,127,10,11,6,127,11,12,7,127,11,12,7,127,10,11,7,127,9,10,6,127,6,6,4,127,4,4,3,127,3,3,2,127,1,1,1,127,0,0,1,127,0,0,4,120,2,1,8,78,9,8,25,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
1656[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,20,20,0,5,4,11,62,4,4,6,123,4,5,3,127,3,4,2,127,3,3,2,127,1,1,1,127,8,8,5,127,16,18,10,127,19,21,12,127,17,19,11,127,17,19,10,127,13,15,8,127,10,11,6,127,9,10,6,127,8,8,5,127,10,11,6,127,14,15,9,127,17,19,11,127,20,22,13,127,19,20,12,127,20,21,12,127,17,18,11,127,13,14,9,127,10,11,6,127,7,7,4,127,6,6,4,127,4,4,2,127,3,3,2,127,1,1,0,127,0,0,2,127,1,0,5,112,2,2,9,68,2,2,13,41,9,9,22,23,16,15,35,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
1657[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,6,27,3,5,6,9,108,11,12,7,127,12,13,7,127,13,15,8,127,10,11,6,127,8,9,5,127,7,8,4,127,18,20,11,127,25,27,15,127,26,29,16,127,26,28,16,127,22,24,14,127,19,20,12,127,15,16,9,127,15,16,9,127,14,16,9,127,24,26,15,127,27,30,17,127,30,32,19,127,29,31,18,127,27,29,17,127,24,26,15,127,22,24,14,127,20,21,13,127,16,17,10,127,12,13,8,127,9,10,5,127,7,8,4,127,5,6,3,127,3,3,2,127,3,3,2,127,2,2,1,127,1,1,0,127,1,1,0,127,0,0,1,127,0,0,2,127,0,0,3,124,1,0,5,115,2,2,9,71,19,18,35,3,0,0,0,0,0,0,0,0,0,0,0,0],
1658[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,9,12,104,18,19,11,127,20,22,12,127,22,24,13,127,20,22,12,127,18,19,11,127,16,18,10,127,18,19,11,127,27,30,17,127,31,34,19,127,35,38,22,127,30,33,19,127,27,30,17,127,24,26,16,127,23,25,15,127,22,24,13,127,24,26,15,127,30,33,19,127,35,38,22,127,33,36,21,127,31,33,19,127,34,37,22,127,35,37,22,127,35,37,23,127,33,35,21,127,24,26,16,127,21,22,13,127,13,14,8,127,9,10,5,127,8,9,5,127,7,8,4,127,5,5,3,127,5,5,3,127,4,5,3,127,4,5,3,127,5,5,3,127,5,5,3,127,4,4,2,127,2,2,1,127,1,1,1,127,1,0,4,123,3,3,10,50,0,0,0,0,0,0,0,0],
1659[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,10,13,89,23,24,14,127,27,30,17,127,29,31,17,127,27,30,16,127,26,28,16,127,24,25,14,127,25,27,15,127,23,26,14,127,32,35,20,127,34,37,22,127,39,42,25,127,43,45,29,127,31,34,20,127,28,30,19,127,27,30,17,127,28,31,17,127,34,37,21,127,36,39,23,127,47,50,31,127,51,54,35,127,50,53,34,127,45,48,31,127,44,47,29,127,43,46,28,127,39,42,26,127,30,31,19,127,19,20,12,127,17,18,11,127,11,12,7,127,10,11,6,127,8,8,5,127,9,10,5,127,9,9,5,127,10,11,6,127,10,11,6,127,10,11,7,127,10,11,6,127,9,10,6,127,8,9,5,127,5,5,3,127,2,2,1,127,0,0,3,124,9,8,22,5,0,0,0,0],
1660[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,11,27,12,3,3,13,45,2,2,7,74,3,3,7,95,0,0,5,99,0,0,4,100,1,1,5,96,24,25,18,127,33,35,20,127,35,38,22,127,37,40,23,127,39,42,25,127,37,40,24,127,34,37,22,127,36,39,23,127,29,32,18,127,36,40,23,127,42,45,27,127,48,51,32,127,41,44,27,127,35,37,22,127,28,30,17,127,33,35,21,127,38,41,24,127,41,44,26,127,38,41,24,127,46,49,31,127,55,58,38,127,48,51,32,127,49,51,33,127,46,49,31,127,39,42,25,127,43,45,28,127,31,34,20,127,24,26,15,127,20,21,13,127,15,16,9,127,10,10,6,127,12,13,8,127,14,15,9,127,14,16,9,127,16,18,11,127,16,17,10,127,17,18,11,127,16,17,10,127,14,15,9,127,10,11,6,127,8,9,5,127,5,6,3,127,1,2,1,127,2,1,8,83,0,0,0,0],
1661[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,45,45,45,0,4,4,11,65,5,6,6,123,5,6,4,127,3,4,1,127,4,5,2,127,3,3,1,127,2,2,1,127,2,3,1,127,3,3,2,127,28,30,18,127,40,43,26,127,38,42,24,127,40,43,25,127,38,41,24,127,38,40,24,127,39,42,25,127,40,43,26,127,31,34,19,127,35,38,22,127,39,43,25,127,42,46,28,127,39,42,25,127,34,37,22,127,23,25,14,127,31,34,19,127,38,41,24,127,37,40,23,127,37,40,23,127,43,47,28,127,45,48,30,127,40,43,27,127,39,42,26,127,37,39,25,127,32,35,21,127,29,31,19,127,24,25,15,127,17,19,10,127,15,16,9,127,12,13,8,127,18,19,11,127,20,22,12,127,23,24,15,127,21,22,13,127,22,24,14,127,21,23,13,127,20,21,12,127,18,19,11,127,15,16,9,127,13,15,8,127,12,13,8,127,9,10,5,127,5,6,3,127,0,0,3,126,8,8,22,13],
1662[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,13,41,5,6,7,118,8,10,4,127,11,13,5,127,10,12,5,127,8,10,4,127,11,13,5,127,14,17,7,127,11,13,5,127,8,9,4,127,9,9,5,127,26,28,16,127,39,42,25,127,38,41,23,127,37,40,22,127,36,39,22,127,36,38,22,127,37,40,23,127,31,34,20,127,27,29,16,127,32,35,20,127,39,43,26,127,36,39,23,127,33,36,21,127,25,27,16,127,18,20,11,127,13,14,8,127,12,14,7,127,16,17,10,127,20,21,12,127,19,21,12,127,22,23,14,127,18,19,11,127,17,19,11,127,18,19,12,127,16,17,11,127,18,19,12,127,17,18,11,127,17,19,11,127,20,21,13,127,25,26,16,127,25,27,16,127,29,31,18,127,31,33,20,127,30,31,19,127,30,32,20,127,26,27,16,127,23,25,15,127,21,22,13,127,19,21,13,127,16,18,10,127,14,16,9,127,10,11,6,127,9,10,5,127,5,5,3,127,1,1,6,92],
1663[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,28,48,2,15,14,32,11,6,5,25,28,5,4,23,31,15,14,34,15,18,17,35,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,9,11,85,14,17,8,127,16,20,8,127,16,20,8,127,15,18,7,127,17,22,8,127,16,20,8,127,13,16,6,127,13,17,6,127,15,18,8,127,15,16,10,127,13,14,8,127,20,22,12,127,27,30,17,127,27,29,17,127,25,27,15,127,22,24,13,127,17,18,10,127,11,12,7,127,9,10,5,127,9,10,6,127,12,14,8,127,13,15,8,127,11,13,7,127,10,11,6,127,12,13,7,127,13,14,8,127,15,17,10,127,16,17,10,127,17,18,11,127,21,22,14,127,22,24,14,127,24,25,15,127,25,26,16,127,22,24,13,127,24,26,15,127,24,25,15,127,24,26,15,127,26,28,17,127,24,26,15,127,24,26,15,127,30,32,19,127,29,31,19,127,33,35,21,127,34,36,22,127,30,32,19,127,30,32,20,127,29,31,18,127,26,28,16,127,27,28,17,127,23,25,15,127,19,20,12,127,16,18,10,127,13,14,8,127,10,11,6,127,5,6,3,127,1,1,5,112],
1664[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,5,24,31,4,3,23,72,4,3,22,106,2,2,20,123,1,0,16,127,2,1,18,127,1,0,19,127,1,1,17,127,3,2,19,125,2,1,23,115,2,1,14,104,1,0,15,91,7,8,12,101,17,21,9,127,19,24,9,127,21,26,10,127,18,23,9,127,22,28,11,127,27,33,13,127,28,34,13,127,30,36,16,127,27,34,14,127,23,26,13,127,24,26,15,127,19,21,12,127,16,18,10,127,16,18,10,127,17,18,10,127,17,19,10,127,15,17,9,127,16,17,10,127,16,17,9,127,17,19,10,127,20,22,13,127,22,24,13,127,27,29,17,127,24,26,15,127,25,27,16,127,24,26,15,127,26,28,16,127,28,30,18,127,31,33,20,127,34,36,22,127,37,39,24,127,36,38,24,127,31,33,20,127,37,39,25,127,31,33,20,127,31,34,20,127,30,32,19,127,31,33,20,127,28,31,18,127,28,30,18,127,29,31,19,127,27,29,17,127,32,34,21,127,31,33,19,127,30,33,19,127,31,33,19,127,33,35,22,127,29,32,19,127,31,33,20,127,28,30,18,127,25,27,15,127,19,21,11,127,19,21,12,127,14,15,8,127,10,10,6,127,4,4,2,127,2,2,7,84],
1665[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,6,26,45,6,5,28,116,6,5,21,127,6,5,18,127,7,6,17,127,4,3,12,127,4,4,13,127,8,7,21,127,4,4,18,127,4,3,17,127,2,2,13,127,2,2,12,127,3,2,17,127,8,9,12,127,24,30,12,127,32,39,15,127,31,38,16,127,32,38,17,127,27,34,13,127,33,41,16,127,34,41,16,127,41,49,23,127,57,66,36,127,47,55,28,127,27,30,17,127,27,30,17,127,27,29,17,127,25,27,16,127,25,27,15,127,26,28,16,127,26,28,15,127,27,30,16,127,29,31,18,127,31,33,19,127,30,33,18,127,34,37,22,127,35,38,22,127,36,39,23,127,39,42,25,127,37,40,24,127,40,43,26,127,44,47,29,127,48,51,33,127,44,47,30,127,40,42,26,127,40,43,27,127,37,39,24,127,36,39,23,127,32,35,20,127,30,33,19,127,32,34,20,127,30,32,19,127,31,33,20,127,30,32,19,127,28,30,18,127,29,31,18,127,30,32,19,127,33,35,22,127,33,35,21,127,33,36,21,127,37,39,24,127,34,36,22,127,31,34,20,127,34,36,22,127,31,33,20,127,24,27,15,127,20,22,12,127,19,21,12,127,14,15,8,127,8,9,5,127,2,3,3,127,6,5,18,20],
1666[0,0,0,0,0,0,0,0,0,0,0,0,7,6,26,3,11,10,30,88,8,7,24,126,11,11,22,127,6,5,20,127,2,2,15,127,5,5,19,127,3,2,16,127,11,11,30,127,8,7,23,127,3,3,15,127,4,4,15,127,7,6,18,127,1,0,5,127,2,3,7,127,20,25,11,127,28,34,14,127,32,40,16,127,31,39,16,127,33,41,17,127,34,42,17,127,40,49,21,127,46,55,24,127,61,70,37,127,67,77,43,127,53,60,33,127,27,29,17,127,29,32,19,127,30,32,19,127,29,32,19,127,30,33,18,127,32,35,20,127,32,35,20,127,35,38,21,127,35,38,21,127,36,40,22,127,38,41,24,127,41,44,26,127,44,47,28,127,44,47,29,127,47,51,32,127,49,52,33,127,52,55,36,127,50,53,35,127,45,48,31,127,50,52,34,127,48,51,33,127,45,47,30,127,42,44,28,127,38,40,25,127,36,38,23,127,33,36,21,127,30,33,18,127,30,33,19,127,31,33,20,127,32,34,20,127,29,31,18,127,29,31,18,127,30,32,19,127,33,35,21,127,34,36,22,127,35,38,23,127,35,37,22,127,35,38,23,127,35,37,23,127,35,37,23,127,29,32,18,127,26,29,16,127,22,24,13,127,18,20,11,127,13,14,7,127,7,8,4,127,3,3,6,115,0,0,0,0],
1667[0,0,0,0,0,0,0,0,16,16,34,2,11,10,29,96,21,21,36,127,10,10,25,127,6,5,18,127,3,2,12,127,0,0,3,127,6,5,8,127,13,12,26,127,3,2,8,127,7,7,16,127,5,4,11,127,0,0,0,127,3,3,6,127,2,2,4,127,9,11,8,127,28,35,14,127,42,50,23,127,52,61,31,127,51,60,31,127,43,52,24,127,38,47,19,127,50,60,28,127,49,59,28,127,50,59,29,127,46,55,27,127,40,46,23,127,30,32,19,127,31,34,20,127,32,34,20,127,29,32,18,127,31,34,19,127,34,37,21,127,35,38,21,127,37,40,23,127,36,40,22,127,39,42,24,127,44,47,28,127,46,50,30,127,44,47,29,127,46,50,30,127,52,55,35,127,50,54,34,127,63,66,45,127,51,55,35,127,50,54,34,127,48,51,32,127,51,53,34,127,50,53,34,127,43,46,29,127,38,41,25,127,34,37,22,127,36,38,23,127,30,33,19,127,32,34,21,127,33,35,22,127,30,32,19,127,29,32,18,127,26,29,16,127,28,30,17,127,29,32,18,127,31,33,19,127,36,38,23,127,37,40,24,127,35,38,23,127,36,38,24,127,34,36,22,127,31,33,20,127,26,29,16,127,22,24,13,127,16,18,10,127,12,13,7,127,9,10,6,127,5,5,11,63,0,0,0,0],
1668[0,0,0,0,0,0,0,0,9,8,22,63,32,31,42,127,18,17,32,127,10,10,23,127,7,7,17,127,12,11,21,127,10,9,20,127,11,10,17,127,10,9,20,127,4,4,9,127,0,0,0,127,6,6,12,127,0,0,0,127,0,0,0,127,7,7,13,127,17,20,14,127,41,49,22,127,45,54,24,127,61,70,38,127,41,48,25,127,21,25,11,127,22,27,11,127,20,24,10,127,20,25,10,127,24,29,12,127,33,39,18,127,46,54,27,127,33,36,21,127,33,36,21,127,33,35,21,127,29,31,18,127,31,34,19,127,35,38,21,127,36,40,22,127,41,45,26,127,41,44,25,127,40,43,24,127,45,49,29,127,45,49,29,127,51,55,34,127,56,60,38,127,51,54,34,127,54,58,37,127,54,58,38,127,57,61,40,127,55,58,37,127,52,54,35,127,52,55,36,127,47,50,32,127,48,50,32,127,42,45,28,127,36,39,23,127,33,36,21,127,34,36,21,127,34,36,22,127,36,38,24,127,32,34,21,127,29,31,18,127,28,30,17,127,29,31,18,127,33,35,21,127,31,33,19,127,35,37,22,127,35,38,23,127,35,37,23,127,34,36,23,127,34,36,22,127,28,31,18,127,25,28,16,127,20,22,12,127,12,13,7,127,25,28,15,127,15,16,13,122,12,12,12,1,0,0,0,0],
1669[0,0,0,0,0,0,0,0,10,9,21,104,43,42,55,127,26,25,41,127,18,18,28,127,9,8,17,127,17,16,33,127,8,8,18,127,11,10,20,127,7,6,12,127,0,0,0,127,0,0,0,127,7,7,13,127,5,5,8,127,10,9,16,127,23,22,33,127,10,12,10,127,26,31,14,127,17,21,9,127,12,14,6,127,14,17,7,127,20,25,10,127,29,36,14,127,33,41,17,127,37,44,19,127,47,55,28,127,54,62,33,127,56,65,35,127,46,52,29,127,33,36,21,127,34,36,21,127,31,33,19,127,30,33,18,127,35,38,21,127,36,39,22,127,41,44,26,127,40,44,25,127,43,47,27,127,47,51,31,127,52,55,34,127,57,61,39,127,54,57,36,127,55,58,37,127,61,64,42,127,63,67,44,127,58,62,40,127,58,61,40,127,57,60,39,127,54,57,37,127,48,51,31,127,41,44,26,127,39,42,25,127,37,40,23,127,34,37,22,127,34,37,22,127,33,35,21,127,34,36,22,127,30,33,19,127,27,30,17,127,28,30,18,127,33,35,21,127,32,34,20,127,30,33,19,127,34,36,22,127,35,38,23,127,34,36,22,127,34,36,22,127,29,31,19,127,25,28,16,127,25,26,15,127,41,39,23,127,19,18,11,127,25,28,15,127,6,6,10,82,0,0,0,0,0,0,0,0],
1670[0,0,0,0,18,17,38,6,24,24,37,126,38,37,50,127,33,33,41,127,25,24,36,127,8,7,18,127,11,10,19,127,11,11,18,127,0,0,0,127,8,7,13,127,0,0,0,127,5,4,8,127,18,18,27,127,5,5,8,127,9,9,14,127,9,10,13,127,17,21,11,127,20,25,10,127,25,31,12,127,27,34,13,127,31,39,16,127,38,46,20,127,41,50,22,127,46,55,26,127,57,65,36,127,61,69,39,127,61,70,39,127,61,69,39,127,60,68,38,127,33,36,21,127,34,37,22,127,31,33,19,127,31,34,19,127,36,39,22,127,39,42,24,127,39,43,24,127,41,45,25,127,41,45,25,127,46,49,29,127,52,55,34,127,53,57,36,127,60,64,41,127,62,66,43,127,67,71,48,127,67,70,47,127,63,67,44,127,65,69,46,127,58,61,40,127,53,57,36,127,46,48,30,127,42,44,27,127,39,42,25,127,36,38,23,127,34,37,22,127,34,36,22,127,35,37,23,127,29,32,18,127,29,32,19,127,27,30,17,127,27,29,17,127,29,31,18,127,30,32,19,127,31,34,20,127,33,35,21,127,32,34,20,127,29,31,18,127,28,30,18,127,21,23,13,127,48,46,26,127,99,92,54,127,75,70,40,127,26,24,14,127,14,15,11,127,9,8,22,14,0,0,0,0,0,0,0,0],
1671[0,0,0,0,16,15,33,7,26,25,38,124,21,21,29,127,14,13,30,127,20,19,29,127,11,11,19,127,0,0,0,127,8,7,13,127,0,0,0,127,11,11,16,127,10,10,16,127,5,4,8,127,0,0,0,127,11,10,17,127,11,10,18,127,18,22,10,127,27,34,13,127,31,39,15,127,34,42,17,127,43,52,23,127,49,58,28,127,50,59,28,127,50,59,28,127,51,60,30,127,53,61,32,127,54,62,33,127,57,65,35,127,60,68,38,127,61,70,39,127,52,59,34,127,31,33,20,127,32,34,20,127,32,34,20,127,34,37,21,127,39,42,24,127,41,45,26,127,43,47,27,127,42,46,26,127,46,50,29,127,47,51,30,127,52,56,35,127,58,61,39,127,62,66,43,127,61,65,42,127,65,69,45,127,65,69,46,127,60,63,41,127,56,59,38,127,54,57,36,127,46,49,30,127,43,46,28,127,36,39,23,127,32,35,20,127,35,38,23,127,31,33,19,127,31,33,20,127,31,34,20,127,29,31,18,127,27,30,18,127,28,30,17,127,28,30,18,127,30,32,19,127,28,30,18,127,29,31,18,127,27,30,17,127,22,24,14,127,14,15,9,127,38,36,21,127,101,94,54,127,108,100,57,127,78,73,42,127,28,26,15,127,4,4,8,95,0,0,0,0,0,0,0,0,0,0,0,0],
1672[12,11,26,28,9,9,24,116,20,19,30,127,14,13,23,127,7,7,11,127,8,8,14,127,0,0,0,127,0,0,0,127,8,8,14,127,9,8,14,127,8,8,13,127,9,8,14,127,0,0,0,127,0,0,0,127,10,10,16,127,33,33,41,127,29,36,14,127,35,43,17,127,38,47,18,127,38,47,19,127,44,54,24,127,50,59,28,127,50,59,28,127,50,59,28,127,51,60,29,127,53,62,33,127,53,61,33,127,53,61,32,127,53,61,32,127,56,64,34,127,60,69,37,127,46,53,27,127,31,34,19,127,29,31,18,127,36,39,23,127,38,42,24,127,40,43,24,127,43,46,27,127,45,49,29,127,44,48,27,127,47,51,31,127,54,58,37,127,59,63,41,127,62,66,43,127,59,63,41,127,61,65,42,127,63,67,44,127,57,60,38,127,53,56,36,127,50,53,34,127,46,49,30,127,43,46,28,127,40,43,26,127,36,38,22,127,31,33,19,127,33,36,21,127,30,32,19,127,30,32,19,127,28,30,18,127,27,29,17,127,27,29,17,127,28,30,18,127,25,27,15,127,26,28,16,127,22,24,14,127,16,17,10,127,11,12,7,127,10,11,6,127,36,34,20,127,94,87,51,127,106,98,58,127,80,75,44,127,25,23,15,127,10,9,22,17,0,0,0,0,0,0,0,0,0,0,0,0],
1673[22,21,35,97,9,8,16,127,8,7,14,127,0,0,0,127,0,0,0,127,4,3,6,127,5,5,8,127,8,7,12,127,16,16,24,127,4,4,7,127,0,0,0,127,9,8,14,127,1,1,2,127,7,7,11,127,10,10,15,127,35,35,41,127,29,36,14,127,35,43,17,127,37,46,18,127,38,47,19,127,43,52,23,127,50,59,28,127,50,59,29,127,50,60,29,127,51,60,30,127,54,62,33,127,54,63,33,127,54,63,33,127,54,63,33,127,54,63,33,127,56,65,34,127,57,66,35,127,54,62,32,127,38,43,24,127,32,35,20,127,40,43,25,127,42,45,26,127,41,45,25,127,43,47,27,127,42,46,26,127,48,52,31,127,51,55,34,127,59,63,41,127,55,59,37,127,60,64,41,127,65,68,46,127,61,65,42,127,58,62,40,127,50,53,33,127,47,51,31,127,46,49,30,127,39,42,25,127,42,44,27,127,36,38,23,127,32,35,21,127,33,36,21,127,31,34,20,127,28,30,17,127,25,27,15,127,26,28,16,127,24,26,15,127,24,26,16,127,20,21,12,127,19,20,12,127,17,19,11,127,12,13,7,127,11,12,6,127,10,11,6,127,34,32,18,127,97,90,52,127,105,97,56,127,74,69,39,127,23,21,16,125,17,15,31,2,0,0,0,0,0,0,0,0,0,0,0,0],
1674[13,12,25,110,0,0,0,127,8,8,13,127,0,0,0,127,1,1,2,127,9,8,14,127,19,18,28,127,2,2,3,127,0,0,0,127,10,10,16,127,0,0,0,127,2,2,3,127,25,25,37,127,6,6,11,127,10,9,18,127,15,14,26,127,29,35,14,127,35,43,17,127,37,46,18,127,38,48,19,127,43,52,23,127,54,63,31,127,56,65,33,127,58,67,34,127,60,69,36,127,55,63,34,127,54,63,33,127,54,63,33,127,54,63,33,127,54,63,32,127,54,63,33,127,57,66,35,127,57,66,35,127,57,65,34,127,38,43,23,127,35,38,21,127,40,44,25,127,40,43,24,127,40,44,24,127,41,45,25,127,43,47,27,127,50,54,33,127,54,58,36,127,54,58,36,127,53,56,35,127,56,59,37,127,57,60,39,127,51,54,34,127,47,50,31,127,44,47,28,127,46,49,30,127,40,43,25,127,38,41,24,127,35,37,22,127,35,37,23,127,29,31,18,127,29,31,18,127,27,29,17,127,23,25,14,127,21,23,13,127,24,25,15,127,22,24,14,127,24,26,15,127,22,23,14,127,19,21,13,127,12,13,8,127,11,12,6,127,11,12,6,127,34,32,18,127,95,88,52,127,98,91,53,127,62,58,33,127,55,40,17,127,35,19,10,102,0,0,0,0,0,0,0,0,0,0,0,0],
1675[13,12,27,101,0,0,0,127,14,14,22,127,9,8,15,127,7,7,12,127,0,0,0,127,9,9,15,127,0,0,0,127,0,0,0,127,2,2,3,127,15,15,23,127,10,10,17,127,11,10,19,127,20,20,31,127,10,10,22,127,12,11,30,127,33,40,21,127,37,45,18,127,40,49,19,127,41,51,20,127,43,52,22,127,61,70,37,127,60,70,36,127,59,69,36,127,60,69,36,127,56,65,34,127,54,62,33,127,53,62,32,127,53,61,32,127,53,62,32,127,53,62,32,127,55,64,33,127,49,58,28,127,42,51,23,127,40,47,21,127,33,36,20,127,35,39,22,127,37,40,22,127,38,41,23,127,39,42,23,127,39,43,24,127,42,46,27,127,43,47,27,127,42,46,27,127,46,49,31,127,43,46,27,127,40,44,25,127,41,44,26,127,40,43,25,127,39,42,25,127,37,39,23,127,34,37,21,127,32,34,20,127,29,32,19,127,27,28,17,127,23,25,15,127,26,28,17,127,27,29,17,127,29,32,19,127,30,33,19,127,29,31,19,127,27,29,17,127,25,27,16,127,21,23,13,127,20,21,13,127,13,14,9,127,11,12,7,127,12,13,7,127,43,40,23,127,91,84,47,127,93,86,49,127,50,46,27,127,67,43,15,127,42,20,8,124,0,0,0,0,0,0,0,0,0,0,0,0],
1676[17,16,30,91,11,10,18,127,4,4,7,127,7,7,11,127,0,0,0,127,0,0,0,127,0,0,1,127,10,9,15,127,6,6,9,127,10,9,16,127,8,8,13,127,19,18,29,127,12,11,22,127,8,8,22,127,15,15,31,127,15,15,25,127,34,37,32,127,35,43,17,127,39,48,19,127,41,51,20,127,41,51,20,127,55,65,32,127,62,72,38,127,62,72,38,127,61,70,37,127,58,67,35,127,54,62,33,127,52,60,31,127,48,56,28,127,44,52,25,127,40,48,21,127,39,47,20,127,40,48,21,127,44,51,23,127,90,85,47,127,52,50,28,127,28,30,18,127,31,34,19,127,33,36,20,127,31,33,19,127,31,34,19,127,31,34,19,127,32,34,20,127,35,38,22,127,38,40,25,127,40,43,26,127,38,41,25,127,40,43,27,127,42,45,29,127,43,45,29,127,43,45,29,127,40,42,26,127,43,46,29,127,44,46,29,127,40,43,26,127,38,41,24,127,36,39,23,127,32,35,20,127,33,35,21,127,29,32,18,127,32,34,21,127,27,29,18,127,25,27,16,127,21,23,14,127,18,19,11,127,14,15,9,127,11,13,7,127,13,14,8,127,50,47,27,127,94,88,50,127,81,76,44,127,43,35,18,127,93,63,23,127,43,23,12,120,0,0,0,0,0,0,0,0,0,0,0,0],
1677[16,15,28,62,8,8,14,127,0,0,0,127,9,9,15,127,0,0,0,127,2,2,4,127,9,9,14,127,18,17,27,127,5,5,9,127,2,2,3,127,9,9,15,127,14,14,25,127,20,19,34,127,17,16,27,127,4,4,7,127,23,22,33,127,16,17,18,127,31,39,16,127,39,48,19,127,41,51,20,127,41,51,21,127,48,58,26,127,61,71,37,127,58,68,35,127,55,65,32,127,51,60,29,127,40,49,22,127,37,46,19,127,37,45,19,127,37,45,19,127,36,44,18,127,36,44,18,127,37,45,19,127,49,55,27,127,99,92,54,127,61,57,33,127,29,31,19,127,35,38,22,127,39,42,25,127,40,43,25,127,39,43,24,127,44,47,27,127,43,47,27,127,49,53,32,127,57,61,39,127,64,67,45,127,65,68,45,127,67,71,48,127,65,68,46,127,66,69,46,127,61,64,43,127,55,59,38,127,50,54,34,127,49,52,33,127,42,46,27,127,40,43,25,127,35,38,22,127,37,39,23,127,33,36,21,127,32,34,20,127,30,32,19,127,26,28,16,127,24,26,15,127,21,22,13,127,17,18,11,127,13,15,8,127,13,15,8,127,13,15,8,127,61,56,32,127,93,86,48,127,61,57,33,127,75,56,26,127,94,65,30,127,35,20,13,95,0,0,0,0,0,0,0,0,0,0,0,0],
1678[23,23,37,12,16,15,26,124,1,1,3,127,15,14,22,127,13,12,20,127,6,6,10,127,0,0,0,127,6,6,9,127,22,22,32,127,12,11,22,127,15,14,26,127,10,9,17,127,1,1,6,127,21,20,33,127,7,7,11,127,2,2,4,127,9,9,15,127,36,40,29,127,36,45,18,127,39,48,19,127,40,49,19,127,40,49,20,127,43,53,22,127,44,53,23,127,44,53,23,127,43,52,22,127,36,44,18,127,32,40,16,127,31,38,15,127,36,43,19,127,49,57,29,127,61,70,39,127,71,80,47,127,64,73,41,127,85,81,46,127,62,58,33,127,24,24,14,127,33,36,21,127,37,40,23,127,39,42,24,127,40,44,25,127,43,47,27,127,44,48,28,127,49,52,31,127,57,61,38,127,63,66,43,127,64,68,45,127,68,72,49,127,66,70,47,127,65,69,46,127,61,64,42,127,55,59,38,127,52,56,36,127,46,49,30,127,42,45,27,127,40,43,26,127,35,37,22,127,34,37,22,127,33,36,21,127,31,34,20,127,29,31,18,127,26,28,16,127,22,23,13,127,19,20,12,127,16,17,10,127,13,15,8,127,13,14,8,127,14,15,9,127,72,67,39,127,84,78,45,127,54,45,23,127,116,99,66,127,95,70,36,127,26,17,21,25,0,0,0,0,0,0,0,0,0,0,0,0],
1679[0,0,0,0,21,20,34,89,15,15,24,127,0,0,0,127,16,15,24,127,14,13,23,127,13,13,23,127,22,21,40,127,17,16,33,127,9,8,26,127,11,10,21,127,9,8,20,127,11,10,19,127,11,11,17,127,5,5,8,127,10,10,16,127,20,20,29,127,49,48,55,127,35,40,28,127,30,37,17,127,30,37,15,127,28,35,18,127,31,38,16,127,30,37,15,127,29,35,14,127,30,37,15,127,34,42,17,127,44,53,25,127,51,61,29,127,68,77,43,127,74,83,49,127,74,83,49,127,74,83,48,127,66,75,42,127,73,75,43,127,76,71,42,127,16,15,9,127,35,39,22,127,37,40,24,127,39,42,24,127,42,45,25,127,42,46,26,127,42,46,26,127,47,50,29,127,59,63,40,127,60,64,41,127,61,65,42,127,70,73,50,127,67,71,48,127,61,64,42,127,59,62,41,127,55,58,37,127,49,53,33,127,43,46,28,127,41,44,26,127,39,42,25,127,35,38,22,127,34,37,21,127,32,35,20,127,31,34,20,127,27,29,17,127,25,27,15,127,20,22,13,127,17,19,11,127,15,17,9,127,14,16,9,127,12,13,7,127,17,18,10,127,85,79,45,127,63,56,31,127,98,78,48,127,123,111,86,127,58,44,29,95,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
1680[0,0,0,0,19,18,32,6,21,20,34,91,21,20,37,111,8,8,23,119,10,9,30,119,26,25,40,125,11,10,26,127,8,7,18,127,8,7,21,127,19,19,30,127,2,2,3,127,5,5,9,127,27,26,37,127,20,20,30,127,9,9,14,127,13,13,21,127,58,58,64,127,58,57,68,127,38,37,49,127,12,12,26,127,10,10,15,127,41,45,34,127,35,43,17,127,39,48,19,127,41,51,21,127,44,54,23,127,55,64,32,127,55,65,32,127,65,74,41,127,72,81,47,127,72,81,47,127,71,80,46,127,68,77,44,127,58,65,35,127,85,80,46,127,29,31,16,127,34,40,19,127,35,38,22,127,38,41,23,127,38,42,23,127,45,49,29,127,43,47,28,127,46,50,29,127,56,60,37,127,60,64,41,127,60,64,41,127,66,70,46,127,63,67,44,127,59,63,40,127,54,57,36,127,50,54,34,127,45,48,29,127,41,44,26,127,38,42,24,127,36,39,23,127,38,41,25,127,34,36,21,127,32,35,20,127,29,32,18,127,27,29,16,127,23,26,14,127,21,23,13,127,18,20,11,127,17,18,10,127,14,16,9,127,13,15,8,127,15,16,9,127,89,78,43,127,95,72,36,127,125,114,81,127,82,71,54,117,17,12,25,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
1681[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,19,35,93,13,13,21,127,18,17,27,127,2,2,3,127,8,7,12,127,18,17,24,127,9,9,14,127,7,7,11,127,12,12,19,127,12,11,19,127,30,29,39,127,53,52,59,127,50,49,62,127,31,31,46,127,2,1,6,127,19,18,27,127,42,44,43,127,34,43,17,127,38,47,19,127,41,51,21,127,43,52,22,127,54,64,31,127,54,64,31,127,59,68,35,127,68,77,44,127,67,77,43,127,62,71,38,127,59,69,36,127,49,58,28,127,56,61,31,127,36,43,18,127,34,41,17,127,33,36,21,127,36,39,22,127,40,43,25,127,43,47,28,127,43,47,28,127,44,48,28,127,51,55,34,127,59,63,40,127,59,63,40,127,63,67,44,127,59,63,40,127,54,58,37,127,50,54,33,127,47,50,31,127,45,48,29,127,40,44,25,127,39,42,25,127,37,40,23,127,35,38,22,127,34,37,22,127,32,34,20,127,31,33,19,127,27,30,17,127,23,26,14,127,21,23,13,127,20,22,13,127,17,19,11,127,16,17,10,127,13,14,8,127,18,16,9,127,102,78,41,127,122,102,68,127,76,66,48,124,37,32,40,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
1682[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,25,40,64,15,14,23,127,11,11,16,127,10,9,16,127,15,14,24,127,10,10,17,127,12,12,20,127,11,10,16,127,19,18,27,127,0,0,0,127,9,9,16,127,54,53,56,127,63,63,64,127,30,29,38,127,10,10,16,127,17,17,28,127,35,35,44,127,34,42,18,127,37,46,18,127,41,51,20,127,43,53,22,127,52,62,29,127,54,64,31,127,52,62,30,127,54,64,32,127,48,57,26,127,52,61,30,127,59,69,36,127,50,59,29,127,39,47,21,127,40,48,22,127,43,51,24,127,35,39,21,127,33,36,20,127,39,42,25,127,41,44,26,127,41,45,26,127,41,45,25,127,48,52,31,127,58,62,40,127,60,64,41,127,57,60,38,127,53,57,35,127,51,55,34,127,48,51,31,127,44,48,29,127,44,47,29,127,38,41,24,127,37,40,23,127,37,40,23,127,35,38,22,127,33,36,21,127,32,34,20,127,30,32,19,127,26,28,16,127,24,26,15,127,22,24,14,127,22,24,14,127,18,20,11,127,16,17,10,127,10,11,6,127,41,34,17,127,101,84,54,127,57,52,36,127,5,5,7,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
1683[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,25,41,8,26,25,41,122,10,9,17,127,9,9,15,127,11,11,18,127,17,16,24,127,26,25,37,127,9,9,14,127,19,18,26,127,10,9,15,127,21,20,29,127,24,24,29,127,9,9,12,127,10,9,15,127,0,0,0,127,16,16,27,127,11,10,20,127,34,37,32,127,36,45,18,127,40,50,20,127,42,51,21,127,44,54,23,127,46,55,24,127,45,55,23,127,46,55,24,127,52,61,30,127,57,67,34,127,55,64,32,127,48,57,27,127,43,52,24,127,43,51,24,127,43,51,24,127,40,48,22,127,32,34,19,127,34,37,21,127,38,41,24,127,43,46,28,127,44,48,29,127,45,49,29,127,51,55,33,127,56,60,37,127,56,60,38,127,52,55,34,127,47,51,30,127,45,48,29,127,41,45,26,127,39,42,25,127,38,42,24,127,36,39,23,127,35,38,22,127,34,37,22,127,32,35,20,127,29,32,18,127,28,31,17,127,26,29,16,127,24,27,15,127,23,26,15,127,21,23,13,127,19,21,12,127,14,16,9,127,7,8,4,127,27,30,17,127,36,39,24,127,19,20,11,127,8,8,9,106,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
1684[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,22,39,74,18,18,29,127,16,15,23,127,23,23,34,127,8,8,14,127,23,22,32,127,13,12,21,127,30,30,40,127,10,10,17,127,18,18,26,127,3,3,4,127,7,7,12,127,13,13,20,127,11,10,16,127,19,19,31,127,18,18,32,127,23,23,35,127,31,38,19,127,40,49,20,127,42,51,21,127,42,51,21,127,45,55,24,127,44,54,23,127,44,53,23,127,46,55,25,127,46,55,24,127,47,56,25,127,47,56,25,127,39,47,20,127,38,46,20,127,40,49,21,127,39,47,20,127,34,41,19,127,32,35,20,127,34,37,21,127,39,42,25,127,42,45,27,127,43,47,28,127,46,50,29,127,55,59,37,127,55,59,37,127,48,51,31,127,44,48,29,127,41,45,26,127,40,43,25,127,37,40,23,127,36,39,22,127,35,38,22,127,33,36,21,127,33,36,21,127,32,35,20,127,32,35,20,127,28,31,18,127,27,30,17,127,25,27,16,127,24,26,15,127,21,23,13,127,17,19,10,127,10,11,6,127,11,12,7,127,31,34,20,127,32,35,20,127,22,24,14,127,7,7,8,110,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
1685[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,25,40,7,21,20,37,116,14,13,21,127,17,16,26,127,10,9,16,127,16,15,23,127,26,25,38,127,15,15,23,127,10,10,18,127,18,18,25,127,6,6,9,127,15,14,20,127,2,2,4,127,7,7,12,127,9,9,18,127,12,11,21,127,18,17,30,127,42,42,50,127,38,46,19,127,43,53,21,127,42,52,21,127,44,53,22,127,44,53,23,127,42,51,21,127,41,51,21,127,42,51,21,127,43,52,22,127,41,50,21,127,38,47,20,127,41,50,21,127,40,48,20,127,38,47,19,127,38,47,19,127,31,36,18,127,32,35,19,127,32,35,20,127,36,39,23,127,38,42,24,127,40,44,25,127,58,62,40,127,55,59,37,127,45,49,29,127,42,45,27,127,39,42,25,127,36,39,23,127,35,38,22,127,33,36,21,127,34,37,21,127,33,36,21,127,32,34,19,127,33,36,20,127,31,34,20,127,28,31,18,127,27,29,16,127,26,28,16,127,23,25,14,127,21,22,13,127,13,15,8,127,11,12,7,127,14,15,8,127,29,31,18,127,52,55,35,127,32,35,21,127,15,17,12,127,14,13,28,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
1686[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,25,40,52,30,29,43,127,3,3,6,127,23,22,34,127,12,11,20,127,25,25,38,127,26,26,38,127,23,23,34,127,24,23,34,127,9,8,14,127,18,17,26,127,8,8,14,127,17,16,26,127,22,22,35,127,18,17,27,127,34,33,42,127,50,49,59,127,26,30,27,127,41,51,21,127,42,51,21,127,40,50,20,127,39,48,19,127,39,49,20,127,39,48,19,127,39,48,19,127,38,47,19,127,38,47,18,127,38,47,19,127,39,48,20,127,37,46,19,127,38,47,19,127,38,47,20,127,36,44,19,127,30,32,18,127,33,36,20,127,34,38,21,127,31,35,19,127,35,38,22,127,38,41,25,127,41,44,28,127,39,42,25,127,38,41,24,127,33,36,20,127,34,36,21,127,32,35,20,127,33,36,21,127,36,39,22,127,36,39,22,127,35,38,22,127,33,36,21,127,31,34,20,127,28,30,17,127,26,28,16,127,24,27,15,127,22,23,13,127,16,18,10,127,11,12,7,127,16,18,10,127,20,22,13,127,13,14,8,127,53,56,36,127,42,45,28,127,22,24,14,127,9,10,12,77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
1687[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,22,32,26,31,30,45,125,38,37,49,127,24,23,33,127,27,27,38,127,33,33,41,127,34,33,42,127,28,27,35,127,22,22,34,127,17,17,24,127,13,13,18,127,0,0,0,127,25,24,32,127,31,30,39,127,13,13,21,127,26,26,39,127,38,37,48,127,17,17,33,127,31,37,19,127,49,58,27,127,40,49,20,127,39,48,19,127,38,47,19,127,39,48,19,127,42,51,22,127,45,54,24,127,44,54,24,127,46,56,25,127,45,54,24,127,43,52,23,127,42,51,22,127,40,49,21,127,38,46,19,127,24,28,13,127,20,22,12,127,26,28,16,127,27,30,17,127,31,34,21,127,18,20,11,127,14,15,9,127,23,25,14,127,32,34,20,127,8,9,5,127,24,26,14,127,29,32,18,127,30,32,19,127,28,31,17,127,25,28,16,127,21,23,13,127,17,19,11,127,14,16,9,127,15,16,9,127,20,22,12,127,20,22,13,127,19,21,12,127,14,15,8,127,14,15,9,127,18,20,11,127,31,33,20,127,20,21,13,127,40,43,27,127,47,50,31,127,21,23,13,127,14,15,13,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
1688[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,24,32,104,40,39,48,127,33,32,43,127,50,49,57,127,28,27,40,127,27,27,41,127,22,21,34,127,22,21,33,127,26,26,34,127,23,22,33,127,19,18,28,127,12,11,17,127,33,32,42,127,35,34,46,127,17,16,24,127,27,26,37,127,13,13,21,127,23,24,30,127,15,18,7,127,53,61,33,127,18,22,9,127,19,24,10,127,36,44,18,127,34,41,18,127,26,32,14,127,25,30,12,127,27,33,15,127,32,38,18,127,27,33,14,127,33,40,18,127,46,54,26,127,47,56,27,127,40,48,21,127,29,33,17,127,32,34,19,127,33,36,21,127,21,23,13,127,24,26,15,127,41,45,28,127,31,34,21,127,11,12,7,127,15,16,9,127,8,8,5,127,27,29,18,127,30,33,19,127,33,36,22,127,33,36,21,127,31,33,19,127,27,29,17,127,25,27,16,127,20,22,13,127,12,14,8,127,9,10,5,127,16,18,10,127,16,17,10,127,12,13,7,127,16,18,10,127,20,22,12,127,25,27,15,127,22,23,14,127,40,42,27,127,49,52,33,127,28,31,17,127,24,26,15,127,9,9,16,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
1689[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,36,48,92,37,36,49,127,28,27,41,127,51,50,57,127,33,33,47,127,30,30,40,127,36,35,46,127,20,20,29,127,32,31,46,127,20,20,27,127,28,27,37,127,11,10,17,127,32,31,44,127,24,23,36,127,19,18,28,127,2,2,4,127,26,25,36,127,22,27,17,127,23,29,12,127,31,37,17,127,27,34,14,127,24,29,12,127,26,32,13,127,38,46,21,127,50,58,29,127,55,64,34,127,55,63,34,127,55,64,34,127,45,53,27,127,25,30,13,127,20,24,10,127,42,49,24,127,40,48,22,127,33,37,20,127,55,50,37,127,51,49,33,127,42,45,28,127,41,45,26,127,53,57,36,127,58,61,40,127,38,42,24,127,22,24,13,127,26,28,17,127,50,53,34,127,48,51,32,127,46,48,30,127,56,56,38,127,49,48,31,127,48,44,34,127,42,42,30,127,31,32,20,127,22,24,14,127,8,9,5,127,11,12,7,127,12,13,7,127,8,9,5,127,13,15,8,127,22,24,13,127,22,24,13,127,19,21,12,127,44,46,30,127,48,52,32,127,26,29,15,127,28,30,18,127,9,10,10,105,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
1690[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,22,44,4,26,25,38,73,27,26,39,124,18,18,26,127,43,43,50,127,28,28,40,127,20,19,30,127,35,34,44,127,25,24,35,127,16,16,26,127,19,19,26,127,11,10,18,127,24,23,34,127,18,17,32,127,32,32,43,127,30,29,43,127,18,17,25,127,24,25,27,127,40,49,21,127,36,44,18,127,26,32,13,127,31,38,17,127,44,53,23,127,47,57,26,127,44,53,24,127,48,56,28,127,48,56,28,127,50,58,30,127,50,59,30,127,49,57,29,127,27,33,14,127,19,23,9,127,33,40,18,127,37,40,23,127,59,55,39,127,55,48,29,127,73,68,53,127,27,29,17,127,36,40,23,127,36,40,23,127,29,32,18,127,19,21,12,127,28,30,18,127,33,35,22,127,59,58,42,127,82,74,62,127,66,59,44,127,27,25,1,127,57,49,38,127,54,50,40,127,43,41,32,127,29,30,18,127,15,16,9,127,5,6,3,127,9,10,6,127,4,4,2,127,16,18,10,127,21,22,13,127,16,17,10,127,14,16,8,127,52,55,36,127,53,56,36,127,29,32,17,127,30,34,18,127,18,19,13,127,8,8,20,17,0,0,0,0,0,0,0,0,0,0,0,0],
1691[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,52,52,1,28,27,37,123,10,9,24,127,8,8,21,127,14,13,30,127,28,28,42,127,40,40,50,127,39,38,44,127,22,21,30,127,19,18,30,127,20,19,31,127,15,14,22,127,20,20,35,127,25,25,40,127,26,25,42,127,19,18,35,127,26,25,39,127,19,20,27,127,28,34,13,127,22,26,11,127,14,17,7,127,11,13,5,127,24,29,12,127,43,51,23,127,52,61,30,127,58,67,35,127,54,62,33,127,40,48,23,127,46,54,27,127,45,53,27,127,21,26,10,127,24,29,12,127,25,29,14,127,62,58,45,127,60,55,39,127,31,29,21,127,28,30,19,127,29,31,18,127,38,41,24,127,35,38,23,127,28,30,19,127,13,15,8,127,10,11,6,127,11,11,8,127,43,41,32,127,70,66,50,127,55,50,28,127,67,59,46,127,64,60,47,127,53,50,39,127,30,30,21,127,18,19,11,127,3,3,2,127,5,5,3,127,5,6,3,127,4,5,3,127,10,11,6,127,10,11,6,127,6,6,3,127,42,45,28,127,44,47,30,127,32,35,20,127,37,40,23,127,21,24,12,127,6,7,9,90,0,0,0,0,0,0,0,0,0,0,0,0],
1692[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,34,51,6,28,27,40,125,20,20,32,127,8,8,16,127,11,10,23,127,9,9,22,127,12,12,23,127,16,15,30,127,14,13,27,127,9,9,22,127,17,16,28,127,17,17,25,127,15,15,23,127,20,19,29,127,24,23,32,127,22,21,33,127,24,23,35,127,19,21,21,127,30,37,14,127,30,37,15,127,31,37,16,127,23,28,12,127,11,13,5,127,6,8,3,127,12,15,6,127,20,24,9,127,27,32,14,127,27,33,14,127,24,29,13,127,30,35,17,127,15,18,7,127,8,9,4,127,4,4,2,127,2,3,1,127,16,18,10,127,33,35,21,127,38,41,24,127,43,47,29,127,49,53,33,127,45,49,30,127,53,57,38,127,37,39,24,127,25,28,16,127,14,16,9,127,11,12,7,127,3,3,2,127,11,10,8,127,26,25,19,127,26,25,19,127,22,20,16,127,15,15,11,127,5,5,3,127,2,2,1,127,1,1,0,127,6,7,4,127,11,12,7,127,17,18,10,127,19,21,12,127,15,16,10,127,12,13,7,127,24,27,15,127,31,34,20,127,26,29,15,127,33,36,21,127,15,16,12,126,12,11,27,9,0,0,0,0,0,0,0,0],
1693[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,23,34,48,29,28,39,126,18,18,28,127,16,15,24,127,16,16,26,127,5,5,10,127,23,22,34,127,27,26,41,127,15,15,28,127,19,18,30,127,16,16,25,127,13,13,21,127,19,19,26,127,32,31,47,127,37,36,50,127,30,29,46,127,27,31,27,127,35,43,17,127,36,44,18,127,45,53,25,127,41,49,23,127,32,39,17,127,24,29,12,127,16,19,8,127,11,13,5,127,12,15,6,127,14,17,7,127,15,19,7,127,16,20,8,127,15,19,7,127,12,14,7,127,13,15,8,127,18,20,11,127,31,33,18,127,38,42,24,127,47,50,30,127,65,68,46,127,53,57,36,127,54,58,37,127,61,64,43,127,55,58,39,127,42,45,28,127,29,32,19,127,22,24,14,127,16,17,10,127,11,12,7,127,6,7,4,127,8,9,5,127,8,8,5,127,10,11,6,127,5,6,3,127,6,7,4,127,2,2,1,127,7,7,4,127,20,22,12,127,32,34,20,127,30,32,18,127,29,32,18,127,15,17,9,127,9,10,5,127,25,27,16,127,33,36,21,127,31,34,19,127,25,28,16,127,9,9,12,85,0,0,0,0,0,0,0,0],
1694[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,34,46,91,33,33,46,127,24,23,37,127,18,18,26,127,7,7,13,127,46,45,59,127,32,31,44,127,23,23,38,127,18,18,26,127,15,14,28,127,19,19,30,127,14,13,23,127,12,12,22,127,12,11,26,127,27,26,41,127,23,23,30,127,34,41,19,127,36,45,18,127,42,51,21,127,51,60,29,127,56,65,34,127,49,58,29,127,45,53,26,127,40,47,22,127,34,41,18,127,31,38,17,127,28,34,14,127,28,34,14,127,23,29,11,127,21,23,12,127,27,30,16,127,33,36,20,127,42,45,26,127,42,46,26,127,48,52,32,127,53,57,35,127,59,62,40,127,48,52,31,127,58,62,40,127,61,65,42,127,58,61,39,127,48,51,32,127,37,41,24,127,31,34,20,127,25,28,16,127,20,23,13,127,18,20,11,127,21,23,14,127,14,15,8,127,13,15,8,127,13,14,8,127,6,7,4,127,6,7,6,127,23,25,16,127,37,41,24,127,39,43,26,127,43,46,29,127,37,40,23,127,20,22,12,127,10,11,6,127,16,17,10,127,24,26,15,127,18,20,11,127,10,10,9,125,19,18,33,4,0,0,0,0],
1695[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,17,33,77,17,17,28,127,20,20,29,127,37,37,45,127,34,33,40,127,24,24,35,127,28,27,35,127,7,6,13,127,2,2,4,127,6,6,12,127,3,2,7,127,5,5,13,127,12,11,25,122,11,10,25,51,17,17,30,62,25,26,37,123,35,43,21,127,39,48,19,127,39,48,19,127,42,51,22,127,51,60,29,127,61,70,37,127,67,76,43,127,64,73,40,127,58,66,36,127,50,58,30,127,44,52,26,127,33,39,17,127,27,31,15,127,34,37,21,127,38,42,24,127,42,45,27,127,44,48,29,127,42,46,27,127,43,47,27,127,46,50,30,127,49,52,32,127,44,48,29,127,44,48,28,127,44,47,28,127,42,45,26,127,42,46,27,127,40,44,26,127,35,39,22,127,32,35,20,127,30,33,19,127,28,30,18,127,25,27,15,127,21,24,13,127,17,19,11,127,10,11,6,127,4,3,11,65,19,19,31,12,18,19,18,77,24,26,19,119,42,45,28,127,46,49,30,127,33,36,21,127,32,35,20,127,15,16,10,127,10,11,6,127,13,14,8,127,12,13,7,127,6,6,12,63,0,0,0,0],
1696[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,12,27,42,18,17,31,116,8,8,15,127,14,14,23,127,8,8,16,127,18,17,29,127,6,6,12,127,6,5,12,127,10,10,20,127,6,5,14,124,6,6,17,96,11,10,28,25,0,0,0,0,0,0,0,0,17,16,33,17,18,19,24,104,36,44,18,127,44,53,23,127,53,63,31,127,54,63,32,127,45,54,25,127,43,52,23,127,48,57,27,127,51,60,30,127,50,59,29,127,43,51,24,127,30,37,15,127,24,30,12,127,29,32,17,127,33,37,20,127,35,38,22,127,38,41,24,127,43,47,28,127,43,47,27,127,46,50,30,127,49,53,33,127,49,53,32,127,51,55,34,127,43,48,28,127,39,42,24,127,37,41,23,127,37,40,23,127,37,41,23,127,36,39,22,127,35,38,21,127,33,36,21,127,32,35,20,127,27,29,17,127,21,23,13,127,7,7,10,111,15,14,29,2,0,0,0,0,0,0,0,0,0,0,0,0,18,19,21,48,26,28,21,116,38,41,25,127,44,47,29,127,30,32,18,127,24,26,15,127,6,6,4,127,13,14,8,127,9,10,10,115,0,0,0,0],
1697[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,21,41,4,12,12,28,44,13,12,24,83,11,10,20,107,11,11,21,104,10,10,18,91,11,10,23,78,13,13,28,52,16,16,31,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,36,36,1,19,22,17,82,33,41,17,127,37,46,18,127,43,52,22,127,49,59,28,127,51,60,29,127,38,46,19,127,35,43,17,127,32,40,15,127,32,40,16,127,25,31,12,127,20,24,9,127,17,21,9,127,10,12,10,115,20,21,19,100,34,38,21,127,42,46,27,127,43,47,28,127,43,47,27,127,47,51,31,127,44,48,28,127,43,47,28,127,43,47,27,127,41,45,26,127,38,41,23,127,37,41,23,127,36,39,22,127,33,36,20,127,33,36,21,127,28,31,18,127,24,25,16,127,15,17,14,107,10,11,13,74,12,11,27,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,12,17,46,24,26,19,113,41,44,27,127,47,50,31,127,33,35,21,127,14,16,9,127,10,11,8,127,9,9,20,25],
1698[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,17,16,45,21,26,15,108,30,37,17,127,35,43,17,127,34,42,16,127,33,41,16,127,32,40,15,127,31,39,15,127,26,32,13,127,16,19,10,127,10,13,10,108,8,8,15,41,0,0,0,0,34,34,34,0,19,20,18,93,33,36,21,127,39,42,25,127,39,42,24,127,40,44,25,127,38,42,24,127,44,48,29,127,42,46,27,127,41,45,26,127,37,41,23,127,34,38,21,127,32,36,20,127,29,33,18,127,23,26,14,127,19,20,13,127,8,9,12,69,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,17,22,34,21,22,18,99,30,33,23,125,35,38,22,127,21,22,13,127,6,6,9,92],
1699[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,15,26,10,15,17,15,68,17,21,13,103,18,22,13,110,17,21,13,100,14,17,13,80,9,11,16,44,18,18,30,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,15,19,33,22,24,18,114,32,35,21,127,34,38,21,127,37,40,23,127,35,39,22,127,38,41,24,127,36,39,22,127,34,38,22,127,32,35,20,127,29,31,18,127,21,23,15,127,19,21,15,124,24,29,15,127,10,11,19,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,14,32,3,16,17,21,52,12,12,12,96,6,6,10,106],
1700[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,14,24,24,15,16,17,65,17,19,16,98,21,23,17,118,23,25,19,125,22,24,18,122,19,21,16,116,16,17,14,106,14,14,14,87,12,12,22,21,0,0,0,0,15,17,23,25,17,19,24,23,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]])
1701
1702#######################################################################################
1703## math functions
1704
1705def matrix_translate(m, v):
1706  m[3][0] += v[0]
1707  m[3][1] += v[1]
1708  m[3][2] += v[2]
1709  return m
1710
1711def matrix_multiply(b, a):
1712  """ matrix_multiply(b, a) = a*b
1713  """
1714  return [ [
1715    a[0][0] * b[0][0] + a[0][1] * b[1][0] + a[0][2] * b[2][0],
1716    a[0][0] * b[0][1] + a[0][1] * b[1][1] + a[0][2] * b[2][1],
1717    a[0][0] * b[0][2] + a[0][1] * b[1][2] + a[0][2] * b[2][2],
1718    0.0,
1719    ], [
1720    a[1][0] * b[0][0] + a[1][1] * b[1][0] + a[1][2] * b[2][0],
1721    a[1][0] * b[0][1] + a[1][1] * b[1][1] + a[1][2] * b[2][1],
1722    a[1][0] * b[0][2] + a[1][1] * b[1][2] + a[1][2] * b[2][2],
1723    0.0,
1724    ], [
1725    a[2][0] * b[0][0] + a[2][1] * b[1][0] + a[2][2] * b[2][0],
1726    a[2][0] * b[0][1] + a[2][1] * b[1][1] + a[2][2] * b[2][1],
1727    a[2][0] * b[0][2] + a[2][1] * b[1][2] + a[2][2] * b[2][2],
1728     0.0,
1729    ], [
1730    a[3][0] * b[0][0] + a[3][1] * b[1][0] + a[3][2] * b[2][0] + b[3][0],
1731    a[3][0] * b[0][1] + a[3][1] * b[1][1] + a[3][2] * b[2][1] + b[3][1],
1732    a[3][0] * b[0][2] + a[3][1] * b[1][2] + a[3][2] * b[2][2] + b[3][2],
1733    1.0,
1734    ] ]
1735
1736def matrix_invert(m):
1737  det = (m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2])
1738       - m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2])
1739       + m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2]))
1740  if det == 0.0: return None
1741  det = 1.0 / det
1742  r = [ [
1743      det * (m[1][1] * m[2][2] - m[2][1] * m[1][2]),
1744    - det * (m[0][1] * m[2][2] - m[2][1] * m[0][2]),
1745      det * (m[0][1] * m[1][2] - m[1][1] * m[0][2]),
1746      0.0,
1747    ], [
1748    - det * (m[1][0] * m[2][2] - m[2][0] * m[1][2]),
1749      det * (m[0][0] * m[2][2] - m[2][0] * m[0][2]),
1750    - det * (m[0][0] * m[1][2] - m[1][0] * m[0][2]),
1751      0.0
1752    ], [
1753      det * (m[1][0] * m[2][1] - m[2][0] * m[1][1]),
1754    - det * (m[0][0] * m[2][1] - m[2][0] * m[0][1]),
1755      det * (m[0][0] * m[1][1] - m[1][0] * m[0][1]),
1756      0.0,
1757    ] ]
1758  r.append([
1759    -(m[3][0] * r[0][0] + m[3][1] * r[1][0] + m[3][2] * r[2][0]),
1760    -(m[3][0] * r[0][1] + m[3][1] * r[1][1] + m[3][2] * r[2][1]),
1761    -(m[3][0] * r[0][2] + m[3][1] * r[1][2] + m[3][2] * r[2][2]),
1762    1.0,
1763    ])
1764  return r
1765
1766def matrix_transpose(m):
1767  return [ [ m[0][0], m[1][0], m[2][0], m[3][0] ],
1768           [ m[0][1], m[1][1], m[2][1], m[3][1] ],
1769           [ m[0][2], m[1][2], m[2][2], m[3][2] ],
1770           [ m[0][3], m[1][3], m[2][3], m[3][3] ] ]
1771
1772def matrix_rotate(axis, angle):
1773  vx  = axis[0]
1774  vy  = axis[1]
1775  vz  = axis[2]
1776  vx2 = vx * vx
1777  vy2 = vy * vy
1778  vz2 = vz * vz
1779  cos = math.cos(angle)
1780  sin = math.sin(angle)
1781  co1 = 1.0 - cos
1782  return [
1783    [vx2 * co1 + cos,          vx * vy * co1 + vz * sin, vz * vx * co1 - vy * sin, 0.0],
1784    [vx * vy * co1 - vz * sin, vy2 * co1 + cos,          vy * vz * co1 + vx * sin, 0.0],
1785    [vz * vx * co1 + vy * sin, vy * vz * co1 - vx * sin, vz2 * co1 + cos,          0.0],
1786    [0.0, 0.0, 0.0, 1.0],
1787    ]
1788
1789def point_by_matrix(p, m):
1790  return [p[0] * m[0][0] + p[1] * m[1][0] + p[2] * m[2][0] + m[3][0],
1791          p[0] * m[0][1] + p[1] * m[1][1] + p[2] * m[2][1] + m[3][1],
1792          p[0] * m[0][2] + p[1] * m[1][2] + p[2] * m[2][2] + m[3][2]]
1793
1794def vector_by_matrix(p, m):
1795  return [p[0] * m[0][0] + p[1] * m[1][0] + p[2] * m[2][0],
1796          p[0] * m[0][1] + p[1] * m[1][1] + p[2] * m[2][1],
1797          p[0] * m[0][2] + p[1] * m[1][2] + p[2] * m[2][2]]
1798
1799def vector_normalize(v):
1800  global exportLogger
1801  l = math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2])
1802  if l <= 0.000001:
1803    exportLogger.logError("error in normalize")
1804    return [0 , l, 0]
1805  return [v[0] / l, v[1] / l, v[2] / l]
1806
1807def normal_by_matrix(n, m):
1808  m = matrix_transpose(matrix_invert(m))
1809  return vector_normalize(vector_by_matrix(n, m))
1810
1811
1812def vector_dotproduct(v1, v2):
1813  return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]
1814
1815def vector_crossproduct(v1, v2):
1816  return [
1817    v1[1] * v2[2] - v1[2] * v2[1],
1818    v1[2] * v2[0] - v1[0] * v2[2],
1819    v1[0] * v2[1] - v1[1] * v2[0],
1820    ]
1821
1822#######################################################################################
1823## data structures
1824       
1825class MaterialInterface:
1826        def getName(self):
1827                """Returns the material name.
1828               
1829                   @return Material name.
1830                """
1831                return
1832        def write(self, f):
1833                """Write material script entry.
1834               
1835                   @param f Material script file object to write into.
1836                """
1837                return
1838
1839class DefaultMaterial(MaterialInterface):
1840        def __init__(self, name):
1841                self.name = name
1842                return
1843        def getName(self):
1844                return self.name
1845        def write(self, f):
1846                f.write("material %s\n" % self.getName())
1847                f.write("{\n")
1848                self.writeTechniques(f)
1849                f.write("}\n")
1850                return
1851        def writeTechniques(self, f):
1852                f.write(tab(1) + "technique\n" + tab(1) + "{\n")
1853                f.write(tab(2) + "pass\n" + tab(2) + "{\n")
1854                # empty pass
1855                f.write(tab(2) + "}\n") # pass
1856                f.write(tab(1) + "}\n") # technique
1857                return
1858       
1859class GameEngineMaterial(DefaultMaterial):
1860        def __init__(self, blenderMesh, blenderFace):
1861                self.mesh = blenderMesh
1862                self.face = blenderFace
1863                DefaultMaterial.__init__(self, self._createName())
1864                return
1865        def writeTechniques(self, f):
1866                mat = self.mesh.getMaterials(1)[self.face.materialIndex]
1867                if (not(mat)
1868                        and not(self.mesh.hasVertexColours())
1869                        and not(self.mesh.hasVertexUV() or self.mesh.hasFaceUV())):
1870                        # default material
1871                        DefaultMaterial.writeTechniques(self, f)
1872                else:
1873                        # default material
1874                        # SOLID, white, no specular
1875                        f.write(tab(1)+"technique\n")
1876                        f.write(tab(1)+"{\n")
1877                        f.write(tab(2)+"pass\n")
1878                        f.write(tab(2)+"{\n")
1879                        # ambient
1880                        # (not used in Blender's game engine)
1881                        if mat:
1882                                if (not(mat.mode & Blender.Material.Modes["TEXFACE"])
1883                                        and not(mat.mode & Blender.Material.Modes["VCOL_PAINT"])
1884                                        and (ambientToggle.val)):
1885                                        ambientRGBList = mat.rgbCol
1886                                else:
1887                                        ambientRGBList = [1.0, 1.0, 1.0]
1888                                # ambient <- amb * ambient RGB
1889                                ambR = clamp(mat.amb * ambientRGBList[0])
1890                                ambG = clamp(mat.amb * ambientRGBList[1])
1891                                ambB = clamp(mat.amb * ambientRGBList[2])
1892                                ##f.write(tab(3)+"ambient %f %f %f\n" % (ambR, ambG, ambB))
1893                        # diffuse
1894                        # (Blender's game engine uses vertex colours
1895                        #  instead of diffuse colour.
1896                        #
1897                        #  diffuse is defined as
1898                        #  (mat->r, mat->g, mat->b)*(mat->emit + mat->ref)
1899                        #  but it's not used.)
1900                        if self.mesh.hasVertexColours():
1901                                #TODO: Broken in Blender 2.36.
1902                                # Blender does not handle "texface" mesh with vertexcolours
1903                                f.write(tab(3)+"diffuse vertexcolour\n")
1904                        elif mat:
1905                                if (not(mat.mode & Blender.Material.Modes["TEXFACE"])
1906                                        and not(mat.mode & Blender.Material.Modes["VCOL_PAINT"])):
1907                                        # diffuse <- rgbCol
1908                                        diffR = clamp(mat.rgbCol[0])
1909                                        diffG = clamp(mat.rgbCol[1])
1910                                        diffB = clamp(mat.rgbCol[2])
1911                                        f.write(tab(3)+"diffuse %f %f %f\n" % (diffR, diffG, diffB))
1912                                elif (mat.mode & Blender.Material.Modes["VCOL_PAINT"]):
1913                                        f.write(tab(3)+"diffuse vertexcolour\n")
1914                        if mat:
1915                                # specular <- spec * specCol, hard/4.0
1916                                specR = clamp(mat.spec * mat.specCol[0])
1917                                specG = clamp(mat.spec * mat.specCol[1])
1918                                specB = clamp(mat.spec * mat.specCol[2])
1919                                specShine = mat.hard/4.0
1920                                f.write(tab(3)+"specular %f %f %f %f\n" % (specR, specG, specB, specShine))
1921                                # emissive
1922                                # (not used in Blender's game engine)
1923                                if(not(mat.mode & Blender.Material.Modes["TEXFACE"])
1924                                        and not(mat.mode & Blender.Material.Modes["VCOL_PAINT"])):
1925                                        # emissive <-emit * rgbCol
1926                                        emR = clamp(mat.emit * mat.rgbCol[0])
1927                                        emG = clamp(mat.emit * mat.rgbCol[1])
1928                                        emB = clamp(mat.emit * mat.rgbCol[2])
1929                                        ##f.write(tab(3)+"emissive %f %f %f\n" % (emR, emG, emB))
1930                        # scene_blend <- transp
1931                        if (self.face.mode == Blender.NMesh.FaceTranspModes["ALPHA"]):
1932                                f.write(tab(3)+"scene_blend alpha_blend \n")
1933                        elif (self.face.mode == Blender.NMesh.FaceTranspModes["ADD"]):
1934                                #TODO: Broken in Blender 2.36.
1935                                #f.write(tab(3)+"scene_blend add\n")
1936                                pass
1937                        # cull_hardware/cull_software
1938                        if (self.face.mode & Blender.NMesh.FaceModes['TWOSIDE']):
1939                                f.write(tab(3) + "cull_hardware none\n")
1940                                f.write(tab(3) + "cull_software none\n")
1941                        # shading
1942                        # (Blender's game engine is initialized with glShadeModel(GL_FLAT))
1943                        ##f.write(tab(3) + "shading flat\n")
1944                        # texture
1945                        if (self.face.mode & Blender.NMesh.FaceModes['TEX']) and (self.face.image):
1946                                f.write(tab(3)+"texture_unit\n")
1947                                f.write(tab(3)+"{\n")
1948                                f.write(tab(4)+"texture %s\n" % PathName(self.face.image.filename).basename())
1949                                f.write(tab(3)+"}\n") # texture_unit
1950                        f.write(tab(2)+"}\n") # pass
1951                        f.write(tab(1)+"}\n") # technique
1952                return
1953        # private
1954        def _createName(self):
1955                """Create unique material name.
1956               
1957                   The name consists of several parts:
1958                   <OL>
1959                   <LI>rendering material name/</LI>
1960                   <LI>blend mode (ALPHA, ADD, SOLID)</LI>
1961                   <LI>/TEX</LI>
1962                   <LI>/texture file name</LI>
1963                   <LI>/VertCol</LI>
1964                   <LI>/TWOSIDE></LI>
1965                   </OL>
1966                """
1967                materialName = ''
1968                # nonempty rendering material?
1969                faceMaterial = self.mesh.getMaterials(1)[self.face.materialIndex]
1970                if faceMaterial:
1971                        materialName += faceMaterial.getName() + '/'
1972                # blend mode
1973                if (self.face.transp == Blender.NMesh.FaceTranspModes['ALPHA']):
1974                        materialName += 'ALPHA'
1975                elif (self.face.transp == Blender.NMesh.FaceTranspModes['ADD']):
1976                        materialName += 'ADD'
1977                else:
1978                        materialName += 'SOLID'
1979                # TEX face mode and texture?
1980                if (self.face.mode & Blender.NMesh.FaceModes['TEX']):
1981                        materialName += '/TEX'
1982                        if self.face.image:
1983                                materialName += '/' + PathName(self.face.image.filename).basename()
1984                # vertex colours?
1985                if self.mesh.hasVertexColours():
1986                        materialName += '/VertCol'
1987                # two sided?
1988                if (self.face.mode & Blender.NMesh.FaceModes['TWOSIDE']):
1989                        materialName += '/TWOSIDE'
1990                return materialName
1991
1992class RenderingMaterial(DefaultMaterial):
1993        def __init__(self, blenderMesh, blenderFace):
1994                self.mesh = blenderMesh
1995                self.face = blenderFace
1996                self.material = self.mesh.getMaterials(1)[self.face.materialIndex]
1997                if self.material:
1998                        DefaultMaterial.__init__(self, self._createName())
1999                else:
2000                        DefaultMaterial.__init__(self, 'None')
2001                return
2002        def writeTechniques(self, f):
2003                # parse material
2004                if self.material:
2005                        if not(self.material.mode & Blender.Material.Modes['HALO']):
2006                                # non-Halo
2007                                key = 0
2008                                if (self.material.mode & Blender.Material.Modes['VCOL_LIGHT']):
2009                                        key |= self.VCOLLIGHT
2010                                if (self.material.mode & Blender.Material.Modes['VCOL_PAINT']):
2011                                        key |= self.VCOLPAINT
2012                                if (self.material.mode & Blender.Material.Modes['TEXFACE']):
2013                                        key |= self.TEXFACE
2014                                # textures
2015                                for mtex in self.material.getTextures():
2016                                        if mtex:
2017                                                if (mtex.tex.type == Blender.Texture.Types['IMAGE']):
2018                                                        if (mtex.texco & Blender.Texture.TexCo['UV']):
2019                                                                if (mtex.mapto & Blender.Texture.MapTo['COL']):
2020                                                                        key |= self.IMAGEUVCOL
2021                                                                if (mtex.mapto & Blender.Texture.MapTo['NOR']):
2022                                                                        # Check "Normal Map" image option
2023                                                                        if (mtex.tex.imageFlags & 2048):
2024                                                                                key |= self.IMAGEUVNOR
2025                                                                        # else bumpmap
2026                                                                if (mtex.mapto & Blender.Texture.MapTo['CSP']):
2027                                                                        key |= self.IMAGEUVCSP
2028                                # choose techniques
2029                                if self.TECHNIQUES.has_key(key):
2030                                        techniques = self.TECHNIQUES[key]
2031                                        techniques(self, f)
2032                                else:
2033                                        # default
2034                                        self.writeColours(f)
2035                        else:
2036                                # Halo
2037                                DefaultMaterial('').writeTechniques(f)
2038                else:
2039                        DefaultMaterial('').writeTechniques(f)
2040                return
2041        def writeColours(self, f):
2042                global ambientToggle
2043                # receive_shadows
2044                self.writeReceiveShadows(f, 1)
2045                f.write(tab(1) + "technique\n" + tab(1) + "{\n")
2046                f.write(tab(2) + "pass\n" + tab(2) + "{\n")
2047                # ambient
2048                if (ambientToggle.val):
2049                        col = self.material.getRGBCol()
2050                else:
2051                        col = [1.0, 1.0, 1.0]
2052                self.writeAmbient(f, col, 3)
2053                # diffuse
2054                self.writeDiffuse(f, self.material.rgbCol, 3)
2055                # specular
2056                self.writeSpecular(f, 3)
2057                # emissive
2058                self.writeEmissive(f, self.material.rgbCol, 3)
2059                # blend mode
2060                self.writeSceneBlend(f,3)
2061                # options
2062                self.writeCommonOptions(f, 3)
2063                # texture units
2064                self.writeDiffuseTexture(f, 3)
2065                f.write(tab(2) + "}\n") # pass
2066                f.write(tab(1) + "}\n") # technique
2067                return
2068        def writeVertexColours(self, f):
2069                # preconditions: VCOL_PAINT set
2070                #
2071                # ambient = Amb*White resp. Amb*VCol if "Coloured Ambient"
2072                # diffuse = Ref*VCol
2073                # specular = Spec*SpeRGB, Hard/4.0
2074                # emissive = Emit*VCol
2075                # alpha = A
2076                #
2077                # Best match without vertex shader:
2078                # ambient = Amb*white
2079                # diffuse = Ref*VCol
2080                # specular = Spec*SpeRGB, Hard/4.0
2081                # emissive = black
2082                # alpha = 1
2083                #
2084                self.writeReceiveShadows(f, 1)
2085                f.write(tab(1) + "technique\n" + tab(1) + "{\n")
2086                if (self.material.mode & Blender.Material.Modes['SHADELESS']):
2087                        f.write(tab(2) + "pass\n" + tab(2) + "{\n")
2088                        self.writeCommonOptions(f,3)
2089                        f.write(tab(2) + "}\n")
2090                else:
2091                        # vertex colour pass
2092                        f.write(tab(2) + "pass\n" + tab(2) + "{\n")
2093                        f.write(tab(3) + "ambient 0.0 0.0 0.0\n")
2094                        f.write(tab(3) + "diffuse vertexcolour\n")
2095                        self.writeCommonOptions(f, 3)
2096                        f.write(tab(2) + "}\n") # vertex colour pass
2097                       
2098                        # factor pass
2099                        f.write(tab(2) + "pass\n" + tab(2) + "{\n")
2100                        f.write(tab(3) + "ambient 0.0 0.0 0.0\n")
2101                        ref = self.material.getRef()
2102                        f.write(tab(3) + "diffuse %f %f %f\n" % (ref, ref, ref))
2103                        f.write(tab(3) + "scene_blend modulate\n")
2104                        self.writeCommonOptions(f, 3)
2105                        f.write(tab(2) + "}\n") # factor pass
2106                       
2107                        # ambient and specular pass
2108                        f.write(tab(2) + "pass\n" + tab(2) + "{\n")
2109                        self.writeAmbient(f, [1.0, 1.0, 1.0], 3)
2110                        f.write(tab(3) + "diffuse 0.0 0.0 0.0\n")
2111                        self.writeSpecular(f, 3)
2112                        f.write(tab(3) + "scene_blend add\n")
2113                        self.writeCommonOptions(f, 3)
2114                        f.write(tab(2) + "}\n") # specular pass
2115               
2116                f.write(tab(1) + "}\n") # technique
2117                return
2118        def writeNormalMap(self, f):
2119                # preconditions COL and NOR textures
2120                for mtex in self.material.getTextures():
2121                        if mtex:
2122                                if (mtex.tex.type == Blender.Texture.Types['IMAGE']):
2123                                        if (mtex.texco & Blender.Texture.TexCo['UV']):
2124                                                if (mtex.mapto & Blender.Texture.MapTo['COL']):
2125                                                        colImage = PathName(mtex.tex.image.filename).basename()
2126                                                if (mtex.mapto & Blender.Texture.MapTo['NOR']):
2127                                                        norImage = PathName(mtex.tex.image.filename).basename()
2128                f.write("""     technique
2129        {
2130                pass
2131                {
2132                        ambient 1 1 1
2133                        diffuse 0 0 0
2134                        specular 0 0 0 0
2135                        vertex_program_ref Ogre/BasicVertexPrograms/AmbientOneTexture
2136                        {
2137                                param_named_auto worldViewProj worldviewproj_matrix
2138                                param_named_auto ambient ambient_light_colour
2139                        }
2140                }
2141                pass
2142                {
2143                        ambient 0 0 0
2144                        iteration once_per_light
2145                        scene_blend add
2146                        vertex_program_ref Examples/BumpMapVPSpecular
2147                        {
2148                                param_named_auto lightPosition light_position_object_space 0
2149                                param_named_auto eyePosition camera_position_object_space
2150                                param_named_auto worldViewProj worldviewproj_matrix
2151                        }
2152                        fragment_program_ref Examples/BumpMapFPSpecular
2153                        {
2154                                param_named_auto lightDiffuse light_diffuse_colour 0
2155                                param_named_auto lightSpecular light_specular_colour 0
2156                        }
2157                        texture_unit
2158                        {
2159                                texture %s
2160                                colour_op replace
2161                        }
2162                        texture_unit
2163                        {
2164                                cubic_texture nm.png combinedUVW
2165                                tex_coord_set 1
2166                                tex_address_mode clamp
2167                        }
2168                        texture_unit
2169                        {
2170                                cubic_texture nm.png combinedUVW
2171                                tex_coord_set 2
2172                                tex_address_mode clamp
2173                        }
2174                }
2175                pass
2176                {
2177                        lighting off
2178                        vertex_program_ref Ogre/BasicVertexPrograms/AmbientOneTexture
2179                        {
2180                                param_named_auto worldViewProj worldviewproj_matrix
2181                                param_named ambient float4 1 1 1 1
2182                        }
2183                        scene_blend dest_colour zero
2184                        texture_unit
2185                        {
2186                                texture %s
2187                        }
2188                }
2189        }
2190        technique
2191        {
2192                pass
2193                {
2194                        ambient 1 1 1
2195                        diffuse 0 0 0
2196                        specular 0 0 0 0
2197                        vertex_program_ref Ogre/BasicVertexPrograms/AmbientOneTexture
2198                        {
2199                                param_named_auto worldViewProj worldviewproj_matrix
2200                                param_named_auto ambient ambient_light_colour
2201                        }
2202                }
2203                pass
2204                {
2205                        ambient 0 0 0
2206                        iteration once_per_light
2207                        scene_blend add
2208                        vertex_program_ref Examples/BumpMapVP
2209                        {
2210                                param_named_auto lightPosition light_position_object_space 0
2211                                param_named_auto eyePosition camera_position_object_space
2212                                param_named_auto worldViewProj worldviewproj_matrix
2213                        }
2214                        texture_unit
2215                        {
2216                                texture %s
2217                                colour_op replace
2218                        }
2219                        texture_unit
2220                        {
2221                                cubic_texture nm.png combinedUVW
2222                                tex_coord_set 1
2223                                tex_address_mode clamp
2224                                colour_op_ex dotproduct src_texture src_current
2225                                colour_op_multipass_fallback dest_colour zero
2226                        }
2227                }
2228                pass
2229                {
2230                        lighting off
2231                        vertex_program_ref Ogre/BasicVertexPrograms/AmbientOneTexture
2232                        {
2233                                param_named_auto worldViewProj worldviewproj_matrix
2234                                param_named ambient float4 1 1 1 1
2235                        }
2236                        scene_blend dest_colour zero
2237                        texture_unit
2238                        {
2239                                texture %s
2240                        }
2241                }
2242        }
2243""" % (norImage, colImage, norImage, colImage))
2244                return
2245        def writeReceiveShadows(self, f, indent=0):
2246                if (self.material.mode & Blender.Material.Modes["SHADOW"]):
2247                        f.write(tab(indent)+"receive_shadows on\n")
2248                else:
2249                        f.write(tab(indent)+"receive_shadows off\n")
2250                return
2251        def writeAmbient(self, f, col, indent=0):
2252                # ambient <- amb * ambient RGB
2253                ambR = clamp(self.material.getAmb() * col[0])
2254                ambG = clamp(self.material.getAmb() * col[1])
2255                ambB = clamp(self.material.getAmb() * col[2])
2256                if len(col) < 4:
2257                        alpha = self.material.getAlpha()
2258                else:
2259                        alpha = col[3]
2260                f.write(tab(indent)+"ambient %f %f %f %f\n" % (ambR, ambG, ambB, alpha))
2261                return
2262        def writeDiffuse(self, f, col, indent=0):
2263                # diffuse = reflectivity*colour
2264                diffR = clamp(col[0] * self.material.getRef())
2265                diffG = clamp(col[1] * self.material.getRef())
2266                diffB = clamp(col[2] * self.material.getRef())
2267                if len(col) < 4:
2268                        alpha = self.material.getAlpha()
2269                else:
2270                        alpha = col[3]
2271                f.write(tab(indent)+"diffuse %f %f %f %f\n" % (diffR, diffG, diffB, alpha))
2272                return
2273        def writeSpecular(self, f, indent=0):
2274                # specular <- spec * specCol, hard/4.0
2275                specR = clamp(self.material.getSpec() * self.material.getSpecCol()[0])
2276                specG = clamp(self.material.getSpec() * self.material.getSpecCol()[1])
2277                specB = clamp(self.material.getSpec() * self.material.getSpecCol()[2])
2278                specShine = self.material.getHardness()/4.0
2279                alpha = self.material.getAlpha()
2280                f.write(tab(indent)+"specular %f %f %f %f %f\n" % (specR, specG, specB, alpha, specShine))
2281                return
2282        def writeEmissive(self, f, col, indent=0):
2283                # emissive <-emit * rgbCol
2284                emR = clamp(self.material.getEmit() * col[0])
2285                emG = clamp(self.material.getEmit() * col[1])
2286                emB = clamp(self.material.getEmit() * col[2])
2287                if len(col) < 4:
2288                        alpha = self.material.getAlpha()
2289                else:
2290                        alpha = col[3]
2291                f.write(tab(indent)+"emissive %f %f %f %f\n" % (emR, emG, emB, alpha))
2292                return
2293        def writeSceneBlend(self, f, indent=0):
2294                hasAlpha = 0
2295                if (self.material.getAlpha() < 1.0):
2296                        hasAlpha = 1
2297                else:
2298                        for mtex in self.material.getTextures():
2299                                if mtex:
2300                                        if ((mtex.tex.type == Blender.Texture.Types['IMAGE'])
2301                                                and (mtex.mapto & Blender.Texture.MapTo['ALPHA'])):
2302                                                hasAlpha = 1
2303                if (hasAlpha):
2304                        f.write(tab(indent) + "scene_blend alpha_blend\n")
2305                        f.write(tab(indent) + "depth_write off\n")
2306                return
2307        def writeCommonOptions(self, f, indent=0):
2308                # Shadeless, ZInvert, NoMist, Env
2309                # depth_func  <- ZINVERT; ENV
2310                if (self.material.mode & Blender.Material.Modes['ENV']):
2311                        f.write(tab(indent)+"depth_func always_fail\n")
2312                elif (self.material.mode & Blender.Material.Modes['ZINVERT']):
2313                        f.write(tab(indent)+"depth_func greater_equal\n")
2314                # twoside
2315                if (self.face.mode & Blender.NMesh.FaceModes['TWOSIDE']):
2316                        f.write(tab(3) + "cull_hardware none\n")
2317                        f.write(tab(3) + "cull_software none\n")
2318                # lighting <- SHADELESS
2319                if (self.material.mode & Blender.Material.Modes['SHADELESS']):
2320                        f.write(tab(indent)+"lighting off\n")
2321                # fog_override <- NOMIST
2322                if (self.material.mode & Blender.Material.Modes['NOMIST']):
2323                        f.write(tab(indent)+"fog_override true\n")
2324                return
2325        def writeDiffuseTexture(self, f, indent = 0):
2326                diffuseMTex = None
2327                for mtex in self.material.getTextures():
2328                        if mtex:
2329                                if ((mtex.tex.type == Blender.Texture.Types['IMAGE'])
2330                                and (mtex.texco & Blender.Texture.TexCo['UV'])
2331                                and (mtex.mapto & Blender.Texture.MapTo['COL'])):
2332                                        diffuseMTex = mtex
2333                if diffuseMTex:
2334                        f.write(tab(indent)+"texture_unit\n")
2335                        f.write(tab(indent)+"{\n")
2336                        f.write(tab(indent + 1) + "texture %s\n" % PathName(diffuseMTex.tex.getImage().getFilename()).basename())
2337                        self.writeTextureAddressMode(f, diffuseMTex, indent + 1)
2338                        self.writeTextureFiltering(f, diffuseMTex, indent + 1)                 
2339                        self.writeTextureColourOp(f, diffuseMTex, indent + 1)
2340                        f.write(tab(indent)+"}\n") # texture_unit
2341                return
2342        def writeTextureAddressMode(self, f, blenderMTex, indent = 0):
2343                # tex_address_mode inside texture_unit
2344                #
2345                # EXTEND   | clamp
2346                # CLIP     |
2347                # CLIPCUBE |
2348                # REPEAT   | wrap
2349                #
2350                if (blenderMTex.tex.extend & Blender.Texture.ExtendModes['REPEAT']):
2351                        f.write(tab(indent) + "tex_address_mode wrap\n")
2352                elif (blenderMTex.tex.extend & Blender.Texture.ExtendModes['EXTEND']):
2353                        f.write(tab(indent) + "tex_address_mode clamp\n")               
2354                return
2355        def writeTextureFiltering(self, f, blenderMTex, indent = 0):
2356                # filtering inside texture_unit
2357                #
2358                # InterPol | MidMap | filtering
2359                # ---------+--------+----------
2360                #    yes   |   yes  | trilinear
2361                #    yes   |   no   | linear linear none
2362                #    no    |   yes  | bilinear
2363                #    no    |   no   | none
2364                #
2365                if (blenderMTex.tex.imageFlags & Blender.Texture.ImageFlags['INTERPOL']):
2366                        if (blenderMTex.tex.imageFlags & Blender.Texture.ImageFlags['MIPMAP']):
2367                                f.write(tab(indent) + "filtering trilinear\n")
2368                        else:
2369                                f.write(tab(indent) + "filtering linear linear none\n")
2370                else:
2371                        if (blenderMTex.tex.imageFlags & Blender.Texture.ImageFlags['MIPMAP']):
2372                                f.write(tab(indent) + "filtering bilinear\n")
2373                        else:
2374                                f.write(tab(indent) + "filtering none\n")
2375                return
2376        def writeTextureColourOp(self, f, blenderMTex, indent = 0):
2377                # colour_op inside texture_unit
2378                if ((blenderMTex.tex.imageFlags & Blender.Texture.ImageFlags['USEALPHA'])
2379                        and not(blenderMTex.mapto & Blender.Texture.MapTo['ALPHA'])):
2380                        f.write(tab(indent) + "colour_op alpha_blend\n")
2381                return
2382        # private
2383        def _createName(self):
2384                materialName = self.material.getName()
2385                # two sided?
2386                if (self.face.mode & Blender.NMesh.FaceModes['TWOSIDE']):
2387                        materialName += '/TWOSIDE'
2388                return materialName
2389        VCOLLIGHT = 1
2390        VCOLPAINT = 2
2391        TEXFACE = 4
2392        IMAGEUVCOL = 8
2393        IMAGEUVNOR = 16
2394        IMAGEUVCSP = 32
2395        # material techniques export methods
2396        TECHNIQUES = {
2397        IMAGEUVCOL : writeColours,
2398        IMAGEUVCOL|IMAGEUVCSP : writeColours,
2399        VCOLPAINT : writeVertexColours,
2400        IMAGEUVCOL|IMAGEUVNOR : writeNormalMap,
2401        IMAGEUVCOL|IMAGEUVNOR|VCOLLIGHT : writeNormalMap,
2402        IMAGEUVCOL|IMAGEUVNOR|VCOLPAINT : writeNormalMap,
2403        IMAGEUVCOL|IMAGEUVNOR|TEXFACE : writeNormalMap,
2404        IMAGEUVCOL|IMAGEUVNOR|IMAGEUVCSP : writeNormalMap,
2405        IMAGEUVCOL|IMAGEUVNOR|VCOLLIGHT|VCOLPAINT : writeNormalMap,
2406        IMAGEUVCOL|IMAGEUVNOR|VCOLLIGHT|TEXFACE : writeNormalMap,
2407        IMAGEUVCOL|IMAGEUVNOR|VCOLLIGHT|IMAGEUVCSP : writeNormalMap,
2408        IMAGEUVCOL|IMAGEUVNOR|VCOLPAINT|TEXFACE : writeNormalMap,
2409        IMAGEUVCOL|IMAGEUVNOR|VCOLPAINT|IMAGEUVCSP : writeNormalMap,
2410        IMAGEUVCOL|IMAGEUVNOR|TEXFACE|IMAGEUVCSP : writeNormalMap,
2411        IMAGEUVCOL|IMAGEUVNOR|VCOLPAINT|TEXFACE|IMAGEUVCSP : writeNormalMap,
2412        IMAGEUVCOL|IMAGEUVNOR|VCOLLIGHT|TEXFACE|IMAGEUVCSP : writeNormalMap,
2413        IMAGEUVCOL|IMAGEUVNOR|VCOLLIGHT|VCOLPAINT|IMAGEUVCSP : writeNormalMap,
2414        IMAGEUVCOL|IMAGEUVNOR|VCOLLIGHT|VCOLPAINT|TEXFACE : writeNormalMap,
2415        IMAGEUVCOL|IMAGEUVNOR|VCOLLIGHT|VCOLPAINT|TEXFACE|IMAGEUVCSP : writeNormalMap
2416        }       
2417
2418class Mesh:
2419        def __init__(self, submeshList, skeleton=None, nmesh=None):
2420                """Constructor.
2421               
2422                   @param submeshList Submeshes.
2423                   @param nmesh Blender NMesh.
2424                """
2425                self.name = ''
2426                self.submeshList = submeshList
2427                self.skeleton = skeleton
2428                # boolean
2429                self.vertexColours = 0
2430                # boolean
2431                self.uvCoordinates = 0
2432                # parse nmesh
2433                self._parseNMesh(nmesh)
2434                return
2435        def hasVertexColours(self):
2436                return self.vertexColours
2437        def hasUVCoordinates(self):
2438                return self.uvCoordinates
2439        def write(self):
2440                # write_mesh(name, submeshes, skeleton):
2441                global pathString, exportLogger
2442                file = self.name + ".mesh.xml"
2443                exportLogger.logInfo("Mesh \"%s\"" % file)
2444               
2445                f = open(os.path.join(pathString.val, file), "w")
2446                f.write(tab(0)+"<mesh>\n")
2447                f.write(tab(1)+"<submeshes>\n")
2448                for submesh in self.submeshList:
2449                        f.write(tab(2)+"<submesh")
2450                        f.write(" material=\"%s\"" % submesh.material.name)
2451                        f.write(" usesharedvertices=\"false\"")
2452                        f.write(" use32bitindexes=\"false\"")
2453                        f.write(" operationtype=\"triangle_list\"")
2454                        f.write(">\n")
2455                       
2456                        f.write(tab(3)+"<faces count=\"%d\">\n" % len(submesh.faces))
2457                        for face in submesh.faces:
2458                                v1, v2, v3  = face.vertex1.id, face.vertex2.id, face.vertex3.id
2459                                f.write(tab(4)+"<face v1=\"%d\" v2=\"%d\" v3=\"%d\"/>\n" % (v1, v2, v3))
2460                        f.write(tab(3)+"</faces>\n")
2461       
2462                        f.write(tab(3)+"<geometry vertexcount=\"%d\">\n" % len(submesh.vertices))
2463                        if (armatureToggle.val):
2464                                # use seperate vertexbuffer for position and normals when animated
2465                                f.write(tab(4)+"<vertexbuffer positions=\"true\" normals=\"true\">\n")
2466                                for v in submesh.vertices:
2467                                        f.write(XMLVertexStringView(v.xmlVertex).toString(5, ['normal','position']))
2468                                f.write(tab(4)+"</vertexbuffer>\n")
2469                                if (self.hasUVCoordinates() and self.hasVertexColours()):
2470                                        f.write(tab(4)+"<vertexbuffer")
2471                                        f.write(" texture_coord_dimensions_0=\"2\" texture_coords=\"1\"")
2472                                        f.write(" colours_diffuse=\"true\">\n")
2473                                        for v in submesh.vertices:
2474                                                        f.write(XMLVertexStringView(v.xmlVertex).toString(5, ['texcoordList','colourDiffuse']))
2475                                        f.write(tab(4)+"</vertexbuffer>\n")
2476                                elif self.hasUVCoordinates():
2477                                        f.write(tab(4)+"<vertexbuffer")
2478                                        f.write(" texture_coord_dimensions_0=\"2\" texture_coords=\"1\">\n")
2479                                        for v in submesh.vertices:
2480                                                        f.write(XMLVertexStringView(v.xmlVertex).toString(5, ['texcoordList']))
2481                                        f.write(tab(4)+"</vertexbuffer>\n")
2482                                elif self.hasVertexColours():
2483                                        f.write(tab(4)+"<vertexbuffer")
2484                                        f.write(" colours_diffuse=\"true\">\n")
2485                                        for v in submesh.vertices:
2486                                                        f.write(XMLVertexStringView(v.xmlVertex).toString(5, ['colourDiffuse']))
2487                                        f.write(tab(4)+"</vertexbuffer>\n")
2488                        else:
2489                                # use only one vertex buffer if mesh is not animated
2490                                f.write(tab(4)+"<vertexbuffer ")
2491                                f.write("positions=\"true\" ")
2492                                f.write("normals=\"true\"")
2493                                if self.hasUVCoordinates():
2494                                        f.write(" texture_coord_dimensions_0=\"2\" texture_coords=\"1\"")
2495                                if self.hasVertexColours():
2496                                        f.write(" colours_diffuse=\"true\"")
2497                                f.write(">\n")
2498                                for v in submesh.vertices:
2499                                        f.write(XMLVertexStringView(v.xmlVertex).toString(5))
2500                                f.write(tab(4)+"</vertexbuffer>\n")
2501                        f.write(tab(3)+"</geometry>\n")
2502               
2503                        if self.skeleton:
2504                                f.write(tab(3)+"<boneassignments>\n")
2505                                for v in submesh.vertices:
2506                                        for influence in v.influences:
2507                                                f.write(tab(4)+"<vertexboneassignment ")
2508                                                f.write("vertexindex=\"%d\" boneindex=\"%d\" weight=\"%.6f\"/>\n"
2509                                                        % (v.id, influence.bone.id, influence.weight))
2510                                f.write(tab(3)+"</boneassignments>\n")
2511                        f.write(tab(2)+"</submesh>\n")
2512                f.write(tab(1)+"</submeshes>\n")
2513       
2514                if self.skeleton:
2515                        f.write(tab(1)+"<skeletonlink name=\"%s.skeleton\"/>\n" % self.skeleton.name)
2516       
2517                f.write(tab(0)+"</mesh>\n")   
2518                f.close()
2519                convertXMLFile(os.path.join(pathString.val, file))
2520                return
2521        # private
2522        def _parseNMesh(self, nmesh):
2523                if nmesh:
2524                        self.name = nmesh.name
2525                        if nmesh.hasVertexColours():
2526                                self.vertexColours = 1
2527                        if (nmesh.hasFaceUV() or nmesh.hasVertexUV()):
2528                                self.uvCoordinates = 1
2529                return
2530
2531class SubMesh:
2532  def __init__(self, material):
2533    self.material   = material
2534    self.vertices   = []
2535    self.faces      = []
2536
2537  def rename_vertices(self, new_vertices):
2538    # Rename (change ID) of all vertices, such as self.vertices == new_vertices.
2539    for i in range(len(new_vertices)): new_vertices[i].id = i
2540    self.vertices = new_vertices
2541
2542class XMLVertex:
2543        """Vertex in Ogre.
2544       
2545           @cvar threshold Floating point precicsion.
2546        """
2547        threshold = 1e-6
2548        def __init__(self, position=None, normal=None, colourDiffuse=None, colourSpecular=None, texcoordList=None):
2549                """Constructor.
2550               
2551                   @param position       list with x, y and z coordinate of the position
2552                   @param normal         list with x, y and z coordinate of the normal vector
2553                   @param colourDiffuse  list with RGBA floats
2554                   @param colourSpecular list with RGBA floats
2555                   @param texcoordList   list of list with u and v texture coordinates.
2556                """
2557                self.elementDict = {}
2558                if position:
2559                        self.elementDict['position'] = position
2560                if normal:
2561                        self.elementDict['normal'] = normal
2562                if colourDiffuse:
2563                        self.elementDict['colourDiffuse'] = colourDiffuse
2564                if colourSpecular:
2565                        self.elementDict['colourSpecular'] = colourSpecular
2566                if texcoordList:
2567                        self.elementDict['texcoordList'] = texcoordList
2568                return
2569        def hasPosition(self):
2570                return self.elementDict.has_key('position')
2571        def hasNormal(self):
2572                return self.elementDict.has_key('normal')
2573        def hasVertexColour(self):
2574                return self.elementDict.has_key('colourDiffuse') or self.elementDict.has_key('colourSpecular')
2575        def hasDiffuseColour(self):
2576                return self.elementDict.has_key('colourDiffuse')
2577        def hasSpecularColour(self):
2578                return self.elementDict.has_key('colourSpecular')
2579        def nTextureCoordinates(self):
2580                nTextureCoordinates = 0
2581                if self.elementDict.has_key('texcoordList'):
2582                        nTextureCoordinates = len(self.elementDict['texcoordList'])
2583                return nTextureCoordinates
2584        def __getitem__(self, key):
2585                return self.elementDict[key]
2586        def __ne__(self, other):
2587                """Tests if it differs from another Vertex.
2588                   
2589                   @param other the XMLVertex to compare with
2590                   @return <code>true</code> if they differ, else <code>false</code>
2591                """
2592                return not self.__eq__(other)
2593        def __eq__(self, other):
2594                """Tests if it is equal to another Vertex.
2595               
2596                   @param other the XMLVertex to compare with
2597                   @return <code>true</code> if they are equal, else <code>false</code>
2598                """
2599                areEqual = 0
2600                if (self.getElements() == other.getElements()):
2601                        compared = 0
2602                        itemIterator = self.elementDict.iteritems()
2603                        while (not compared):
2604                                try:
2605                                        (element, value) = itemIterator.next()
2606                                        if element == 'position' or element == 'normal':
2607                                                otherValue = other[element]
2608                                                if ((math.fabs(value[0] - otherValue[0]) > XMLVertex.threshold) or
2609                                                        (math.fabs(value[1] - otherValue[1]) > XMLVertex.threshold) or
2610                                                        (math.fabs(value[2] - otherValue[2]) > XMLVertex.threshold)):
2611                                                        # test fails
2612                                                        compared = 1
2613                                        elif element == 'colourDiffuse' or element == 'colourSpecular':
2614                                                otherValue = other[element]
2615                                                if ((math.fabs(value[0] - otherValue[0]) > XMLVertex.threshold) or
2616                                                        (math.fabs(value[1] - otherValue[1]) > XMLVertex.threshold) or
2617                                                        (math.fabs(value[2] - otherValue[2]) > XMLVertex.threshold) or
2618                                                        (math.fabs(value[3] - otherValue[3]) > XMLVertex.threshold)):
2619                                                        # test fails
2620                                                        compared = 1
2621                                        elif element == 'texcoordList':
2622                                                otherValue = other[element]
2623                                                if len(value) == len(otherValue):
2624                                                        for uv, otherUV in zip(value, otherValue):
2625                                                                if ((math.fabs(uv[0] - otherUV[0]) > XMLVertex.threshold) or
2626                                                                        (math.fabs(uv[1] - otherUV[1]) > XMLVertex.threshold)):
2627                                                                        # test fails
2628                                                                        compared = 1
2629                                                else:
2630                                                        # test fails
2631                                                        compared = 1
2632                                        else:
2633                                                # test fails, unknown element
2634                                                compared = 1
2635                                except StopIteration:
2636                                        # objects are equal
2637                                        areEqual = 1
2638                                        compared = 1
2639                return areEqual
2640        # getter and setter
2641        def getElements(self):
2642                return self.elementDict.keys()
2643        def getPosition(self):
2644                return self.elementDict['position']
2645        def getNormal(self):
2646                return self.elementDict['normal']
2647        def getColourDiffuse(self):
2648                return self.elementDict['colourDiffuse']
2649        def getColourSpecular(self):
2650                return self.elementDict['colourSpecular']
2651        def getTextureCoordinatesList(self):
2652                return self.elementDict['texcoordList']
2653        def setPosition(self, position):
2654                if position:
2655                        self.elementDict['position'] = position
2656                else:
2657                        del self.elementDict['position']
2658                return
2659        def setNormal(self, normal):
2660                if normal:
2661                        self.elementDict['normal'] = normal
2662                else:
2663                        del self.elementDict['normal']
2664                return
2665        def setColourDiffuse(self, colourDiffuse):
2666                if colourDiffuse:
2667                        self.elementDict['colourDiffuse'] = colourDiffuse
2668                else:
2669                        del self.colourDiffuse
2670                return
2671        def setColourSpecular(self, colourSpecular):
2672                if colourSpecular:
2673                        self.elementDict['colourSpecular'] = colourSpecular
2674                else:
2675                        del self.elementDict['colourSpecular']
2676                return
2677        # special getter and setter
2678        def appendTextureCoordinates(self, uvList):
2679                """Appends new texture coordinate.
2680               
2681                   @param uvList list with u and v coordinate
2682                   @return list index
2683                """
2684                if self.elementDict.has_key('texcoordList'):
2685                        self.elementDict['texcoordList'].append(uvList)
2686                else:
2687                        self.elementDict['texcoordList'] = [uvList]
2688                return (len(self.elementDict['texcoordList']) -1 )
2689        def setTextureCorrdinates(self, index, uvList):
2690                self.elementDict['texcoordList'][index] = uvList
2691                return
2692        def getTextureCoordinates(self, index=None):
2693                return self.elementDict['texcoordList'][index]
2694        def deleteTextureCoordinates(self, index=None):
2695                """Delete texture coordinates.
2696               
2697                   Delete a pair or all texture coordinates of the vertex.
2698                   
2699                   @param index the index of the texture coordinates in the vertex's list of
2700                                texture coordinates. If <code>None</code> the complete list
2701                                is deleted.
2702                """
2703                if (index != None):
2704                        del self.elementDict['texcoordList'][index]
2705                else:
2706                        del self.elementDict['texcoordList']
2707                return
2708
2709class XMLVertexStringView:
2710        """Viewer class for textual representation of a XMLVertex.
2711       
2712           @see XMLVertex
2713        """
2714        def __init__(self, xmlVertex):
2715                if isinstance(xmlVertex, XMLVertex):
2716                        self.xmlVertex = xmlVertex
2717                return
2718        def __str__(self):
2719                return self.toString()
2720        def toString(self, indent=0, keyList=None):
2721                """Returns textual representations of its XMLVertex.
2722               
2723                   @param indent Indentation level of the string.
2724                   @param keyList List of keys of elements to represent in the string.
2725                   @return string String representation of the XMLVertex.
2726                   @see XMLVertex#__init__
2727                """
2728                if not keyList:
2729                        keyList = self.xmlVertex.getElements()
2730                else:
2731                        # remove unavailable elements
2732                        keyList = [key for key in keyList if key in self.xmlVertex.getElements()]
2733                s = self._indent(indent) + "<vertex>\n"
2734                if keyList.count('position'):
2735                        position = self.xmlVertex.getPosition()
2736                        s += self._indent(indent+1)+"<position x=\"%.6f\" y=\"%.6f\" z=\"%.6f\"/>\n" % tuple(position)
2737                if keyList.count('normal'):
2738                        normal = self.xmlVertex.getNormal()
2739                        s += self._indent(indent+1)+"<normal x=\"%.6f\" y=\"%.6f\" z=\"%.6f\"/>\n" % tuple(normal)
2740                if keyList.count('colourDiffuse'):
2741                        colourDiffuse = self.xmlVertex.getColourDiffuse()
2742                        if OGRE_OPENGL_VERTEXCOLOUR:
2743                                (r, g, b, a) = tuple(colourDiffuse)
2744                                s += self._indent(indent+1)+"<colour_diffuse value=\"%.6f %.6f %.6f %.6f\"/>\n" % (b, g, r, a)
2745                        else:
2746                                s += self._indent(indent+1)+"<colour_diffuse value=\"%.6f %.6f %.6f %.6f\"/>\n" % tuple(colourDiffuse)
2747                if keyList.count('colourSpecular'):
2748                        colourSpecular = self.xmlVertex.getColourSpecular()
2749                        if OGRE_OPENGL_VERTEXCOLOUR:
2750                                (r, g, b, a) = tuple(colourSpecular)
2751                                s += self._indent(indent+1)+"<colour_specular value=\"%.6f %.6f %.6f %.6f\"/>\n" % (b, g, r, a)
2752                        else:
2753                                s += self._indent(indent+1)+"<colour_specular value=\"%.6f %.6f %.6f %.6f\"/>\n" % tuple(colourSpecular)
2754                if keyList.count('texcoordList'):
2755                        for uv in self.xmlVertex.getTextureCoordinatesList():
2756                                s+=self._indent(indent+1)+"<texcoord u=\"%.6f\" v=\"%.6f\"/>\n" % tuple(uv)
2757                s += self._indent(indent) + "</vertex>\n"
2758                return s
2759        def _indent(self, indent):
2760                return "        "*indent
2761
2762class Vertex:
2763  def __init__(self, submesh, xmlVertex):
2764    self.xmlVertex = xmlVertex
2765    self.influences = []
2766   
2767    self.cloned_from = None
2768    self.clones      = []
2769    self.submesh = submesh
2770    self.id = len(submesh.vertices)
2771    submesh.vertices.append(self)
2772
2773class Influence:
2774  def __init__(self, bone, weight):
2775    self.bone   = bone
2776    self.weight = weight
2777   
2778class Face:
2779  def __init__(self, submesh, vertex1, vertex2, vertex3):
2780    self.vertex1 = vertex1
2781    self.vertex2 = vertex2
2782    self.vertex3 = vertex3
2783    self.submesh = submesh
2784    submesh.faces.append(self)
2785
2786class Skeleton:
2787  def __init__(self, name):
2788    self.name = name
2789    self.bones = []
2790    self.bonesDict = {}
2791    self.animationsDict = {}
2792
2793class Bone:
2794  def __init__(self, skeleton, parent, name, loc, rotQuat, conversionMatrix):
2795    self.parent = parent
2796    self.name   = name
2797    self.loc = loc # offset from parent bone
2798    self.rotQuat = rotQuat # axis as quaternion
2799    self.children = []
2800    self.conversionMatrix = conversionMatrix # converts Blender's local bone coordinates into Ogre's local bone coordinates
2801
2802    if parent:
2803      parent.children.append(self)
2804   
2805    self.id = len(skeleton.bones)
2806    skeleton.bones.append(self)
2807    skeleton.bonesDict[name] =self
2808
2809class Animation:
2810  def __init__(self, name, duration = 0.0):
2811    self.name     = name
2812    self.duration = duration
2813    self.tracksDict = {} # Map bone names to tracks
2814   
2815class Track:
2816  def __init__(self, animation, bone):
2817    self.bone      = bone
2818    self.keyframes = []
2819   
2820    self.animation = animation
2821    animation.tracksDict[bone.name] = self
2822   
2823class KeyFrame:
2824  def __init__(self, track, time, loc, rotQuat, scale):
2825    self.time = time
2826    self.loc  = loc
2827    self.rotQuat  = rotQuat
2828    self.scale = scale
2829   
2830    self.track = track
2831    track.keyframes.append(self)
2832
2833#######################################################################################
2834## Armature stuff
2835
2836def blender_bone2matrix(head, tail, roll):
2837  # Convert bone rest state (defined by bone.head, bone.tail and bone.roll)
2838  # to a matrix (the more standard notation).
2839  # Taken from blenkernel/intern/armature.c in Blender source.
2840  # See also DNA_armature_types.h:47.
2841 
2842  nor = vector_normalize([ tail[0] - head[0],
2843                           tail[1] - head[1],
2844                           tail[2] - head[2] ])
2845
2846  # Find Axis & Amount for bone matrix
2847  target = [0.0, 1.0, 0.0]
2848  axis = vector_crossproduct(target, nor)
2849 
2850  # is nor a multiple of target?
2851  if vector_dotproduct(axis, axis) > 0.0000000000001:
2852    axis  = vector_normalize(axis)
2853    theta = math.acos(vector_dotproduct(target, nor))
2854    bMatrix = matrix_rotate(axis, theta)
2855
2856  else:
2857    # point same direction, or opposite?
2858    if vector_dotproduct(target, nor) > 0.0:
2859      updown = 1.0   
2860    else:
2861      updown = -1.0
2862   
2863    # Quoted from Blender source : "I think this should work ..."
2864    bMatrix = [ [updown,    0.0, 0.0, 0.0],
2865                [   0.0, updown, 0.0, 0.0],
2866                [   0.0,    0.0, 1.0, 0.0],
2867                [   0.0,    0.0, 0.0, 1.0] ]
2868
2869  rMatrix = matrix_rotate(nor, roll)
2870  return matrix_multiply(rMatrix, bMatrix)
2871
2872#######################################################################################
2873## Mesh stuff
2874
2875# remap vertices for faces
2876def process_face(face, submesh, mesh, matrix, skeleton=None):
2877        """Process a face of a mesh.
2878       
2879           @param face Blender.NMesh.NMFace.
2880           @param submesh SubMesh the face belongs to.
2881           @param mesh Blender.NMesh.NMesh the face belongs to.
2882           @param matrix Export translation.
2883           @param skeleton Skeleton of the mesh (if any).
2884        """
2885        global verticesDict
2886        global exportLogger
2887        # threshold to compare floats
2888        threshold = 1e-6
2889        if len(face.v) in [ 3, 4 ]:
2890                if not face.smooth:
2891                        # calculate the face normal.
2892                        p1 = face.v[0].co
2893                        p2 = face.v[1].co
2894                        p3 = face.v[2].co
2895                        faceNormal = vector_crossproduct(
2896                                [p3[0] - p2[0], p3[1] - p2[1], p3[2] - p2[2]],
2897                                [p1[0] - p2[0], p1[1] - p2[1], p1[2] - p2[2]],
2898                                )
2899                        faceNormal = normal_by_matrix(faceNormal, matrix)
2900
2901                face_vertices = [ 0, 0, 0, 0]
2902                for i in range(len(face.v)):
2903                        # position
2904                        position  = point_by_matrix (face.v[i].co, matrix)
2905                        # Blender separates normal, uv coordinates and colour from vertice coordinates.
2906                        # normal
2907                        if face.smooth:
2908                                normal = normal_by_matrix(face.v[i].no, matrix)
2909                        else:
2910                                normal = faceNormal
2911                        xmlVertex = XMLVertex(position, normal)
2912                        # uv coordinates
2913                        #remove#if submesh.material.texture:
2914                        if (mesh.hasVertexUV() or mesh.hasFaceUV()):
2915                                uv = [0,0]
2916                                if mesh.hasVertexUV():
2917                                        # mesh has sticky/per vertex uv coordinates
2918                                        uv[0] = face.v[i].uvco[0]
2919                                        # origin is now in the top-left (Ogre v0.13.0)
2920                                        uv[1] = 1 - face.v[i].uvco[1]
2921                                else:
2922                                        # mesh has per face vertex uv coordinates
2923                                        uv[0] = face.uv[i][0]
2924                                        # origin is now in the top-left (Ogre v0.13.0)
2925                                        uv[1] = 1 - face.uv[i][1]
2926                                xmlVertex.appendTextureCoordinates(uv)
2927                        # vertex colour
2928                        #remove#if submesh.material.mat:
2929                        #remove#        if (submesh.material.mat.mode & Blender.Material.Modes["VCOL_PAINT"]):
2930                        if (mesh.hasVertexColours()):
2931                                colour = face.col[i]
2932                                xmlVertex.setColourDiffuse([colour.r/255.0, colour.g/255.0, colour.b/255.0, colour.a/255.0])
2933                        # check if an equal xmlVertex already exist
2934                        # get vertex
2935                        if verticesDict.has_key(face.v[i].index):
2936                                # vertex already exists
2937                                vertex = verticesDict[face.v[i].index]
2938                                # compare xmlVertex to vertex and its clones
2939                                if (vertex.xmlVertex != xmlVertex):
2940                                        vertexFound = 0
2941                                        iClone = 0
2942                                        while ((iClone < len(vertex.clones)) and (not vertexFound)):
2943                                                clone = vertex.clones[iClone]
2944                                                if (clone.xmlVertex == xmlVertex):
2945                                                        vertexFound = 1
2946                                                        vertex = clone
2947                                                iClone += 1
2948                                        if not vertexFound:
2949                                                # create new clone
2950                                                clone = Vertex(submesh, xmlVertex)
2951                                                clone.cloned_from = vertex
2952                                                clone.influences = vertex.influences
2953                                                vertex.clones.append(clone)
2954                                                # write back to dictionary
2955                                                verticesDict[face.v[i].index] = vertex
2956                                                vertex = clone
2957                        else:
2958                                # vertex does not exist yet
2959                                # create vertex
2960                                vertex = Vertex(submesh, xmlVertex)
2961                                # set bone influences
2962                                if skeleton:
2963                                        influences = mesh.getVertexInfluences(face.v[i].index)
2964                                        if not influences:
2965                                                exportLogger.logError("Vertex in skinned mesh without influence, check your mesh!")
2966                                        # limit influences to 4 bones per vertex
2967                                        def cmpfunc(x, y):
2968                                                xname, xweight = x
2969                                                yname, yweight = y
2970                                                return cmp(yweight, xweight)
2971                                        influences.sort(cmpfunc)
2972                                        influences = influences[0:4]
2973                                        # and make sure the sum is 1.0
2974                                        total = 0.0
2975                                        for name, weight in influences:
2976                                                total += weight
2977                                        for name, weight in influences:
2978                                                vertex.influences.append(Influence(skeleton.bonesDict[name], weight/total))
2979                                verticesDict[face.v[i].index] = vertex
2980                        # postcondition: vertex is current vertex
2981                        face_vertices[i] = vertex
2982               
2983                if len(face.v) == 3:
2984                        Face(submesh, face_vertices[0], face_vertices[1], face_vertices[2])
2985                elif len(face.v) == 4:
2986                        # Split faces with 4 vertices on the shortest edge
2987                        differenceVectorList = [[0,0,0],[0,0,0]]
2988                        for indexOffset in range(2):
2989                                for coordinate in range(3):
2990                                        differenceVectorList[indexOffset][coordinate] = face_vertices[indexOffset].xmlVertex.getPosition()[coordinate] \
2991                                                                                      - face_vertices[indexOffset+2].xmlVertex.getPosition()[coordinate]
2992                        if Mathutils.Vector(differenceVectorList[0]).length < Mathutils.Vector(differenceVectorList[1]).length:
2993                                Face(submesh, face_vertices[0], face_vertices[1], face_vertices[2])
2994                                Face(submesh, face_vertices[2], face_vertices[3], face_vertices[0])
2995                        else:
2996                                Face(submesh, face_vertices[0], face_vertices[1], face_vertices[3])
2997                                Face(submesh, face_vertices[3], face_vertices[1], face_vertices[2])
2998        else:
2999                exportLogger.logWarning("Ignored face with %d edges." % len(face.v))
3000        return
3001
3002def export_mesh(object, exportOptions):
3003        global gameEngineMaterialsToggle
3004        global armatureToggle
3005        global verticesDict
3006        global skeletonsDict
3007        global materialsDict
3008        global exportLogger
3009       
3010        if (object.getType() == "Mesh"):
3011                # is this mesh attached to an armature?
3012                skeleton = None
3013                if armatureToggle.val:
3014                        parent = object.getParent()
3015                        #if parent and parent.getType() == "Armature" and (not skeletonsDict.has_key(parent.getName())):
3016                        if (parent and (parent.getType() == "Armature")):
3017                                if armatureActionActuatorListViewDict.has_key(parent.getName()):
3018                                        actionActuatorList = armatureActionActuatorListViewDict[parent.getName()].armatureActionActuatorList
3019                                        armatureExporter = ArmatureExporter(MeshExporter(object), parent)
3020                                        armatureExporter.export(actionActuatorList, exportOptions, exportLogger)
3021                                        skeleton = armatureExporter.skeleton
3022                                        #export_skeleton(parent, object)
3023                                        #skeleton = skeletonsDict[parent.getName()]
3024
3025                #NMesh of the object
3026                data = object.getData()
3027                matrix = None
3028                if worldCoordinatesToggle.val:
3029                        matrix = object.getMatrix("worldspace")
3030                else:
3031                        matrix = Mathutils.Matrix([1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1])
3032                matrix = matrix*BASE_MATRIX
3033                # materials of the object
3034                # note: ogre assigns different textures and different facemodes
3035                #       to different materials
3036                objectMaterialDict = {}
3037                # faces assign to objectMaterial keys
3038                objectMaterialFacesDict = {}
3039
3040                # note: these are blender materials. Evene if nMaterials = 0
3041                #       the face can still have a texture (see above)
3042                meshMaterialList = data.getMaterials(1)
3043                # note: material slots may be empty, resp. meshMaterialList entries may be None
3044                nMaterials = len(meshMaterialList)
3045
3046                # create ogre materials
3047                for face in data.faces:
3048                        faceMaterial = None
3049                        # choose "rendering materials" or "game engine materials"
3050                        if not(gameEngineMaterialsToggle.val):
3051                                # rendering materials
3052                                blenderMaterial = meshMaterialList[face.materialIndex]
3053                                if blenderMaterial:
3054                                        # non-empty material slot
3055                                        faceMaterial = RenderingMaterial(data, face)
3056                                else:
3057                                        faceMaterial = DefaultMaterial('default')
3058                        else:
3059                                # game engine materials
3060                                if (not(face.mode & Blender.NMesh.FaceModes['INVISIBLE'])
3061                                        and not(face.flag & Blender.NMesh.FaceFlags['HIDE'])):
3062                                        faceMaterial = GameEngineMaterial(data, face)
3063                        if faceMaterial:
3064                                # insert into Dicts
3065                                materialName = faceMaterial.getName()
3066                                material = objectMaterialDict.get(materialName)
3067                                if material:
3068                                        # append faces
3069                                        faceList = objectMaterialFacesDict[materialName]
3070                                        faceList.append(face)
3071                                        objectMaterialFacesDict[materialName] = faceList
3072                                else:
3073                                        # create new faces list
3074                                        objectMaterialDict[materialName] = faceMaterial
3075                                        objectMaterialFacesDict[materialName] = [face]
3076                # process faces
3077                submeshes = []
3078                for materialKey in objectMaterialDict.keys():
3079                        submesh = SubMesh(objectMaterialDict[materialKey])
3080                        verticesDict = {}
3081                        for face in objectMaterialFacesDict[materialKey]:
3082                                process_face(face, submesh, data, matrix, skeleton)
3083                        if len(submesh.faces):
3084                                submeshes.append(submesh)
3085                                # update global materialsDict
3086                                material = materialsDict.get(materialKey)
3087                                if not material:
3088                                        materialsDict[materialKey] = objectMaterialDict[materialKey]
3089                # write mesh
3090                if len(submeshes) == 0:
3091                        # no submeshes
3092                        exportLogger.logWarning("Mesh %s has no visible faces!" % data.name)
3093                else:
3094                        # write mesh
3095                        mesh = Mesh(submeshes, skeleton, data)
3096                        mesh.write()
3097        return
3098
3099#######################################################################################
3100## file output
3101
3102def tab(tabsize):
3103    return "\t" * tabsize
3104   
3105def clamp(val):
3106    if val < 0.0:
3107        val = 0.0
3108    if val > 1.0:
3109        val = 1.0
3110    return val
3111
3112def convertXMLFile(filename):
3113        """Calls the OgreXMLConverter on a file.
3114           
3115           If the script variable <code>OGRE_XML_CONVERTER</code> is nonempty, the
3116           OgreXMLConverter is called to convert the given file.
3117           
3118           @param filename filename of the XML file to convert.
3119        """
3120        global exportLogger
3121        if OGRE_XML_CONVERTER != '':
3122                commandLine = OGRE_XML_CONVERTER + ' "' + filename + '"'
3123                exportLogger.logInfo("Running OgreXMLConverter: " + commandLine)
3124                xmlConverter = os.popen(commandLine, 'r')
3125                if xmlConverter == None:
3126                        exportLogger.logError('Could not run OgreXMLConverter!')
3127                else:
3128                        for line in xmlConverter:
3129                                exportLogger.logInfo("OgreXMLConverter: " + line)
3130                        xmlConverter.close()
3131        return
3132
3133def write_materials():
3134        global ambientToggle, pathString, materialString, exportLogger
3135        global materialsDict
3136        file = materialString.val
3137        exportLogger.logInfo("Materials \"%s\"" % file)
3138
3139        f = open(os.path.join(pathString.val, file), "w")
3140        for material in materialsDict.values():
3141                material.write(f)
3142        f.close()
3143
3144#######################################################################################
3145## main export
3146
3147def export(selectedObjectsList):
3148    global pathString, scaleNumber, rotXNumber, rotYNumber, rotZNumber
3149    global materialsDict
3150    global skeletonsDict
3151    global BASE_MATRIX
3152    global exportLogger
3153   
3154    materialsDict = {}
3155    skeletonsDict = {}
3156
3157    # default: set matrix to 90 degree rotation around x-axis
3158    # rotation order: x, y, z
3159    # WARNING: Blender uses left multiplication!
3160    rotationMatrix = Mathutils.RotationMatrix(rotXNumber.val,4,'x')
3161    rotationMatrix *= Mathutils.RotationMatrix(rotYNumber.val,4,'y')
3162    rotationMatrix *= Mathutils.RotationMatrix(rotZNumber.val,4,'z')
3163    scaleMatrix = Mathutils.Matrix([scaleNumber.val,0,0],[0,scaleNumber.val,0],[0,0,scaleNumber.val])
3164    scaleMatrix.resize4x4()
3165    BASE_MATRIX = rotationMatrix*scaleMatrix
3166
3167    exportOptions = ExportOptions(rotXNumber.val, rotYNumber.val, rotZNumber.val, scaleNumber.val,
3168        worldCoordinatesToggle.val, ambientToggle.val, pathString.val, materialString.val)
3169
3170    if not os.path.exists(pathString.val):
3171      exportLogger.logError("Invalid path: "+pathString.val)
3172    else:
3173      exportLogger.logInfo("Exporting selected objects into \"" + pathString.val + "\":")
3174      n = 0
3175      for obj in selectedObjectsList:
3176          if obj:
3177              if obj.getType() == "Mesh":
3178                  exportLogger.logInfo("Exporting object \"%s\":" % obj.getName())
3179                  export_mesh(obj, exportOptions)
3180                  n = 1
3181              elif obj.getType() == "Armature":
3182                  exportLogger.logInfo("Exporting object \"%s\":" % obj.getName())
3183                  actionActuatorList = armatureActionActuatorListViewDict[obj.getName()].armatureActionActuatorList
3184                  armatureMeshExporter = ArmatureMeshExporter(obj)
3185                  armatureMeshExporter.export(materialsDict, actionActuatorList, exportOptions, exportLogger)
3186      if n == 0:
3187          exportLogger.logWarning("No mesh objects selected!")
3188      if len(materialsDict) == 0:
3189          exportLogger.logWarning("No materials or textures defined!")
3190      else:
3191          write_materials()
3192     
3193      exportLogger.logInfo("Finished.")
3194    return exportLogger.getStatus()
3195   
3196#######################################################################################
3197## GUI
3198
3199######
3200# global variables
3201######
3202# see above
3203
3204######
3205# methods
3206######
3207def saveSettings():
3208        """Save all exporter settings of selected and unselected objects into a blender text object.
3209       
3210           Settings are saved to the text 'ogreexport.cfg' inside the current .blend file. Settings
3211           belonging to removed objects in the .blend file will not be saved.
3212           
3213           @return <code>true</code> on success, else <code>false</code>
3214        """
3215        global gameEngineMaterialsToggle
3216        global armatureToggle
3217        global worldCoordinatesToggle
3218        global ambientToggle
3219        global pathString
3220        global materialString
3221        global scaleNumber
3222        global rotXNumber, rotYNumber, rotZNumber
3223        global fpsNumber
3224        global selectedObjectsList
3225        global armatureDict
3226        global armatureActionActuatorListViewDict
3227        global armatureAnimationDictListDict
3228        settingsDict = {}
3229        success = 0
3230        # save general settings
3231        settingsDict['gameEngineMaterialsToggle'] = gameEngineMaterialsToggle.val
3232        settingsDict['armatureToggle'] = armatureToggle.val
3233        settingsDict['worldCoordinatesToggle'] = worldCoordinatesToggle.val
3234        settingsDict['ambientToggle'] = ambientToggle.val
3235        settingsDict['pathString'] = pathString.val
3236        settingsDict['materialString'] = materialString.val
3237        settingsDict['scaleNumber'] = scaleNumber.val
3238        settingsDict['rotXNumber'] = rotXNumber.val
3239        settingsDict['rotYNumber'] = rotYNumber.val
3240        settingsDict['rotZNumber'] = rotZNumber.val
3241        if (Blender.Get("version") < 233):
3242                settingsDict['fpsNumber'] = fpsNumber.val
3243        else:
3244                # get blender's "scene->format->frames per second" setting
3245                settingsDict['fpsNumber'] = Blender.Scene.GetCurrent().getRenderingContext().framesPerSec()
3246        # save object specific settings
3247        # check if armature exists (I think this is cleaner than catching NameError exceptions.)
3248        # create list of valid armature names
3249        armatureNameList = []
3250        for object in Blender.Object.Get():
3251                if (object.getType() == "Armature"):
3252                        armatureNameList.append(object.getName())
3253        for armatureName in armatureAnimationDictListDict.keys():
3254                if not(armatureName in armatureNameList):
3255                        # remove obsolete settings
3256                        del armatureAnimationDictListDict[armatureName]
3257        # update settings
3258        for armatureName in armatureActionActuatorListViewDict.keys():
3259                armatureAnimationDictListDict[armatureName] = armatureActionActuatorListViewDict[armatureName].getArmatureAnimationDictList()
3260        settingsDict['armatureAnimationDictListDict'] = armatureAnimationDictListDict
3261               
3262        configTextName = 'ogreexport.cfg'
3263        # remove old configuration text
3264        if configTextName in [text.getName() for text in Blender.Text.Get()]:
3265                oldConfigText = Blender.Text.Get(configTextName)
3266                oldConfigText.setName('ogreexport.old')
3267                Blender.Text.unlink(oldConfigText)
3268        # write new configuration text
3269        configText = Blender.Text.New(configTextName)
3270        configText.write('Ogreexport configuration file.\n\nThis file is automatically created. Please don\'t edit this file directly.\n\n')
3271        try:
3272                # pickle
3273                configText.write(pickle.dumps(settingsDict))
3274        except (PickleError):
3275                pass
3276        else:
3277                success = 1
3278        return success
3279
3280def loadSettings(filename):
3281        """Load all exporter settings from text or file.
3282       
3283           Settings are loaded from a text object called 'ogreexport.cfg'.
3284           If it is not found, settings are loaded from the file with the given filename.
3285           <p>
3286           You have to create armatureActionActuatorListViews with the new
3287           armatuerAnimationDictListDict if you want the animation settings
3288           to take effect.
3289       
3290           @param filename where to store the settings
3291           @return <code>true</code> on success, else <code>false</code>
3292        """
3293        global gameEngineMaterialsToggle
3294        global armatureToggle
3295        global worldCoordinatesToggle
3296        global ambientToggle
3297        global pathString
3298        global materialString
3299        global scaleNumber
3300        global rotXNumber, rotYNumber, rotZNumber
3301        global fpsNumber
3302        global selectedObjectsList
3303        global armatureDict
3304        global armatureAnimationDictListDict
3305        settingsDict = {}
3306        success = 0
3307        # try open 'ogreexport.cfg' text
3308        configTextName = 'ogreexport.cfg'
3309        if configTextName in [text.getName() for text in Blender.Text.Get()]:
3310                configText = Blender.Text.Get(configTextName)
3311                # compose string from text and unpickle
3312                try:
3313                        # unpickle
3314                        settingsDict = pickle.loads(string.join(configText.asLines()[4:],'\n'))
3315                except (PickleError):
3316                        pass
3317                else:
3318                        success = 1             
3319        # else try open filename
3320        if not success and os.path.isfile(filename):
3321                # open file
3322                try:
3323                        fileHandle = open(filename,'r')
3324                except IOError, (errno, strerror):
3325                        print "I/O Error(%s): %s" % (errno, strerror)
3326                else:
3327                        try:
3328                                # load settings
3329                                unpickler = pickle.Unpickler(fileHandle)
3330                                settingsDict = unpickler.load()
3331                                # close file
3332                                fileHandle.close()
3333                        except EOFError:
3334                                print "EOF Error"
3335                        else:
3336                                success = 1
3337        # set general settings
3338        if settingsDict.has_key('gameEngineMaterialsToggle'):
3339                gameEngineMaterialsToggle = Blender.Draw.Create(settingsDict['gameEngineMaterialsToggle'])
3340        if settingsDict.has_key('armatureToggle'):
3341                armatureToggle = Blender.Draw.Create(settingsDict['armatureToggle'])
3342        if settingsDict.has_key('worldCoordinatesToggle'):
3343                worldCoordinatesToggle = Blender.Draw.Create(settingsDict['worldCoordinatesToggle'])
3344        if settingsDict.has_key('ambientToggle'):
3345                ambientToggle = Blender.Draw.Create(settingsDict['ambientToggle'])
3346        elif settingsDict.has_key('armatureMeshToggle'):
3347                # old default was export in world coordinates
3348                worldCoordinatesToggle = Blender.Draw.Create(1)
3349        if settingsDict.has_key('pathString'):
3350                pathString = Blender.Draw.Create(settingsDict['pathString'])
3351        if settingsDict.has_key('materialString'):
3352                materialString = Blender.Draw.Create(settingsDict['materialString'])
3353        if settingsDict.has_key('scaleNumber'):
3354                scaleNumber = Blender.Draw.Create(settingsDict['scaleNumber'])
3355        if settingsDict.has_key('rotXNumber'):
3356                rotXNumber = Blender.Draw.Create(settingsDict['rotXNumber'])
3357        if settingsDict.has_key('rotYNumber'):
3358                rotYNumber = Blender.Draw.Create(settingsDict['rotYNumber'])
3359        if settingsDict.has_key('rotZNumber'):
3360                rotZNumber = Blender.Draw.Create(settingsDict['rotZNumber'])
3361        if settingsDict.has_key('fpsNumber'):
3362                fpsNumber = Blender.Draw.Create(settingsDict['fpsNumber'])
3363        # set object specific settings
3364        if settingsDict.has_key('armatureAnimationDictListDict'):
3365                armatureAnimationDictListDict = settingsDict['armatureAnimationDictListDict']
3366        elif settingsDict.has_key('animationDictListDict'):
3367                # convert old animationDictListDict
3368                ## create list of valid armature names
3369                armatureNameList = []
3370                for object in Blender.Object.Get():
3371                        if (object.getType() == "Armature"):
3372                                armatureNameList.append(object.getName())
3373                # create armatureAnimationDictListDict
3374                armatureAnimationDictListDict = {}
3375                animationDictListDict = settingsDict['animationDictListDict']
3376                for armatureName in armatureNameList:
3377                        if animationDictListDict.has_key(armatureName):
3378                                # convert animationDictList
3379                                armatureActionDict = ArmatureAction.createArmatureActionDict(Blender.Object.Get(armatureName))
3380                                armatureActionActuatorListView = ArmatureActionActuatorListView(armatureActionDict, MAXACTUATORS, BUTTON_EVENT_ACTUATOR_RANGESTART,{})
3381                                armatureActionActuatorListView.setAnimationDictList(animationDictListDict[armatureName])
3382                                armatureAnimationDictListDict[armatureName] = armatureActionActuatorListView.getArmatureAnimationDictList()
3383        return success
3384       
3385def refreshGUI():
3386        """refresh GUI after export and selection change
3387        """
3388        global exportLogger
3389        global selectedObjectsList, armatureToggle, armatureDict
3390        global armatureActionActuatorListViewDict
3391        global armatureAnimationDictListDict
3392        # export settings
3393        exportLogger = Logger()
3394        # synchronize armatureAnimationDictListDict
3395        for armatureName in armatureActionActuatorListViewDict.keys():
3396                armatureAnimationDictListDict[armatureName] = armatureActionActuatorListViewDict[armatureName].getArmatureAnimationDictList()
3397        selectedObjectsList = Blender.Object.GetSelected()
3398        if not selectedObjectsList:
3399                # called from command line
3400                selectedObjectsList = []
3401        armatureDict = {}
3402        # create fresh armatureDict
3403        for object in selectedObjectsList:
3404                if (object.getType() == "Armature"):
3405                        # add armature to armatureDict
3406                        armatureDict[object.getName()] = object.getName()
3407                elif (object.getType() == "Mesh"):
3408                        parent = object.getParent()
3409                        if parent and parent.getType() == "Armature":
3410                                # add armature to armatureDict
3411                                armatureDict[object.getName()] = parent.getName()
3412        # refresh ArmatureActionActuatorListViews
3413        for armatureName in armatureDict.values():
3414                # create armatureActionDict
3415                armatureActionDict = ArmatureAction.createArmatureActionDict(Blender.Object.Get(armatureName))
3416                # get animationDictList
3417                armatureAnimationDictList = None
3418                if armatureAnimationDictListDict.has_key(armatureName):
3419                        armatureAnimationDictList = armatureAnimationDictListDict[armatureName]
3420                if armatureActionActuatorListViewDict.has_key(armatureName):
3421                        # refresh armatureActionActuators
3422                        armatureActionActuatorListViewDict[armatureName].refresh(armatureActionDict)
3423                else:
3424                        # create armatureActionActuatorListView
3425                        armatureActionActuatorListViewDict[armatureName] = ArmatureActionActuatorListView(armatureActionDict, MAXACTUATORS, BUTTON_EVENT_ACTUATOR_RANGESTART, armatureAnimationDictList)
3426        return
3427
3428def initGUI():
3429        """initialization of the GUI
3430        """
3431        global armatureActionActuatorListViewDict
3432        if KEEP_SETTINGS:
3433                # load exporter settings
3434                loadSettings(Blender.Get('filename')+".ogre")
3435        armatureActionActuatorListViewDict = {}
3436        refreshGUI()
3437        return
3438
3439def exitGUI():
3440        if KEEP_SETTINGS:
3441                # save exporter settings
3442                saveSettings()
3443        Blender.Draw.Exit()
3444        return
3445
3446def pathSelectCallback(fileName):
3447        """handles FileSelector output
3448        """
3449        global pathString
3450        # strip path from fileName
3451        pathString = Blender.Draw.Create(os.path.dirname(fileName))
3452        return
3453       
3454def eventCallback(event,value):
3455        """handles keyboard and mouse events
3456           <p> 
3457           exits on ESCKEY<br>
3458           exits on QKEY
3459        """
3460        global scrollbar
3461        global selectedObjectsList, selectedObjectsMenu, armatureActionActuatorListViewDict, armatureDict
3462        # eventFilter for current ArmatureActionActuatorListView
3463        if (len(selectedObjectsList) > 0):
3464                selectedObjectsListIndex = selectedObjectsMenu.val
3465                selectedObjectName = selectedObjectsList[selectedObjectsListIndex].getName()
3466                if armatureDict.has_key(selectedObjectName):
3467                        armatureName = armatureDict[selectedObjectName]
3468                        armatureActionActuatorListViewDict[armatureName].eventFilter(event, value)
3469        scrollbar.eventFilter(event, value)
3470        if (value != 0):
3471                # pressed
3472                if (event == Draw.ESCKEY):
3473                        exitGUI()
3474                if (event == Draw.QKEY):
3475                        exitGUI()
3476        return
3477
3478def buttonCallback(event):
3479        """handles button events
3480        """
3481        global materialString, doneMessageBox, eventCallback, buttonCallback, scrollbar
3482        global selectedObjectsList, selectedObjectsMenu, armatureActionActuatorListViewDict, armatureDict
3483        global fpsNumber
3484        # buttonFilter for current ArmatureActionActuatorListView
3485        if (len(selectedObjectsList) > 0):
3486                selectedObjectsListIndex = selectedObjectsMenu.val
3487                selectedObjectName = selectedObjectsList[selectedObjectsListIndex].getName()
3488                if armatureDict.has_key(selectedObjectName):
3489                        armatureName = armatureDict[selectedObjectName]
3490                        armatureActionActuatorListViewDict[armatureName].buttonFilter(event)
3491        scrollbar.buttonFilter(event)
3492        if (event == BUTTON_EVENT_OK): # Ok
3493                # restart
3494                refreshGUI()
3495                Draw.Register(gui, eventCallback, buttonCallback)
3496        elif (event == BUTTON_EVENT_UPDATEBUTTON):
3497                # update list of selected objects
3498                refreshGUI()
3499                Draw.Redraw(1)
3500        elif (event == BUTTON_EVENT_SELECTEDOBJECTSMENU):
3501                # selected object changed
3502                Draw.Redraw(1)
3503        elif (event  == BUTTON_EVENT_QUIT): # Quit
3504                exitGUI()
3505        elif (event == BUTTON_EVENT_GAMEENGINEMATERIALSTOGGLE):
3506                Draw.Redraw(1)
3507        elif (event == BUTTON_EVENT_ARMATURETOGGLE): # armatureToggle
3508                Draw.Redraw(1)
3509        elif (event == BUTTON_EVENT_PATHBUTTON): # pathButton
3510                Blender.Window.FileSelector(pathSelectCallback, "Export Directory", pathString.val)
3511                Draw.Redraw(1)
3512        elif (event == BUTTON_EVENT_MATERIALSTRING): # materialString
3513                materialString = Blender.Draw.Create(PathName(materialString.val).basename())
3514                if (len(materialString.val) == 0):
3515                        materialString = Blender.Draw.Create(Blender.Scene.GetCurrent().getName() + ".material")
3516                Draw.Redraw(1)
3517        elif (event == BUTTON_EVENT_SCROLLBAR): # scrollbar
3518                Draw.Redraw(1)
3519        elif (event == BUTTON_EVENT_EXPORT): # export
3520                Draw.Register(exportMessageBox, None, None)
3521                Draw.Draw()
3522                # export
3523                if (Blender.Get("version") >= 233):
3524                        # get blender's current "scene->format->frames per second" setting
3525                        fpsNumber = Draw.Create(Blender.Scene.GetCurrent().getRenderingContext().framesPerSec())
3526                export(selectedObjectsList)
3527                # set donemessage
3528                scrollbar = ReplacementScrollbar(0,0,len(exportLogger.getMessageList())-1,BUTTON_EVENT_SCROLLBARUP,BUTTON_EVENT_SRCROLLBARDOWN)
3529                Draw.Register(doneMessageBox, eventCallback, buttonCallback)
3530                Draw.Redraw(1)
3531        return
3532
3533def frameDecorator(x, y, width):
3534        """draws title and logo onto the frame
3535       
3536                @param x upper left x coordinate
3537                @param y upper left y coordinate
3538                @param width screen width to use
3539                @return used height
3540        """
3541        # title
3542        glColor3f(0, 0.2, 0)
3543        glRectf(x,y-36,x+width,y-16)
3544        glColor3f(1.0,1.0,0)
3545        glRasterPos2i(x+85, y-30)
3546        Draw.Text("OGRE Exporter 0.15.0", "normal")
3547
3548        # logo
3549        glRasterPos2i(x+1, y-48)       
3550        glEnable(GL_BLEND)
3551        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
3552        glDrawPixels(77, 48, GL_RGBA, GL_BYTE, OGRE_LOGO)
3553        glColor3f(0,0,0)       
3554        return 50
3555
3556def gui():
3557        """draws the screen
3558        """
3559        global gameEngineMaterialsToggle, armatureToggle, worldCoordinatesToggle, \
3560                ambientToggle, pathString, materialString, scaleNumber, fpsNumber, \
3561                scrollbar, rotXNumber, rotYNumber, rotZNumber
3562        global selectedObjectsList, selectedObjectsMenu, armatureActionActuatorListViewDict, armatureDict
3563        # get size of the window
3564        guiRectBuffer = Buffer(GL_FLOAT, 4)
3565        glGetFloatv(GL_SCISSOR_BOX, guiRectBuffer)
3566        guiRect = [0, 0, int(guiRectBuffer.list[2]), int(guiRectBuffer.list[3])]
3567       
3568        remainRect = guiRect[:]
3569        remainRect[0] += 10
3570        remainRect[1] += 10
3571        remainRect[2] -= 10
3572        remainRect[3] -= 10
3573       
3574        # clear background
3575        glClearColor(0.6,0.6,0.6,1) # Background: grey
3576        glClear(GL_COLOR_BUFFER_BIT)
3577       
3578        remainRect[3] -= frameDecorator(remainRect[0], remainRect[3], remainRect[2]-remainRect[0])
3579       
3580        # export settings
3581        remainRect[3] -= 5
3582        # first row
3583        materialString = Draw.String("Material File: ", BUTTON_EVENT_MATERIALSTRING, \
3584                        remainRect[0],remainRect[3]-25, 450, 20, \
3585                        materialString.val, 255,"all material definitions go in this file (relative to the save path)")
3586        remainRect[3] -= 25
3587        # second row
3588        gameEngineMaterialsToggle = Draw.Toggle("Game Engine Materials", BUTTON_EVENT_GAMEENGINEMATERIALSTOGGLE, \
3589                                remainRect[0], remainRect[3]-25, 220, 20, \
3590                                gameEngineMaterialsToggle.val, "export game engine materials instead of rendering materials")
3591        # scale settings
3592        scaleNumber = Draw.Number("Mesh Scale Factor: ", BUTTON_EVENT_SCALENUMBER, \
3593                        remainRect[0]+230, remainRect[3]-25, 220, 20, \
3594                        scaleNumber.val, 0.0, 1000.0, "scale factor")
3595        remainRect[3] -= 25
3596        # third row     
3597        armatureToggle = Draw.Toggle("Export Armatures", BUTTON_EVENT_ARMATURETOGGLE, \
3598                                remainRect[0], remainRect[3]-25, 220, 20, \
3599                                armatureToggle.val, "export skeletons and bone weights in meshes")
3600        rotXNumber = Draw.Number("RotX: ", BUTTON_EVENT_ROTXNUMBER, \
3601                        remainRect[0]+230, remainRect[3]-25, 220, 20, \
3602                        rotXNumber.val, -360.0, 360.0, "angle of the first rotation, around the x-axis")
3603        remainRect[3] -= 25
3604        # fourth row
3605        worldCoordinatesToggle = Draw.Toggle("World Coordinates", BUTTON_EVENT_WORLDCOORDINATESTOGGLE, \
3606                        remainRect[0], remainRect[3]-25, 220, 20, \
3607                        worldCoordinatesToggle.val, "use world coordinates instead of object coordinates")
3608        rotYNumber = Draw.Number("RotY: ", BUTTON_EVENT_ROTYNUMBER, \
3609                        remainRect[0]+230, remainRect[3]-25, 220, 20, \
3610                        rotYNumber.val, -360.0, 360.0, "angle of the second rotation, around the y-axis")
3611        remainRect[3] -= 25
3612        # fifth row
3613        ambientToggle = Draw.Toggle("Coloured Ambient", BUTTON_EVENT_AMBIENTTOGGLE, \
3614                        remainRect[0], remainRect[3]-25, 220, 20, \
3615                        ambientToggle.val, "use Amb factor times diffuse colour as ambient instead of Amb factor times white")
3616        rotZNumber = Draw.Number("RotZ: ", BUTTON_EVENT_ROTZNUMBER, \
3617                        remainRect[0]+230, remainRect[3]-25, 220, 20, \
3618                        rotZNumber.val, -360.0, 360.0, "angle of the third rotation, around the z-axis")
3619        # sixth row
3620        if ((armatureToggle.val == 1) and (Blender.Get("version") < 233)):
3621                remainRect[3] -= 25
3622                fpsNumber = Draw.Number("Frs/Sec: ", BUTTON_EVENT_FPSNUMBER, \
3623                                remainRect[0]+230, remainRect[3]-25, 220, 20, \
3624                                fpsNumber.val, 1, 120, "animation speed in frames per second")
3625        remainRect[3] -= 35
3626       
3627        # Path setting
3628        pathString = Draw.String("Path: ", BUTTON_EVENT_PATHSTRING, \
3629                        10, 50, guiRect[2]-91, 20, \
3630                        pathString.val, 255, "the directory where the exported files are saved")
3631        Draw.Button("Select", BUTTON_EVENT_PATHBUTTON, guiRect[2]-80, 50, 70, 20, "select the export directory")
3632        # button panel
3633        Draw.Button("Export", BUTTON_EVENT_EXPORT,10,10,100,30,"export selected objects")
3634        # Draw.Button("Help",BUTTON_EVENT_HELP ,(guiRect[2])/2-50,10,100,30,"notes on usage")   
3635        Draw.Button("Quit", BUTTON_EVENT_QUIT,guiRect[2]-110,10,100,30,"quit without exporting")
3636        remainRect[1] += 70
3637       
3638        # rename animation part
3639        #if (armatureToggle.val == 1):
3640        animationText = "Animation settings of"
3641        xOffset = Draw.GetStringWidth(animationText) + 5
3642        selectedObjectsMenuName = ""
3643        selectedObjectsMenuIndex = 0
3644        if (len(selectedObjectsList) > 0):
3645                for object in selectedObjectsList:
3646                        # add menu string
3647                        selectedObjectsMenuName += object.getName() + " %x" + ("%d" % selectedObjectsMenuIndex) + "|"
3648                        selectedObjectsMenuIndex += 1
3649        else:
3650                selectedObjectsMenuName = "No objects selected! %t"
3651        selectedObjectsMenu = Draw.Menu(selectedObjectsMenuName, BUTTON_EVENT_SELECTEDOBJECTSMENU, \
3652                              remainRect[0]+xOffset, remainRect[3]-20, 140, 20, \
3653                              selectedObjectsMenu.val, "Objects selected for export")
3654        xOffset += 141
3655        # update button
3656        Draw.Button("Update", BUTTON_EVENT_UPDATEBUTTON, remainRect[0]+xOffset, remainRect[3]-20, 60, 20, "update list of selected objects")
3657        remainRect[3] -= 25
3658        if (armatureToggle.val == 1):
3659                # draw armatureActionActuator
3660                if (len(selectedObjectsList) > 0):
3661                        selectedObjectsListIndex = selectedObjectsMenu.val
3662                        selectedObjectName = selectedObjectsList[selectedObjectsListIndex].getName()
3663                        if armatureDict.has_key(selectedObjectName):
3664                                glRasterPos2i(remainRect[0],remainRect[3]+10)
3665                                Draw.Text(animationText)
3666                                armatureName = armatureDict[selectedObjectName]
3667                                armatureActionActuatorListViewDict[armatureName].draw(remainRect[0], remainRect[1], remainRect[2]-remainRect[0], remainRect[3]-remainRect[1])
3668        return
3669
3670def exportMessageBox():
3671        """informs on the export progress
3672        """
3673        # get size of the window
3674        guiRectBuffer = Buffer(GL_FLOAT, 4)
3675        glGetFloatv(GL_SCISSOR_BOX, guiRectBuffer)
3676        guiRect = [0, 0, int(guiRectBuffer.list[2]), int(guiRectBuffer.list[3])]
3677       
3678        remainRect = guiRect[:]
3679        remainRect[0] += 10
3680        remainRect[1] += 10
3681        remainRect[2] -= 10
3682        remainRect[3] -= 10
3683
3684        # clear background
3685        glClearColor(0.6,0.6,0.6,1) # Background: grey
3686        glClear(GL_COLOR_BUFFER_BIT)
3687       
3688        remainRect[3] -= frameDecorator(remainRect[0], remainRect[3], remainRect[2]-remainRect[0])
3689       
3690        # export information
3691        ## center view
3692        exportMessage = "Exporting, please wait!"
3693        exportMessageWidth = Draw.GetStringWidth(exportMessage, 'normal')
3694        textPosition = [0, 0]
3695        textPosition[0] = (remainRect[0] + remainRect[2] - exportMessageWidth)/2
3696        textPosition[1] = (remainRect[1] + remainRect[3])/2
3697        glRasterPos2i(textPosition[0], textPosition[1])
3698        glColor3f(0,0,0) # Defaul color: black
3699        Draw.Text(exportMessage, "normal")
3700        return
3701       
3702def doneMessageBox():
3703        """displays export message and log
3704        """
3705        global exportLogger
3706        EXPORT_SUCCESS_MESSAGE = "Successfully exported!"
3707        EXPORT_WARNING_MESSAGE = "Exported with warnings!"
3708        EXPORT_ERROR_MESSAGE = "Error in export!"       
3709        # get size of the window
3710        guiRectBuffer = Buffer(GL_FLOAT, 4)
3711        glGetFloatv(GL_SCISSOR_BOX, guiRectBuffer)
3712        guiRect = [0, 0, int(guiRectBuffer.list[2]), int(guiRectBuffer.list[3])]
3713               
3714        remainRect = guiRect[:]
3715        remainRect[0] += 10
3716        remainRect[1] += 10
3717        remainRect[2] -= 10
3718        remainRect[3] -= 10
3719       
3720        # clear background
3721        glClearColor(0.6,0.6,0.6,1) # Background: grey
3722        glClear(GL_COLOR_BUFFER_BIT)
3723       
3724        remainRect[3] -= frameDecorator(remainRect[0], remainRect[3], remainRect[2]-remainRect[0])
3725       
3726        # OK button
3727        Draw.Button("OK", BUTTON_EVENT_OK,10,10,100,30,"return to export settings")
3728        Draw.Button("Quit", BUTTON_EVENT_QUIT,guiRect[2]-110,10,100,30,"quit export script")
3729        remainRect[1] += 40
3730       
3731        # message
3732        status = exportLogger.getStatus()
3733        doneMessage = ''
3734        if (status == Logger.INFO):
3735                doneMessage= EXPORT_SUCCESS_MESSAGE
3736        elif (status == Logger.WARNING):
3737                doneMessage = EXPORT_WARNING_MESSAGE
3738                glColor3f(1.0,1.0,0.0)
3739                Blender.BGL.glRectf(remainRect[0], remainRect[3]-24, remainRect[0]+Draw.GetStringWidth(doneMessage), remainRect[3]-7)
3740        elif (status == Logger.ERROR):
3741                doneMessage = EXPORT_ERROR_MESSAGE
3742                glColor3f(1.0,0.0,0.0)
3743                Blender.BGL.glRectf(remainRect[0], remainRect[3]-24, remainRect[0]+Draw.GetStringWidth(doneMessage), remainRect[3]-7)
3744        remainRect[3] -= 20
3745        glColor3f(0.0,0.0,0.0) # Defaul color: black
3746        glRasterPos2i(remainRect[0],remainRect[3])
3747        Draw.Text(doneMessage,"normal")
3748       
3749        remainRect[3] -= 20
3750        glColor3f(0.0,0.0,0.0) # Defaul color: black
3751        glRasterPos2i(remainRect[0],remainRect[3])
3752        Draw.Text("Export Log:","small")
3753        remainRect[3] -= 4
3754       
3755        # black border
3756        logRect = remainRect[:]
3757        logRect[2] -= 22
3758        glColor3f(0,0,0) # Defaul color: black
3759        glRectf(logRect[0],logRect[1],logRect[2],logRect[3])
3760        logRect[0] += 1
3761        logRect[1] += 1
3762        logRect[2] -= 1
3763        logRect[3] -= 1
3764        glColor3f(0.662,0.662,0.662) # Background: grey
3765        glRectf(logRect[0],logRect[1],logRect[2],logRect[3])
3766       
3767        # display export log
3768        exportLog = exportLogger.getMessageList()
3769        scrollPanelRect = remainRect[:]
3770        loglineiMax = len(exportLog)
3771        loglinei = scrollbar.getCurrentValue()
3772        while (((logRect[3]-logRect[1]) >= 20) and ( loglinei < loglineiMax )):
3773                logRect[3] -= 16
3774                (status, message) = exportLog[loglinei]
3775                if (status == Logger.WARNING):
3776                        glColor3f(1.0,1.0,0.0)
3777                        glRecti(logRect[0],logRect[3]-4,logRect[2],logRect[3]+13)
3778                elif (status == Logger.ERROR):
3779                        glColor3f(1.0,0.0,0.0)
3780                        glRecti(logRect[0],logRect[3]-4,logRect[2],logRect[3]+13)
3781                glColor3f(0,0,0)
3782                glRasterPos2i(logRect[0]+4,logRect[3])
3783                Draw.Text(message)
3784                loglinei += 1
3785        # clip log text
3786        glColor3f(0.6,0.6,0.6) # Background: grey
3787        glRectf(scrollPanelRect[2]-22,scrollPanelRect[1], guiRect[2],scrollPanelRect[3])
3788        # draw scrollbar
3789        scrollbar.draw(scrollPanelRect[2]-20, scrollPanelRect[1], 20, scrollPanelRect[3]-scrollPanelRect[1])
3790        return
3791
3792######
3793# Main
3794######
3795initGUI()
3796Draw.Register(gui, eventCallback, buttonCallback)
Note: See TracBrowser for help on using the repository browser.