Use xrange() rather than range() for loop iterations.
[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 xrange(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 xrange(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 xrange(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 xrange(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 = len(me.materials)
560
561                 # Vertex and Face colors trump materials and image textures
562                 if (self.facecolors or self.vcolors):
563                         if nummats > 0:
564                                 self.writeShape(ob, me, 0, None)
565                         else:
566                                 self.writeShape(ob, me, -1, None)
567
568                 # Do meshes with materials, possibly with image textures
569                 elif nummats > 0:
570                         for matnum in xrange(len(maters)):
571                                 images = []
572                                 if me.faceUV:
573                                         images = self.getImages(me, matnum)
574                                         if len(images) > 0:
575                                                 for image in images:
576                                                         self.writeShape(ob, me, matnum, image)
577                                         else:
578                                                 self.writeShape(ob, me, matnum, None)
579                                 else:
580                                         self.writeShape(ob, me, matnum, None)
581                 else:
582                         if me.faceUV:
583                                 images = self.getImages(me, -1)
584                                 if len(images) > 0:
585                                         for image in images:
586                                                 self.writeShape(ob, me, -1, image)
587                                 else:
588                                         self.writeShape(ob, me, -1, None)
589                         else:
590                                 self.writeShape(ob, me, -1, None)
591
592                         
593                 self.writeIndented("]\n", -1)
594                 self.writeIndented("}\n", -1)
595
596         def getImages(self, me, matnum):
597                 imageNames = {}
598                 images = []
599                 for face in me.faces:
600                         if (matnum == -1) or (face.mat == matnum):
601                                 if (face.image):
602                                         imName = self.cleanStr(face.image.name)
603                                         if not imageNames.has_key(imName):
604                                                 images.append(face.image)
605                                                 imageNames[imName]=1
606                 return images
607
608         def writeCoordinates(self, me, meshName):
609                 coordName = "coord_%s" % (meshName)
610                 # look up coord name, use it if available
611                 if self.coordNames.has_key(coordName):
612                         self.writeIndented("coord USE %s\n" % coordName, 0)
613                         self.coordNames[coordName]+=1
614                         return;
615         
616                 self.coordNames[coordName]=1
617
618                 #-- vertices
619                 self.writeIndented("coord DEF %s Coordinate {\n" % (coordName), 1)
620                 self.writeIndented("point [\n", 1)
621                 meshVertexList = me.verts
622
623                 for vertex in meshVertexList:
624                         vrmlvert = blenvert = Mathutils.Vector(vertex.co)
625                         if export_rotate_z_to_y.val:
626                                 vrmlvert = M_blen2vrml * vrmlvert
627                         self.writeUnindented("%s %s %s\n " % \
628                                                                  (vrmlvert[0], \
629                                                                   vrmlvert[1], \
630                                                                   vrmlvert[2]))
631                 self.writeIndented("]\n", -1)
632                 self.writeIndented("}\n", -1)
633                 self.writeIndented("\n")
634
635         def testShape(self, ob, me, matnum, image):
636                 if ( (matnum == -1) and (image == None) ):
637                         if ( len(me.faces) > 0 ):
638                                 return True
639                 # Check if any faces the material or image
640                 for face in me.faces:
641                         if (matnum == -1):
642                                 if (f.image == image):
643                                         return True
644                         elif (image == None):
645                                 if (face.mat == matnum):
646                                         return True
647                         else:
648                                 if ((face.image == image) and (face.mat == matnum)):
649                                         return True
650
651                 return False
652
653         def writeShape(self, ob, me, matnum, image):
654                 # matnum == -1  means don't check the face.mat
655                 # image == None means don't check face.image
656
657                 if ( not self.testShape(ob, me, matnum, image) ):
658                         return False
659
660                 self.writeIndented("Shape {\n",1)
661
662                 self.writeIndented("appearance Appearance {\n", 1)
663                 if (matnum != -1):
664                         mater = me.materials[matnum]
665                         if (mater):
666                                 self.writeMaterial(mater, self.cleanStr(mater.name,''))
667                                 if (mater.mode & Blender.Material.Modes['TEXFACE']):
668                                         if image != None:
669                                                 self.writeImageTexture(image.name, image.filename)
670                         else:
671                                 self.writeDefaultMaterial()     
672                 else:
673                         if image != None:
674                                 self.writeImageTexture(image.name, image.filename)
675
676                 self.writeIndented("}\n", -1)
677
678                 self.writeGeometry(ob, me, matnum, image)
679
680                 self.writeIndented("}\n", -1)
681
682                 return True
683
684         def writeGeometry(self, ob, me, matnum, image):
685
686                 #-- IndexedFaceSet or IndexedLineSet
687                 meshName = self.cleanStr(me.name)
688
689                 # check if object is wireframe only
690                 if (self.wire):
691                         ifStyle="IndexedLineSet"
692                 else:
693                         # user selected BOUNDS=1, SOLID=3, SHARED=4, or TEXTURE=5
694                         ifStyle="IndexedFaceSet"
695
696                 self.writeIndented("geometry %s {\n" % ifStyle, 1)
697                 if not self.wire:
698                         if self.twosided == 1:
699                                 self.writeIndented("solid FALSE\n")
700                         else:
701                                 self.writeIndented("solid TRUE\n")
702
703                 self.writeCoordinates(me, meshName)
704                 self.writeCoordIndex(me, meshName, matnum, image)
705                 self.writeTextureCoordinates(me, meshName, matnum, image)
706                 if self.facecolors:
707                         self.writeFaceColors(me)
708                 elif self.vcolors:
709                         self.writeVertexColors(me)
710                 self.writeIndented("}\n", -1)
711
712         def writeCoordIndex(self, me, meshName, matnum, image):
713                 meshVertexList = me.verts
714                 self.writeIndented("coordIndex [\n", 1)
715                 coordIndexList=[]  
716                 for face in me.faces:
717                         if (matnum == -1) or (face.mat == matnum):
718                                 if (image == None) or (face.image == image):
719                                         cordStr=""
720                                         for v in face.verts:
721                                                 indx=v.index
722                                                 cordStr = cordStr + "%s " % indx
723                                         self.writeUnindented(cordStr + "-1, \n")
724                 self.writeIndented("]\n", -1)
725
726         def writeTextureCoordinates(self, me, meshName, matnum, image):
727                 if (image == None):
728                         return
729                 
730                 texCoordList=[] 
731                 texIndexList=[]
732                 j=0
733
734                 for face in me.faces:
735                         coordStr = ""
736                         indexStr = ""
737                         if (matnum == -1) or (face.mat == matnum):
738                                 if (face.image == image):
739                                         for i in xrange(len(face.verts)):
740                                                 uv = face.uv[i]
741                                                 indexStr += "%s " % (j)
742                                                 coordStr += "%s %s, " % \
743                                                                         (round(uv[0], self.tp), \
744                                                                          round(uv[1], self.tp))
745                                                 j=j+1
746                                         indexStr += "-1"
747                                         texIndexList.append(indexStr)
748                                         texCoordList.append(coordStr)
749
750                 self.writeIndented("texCoord TextureCoordinate {\n", 1)
751                 self.writeIndented("point [\n", 1)
752                 for coord in texCoordList:
753                         self.writeUnindented("%s\n" % (coord))
754                 self.writeIndented("]\n", -1)
755                 self.writeIndented("}\n", -1)
756
757                 self.writeIndented("texCoordIndex [\n", 1)
758                 for ind in texIndexList:
759                         self.writeUnindented("%s\n" % (ind))
760                 self.writeIndented("]\n", -1)
761
762         def writeFaceColors(self, me):
763                 self.writeIndented("colorPerVertex FALSE\n")
764                 self.writeIndented("color Color {\n",1)
765                 self.writeIndented("color [\n", 1)
766
767                 for face in me.faces:
768                         if face.col:
769                                 c=face.col[0]
770                                 if self.verbose >= 2:
771                                         print "Debug: face.col r=%d g=%d b=%d" % (c.r, c.g, c.b)
772
773                                 aColor = self.rgbToFS(c)
774                                 self.writeUnindented("%s,\n" % aColor)
775                 self.writeIndented("]\n",-1)
776                 self.writeIndented("}\n",-1)
777
778         def writeVertexColors(self, me):
779                 self.writeIndented("colorPerVertex TRUE\n")
780                 self.writeIndented("color Color {\n",1)
781                 self.writeIndented("color [\n\t\t\t\t\t\t", 1)
782
783                 cols = [None] * len(me.verts)
784
785                 for face in me.faces:
786                         for vind in xrange(len(face.v)):
787                                 vertex = face.v[vind]
788                                 i = vertex.index
789                                 if cols[i] == None:
790                                         cols[i] = face.col[vind]
791                                         
792                 for i in xrange(len(me.verts)):
793                         aColor = self.rgbToFS(cols[i])
794                         self.writeUnindented("%s\n" % aColor)
795
796                 self.writeIndented("\n", 0)
797                 self.writeIndented("]\n",-1)
798                 self.writeIndented("}\n",-1)
799
800         def writeDefaultMaterial(self):
801                 matName = "default"
802
803                 # look up material name, use it if available
804                 if self.matNames.has_key(matName):
805                         self.writeIndented("material USE MA_%s\n" % matName)
806                         self.matNames[matName]+=1
807                         return;
808
809                 self.matNames[matName]=1
810                 self.writeIndented("material DEF MA_%s Material {\n" % matName, 1)
811                 self.writeIndented("diffuseColor 0.8 0.8 0.8\n")
812                 self.writeIndented("specularColor 1.0 1.0 1.0\n")
813                 self.writeIndented("shininess 0.5\n")
814                 self.writeIndented("transparency 0.0\n")
815                 self.writeIndented("}\n",-1)
816
817         def writeMaterial(self, mat, matName):
818                 # look up material name, use it if available
819                 if self.matNames.has_key(matName):
820                         self.writeIndented("material USE MA_%s\n" % matName)
821                         self.matNames[matName]+=1
822                         return;
823         
824                 self.matNames[matName]=1
825
826                 ambient = mat.amb/3
827                 diffuseR, diffuseG, diffuseB = \
828                                   mat.rgbCol[0], mat.rgbCol[1],mat.rgbCol[2]
829                 if world:
830                         ambi = world.getAmb()
831                         ambi0, ambi1, ambi2 = (ambi[0]*mat.amb) * 2, \
832                                                                   (ambi[1]*mat.amb) * 2, \
833                                                                   (ambi[2]*mat.amb) * 2
834                 else:
835                         ambi0, ambi1, ambi2 = 0, 0, 0
836                 emisR, emisG, emisB = (diffuseR*mat.emit+ambi0) / 2, \
837                                                           (diffuseG*mat.emit+ambi1) / 2, \
838                                                           (diffuseB*mat.emit+ambi2) / 2
839
840                 shininess = mat.hard/512.0
841                 specR = (mat.specCol[0]+0.001) / (1.25/(mat.getSpec()+0.001))
842                 specG = (mat.specCol[1]+0.001) / (1.25/(mat.getSpec()+0.001))
843                 specB = (mat.specCol[2]+0.001) / (1.25/(mat.getSpec()+0.001))
844                 transp = 1 - mat.alpha
845                 matFlags = mat.getMode()
846                 if matFlags & Blender.Material.Modes['SHADELESS']:
847                         ambient = 1
848                         shine = 1
849                         specR = emitR = diffuseR
850                         specG = emitG = diffuseG
851                         specB = emitB = diffuseB
852                 self.writeIndented("material DEF MA_%s Material {\n" % matName, 1)
853                 self.writeIndented("diffuseColor %s %s %s\n" % \
854                                                    (round(diffuseR,self.cp), \
855                                                         round(diffuseG,self.cp), \
856                                                         round(diffuseB,self.cp)))
857                 self.writeIndented("ambientIntensity %s\n" % \
858                                                    (round(ambient,self.cp)))
859                 self.writeIndented("specularColor %s %s %s\n" % \
860                                                    (round(specR,self.cp), \
861                                                         round(specG,self.cp), \
862                                                         round(specB,self.cp)))
863                 self.writeIndented("emissiveColor  %s %s %s\n" % \
864                                                    (round(emisR,self.cp), \
865                                                         round(emisG,self.cp), \
866                                                         round(emisB,self.cp)))
867                 self.writeIndented("shininess %s\n" % (round(shininess,self.cp)))
868                 self.writeIndented("transparency %s\n" % (round(transp,self.cp)))
869                 self.writeIndented("}\n",-1)
870
871         def writeImageTexture(self, name, filename):
872                 if self.texNames.has_key(name):
873                         self.writeIndented("texture USE %s\n" % self.cleanStr(name))
874                         self.texNames[name] += 1
875                         return
876                 else:
877                         self.writeIndented("texture DEF %s ImageTexture {\n" % \
878                                                            self.cleanStr(name), 1)
879                         self.writeIndented('url "%s"\n' % \
880                                                            filename.split("\\")[-1].split("/")[-1])
881                         self.writeIndented("}\n",-1)
882                         self.texNames[name] = 1
883
884         def writeBackground(self):
885                 if world:
886                         worldname = world.getName()
887                 else:
888                         return
889                 blending = world.getSkytype()   
890                 grd = world.getHor()
891                 grd0, grd1, grd2 = grd[0], grd[1], grd[2]
892                 sky = world.getZen()
893                 sky0, sky1, sky2 = sky[0], sky[1], sky[2]
894                 mix0, mix1, mix2 = grd[0]+sky[0], grd[1]+sky[1], grd[2]+sky[2]
895                 mix0, mix1, mix2 = mix0/2, mix1/2, mix2/2
896                 if worldname in self.namesStandard:
897                         self.writeIndented("Background {\n",1)
898                 else:
899                         self.writeIndented("DEF %s Background {\n" % \
900                                                            self.secureName(worldname),1)
901                 # No Skytype - just Hor color
902                 if blending == 0:
903                         self.writeIndented("groundColor %s %s %s\n" % \
904                                                            (round(grd0,self.cp), \
905                                                                 round(grd1,self.cp), \
906                                                                 round(grd2,self.cp)))
907                         self.writeIndented("skyColor %s %s %s\n" % \
908                                                            (round(grd0,self.cp), \
909                                                                 round(grd1,self.cp), \
910                                                                 round(grd2,self.cp)))
911                 # Blend Gradient
912                 elif blending == 1:
913                         self.writeIndented("groundColor [ %s %s %s, " % \
914                                                            (round(grd0,self.cp), \
915                                                                 round(grd1,self.cp), \
916                                                                 round(grd2,self.cp)))
917                         self.writeIndented("%s %s %s ]\n" % \
918                                                            (round(mix0,self.cp), \
919                                                                 round(mix1,self.cp), \
920                                                                 round(mix2,self.cp)))
921                         self.writeIndented("groundAngle [ 1.57, 1.57 ]\n")
922                         self.writeIndented("skyColor [ %s %s %s, " % \
923                                                            (round(sky0,self.cp), \
924                                                                 round(sky1,self.cp), \
925                                                                 round(sky2,self.cp)))
926                         self.writeIndented("%s %s %s ]\n" % \
927                                                            (round(mix0,self.cp), \
928                                                                 round(mix1,self.cp), \
929                                                                 round(mix2,self.cp)))
930                         self.writeIndented("skyAngle [ 1.57, 1.57 ]\n")
931                 # Blend+Real Gradient Inverse
932                 elif blending == 3:
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(mix0,self.cp), \
939                                                                 round(mix1,self.cp), \
940                                                                 round(mix2,self.cp)))
941                         self.writeIndented("groundAngle [ 1.57, 1.57 ]\n")
942                         self.writeIndented("skyColor [ %s %s %s, " % \
943                                                            (round(grd0,self.cp), \
944                                                                 round(grd1,self.cp), \
945                                                                 round(grd2,self.cp)))
946                         self.writeIndented("%s %s %s ]\n" % \
947                                                            (round(mix0,self.cp), \
948                                                                 round(mix1,self.cp), \
949                                                                 round(mix2,self.cp)))
950                         self.writeIndented("skyAngle [ 1.57, 1.57 ]\n")
951                 # Paper - just Zen Color
952                 elif blending == 4:
953                         self.writeIndented("groundColor %s %s %s\n" % \
954                                                            (round(sky0,self.cp), \
955                                                                 round(sky1,self.cp), \
956                                                                 round(sky2,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                 # Blend+Real+Paper - komplex gradient
962                 elif blending == 7:
963                         self.writeIndented("groundColor [ %s %s %s, " % \
964                                                            (round(sky0,self.cp), \
965                                                                 round(sky1,self.cp), \
966                                                                 round(sky2,self.cp)))
967                         self.writeIndented("%s %s %s ]\n" % \
968                                                            (round(grd0,self.cp), \
969                                                                 round(grd1,self.cp), \
970                                                                 round(grd2,self.cp)))
971                         self.writeIndented("groundAngle [ 1.57, 1.57 ]\n")
972                         self.writeIndented("skyColor [ %s %s %s, " % \
973                                                            (round(sky0,self.cp), \
974                                                                 round(sky1,self.cp), \
975                                                                 round(sky2,self.cp)))
976                         self.writeIndented("%s %s %s ]\n" % \
977                                                            (round(grd0,self.cp),
978                                                                 round(grd1,self.cp),
979                                                                 round(grd2,self.cp)))
980                         self.writeIndented("skyAngle [ 1.57, 1.57 ]\n")
981                 # Any Other two colors
982                 else:
983                         self.writeIndented("groundColor %s %s %s\n" % \
984                                                            (round(grd0,self.cp), \
985                                                                 round(grd1,self.cp), \
986                                                                 round(grd2,self.cp)))
987                         self.writeIndented("skyColor %s %s %s\n" % \
988                                                            (round(sky0,self.cp), \
989                                                                 round(sky1,self.cp), \
990                                                                 round(sky2,self.cp)))
991                 alltexture = len(worldmat)
992                 for i in xrange(alltexture):
993                         namemat = worldmat[i].getName()
994                         pic = worldmat[i].getImage()
995                         if pic:
996                                 # Stripped path.
997                                 pic_path= pic.filename.split('\\')[-1].split('/')[-1]
998                                 if namemat == "back":
999                                         self.writeIndented('backUrl "%s"\n' % pic_path)
1000                                 elif namemat == "bottom":
1001                                         self.writeIndented('bottomUrl "%s"\n' % pic_path)
1002                                 elif namemat == "front":
1003                                         self.writeIndented('frontUrl "%s"\n' % pic_path)
1004                                 elif namemat == "left":
1005                                         self.writeIndented('leftUrl "%s"\n' % pic_path)
1006                                 elif namemat == "right":
1007                                         self.writeIndented('rightUrl "%s"\n' % pic_path)
1008                                 elif namemat == "top":
1009                                         self.writeIndented('topUrl "%s"\n' % pic_path)
1010                 self.writeIndented("}",-1)
1011                 self.writeIndented("\n\n")
1012
1013         def writeLamp(self, ob):
1014                 la = ob.data
1015                 laType = la.getType()
1016
1017                 if laType == Lamp.Types.Lamp:
1018                         self.writePointLight(ob, la)
1019                 elif laType == Lamp.Types.Spot:
1020                         self.writeSpotLight(ob, la)
1021                 elif laType == Lamp.Types.Sun:
1022                         self.writeDirectionalLight(ob, la)
1023                 else:
1024                         self.writeDirectionalLight(ob, la)
1025
1026         def writeObject(self, ob):
1027
1028                 obname = self.cleanStr(ob.name)
1029
1030                 try:
1031                         obtype=ob.getType()
1032                 except AttributeError:
1033                         print "Error: Unable to get type info for %s" % obname
1034                         return
1035
1036                 if self.verbose >= 1:
1037                         print "++ Writing %s object %s (Blender name: %s)\n" % \
1038                                   (obtype, obname, ob.name)
1039
1040                 # Note: I am leaving empties out for now -- the original
1041                 # script does some really weird stuff with empties
1042                 if ( (obtype != "Camera") and \
1043                          (obtype != "Mesh") and \
1044                          (obtype != "Lamp") ):
1045                         print "Info: Ignoring [%s], object type [%s] " \
1046                                   "not handle yet" % (obname, obtype)
1047                         return
1048
1049                 ob_matrix = Mathutils.Matrix(ob.getMatrix('worldspace'))
1050                 if export_rotate_z_to_y.val:
1051                         matrix = M_blen2vrml * ob_matrix * M_vrml2blen
1052                 else:
1053                         matrix = ob_matrix
1054                 e      = matrix.rotationPart().toEuler()
1055
1056                 v = matrix.translationPart()
1057                 (axis, angle) = self.eulToVecRot(self.deg2rad(e.x), \
1058                                                                                  self.deg2rad(e.y), \
1059                                                                                  self.deg2rad(e.z))
1060
1061                 mrot = e.toMatrix().resize4x4()
1062                 try:
1063                         mrot.invert()
1064                 except:
1065                         print "Warning: %s has degenerate transformation!" % (obname)
1066                         return
1067                 
1068                 diag = matrix * mrot
1069                 sizeX = diag[0][0]
1070                 sizeY = diag[1][1]
1071                 sizeZ = diag[2][2]
1072
1073                 if self.verbose >= 1:
1074                         print "  Transformation:\n" \
1075                                   "    loc:  %f %f %f\n" \
1076                                   "    size: %f %f %f\n" \
1077                                   "    Rot:  (%f %f %f), %f\n" % \
1078                                   (v.x, v.y, v.z, \
1079                                    sizeX, sizeY, sizeZ, \
1080                                    axis[0], axis[1], axis[2], angle)
1081
1082                 self.writeIndented("DEF OB_%s Transform {\n" % (obname), 1)
1083                 self.writeIndented("translation %f %f %f\n" % \
1084                                                    (v.x, v.y, v.z) )
1085
1086                 self.writeIndented("rotation %f %f %f %f\n" % \
1087                                                    (axis[0],axis[1],axis[2],angle) )
1088                 
1089                 self.writeIndented("scale %f %f %f\n" % \
1090                                                    (sizeX, sizeY, sizeZ) )
1091
1092                 self.writeIndented("children [\n", 1)
1093
1094                 self.writeObData(ob)
1095
1096                 self.writeIndented("]\n", -1) # end object
1097                 self.writeIndented("}\n", -1) # end object
1098
1099         def writeObData(self, ob):
1100
1101                 obtype = ob.getType()
1102
1103                 if obtype == "Camera":
1104                         self.writeViewpoint(ob)
1105                 elif obtype == "Mesh":
1106                         self.writeMesh(ob)
1107                 elif obtype == "Lamp":
1108                         self.writeLamp(ob)
1109                 elif obtype == "Empty":
1110                         self.writeNode(ob)
1111
1112
1113 ##########################################################
1114 # export routine
1115 ##########################################################
1116
1117         def export(self, scene, world, worldmat):
1118                 print "Info: starting VRML97 export to " + self.filename + "..."
1119                 self.writeHeader()
1120                 self.writeScript()
1121                 self.writeNavigationInfo(scene)
1122                 self.writeBackground()
1123                 self.writeFog()
1124                 self.proto = 0
1125                 allObj = []
1126                 if export_selection_only.val:
1127                         allObj = list(scene.objects.context)
1128                 else:
1129                         allObj = list(scene.objects)
1130                         self.writeInline()
1131
1132                 for thisObj in allObj:
1133                         self.writeObject(thisObj)
1134
1135                 if not export_selection_only.val:
1136                         self.writeScript()
1137                 self.cleanup()
1138
1139 ##########################################################
1140 # Utility methods
1141 ##########################################################
1142
1143         def cleanup(self):
1144                 self.file.close()
1145                 self.texNames={}
1146                 self.matNames={}
1147                 self.indentLevel=0
1148                 print "Info: finished VRML97 export to %s\n" % self.filename
1149
1150         def cleanStr(self, name, prefix='rsvd_'):
1151                 """cleanStr(name,prefix) - try to create a valid VRML DEF \
1152                 name from object name"""
1153
1154                 newName=name[:]
1155                 if len(newName) == 0:
1156                         self.nNodeID+=1
1157                         return "%s%d" % (prefix, self.nNodeID)
1158                 
1159                 if newName in self.namesReserved:
1160                         newName='%s%s' % (prefix,newName)
1161                 
1162                 if newName[0].isdigit():
1163                         newName='%s%s' % ('_',newName)
1164
1165                 for bad in (' ','"','#',"'",',','.','[','\\',']','{','}'):
1166                         newName=newName.replace(bad,'_')
1167                 return newName
1168
1169         def rgbToFS(self, c):
1170                 s = "%s %s %s" % \
1171                         (round(c.r/255.0,self.cp), \
1172                          round(c.g/255.0,self.cp), \
1173                          round(c.b/255.0,self.cp))
1174                 return s
1175
1176         def rad2deg(self, v):
1177                 return round(v*180.0/math.pi,4)
1178
1179         def deg2rad(self, v):
1180                 return (v*math.pi)/180.0;
1181
1182         def eulToVecRot(self, RotX, RotY, RotZ):
1183                 
1184                 ti = RotX*0.5
1185                 tj = RotY*0.5
1186                 th = RotZ*0.5
1187
1188                 ci = math.cos(ti)
1189                 cj = math.cos(tj)
1190                 ch = math.cos(th)
1191                 si = math.sin(ti)
1192                 sj = math.sin(tj)
1193                 sh = math.sin(th)
1194                 cc = ci*ch
1195                 cs = ci*sh
1196                 sc = si*ch
1197                 ss = si*sh
1198         
1199                 q0 = cj*cc + sj*ss
1200                 q1 = cj*sc - sj*cs
1201                 q2 = cj*ss + sj*cc
1202                 q3 = cj*cs - sj*sc
1203
1204                 angle = 2 * math.acos(q0)
1205                 if (math.fabs(angle) < 0.000001):
1206                         axis = [1.0, 0.0, 0.0]
1207                 else:
1208                         sphi = 1.0/math.sqrt(1.0 - (q0*q0))
1209                         axis = [q1 * sphi, q2 * sphi, q3 * sphi]
1210
1211                 a = Mathutils.Vector(axis)
1212                 a.normalize()
1213                 return ([a.x, a.y, a.z], angle)
1214
1215
1216         # For writing well formed VRML code
1217         #----------------------------------
1218         def writeIndented(self, s, inc=0):
1219                 if inc < 1:
1220                         self.indentLevel = self.indentLevel + inc
1221                 
1222                 self.file.write( self.indentLevel*"\t" + s)
1223
1224                 if inc > 0:
1225                         self.indentLevel = self.indentLevel + inc
1226
1227         # Sometimes better to not have too many
1228         # tab characters in a long list, for file size
1229         #----------------------------------
1230         def writeUnindented(self, s):
1231                 self.file.write(s)
1232
1233 ##########################################################
1234 # Callbacks, needed before Main
1235 ##########################################################
1236
1237 def select_file(filename):
1238         if sys.exists(filename) and _safeOverwrite:
1239                 result = \
1240                         Draw.PupMenu("File Already Exists, Overwrite?%t|Yes%x1|No%x0")
1241                 if(result != 1):
1242                         return
1243
1244         if not filename.endswith(extension):
1245                 filename += extension
1246
1247         wrlexport=VRML2Export(filename)
1248         wrlexport.export(scene, world, worldmat)
1249
1250 #########################################################
1251 # UI and Registry utilities
1252 #########################################################
1253
1254 export_selection_only = Draw.Create(0)
1255 export_rotate_z_to_y = Draw.Create(1)
1256 export_compressed = Draw.Create(0)
1257
1258 def save_to_registry():
1259         d = {}
1260         d['selection_only'] = export_selection_only.val
1261         d['rotate_z_to_y'] = export_rotate_z_to_y.val
1262         d['compressed'] = export_compressed.val
1263         Registry.SetKey('vrml97_export', d, True)
1264
1265 def load_from_registry():
1266         d = Registry.GetKey('vrml97_export', True)
1267         if d:
1268                 try:
1269                         export_selection_only.val = d['selection_only']
1270                         export_rotate_z_to_y.val = d['rotate_z_to_y']
1271                         export_compressed.val = d['compressed']
1272                 except: save_to_registry() # If data is not valid, rewrite it.
1273
1274 def show_popup():
1275         pup_block = [
1276                 ('Selection Only', export_selection_only, 'Only export objects in visible selection. Else export whole scene.'),
1277                 ('Rotate +Z to +Y', export_rotate_z_to_y, 'Rotate such that +Z axis (Blender up) becomes +Y (VRML up).'),
1278                 ('Compress', export_compressed, 'Generate a .wrz file (normal VRML compressed by gzip).')
1279                 ]
1280         return Draw.PupBlock('Export VRML 97...', pup_block) 
1281
1282 #########################################################
1283 # main routine
1284 #########################################################
1285
1286 load_from_registry()
1287
1288 # Note that show_popup must be done before Blender.Window.FileSelector,
1289 # because export_compressed affects the suggested extension of resulting
1290 # file.
1291
1292 if show_popup():
1293         save_to_registry()
1294         if export_compressed.val:
1295                 extension=".wrz"
1296                 from gzip import *
1297         else:
1298                 extension=".wrl"
1299         Blender.Window.FileSelector(select_file, "Export VRML97", \
1300                                                                 sys.makename(ext=extension))