Merged 15170:15635 from trunk (no conflicts or even merges)
[blender.git] / release / scripts / vrml97_export.py
1 #!BPY
2 """ Registration info for Blender menus:
3 Name: 'VRML97 (.wrl)...'
4 Blender: 241
5 Group: 'Export'
6 Tooltip: 'Export to VRML97 file (.wrl)'
7 """
8
9 __author__ = ("Rick Kimball", "Ken Miller", "Steve Matthews", "Bart")
10 __url__ = ["blender", "blenderartists.org",
11 "Author's (Rick) homepage, http://kimballsoftware.com/blender",
12 "Author's (Bart) homepage, http://www.neeneenee.de/vrml"]
13 __email__ = ["Bart, bart:neeneenee*de"]
14 __version__ = "2006/01/17"
15 __bpydoc__ = """\
16 This script exports to VRML97 format.
17
18 Usage:
19
20 Run this script from "File->Export" menu.  A pop-up will ask whether you
21 want to export only selected or all relevant objects.
22 """
23
24
25 # $Id$
26 #
27 #------------------------------------------------------------------------
28 # VRML97 exporter for blender 2.36 or above
29 #
30 # ***** BEGIN GPL LICENSE BLOCK *****
31 #
32 # This program is free software; you can redistribute it and/or
33 # modify it under the terms of the GNU General Public License
34 # as published by the Free Software Foundation; either version 2
35 # of the License, or (at your option) any later version.
36 #
37 # This program is distributed in the hope that it will be useful,
38 # but WITHOUT ANY WARRANTY; without even the implied warranty of
39 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
40 # GNU General Public License for more details.
41 #
42 # You should have received a copy of the GNU General Public License
43 # along with this program; if not, write to the Free Software Foundation,
44 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
45 #
46 # ***** END GPL LICENCE BLOCK *****
47 #
48
49 ####################################
50 # Library dependancies
51 ####################################
52
53 import Blender
54 from Blender import Object, Mesh, Lamp, Draw, BGL, \
55          Image, Text, sys, Mathutils, Registry
56 from Blender.Scene import Render
57
58 import math
59
60 ####################################
61 # Global Variables
62 ####################################
63
64 scene = Blender.Scene.getCurrent()
65 world = Blender.World.GetCurrent() 
66 worldmat = Blender.Texture.Get()
67 filename = Blender.Get('filename')
68 _safeOverwrite = True
69 extension = ''
70
71 # Matrices below are used only when export_rotate_z_to_y.val:
72 #
73 # Blender is Z up, VRML is Y up, both are right hand coordinate
74 # systems, so to go from Blender coords to VRML coords we rotate
75 # by 90 degrees around the X axis. In matrix notation, we have a
76 # matrix, and it's inverse, as:
77 M_blen2vrml = Mathutils.Matrix([1,0,0,0], \
78                                                            [0,0,1,0], \
79                                                            [0,-1,0,0], \
80                                                            [0,0,0,1])
81 M_vrml2blen = Mathutils.Matrix([1,0,0,0], \
82                                                            [0,0,-1,0], \
83                                                            [0,1,0,0], \
84                                                            [0,0,0,1])
85
86
87 class DrawTypes:
88         """Object DrawTypes enum values
89     BOUNDS - draw only the bounding box of the object
90     WIRE - draw object as a wire frame
91     SOLID - draw object with flat shading
92     SHADED - draw object with OpenGL shading
93 """
94         BOUNDBOX  = 1
95         WIRE      = 2
96         SOLID     = 3
97         SHADED    = 4
98         TEXTURE   = 5
99
100 if not hasattr(Blender.Object,'DrawTypes'):
101         Blender.Object.DrawTypes = DrawTypes()
102
103 ##########################################################
104 # Functions for writing output file
105 ##########################################################
106
107 class VRML2Export:
108
109         def __init__(self, filename):
110                 #--- public you can change these ---
111                 self.wire = 0
112                 self.proto = 1
113                 self.facecolors = 0
114                 self.vcolors = 0
115                 self.billnode = 0
116                 self.halonode = 0
117                 self.collnode = 0
118                 self.tilenode = 0
119                 self.wire     = 0
120                 self.twosided = 0
121
122                 # level of verbosity in console 0-none, 1-some, 2-most
123                 try:
124                         rt = Blender.Get('rt')
125                         if (rt == 42):
126                                 self.verbose = 1
127                         elif (rt == 43):
128                                 self.verbose = 2
129                         else:
130                                 self.verbose = 0
131                 except:
132                         self.verbose = 0
133                         
134                 # decimals for material color values     0.000 - 1.000
135                 self.cp=7
136                 # decimals for vertex coordinate values  0.000 - n.000
137                 self.vp=7
138                 # decimals for texture coordinate values 0.000 - 1.000
139                 self.tp=7
140                 
141                 #--- class private don't touch ---
142                 self.texNames={}   # dictionary of textureNames
143                 self.matNames={}   # dictionary of materialNames
144                 self.meshNames={}   # dictionary of meshNames
145                 self.coordNames={}   # dictionary of coordNames
146                 self.indentLevel=0 # keeps track of current indenting
147                 self.filename=filename
148                 self.file = open(filename, "w")
149                 self.bNav=0
150                 self.nodeID=0
151                 self.namesReserved=[ "Anchor", "Appearance", "AudioClip",
152                                                          "Background","Billboard", "Box",
153                                                          "Collision", "Color", "ColorInterpolator",
154                                                          "Cone", "Coordinate",
155                                                          "CoordinateInterpolator", "Cylinder",
156                                                          "CylinderSensor",
157                                                          "DirectionalLight",
158                                                          "ElevationGrid", "Extrustion",
159                                                          "Fog", "FontStyle", "Group",
160                                                          "ImageTexture", "IndexedFaceSet",
161                                                          "IndexedLineSet", "Inline",
162                                                          "LOD", "Material", "MovieTexture",
163                                                          "NavigationInfo", "Normal",
164                                                          "NormalInterpolator",
165                                                          "OrientationInterpolator", "PixelTexture",
166                                                          "PlaneSensor", "PointLight", "PointSet",
167                                                          "PositionInterpolator", "ProxmimitySensor",
168                                                          "ScalarInterpolator", "Script", "Shape",
169                                                          "Sound", "Sphere", "SphereSensor",
170                                                          "SpotLight", "Switch", "Text",
171                                                          "TextureCoordinate", "TextureTransform",
172                                                          "TimeSensor", "TouchSensor", "Transform",
173                                                          "Viewpoint", "VisibilitySensor", "WorldInfo" ]
174                 self.namesStandard=[ "Empty", "Empty.000", "Empty.001",
175                                                          "Empty.002", "Empty.003", "Empty.004",
176                                                          "Empty.005", "Empty.006", "Empty.007",
177                                                          "Empty.008", "Empty.009", "Empty.010",
178                                                          "Empty.011", "Empty.012",
179                                                          "Scene.001", "Scene.002", "Scene.003",
180                                                          "Scene.004", "Scene.005", "Scene.06",
181                                                          "Scene.013", "Scene.006", "Scene.007",
182                                                          "Scene.008", "Scene.009", "Scene.010",
183                                                          "Scene.011","Scene.012",
184                                                          "World", "World.000", "World.001",
185                                                          "World.002", "World.003", "World.004",
186                                                          "World.005" ]
187                 self.namesFog=[ "", "LINEAR"," EXPONENTIAL", "" ]
188
189 ##########################################################
190 # Writing nodes routines
191 ##########################################################
192
193         def writeHeader(self):
194                 bfile = sys.expandpath(Blender.Get('filename'))
195                 self.file.write("#VRML V2.0 utf8\n\n")
196                 self.file.write("# This file was authored with Blender " \
197                                                 "(http://www.blender.org/)\n")
198                 self.file.write("# Blender version %s\n" % Blender.Get('version'))
199                 self.file.write("# Blender file %s\n" % sys.basename(bfile))
200                 self.file.write("# Exported using VRML97 exporter " \
201                                                 "v1.55 (2006/01/17)\n\n")
202
203         def writeInline(self):
204                 inlines = Blender.Scene.Get()
205                 allinlines = len(inlines)
206                 if scene != inlines[0]:
207                         return
208                 else:
209                         for i in range(allinlines):
210                                 nameinline=inlines[i].getName()
211                                 if (nameinline not in self.namesStandard) and (i > 0):
212                                         self.writeIndented("DEF %s Inline {\n" % \
213                                                                            (self.cleanStr(nameinline)), 1)
214                                         nameinline = nameinline+".wrl"
215                                         self.writeIndented("url \"%s\" \n" % nameinline)
216                                         self.writeIndented("}\n", -1)
217                                         self.writeIndented("\n")
218
219         def writeScript(self):
220                 textEditor = Blender.Text.Get() 
221                 alltext = len(textEditor)
222                 for i in range(alltext):
223                         nametext = textEditor[i].getName()
224                         nlines = textEditor[i].getNLines()
225                         if (self.proto == 1):
226                                 if (nametext == "proto" or nametext == "proto.js" or \
227                                         nametext == "proto.txt") and (nlines != None):
228                                         nalllines = len(textEditor[i].asLines())
229                                         alllines = textEditor[i].asLines()
230                                         for j in range(nalllines):
231                                                 self.writeIndented(alllines[j] + "\n")
232                         elif (self.proto == 0):
233                                 if (nametext == "route" or nametext == "route.js" or \
234                                         nametext == "route.txt") and (nlines != None):
235                                         nalllines = len(textEditor[i].asLines())
236                                         alllines = textEditor[i].asLines()
237                                         for j in range(nalllines):
238                                                 self.writeIndented(alllines[j] + "\n")
239                 self.writeIndented("\n")
240
241         def writeViewpoint(self, thisObj):
242                 # NOTE: The transform node above this will take care of
243                 # the position and orientation of the camera
244                 context = scene.getRenderingContext()
245                 ratio = float(context.imageSizeY()) / float(context.imageSizeX())
246                 temp  = ratio * 16 / thisObj.data.getLens()
247                 lens = 2 * math.atan(temp)
248                 lens = min(lens, math.pi) 
249
250                 self.writeIndented("DEF %s Viewpoint {\n" % \
251                                                    (self.cleanStr(thisObj.name)), 1)
252                 self.writeIndented('description "%s" \n' % thisObj.name)
253                 self.writeIndented("position 0.0 0.0 0.0\n")
254                 # Need camera to point to -y in local space to accomodate
255                 # the transforma node above
256                 self.writeIndented("orientation 1.0 0.0 0.0 %f\n" % (-math.pi/2.0))
257                 self.writeIndented("fieldOfView %.3f\n" % (lens))
258                 self.writeIndented("}\n", -1)
259                 self.writeIndented("\n")
260
261         def writeFog(self):
262                 if world:
263                         mtype = world.getMistype()
264                         mparam = world.getMist()
265                         grd = world.getHor()
266                         grd0, grd1, grd2 = grd[0], grd[1], grd[2]
267                 else:
268                         return
269                 if (mtype == 1 or mtype == 2):
270                         self.writeIndented("Fog {\n",1)
271                         self.writeIndented('fogType "%s"\n' % self.namesFog[mtype])
272                         self.writeIndented("color %s %s %s\n" % \
273                                                            (round(grd0,self.cp), \
274                                                                 round(grd1,self.cp), \
275                                                                 round(grd2,self.cp)))
276                         self.writeIndented("visibilityRange %s\n" % \
277                                                            round(mparam[2],self.cp))  
278                         self.writeIndented("}\n",-1)
279                         self.writeIndented("\n")   
280                 else:
281                         return
282
283         def writeNavigationInfo(self, scene):
284                 allObj = []
285                 allObj = list(scene.objects)
286                 headlight = "TRUE"
287                 vislimit = 0.0
288                 for thisObj in allObj:
289                         objType=thisObj.type
290                         if objType == "Camera":
291                                 vislimit = thisObj.data.getClipEnd()
292                         elif objType == "Lamp":
293                                 headlight = "FALSE"
294                 self.writeIndented("NavigationInfo {\n",1)
295                 self.writeIndented("headlight %s\n" % headlight)
296                 self.writeIndented("visibilityLimit %s\n" % \
297                                                    (round(vislimit,self.cp)))
298                 self.writeIndented("type [\"EXAMINE\", \"ANY\"]\n")            
299                 self.writeIndented("avatarSize [0.25, 1.75, 0.75]\n")
300                 self.writeIndented("} \n",-1)
301                 self.writeIndented(" \n")
302
303         def writeSpotLight(self, object, lamp):
304                 # Note: location and orientation are handled by the
305                 # transform node above this object
306                 if world:
307                         ambi = world.getAmb()
308                         ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5
309                 else:
310                         ambi = 0
311                         ambientIntensity = 0
312
313                 # compute cutoff and beamwidth
314                 intensity=min(lamp.energy/1.75,1.0)
315                 beamWidth=((lamp.spotSize*math.pi)/180.0)*.37;
316                 cutOffAngle=beamWidth*1.3
317
318                 radius = lamp.dist*math.cos(beamWidth)
319                 self.writeIndented("DEF %s SpotLight {\n" % \
320                                                    self.cleanStr(object.name),1)
321                 self.writeIndented("radius %s\n" % (round(radius,self.cp)))
322                 self.writeIndented("ambientIntensity %s\n" % \
323                                                    (round(ambientIntensity,self.cp)))
324                 self.writeIndented("intensity %s\n" % (round(intensity,self.cp)))
325                 self.writeIndented("color %s %s %s\n" % \
326                                                    (round(lamp.col[0],self.cp), \
327                                                         round(lamp.col[1],self.cp), \
328                                                         round(lamp.col[2],self.cp)))
329                 self.writeIndented("beamWidth %s\n" % (round(beamWidth,self.cp)))
330                 self.writeIndented("cutOffAngle %s\n" % \
331                                                    (round(cutOffAngle,self.cp)))
332                 # Note: point down -Y axis, transform node above will rotate
333                 self.writeIndented("direction 0.0 -1.0 0.0\n")
334                 self.writeIndented("location 0.0 0.0 0.0\n")
335                 self.writeIndented("}\n",-1)
336                 self.writeIndented("\n")
337                 
338         def writeDirectionalLight(self, object, lamp):
339                 # Note: location and orientation are handled by the
340                 # transform node above this object
341                 if world:
342                         ambi = world.getAmb()
343                         ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5
344                 else:
345                         ambi = 0
346                         ambientIntensity = 0
347
348                 intensity=min(lamp.energy/1.75,1.0) 
349                 self.writeIndented("DEF %s DirectionalLight {\n" % \
350                                                    self.cleanStr(object.name),1)
351                 self.writeIndented("ambientIntensity %s\n" % \
352                                                    (round(ambientIntensity,self.cp)))
353                 self.writeIndented("color %s %s %s\n" % \
354                                                    (round(lamp.col[0],self.cp), \
355                                                         round(lamp.col[1],self.cp), \
356                                                         round(lamp.col[2],self.cp)))
357                 self.writeIndented("intensity %s\n" % \
358                                                    (round(intensity,self.cp)))
359                 # Note: point down -Y axis, transform node above will rotate
360                 self.writeIndented("direction 0.0 -1.0 0.0\n")
361                 self.writeIndented("}\n",-1)
362                 self.writeIndented("\n")
363
364         def writePointLight(self, object, lamp):
365                 # Note: location is at origin because parent transform node
366                 # takes care of this
367                 if world:
368                         ambi = world.getAmb()
369                         ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5
370                 else:
371                         ambi = 0
372                         ambientIntensity = 0
373                 om = object.getMatrix()
374                 intensity=min(lamp.energy/1.75,1.0) 
375                 radius = lamp.dist
376                 self.writeIndented("DEF %s PointLight {\n" % \
377                                                    self.cleanStr(object.name),1)
378                 self.writeIndented("ambientIntensity %s\n" % \
379                                                    (round(ambientIntensity,self.cp)))
380                 self.writeIndented("color %s %s %s\n" % \
381                                                    (round(lamp.col[0],self.cp), \
382                                                         round(lamp.col[1],self.cp), \
383                                                         round(lamp.col[2],self.cp)))
384                 self.writeIndented("intensity %s\n" % (round(intensity,self.cp)))
385                 self.writeIndented("location 0.0 0.0 0.0\n")
386                 self.writeIndented("radius %s\n" % radius )
387                 self.writeIndented("}\n",-1)
388                 self.writeIndented("\n")
389
390         def writeNode(self, thisObj):
391                 # Note: location and orientation are handled by the
392                 # transform node above this object
393                 objectname=str(thisObj.getName())
394                 if objectname in self.namesStandard:
395                         return
396                 else:
397                         self.writeIndented("%s {\n" % objectname,1)
398                         # May need to check that the direction is done right
399                         self.writeIndented("direction 0.0 -1.0 0.0\n")
400                         self.writeIndented("location 0.0 0.0 0.0\n")
401                         self.writeIndented("}\n",-1)
402                         self.writeIndented("\n")
403
404         def secureName(self, name):
405                 name = name + str(self.nodeID)
406                 self.nodeID += 1
407                 if len(name) <= 3:
408                         newname = "_" + str(self.nodeID)
409                         return "%s" % (newname)
410                 else:
411                         for bad in ['"','#',"'",',','.','[','\\',']','{','}']:
412                                 name=name.replace(bad,'_')
413                         if name in self.namesReserved:
414                                 newname = name[0:3] + "_" + str(self.nodeID)
415                                 return "%s" % (newname)
416                         elif name[0].isdigit():
417                                 newname = "_" + name + str(self.nodeID)
418                                 return "%s" % (newname)
419                         else:
420                                 newname = name
421                                 return "%s" % (newname)
422
423         def classifyMesh(self, me, ob):
424                 self.halonode = 0
425                 self.billnode = 0
426                 self.facecolors = 0
427                 self.vcolors = 0
428                 self.tilenode = 0
429                 self.colnode = 0
430                 self.wire = 0
431                 if me.faceUV:
432                         for face in me.faces:
433                                 if (face.mode & Mesh.FaceModes['HALO']):
434                                         self.halonode = 1
435                                 if (face.mode & Mesh.FaceModes['BILLBOARD']):
436                                         self.billnode = 1
437                                 if (face.mode & Mesh.FaceModes['OBCOL']):
438                                         self.facecolors = 1
439                                 if (face.mode & Mesh.FaceModes['SHAREDCOL']):
440                                         self.vcolors = 1
441                                 if (face.mode & Mesh.FaceModes['TILES']):
442                                         self.tilenode = 1
443                                 if not (face.mode & Mesh.FaceModes['DYNAMIC']):
444                                         self.collnode = 1
445                                 if (face.mode & Mesh.FaceModes['TWOSIDE']):
446                                         self.twosided = 1
447
448                 # Bit of a crufty trick, but if mesh has vertex colors
449                 # (as a non-face property) and if first material has
450                 # vcol paint set, we export the vertex colors
451                 if (me.vertexColors):
452                         if len(me.materials) > 0:
453                                 mat = me.materials[0]
454                                 if mat:
455                                         if (mat.mode & Blender.Material.Modes['VCOL_PAINT']):
456                                                 self.vcolors = 1
457                 else:
458                         self.vcolors = 0
459                         
460                 # check if object is wireframe only
461                 if ob.drawType == Blender.Object.DrawTypes.WIRE:
462                         # user selected WIRE=2 on the Drawtype=Wire on (F9) Edit page
463                         self.wire = 1
464
465         ###
466         ### The next few functions nest Collision/Billboard/Halo nodes.
467         ### For real mesh data export, jump down to writeMeshData()
468         ###
469         def writeMesh(self, ob, normals = 0):
470
471                 imageMap={}   # set of used images
472                 sided={}      # 'one':cnt , 'two':cnt
473                 vColors={}    # 'multi':1
474
475                 if (len(ob.modifiers) > 0):
476                         me = Mesh.New()
477                         me.getFromObject(ob.name)
478                         # Careful with the name, the temporary mesh may
479                         # reuse the default name for other meshes. So we
480                         # pick our own name.
481                         me.name = "MOD_%s" % (ob.name)
482                 else:
483                         me = ob.getData(mesh = 1)
484
485                 self.classifyMesh(me, ob)
486
487                 if (self.collnode):
488                         self.writeCollisionMesh(me, ob, normals)
489                         return
490                 else:
491                         self.writeRegularMesh(me, ob, normals)
492                         return
493
494         def writeCollisionMesh(self, me, ob, normals = 0):
495                 self.writeIndented("Collision {\n",1)
496                 self.writeIndented("collide FALSE\n")
497                 self.writeIndented("children [\n")
498
499                 self.writeRegularMesh(me, ob, normals)
500
501                 self.writeIndented("]\n", -1)
502                 self.writeIndented("}\n", -1)
503                 
504         def writeRegularMesh(self, me, ob, normals = 0):
505                 if (self.billnode):
506                         self.writeBillboardMesh(me, ob, normals)
507                 elif (self.halonode):
508                         self.writeHaloMesh(me, ob, normals)
509                 else:
510                         self.writeMeshData(me, ob, normals)
511
512         def writeBillboardMesh(self, me, ob, normals = 0):
513                 self.writeIndented("Billboard {\n",1)
514                 self.writeIndented("axisOfRotation 0 1 0\n")
515                 self.writeIndented("children [\n")
516
517                 self.writeMeshData(me, ob, normals)
518
519                 self.writeIndented("]\n", -1)
520                 self.writeIndented("}\n", -1)
521                 
522         def writeHaloMesh(self, me, ob, normals = 0):
523                 self.writeIndented("Billboard {\n",1)
524                 self.writeIndented("axisOfRotation 0 0 0\n")
525                 self.writeIndented("children [\n")
526
527                 self.writeMeshData(me, ob, normals)
528
529                 self.writeIndented("]\n", -1)
530                 self.writeIndented("}\n", -1)
531
532         ###
533         ### Here is where real mesh data is written
534         ### 
535         def writeMeshData(self, me, ob, normals = 0):
536                 meshName = self.cleanStr(me.name)
537
538                 if self.meshNames.has_key(meshName):
539                         self.writeIndented("USE ME_%s\n" % meshName, 0)
540                         self.meshNames[meshName]+=1
541                         if (self.verbose == 1):
542                                 print "  Using Mesh %s (Blender mesh: %s)\n" % \
543                                           (meshName, me.name)
544                         return
545                 self.meshNames[meshName]=1
546
547                 if (self.verbose == 1):
548                         print "  Writing Mesh %s (Blender mesh: %s)\n" % \
549                                   (meshName, me.name)
550                         return
551
552                 self.writeIndented("DEF ME_%s Group {\n" % meshName,1)
553                 self.writeIndented("children [\n", 1)
554                         
555                 hasImageTexture = 0
556                 issmooth = 0
557
558                 maters = me.materials
559                 nummats = self.getNumMaterials(me)
560
561                 # Vertex and Face colors trump materials and image textures
562                 if (self.facecolors or self.vcolors):
563                         if nummats > 0:
564                                 if maters[0]:
565                                         self.writeShape(ob, me, 0, None)
566                                 else:
567                                         self.writeShape(ob, me, -1, None)
568                         else:
569                                 self.writeShape(ob, me, -1, None)
570                 # Do meshes with materials, possible with image textures
571                 elif nummats > 0:
572                         for matnum in range(len(maters)):
573                                 if maters[matnum]:
574                                         images = []
575                                         if me.faceUV:
576                                                 images = self.getImages(me, matnum)
577                                                 if len(images) > 0:
578                                                         for image in images:
579                                                                 self.writeShape(ob, me, matnum, image)
580                                                 else:
581                                                         self.writeShape(ob, me, matnum, None)
582                                         else:
583                                                 self.writeShape(ob, me, matnum, None)
584                 else:
585                         if me.faceUV:
586                                 images = self.getImages(me, -1)
587                                 if len(images) > 0:
588                                         for image in images:
589                                                 self.writeShape(ob, me, -1, image)
590                                 else:
591                                         self.writeShape(ob, me, -1, None)
592                         else:
593                                 self.writeShape(ob, me, -1, None)
594
595                         
596                 self.writeIndented("]\n", -1)
597                 self.writeIndented("}\n", -1)
598
599         def getImages(self, me, matnum):
600                 imageNames = {}
601                 images = []
602                 for face in me.faces:
603                         if (matnum == -1) or (face.mat == matnum):
604                                 if (face.image):
605                                         imName = self.cleanStr(face.image.name)
606                                         if not imageNames.has_key(imName):
607                                                 images.append(face.image)
608                                                 imageNames[imName]=1
609                 return images
610
611         def getNumMaterials(self, me):
612                 # Oh silly Blender, why do you sometimes have 'None' as
613                 # a member of the me.materials array?
614                 num = 0
615                 for mat in me.materials:
616                         if mat:
617                                 num = num + 1
618                 return num
619
620         def writeCoordinates(self, me, meshName):
621                 coordName = "coord_%s" % (meshName)
622                 # look up coord name, use it if available
623                 if self.coordNames.has_key(coordName):
624                         self.writeIndented("coord USE %s\n" % coordName, 0)
625                         self.coordNames[coordName]+=1
626                         return;
627         
628                 self.coordNames[coordName]=1
629
630                 #-- vertices
631                 self.writeIndented("coord DEF %s Coordinate {\n" % (coordName), 1)
632                 self.writeIndented("point [\n", 1)
633                 meshVertexList = me.verts
634
635                 for vertex in meshVertexList:
636                         vrmlvert = blenvert = Mathutils.Vector(vertex.co)
637                         if export_rotate_z_to_y.val:
638                                 vrmlvert = M_blen2vrml * vrmlvert
639                         self.writeUnindented("%s %s %s\n " % \
640                                                                  (vrmlvert[0], \
641                                                                   vrmlvert[1], \
642                                                                   vrmlvert[2]))
643                 self.writeIndented("]\n", -1)
644                 self.writeIndented("}\n", -1)
645                 self.writeIndented("\n")
646
647         def writeShape(self, ob, me, matnum, image):
648                 # Note: at this point it is assumed for matnum!=-1 that the 
649                 # material in me.materials[matnum] is not equal to 'None'.
650                 # Such validation should be performed by the function that
651                 # calls this one.
652                 self.writeIndented("Shape {\n",1)
653
654                 self.writeIndented("appearance Appearance {\n", 1)
655                 if (matnum != -1):
656                         mater = me.materials[matnum]
657                         self.writeMaterial(mater, self.cleanStr(mater.name,''))
658                         if (mater.mode & Blender.Material.Modes['TEXFACE']):
659                                 if image != None:
660                                         self.writeImageTexture(image.name, image.filename)
661                 else:
662                         if image != None:
663                                 self.writeImageTexture(image.name, image.filename)
664
665                 self.writeIndented("}\n", -1)
666
667                 self.writeGeometry(ob, me, matnum, image)
668
669                 self.writeIndented("}\n", -1)
670
671         def writeGeometry(self, ob, me, matnum, image):
672
673                 #-- IndexedFaceSet or IndexedLineSet
674                 meshName = self.cleanStr(me.name)
675
676                 # check if object is wireframe only
677                 if (self.wire):
678                         ifStyle="IndexedLineSet"
679                 else:
680                         # user selected BOUNDS=1, SOLID=3, SHARED=4, or TEXTURE=5
681                         ifStyle="IndexedFaceSet"
682
683                 self.writeIndented("geometry %s {\n" % ifStyle, 1)
684                 if not self.wire:
685                         if self.twosided == 1:
686                                 self.writeIndented("solid FALSE\n")
687                         else:
688                                 self.writeIndented("solid TRUE\n")
689
690                 self.writeCoordinates(me, meshName)
691                 self.writeCoordIndex(me, meshName, matnum, image)
692                 self.writeTextureCoordinates(me, meshName, matnum, image)
693                 if self.facecolors:
694                         self.writeFaceColors(me)
695                 elif self.vcolors:
696                         self.writeVertexColors(me)
697                 self.writeIndented("}\n", -1)
698
699         def writeCoordIndex(self, me, meshName, matnum, image):
700                 meshVertexList = me.verts
701                 self.writeIndented("coordIndex [\n", 1)
702                 coordIndexList=[]  
703                 for face in me.faces:
704                         if (matnum == -1) or (face.mat == matnum):
705                                 if (image == None) or (face.image == image):
706                                         cordStr=""
707                                         for v in face.verts:
708                                                 indx=v.index
709                                                 cordStr = cordStr + "%s " % indx
710                                         self.writeUnindented(cordStr + "-1, \n")
711                 self.writeIndented("]\n", -1)
712
713         def writeTextureCoordinates(self, me, meshName, matnum, image):
714                 if (image == None):
715                         return
716                 
717                 texCoordList=[] 
718                 texIndexList=[]
719                 j=0
720
721                 for face in me.faces:
722                         coordStr = ""
723                         indexStr = ""
724                         if (matnum == -1) or (face.mat == matnum):
725                                 if (face.image == image):
726                                         for i in range(len(face.verts)):
727                                                 uv = face.uv[i]
728                                                 indexStr += "%s " % (j)
729                                                 coordStr += "%s %s, " % \
730                                                                         (round(uv[0], self.tp), \
731                                                                          round(uv[1], self.tp))
732                                                 j=j+1
733                                         indexStr += "-1"
734                                         texIndexList.append(indexStr)
735                                         texCoordList.append(coordStr)
736
737                 self.writeIndented("texCoord TextureCoordinate {\n", 1)
738                 self.writeIndented("point [\n", 1)
739                 for coord in texCoordList:
740                         self.writeUnindented("%s\n" % (coord))
741                 self.writeIndented("]\n", -1)
742                 self.writeIndented("}\n", -1)
743
744                 self.writeIndented("texCoordIndex [\n", 1)
745                 for ind in texIndexList:
746                         self.writeUnindented("%s\n" % (ind))
747                 self.writeIndented("]\n", -1)
748
749         def writeFaceColors(self, me):
750                 self.writeIndented("colorPerVertex FALSE\n")
751                 self.writeIndented("color Color {\n",1)
752                 self.writeIndented("color [\n", 1)
753
754                 for face in me.faces:
755                         if face.col:
756                                 c=face.col[0]
757                                 if self.verbose >= 2:
758                                         print "Debug: face.col r=%d g=%d b=%d" % (c.r, c.g, c.b)
759
760                                 aColor = self.rgbToFS(c)
761                                 self.writeUnindented("%s,\n" % aColor)
762                 self.writeIndented("]\n",-1)
763                 self.writeIndented("}\n",-1)
764
765         def writeVertexColors(self, me):
766                 self.writeIndented("colorPerVertex TRUE\n")
767                 self.writeIndented("color Color {\n",1)
768                 self.writeIndented("color [\n\t\t\t\t\t\t", 1)
769
770                 cols = [None] * len(me.verts)
771
772                 for face in me.faces:
773                         for vind in range(len(face.v)):
774                                 vertex = face.v[vind]
775                                 i = vertex.index
776                                 if cols[i] == None:
777                                         cols[i] = face.col[vind]
778                                         
779                 for i in range(len(me.verts)):
780                         aColor = self.rgbToFS(cols[i])
781                         self.writeUnindented("%s\n" % aColor)
782
783                 self.writeIndented("\n", 0)
784                 self.writeIndented("]\n",-1)
785                 self.writeIndented("}\n",-1)
786
787         def writeMaterial(self, mat, matName):
788                 # look up material name, use it if available
789                 if self.matNames.has_key(matName):
790                         self.writeIndented("material USE MA_%s\n" % matName)
791                         self.matNames[matName]+=1
792                         return;
793         
794                 self.matNames[matName]=1
795
796                 ambient = mat.amb/3
797                 diffuseR, diffuseG, diffuseB = \
798                                   mat.rgbCol[0], mat.rgbCol[1],mat.rgbCol[2]
799                 if world:
800                         ambi = world.getAmb()
801                         ambi0, ambi1, ambi2 = (ambi[0]*mat.amb) * 2, \
802                                                                   (ambi[1]*mat.amb) * 2, \
803                                                                   (ambi[2]*mat.amb) * 2
804                 else:
805                         ambi0, ambi1, ambi2 = 0, 0, 0
806                 emisR, emisG, emisB = (diffuseR*mat.emit+ambi0) / 2, \
807                                                           (diffuseG*mat.emit+ambi1) / 2, \
808                                                           (diffuseB*mat.emit+ambi2) / 2
809
810                 shininess = mat.hard/512.0
811                 specR = (mat.specCol[0]+0.001) / (1.25/(mat.getSpec()+0.001))
812                 specG = (mat.specCol[1]+0.001) / (1.25/(mat.getSpec()+0.001))
813                 specB = (mat.specCol[2]+0.001) / (1.25/(mat.getSpec()+0.001))
814                 transp = 1 - mat.alpha
815                 matFlags = mat.getMode()
816                 if matFlags & Blender.Material.Modes['SHADELESS']:
817                         ambient = 1
818                         shine = 1
819                         specR = emitR = diffuseR
820                         specG = emitG = diffuseG
821                         specB = emitB = diffuseB
822                 self.writeIndented("material DEF MA_%s Material {\n" % matName, 1)
823                 self.writeIndented("diffuseColor %s %s %s\n" % \
824                                                    (round(diffuseR,self.cp), \
825                                                         round(diffuseG,self.cp), \
826                                                         round(diffuseB,self.cp)))
827                 self.writeIndented("ambientIntensity %s\n" % \
828                                                    (round(ambient,self.cp)))
829                 self.writeIndented("specularColor %s %s %s\n" % \
830                                                    (round(specR,self.cp), \
831                                                         round(specG,self.cp), \
832                                                         round(specB,self.cp)))
833                 self.writeIndented("emissiveColor  %s %s %s\n" % \
834                                                    (round(emisR,self.cp), \
835                                                         round(emisG,self.cp), \
836                                                         round(emisB,self.cp)))
837                 self.writeIndented("shininess %s\n" % (round(shininess,self.cp)))
838                 self.writeIndented("transparency %s\n" % (round(transp,self.cp)))
839                 self.writeIndented("}\n",-1)
840
841         def writeImageTexture(self, name, filename):
842                 if self.texNames.has_key(name):
843                         self.writeIndented("texture USE %s\n" % self.cleanStr(name))
844                         self.texNames[name] += 1
845                         return
846                 else:
847                         self.writeIndented("texture DEF %s ImageTexture {\n" % \
848                                                            self.cleanStr(name), 1)
849                         self.writeIndented('url "%s"\n' % \
850                                                            filename.split("\\")[-1].split("/")[-1])
851                         self.writeIndented("}\n",-1)
852                         self.texNames[name] = 1
853
854         def writeBackground(self):
855                 if world:
856                         worldname = world.getName()
857                 else:
858                         return
859                 blending = world.getSkytype()   
860                 grd = world.getHor()
861                 grd0, grd1, grd2 = grd[0], grd[1], grd[2]
862                 sky = world.getZen()
863                 sky0, sky1, sky2 = sky[0], sky[1], sky[2]
864                 mix0, mix1, mix2 = grd[0]+sky[0], grd[1]+sky[1], grd[2]+sky[2]
865                 mix0, mix1, mix2 = mix0/2, mix1/2, mix2/2
866                 if worldname in self.namesStandard:
867                         self.writeIndented("Background {\n",1)
868                 else:
869                         self.writeIndented("DEF %s Background {\n" % \
870                                                            self.secureName(worldname),1)
871                 # No Skytype - just Hor color
872                 if blending == 0:
873                         self.writeIndented("groundColor %s %s %s\n" % \
874                                                            (round(grd0,self.cp), \
875                                                                 round(grd1,self.cp), \
876                                                                 round(grd2,self.cp)))
877                         self.writeIndented("skyColor %s %s %s\n" % \
878                                                            (round(grd0,self.cp), \
879                                                                 round(grd1,self.cp), \
880                                                                 round(grd2,self.cp)))
881                 # Blend Gradient
882                 elif blending == 1:
883                         self.writeIndented("groundColor [ %s %s %s, " % \
884                                                            (round(grd0,self.cp), \
885                                                                 round(grd1,self.cp), \
886                                                                 round(grd2,self.cp)))
887                         self.writeIndented("%s %s %s ]\n" % \
888                                                            (round(mix0,self.cp), \
889                                                                 round(mix1,self.cp), \
890                                                                 round(mix2,self.cp)))
891                         self.writeIndented("groundAngle [ 1.57, 1.57 ]\n")
892                         self.writeIndented("skyColor [ %s %s %s, " % \
893                                                            (round(sky0,self.cp), \
894                                                                 round(sky1,self.cp), \
895                                                                 round(sky2,self.cp)))
896                         self.writeIndented("%s %s %s ]\n" % \
897                                                            (round(mix0,self.cp), \
898                                                                 round(mix1,self.cp), \
899                                                                 round(mix2,self.cp)))
900                         self.writeIndented("skyAngle [ 1.57, 1.57 ]\n")
901                 # Blend+Real Gradient Inverse
902                 elif blending == 3:
903                         self.writeIndented("groundColor [ %s %s %s, " % \
904                                                            (round(sky0,self.cp), \
905                                                                 round(sky1,self.cp), \
906                                                                 round(sky2,self.cp)))
907                         self.writeIndented("%s %s %s ]\n" % \
908                                                            (round(mix0,self.cp), \
909                                                                 round(mix1,self.cp), \
910                                                                 round(mix2,self.cp)))
911                         self.writeIndented("groundAngle [ 1.57, 1.57 ]\n")
912                         self.writeIndented("skyColor [ %s %s %s, " % \
913                                                            (round(grd0,self.cp), \
914                                                                 round(grd1,self.cp), \
915                                                                 round(grd2,self.cp)))
916                         self.writeIndented("%s %s %s ]\n" % \
917                                                            (round(mix0,self.cp), \
918                                                                 round(mix1,self.cp), \
919                                                                 round(mix2,self.cp)))
920                         self.writeIndented("skyAngle [ 1.57, 1.57 ]\n")
921                 # Paper - just Zen Color
922                 elif blending == 4:
923                         self.writeIndented("groundColor %s %s %s\n" % \
924                                                            (round(sky0,self.cp), \
925                                                                 round(sky1,self.cp), \
926                                                                 round(sky2,self.cp)))
927                         self.writeIndented("skyColor %s %s %s\n" % \
928                                                            (round(sky0,self.cp), \
929                                                                 round(sky1,self.cp), \
930                                                                 round(sky2,self.cp)))
931                 # Blend+Real+Paper - komplex gradient
932                 elif blending == 7:
933                         self.writeIndented("groundColor [ %s %s %s, " % \
934                                                            (round(sky0,self.cp), \
935                                                                 round(sky1,self.cp), \
936                                                                 round(sky2,self.cp)))
937                         self.writeIndented("%s %s %s ]\n" % \
938                                                            (round(grd0,self.cp), \
939                                                                 round(grd1,self.cp), \
940                                                                 round(grd2,self.cp)))
941                         self.writeIndented("groundAngle [ 1.57, 1.57 ]\n")
942                         self.writeIndented("skyColor [ %s %s %s, " % \
943                                                            (round(sky0,self.cp), \
944                                                                 round(sky1,self.cp), \
945                                                                 round(sky2,self.cp)))
946                         self.writeIndented("%s %s %s ]\n" % \
947                                                            (round(grd0,self.cp),
948                                                                 round(grd1,self.cp),
949                                                                 round(grd2,self.cp)))
950                         self.writeIndented("skyAngle [ 1.57, 1.57 ]\n")
951                 # Any Other two colors
952                 else:
953                         self.writeIndented("groundColor %s %s %s\n" % \
954                                                            (round(grd0,self.cp), \
955                                                                 round(grd1,self.cp), \
956                                                                 round(grd2,self.cp)))
957                         self.writeIndented("skyColor %s %s %s\n" % \
958                                                            (round(sky0,self.cp), \
959                                                                 round(sky1,self.cp), \
960                                                                 round(sky2,self.cp)))
961                 alltexture = len(worldmat)
962                 for i in xrange(alltexture):
963                         namemat = worldmat[i].getName()
964                         pic = worldmat[i].getImage()
965                         if pic:
966                                 # Stripped path.
967                                 pic_path= pic.filename.split('\\')[-1].split('/')[-1]
968                                 if namemat == "back":
969                                         self.writeIndented('backUrl "%s"\n' % pic_path)
970                                 elif namemat == "bottom":
971                                         self.writeIndented('bottomUrl "%s"\n' % pic_path)
972                                 elif namemat == "front":
973                                         self.writeIndented('frontUrl "%s"\n' % pic_path)
974                                 elif namemat == "left":
975                                         self.writeIndented('leftUrl "%s"\n' % pic_path)
976                                 elif namemat == "right":
977                                         self.writeIndented('rightUrl "%s"\n' % pic_path)
978                                 elif namemat == "top":
979                                         self.writeIndented('topUrl "%s"\n' % pic_path)
980                 self.writeIndented("}",-1)
981                 self.writeIndented("\n\n")
982
983         def writeLamp(self, ob):
984                 la = ob.data
985                 laType = la.getType()
986
987                 if laType == Lamp.Types.Lamp:
988                         self.writePointLight(ob, la)
989                 elif laType == Lamp.Types.Spot:
990                         self.writeSpotLight(ob, la)
991                 elif laType == Lamp.Types.Sun:
992                         self.writeDirectionalLight(ob, la)
993                 else:
994                         self.writeDirectionalLight(ob, la)
995
996         def writeObject(self, ob):
997
998                 obname = self.cleanStr(ob.name)
999
1000                 try:
1001                         obtype=ob.getType()
1002                 except AttributeError:
1003                         print "Error: Unable to get type info for %s" % obname
1004                         return
1005
1006                 if self.verbose >= 1:
1007                         print "++ Writing %s object %s (Blender name: %s)\n" % \
1008                                   (obtype, obname, ob.name)
1009
1010                 # Note: I am leaving empties out for now -- the original
1011                 # script does some really weird stuff with empties
1012                 if ( (obtype != "Camera") and \
1013                          (obtype != "Mesh") and \
1014                          (obtype != "Lamp") ):
1015                         print "Info: Ignoring [%s], object type [%s] " \
1016                                   "not handle yet" % (obname, obtype)
1017                         return
1018
1019                 ob_matrix = Mathutils.Matrix(ob.getMatrix('worldspace'))
1020                 if export_rotate_z_to_y.val:
1021                         matrix = M_blen2vrml * ob_matrix * M_vrml2blen
1022                 else:
1023                         matrix = ob_matrix
1024                 e      = matrix.rotationPart().toEuler()
1025
1026                 v = matrix.translationPart()
1027                 (axis, angle) = self.eulToVecRot(self.deg2rad(e.x), \
1028                                                                                  self.deg2rad(e.y), \
1029                                                                                  self.deg2rad(e.z))
1030
1031                 mrot = e.toMatrix().resize4x4()
1032                 try:
1033                         mrot.invert()
1034                 except:
1035                         print "Warning: %s has degenerate transformation!" % (obname)
1036                         return
1037                 
1038                 diag = matrix * mrot
1039                 sizeX = diag[0][0]
1040                 sizeY = diag[1][1]
1041                 sizeZ = diag[2][2]
1042
1043                 if self.verbose >= 1:
1044                         print "  Transformation:\n" \
1045                                   "    loc:  %f %f %f\n" \
1046                                   "    size: %f %f %f\n" \
1047                                   "    Rot:  (%f %f %f), %f\n" % \
1048                                   (v.x, v.y, v.z, \
1049                                    sizeX, sizeY, sizeZ, \
1050                                    axis[0], axis[1], axis[2], angle)
1051
1052                 self.writeIndented("DEF OB_%s Transform {\n" % (obname), 1)
1053                 self.writeIndented("translation %f %f %f\n" % \
1054                                                    (v.x, v.y, v.z) )
1055
1056                 self.writeIndented("rotation %f %f %f %f\n" % \
1057                                                    (axis[0],axis[1],axis[2],angle) )
1058                 
1059                 self.writeIndented("scale %f %f %f\n" % \
1060                                                    (sizeX, sizeY, sizeZ) )
1061
1062                 self.writeIndented("children [\n", 1)
1063
1064                 self.writeObData(ob)
1065
1066                 self.writeIndented("]\n", -1) # end object
1067                 self.writeIndented("}\n", -1) # end object
1068
1069         def writeObData(self, ob):
1070
1071                 obtype = ob.getType()
1072
1073                 if obtype == "Camera":
1074                         self.writeViewpoint(ob)
1075                 elif obtype == "Mesh":
1076                         self.writeMesh(ob)
1077                 elif obtype == "Lamp":
1078                         self.writeLamp(ob)
1079                 elif obtype == "Empty":
1080                         self.writeNode(ob)
1081
1082
1083 ##########################################################
1084 # export routine
1085 ##########################################################
1086
1087         def export(self, scene, world, worldmat):
1088                 print "Info: starting VRML97 export to " + self.filename + "..."
1089                 self.writeHeader()
1090                 self.writeScript()
1091                 self.writeNavigationInfo(scene)
1092                 self.writeBackground()
1093                 self.writeFog()
1094                 self.proto = 0
1095                 allObj = []
1096                 if export_selection_only.val:
1097                         allObj = list(scene.objects.context)
1098                 else:
1099                         allObj = list(scene.objects)
1100                         self.writeInline()
1101
1102                 for thisObj in allObj:
1103                         self.writeObject(thisObj)
1104
1105                 if not export_selection_only.val:
1106                         self.writeScript()
1107                 self.cleanup()
1108
1109 ##########################################################
1110 # Utility methods
1111 ##########################################################
1112
1113         def cleanup(self):
1114                 self.file.close()
1115                 self.texNames={}
1116                 self.matNames={}
1117                 self.indentLevel=0
1118                 print "Info: finished VRML97 export to %s\n" % self.filename
1119
1120         def cleanStr(self, name, prefix='rsvd_'):
1121                 """cleanStr(name,prefix) - try to create a valid VRML DEF \
1122                 name from object name"""
1123
1124                 newName=name[:]
1125                 if len(newName) == 0:
1126                         self.nNodeID+=1
1127                         return "%s%d" % (prefix, self.nNodeID)
1128                 
1129                 if newName in self.namesReserved:
1130                         newName='%s%s' % (prefix,newName)
1131                 
1132                 if newName[0].isdigit():
1133                         newName='%s%s' % ('_',newName)
1134
1135                 for bad in (' ','"','#',"'",',','.','[','\\',']','{','}'):
1136                         newName=newName.replace(bad,'_')
1137                 return newName
1138
1139         def rgbToFS(self, c):
1140                 s = "%s %s %s" % \
1141                         (round(c.r/255.0,self.cp), \
1142                          round(c.g/255.0,self.cp), \
1143                          round(c.b/255.0,self.cp))
1144                 return s
1145
1146         def rad2deg(self, v):
1147                 return round(v*180.0/math.pi,4)
1148
1149         def deg2rad(self, v):
1150                 return (v*math.pi)/180.0;
1151
1152         def eulToVecRot(self, RotX, RotY, RotZ):
1153                 
1154                 ti = RotX*0.5
1155                 tj = RotY*0.5
1156                 th = RotZ*0.5
1157
1158                 ci = math.cos(ti)
1159                 cj = math.cos(tj)
1160                 ch = math.cos(th)
1161                 si = math.sin(ti)
1162                 sj = math.sin(tj)
1163                 sh = math.sin(th)
1164                 cc = ci*ch
1165                 cs = ci*sh
1166                 sc = si*ch
1167                 ss = si*sh
1168         
1169                 q0 = cj*cc + sj*ss
1170                 q1 = cj*sc - sj*cs
1171                 q2 = cj*ss + sj*cc
1172                 q3 = cj*cs - sj*sc
1173
1174                 angle = 2 * math.acos(q0)
1175                 if (math.fabs(angle) < 0.000001):
1176                         axis = [1.0, 0.0, 0.0]
1177                 else:
1178                         sphi = 1.0/math.sqrt(1.0 - (q0*q0))
1179                         axis = [q1 * sphi, q2 * sphi, q3 * sphi]
1180
1181                 a = Mathutils.Vector(axis)
1182                 a.normalize()
1183                 return ([a.x, a.y, a.z], angle)
1184
1185
1186         # For writing well formed VRML code
1187         #----------------------------------
1188         def writeIndented(self, s, inc=0):
1189                 if inc < 1:
1190                         self.indentLevel = self.indentLevel + inc
1191                 
1192                 self.file.write( self.indentLevel*"\t" + s)
1193
1194                 if inc > 0:
1195                         self.indentLevel = self.indentLevel + inc
1196
1197         # Sometimes better to not have too many
1198         # tab characters in a long list, for file size
1199         #----------------------------------
1200         def writeUnindented(self, s):
1201                 self.file.write(s)
1202
1203 ##########################################################
1204 # Callbacks, needed before Main
1205 ##########################################################
1206
1207 def select_file(filename):
1208         if sys.exists(filename) and _safeOverwrite:
1209                 result = \
1210                         Draw.PupMenu("File Already Exists, Overwrite?%t|Yes%x1|No%x0")
1211                 if(result != 1):
1212                         return
1213
1214         if not filename.endswith(extension):
1215                 filename += extension
1216
1217         wrlexport=VRML2Export(filename)
1218         wrlexport.export(scene, world, worldmat)
1219
1220 #########################################################
1221 # UI and Registry utilities
1222 #########################################################
1223
1224 export_selection_only = Draw.Create(0)
1225 export_rotate_z_to_y = Draw.Create(1)
1226 export_compressed = Draw.Create(0)
1227
1228 def save_to_registry():
1229         d = {}
1230         d['selection_only'] = export_selection_only.val
1231         d['rotate_z_to_y'] = export_rotate_z_to_y.val
1232         d['compressed'] = export_compressed.val
1233         Registry.SetKey('vrml97_export', d, True)
1234
1235 def load_from_registry():
1236         d = Registry.GetKey('vrml97_export', True)
1237         if d:
1238                 try:
1239                         export_selection_only.val = d['selection_only']
1240                         export_rotate_z_to_y.val = d['rotate_z_to_y']
1241                         export_compressed.val = d['compressed']
1242                 except: save_to_registry() # If data is not valid, rewrite it.
1243
1244 def show_popup():
1245         pup_block = [
1246                 ('Selection Only', export_selection_only, 'Only export objects in visible selection. Else export whole scene.'),
1247                 ('Rotate +Z to +Y', export_rotate_z_to_y, 'Rotate such that +Z axis (Blender up) becomes +Y (VRML up).'),
1248                 ('Compress', export_compressed, 'Generate a .wrz file (normal VRML compressed by gzip).')
1249                 ]
1250         return Draw.PupBlock('Export VRML 97...', pup_block) 
1251
1252 #########################################################
1253 # main routine
1254 #########################################################
1255
1256 load_from_registry()
1257
1258 # Note that show_popup must be done before Blender.Window.FileSelector,
1259 # because export_compressed affects the suggested extension of resulting
1260 # file.
1261
1262 if show_popup():
1263         save_to_registry()
1264         if export_compressed.val:
1265                 extension=".wrz"
1266                 from gzip import *
1267         else:
1268                 extension=".wrl"
1269         Blender.Window.FileSelector(select_file, "Export VRML97", \
1270                                                                 sys.makename(ext=extension))