added encoding "# coding: utf-8" to the headers of scripts that python would not...
[blender.git] / release / scripts / export_m3g.py
1 #!BPY
2 # coding: utf-8
3 """ Registration info for Blender menus:
4 Name: 'M3G (.m3g, .java)...'
5 Blender: 244
6 Group: 'Export'
7 Tooltip: 'Export to M3G'
8 """
9 #------------------------------------------------------------------------
10 # M3G exporter for blender 2.37 or above
11 #
12 # Source: http://www.nelson-games.de/bl2m3g/source
13 #
14 # $Id: m3g_export.py,v 0.1 2005/04/19 12:25 gerhardv Exp gerhardv $
15 #
16 # Author: Gerhard Völkl
17 #
18 # ***** BEGIN GPL LICENSE BLOCK *****
19 #
20 # Copyright (C) 2005: gerhard völkl gkvoelkl@yahoo.de
21 #
22 # This program is free software; you can redistribute it and/or
23 # modify it under the terms of the GNU General Public License
24 # as published by the Free Software Foundation; either version 2
25 # of the License, or (at your option) any later version.
26 #
27 # This program is distributed in the hope that it will be useful,
28 # but WITHOUT ANY WARRANTY; without even the implied warranty of
29 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
30 # GNU General Public License for more details.
31 #
32 # You should have received a copy of the GNU General Public License
33 # along with this program; if not, write to the Free Software
34 # Foundation,
35 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
36
37 # ***** END GPL LICENCE BLOCK *****
38 #
39 # To use script:
40 # 1.) load this file in the text window.
41 #     (press SHIFT+F11, Open New via Datablock button)
42 # 2.) make sure your mouse is over the text edit window and
43 #     run this script. (press ALT+P)
44 # Or:
45 #   copy to the scripts directory and it will appear in the
46 #   export list. (Needs 2.32 or higher)
47 #
48 # Based on informations from:
49 #   wrl2export.py from Rick Kimball and others
50 # --------------------------------------------------------------------------#
51 # History 0.2
52 # * maximal Precision in VertexArray (with algorithms from Kalle Raita)
53 # * IPO Animation with mesh: Rotation, Translation and Size
54 # History 0.3
55 # * to find a 3d object in your java programm you can assign a userID
56 #   your blender object has name 'cube#01' your 3d object will have ID 01
57 #   the number after '#' is taken
58 # * more than one material per mesh can be used
59 # * uv texture support (implemented by Aki Koskinen and Juha Laitinen)
60 #   The image which is bound to the faces will be exportet within m3g-file
61 #   Limitations by M3G-API:
62 #   The width and height of the image must be non-negative powers of two, 
63 #   but they need not to be equal. Maximum value is 256.
64 #   *.java export: Only PNG images can be used.
65 # History 0.4
66 # * check limitation of texture images (credit to MASTER_ZION for Brasil)
67 # * Better light: The light modeles of Blender and M3G are naturally 
68 #   different. So the export script trys to translate as much as possible
69 #
70 #   M3G Light type          Blender Light type
71 #   --------------------------------------------------------------
72 #   AMIENT Light            Not available as light type in Blender
73 #   DIRECTIONAL Light       SUN
74 #   OMNIdirectional light   LAMP
75 #   SPOT light              SPOT
76 #   not translated          HEMI
77 #   not translated          AREA
78 #
79 #   Attributs of M3G Lights:
80 #
81 #   Attenuation (OMNI,SPOT):
82 #     Intensity of light changes with distance
83 #     The attenuation factor is 1 / (c + l d + q d2)
84 #     where d is the distance between the light and the vertex being lighted
85 #     and c, l, q are the constant, linear, and quadratic coefficients.
86 #     In Blender exists much complex posibilies. To simplify exporter uses
87 #     only button Dist: distance at which the light intensity is half 
88 #     the Energy 
89 #   Color (ALL)
90 #     Color of light 
91 #   Intensity (ALL)
92 #     The RGB color of this Light is multiplied component-wise with the 
93 #     intensity. In Blender : energy
94 #   SpotAngle (SPOT)
95 #     the spot cone angle for this Light
96 #     In Blender: spotSize
97 #   SpotExponent (SPOT)
98 #     The spot exponent controls the distribution of the intensity of 
99 #     this Light within the spot cone, such that larger values yield 
100 #     a more concentrated cone. In Blender: SpotBl 
101 #
102 # * Some GUI for options
103 #   First prototype of GUI was created using RipSting's Blender-Python 
104 #   GUI designer. Download at Http://oregonstate.edu/~dennisa/Blender/BPG/
105 #
106 # * Ambiente light
107 #   Information is taken by world ambiente attribute
108 #
109 # * Parenting Part 1
110 #   In Blender the Empty object is used to group objects. All objects
111 #   which have the same empty as parent are the member of the same group.
112 #   
113 #   empty <-- Parent of -- element 1
114 #        <-- Parent of -- element 2
115 #
116 #       is translated in M3G
117 #
118 #   group-Node -- Member --> element 1
119 #              -- Member --> element 2
120 #
121 #   In Blender every object can be the parent of every other object
122 #   In M3G that is not possible. Only a group object can be parent.
123 #   (Or the world object which is derived from group).
124 #   That will come later as Parenting Part 2
125 #
126 # * Backface Culling
127 #   you can use backface culling, if option "use backface culloing" is on.
128 #   Culling will be set in PolygonMode object of every mesh. The correct
129 #   winding is controlled.
130 # History 0.5
131 #* Bone Animation - Armature (Part 1)
132 #
133 #  Armature is the skeleton for skinned meshes. It stores the bones in 
134 #  rest position (more information http://www.blender.org/cms/How_Armatures_work.634.0.html)
135 #  You can work in Blender with bones and meshes in different ways. In
136 #  this first attempt only the use of vertex groups is assisted.
137 #
138 #  Blender-Objekts       translated into      M3G-Objects
139 #
140 #      MESH                                  SkinnedMesh
141 #        |                                       |
142 #        v                                       v
143 #     ARMATURE                                 Group
144 #        |                                       |
145 #        v                                       v
146 #      BONE_1                                  Group
147 #                                              Group_second
148 #        |                                       |
149 #        V                                       v
150 #      BONE_2                                  Group
151 #                                              Group_secound
152 #
153 #  Every bone is translated into two groups at the moment, because
154 #  the second bone is needed to do the animation in an easy way.
155 #
156 #  The animations in Blender for meshes are stored in action objects.
157
158 #  Blender Objects      translated into      M3G-Objects
159 #
160 #   ARMATURE
161 #       | activ
162 #       v
163 #    ACTION                               ANIMATIONCONTROLLER
164 #       | 1..n                                    ^
165 #       v                                 ANIMATIONTRACK  --> Group_second
166 #     IPOs                                        |
167 #                                                 v
168 #                                            KEYSEQUENZE
169 #
170 #  One action is translated into one animationcontroller. One IPO is 
171 #  translated in one KEYSEQUENZE and one ANIMATIONTRACK.
172 #
173 #  At the moment only the active action of the armature object is translated.
174 #
175 #* Print Info, if type of light is used that is not supported
176 #
177 # History 0.5
178 #
179 #* New Option exportAllAction (default value: false)
180 #  If that option is true, all actions will be exported - not only the active
181 #  action. 
182 #  At the moment you can only assign one action to one armature.
183 #  To know which action is used with which armature the action
184 #  needs a special name : 
185 #        <Action Name>#A<M3G ID of Armature>E<End Frame>#<ID of Action>
186
187 #  Example: Name of action : walk#A10E250#02
188 #           Name of armature : man#10
189 #           End Frame: 250
190
191 # History 0.6
192 # Include the same image only one time into the m3g-file
193 #
194 # All the following changes of this version was made by Claus Hoefele
195 #
196 #* Until now all vertices of the faces was been written.
197 #  Now the vertices will be used again if possible: 
198 #     normal and texture coordinates of to vertices have to be the same
199 #
200 #* Smooth/solid shading can now be defined for every single material:
201 #     in Editing panel (F9)>Link and Materials
202 #
203 #* This script uses now correctly the TexFace and Shadless Buttons in
204 #  Shading panel (F5)>Material buttons>Material box. 
205 #  TexFace switches on/off the Export of texture coordinates.
206 #  Shadeless does the some with the normal coordinates
207 #
208 #* The GUI was redesigned in a PupBlock
209 #  
210 #* Options:
211 #
212 #** Texturing Enabled: Switches on/off export of textures and texture 
213 #           coordinates. Attention: the TextFace button switches only
214 #           for one mesh
215 #** Texturing External: the textures will be included it mg3-file or
216 #           exported in seperate file
217 #** Lighting Enabled: turns on/off export of lights and normal completly
218 #           Attention: Shadeless only for one mesh
219 #** Persp. Correction: turns on/off perspective correction in PolygonMode.
220 #** Smooth Shading: turns on/off smooth shading in PolygonMode.
221 #
222 #* Textures in external references are used again (with ImageFactory)
223 #
224 #* Blender function: Double Sided button in Editing Context
225 #                    (F9)>Mesh panel)
226 #  turn on/off PolygonMode.CULL_BACK anzuschalten.
227 #
228 #* Script ingnores meshes that have no faces
229 #
230 # History 0.7
231 #
232 # * Exporter can work with texture coordinates greater 1 and smaller 0
233 #
234 # * Adler32 did not work always correct. New implementation made.
235 #
236 # * Modul shutil is not needed any longer. Exporter has its own copy_file.
237 #   (realized and inspired by ideasman_42 and Martin Neumann)
238 #
239 # History 0.8
240 #
241 # * Blender works with SpotAngles 1..180 but M3G works only with 0..90
242 #   M3G use the 'half angle' (cut off angle) (Thanks to Martin Storsjö)
243 #
244 # * Error fixed: Texture coordinates was not calculated correct.
245 #   (Thanks to Milan Piskla, Vlad, Max Gilead, Regis Cosnier ...)
246 #
247 # * New options in GUI:
248 #      M3G Version 2.0 : Will export M3G files Vers. 2.0 in future
249 #      Game Physics: Adds Game Physics infos for NOPE API
250 #   
251 # --------------------------------------------------------------------------#
252 # TODO: Export only selected mesh
253 # TODO: Optimize Bones <--> Vertex Group mapping
254 # TODO: Compressed File
255 # TODO: MTex - Support
256 # TODO: By Rotating use SQUAD instead of Beziere. It's smoother
257 import Blender
258 from Blender import Types,Lamp,Material,Texture,Window,Registry,Draw
259 from Blender.BGL import *
260 from Blender.Object import *
261 from Blender.Camera import *
262 from Blender.Mesh import *
263 from array import array
264 import sys, struct, zlib
265 from inspect import *
266 from types import *
267 from Blender.Mathutils import *
268 from os.path import *
269 #import rpdb2
270
271 # ---- Helper Functions -------------------------------------------------------#
272 def copy_file(source, dest):
273         file = open(source, 'rb')
274         data = file.read()
275         file.close()
276         
277         file = open(dest, 'wb')
278         file.write(data)
279         file.close()
280
281 def tracer(frame, event, arg):
282     '''Global trace function'''
283     if event=='call':
284         tmp = getargvalues(frame)
285         print event, frame.f_code.co_name, frame.f_lineno, \
286                      formatargvalues(tmp[0],tmp[1],tmp[2],tmp[3])
287     elif event=='line':
288         print event, frame.f_code.co_name, frame.f_lineno
289         #print event, frame.f_code.co_name, frame.f_lineno, \
290         #             getsourcelines(frame.f_code)[frame.f_lineno]
291     elif event=='return':
292         print event, frame.f_code.co_name, frame.f_lineno, "->", arg
293     return tracer
294
295 def doSearchDeep(inList,outList):
296     '''Does deepsearch for all elements in inList'''
297     for element in inList:
298         if element != None : outList = element.searchDeep(outList)
299     return outList
300
301
302 def getId(aObject):
303     ''' returns 0 if Object is None: M3G value for null'''
304     if aObject == None: return 0
305     return aObject.id
306  
307 def toJavaBoolean(aValue):
308     ''' returns java equivalent to boolean'''
309     if aValue:
310         return 'true'
311     else :
312         return 'false'
313
314 def sign(a):
315     if a<0 : return -1
316     elif a>0 : return 1
317     else : return 0
318      
319 def isOrderClockWise(v,normal):
320     ''' returns true, if order of vertices is clockwise. Important for 
321         culling '''
322     # (v2-v0)x(v2-v1)=surface_normal
323     #
324     if type(v[0]) is Types.MVertType:
325         mNormal = TriangleNormal(Vector(v[0].co),Vector(v[1].co),Vector(v[2].co))
326     else:
327         mNormal = TriangleNormal(Vector(v[0]),Vectot(v[1]),Vector(v[2]))
328     #print "normal ",mNormal.normalize()
329     #print "BNormal ",normal.normalize()
330     
331     # Do not use any longer. Blender does it correct
332     
333     result = (sign(normal.x)==sign(mNormal.x) and
334               sign(normal.y)==sign(mNormal.y) and
335               sign(normal.z)==sign(mNormal.z))
336     #print "Result ",result
337     
338     return True
339     
340     
341 # ---- M3G Types --------------------------------------------------------------#
342 class M3GVertexList:
343     def __init__(self, wrapList):
344         self.mlist = wrapList
345
346     def __getitem__(self, key):
347         item = self.mlist[key]
348         if type(item) is Types.MVertType:
349             result =(item.co[0],item.co[1],item.co[2])
350         else:
351             result = item
352         return result
353
354 class M3GBoneReference:
355     def __init__(self,first,count):
356         self.firstVertex=first #UInt32 
357         self.vertexCount=count #UInt32 
358         
359         
360 class M3GBone:
361     def __init__(self):
362         self.verts=[] #List of influenced verts
363         self.transformNode=None #ObjectIndex
364         self.references = [] #References to Verts that are needed
365         self.weight=0      #Int32
366
367     
368     def setVerts(self,aVerts):
369         self.verts = aVerts
370         self.createReferences()
371         
372     def createReferences(self):
373         #print "createReference::len(verts) ",len(self.verts)
374         if len(self.verts)==0: return #No Verts available
375         self.verts.sort()
376         ref = []
377         list = []
378         last = self.verts[0]-1
379         count = 0
380         for vert in self.verts:
381             #print "vert ",vert
382             if vert==last+1:
383                 list.append(vert)
384             else:
385                 ref.append(M3GBoneReference(list[0],len(list)))
386                 #print list[0],len(list)
387                 list=[vert]
388             last=vert
389             #print "list ",list
390         if len(list)>0:
391             ref.append(M3GBoneReference(list[0],len(list)))
392         self.references = ref
393         
394     
395 class M3GVector3D:
396     def __init__(self,ax=0.0,ay=0.0,az=0.0):
397         self.x = ax #Float32
398         self.y = ay #Float32
399         self.z = az #Float32
400     
401     def writeJava(self):
402         return str(self.x)+"f, "+str(self.y)+"f, "+str(self.z)+"f"
403     
404     def getData(self):
405         return struct.pack("<3f",self.x,self.y,self.z)
406     
407     def getDataLength(self):
408         return struct.calcsize("<3f")
409
410 class M3GMatrix:
411     """ A 4x4 generalized matrix. The 16 elements of the
412         matrix are output in the same order as they are
413         retrieved using the API Transform.get method. In
414         other words, in this order:
415         0 1 2 3
416         4 5 6 7
417         8 9 10 11
418         12 13 14 15 """
419     def __init__(self):
420         self.elements=16 * [0.0] #Float32
421         
422     def identity(self):
423         self.elements[ 0] = 1.0
424         self.elements[ 5] = 1.0
425         self.elements[10] = 1.0
426         self.elements[15] = 1.0
427     
428     def getData(self):
429         return struct.pack('<16f',self.elements[0],self.elements[1],
430         self.elements[2],self.elements[3],
431         self.elements[4],self.elements[5],
432         self.elements[6],self.elements[7],
433         self.elements[8],self.elements[9],
434         self.elements[10],self.elements[11],
435         self.elements[12],self.elements[13],
436         self.elements[14],self.elements[15])
437
438     def getDataLength(self):
439         return struct.calcsize('<16f')
440         
441         
442 class M3GColorRGB:
443     """ A color, with no alpha information. Each compo-
444         nent is scaled so that 0x00 is 0.0, and 0xFF is 1.0.
445         """
446     def __init__(self,ared=0,agreen=0,ablue=0):
447         self.red = ared #Byte
448         self.green = agreen #Byte
449         self.blue = ablue #Byte
450         
451     def writeJava(self):
452         return "0x"+("%02X%02X%02X%02X" % (0.0, self.red, self.green, self.blue))
453     
454     def getData(self):
455         return struct.pack('3B',self.red,self.green,self.blue)
456     
457     def getDataLength(self):
458         return struct.calcsize('3B')
459
460
461 class M3GColorRGBA:
462     """ A color, with alpha information. Each component
463         is scaled so that 0x00 is 0.0, and 0xFF is 1.0. The
464         alpha value is scaled so that 0x00 is completely
465         transparent, and 0xFF is completely opaque. 
466         """
467     def __init__(self,ared=0,agreen=0,ablue=0,aalpha=0):
468         self.red = ared #Byte 
469         self.green = agreen #Byte 
470         self.blue = ablue #Byte 
471         self.alpha = aalpha #Byte
472
473     def writeJava(self):
474         return "0x"+("%02X%02X%02X%02X" % (self.alpha, self.red, self.green, self.blue))
475         
476     def getData(self):
477         return struct.pack('4B',self.red,self.green,self.blue,self.alpha)
478     
479     def getDataLength(self):
480         return struct.calcsize('4B')
481     
482     
483 #ObjectIndex
484 #The index of a previously encountered object in
485 #the file. Although this is serialized as a single
486 #unsigned integer, it is included in the compound
487 #type list because of the additional semantic infor-
488 #mation embodied in its type. A value of 0 is
489 #reserved to indicate a null reference; actual object indices start from 1. Object indices must refer
490 #only to null or to an object which has already been
491 #created during the input deserialization of a file -
492 #they must be less than or equal to the index of the
493 #object in which they appear. Other values are dis-
494 #allowed and must be treated as errors.
495 #UInt32
496 #index;
497
498 # ---- M3G Proxy --------------------------------------------------------------- #
499 class M3GProxy:
500     def __init__(self):
501         self.name = ""
502         self.id=0
503         self.ObjectType=0
504         self.binaryFormat=''
505         
506     def __repr__(self):
507         return "<"+str(self.__class__)[9:] + ":" + str(self.name) + ":" + str(self.id) + ">"
508
509         
510 class M3GHeaderObject(M3GProxy):
511     def __init__(self):
512         M3GProxy.__init__(self)
513         self.M3GHeaderObject_binaryFormat = '<BBBII'
514         self.ObjectType=0
515         self.id = 1   #Special Object: always 1
516         self.VersionNumber=[1,0] #Byte[2] 
517         self.hasExternalReferences=False #Boolean External Files needed? eg. Textures
518         self.TotalFileSize=0 #UInt32 
519         self.ApproximateContentSize=0 #UInt32 Only a hint! External sources included
520         self.AuthoringField='Blender M3G Export' #String 
521         
522     def getData(self):
523         data = struct.pack(self.M3GHeaderObject_binaryFormat,
524                            self.VersionNumber[0],
525                            self.VersionNumber[1],
526                            self.hasExternalReferences,
527                            self.TotalFileSize,
528                            self.ApproximateContentSize)
529         data += struct.pack(str(len(self.AuthoringField)+1)+'s',self.AuthoringField)
530         return data
531     
532     def getDataLength(self):
533         value = struct.calcsize(self.M3GHeaderObject_binaryFormat)
534         return value + struct.calcsize(str(len(self.AuthoringField)+1)+'s')
535         
536 class M3GExternalReference(M3GProxy):
537     def __init__(self):         
538         M3GProxy.__init__(self)
539         self.ObjectType=0xFF
540         self.URI=''             #reference URI
541         
542     def getData(self):
543         data = struct.pack(str(len(self.URI)+1) + 's', self.URI)
544         return data
545         
546     def getDataLength(self):
547         return struct.calcsize(str(len(self.URI)+1)+'s')
548         
549     def searchDeep(self,alist):
550         if not(self in alist): alist.append(self)
551         return alist
552         
553     def __repr__(self):
554         return M3GProxy.__repr__(self) + " (" + self.URI + ")"
555        
556         
557 class M3GObject3D(M3GProxy):
558     def __init__(self):
559         M3GProxy.__init__(self)
560         self.userID=0 #UInt32 - field may be any value
561         self.animationTracks=[] #ObjectIndex[] 
562         self.userParameterCount=0 #UInt32  - No user parameter used 
563         
564     def searchDeep(self,alist):
565         alist = doSearchDeep(self.animationTracks,alist)
566         if not(self in alist): alist.append(self)
567         return alist
568         
569     def getData(self):
570         data = struct.pack('<I',self.userID)
571         print "write userID",self.userID,self.name,str(self), self.getDataLength()
572         data += struct.pack('<I',len(self.animationTracks))
573         for element in self.animationTracks:
574             data += struct.pack('<I',getId(element))
575         data += struct.pack('<I',self.userParameterCount)
576         return data
577
578     def getDataLength(self):
579         value = struct.calcsize('<3I')
580         if len(self.animationTracks) > 0: 
581             value += struct.calcsize('<'+str(len(self.animationTracks))+'I')
582         return value
583
584     def writeJava(self,aWriter,aCreate):
585         if aCreate : pass #Abstract! Could not be created
586         if len(self.animationTracks) > 0 :
587             aWriter.write(2)
588             for iTrack in self.animationTracks:
589                 aWriter.write(2,"BL%i.addAnimationTrack(BL%i);" % (self.id,iTrack.id))
590             
591             
592 class M3GTransformable(M3GObject3D):
593     def __init__(self):
594         M3GObject3D.__init__(self)
595         self.hasComponentTransform=False #Boolean 
596         #IF hasComponentTransform==TRUE, THEN
597         self.translation=M3GVector3D(0,0,0) #Vector3D 
598         self.scale=M3GVector3D(1,1,1) #Vector3D 
599         self.orientationAngle=0 #Float32 
600         self.orientationAxis=M3GVector3D(0,0,0) #Vector3D undefined
601         #END
602         self.hasGeneralTransform=False #Boolean 
603         #IF hasGeneralTransform==TRUE, THEN
604         self.transform = M3GMatrix() #Matrix identity
605         self.transform.identity()
606         #END
607         #If either hasComponentTransform or hasGeneralTransform is false, the omitted fields will be
608         #initialized to their default values (equivalent to an identity transform in both cases).
609
610     def writeJava(self,aWriter,aCreate):
611         if aCreate: pass #Abstract Base Class! Cant't be created
612         M3GObject3D.writeJava(self,aWriter,False)
613         if self.hasGeneralTransform :
614             aWriter.write(2,"float[] BL%i_matrix = {" % (self.id))
615             aWriter.writeList(self.transform.elements,4,"f")
616             aWriter.write(2,"};")
617             aWriter.write(2)
618             aWriter.write(2,"Transform BL%i_transform = new Transform();" % (self.id))
619             aWriter.write(2,"BL%i_transform.set(BL%i_matrix);" % (self.id,self.id))
620             aWriter.write(2,"BL%i.setTransform(BL%i_transform);" % (self.id,self.id))
621             aWriter.write(2)
622         if self.hasComponentTransform:
623             aWriter.write(2,("BL%i.setTranslation("+self.translation.writeJava()+");")
624                              %(self.id))
625     
626     def getData(self):
627         data = M3GObject3D.getData(self)
628         data += struct.pack("<B",self.hasComponentTransform) 
629         if self.hasComponentTransform==True:
630             data += self.translation.getData()
631             data += self.scale.getData() 
632             data += struct.pack('<f',self.orientationAngle) 
633             data += self.orientationAxis.getData()
634         data += struct.pack("<B",self.hasGeneralTransform) 
635         if self.hasGeneralTransform==True:
636             data += self.transform.getData()
637         return data
638         
639     def getDataLength(self):
640         value = M3GObject3D.getDataLength(self)
641         value += struct.calcsize("<B")
642         if self.hasComponentTransform==True:
643             value += self.translation.getDataLength() 
644             value += self.scale.getDataLength() 
645             value += struct.calcsize('<f') 
646             value += self.orientationAxis.getDataLength()
647         value += struct.calcsize("<B") 
648         if self.hasGeneralTransform==True:
649             value += self.transform.getDataLength()
650         return value
651         
652         
653 class M3GNode(M3GTransformable):
654     def __init__(self):
655         M3GTransformable.__init__(self)
656         self.blenderObj = None #Pointer to corrsponding BlenderObj 
657         self.parentBlenderObj = None #Pointer to Parent in Blender
658         self.blenderMatrixWorld = None #BlenderObj matrixWorld
659         self.M3GNode_binaryFormat = '<BBBIB'
660         self.enableRendering=True #Boolean 
661         self.enablePicking=True #Boolean 
662         self.alphaFactor=255 #Byte 0x00 is equivalent to 0.0 (fully transparent), and 255 is equivalent to 1.0 (fully opaque);
663         self.scope=4294967295 #-1 #UInt32 
664         self.hasAlignment = False #Boolean 
665         #IF hasAlignment==TRUE, THEN
666         self.M3GNode_binaryFormat_2 = '<BBII'
667         self.zTarget=0 #Byte   The zTarget and yTarget fields must each hold a valid enumerated value, 
668         self.yTarget=0 #Byte   as specified in the class definition. Other values must be treated as errors.
669         self.zReference=None #ObjectIndex 
670         self.yReference=None #ObjectIndex 
671         #END
672         #If the hasAlignment field is false, the omitted fields are initialized to their default values.
673
674
675     def getData(self):
676         data = M3GTransformable.getData(self)
677         #print "Binary ",self.binaryFormat
678         data += struct.pack(self.M3GNode_binaryFormat, 
679                             self.enableRendering, 
680                             self.enablePicking,  
681                             self.alphaFactor, 
682                             self.scope,  
683                             self.hasAlignment)
684                             
685         if self.hasAlignment:
686             data += pack(self.M3GNode_binaryFormat_2, 
687                          self.zTarget,  
688                          self.yTarget, 
689                          getId(self.zReference),  
690                          getId(self.yReference)) 
691         return data
692         
693     def getDataLength(self):
694         value = M3GTransformable.getDataLength(self) + \
695                      struct.calcsize(self.M3GNode_binaryFormat)
696         if self.hasAlignment:
697             value += struct.calcsize(self.M3GNode_binaryFormat_2)
698         return value
699         
700     def writeJava(self,aWriter,aCreate):
701         if aCreate: pass #Abstract Base Class! Cant't be created
702         M3GTransformable.writeJava(self,aWriter,False)    
703         
704 class M3GGroup(M3GNode):
705     def __init__(self):
706         M3GNode.__init__(self)
707         self.ObjectType=9
708         self.children = [] #ObjectIndex[] 
709         
710     def searchDeep(self,alist):
711         for element in self.children:
712             alist = element.searchDeep(alist)
713         return M3GNode.searchDeep(self,alist)
714
715     def writeJava(self,aWriter,aCreate):
716         if aCreate:
717             aWriter.write(2,"//Group:"+self.name )
718             aWriter.write(2,"Group BL"+str(self.id)+" = new Group();")
719         M3GNode.writeJava(self,aWriter,False)
720         for element in self.children:
721             aWriter.write(2,"BL%i.addChild(BL%i);" % (self.id,element.id))
722    
723     def getData(self):
724         data = M3GNode.getData(self)
725         data = data + struct.pack("<I",len(self.children))
726         for element in self.children:
727             data += struct.pack("<I",getId(element))
728         return data
729     
730     def getDataLength(self):
731         return M3GNode.getDataLength(self)+ \
732                  struct.calcsize("<"+str(len(self.children)+1)+"I")
733
734         
735 class M3GWorld(M3GGroup):
736     def __init__(self):
737         M3GGroup.__init__(self)
738         self.ObjectType=22
739         self.activeCamera=None #ObjectIndex 
740         self.background=None #ObjectIndex UInt32 0=None
741         self.M3GWorld_binaryFormat='<II'
742         
743     def searchDeep(self,alist):
744         alist = doSearchDeep([self.activeCamera, self.background],alist)
745         return M3GGroup.searchDeep(self,alist)
746
747         
748     def writeJava(self,aWriter,aCreate):
749         if aCreate:
750             aWriter.write(2,"//World:"+self.name )
751             aWriter.write(2,"World BL"+str(self.id)+" = new World();")
752         M3GGroup.writeJava(self,aWriter,False)
753         if self.background != None:
754             aWriter.write(2,"BL"+str(self.id)+".setBackground(BL"+str(self.background.id)+");")
755         if self.activeCamera != None:
756             aWriter.write(2,"BL%i.setActiveCamera(BL%i);" %
757                             (self.id,self.activeCamera.id))
758         aWriter.write(2)
759
760         
761     def getData(self):
762         data = M3GGroup.getData(self)
763         return data + \
764                 struct.pack(self.M3GWorld_binaryFormat,getId(self.activeCamera),getId(self.background))
765
766
767     def getDataLength(self):
768         return M3GGroup.getDataLength(self) + struct.calcsize(self.M3GWorld_binaryFormat)
769  
770             
771 class M3GBackground(M3GObject3D):
772     def __init__(self):
773         M3GObject3D.__init__(self)
774         self.ObjectType=4
775         self.M3GBackground_binaryFormat = '<BBiiiiBB'
776         self.backgroundColor=M3GColorRGBA(0,0,0,0) #ColorRGBA 0x00000000 (black, transparent)
777         self.backgroundImage=None #ObjectIndex null (use the background color only)
778         self.backgroundImageModeX=32; #Byte BORDER=32 REPEAT=33
779         self.backgroundImageModeY=32; #Byte BORDER
780         self.cropX = 0; #Int32 
781         self.cropY = 0 #Int32 ;
782         self.cropWidth = 0 #Int32 ;
783         self.cropHeight = 0;#Int32 
784         self.depthClearEnabled = True #Boolean 
785         self.colorClearEnabled = True #Boolean 
786
787     def writeJava(self,aWriter,aCreate):
788         if aCreate:
789             aWriter.write(2,"//Background:"+self.name )
790             aWriter.write(2,"Background BL"+str(self.id)+" = new Background();")
791         M3GObject3D.writeJava(self,aWriter,False)
792         aWriter.write(2,"BL"+str(self.id)+".setColor("+self.backgroundColor.writeJava()+");")
793         aWriter.write(2,"")
794
795     def getData(self):
796         data = M3GObject3D.getData(self)
797         data += self.backgroundColor.getData()
798         data += struct.pack('<I',getId(self.backgroundImage))
799         data += struct.pack(self.M3GBackground_binaryFormat, self.backgroundImageModeX, 
800                             self.backgroundImageModeY,
801                             self.cropX, 
802                             self.cropY, 
803                             self.cropWidth, 
804                             self.cropHeight, 
805                             self.depthClearEnabled,  
806                             self.colorClearEnabled)
807         return data
808     
809     def getDataLength(self):
810         value=M3GObject3D.getDataLength(self)
811         value += self.backgroundColor.getDataLength()
812         value += struct.calcsize('<I')
813         value += struct.calcsize(self.M3GBackground_binaryFormat)
814         return value
815         
816         
817 class M3GCamera(M3GNode):
818     GENERIC=48      #Projection-Types
819     PARALLEL=49
820     PERSPECTIVE=50
821     
822     def __init__(self):
823         M3GNode.__init__(self)
824         self.ObjectType=5
825         self.projectionType=M3GCamera.PARALLEL #Byte 
826         #IF projectionType==GENERIC, THEN
827         self.projectionMatrix=M3GMatrix() #Matrix \95view volume : opposite corners at (-1 -1 -1) and (1 1 1)
828         # TODO: Set right matrix    
829         #ELSE
830         self.fovy=0.0 #Float32 
831         self.AspectRatio=0.0#Float32 
832         self.near=0.0#Float32 
833         self.far=0.0#Float32 
834         #END
835     
836     def writeJava(self,aWriter,aCreate):
837         if aCreate:
838             aWriter.write(2,"//Camera " + self.name)
839             aWriter.write(2,"Camera BL%i = new Camera();" % (self.id))
840         M3GNode.writeJava(self,aWriter,False)
841         aWriter.write(2,"BL%i.setPerspective(%ff,  //Field of View" % \
842                           (self.id,self.fovy))
843         aWriter.write(4,"(float)aCanvas.getWidth()/(float)aCanvas.getHeight(),")
844         aWriter.write(4,str(self.near)+"f, //Near Clipping Plane")
845         aWriter.write(4,str(self.far)+"f); //Far Clipping Plane")              
846         
847     def getData(self):
848         data = M3GNode.getData(self)
849         data += struct.pack("B",self.projectionType)
850         if self.projectionType == self.GENERIC:
851             data += self.projectionMatrix.getData()
852         else:
853             data += struct.pack("<4f",self.fovy,self.AspectRatio,self.near,self.far)
854         return data
855     
856     def getDataLength(self):
857         value = M3GNode.getDataLength(self)
858         value += struct.calcsize("B")
859         if self.projectionType == self.GENERIC:
860             value += self.projectionMatrix.getDataLength()
861         else:
862             value += struct.calcsize("<4f")
863         return value
864         
865
866 class M3GMesh(M3GNode):
867     def __init__(self,aVertexBuffer=None, aIndexBuffer=[], aAppearance=[]):
868         M3GNode.__init__(self)
869         self.ObjectType=14
870         self.vertexBuffer = aVertexBuffer #ObjectIndex 
871         self.submeshCount=len(aIndexBuffer) #UInt32 
872         #FOR each submesh...
873         self.indexBuffer=aIndexBuffer #ObjectIndex 
874         self.appearance=aAppearance #;ObjectIndex 
875         #END
876
877     def getData(self):
878         data = M3GNode.getData(self)
879         data += struct.pack('<2I', getId(self.vertexBuffer), 
880                                   self.submeshCount)
881         for i in range(len(self.indexBuffer)):
882             data += struct.pack('<2I',getId(self.indexBuffer[i]),
883                                       getId(self.appearance[i]))
884         return data
885         
886     def getDataLength(self):
887         value = M3GNode.getDataLength(self)
888         value += struct.calcsize('<2I')
889         for i in range(len(self.indexBuffer)):
890             value += struct.calcsize('<2I')
891         return value
892             
893     def searchDeep(self,alist):
894         alist = doSearchDeep([self.vertexBuffer] +self.indexBuffer
895                               + self.appearance ,alist)
896         return M3GNode.searchDeep(self,alist)
897             
898     def writeJava(self,aWriter,aCreate):
899         self.writeBaseJava(aWriter,aCreate,"Mesh","")
900         
901     def writeBaseJava(self,aWriter,aCreate,aClassName,aExtension):
902         if aCreate:
903             aWriter.writeClass(aClassName,self)
904             if self.submeshCount > 1:
905                 aWriter.write(2,"IndexBuffer[] BL%i_indexArray = {" % (self.id))
906                 aWriter.write(4,",".join(["BL%i" %(i.id) for i in self.indexBuffer ]))
907                 aWriter.write(2,"                                };")
908                 aWriter.write(2)
909                 aWriter.write(2,"Appearance[] BL%i_appearanceArray = {" % (self.id))
910                 aWriter.write(4,",".join(["BL%i" %(i.id) for i in self.appearance ]))
911                 aWriter.write(2,"                                };")
912                 aWriter.write(2)
913                 aWriter.write(2,"%s BL%i = new %s(BL%i,BL%i_indexArray,BL%i_appearanceArray%s);" % \
914                                 (aClassName,self.id,aClassName,self.vertexBuffer.id, self.id,self.id,aExtension))
915             else:
916                 #print "indexBuffer", len(self.indexBuffer)
917                 #print "appearance", len(self.appearance)
918                 aWriter.write(2,"%s BL%i = new %s(BL%i,BL%i,BL%i%s);" % \
919                                 (aClassName, 
920                                  self.id,
921                                  aClassName,
922                                  self.vertexBuffer.id,
923                                  self.indexBuffer[0].id,
924                                  self.appearance[0].id,
925                                  aExtension))
926         M3GNode.writeJava(self,aWriter,False)
927         aWriter.write(2)  
928
929
930 class M3GSkinnedMesh(M3GMesh):
931     def __init__(self,aVertexBuffer=None, aIndexBuffer=[], aAppearance=[]):
932         M3GMesh.__init__(self,aVertexBuffer, aIndexBuffer, aAppearance)
933         self.ObjectType=16
934         self.skeleton=None #ObjectIndex
935         self.bones={}
936         #print"M3GSkinnedMesh.__init__::self.vertexBuffer:",self.vertexBuffer
937         ##ObjectIndex skeleton;
938         ##UInt32 transformReferenceCount;
939         ##FOR each bone reference... 
940         ##    ObjectIndex transformNode; 
941         ##    UInt32 firstVertex; 
942         ##    UInt32 vertexCount; 
943         ##    Int32 weight;
944         ##END
945
946     def searchDeep(self,alist):
947         alist = doSearchDeep([self.skeleton],alist)
948         return M3GMesh.searchDeep(self,alist)
949
950     def addSecondBone(self):
951         secondBones = {}
952         for bone in self.bones.values():
953             bone2 = M3GBone()
954             bone2.verts=bone.verts
955             bone.verts=[]
956             mGroup = M3GGroup()
957             mGroup.name=bone.transformNode.name+"_second"
958             bone2.transformNode=mGroup
959             bone2.references = bone.references
960             bone.references = [] 
961             bone2.weight = bone.weight
962             bone.weight=0
963             mGroup.children = bone.transformNode.children
964             bone.transformNode.children = [mGroup]
965             mGroup.animationTracks=bone.transformNode.animationTracks
966             bone.transformNode.animationTracks = []
967             secondBones[bone.transformNode.name+"_second"]=bone2
968         for bone in secondBones.values():
969             self.bones[bone.transformNode.name] = bone
970             
971     def getBlenderIndexes(self):
972         #print "M3GSkinnedMesh.vertexBuffer:",self.vertexBuffer
973         return self.vertexBuffer.positions.blenderIndexes
974     
975     def writeJava(self,aWriter,aCreate):
976         self.writeBaseJava(aWriter,aCreate,"SkinnedMesh",
977                            (",BL%i" % (self.skeleton.id)))
978         aWriter.write(2,"//Transforms")
979         for bone in self.bones.values():
980             #print "bone: ", bone
981             #print "bone.references: ", bone.references
982             for ref in bone.references:
983                 aWriter.write(2,"BL%i.addTransform(BL%i,%i,%i,%i);" % 
984                                  (self.id,
985                                   bone.transformNode.id,bone.weight,
986                                   ref.firstVertex, ref.vertexCount))
987         aWriter.write(2)
988         
989     def getDataLength(self):
990         value = M3GMesh.getDataLength(self)
991         value += struct.calcsize('<I')  #skeleton
992         value += struct.calcsize('<I')  #transformReferenceCount
993         for bone in self.bones.values():
994             for ref in bone.references:
995                 value += struct.calcsize('<3Ii')
996         return value
997  
998     def getData(self):
999         data = M3GMesh.getData(self)
1000         data += struct.pack('<I', getId(self.skeleton))
1001         count = 0
1002         for bone in self.bones.values(): count+=len(bone.references)
1003         data += struct.pack('<I',count)
1004         for bone in self.bones.values():
1005             for ref in bone.references:
1006                 data += struct.pack('<I',getId(bone.transformNode))
1007                 data += struct.pack('<2I',ref.firstVertex,ref.vertexCount)
1008                 data += struct.pack('<i',bone.weight)
1009         return data
1010
1011 class M3GLight(M3GNode):
1012     def __init__(self):
1013         M3GNode.__init__(self)
1014         self.ObjectType=12
1015         self.modes = {'AMBIENT':128,
1016                       'DIRECTIONAL':129,
1017                       'OMNI':130,
1018                       'SPOT':131}
1019         self.attenuationConstant = 1.0 #Float32
1020         self.attenuationLinear = 0.0 #Float32 
1021         self.attenuationQuadratic = 0.0 #Float32 
1022         self.color = M3GColorRGB(1.0, 1.0, 1.0) #ColorRGB 
1023         self.mode = self.modes['DIRECTIONAL'] #Byte Enumurator mode: DIRECTIONAL
1024         self.intensity = 1.0 #Float32 
1025         self.spotAngle = 45 #Float32 
1026         self.spotExponent = 0.0 #Float32
1027     
1028     def writeJava(self,aWriter,aCreate):
1029         if aCreate:
1030             aWriter.write(2,"//Light: " + self.name)
1031             aWriter.write(2,"Light BL%i = new Light();" % (self.id))
1032         aWriter.write(2,"BL%i.setMode(%i);" % (self.id,self.mode)) #Light.OMNI
1033         if self.mode in [self.modes['OMNI'],self.modes['SPOT']]:#Attenuation
1034             aWriter.write(2,"BL%i.setAttenuation(%ff, %ff,%ff);" 
1035                              % (self.id,
1036                                 self.attenuationConstant,
1037                                 self.attenuationLinear, 
1038                                 self.attenuationQuadratic))
1039         aWriter.write(2,("BL%i.setColor("+self.color.writeJava()+");") 
1040                              % (self.id))
1041         aWriter.write(2,"BL%i.setIntensity(%ff);" 
1042                              % (self.id,self.intensity))
1043         if self.mode == self.modes['SPOT']:
1044             aWriter.write(2,"BL%i.setSpotAngle(%ff);" 
1045                              % (self.id,self.spotAngle))
1046             aWriter.write(2,"BL%i.setSpotExponent(%ff);" 
1047                              % (self.id,self.spotExponent))
1048         M3GNode.writeJava(self,aWriter,False)
1049         aWriter.write(2)
1050         
1051         
1052     def getData(self):
1053         data = M3GNode.getData(self)
1054         data += struct.pack("<fff", self.attenuationConstant,
1055                                     self.attenuationLinear, 
1056                                     self.attenuationQuadratic) 
1057         data += self.color.getData() 
1058         data += struct.pack("<Bfff", self.mode,
1059                                      self.intensity, 
1060                                      self.spotAngle, 
1061                                      self.spotExponent)
1062         return data
1063
1064     def getDataLength(self):
1065         value = M3GNode.getDataLength(self)
1066         value += self.color.getDataLength()
1067         value += struct.calcsize('<B6f')
1068         return value
1069         
1070 class  M3GMaterial(M3GObject3D):
1071     def __init__(self):
1072         M3GObject3D.__init__(self)
1073         self.ObjectType=13
1074         self.ambientColor=M3GColorRGB(0.2, 0.2, 0.2) #ColorRGB 
1075         self.diffuseColor=M3GColorRGBA(0.8, 0.8, 0.8, 1.0) #ColorRGBA 
1076         self.emissiveColor=M3GColorRGB(0.0, 0.0, 0.0) #ColorRGB 
1077         self.specularColor=M3GColorRGB(0.0, 0.0, 0.0) #ColorRGB 
1078         self.shininess=0.0 #Float32 
1079         self.vertexColorTrackingEnabled=False #Boolean
1080
1081     def writeJava(self,aWriter,aCreate):
1082         if aCreate :
1083             aWriter.write(2,"//Material: "+self.name )
1084             aWriter.write(2,"Material BL%i = new Material();" % (self.id))
1085         aWriter.write(2,("BL%i.setColor(Material.AMBIENT," + 
1086                                  self.ambientColor.writeJava()+");") % (self.id) )
1087         aWriter.write(2,("BL%i.setColor(Material.SPECULAR," + 
1088                                  self.specularColor.writeJava()+");") % (self.id) )
1089         aWriter.write(2,("BL%i.setColor(Material.DIFFUSE," +
1090                                  self.diffuseColor.writeJava()+");") % (self.id))
1091         aWriter.write(2,("BL%i.setColor(Material.EMISSIVE," +
1092                                  self.emissiveColor.writeJava()+");") % (self.id))
1093         aWriter.write(2,("BL%i.setShininess(%ff);") % (self.id,self.shininess))
1094         aWriter.write(2,("BL%i.setVertexColorTrackingEnable(" + 
1095                                toJavaBoolean(self.vertexColorTrackingEnabled) + ");") % 
1096                                 (self.id))
1097         M3GObject3D.writeJava(self,aWriter,False)
1098         
1099     def getData(self):
1100         data = M3GObject3D.getData(self)
1101         data += self.ambientColor.getData()
1102         data += self.diffuseColor.getData()
1103         data += self.emissiveColor.getData()
1104         data += self.specularColor.getData()
1105         data += struct.pack('<fB',self.shininess,
1106                                   self.vertexColorTrackingEnabled)
1107         return data
1108
1109
1110     def getDataLength(self):
1111         value = M3GObject3D.getDataLength(self)
1112         value += self.ambientColor.getDataLength()
1113         value += self.diffuseColor.getDataLength()
1114         value += self.emissiveColor.getDataLength()
1115         value += self.specularColor.getDataLength()
1116         value += struct.calcsize('<fB')
1117         return value
1118
1119
1120 class M3GVertexArray(M3GObject3D):
1121     def __init__(self,aNumComponents,aComponentSize,aAutoScaling=False,aUVMapping=False):
1122         M3GObject3D.__init__(self)
1123         self.ObjectType=20
1124         self.blenderIndexes={} #Translation-Table from Blender index to m3g index
1125         self.autoscaling = aAutoScaling #bias and scale should be computed internal
1126         self.uvmapping=aUVMapping #Change coordinates from blender uv to uv-m3g
1127         self.bias = [0.0,0.0,0.0] 
1128         self.scale = 1.0 
1129         self.componentSize=aComponentSize #Byte number of bytes per component; must be [1, 2]
1130         self.componentCount=aNumComponents #Byte number of components per vertex; must be [2, 4]
1131         self.encoding=0 #Byte 0="raw" as bytes or 16 bit integers.
1132         self.vertexCount=0 #UInt16 number of vertices in this VertexArray; must be [1, 65535]
1133         if self.autoscaling==True:
1134             self.components = array('f')
1135         else:
1136             self.components = self.createComponentArray()
1137         #FOR each vertex...
1138         # IF componentSize==1, THEN
1139         #  IF encoding==0, THEN
1140         #   Byte[componentCount] 
1141         #  ELSE IF encoding==1, THEN
1142         #   Byte[componentCount] 
1143         #  END
1144         # ELSE
1145         #  IF encoding==0, THEN
1146         #   Int16[componentCount] 
1147         #  ELSE IF encoding==1, THEN
1148         #   Int16[componentCount] 
1149         #  END
1150         # END
1151         #END
1152     
1153     def createComponentArray(self):
1154         if self.componentSize == 1:
1155             return array('b') #Byte-Array
1156         else:
1157             return array('h') #Short-Array
1158             
1159     def useMaxPrecision(self,aBoundingBox):
1160         """With Bias and Scale you can maximize the precision of the array"""
1161         #print "useMaxPrecision"
1162         vertexList = M3GVertexList(aBoundingBox)
1163         first = vertexList[0]
1164         minimum =[first[0],first[1],first[2]]
1165         maximum = [first[0],first[1],first[2]] #Search maximal Dimension
1166         for element in vertexList:
1167             for i in range(3):
1168                 if minimum[i] > element[i] : minimum[i] = element[i]
1169                 if maximum[i] < element[i] : maximum[i] = element[i]
1170                 #print i, maximum[i],element[i]
1171         lrange=[0,0,0]
1172         maxRange=0.0
1173         maxDimension=-1
1174         for i in range(3):  #set bias
1175             lrange[i] = maximum[i]-minimum[i]
1176             self.bias[i] = minimum[i]*0.5+maximum[i]*0.5
1177             #print "Bias",i,self.bias[i],"min-max",minimum[i],maximum[i],"lrang",lrange[i]
1178             if lrange[i] > maxRange:
1179                 maxRange = lrange[i]
1180                 maxDimension=i
1181         self.scale = maxRange/65533.0
1182         #print "MaxRange ",maxRange
1183         #print "scale",self.scale
1184
1185
1186     def internalAutoScaling(self):
1187         print "internalAutoScaling"
1188         #Already done?
1189         print self.components.typecode
1190         if not self.autoscaling or self.components.typecode!="f":return
1191         #Find bais and scale
1192         minimum=[]
1193         maximum=[]
1194         for i in range(self.componentCount):
1195             minimum.append(self.components[i])
1196             maximum.append(self.components[i])         
1197         for i in range(0,len(self.components),self.componentCount):
1198             for j in range(self.componentCount):
1199                 if minimum[j] > self.components[i+j] : minimum[j] = self.components[i+j]
1200                 if maximum[j] < self.components[i+j] : maximum[j] = self.components[i+j]
1201                 #print "i+j=",i+j,"min=",minimum[j],"max=",maximum[j],"elem=",self.components[i+j]
1202         #print "min=", minimum
1203         #print "max=", maximum
1204         lrange=[0] * self.componentCount
1205         maxRange=0.0
1206         maxDimension=-1
1207         for i in range(self.componentCount):  #set bias
1208             lrange[i] = maximum[i]-minimum[i]
1209             self.bias[i] = minimum[i]*0.5+maximum[i]*0.5
1210             #print "Bias",i,self.bias[i],"min-max",minimum[i],maximum[i],"lrang",lrange[i]
1211             if lrange[i] > maxRange:
1212                 maxRange = lrange[i]
1213                 maxDimension=i
1214         maxValue=(2**(8*self.componentSize)*1.0)-2.0
1215         #print "MaxValue=",maxValue
1216         self.scale = maxRange/maxValue
1217         #print "MaxRange ",maxRange
1218         #print "scale",self.scale
1219         #Copy Components
1220         oldArray=self.components
1221         self.components=self.createComponentArray()
1222         for i in range(0,len(oldArray),self.componentCount):
1223             for j in range(self.componentCount):
1224                 element=int((oldArray[i+j]-self.bias[j])/self.scale)
1225                 #print "element",element
1226                 self.components.append(element)
1227         # Reverse t coordinate because M3G uses a different 2D coordinate system than Blender.
1228         if self.uvmapping:
1229             for i in range(0,len(self.components),2):
1230                 self.components[i+1]= int(self.components[i+1]*(-1)) #Error in Version 0.7
1231         for i in range(len(self.components)):
1232             if abs(self.components[i])>maxValue:raise Exception( i+". element too great/small!")
1233                 
1234     def writeJava(self,aWriter,aCreate):
1235         self.internalAutoScaling()
1236         if aCreate:
1237             aWriter.write(2,"// VertexArray " + self.name)
1238             if self.componentSize == 1:
1239                 aWriter.write(2,"byte[] BL%i_array = {" % (self.id))
1240             else:
1241                 aWriter.write(2,"short[] BL%i_array = {" % (self.id))
1242             aWriter.writeList(self.components)
1243             aWriter.write(2,"};")
1244             aWriter.write(2)
1245             aWriter.write(2,"VertexArray BL%i = new VertexArray(BL%i_array.length/%i,%i,%i);" %
1246                         (self.id,self.id,
1247                             self.componentCount,self.componentCount,self.componentSize))
1248             aWriter.write(2,"BL%i.set(0,BL%i_array.length/%i,BL%i_array);" % 
1249                     (self.id,self.id,self.componentCount,self.id))
1250         M3GObject3D.writeJava(self,aWriter,False)
1251         aWriter.write(2)
1252      
1253     
1254     def getData(self):
1255         self.internalAutoScaling()
1256         self.vertexCount = len(self.components)/self.componentCount
1257         data = M3GObject3D.getData(self)
1258         data += struct.pack('<3BH',self.componentSize,
1259                                  self.componentCount,
1260                                  self.encoding,
1261                                  self.vertexCount)
1262         componentType = ""
1263         if self.componentSize == 1:
1264             componentType = "b"
1265         else:
1266             componentType = "h"
1267         for element in self.components:
1268             data += struct.pack('<'+componentType,element)
1269         return data
1270         
1271     def getDataLength(self):
1272         self.internalAutoScaling()
1273         value = M3GObject3D.getDataLength(self)
1274         value += struct.calcsize('<3BH')
1275         componentType = ""
1276         if self.componentSize == 1:
1277             componentType = "b"
1278         else:
1279             componentType = "h"
1280         value += struct.calcsize('<'+str(len(self.components))+componentType)
1281         return value
1282         
1283     def append(self,element,index=None):
1284         #print "type(element):",type(element)
1285         if type(element) is Types.vectorType :
1286             for i in range(3):
1287                 value = int((element[i]-self.bias[i])/self.scale)  
1288                 #print "append:",i,element[i],(element[i]-self.bias[i]),value                  
1289                 self.components.append(value)
1290         elif type(element) is Types.MVertType:
1291             for i in range(3):
1292                 value = int((element.co[i]-self.bias[i])/self.scale)  
1293                 #print "append:",i,element[i],(element[i]-self.bias[i]),value                  
1294                 self.components.append(value)
1295             if index!=None:
1296                 key=str(len(self.blenderIndexes))
1297                 #print"key,index:",key,index
1298                 self.blenderIndexes[key]=index
1299                 #print"blenderIndexes",self.blenderIndexes
1300         else:
1301             print "VertexArray.append: element=",element
1302             self.components.append(element)
1303             
1304 class M3GVertexBuffer(M3GObject3D):
1305     def __init__(self):
1306         M3GObject3D.__init__(self)
1307         self.ObjectType=21
1308         self.defaultColor=M3GColorRGBA(255,255,255) #ColorRGBA 0xFFFFFFFF (opaque white).
1309         self.positions = None #ObjectIndex 
1310         self.positionBias = [0.0,0.0,0.0] #Float32[3] 
1311         self.positionScale = 1.0 #Float32 
1312         self.normals = None #ObjectIndex 
1313         self.colors = None #ObjectIndex
1314         self.texCoordArrays = [] 
1315         self.texcoordArrayCount = 0 #UInt32 
1316 ##        #FOR each texture coordinate array...
1317 ##        self.texCoords = [] #ObjectIndex 
1318 ##        self.texCoordBias=[] #Float32[3] 
1319 ##        self.texCoordScale=[] #;Float32 
1320 ##        #END
1321 ##        #If a texture coordinate array has only two components, the corresponding texCoordBias[2] element
1322 ##        #must be 0.0.
1323 ##        #Null texture coordinate arrays are never serialized, regardless of their position. A single texture
1324 ##        #coordinate array will therefore always be serialized as belonging to texturing unit 0, regardless of
1325 ##        #its original unit it was assigned to.
1326 ##        #There are as many references in the texture coordinates array as there are active texture units for
1327 ##        #this geometry. The texture coordinate references are loaded sequentially from texture unit 0. If the
1328 ##        #implementation supports more texture units than are specified, these are left in their default, inactive
1329 ##        #state, with a null texture coordinate reference and an undefined bias and scale.
1330 ##        #If more texture coordinate references are specified than are supported by the implementation, then
1331 ##        #this must be treated as an error, as it would be in the API. The application can then decide on an
1332 ##        #appropriate course of action to handle this case.
1333
1334     def searchDeep(self,alist):
1335         if self.positions!=None: alist = self.positions.searchDeep(alist)
1336         if self.normals != None: alist = self.normals.searchDeep(alist)
1337         if self.colors!= None: alist = self.colors.searchDeep(alist)
1338         alist = doSearchDeep(self.texCoordArrays, alist)
1339         return M3GObject3D.searchDeep(self,alist)
1340     
1341     def setPositions(self,aVertexArray):
1342         self.positions = aVertexArray
1343         self.positionBias = aVertexArray.bias
1344         self.positionScale = aVertexArray.scale
1345     
1346     def writeJava(self,aWriter,aCreate):
1347         if aCreate:
1348             aWriter.write(2,"//VertexBuffer"+self.name )
1349             aWriter.write(2,"VertexBuffer BL%i = new VertexBuffer();" % (self.id))
1350         aWriter.write(2,"float BL%i_Bias[] = { %ff, %ff, %ff};" %
1351                             (self.id,self.positionBias[0],
1352                              self.positionBias[1],self.positionBias[2]))
1353         aWriter.write(2,"BL%i.setPositions(BL%i,%ff,BL%i_Bias);" % 
1354                                (self.id, self.positions.id,
1355                                 self.positionScale,self.id))
1356         aWriter.write(2,"BL%i.setNormals(BL%i);" % (self.id,self.normals.id))
1357         #if self.colors != None: aWriter.write(2,"BL%i.setTexCoords(0,BL%i,1.0f,null);" % 
1358         #                                           (self.id,self.colors.id))
1359         lIndex = 0
1360         for iTexCoord in self.texCoordArrays:
1361             aWriter.write(2,"float BL%i_%i_TexBias[] = { %ff, %ff, %ff};" %
1362                             (self.id,lIndex, iTexCoord.bias[0],
1363                              iTexCoord.bias[1],iTexCoord.bias[2]))
1364            #int index, javax.microedition.m3g.VertexArray194 texCoords, float scale, float[] bias
1365             aWriter.write(2,"BL%i.setTexCoords(%i,BL%i,%ff,BL%i_%i_TexBias);" % 
1366                                (self.id, lIndex, iTexCoord.id, iTexCoord.scale,self.id,lIndex))
1367             lIndex += 1
1368    
1369         M3GObject3D.writeJava(self,aWriter,False)
1370     
1371     
1372     def getData(self):
1373         self.texcoordArrayCount = len(self.texCoordArrays)
1374         data = M3GObject3D.getData(self)
1375         data += self.defaultColor.getData()
1376         data += struct.pack('<I4f3I',getId(self.positions),
1377                                      self.positionBias[0],
1378                                      self.positionBias[1],
1379                                      self.positionBias[2],
1380                                      self.positionScale,
1381                                      getId(self.normals),
1382                                      getId(self.colors),
1383                                      self.texcoordArrayCount)
1384         for iTexCoord in self.texCoordArrays:
1385             data += struct.pack('<I', getId(iTexCoord))
1386             data += struct.pack('<ffff', iTexCoord.bias[0],
1387                                     iTexCoord.bias[1],
1388                                     iTexCoord.bias[2],
1389                                     iTexCoord.scale)
1390         return data
1391
1392     
1393     def getDataLength(self):
1394         value = M3GObject3D.getDataLength(self)
1395         value += self.defaultColor.getDataLength()
1396         value += struct.calcsize('<I4f3I')
1397         value += struct.calcsize('<Iffff') * len(self.texCoordArrays)
1398         return value
1399
1400
1401 class M3GPolygonMode(M3GObject3D):
1402     CULL_BACK=160
1403     CULL_NONE=162
1404     SHADE_FLAT=164
1405     SHADE_SMOOTH=165
1406     WINDING_CCW=168
1407     WINDING_CW=169
1408     
1409     def __init__(self):
1410         M3GObject3D.__init__(self)
1411         self.ObjectType=8
1412         self.culling=M3GPolygonMode.CULL_BACK #Byte
1413         self.shading=M3GPolygonMode.SHADE_SMOOTH #Byte
1414         self.winding=M3GPolygonMode.WINDING_CCW #Byte
1415         self.twoSidedLightingEnabled = False #Boolean 
1416         self.localCameraLightingEnabled = False #Boolean 
1417         self.perspectiveCorrectionEnabled = False #Boolean
1418         
1419     def writeJava(self,aWriter,aCreate):
1420         if aCreate:
1421             aWriter.write(2,"PolygonMode BL%i = new PolygonMode();" % (self.id))
1422         aWriter.write(2,"BL%i.setCulling(%i);" % (self.id,self.culling))
1423         aWriter.write(2,"BL%i.setShading(%i);" % (self.id,self.shading))
1424         aWriter.write(2,"BL%i.setWinding(%i);" % (self.id,self.winding))
1425         aWriter.write(2,("BL%i.setTwoSidedLightingEnable(" + 
1426                 toJavaBoolean(self.twoSidedLightingEnabled) + ");") % 
1427                        (self.id))
1428         aWriter.write(2,("BL%i.setLocalCameraLightingEnable(" + 
1429                 toJavaBoolean(self.localCameraLightingEnabled) + ");") % 
1430                        (self.id))
1431         aWriter.write(2,("BL%i.setPerspectiveCorrectionEnable(" + 
1432                 toJavaBoolean(self.perspectiveCorrectionEnabled) + ");") % 
1433                        (self.id))
1434         aWriter.write(2)
1435         M3GObject3D.writeJava(self,aWriter,False)
1436     
1437     def getData(self):
1438         data = M3GObject3D.getData(self)
1439         data += struct.pack('6B',self.culling,
1440                                  self.shading,
1441                                  self.winding,
1442                                  self.twoSidedLightingEnabled, 
1443                                  self.localCameraLightingEnabled, 
1444                                  self.perspectiveCorrectionEnabled)
1445         return data
1446
1447     def getDataLength(self):
1448         value = M3GObject3D.getDataLength(self)
1449         value += struct.calcsize('6B')
1450         return value
1451
1452 class M3GIndexBuffer(M3GObject3D):
1453     def __init__(self):
1454         M3GObject3D.__init__(self)
1455
1456     def getData(self):
1457         return M3GObject3D.getData(self)
1458         
1459     def getDataLength(self):
1460         return M3GObject3D.getDataLength(self)
1461     
1462     def writeJava(self,aWriter,aCreate):
1463         M3GObject3D.writeJava(self,aWriter,False)
1464         
1465         
1466 class M3GTriangleStripArray(M3GIndexBuffer):
1467     def __init__(self):
1468         M3GIndexBuffer.__init__(self)
1469         self.ObjectType=11 
1470         self.encoding=128 #Byte Bit 7: 1 = explicit property on index buffer true
1471                       #Bit 1 .. 6: 0 = "raw" integer values 1= a single byte will suffice
1472                                   #2 = a 16 bit integer is suffi to hold all the given index values
1473         #IF encoding == 0, THEN
1474         #self.startIndex = 0  #;UInt32 
1475         #ELSE IF encoding == 1, THEN
1476         #Byte startIndex;
1477         #ELSE IF encoding == 2, THEN
1478         #UInt16 startIndex;
1479         #ELSE IF encoding == 128, THEN
1480         self.indices = [] #;UInt32[]
1481         #ELSE IF encoding == 129, THEN
1482         #Byte[] indices;
1483         #ELSE IF encoding == 130, THEN
1484         #UInt16[] indices;
1485         #END
1486         self.stripLengths = [] #;UInt32[]
1487
1488     def writeJava(self,aWriter,aCreate):
1489         if aCreate:
1490             aWriter.write(2,"//length of TriangleStrips")
1491             aWriter.write(2,"int[] BL"+str(self.id)+"_stripLength ={"+
1492                 ",".join([str(element) for element in self.stripLengths])+"};")
1493             aWriter.write(2)
1494             aWriter.write(2,"//IndexBuffer")
1495             aWriter.write(2,"int[] BL%i_Indices = {" % (self.id))
1496             aWriter.write(2,",".join([str(element) for element in self.indices])+"};")
1497             aWriter.write(2)
1498             aWriter.write(2,"IndexBuffer BL%i=new TriangleStripArray(BL%i_Indices,BL%i_stripLength);" %
1499                             (self.id, self.id, self.id))
1500         M3GIndexBuffer.writeJava(self,aWriter,False)
1501         aWriter.write(2)
1502      
1503     
1504     def getData(self):
1505         data = M3GIndexBuffer.getData(self)
1506         data += struct.pack('<BI',self.encoding,
1507                                   len(self.indices))
1508         for element in self.indices:
1509             data += struct.pack('<I',element)
1510         data += struct.pack('<I',len(self.stripLengths))
1511         for element in self.stripLengths:
1512             data += struct.pack('<I',element)
1513         return data
1514     
1515     def getDataLength(self):
1516         value = M3GIndexBuffer.getDataLength(self)
1517         value += struct.calcsize('<BI')
1518         if len(self.indices) > 0 :
1519             value += struct.calcsize('<' + str(len(self.indices)) + 'I')
1520         value += struct.calcsize('<I')
1521         if len(self.stripLengths) > 0:
1522             value+= struct.calcsize('<'+str(len(self.stripLengths))+'I')
1523         return value
1524         
1525         
1526 class M3GAppearance(M3GObject3D):
1527     def __init__(self):
1528         M3GObject3D.__init__(self)
1529         self.ObjectType=3
1530         self.layer=0 #Byte 
1531         self.compositingMode=None #ObjectIndex
1532         self.fog=None #ObjectIndex 
1533         self.polygonMode=None #ObjectIndex 
1534         self.material=None #ObjectIndex 
1535         self.textures=[] #;ObjectIndex[]
1536         
1537     def searchDeep(self,alist):
1538         alist = doSearchDeep([self.compositingMode,self.fog,
1539                               self.polygonMode,self.material]
1540                              + self.textures,alist)
1541         return M3GObject3D.searchDeep(self,alist)
1542
1543     def getData(self):
1544         data = M3GObject3D.getData(self)
1545         data += struct.pack("<B5I", self.layer,
1546                                     getId(self.compositingMode),
1547                                     getId(self.fog), 
1548                                     getId(self.polygonMode), 
1549                                     getId(self.material), 
1550                                     len(self.textures))
1551         for element in self.textures:
1552             data += struct.pack("<I",getId(element))
1553         return data
1554     
1555     def getDataLength(self):
1556         value = M3GObject3D.getDataLength(self)
1557         value += struct.calcsize("<B5I")
1558         if len(self.textures) > 0 : 
1559             value += struct.calcsize("<"+str(len(self.textures))+'I')
1560         return value
1561         
1562         
1563     def writeJava(self,aWriter,aCreate):
1564         if aCreate:
1565             aWriter.write(2,"//Appearance")
1566             aWriter.write(2,"Appearance BL%i = new Appearance();" % (self.id))
1567         if self.compositingMode!= None:
1568             aWriter.write(2,"BL%i.setPolygonMode(BL%i);" % 
1569                              (self.id,self.compositingMode.id))
1570         if self.fog!=None:
1571             aWriter.write(2,"BL%i.setFog(BL%i);" %
1572                               (self.id,self.fog.id))
1573         if self.polygonMode!=None:
1574             aWriter.write(2,"BL%i.setPolygonMode(BL%i);" %
1575                               (self.id,self.polygonMode.id))
1576         if self.material!=None: 
1577             aWriter.write(2,"BL%i.setMaterial(BL%i);" %
1578                               (self.id,self.material.id))
1579         i=0
1580         for itexture in self.textures:
1581             aWriter.write(2,"BL%i.setTexture(%i,BL%i);" %           
1582                              (self.id,i,itexture.id))
1583             i =+ 1
1584         M3GObject3D.writeJava(self,aWriter,False)
1585         aWriter.write(2)  
1586
1587 class M3GTexture2D(M3GTransformable):
1588     #M3G imposes the following restrictions when assigning textures to a model: 
1589     #The dimensions must be powers of two (4, 8, 16, 32, 64, 128...).
1590
1591     WRAP_REPEAT = 241
1592     WRAP_CLAMP = 240
1593     FILTER_BASE_LEVEL=208
1594     FILTER_LINEAR=209
1595     FILTER_NEAREST=210
1596     FUNC_ADD=224
1597     FUNC_BLEND=225
1598     FUNC_DECAL=226
1599     FUNC_MODULATE=227
1600     FUNC_REPLACE=228
1601
1602     def __init__(self,aImage):
1603         M3GTransformable.__init__(self)
1604         self.ObjectType=17
1605         self.Image = aImage #ObjectIndex
1606         self.blendColor=M3GColorRGB(0,0,0)
1607         self.blending=M3GTexture2D.FUNC_MODULATE #Byte
1608         self.wrappingS=M3GTexture2D.WRAP_REPEAT #Byte 
1609         self.wrappingT=M3GTexture2D.WRAP_REPEAT #Byte 
1610         self.levelFilter=M3GTexture2D.FILTER_BASE_LEVEL #Byte 
1611         self.imageFilter=M3GTexture2D.FILTER_NEAREST #Byte
1612
1613     def searchDeep(self,alist):
1614         alist = doSearchDeep([self.Image],alist)
1615         return M3GTransformable.searchDeep(self,alist)
1616
1617     def getData(self):
1618         data = M3GTransformable.getData(self)
1619         data += struct.pack('<I', getId(self.Image))
1620         data += self.blendColor.getData()
1621         data += struct.pack('5B',self.blending,
1622                                  self.wrappingS, 
1623                                  self.wrappingT, 
1624                                  self.levelFilter, 
1625                                  self.imageFilter)
1626         return data
1627     
1628     def getDataLength(self):
1629         value = M3GTransformable.getDataLength(self)
1630         value += struct.calcsize('<I')
1631         value += self.blendColor.getDataLength()
1632         value += struct.calcsize('5B')
1633         return value
1634             
1635     def writeJava(self,aWriter,aCreate):
1636         if aCreate:
1637             aWriter.write(2,"//Texture")
1638             aWriter.write(2,"Texture2D BL%i = new Texture2D(BL%i);" % (self.id,
1639                                                                       self.Image.id))
1640         aWriter.write(2,"BL%i.setFiltering(%i,%i);" % (self.id,
1641                                    self.levelFilter, 
1642                                    self.imageFilter))
1643         aWriter.write(2,"BL%i.setWrapping(%i,%i);" % (self.id,
1644                                     self.wrappingS,
1645                                     self.wrappingT))
1646         aWriter.write(2,"BL%i.setBlending(%i);" % (self.id,self.blending))
1647         aWriter.write(2)
1648         M3GTransformable.writeJava(self,aWriter,False)
1649
1650 class ImageFactory:
1651     images={}
1652     def getImage(self, image, externalReference):
1653         # It's important to use getFilename() because getName() returns a 
1654         # truncated string depending on the length of the file name.
1655         filename = Blender.sys.expandpath(image.getFilename())
1656
1657         if self.images.has_key(filename):
1658             image = self.images[filename]
1659         elif externalReference:
1660             # Check for file ending (only relevant for external images). The M3G specification 
1661             # mandates only PNG support, but some devices might also support additional image types.
1662             [path,ext] = splitext(filename)
1663             if ext != ".png":
1664                 print "Warning: image file ends with " + ext + ". M3G specification only mandates PNG support."
1665
1666             image = M3GExternalReference()
1667             image.URI = Blender.sys.basename(filename)
1668             self.images[filename] = image
1669         else:
1670             image = M3GImage2D(image)
1671             self.images[filename] = image
1672         return image
1673             
1674     getImage = classmethod(getImage)
1675
1676         
1677 class M3GImage2D(M3GObject3D):
1678     ALPHA=96             #a single byte per pixel, representing pixel opacity
1679     LUMINANCE=97         #a single byte per pixel, representing pixel luminance.
1680     LUMINANCE_ALPHA=98   #two bytes per pixel. The first: luminance, the second: alpha.
1681     RGB=99               #three bytes per pixel, representing red, green and blue
1682     RGBA=100             #four bytes per pixel, representing red, green, blue and alpha
1683
1684     def __init__(self, aImage, aFormat=RGBA):
1685         M3GObject3D.__init__(self)
1686         self.ObjectType=10
1687         self.image=aImage #Blender Image
1688         self.format=aFormat #Byte 
1689         self.isMutable=False #Boolean changable or not
1690         [self.width, self.height] = aImage.getSize()
1691
1692         #IF isMutable==false, THEN
1693         self.palette=0 #Byte[] 
1694         self.pixels = array('B') #Byte[] 
1695         self.extractPixelsFromImage()
1696         #END
1697 #For a palettised format, the pixels array contains a single palette 
1698 #index per pixel, and the palette array will contain up to 256 entries, 
1699 #each consisting of a pixel specifier appropriate to the format chosen.
1700
1701 #For a non-palettised format, the palette array will be empty, 
1702 #and the pixels array contains a pixel specifier appropriate to the format 
1703 #chosen.
1704 #In a pixel specifier, each byte is scaled such that 0 represents the 
1705 #value 0.0 and 255 represents the value 1.0. The different formats 
1706 #require different data to be serialized, as follows:
1707
1708 #The width and height of the image must be non-negative powers of two, but they need not be equal.
1709
1710     def getData(self):
1711         data = M3GObject3D.getData(self)
1712         data += struct.pack('2B', self.format, self.isMutable)
1713         data += struct.pack('<2I', self.width, self.height)
1714         if self.isMutable == False:
1715             # TODO: support palettised formats also
1716             # export palette data
1717             data += struct.pack('<I', 0)
1718             
1719             # export pixel data
1720             if self.format == M3GImage2D.RGBA:
1721                 #print "len pixels",len(self.pixels)
1722                 data += struct.pack('<I', len(self.pixels))
1723                 for pixel in self.pixels:
1724                     data += struct.pack('B', pixel)
1725             #elif...
1726         return data
1727
1728     def getDataLength(self):
1729         value = M3GObject3D.getDataLength(self)
1730         value += struct.calcsize('2B')
1731         value += struct.calcsize('<2I')
1732         if self.isMutable == False:
1733             # TODO: support palettised formats also
1734             value+= struct.calcsize('<I')
1735             
1736             # pixel data size
1737             if self.format == M3GImage2D.RGBA:
1738                 value += struct.calcsize('<I')
1739                 value += struct.calcsize(str(len(self.pixels))+'B')
1740         return value
1741     
1742     def writeJava(self,aWriter,aCreate):
1743         if aCreate:
1744             lFileName = self.image.filename
1745             if not Blender.sys.exists(lFileName) :
1746                 lFileName = Blender.sys.join(dirname(Blender.Get('filename')),
1747                                              basename(self.image.filename))
1748             elif not Blender.sys.exists(lFileName):
1749                 raise FileError, 'Image file not found!'
1750             lTargetFile = Blender.sys.join(Blender.sys.dirname(aWriter.filename),
1751                                              Blender.sys.basename(self.image.filename))   
1752             copy_file(lFileName,lTargetFile)
1753             #shutil.copyfile(lFileName,lTargetFile)
1754             aWriter.write(2,"//Image2D")
1755             aWriter.write(2,"Image BL%i_Image = null;" % (self.id))
1756             aWriter.write(2,"try {")
1757             aWriter.write(3,"BL%i_Image = Image.createImage(\"/%s\");" %
1758                                     (self.id,basename(self.image.filename)))
1759             aWriter.write(2,"} catch (IOException e) {")
1760             aWriter.write(3,"e.printStackTrace();")
1761             aWriter.write(2,"}")
1762             aWriter.write(2,"Image2D BL%i = new Image2D(Image2D.RGBA,BL%i_Image);" % 
1763                                     (self.id,self.id))   
1764         aWriter.write(2)
1765         M3GObject3D.writeJava(self,aWriter,False)
1766         aWriter.write(2)  
1767         
1768     def extractPixelsFromImage(self):
1769         # Reverse t coordiante because M3G uses a different 2D coordinate system than OpenGL.
1770         for y in range(self.height):
1771             for x in range(self.width):
1772                 [r, g, b, a] = self.image.getPixelI(x, self.height-1-y)
1773                 self.pixels.append(r)
1774                 self.pixels.append(g)
1775                 self.pixels.append(b)
1776                 self.pixels.append(a)
1777                 
1778 class M3GAnimationController(M3GObject3D):
1779     def __init__(self):
1780         M3GObject3D.__init__(self)
1781         self.ObjectType=1
1782         self.speed = 1.0 #Float32
1783         self.weight = 1.0 #Float32
1784         self.activeIntervalStart = 0 #Int32 - (always active)
1785         self.activeIntervalEnd = 0 #Int32 
1786         self.referenceSequenceTime = 0.0 #Float32 
1787         self.referenceWorldTime = 0 #Int32 
1788
1789     def writeJava(self,aWriter,aCreate):
1790         if aCreate:
1791             aWriter.writeClass("AnimationController",self)
1792             aWriter.write(2,"AnimationController BL%i = new AnimationController();" %
1793                                    (self.id))
1794         aWriter.write(2,"BL%i.setActiveInterval(%i, %i);" %
1795                             (self.id,self.activeIntervalStart,self.activeIntervalEnd))
1796         #lightAnim.setPosition(0, 2000);(2) Applying the animation during rendering
1797         M3GObject3D.writeJava(self,aWriter,False)
1798             
1799     def getData(self):
1800         data = M3GObject3D.getData(self)
1801         data += struct.pack("<ffiifi", self.speed,
1802                                     self.weight,
1803                                     self.activeIntervalStart,
1804                                     self.activeIntervalEnd, 
1805                                     self.referenceSequenceTime, 
1806                                     self.referenceWorldTime)
1807         return data
1808         
1809     def getDataLength(self):
1810         value = M3GObject3D.getDataLength(self)
1811         return value + struct.calcsize("<ffiifi")
1812                 
1813 class M3GAnimationTrack(M3GObject3D):
1814     ALPHA=256
1815     AMBIENT_COLOR=257
1816     COLOR=258
1817     CROP=259
1818     DENSITY=260
1819     DIFFUSE_COLOR=261
1820     EMISSIVE_COLOR=262
1821     FAR_DISTANCE=263
1822     FIELD_OF_VIEW=264
1823     INTENSITY=265
1824     MORPH_WEIGHTS=266
1825     NEAR_DISTANCE=267
1826     ORIENTATION=268
1827     PICKABILITY=269
1828     SCALE=270
1829     SHININESS=271
1830     SPECULAR_COLOR=272
1831     SPOT_ANGLE=273
1832     SPOT_EXPONENT=274
1833     TRANSLATION=275
1834     VISIBILITY=276
1835
1836     def __init__(self,aSequence,aProperty):
1837         M3GObject3D.__init__(self)
1838         self.ObjectType = 2
1839         self.keyframeSequence = aSequence #ObjectIndex 
1840         self.animationController = None #ObjectIndex
1841         self.propertyID = aProperty #UInt32 
1842     
1843     def getData(self):
1844         data = M3GObject3D.getData(self)
1845         data += struct.pack("<3I", getId(self.keyframeSequence),
1846                                    getId(self.animationController),
1847                                    self.propertyID)
1848         return data
1849         
1850     def getDataLength(self):
1851         value = M3GObject3D.getDataLength(self)
1852         return value + struct.calcsize("<3I")
1853             
1854     def writeJava(self,aWriter,aCreate):
1855         if aCreate:
1856                 aWriter.writeClass("AnimationTrack",self)
1857                 #print "self.id,self.keyframeSequence,self.propertyID",self.id,self.keyframeSequence,self.propertyID
1858                 aWriter.write(2,"AnimationTrack BL%i = new AnimationTrack(BL%i,%i);" %
1859                           (self.id,self.keyframeSequence.id,self.propertyID))
1860         aWriter.write(2,"BL%i.setController(BL%i);" %
1861                           (self.id,self.animationController.id))
1862         M3GObject3D.writeJava(self,aWriter,False)
1863         
1864     def searchDeep(self,alist):
1865         alist = doSearchDeep([self.keyframeSequence, self.animationController],alist)
1866         return M3GObject3D.searchDeep(self,alist)
1867        
1868 class M3GKeyframeSequence(M3GObject3D):
1869     CONSTANT=192
1870     LINEAR=176
1871     LOOP=193
1872     SLERP=177
1873     SPLINE=178
1874     SQUAD=179
1875     STEP=180
1876         
1877     def __init__(self,aNumKeyframes, aNumComponents,aBlenderInterpolation,
1878                       aM3GInterpolation=None):
1879         M3GObject3D.__init__(self)
1880         self.ObjectType = 19
1881         if aM3GInterpolation!=None:
1882             self.interpolation = aM3GInterpolation
1883         else:
1884             if aBlenderInterpolation == "Constant":
1885                 self.interpolation = self.STEP #Byte 
1886             elif aBlenderInterpolation == "Bezier":
1887                 self.interpolation = self.SPLINE #Byte 
1888             elif aBlenderInterpolation == "Linear":
1889                 self.interpolation = self.LINEAR #Byte 
1890             else:
1891                 pass # TODO : Throw Error
1892         self.repeatMode =  self.CONSTANT #Byte CONSTANT or LOOP
1893         self.encoding = 0 #Byte 0=raw 
1894         # TODO: Other encodings
1895         self.duration = 0 #UInt32 
1896         self.validRangeFirst = 0 #UInt32 
1897         self.validRangeLast = 0 #UInt32 
1898         self.componentCount = aNumComponents #UInt32
1899         self.keyframeCount = aNumKeyframes #UInt32  
1900         #IF encoding == 0
1901         #FOR each key frame...
1902         self.time = [] #Int32
1903         self.vectorValue = [] # Float32[componentCount]
1904         #END
1905         #ELSE IF encoding == 1
1906         #Float32[componentCount] vectorBias;
1907         #Float32[componentCount] vectorScale;
1908         #FOR each key frame...
1909         #Int32 time;
1910         #Byte[componentCount] vectorValue;
1911         #END
1912         #ELSE IF encoding == 2
1913         #Float32[componentCount] vectorBias;
1914         #Float32[componentCount] vectorScale;
1915         #FOR each key frame...
1916         #Int32 time;
1917         #UInt16[componentCount] vectorValue;
1918         #END
1919         #END
1920         
1921 #All of the vectorValue arrays are the same size, so a separate count is stored outside the individual
1922 #keyframe's data rather than with each array. The encoding field indicates the encoding scheme to be used for the keyframe data. Only the
1923 #nominated values above are allowed. Other values must be treated as errors.
1924
1925 #\95Encoding 0 indicates that the values are stored "raw" as floats.
1926 #\95Encodings 1 and 2 indicate that the values are quantized to 1 or 2 bytes. For each component,
1927 #a bias and scale are calculated from the sequence of values for that component. The bias is the
1928 #mimimum value, the scale is the maximum value minus the minimum value. The raw values
1929 #are then converted to a value 0..1 by subtracting the bias and dividing by the scale. These raw
1930 #values are then quantized into the range of a Byte or UInt16 by multiplying by 255 or 65535
1931 #respectively. The converse operation restores the original value from the quantized values.
1932
1933     def beforeExport(self):
1934         #M3G can not work with negative zero
1935         #print"beforeExport ID= ",self.id
1936         for i in range(self.keyframeCount):
1937             for j in range(self.componentCount):
1938                 x = struct.pack("<f",self.vectorValue[i][j])
1939                 y = struct.unpack("<f",x)
1940                 #print "beforeExport i,j ",i,j,self.vectorValue[i][j],y
1941                 if abs(self.vectorValue[i][j]) < 0.000001 :
1942                     #print "Negative Zero found!",self.vectorValue[i][j]
1943                     self.vectorValue[i][j]=0.0
1944                     #print "zero ",self.vectorValue[i][j]
1945     
1946     def getData(self):
1947         self.beforeExport()
1948         data = M3GObject3D.getData(self)
1949         data += struct.pack("<3B5I",self.interpolation, 
1950                                     self.repeatMode,
1951                                     self.encoding, 
1952                                     self.duration, 
1953                                     self.validRangeFirst, 
1954                                     self.validRangeLast, 
1955                                     self.componentCount,
1956                                     self.keyframeCount) 
1957         #FOR each key frame...
1958         for i in range(self.keyframeCount):
1959             data += struct.pack("<i",self.time[i]) #Int32
1960             for j in range(self.componentCount):
1961                 data += struct.pack("<f",self.vectorValue[i][j]) # Float32[componentCount]
1962         return data
1963
1964     def getDataLength(self):
1965         value = M3GObject3D.getDataLength(self)
1966         value += struct.calcsize("<3B5I")
1967         value += struct.calcsize("<i") * self.keyframeCount
1968         value += struct.calcsize("<f") * self.keyframeCount * self.componentCount
1969         return value
1970         
1971     def setRepeatMode(self,aBlenderMode):
1972         if aBlenderMode == "Constant" :
1973             self.repeatMode = self.CONSTANT
1974         elif aBlenderMode == "Cyclic":
1975             self.repeatMode = self.LOOP
1976         else:
1977             print "In IPO: Mode " + aBlenderMode + " is not assisted!" 
1978
1979     def setKeyframe(self, aIndex, aTime, aVector):
1980         self.time.append(aTime)
1981         self.vectorValue.append(aVector)
1982             
1983     def writeJava(self,aWriter,aCreate):
1984         self.beforeExport()
1985         if aCreate:
1986             aWriter.writeClass("KeyframeSequence",self)
1987             aWriter.write(2,"KeyframeSequence BL%i = new KeyframeSequence(%i, %i, %i);" %
1988                             (self.id,self.keyframeCount,self.componentCount,self.interpolation))
1989             for i in range(len(self.time)):
1990                 lLine = "BL%i.setKeyframe(%i,%i, new float[] { %ff, %ff, %ff" % \
1991                                         (self.id,i,self.time[i],self.vectorValue[i][0], \
1992                                           self.vectorValue[i][1],self.vectorValue[i][2])
1993                 if self.componentCount == 4:
1994                     lLine += ", %ff" % (self.vectorValue[i][3])
1995                 lLine += "});"
1996                 aWriter.write(2,lLine)
1997                     # TODO : Works only with componentCount = 3
1998         aWriter.write(2,"BL%i.setDuration(%i);" % (self.id, self.duration))
1999         aWriter.write(2,"BL%i.setRepeatMode(%i);" % (self.id,self.repeatMode))
2000         M3GObject3D.writeJava(self,aWriter,False)
2001             
2002 # ---- Translator -------------------------------------------------------------- #
2003
2004 class M3GTranslator:
2005     "Trys to translate a blender scene into a mg3 World"
2006    
2007     def __init__(self):
2008         self.world = None
2009         self.scene = None
2010         self.nodes = []
2011     
2012     def start(self):
2013         print "Translate started ..."
2014         
2015         self.scene = Blender.Scene.GetCurrent()
2016         self.world = self.translateWorld(self.scene)
2017         
2018         for obj in self.scene.objects :
2019             if obj.getType()=='Camera': #  older Version: isinstance(obj.getData(),Types.CameraType)
2020                 self.translateCamera(obj)
2021             elif obj.getType()=='Mesh':
2022                 self.translateMesh(obj)
2023             elif obj.getType()=='Lamp' and mOptions.lightingEnabled:  # older Version: isinstance(obj.getData(),Types.LampType)
2024                 self.translateLamp(obj)
2025             elif obj.getType()=='Empty':
2026                 self.translateEmpty(obj)
2027             else:
2028                 print "Warning: could not translate" + str(obj) + ". Try to convert object to mesh using Alt-C"
2029                 
2030         self.translateParenting()
2031             
2032         print "Translate finished."
2033         return self.world
2034         
2035     def translateParenting(self):
2036         for iNode in self.nodes:
2037             if iNode.parentBlenderObj == None:
2038                 self.world.children.append(iNode)
2039             else:
2040                 for jNode in self.nodes:
2041                     if iNode.parentBlenderObj == jNode.blenderObj:
2042                         #TODO : Every object can be parent
2043                         jNode.children.append(iNode)
2044                         #lMatrix = Matrix(iNode.blenderMatrixWorld) * Matrix(jNode.blenderMatrixWorld).invert()
2045                         lMatrix = self.calculateChildMatrix(iNode.blenderMatrixWorld,jNode.blenderMatrixWorld)
2046                         iNode.transform = self.translateMatrix(lMatrix)
2047                         iNode.hasGeneralTransform=True
2048                         break
2049                     
2050     def calculateChildMatrix(self,child,parent):
2051         return Matrix(child) * Matrix(parent).invert()
2052     
2053     def translateArmature(self,obj,meshObj,aSkinnedMesh):
2054         print "translate Armature ..."
2055         #print "translateArmature::aSkinnedMesh.vertexBuffer:",aSkinnedMesh.vertexBuffer
2056         armature = obj.getData()
2057         
2058         #Pose
2059         #pose = obj.getPose()
2060         #print "pose ",pose
2061         #for bone in pose.bones.values():
2062         #    print "bone local",bone.localMatrix
2063         #    print "bone pose",bone.poseMatrix
2064         
2065         #Skeleton
2066         mGroup = M3GGroup()
2067         self.translateCore(obj,mGroup)
2068         aSkinnedMesh.skeleton = mGroup
2069         mGroup.transform = self.translateMatrix(
2070                                self.calculateChildMatrix(obj.matrixWorld,
2071                                                          meshObj.matrixWorld))
2072         
2073         #Bones
2074         #print "armature:",armature.bones
2075         for bone in armature.bones.values(): #Copy Bones
2076             mBone = M3GBone()
2077             mBone.transformNode = M3GGroup()
2078             self.translateCore(bone, mBone.transformNode)
2079             #mBone.transformNode.transform = self.translateMatrix(pose.bones[bone.name].poseMatrix)#Test!!!!
2080             #print "node transform", mBone.transformNode.transform
2081             #mBone.transformNode.transform=self.translateMatrix(self.calculateChildMatrix(bone.matrix['ARMATURESPACE'],meshObj.matrixWorld))
2082             if bone.hasParent():
2083                 mBone.transformNode.transform = self.translateMatrix(
2084                                                     self.calculateChildMatrix(bone.matrix['ARMATURESPACE'],
2085                                                                               bone.parent.matrix['ARMATURESPACE']))
2086             mBone.weight = bone.weight
2087             aSkinnedMesh.bones[bone.name]=mBone
2088             
2089         rootBone = [] #Copy Child-Parent-Structure
2090         for bone in armature.bones.values():
2091             mBone = aSkinnedMesh.bones[bone.name]
2092             if not bone.hasParent(): 
2093                 rootBone.append(mBone)
2094             if bone.hasChildren():
2095                 for childBone in bone.children:
2096                     mChildBone = aSkinnedMesh.bones[childBone.name]
2097                     mBone.transformNode.children.append(mChildBone.transformNode)
2098         for rbone in rootBone:
2099             aSkinnedMesh.skeleton.children.append(rbone.transformNode)
2100         
2101         #VertexGroups - Skinning
2102         if armature.vertexGroups:
2103             for boneName in aSkinnedMesh.bones.keys():
2104                 aSkinnedMesh.bones[boneName].setVerts(self.translateVertsGroup(meshObj.getData(False,True).getVertsFromGroup(boneName),
2105                                                                               aSkinnedMesh))
2106         #Envelope - Skinning
2107         if armature.envelopes:
2108             pass #TODO
2109         
2110         #Action
2111         self.translateAction(obj,aSkinnedMesh)
2112         aSkinnedMesh.addSecondBone()    
2113         
2114         
2115     def translateVertsGroup(self,group,aSkinnedMesh):
2116         #print "group: ",group
2117         #print "items: ",aSkinnedMesh.getBlenderIndexes().items()
2118         ergebnis = [int(k) for k,v in aSkinnedMesh.getBlenderIndexes().items() if v in group]
2119         #print "ergebnis: ",ergebnis
2120         return ergebnis
2121     
2122     def translateAction(self,armatureObj,aSkinnedMesh):
2123         action = armatureObj.getAction()
2124         if action==None: return
2125         
2126         print "tranlating Action ..."
2127         if mOptions.exportAllActions:
2128             lArmatureID = self.translateUserID(armatureObj.getData().name)
2129             print "armatureID ", lArmatureID, armatureObj
2130             for a in Blender.Armature.NLA.GetActions().values():
2131                 (lArmatureActionID,lEndFrame,lActionID) = self.translateActionName(a.name)
2132                 #print "action", a
2133                 #print "lArmatureID", lArmatureActionID
2134                 #print "lEndFrame", lEndFrame
2135                 #print "lActionID", lActionID
2136                 if lArmatureActionID == lArmatureID:
2137                     #print "Action found"
2138                     mController = self.translateActionIPOs(a,aSkinnedMesh,lEndFrame)
2139                     mController.userID = lActionID
2140                     #print "mController.userID ",mController.userID
2141                     
2142         #print "getActionScripts() ", Blender.Armature.NLA.getActionStrips()  
2143         else:
2144             self.translateActionIPOs(action,aSkinnedMesh)
2145             
2146         
2147     def translateActionIPOs(self,aAction,aSkinnedMesh,aEndFrame=0):
2148         ipos = aAction.getAllChannelIpos()
2149         mController=None
2150         for boneName in aSkinnedMesh.bones.keys():
2151             if ipos.has_key(boneName):
2152                 ipo = ipos[boneName]
2153                 if mController==None: mController = M3GAnimationController()
2154                 self.translateIpo(ipo,aSkinnedMesh.bones[boneName].transformNode,mController,aEndFrame)
2155         return mController
2156     
2157     def translateActionName(self,name):
2158         # <Action Name>#A<M3G ID of Armature>E<End Frame>#<ID of Action>
2159         lError = "Armature name " + name + " is not ok. Perhaps you should set option 'ExportAllAction' to false."
2160         #print "name ", name
2161         lLetter = name.find("#")
2162         if lLetter == -1 :raise Exception(lError)
2163         if name[lLetter+1]!='A': raise Exception(lError)
2164         lName = name[lLetter+2:]
2165         #print "lName ", lName
2166         lLetter = lName.find("E")
2167         #print "lLetter ", lLetter
2168         if lLetter == -1 :raise Exception(lError)
2169         #print "lName[:]", lName[:0]
2170         lArmatureID = int(lName[:lLetter])
2171         lName = lName[lLetter+1:]
2172         lLetter = lName.find("#")
2173         if lLetter == -1:raise Exception(lError)
2174         lEndFrame = int(lName[:lLetter])
2175         lActionID = int(lName[lLetter+1:])
2176         return (lArmatureID,lEndFrame,lActionID)
2177
2178     
2179     def translateWorld(self,scene):
2180         "creates world object"
2181         world = M3GWorld()
2182
2183         #Background
2184         world.background = M3GBackground()
2185         blWorld= scene.world
2186         #AllWorlds = Blender.World.Get()  # Set Color
2187         #if len(AllWorlds)>=1:            # world object available
2188         if blWorld != None:
2189             world.background.backgroundColor=self.translateRGBA(blWorld.getHor(),0)  # horizon color of the first world
2190             if mOptions.createAmbientLight & mOptions.lightingEnabled:
2191                 lLight = M3GLight()
2192                 lLight.mode = lLight.modes['AMBIENT']
2193                 lLight.color = self.translateRGB(blWorld.getAmb())
2194                 self.nodes.append(lLight)
2195
2196         #TODO: Set background picture from world
2197             
2198         return world
2199     
2200     def translateEmpty(self,obj):
2201         print "translate empty ..."
2202         mGroup = M3GGroup()
2203         self.translateToNode(obj,mGroup)
2204             
2205     def translateCamera(self,obj):
2206         print "translate camera ..."
2207         camera = obj.getData()
2208         if camera.getType()!=0:
2209             print "Only perscpectiv cameras will work korrekt"
2210             return #Type=0 'perspectiv' Camera will be translated
2211         mCamera = M3GCamera()
2212         mCamera.projectionType=mCamera.PERSPECTIVE
2213         mCamera.fovy=60.0 # TODO: Calculate fovy from Blender.lens 
2214         mCamera.AspectRatio=4.0/3.0 # TODO: different in every device 
2215         mCamera.near=camera.getClipStart()
2216         mCamera.far=camera.getClipEnd()
2217         self.translateToNode(obj,mCamera)
2218         self.world.activeCamera = mCamera # Last one is always the active one
2219     
2220     
2221     def translateMaterials(self, aMaterial, aMesh, aMatIndex, createNormals, createUvs):
2222         print "translate materials ..."
2223         
2224         mAppearance = M3GAppearance()
2225         
2226         if createNormals:
2227             mMaterial = M3GMaterial()
2228             mMaterial.name = aMaterial.name
2229             mMaterial.diffuseColor = self.translateRGBA(aMaterial.rgbCol,1.0) #ColorRGBA 
2230             #material.specularColor= self.translateRGB(mat.specCol) #ColorRGB
2231             mAppearance.material = mMaterial
2232
2233         if createUvs:
2234             # Search file name in mesh face.
2235             lImage = None
2236             for iface in aMesh.faces:
2237                 if iface.mat==aMatIndex:
2238                     if iface.image != None:
2239                         lImage = iface.image
2240                         break
2241             if lImage==None:
2242                 raise Exception("Mesh " + aMesh.name + ": No image found for uv-texture! Perhaps no uv-coordinates ?")
2243
2244             # M3G requires textures to have power-of-two dimensions.
2245             [width, height] = lImage.getSize()
2246             powerWidth = 1
2247             while (powerWidth < width):
2248                 powerWidth *= 2
2249             powerHeight = 1
2250             while (powerHeight < height):
2251                 powerHeight *= 2
2252             if powerWidth != width or powerHeight != height:
2253                 raise Exception("Image " + lImage.filename + ": width and height must be power-of-two!")
2254                 
2255             # ImageFactory reuses existing images.
2256             mImage = ImageFactory.getImage(lImage, mOptions.textureExternal)
2257             mTexture = M3GTexture2D(mImage)
2258             mAppearance.textures.append(mTexture)
2259
2260         mPolygonMode=M3GPolygonMode()
2261         mPolygonMode.perspectiveCorrectionEnabled = mOptions.perspectiveCorrection
2262         if not aMesh.mode & Modes.TWOSIDED:
2263             mPolygonMode.culling=M3GPolygonMode.CULL_BACK
2264         else:
2265             mPolygonMode.culling=M3GPolygonMode.CULL_NONE 
2266         if mOptions.smoothShading:
2267             mPolygonMode.shading=M3GPolygonMode.SHADE_SMOOTH
2268         else:
2269             mPolygonMode.shading=M3GPolygonMode.SHADE_FLAT
2270         
2271         mAppearance.polygonMode = mPolygonMode
2272         
2273         return mAppearance
2274
2275
2276     def translateMesh(self,obj):
2277         print "translate mesh ..." + str(obj)
2278
2279         # Mesh data.
2280         mesh = obj.getData(False, True)          # get Mesh not NMesh object
2281         if len(mesh.faces) <= 0:                 # no need to process empty meshes
2282             print "Empty mesh " + str(obj) + " not processed."
2283             return
2284             
2285         vertexBuffer = M3GVertexBuffer()
2286         positions = M3GVertexArray(3, 2)         # 3 coordinates - 2 bytes
2287         if mOptions.autoscaling: positions.useMaxPrecision(mesh.verts)
2288         indexBuffers = []
2289         appearances = []
2290         print str(len(mesh.materials)) + " material(s) found."
2291         
2292         # Texture coordinates.
2293         createUvs = False
2294         if mOptions.textureEnabled & mesh.faceUV:
2295             for material in mesh.materials:
2296                 if material.getMode() & Material.Modes.TEXFACE: createUvs = True;
2297             
2298         if createUvs:
2299             if mOptions.autoscaling:
2300                 uvCoordinates = M3GVertexArray(2,2,True,True) #2 coordinates - 2 bytes - autoscaling
2301             else:
2302                 uvCoordinates = M3GVertexArray(2, 2) #2 coordinates - 2 bytes
2303                 uvCoordinates.bias[0] = 0.5
2304                 uvCoordinates.bias[1] = 0.5
2305                 uvCoordinates.bias[2] = 0.5
2306                 uvCoordinates.scale = 1.0/65535.0
2307         else:
2308             uvCoordinates = None
2309
2310         # Normals.            
2311         createNormals = False    
2312         if mOptions.lightingEnabled:
2313             for material in mesh.materials:
2314                 if not (material.getMode() & Material.Modes.SHADELESS): createNormals = True;
2315
2316         if createNormals:
2317             normals = M3GVertexArray(3, 1)       # 3 coordinates - 1 byte
2318         else:
2319             normals = None
2320         
2321         # Create a submesh for each material. 
2322         for materialIndex, material in enumerate(mesh.materials):
2323             faces = [face for face in mesh.faces if face.mat == materialIndex]
2324             if len(faces) >= 0:
2325                 indexBuffers.append(self.translateFaces(faces, positions, normals, uvCoordinates, createNormals, createUvs))
2326                 appearances.append(self.translateMaterials(material, mesh, materialIndex, createNormals, createUvs))
2327                 
2328         # If the above didn't result in any IndexBuffer (e.g. there's no material), write a single IndexBuffer 
2329         # with all faces and a default Appearance.
2330         if len(indexBuffers) == 0: 
2331             indexBuffers.append(self.translateFaces(mesh.faces, positions, normals, uvCoordinates, createNormals, createUvs))
2332             appearances.append(M3GAppearance())
2333
2334         vertexBuffer.setPositions(positions)
2335         if createNormals: vertexBuffer.normals = normals
2336         if createUvs: vertexBuffer.texCoordArrays.append(uvCoordinates)
2337
2338         parent = obj.getParent()
2339         if  parent!=None and parent.getType()=='Armature': #Armatures ?
2340             mMesh = M3GSkinnedMesh(vertexBuffer,indexBuffers,appearances)
2341             #print"vertexBuffer.positions:",vertexBuffer.positions
2342             print"mMesh.vertexBuffer:",mMesh.vertexBuffer
2343             self.translateArmature(parent,obj,mMesh)
2344         else:
2345             mMesh = M3GMesh(vertexBuffer,indexBuffers,appearances)
2346             
2347         self.translateToNode(obj,mMesh)
2348         
2349         #Do Animation
2350         self.translateObjectIpo(obj,mMesh)  
2351         
2352     def translateFaces(self, faces, positions, normals, uvCoordinates, createNormals, createUvs):
2353         """Translates a list of faces into vertex data and triangle strips."""
2354         
2355         # Create vertices and triangle strips.
2356         indices = [0, 0, 0, 0]
2357         triangleStrips = M3GTriangleStripArray()
2358         
2359         for face in faces:
2360             for vertexIndex, vertex in enumerate(face.verts):
2361                 # Find candidates for sharing (vertices with same Blender ID).
2362                 vertexCandidateIds = [int(k) for k, v in positions.blenderIndexes.items() if v == vertex.index]
2363
2364                 # Check normal.
2365                 if createNormals and not face.smooth:
2366                     # For solid faces, a vertex can only be shared if the the face normal is 
2367                     # the same as the normal of the shared vertex.
2368                     for candidateId in vertexCandidateIds[:]:
2369                         for j in range(3):
2370                             if face.no[j]*127 != normals.components[candidateId*3 + j]:
2371                                 vertexCandidateIds.remove(candidateId)
2372                                 break
2373
2374                 # Check texture coordinates.
2375                 if createUvs:
2376                     # If texture coordinates are required, a vertex can only be shared if the 
2377                     # texture coordinates match.
2378                     for candidateId in vertexCandidateIds[:]:
2379                         s = int((face.uv[vertexIndex][0]-0.5)*65535)
2380                         t = int((0.5-face.uv[vertexIndex][1])*65535)
2381                         if (s != uvCoordinates.components[candidateId*2 + 0]) or (t != uvCoordinates.components[candidateId*2 + 1]):
2382                             vertexCandidateIds.remove(candidateId)
2383
2384                 if len(vertexCandidateIds) > 0:
2385                     # Share the vertex.
2386                     indices[vertexIndex] = vertexCandidateIds[0]
2387                 else:
2388                     # Create new vertex.
2389                     positions.append(vertex, vertex.index)
2390                     indices[vertexIndex] = len(positions.components)/3 - 1
2391
2392                     # Normal.
2393                     if createNormals:
2394                         for j in range(3):
2395                             if face.smooth:
2396                                 normals.append(int(vertex.no[j]*127))    # vertex normal
2397                             else:
2398                                 normals.append(int(face.no[j]*127))      # face normal
2399
2400                     # Texture coordinates.
2401                     if createUvs:
2402                         lUvCoordinatesFound = True
2403                         print "face.uv[vertexIndex][0]:",face.uv[vertexIndex][0]
2404                         print "face.uv[vertexIndex][1]:",face.uv[vertexIndex][1]
2405                         if mOptions.autoscaling:
2406                             uvCoordinates.append(face.uv[vertexIndex][0])
2407                             uvCoordinates.append(face.uv[vertexIndex][1])
2408                         else:
2409                             uvCoordinates.append(int((face.uv[vertexIndex][0]-0.5)*65535))
2410                             # Reverse t coordinate because M3G uses a different 2D coordinate system than Blender.
2411                             uvCoordinates.append(int((0.5-face.uv[vertexIndex][1])*65535))
2412
2413             # IndexBuffer.
2414             triangleStrips.stripLengths.append(len(face.verts)) 
2415             if len(face.verts) > 3 :
2416                 triangleStrips.indices += [indices[1], indices[2], indices[0], indices[3]]     # quad
2417             else :
2418                 triangleStrips.indices += [indices[0], indices[1], indices[2]]                 # tri
2419                 
2420         return triangleStrips
2421         
2422         
2423     def translateObjectIpo(self,obj,aM3GObject):
2424         if obj.getIpo() == None : return #No Ipo available
2425         print "translate Ipo ..."
2426         
2427         lIpo = obj.getIpo()
2428         self.translateIpo(lIpo,aM3GObject)
2429         
2430         
2431     def translateIpo(self,aIpo,aM3GObject,aM3GAnimContr=None,aEndFrame=0):    
2432         #Print info about curves   
2433         #for iCurve in lIpo.getCurves():
2434         #    print "Extrapolation",iCurve.getExtrapolation() #Constant, Extrapolation, Cyclic or Cyclic_extrapolation
2435         #    print "Interpolation",iCurve.getInterpolation() #Constant, Bezier, or Linear
2436         #    print "Name",iCurve.getName()
2437         #    for iPoint in iCurve.getPoints():
2438         #        print "Knode points",iPoint.getPoints()
2439         types = ['Loc','Rot','Size','Quat']
2440         
2441         for type in types:
2442             if aIpo.getCurve(type+'X'):
2443                 self.translateIpoCurve(aIpo,aM3GObject,type,aM3GAnimContr,aEndFrame)
2444
2445
2446     def translateIpoCurve(self,aIpo,aM3GObject,aCurveType,aM3GAnimContr,aEndFrame=0):
2447         
2448         lContext = self.scene.getRenderingContext()
2449         if aEndFrame==0: 
2450             lEndFrame = lContext.endFrame()
2451         else:
2452             lEndFrame = aEndFrame
2453             
2454         lTimePerFrame = 1.0 / lContext.framesPerSec() * 1000 
2455         
2456         lCurveX = aIpo.getCurve(aCurveType+'X')
2457         lCurveY = aIpo.getCurve(aCurveType+'Y')
2458         lCurveZ = aIpo.getCurve(aCurveType+'Z')
2459         if aCurveType=='Quat': lCurveW = aIpo.getCurve(aCurveType+'W')
2460         
2461         lInterpolation = None
2462         if aCurveType == 'Rot' or aCurveType == 'Quat':
2463             lTrackType = M3GAnimationTrack.ORIENTATION
2464             lNumComponents=4
2465             lCurveFactor= 10 #45 Degrees = 4,5
2466         if aCurveType == 'Quat':
2467             lTrackType = M3GAnimationTrack.ORIENTATION
2468             lNumComponents=4
2469             lCurveFactor= 1
2470             lInterpolation = M3GKeyframeSequence.SLERP
2471             #lInterpolation = M3GKeyframeSequence.SQUAD
2472         elif aCurveType == 'Size':
2473             lTrackType = M3GAnimationTrack.SCALE
2474             lNumComponents=3
2475             lCurveFactor=1
2476         else:
2477             lTrackType = M3GAnimationTrack.TRANSLATION
2478             lNumComponents=3
2479             lCurveFactor=1
2480             
2481         mSequence = M3GKeyframeSequence(len(lCurveX.getPoints()),
2482                                         lNumComponents,
2483                                         lCurveX.getInterpolation(),
2484                                         lInterpolation)
2485
2486         #print 'ComponentCount',mSequence.componentCount
2487         
2488         mSequence.duration = lEndFrame * lTimePerFrame
2489         mSequence.setRepeatMode(lCurveX.getExtrapolation())
2490         
2491         lIndex = 0
2492         for iPoint in lCurveX.getPoints():
2493             lPoint = iPoint.getPoints()
2494              
2495             lPointList = [(lPoint[1]*lCurveFactor),
2496                           (lCurveY.evaluate(lPoint[0])*lCurveFactor),
2497                           (lCurveZ.evaluate(lPoint[0])*lCurveFactor)]
2498                         
2499             #print "aCurveType ", aCurveType
2500             
2501             if aCurveType == 'Loc':
2502                 #print "PointList ", lPointList
2503                 #lorgTransVector = aM3GObject.blenderTransformMatrix.translationPart()
2504                 #ltrans = TranslationMatrix(Vector(lPointList))
2505                 #ltrans2 = self.calculateChildMatrix(ltrans,aM3GObject.blenderTransformMatrix)
2506                 #lVector = ltrans2.translationPart() + lorgTransVector
2507                 #lPointList = [lVector.x, lVector.y,lVector.z] 
2508                 #print "PointList ", lPointList
2509                 pass
2510                            
2511             if aCurveType == 'Quat':
2512                 lPointList.append(lCurveW.evaluate(lPoint[0])*lCurveFactor)
2513                 #lQuat = Quaternion([lPointList[3],lPointList[0],lPointList[1],lPointList[2]])
2514                 #print "Quat ", lQuat
2515                 #print "Quat.angel ", lQuat.angle
2516                 #print "Quat.axis ", lQuat.axis
2517                 #print "PointList ", lPointList
2518                 
2519             #print "PointList",lPointList
2520                                 
2521             if aCurveType =='Rot':
2522                 lQuat = Euler(lPointList).toQuat()
2523                 #lPointList = [lQuat.w,lQuat.x,lQuat.y,lQuat.z]
2524                 lPointList = [lQuat.x,lQuat.y,lQuat.z,lQuat.w]
2525                 #print " Quat=", lPointList
2526         
2527             mSequence.setKeyframe(lIndex,
2528                                   lPoint[0]*lTimePerFrame, 
2529                                   lPointList)
2530             lIndex += 1
2531         mSequence.validRangeFirst = 0 
2532         mSequence.validRangeLast = lIndex - 1  
2533         
2534         mTrack = M3GAnimationTrack(mSequence,lTrackType)
2535         aM3GObject.animationTracks.append(mTrack)
2536         if aM3GAnimContr==None:  aM3GAnimContr = M3GAnimationController()
2537         mTrack.animationController = aM3GAnimContr
2538         
2539         
2540     def translateLamp(self,obj):
2541         print "translate lamp ..."
2542         lamp = obj.getData()
2543         
2544         #Type
2545         lampType=lamp.getType()
2546         if not lampType in [Lamp.Types.Lamp,Lamp.Types.Spot,Lamp.Types.Sun]:
2547             print "INFO: Type of light is not supported. See documentation"
2548             return #create not light; type not supported
2549         mLight = M3GLight()
2550         if lampType == Lamp.Types.Lamp:
2551             mLight.mode = mLight.modes['OMNI']
2552         elif lampType == Lamp.Types.Spot:
2553             mLight.mode = mLight.modes['SPOT']
2554         elif lampType == Lamp.Types.Sun:
2555             mLight.mode = mLight.modes['DIRECTIONAL']
2556         #Attenuation (OMNI,SPOT):
2557         if lampType in [Lamp.Types.Lamp,Lamp.Types.Spot]:
2558             mLight.attenuationConstant = 0.0
2559             mLight.attenuationLinear = 2.0/lamp.dist 
2560             mLight.attenuationQuadratic = 0.0 
2561         #Color
2562         mLight.color = self.translateRGB(lamp.col)        
2563         #Energy  
2564         mLight.intensity = lamp.energy
2565         #SpotAngle, SpotExponent (SPOT)
2566         if lampType == Lamp.Types.Spot:
2567             mLight.spotAngle = lamp.spotSize/2 
2568             mLight.spotExponent = lamp.spotBlend 
2569         self.translateToNode(obj,mLight)
2570
2571
2572     def translateCore(self,obj,node):
2573         #Name
2574         node.name = obj.name
2575         node.userID = self.translateUserID(obj.name)
2576         #Location
2577         #node.translation=self.translateLoc(obj.LocX,obj.LocY,obj.LocZ
2578         #node.hasComponentTransform=True
2579         #Transform
2580         #node.transform = self.translateMatrix(obj.getMatrix('localspace'))
2581         if type(obj) is Types.BoneType:
2582             #print "BoneMatrix ",obj.matrix['BONESPACE']
2583             node.transform = self.translateMatrix(obj.matrix['ARMATURESPACE'])
2584             #'ARMATURESPACE' - this matrix of the bone in relation to the armature 
2585             #'BONESPACE' - the matrix of the bone in relation to itself
2586         else:
2587             node.transform = self.translateMatrix(obj.matrixWorld)
2588         node.hasGeneralTransform=True
2589         
2590         
2591     def translateToNode(self,obj,node):
2592         self.translateCore(obj,node)
2593         #Nodes
2594         self.nodes.append(node)
2595         #Link to Blender Object
2596         node.blenderObj = obj
2597         node.blenderMatrixWorld = obj.matrixWorld
2598         lparent = None
2599         if obj.getParent()!=None:
2600             if obj.getParent().getType()!='Armature':
2601                 lparent = obj.getParent()
2602             else:
2603                 if obj.getParent().getParent()!=None and obj.getParent().getParent().getType()!='Armature':
2604                     lparent = obj.getParent().getParent()
2605         node.parentBlenderObj = lparent
2606         
2607         
2608     def translateUserID(self, name):
2609         id = 0
2610         start = name.find('#')
2611         
2612         # Use digits that follow the # sign for id.
2613         if start != -1:
2614             start += 1
2615             end = start
2616             for char in name[start:]:
2617                 if char.isdigit():
2618                     end += 1
2619                 else:
2620                     break
2621                     
2622             if end > start:
2623                 id = int(name[start:end])
2624         
2625         return id        
2626         
2627     def translateLoc(self,aLocX,aLocY,aLocZ):
2628         return M3GVector3D(aLocX,aLocY,aLocZ)
2629         
2630     def translateRGB(self,color):
2631         return M3GColorRGB(int(color[0]*255),
2632                             int(color[1]*255), 
2633                             int(color[2]*255))
2634     
2635     def translateRGBA(self,color,alpha):
2636         return M3GColorRGBA(int(color[0]*255),
2637                             int(color[1]*255), 
2638                             int(color[2]*255),
2639                             int(alpha*255))
2640     
2641     def translateMatrix(self,aPyMatrix):
2642         """
2643          0   1   2   3 
2644          4   5   6   7 
2645          8   9  10  11
2646          12  13  14  15 """
2647         #print "Matrix:", aPyMatrix
2648         lMatrix = M3GMatrix()
2649         lMatrix.elements[0] = aPyMatrix[0][0]
2650         lMatrix.elements[1] = aPyMatrix[1][0]
2651         lMatrix.elements[2] = aPyMatrix[2][0]
2652         lMatrix.elements[3] = aPyMatrix[3][0]
2653         lMatrix.elements[4] = aPyMatrix[0][1]
2654         lMatrix.elements[5] = aPyMatrix[1][1]
2655         lMatrix.elements[6] = aPyMatrix[2][1]
2656         lMatrix.elements[7] = aPyMatrix[3][1]
2657         lMatrix.elements[8] = aPyMatrix[0][2]
2658         lMatrix.elements[9] = aPyMatrix[1][2]
2659         lMatrix.elements[10] = aPyMatrix[2][2]
2660         lMatrix.elements[11] = aPyMatrix[3][2]
2661         lMatrix.elements[12] = aPyMatrix[0][3]
2662         lMatrix.elements[13] = aPyMatrix[1][3]
2663         lMatrix.elements[14] = aPyMatrix[2][3]
2664         lMatrix.elements[15] = aPyMatrix[3][3]
2665         return lMatrix
2666         
2667     
2668 # ---- Exporter ---------------------------------------------------------------- #
2669
2670 class M3GExporter:
2671     "Exports Blender-Scene to M3D"
2672     def __init__(self, aWriter): 
2673         self.writer = aWriter
2674
2675         
2676     def start(self):
2677         print "Info: starting export ..."
2678         #rpdb2.start_embedded_debugger("t",True)
2679         Translator = M3GTranslator()
2680         world = Translator.start()
2681         
2682         #sys.settrace(tracer)
2683         exportList = self.createDeepSearchList(world)
2684         externalReferences = [element for element in exportList if element.__class__ is M3GExternalReference]
2685         exportList = [element for element in exportList if element.__class__ is  not M3GExternalReference]
2686         #sys.settrace(None)
2687         
2688         # 1 is reservated for HeaderObject.
2689         i=1 
2690         
2691         # Next are the external references.
2692         for element in externalReferences:
2693             i += 1
2694             element.id = i
2695             print "object ",element.id, element
2696             
2697         # And the standard scene objects.
2698         for element in exportList:
2699             i += 1
2700             element.id = i
2701             print "object ",element.id, element
2702             
2703         self.writer.writeFile(world, exportList, externalReferences)
2704         
2705         print("Ready!")
2706
2707
2708     def createDeepSearchList(self,aWorld):
2709         "creates the right order for saving m3g : leafs first"
2710         return aWorld.searchDeep([])
2711          
2712    
2713        
2714 # ---- Writer ---------------------------------------------------------------- #   
2715 class JavaWriter:
2716     "writes a java class which creates m3g-Scene in a j2me programm"
2717     def __init__(self,aFilename):
2718         self.filename = aFilename
2719         self.classname = Blender.sys.basename(aFilename)
2720         self.classname = self.classname[:-5] #without extention ".java"
2721         self.outFile = file(aFilename,"w")
2722         
2723     def write(self, tab, zeile=""):
2724         "writes to file"
2725         #print "\t" * tab + zeile
2726         print >>self.outFile, "\t" * tab + zeile
2727
2728     def writeFile(self,aWorld,aExportList,externalReferences):
2729         self.world = aWorld
2730         self.writeHeader()
2731         for element in aExportList:
2732             element.writeJava(self,True)
2733         self.writeFooter()
2734         self.outFile.close()
2735         
2736     def writeHeader(self):
2737         "writes class header"
2738         self.write(0,"import javax.microedition.lcdui.Image;")
2739         self.write(0,"import javax.microedition.m3g.*;")
2740         self.write(0,"public final class "+self.classname+" {")
2741         self.write(1,"public static World getRoot(Canvas3D aCanvas) {")
2742           
2743     def writeFooter(self):
2744         self.write(1)
2745         self.write(1,"return BL"+str(self.world.id)+";")
2746         self.write(0,"}}")
2747         
2748     def writeList(self,alist,numberOfElementsPerLine=12,aType=""):
2749         '''Writes numberOfElementsPerLine'''
2750         line=""
2751         lastLine=""
2752         counter=0
2753         for element in alist:
2754             if counter!=0:
2755                 line = line + "," + str(element) + aType
2756             else:
2757                 line = str(element) + aType
2758             counter = counter + 1
2759             if counter == numberOfElementsPerLine:
2760                 if len(lastLine)>0:
2761                     self.write(3,lastLine+",")
2762                 lastLine=line
2763                 line=""
2764                 counter = 0
2765         if len(lastLine)>0:
2766             if len(line)>0:
2767                 self.write(3,lastLine+",")
2768             else:
2769                 self.write(3,lastLine)
2770         if len(line) > 0: self.write(3,line)
2771     
2772     def writeClass(self,aName,aM3GObject):
2773         self.write(2)
2774         self.write(2,"//"+aName+":"+aM3GObject.name)
2775       
2776
2777 class M3GSectionObject:
2778     def __init__(self,aObject):
2779         """Object Structure
2780            Each object in the file represents one object in the 
2781            scene graph tree, and is stored in a chunk. The
2782            structure of an object chunk is as follows:
2783                Byte ObjectType
2784                UInt32 Length
2785                Byte[] Data"""
2786         #ObjectType
2787         #This field describes what type of object has been serialized.
2788         #The values 0 and 0xFF are special: 0 represents the header object, 
2789         #and 0xFF represents an external reference.
2790         #Example: Byte ObjectType = 14
2791         self.ObjectType = aObject.ObjectType
2792         self.data = aObject.getData()
2793         self.length = aObject.getDataLength()
2794     
2795     def getData(self):
2796         data = struct.pack('<BI',self.ObjectType,self.length)
2797         data += self.data
2798         return data
2799     
2800     def getDataLength(self):
2801         return struct.calcsize('<BI') + self.length
2802         
2803 class M3GSection:
2804     '''Part of a M3G binary file'
2805        Section Structur
2806            Byte CompressionScheme
2807            UInt32 TotalSectionLength
2808            UInt32 UncompressedLength
2809            Byte[] Objects
2810            UInt32 Checksum '''
2811     def __init__(self,aObjectList,compressed=False):
2812         self.CompressionScheme=0
2813         self.TotalSectionLength=0 #including the CompressionScheme, 
2814                                   #TotalSectionLength, UncompressedLength,
2815                                   #Objects and Checksum fields,
2816         self.UncompressedLength=0 #Length of Objects uncompresses
2817         self.Objects = ''
2818         for element in aObjectList:
2819             lObject = M3GSectionObject(element)
2820             #print "Obj:", lObject, lObject.getDataLength(), len(lObject.getData())
2821             self.Objects += lObject.getData()
2822             self.UncompressedLength += lObject.getDataLength()
2823         self.TotalSectionLength=struct.calcsize('<BIII')+self.UncompressedLength
2824     
2825     def getData(self):
2826         data = struct.pack('<BII',self.CompressionScheme,
2827                                 self.TotalSectionLength,
2828                                 self.UncompressedLength)
2829         data += self.Objects
2830         #self.Checksum = zlib.adler32(data)
2831         self.Checksum = self.ownAdler32(data)
2832         print "Checksum",self.Checksum
2833         #print "Own Checksum",self.ownAdler32(data)
2834         return data + struct.pack('<I',self.Checksum)
2835     
2836     def ownAdler32(self,data):
2837         s1 = int(1) #uint32_t
2838         s2 = int(0) #uint32_t
2839         for n in data:
2840             s1 = (s1 + int(struct.unpack("B",n)[0])) % 65521
2841             s2 = (s2 + s1) % 65521
2842         return (s2 << 16) + s1
2843     
2844     def getLength(self):
2845         return self.TotalSectionLength
2846         
2847     def write(self,aFile):
2848         print "Write Section.."
2849         print "TotalSectionLength:", str(self.TotalSectionLength)
2850         aFile.write(self.getData())
2851             
2852         #CompressionScheme
2853         #Example Byte CompressionScheme = 1;
2854         #0 Uncompressed, Adler32 Checksum
2855         #1 ZLib compression, 32 k buffer size, Adler32 Checksum
2856         #2...255 Reserved
2857         #TotalSectionLength: total length of the section in bytes
2858         #Example: UInt32 TotalSectionLength = 2056
2859         #UncompressedLength 
2860         #contains the length of the Objects field after decompression. 
2861         #If no compression is specified for this section,
2862         #this equals the actual number of bytes serialized in the Objects array.
2863         #CompressionScheme
2864         #Currently, only the Adler32 checksum is mandatory. 
2865         #The checksum is calculated using all preceding bytes in
2866         #the section, i.e. the CompressionScheme,TotalSectionLength, 
2867         #UncompressedLength, and the actual serialized data in the 
2868         #Objects field (i.e. in its compressed form if compression is speci-
2869         #fied).
2870         #Example: UInt32 Checksum = 0xffe806a3
2871     
2872     
2873     
2874     
2875 class M3GFileIdentifier:
2876     def __init__(self):
2877         '''Byte[12] FileIdentifier = { 0xAB, 0x4A, 0x53, 0x52, 0x31, 0x38, 0x34,
2878                         0xBB, 0x0D, 0x0A, 0x1A, 0x0A }
2879            This can also be expressed using C-style character definitions as:
2880            Byte[12] FileIdentifier = { '«', 'J', 'S', 'R', '1', '8', '4', '»', '\r',
2881                         '\n', '\x1A', '\n' }'''
2882         self.data = [ 0xAB, 0x4A, 0x53, 0x52, 0x31, 0x38, 0x34,
2883                         0xBB, 0x0D, 0x0A, 0x1A, 0x0A ]
2884     
2885     def write(self,aFile):
2886         for element in self.data:
2887             aFile.write(struct.pack('B',element))
2888         
2889     def getLength(self):
2890         return len(self.data)
2891         
2892         
2893 class M3GWriter:
2894     """writes a m3g binary file
2895       File Structur
2896         File Identifier
2897         Section 0 File Header Object
2898         Section 1 External Reference Objects
2899         Section 2 Scene Objects
2900         Section 3 Scene Objects
2901                 ... ...
2902         Section n Scene Objects"""
2903         
2904     def __init__(self,aFilename):
2905         self.FileName = aFilename
2906     
2907     
2908     def writeFile(self,aWorld,aExportList,externalReferences):
2909         '''Called after translation
2910            first atempt: all objects in one section'''
2911         print "M3G file writing .."
2912         
2913         fileIdentifier = M3GFileIdentifier()
2914         
2915         fileHeaderObject = M3GHeaderObject()
2916         section0 = M3GSection([fileHeaderObject])
2917         sectionN = M3GSection(aExportList)
2918         
2919         length = fileIdentifier.getLength()
2920         length += section0.getLength()
2921         length += sectionN.getLength()
2922         
2923         if len(externalReferences) != 0:
2924             section1 = M3GSection(externalReferences)
2925             length += section1.getLength()
2926             fileHeaderObject.hasExternalReferences = True
2927         
2928         fileHeaderObject.TotalFileSize=length 
2929         fileHeaderObject.ApproximateContentSize=length
2930         section0 = M3GSection([fileHeaderObject])
2931        
2932         output = open(self.FileName, mode='wb')
2933
2934         fileIdentifier.write(output)
2935         section0.write(output)
2936         if len(externalReferences) != 0:
2937             section1.write(output)
2938         sectionN.write(output)
2939         
2940         output.close()
2941
2942         print "M3G file written."
2943    
2944  
2945 class OptionMgr:
2946     """Reads and saves options """
2947
2948     def __init__(self):
2949         self.setDefault()
2950         rdict = Registry.GetKey('M3GExport', True) # True to check on disk as well
2951         if rdict: # if found, get the values saved there
2952             try:
2953                 self.textureEnabled        = rdict['textureEnabled']
2954                 self.textureExternal       = rdict['textureExternal']
2955                 self.lightingEnabled       = rdict['lightingEnabled']
2956                 self.createAmbientLight    = rdict['createAmbientLight']
2957                 self.exportAllActions      = rdict['exportAllActions']
2958                 self.autoscaling           = rdict['autoscaling']
2959                 self.perspectiveCorrection = rdict['perspectiveCorrection']
2960                 self.smoothShading         = rdict['smoothShading']
2961                 self.exportAsJava          = rdict['exportAsJava']
2962                 self.exportVersion2        = rdict['exportVersion2']
2963                 self.exportGamePhysics     = rdict['exportGamePhysics']
2964                 
2965             except: self.save()     # if data isn't valid, rewrite it
2966
2967     def setDefault(self):
2968         self.textureEnabled         = True
2969         self.textureExternal        = False
2970         self.lightingEnabled        = True
2971         self.createAmbientLight     = False
2972         self.exportAllActions       = False
2973         self.autoscaling            = True
2974         self.perspectiveCorrection  = False
2975         self.smoothShading          = True
2976         self.exportAsJava           = False
2977         self.exportVersion2         = False
2978         self.exportGamePhysics      = False
2979     
2980     def save(self):
2981         d = {}
2982         d['textureEnabled']        = self.textureEnabled
2983         d['textureExternal']       = self.textureExternal
2984         d['lightingEnabled']       = self.lightingEnabled
2985         d['createAmbientLight']    = self.createAmbientLight
2986         d['exportAllActions']      = self.exportAllActions
2987         d['autoscaling']           = self.autoscaling
2988         d['perspectiveCorrection'] = self.perspectiveCorrection
2989         d['smoothShading']         = self.smoothShading
2990         d['exportAsJava']          = self.exportAsJava
2991         d['exportVersion2']        = self.exportVersion2
2992         d['exportGamePhysics']     = self.exportGamePhysics
2993         
2994         Blender.Registry.SetKey('M3GExport', d, True)
2995
2996
2997 # ---- User Interface -------------------------------------------------------- #
2998 mOptions = OptionMgr()
2999
3000 def gui():              
3001     """Draws the options menu."""
3002     # Flush events.
3003     for s in Window.GetScreenInfo():
3004         Window.QHandle(s['id'])
3005         
3006     # Display options.
3007     textureEnabled         = Draw.Create(mOptions.textureEnabled)
3008     textureExternal        = Draw.Create(mOptions.textureExternal)
3009     lightingEnabled        = Draw.Create(mOptions.lightingEnabled)
3010     createAmbientLight     = Draw.Create(mOptions.createAmbientLight)
3011     exportAllActions       = Draw.Create(mOptions.exportAllActions)
3012     autoscaling            = Draw.Create(mOptions.autoscaling)
3013     perspectiveCorrection  = Draw.Create(mOptions.perspectiveCorrection)
3014     smoothShading          = Draw.Create(mOptions.smoothShading)
3015     exportAsJava           = Draw.Create(mOptions.exportAsJava)
3016     exportVersion2         = Draw.Create(mOptions.exportVersion2)
3017     exportGamePhysics      = Draw.Create(mOptions.exportGamePhysics)
3018     
3019     pupBlock = [\
3020         ('Texturing'),\
3021         ('Enabled',              textureEnabled,         'Enables texture export'),\
3022         ('External',             textureExternal,        'References external files for textures'),\
3023         ('Lighting'),\
3024         ('Enabled',              lightingEnabled,        'Enables light export'),\
3025         ('Ambient Light',        createAmbientLight,     'Inserts an extra light object for ambient light'),\
3026         ('Mesh Options'),\
3027         ('Autoscaling',          autoscaling,            'Uses maximum precision for vertex positions'),\
3028         ('Persp. Correction',    perspectiveCorrection,  'Sets perspective correction flag'),\
3029         ('Smooth Shading',       smoothShading,          'Sets smooth shading flag'),\
3030         ('Posing'),\
3031         ('All Armature Actions', exportAllActions,       'Exports all actions for armatures'),\
3032         ('Export'),\
3033         ('As Java Source',       exportAsJava,           'Exports scene as Java source code'),\
3034         ('M3G Version 2.0',      exportVersion2,         'Exports M3G Version 2.0 File'),\
3035         ('Game Physics',         exportGamePhysics,      'Includes Game Physics infos for NOPE in export')
3036     ]
3037     
3038     # Only execute if use didn't quit (ESC).
3039     if Draw.PupBlock('M3G Export', pupBlock):
3040         mOptions.textureEnabled         = textureEnabled.val
3041         mOptions.textureExternal        = textureExternal.val
3042         mOptions.lightingEnabled        = lightingEnabled.val
3043         mOptions.createAmbientLight     = createAmbientLight.val
3044         mOptions.exportAllActions       = exportAllActions.val
3045         mOptions.autoscaling            = autoscaling.val
3046         mOptions.perspectiveCorrection  = perspectiveCorrection.val
3047         mOptions.smoothShading          = smoothShading.val
3048         mOptions.exportAsJava           = exportAsJava.val
3049         mOptions.exportVersion2         = exportVersion2.val
3050         mOptions.exportGamePhysics      = exportGamePhysics.val
3051         mOptions.save()
3052
3053         if mOptions.exportAsJava:
3054             Window.FileSelector(file_callback_java, 'Export M3G as Java', Blender.sys.makename(ext='.java'))
3055         else:
3056             Window.FileSelector(file_callback_m3g,  'Export M3G Binary',  Blender.sys.makename(ext='.m3g'))
3057         
3058 def file_callback_java(filename):
3059     Window.WaitCursor(1)    # Blender will automatically remove wait cursor in case of an exception
3060     exporter=M3GExporter(JavaWriter(filename))
3061     exporter.start()
3062     Window.WaitCursor(0)
3063     Window.RedrawAll()
3064
3065 def file_callback_m3g(filename):
3066     Window.WaitCursor(1)
3067     exporter=M3GExporter(M3GWriter(filename))
3068     exporter.start()
3069     Window.WaitCursor(0)
3070     Window.RedrawAll()
3071     
3072 if __name__ == '__main__':
3073     gui()
3074