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