svn merge -r 14721:14810 https://svn.blender.org/svnroot/bf-blender/trunk/blender
[blender.git] / release / scripts / x3d_export.py
1 #!BPY
2 """ Registration info for Blender menus:
3 Name: 'X3D Extensible 3D (.x3d)...'
4 Blender: 245
5 Group: 'Export'
6 Tooltip: 'Export selection to Extensible 3D file (.x3d)'
7 """
8
9 __author__ = ("Bart", "Campbell Barton")
10 __email__ = ["Bart, bart:neeneenee*de"]
11 __url__ = ["Author's (Bart) homepage, http://www.neeneenee.de/vrml"]
12 __version__ = "2006/01/17"
13 __bpydoc__ = """\
14 This script exports to X3D format.
15
16 Usage:
17
18 Run this script from "File->Export" menu.  A pop-up will ask whether you
19 want to export only selected or all relevant objects.
20
21 Known issues:<br>
22         Doesn't handle multiple materials (don't use material indices);<br>
23         Doesn't handle multiple UV textures on a single mesh (create a mesh for each texture);<br>
24         Can't get the texture array associated with material * not the UV ones;
25 """
26
27
28 # $Id$
29 #
30 #------------------------------------------------------------------------
31 # X3D exporter for blender 2.36 or above
32 #
33 # ***** BEGIN GPL LICENSE BLOCK *****
34 #
35 # This program is free software; you can redistribute it and/or
36 # modify it under the terms of the GNU General Public License
37 # as published by the Free Software Foundation; either version 2
38 # of the License, or (at your option) any later version.
39 #
40 # This program is distributed in the hope that it will be useful,
41 # but WITHOUT ANY WARRANTY; without even the implied warranty of
42 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
43 # GNU General Public License for more details.
44 #
45 # You should have received a copy of the GNU General Public License
46 # along with this program; if not, write to the Free Software Foundation,
47 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
48 #
49 # ***** END GPL LICENCE BLOCK *****
50 #
51
52 ####################################
53 # Library dependancies
54 ####################################
55
56 import Blender
57 from Blender import Object, Lamp, Draw, Image, Text, sys, Mesh
58 from Blender.Scene import Render
59 import math
60 import BPyObject
61 import BPyMesh
62
63
64 DEG2RAD=0.017453292519943295
65 MATWORLD= Blender.Mathutils.RotationMatrix(-90, 4, 'x')
66
67 ####################################
68 # Global Variables
69 ####################################
70
71 filename = Blender.Get('filename')
72 _safeOverwrite = True
73
74 extension = ''
75
76 ##########################################################
77 # Functions for writing output file
78 ##########################################################
79
80 class x3d_class:
81
82         def __init__(self, filename):
83                 #--- public you can change these ---
84                 self.writingcolor = 0
85                 self.writingtexture = 0
86                 self.writingcoords = 0
87                 self.proto = 1
88                 self.matonly = 0
89                 self.share = 0
90                 self.billnode = 0
91                 self.halonode = 0
92                 self.collnode = 0
93                 self.tilenode = 0
94                 self.verbose=2   # level of verbosity in console 0-none, 1-some, 2-most
95                 self.cp=3                 # decimals for material color values   0.000 - 1.000
96                 self.vp=3                 # decimals for vertex coordinate values  0.000 - n.000
97                 self.tp=3                 # decimals for texture coordinate values 0.000 - 1.000
98                 self.it=3
99                 
100                 #--- class private don't touch ---
101                 self.texNames={}   # dictionary of textureNames
102                 self.matNames={}   # dictionary of materiaNames
103                 self.meshNames={}   # dictionary of meshNames
104                 self.indentLevel=0 # keeps track of current indenting
105                 self.filename=filename
106                 self.file = None
107                 if filename.lower().endswith('.x3dz'):
108                         try:
109                                 import gzip
110                                 self.file = gzip.open(filename, "w")                            
111                         except:
112                                 print "failed to import compression modules, exporting uncompressed"
113                                 self.filename = filename[:-1] # remove trailing z
114                 
115                 if self.file == None:
116                         self.file = open(self.filename, "w")
117
118                 self.bNav=0
119                 self.nodeID=0
120                 self.namesReserved=[ "Anchor","Appearance","Arc2D","ArcClose2D","AudioClip","Background","Billboard",
121                                                          "BooleanFilter","BooleanSequencer","BooleanToggle","BooleanTrigger","Box","Circle2D",
122                                                          "Collision","Color","ColorInterpolator","ColorRGBA","component","Cone","connect",
123                                                          "Contour2D","ContourPolyline2D","Coordinate","CoordinateDouble","CoordinateInterpolator",
124                                                          "CoordinateInterpolator2D","Cylinder","CylinderSensor","DirectionalLight","Disk2D",
125                                                          "ElevationGrid","EspduTransform","EXPORT","ExternProtoDeclare","Extrusion","field",
126                                                          "fieldValue","FillProperties","Fog","FontStyle","GeoCoordinate","GeoElevationGrid",
127                                                          "GeoLocationLocation","GeoLOD","GeoMetadata","GeoOrigin","GeoPositionInterpolator",
128                                                          "GeoTouchSensor","GeoViewpoint","Group","HAnimDisplacer","HAnimHumanoid","HAnimJoint",
129                                                          "HAnimSegment","HAnimSite","head","ImageTexture","IMPORT","IndexedFaceSet",
130                                                          "IndexedLineSet","IndexedTriangleFanSet","IndexedTriangleSet","IndexedTriangleStripSet",
131                                                          "Inline","IntegerSequencer","IntegerTrigger","IS","KeySensor","LineProperties","LineSet",
132                                                          "LoadSensor","LOD","Material","meta","MetadataDouble","MetadataFloat","MetadataInteger",
133                                                          "MetadataSet","MetadataString","MovieTexture","MultiTexture","MultiTextureCoordinate",
134                                                          "MultiTextureTransform","NavigationInfo","Normal","NormalInterpolator","NurbsCurve",
135                                                          "NurbsCurve2D","NurbsOrientationInterpolator","NurbsPatchSurface",
136                                                          "NurbsPositionInterpolator","NurbsSet","NurbsSurfaceInterpolator","NurbsSweptSurface",
137                                                          "NurbsSwungSurface","NurbsTextureCoordinate","NurbsTrimmedSurface","OrientationInterpolator",
138                                                          "PixelTexture","PlaneSensor","PointLight","PointSet","Polyline2D","Polypoint2D",
139                                                          "PositionInterpolator","PositionInterpolator2D","ProtoBody","ProtoDeclare","ProtoInstance",
140                                                          "ProtoInterface","ProximitySensor","ReceiverPdu","Rectangle2D","ROUTE","ScalarInterpolator",
141                                                          "Scene","Script","Shape","SignalPdu","Sound","Sphere","SphereSensor","SpotLight","StaticGroup",
142                                                          "StringSensor","Switch","Text","TextureBackground","TextureCoordinate","TextureCoordinateGenerator",
143                                                          "TextureTransform","TimeSensor","TimeTrigger","TouchSensor","Transform","TransmitterPdu",
144                                                          "TriangleFanSet","TriangleSet","TriangleSet2D","TriangleStripSet","Viewpoint","VisibilitySensor",
145                                                          "WorldInfo","X3D","XvlShell","VertexShader","FragmentShader","MultiShaderAppearance","ShaderAppearance" ]
146                 self.namesStandard=[ "Empty","Empty.000","Empty.001","Empty.002","Empty.003","Empty.004","Empty.005",
147                                                          "Empty.006","Empty.007","Empty.008","Empty.009","Empty.010","Empty.011","Empty.012",
148                                                          "Scene.001","Scene.002","Scene.003","Scene.004","Scene.005","Scene.06","Scene.013",
149                                                          "Scene.006","Scene.007","Scene.008","Scene.009","Scene.010","Scene.011","Scene.012",
150                                                          "World","World.000","World.001","World.002","World.003","World.004","World.005" ]
151                 self.namesFog=[ "","LINEAR","EXPONENTIAL","" ]
152
153 ##########################################################
154 # Writing nodes routines
155 ##########################################################
156
157         def writeHeader(self):
158                 #bfile = sys.expandpath( Blender.Get('filename') ).replace('<', '&lt').replace('>', '&gt')
159                 bfile = self.filename.replace('<', '&lt').replace('>', '&gt') # use outfile name
160                 self.file.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
161                 self.file.write("<!DOCTYPE X3D PUBLIC \"ISO//Web3D//DTD X3D 3.0//EN\" \"http://www.web3d.org/specifications/x3d-3.0.dtd\">\n")
162                 self.file.write("<X3D version=\"3.0\" profile=\"Immersive\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema-instance\" xsd:noNamespaceSchemaLocation=\"http://www.web3d.org/specifications/x3d-3.0.xsd\">\n")
163                 self.file.write("<head>\n")
164                 self.file.write("\t<meta name=\"filename\" content=\"%s\" />\n" % sys.basename(bfile))
165                 self.file.write("\t<meta name=\"generator\" content=\"Blender %s\" />\n" % Blender.Get('version'))
166                 self.file.write("\t<meta name=\"translator\" content=\"X3D exporter v1.55 (2006/01/17)\" />\n")
167                 self.file.write("</head>\n")
168                 self.file.write("<Scene>\n")
169         
170         # This functionality is poorly defined, disabling for now - campbell
171         '''
172         def writeInline(self):
173                 inlines = Blender.Scene.Get()
174                 allinlines = len(inlines)
175                 if scene != inlines[0]:
176                         return
177                 else:
178                         for i in xrange(allinlines):
179                                 nameinline=inlines[i].name
180                                 if (nameinline not in self.namesStandard) and (i > 0):
181                                         self.file.write("<Inline DEF=\"%s\" " % (self.cleanStr(nameinline)))
182                                         nameinline = nameinline+".x3d"
183                                         self.file.write("url=\"%s\" />" % nameinline)
184                                         self.file.write("\n\n")
185
186         
187         def writeScript(self):
188                 textEditor = Blender.Text.Get() 
189                 alltext = len(textEditor)
190                 for i in xrange(alltext):
191                         nametext = textEditor[i].name
192                         nlines = textEditor[i].getNLines()
193                         if (self.proto == 1):
194                                 if (nametext == "proto" or nametext == "proto.js" or nametext == "proto.txt") and (nlines != None):
195                                         nalllines = len(textEditor[i].asLines())
196                                         alllines = textEditor[i].asLines()
197                                         for j in xrange(nalllines):
198                                                 self.writeIndented(alllines[j] + "\n")
199                         elif (self.proto == 0):
200                                 if (nametext == "route" or nametext == "route.js" or nametext == "route.txt") and (nlines != None):
201                                         nalllines = len(textEditor[i].asLines())
202                                         alllines = textEditor[i].asLines()
203                                         for j in xrange(nalllines):
204                                                 self.writeIndented(alllines[j] + "\n")
205                 self.writeIndented("\n")
206         '''
207         
208         def writeViewpoint(self, ob, mat, scene):
209                 context = scene.render
210                 ratio = float(context.imageSizeY())/float(context.imageSizeX())
211                 lens = (360* (math.atan(ratio *16 / ob.data.getLens()) / math.pi))*(math.pi/180)
212                 lens = min(lens, math.pi) 
213                 
214                 # get the camera location, subtract 90 degress from X to orient like X3D does
215                 # mat = ob.matrixWorld - mat is now passed!
216                 
217                 loc = self.rotatePointForVRML(mat.translationPart())
218                 rot = mat.toEuler()
219                 rot = (((rot[0]-90)*DEG2RAD), rot[1]*DEG2RAD, rot[2]*DEG2RAD)
220                 nRot = self.rotatePointForVRML( rot )
221                 # convert to Quaternion and to Angle Axis
222                 Q  = self.eulerToQuaternions(nRot[0], nRot[1], nRot[2])
223                 Q1 = self.multiplyQuaternions(Q[0], Q[1])
224                 Qf = self.multiplyQuaternions(Q1, Q[2])
225                 angleAxis = self.quaternionToAngleAxis(Qf)
226                 self.file.write("<Viewpoint DEF=\"%s\" " % (self.cleanStr(ob.name)))
227                 self.file.write("description=\"%s\" " % (ob.name))
228                 self.file.write("centerOfRotation=\"0 0 0\" ")
229                 self.file.write("position=\"%3.2f %3.2f %3.2f\" " % (loc[0], loc[1], loc[2]))
230                 self.file.write("orientation=\"%3.2f %3.2f %3.2f %3.2f\" " % (angleAxis[0], angleAxis[1], -angleAxis[2], angleAxis[3]))
231                 self.file.write("fieldOfView=\"%.3f\" />\n\n" % (lens))
232
233         def writeFog(self, world):
234                 if world:
235                         mtype = world.getMistype()
236                         mparam = world.getMist()
237                         grd = world.getHor()
238                         grd0, grd1, grd2 = grd[0], grd[1], grd[2]
239                 else:
240                         return
241                 if (mtype == 1 or mtype == 2):
242                         self.file.write("<Fog fogType=\"%s\" " % self.namesFog[mtype])                                          
243                         self.file.write("color=\"%s %s %s\" " % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp)))
244                         self.file.write("visibilityRange=\"%s\" />\n\n" % round(mparam[2],self.cp))
245                 else:
246                         return
247         
248         def writeNavigationInfo(self, scene):
249                 self.file.write('<NavigationInfo headlight="FALSE" visibilityLimit="0.0" type=\'"EXAMINE","ANY"\' avatarSize="0.25, 1.75, 0.75" />\n')
250         
251         def writeSpotLight(self, ob, mtx, lamp, world):
252                 safeName = self.cleanStr(ob.name)
253                 if world:
254                         ambi = world.amb
255                         ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5
256                 else:
257                         ambi = 0
258                         ambientIntensity = 0
259
260                 # compute cutoff and beamwidth
261                 intensity=min(lamp.energy/1.75,1.0)
262                 beamWidth=((lamp.spotSize*math.pi)/180.0)*.37;
263                 cutOffAngle=beamWidth*1.3
264
265                 dx,dy,dz=self.computeDirection(mtx)
266                 # note -dx seems to equal om[3][0]
267                 # note -dz seems to equal om[3][1]
268                 # note  dy seems to equal om[3][2]
269
270                 #location=(ob.matrixWorld*MATWORLD).translationPart() # now passed
271                 location=(mtx*MATWORLD).translationPart()
272                 
273                 radius = lamp.dist*math.cos(beamWidth)
274                 self.file.write("<SpotLight DEF=\"%s\" " % safeName)
275                 self.file.write("radius=\"%s\" " % (round(radius,self.cp)))
276                 self.file.write("ambientIntensity=\"%s\" " % (round(ambientIntensity,self.cp)))
277                 self.file.write("intensity=\"%s\" " % (round(intensity,self.cp)))
278                 self.file.write("color=\"%s %s %s\" " % (round(lamp.col[0],self.cp), round(lamp.col[1],self.cp), round(lamp.col[2],self.cp)))
279                 self.file.write("beamWidth=\"%s\" " % (round(beamWidth,self.cp)))
280                 self.file.write("cutOffAngle=\"%s\" " % (round(cutOffAngle,self.cp)))
281                 self.file.write("direction=\"%s %s %s\" " % (round(dx,3),round(dy,3),round(dz,3)))
282                 self.file.write("location=\"%s %s %s\" />\n\n" % (round(location[0],3), round(location[1],3), round(location[2],3)))
283                 
284                 
285         def writeDirectionalLight(self, ob, mtx, lamp, world):
286                 safeName = self.cleanStr(ob.name)
287                 if world:
288                         ambi = world.amb
289                         ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5
290                 else:
291                         ambi = 0
292                         ambientIntensity = 0
293
294                 intensity=min(lamp.energy/1.75,1.0) 
295                 (dx,dy,dz)=self.computeDirection(mtx)
296                 self.file.write("<DirectionalLight DEF=\"%s\" " % safeName)
297                 self.file.write("ambientIntensity=\"%s\" " % (round(ambientIntensity,self.cp)))
298                 self.file.write("color=\"%s %s %s\" " % (round(lamp.col[0],self.cp), round(lamp.col[1],self.cp), round(lamp.col[2],self.cp)))
299                 self.file.write("intensity=\"%s\" " % (round(intensity,self.cp)))
300                 self.file.write("direction=\"%s %s %s\" />\n\n" % (round(dx,4),round(dy,4),round(dz,4)))
301
302         def writePointLight(self, ob, mtx, lamp, world):
303                 safeName = self.cleanStr(ob.name)
304                 if world:
305                         ambi = world.amb
306                         ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5
307                 else:
308                         ambi = 0
309                         ambientIntensity = 0
310                 
311                 # location=(ob.matrixWorld*MATWORLD).translationPart() # now passed
312                 location= (mtx*MATWORLD).translationPart()
313                 
314                 self.file.write("<PointLight DEF=\"%s\" " % safeName)
315                 self.file.write("ambientIntensity=\"%s\" " % (round(ambientIntensity,self.cp)))
316                 self.file.write("color=\"%s %s %s\" " % (round(lamp.col[0],self.cp), round(lamp.col[1],self.cp), round(lamp.col[2],self.cp)))
317                 self.file.write("intensity=\"%s\" " % (round( min(lamp.energy/1.75,1.0) ,self.cp)))
318                 self.file.write("radius=\"%s\" " % lamp.dist )
319                 self.file.write("location=\"%s %s %s\" />\n\n" % (round(location[0],3), round(location[1],3), round(location[2],3)))
320         '''
321         def writeNode(self, ob, mtx):
322                 obname=str(ob.name)
323                 if obname in self.namesStandard:
324                         return
325                 else:
326                         dx,dy,dz = self.computeDirection(mtx)
327                         # location=(ob.matrixWorld*MATWORLD).translationPart()
328                         location=(mtx*MATWORLD).translationPart()
329                         self.writeIndented("<%s\n" % obname,1)
330                         self.writeIndented("direction=\"%s %s %s\"\n" % (round(dx,3),round(dy,3),round(dz,3)))
331                         self.writeIndented("location=\"%s %s %s\"\n" % (round(location[0],3), round(location[1],3), round(location[2],3)))
332                         self.writeIndented("/>\n",-1)
333                         self.writeIndented("\n")
334         '''
335         def secureName(self, name):
336                 name = name + str(self.nodeID)
337                 self.nodeID=self.nodeID+1
338                 if len(name) <= 3:
339                         newname = "_" + str(self.nodeID)
340                         return "%s" % (newname)
341                 else:
342                         for bad in ['"','#',"'",',','.','[','\\',']','{','}']:
343                                 name=name.replace(bad,'_')
344                         if name in self.namesReserved:
345                                 newname = name[0:3] + "_" + str(self.nodeID)
346                                 return "%s" % (newname)
347                         elif name[0].isdigit():
348                                 newname = "_" + name + str(self.nodeID)
349                                 return "%s" % (newname)
350                         else:
351                                 newname = name
352                                 return "%s" % (newname)
353
354         def writeIndexedFaceSet(self, ob, mesh, mtx, world, EXPORT_TRI = False):
355                 imageMap={}   # set of used images
356                 sided={}          # 'one':cnt , 'two':cnt
357                 vColors={}      # 'multi':1
358                 meshName = self.cleanStr(ob.name)
359                 
360                 meshME = self.cleanStr(ob.getData(mesh=1).name) # We dont care if its the mesh name or not
361                 if len(mesh.faces) == 0: return
362                 mode = 0
363                 if mesh.faceUV:
364                         for face in mesh.faces:
365                                 mode |= face.mode 
366                 
367                 if mode & Mesh.FaceModes.HALO and self.halonode == 0:
368                         self.writeIndented("<Billboard axisOfRotation=\"0 0 0\">\n",1)
369                         self.halonode = 1
370                 elif mode & Mesh.FaceModes.BILLBOARD and self.billnode == 0:
371                         self.writeIndented("<Billboard axisOfRotation=\"0 1 0\">\n",1)
372                         self.billnode = 1
373                 elif mode & Mesh.FaceModes.OBCOL and self.matonly == 0:
374                         self.matonly = 1
375                 elif mode & Mesh.FaceModes.TILES and self.tilenode == 0:
376                         self.tilenode = 1
377                 elif not mode & Mesh.FaceModes.DYNAMIC and self.collnode == 0:
378                         self.writeIndented("<Collision enabled=\"false\">\n",1)
379                         self.collnode = 1
380                 
381                 nIFSCnt=self.countIFSSetsNeeded(mesh, imageMap, sided, vColors)
382                 
383                 if nIFSCnt > 1:
384                         self.writeIndented("<Group DEF=\"%s%s\">\n" % ("G_", meshName),1)
385                 
386                 if sided.has_key('two') and sided['two'] > 0:
387                         bTwoSided=1
388                 else:
389                         bTwoSided=0
390
391                 # mtx = ob.matrixWorld * MATWORLD # mtx is now passed
392                 mtx = mtx * MATWORLD
393                 
394                 loc= mtx.translationPart()
395                 sca= mtx.scalePart()
396                 quat = mtx.toQuat()
397                 rot= quat.axis
398
399                 # self.writeIndented('<Transform rotation="%.6f %.6f %.6f %.6f">\n' % (rot[0], rot[1], rot[2], rot[3]))
400                 self.writeIndented('<Transform DEF="%s" translation="%.6f %.6f %.6f" scale="%.6f %.6f %.6f" rotation="%.6f %.6f %.6f %.6f">\n' % \
401                   (meshName, loc[0], loc[1], loc[2], sca[0], sca[1], sca[2], rot[0], rot[1], rot[2], quat.angle*DEG2RAD) )
402
403                 self.writeIndented("<Shape>\n",1)
404                 maters=mesh.materials
405                 hasImageTexture=0
406                 issmooth=0
407
408                 if len(maters) > 0 or mesh.faceUV:
409                         self.writeIndented("<Appearance>\n", 1)
410                         # right now this script can only handle a single material per mesh.
411                         if len(maters) >= 1:
412                                 mat=maters[0]
413                                 matFlags = mat.getMode()
414                                 if not matFlags & Blender.Material.Modes['TEXFACE']:
415                                         self.writeMaterial(mat, self.cleanStr(maters[0].name,''), world)
416                                         if len(maters) > 1:
417                                                 print "Warning: mesh named %s has multiple materials" % meshName
418                                                 print "Warning: only one material per object handled"
419                         
420                                 #-- textures
421                                 if mesh.faceUV:
422                                         for face in mesh.faces:
423                                                 if (hasImageTexture == 0) and (face.image):
424                                                         self.writeImageTexture(face.image)
425                                                         hasImageTexture=1  # keep track of face texture
426                                 if self.tilenode == 1:
427                                         self.writeIndented("<TextureTransform   scale=\"%s %s\" />\n" % (face.image.xrep, face.image.yrep))
428                                         self.tilenode = 0
429                                 self.writeIndented("</Appearance>\n", -1)
430
431                 #-- IndexedFaceSet or IndexedLineSet
432
433                 # user selected BOUNDS=1, SOLID=3, SHARED=4, or TEXTURE=5
434                 ifStyle="IndexedFaceSet"
435                 # look up mesh name, use it if available
436                 if self.meshNames.has_key(meshME):
437                         self.writeIndented("<%s USE=\"ME_%s\">" % (ifStyle, meshME), 1)
438                         self.meshNames[meshME]+=1
439                 else:
440                         if int(mesh.users) > 1:
441                                 self.writeIndented("<%s DEF=\"ME_%s\" " % (ifStyle, meshME), 1)
442                                 self.meshNames[meshME]=1
443                         else:
444                                 self.writeIndented("<%s " % ifStyle, 1)
445                         
446                         if bTwoSided == 1:
447                                 self.file.write("solid=\"false\" ")
448                         else:
449                                 self.file.write("solid=\"true\" ")
450
451                         for face in mesh.faces:
452                                 if face.smooth:
453                                          issmooth=1
454                                          break
455                         if issmooth==1:
456                                 creaseAngle=(mesh.degr)*(math.pi/180.0)
457                                 self.file.write("creaseAngle=\"%s\" " % (round(creaseAngle,self.cp)))
458
459                         #--- output textureCoordinates if UV texture used
460                         if mesh.faceUV:
461                                 if self.matonly == 1 and self.share == 1:
462                                         self.writeFaceColors(mesh)
463                                 elif hasImageTexture == 1:
464                                         self.writeTextureCoordinates(mesh)
465                         #--- output coordinates
466                         self.writeCoordinates(ob, mesh, meshName, EXPORT_TRI)
467
468                         self.writingcoords = 1
469                         self.writingtexture = 1
470                         self.writingcolor = 1
471                         self.writeCoordinates(ob, mesh, meshName, EXPORT_TRI)
472                         
473                         #--- output textureCoordinates if UV texture used
474                         if mesh.faceUV:
475                                 if hasImageTexture == 1:
476                                         self.writeTextureCoordinates(mesh)
477                                 elif self.matonly == 1 and self.share == 1:
478                                         self.writeFaceColors(mesh)
479                         #--- output vertexColors
480                 self.matonly = 0
481                 self.share = 0
482                 
483                 self.writingcoords = 0
484                 self.writingtexture = 0
485                 self.writingcolor = 0
486                 #--- output closing braces
487                 self.writeIndented("</%s>\n" % ifStyle, -1)
488                 self.writeIndented("</Shape>\n", -1)
489                 self.writeIndented("</Transform>\n", -1)
490
491                 if self.halonode == 1:
492                         self.writeIndented("</Billboard>\n", -1)
493                         self.halonode = 0
494
495                 if self.billnode == 1:
496                         self.writeIndented("</Billboard>\n", -1)
497                         self.billnode = 0
498
499                 if self.collnode == 1:
500                         self.writeIndented("</Collision>\n", -1)
501                         self.collnode = 0
502
503                 if nIFSCnt > 1:
504                         self.writeIndented("</Group>\n", -1)
505
506                 self.file.write("\n")
507
508         def writeCoordinates(self, ob, mesh, meshName, EXPORT_TRI = False):
509                 # create vertex list and pre rotate -90 degrees X for VRML
510                 
511                 if self.writingcoords == 0:
512                         self.file.write('coordIndex="')
513                         for face in mesh.faces:
514                                 fv = face.v
515                                 
516                                 if len(face)==3:
517                                                 self.file.write("%i %i %i -1, " % (fv[0].index, fv[1].index, fv[2].index))
518                                 else:
519                                         if EXPORT_TRI:
520                                                 self.file.write("%i %i %i -1, " % (fv[0].index, fv[1].index, fv[2].index))
521                                                 self.file.write("%i %i %i -1, " % (fv[0].index, fv[2].index, fv[3].index))
522                                         else:
523                                                 self.file.write("%i %i %i %i -1, " % (fv[0].index, fv[1].index, fv[2].index, fv[3].index))
524                         
525                         self.file.write("\">\n")
526                 else:
527                         #-- vertices
528                         # mesh.transform(ob.matrixWorld)
529                         self.writeIndented("<Coordinate DEF=\"%s%s\" \n" % ("coord_",meshName), 1)
530                         self.file.write("\t\t\t\tpoint=\"")
531                         for v in mesh.verts:
532                                 self.file.write("%.6f %.6f %.6f, " % tuple(v.co))
533                         self.file.write("\" />")
534                         self.writeIndented("\n", -1)
535
536         def writeTextureCoordinates(self, mesh):
537                 texCoordList=[] 
538                 texIndexList=[]
539                 j=0
540
541                 for face in mesh.faces:
542                         for uv in face.uv:
543                                 texIndexList.append(j)
544                                 texCoordList.append(uv)
545                                 j=j+1
546                         texIndexList.append(-1)
547                 if self.writingtexture == 0:
548                         self.file.write("\n\t\t\ttexCoordIndex=\"")
549                         texIndxStr=""
550                         for i in xrange(len(texIndexList)):
551                                 texIndxStr = texIndxStr + "%d, " % texIndexList[i]
552                                 if texIndexList[i]==-1:
553                                         self.file.write(texIndxStr)
554                                         texIndxStr=""
555                         self.file.write("\"\n\t\t\t")
556                 else:
557                         self.writeIndented("<TextureCoordinate point=\"", 1)
558                         for i in xrange(len(texCoordList)):
559                                 self.file.write("%s %s, " % (round(texCoordList[i][0],self.tp), round(texCoordList[i][1],self.tp)))
560                         self.file.write("\" />")
561                         self.writeIndented("\n", -1)
562
563         def writeFaceColors(self, mesh):
564                 if self.writingcolor == 0:
565                         self.file.write("colorPerVertex=\"false\" ")
566                 else:
567                         self.writeIndented("<Color color=\"", 1)
568                         for face in mesh.faces:
569                                 if face.col:
570                                         c=face.col[0]
571                                         if self.verbose > 2:
572                                                 print "Debug: face.col r=%d g=%d b=%d" % (c.r, c.g, c.b)
573                                         aColor = self.rgbToFS(c)
574                                         self.file.write("%s, " % aColor)
575                         self.file.write("\" />")
576                         self.writeIndented("\n",-1)
577         
578         def writeMaterial(self, mat, matName, world):
579                 # look up material name, use it if available
580                 if self.matNames.has_key(matName):
581                         self.writeIndented("<Material USE=\"MA_%s\" />\n" % matName)
582                         self.matNames[matName]+=1
583                         return;
584
585                 self.matNames[matName]=1
586
587                 ambient = mat.amb/3
588                 diffuseR, diffuseG, diffuseB = mat.rgbCol[0], mat.rgbCol[1],mat.rgbCol[2]
589                 if world:
590                         ambi = world.getAmb()
591                         ambi0, ambi1, ambi2 = (ambi[0]*mat.amb)*2, (ambi[1]*mat.amb)*2, (ambi[2]*mat.amb)*2
592                 else:
593                         ambi0, ambi1, ambi2 = 0, 0, 0
594                 emisR, emisG, emisB = (diffuseR*mat.emit+ambi0)/2, (diffuseG*mat.emit+ambi1)/2, (diffuseB*mat.emit+ambi2)/2
595
596                 shininess = mat.hard/512.0
597                 specR = (mat.specCol[0]+0.001)/(1.25/(mat.spec+0.001))
598                 specG = (mat.specCol[1]+0.001)/(1.25/(mat.spec+0.001))
599                 specB = (mat.specCol[2]+0.001)/(1.25/(mat.spec+0.001))
600                 transp = 1-mat.alpha
601                 matFlags = mat.getMode()
602                 if matFlags & Blender.Material.Modes['SHADELESS']:
603                   ambient = 1
604                   shine = 1
605                   specR = emitR = diffuseR
606                   specG = emitG = diffuseG
607                   specB = emitB = diffuseB
608                 self.writeIndented("<Material DEF=\"MA_%s\" " % matName, 1)
609                 self.file.write("diffuseColor=\"%s %s %s\" " % (round(diffuseR,self.cp), round(diffuseG,self.cp), round(diffuseB,self.cp)))
610                 self.file.write("specularColor=\"%s %s %s\" " % (round(specR,self.cp), round(specG,self.cp), round(specB,self.cp)))
611                 self.file.write("emissiveColor=\"%s %s %s\" \n" % (round(emisR,self.cp), round(emisG,self.cp), round(emisB,self.cp)))
612                 self.writeIndented("ambientIntensity=\"%s\" " % (round(ambient,self.cp)))
613                 self.file.write("shininess=\"%s\" " % (round(shininess,self.cp)))
614                 self.file.write("transparency=\"%s\" />" % (round(transp,self.cp)))
615                 self.writeIndented("\n",-1)
616
617         def writeImageTexture(self, image):
618                 name = image.name
619                 filename = image.filename.split('/')[-1].split('\\')[-1]
620                 if self.texNames.has_key(name):
621                         self.writeIndented("<ImageTexture USE=\"%s\" />\n" % self.cleanStr(name))
622                         self.texNames[name] += 1
623                         return
624                 else:
625                         self.writeIndented("<ImageTexture DEF=\"%s\" " % self.cleanStr(name), 1)
626                         self.file.write("url=\"%s\" />" % name)
627                         self.writeIndented("\n",-1)
628                         self.texNames[name] = 1
629
630         def writeBackground(self, world, alltextures):
631                 if world:       worldname = world.name
632                 else:           return
633                 blending = world.getSkytype()   
634                 grd = world.getHor()
635                 grd0, grd1, grd2 = grd[0], grd[1], grd[2]
636                 sky = world.getZen()
637                 sky0, sky1, sky2 = sky[0], sky[1], sky[2]
638                 mix0, mix1, mix2 = grd[0]+sky[0], grd[1]+sky[1], grd[2]+sky[2]
639                 mix0, mix1, mix2 = mix0/2, mix1/2, mix2/2
640                 self.file.write("<Background ")
641                 if worldname not in self.namesStandard:
642                         self.file.write("DEF=\"%s\" " % self.secureName(worldname))
643                 # No Skytype - just Hor color
644                 if blending == 0:
645                         self.file.write("groundColor=\"%s %s %s\" " % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp)))
646                         self.file.write("skyColor=\"%s %s %s\" " % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp)))
647                 # Blend Gradient
648                 elif blending == 1:
649                         self.file.write("groundColor=\"%s %s %s, " % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp)))
650                         self.file.write("%s %s %s\" groundAngle=\"1.57, 1.57\" " %(round(mix0,self.cp), round(mix1,self.cp), round(mix2,self.cp)))
651                         self.file.write("skyColor=\"%s %s %s, " % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp)))
652                         self.file.write("%s %s %s\" skyAngle=\"1.57, 1.57\" " %(round(mix0,self.cp), round(mix1,self.cp), round(mix2,self.cp)))
653                 # Blend+Real Gradient Inverse
654                 elif blending == 3:
655                         self.file.write("groundColor=\"%s %s %s, " % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp)))
656                         self.file.write("%s %s %s\" groundAngle=\"1.57, 1.57\" " %(round(mix0,self.cp), round(mix1,self.cp), round(mix2,self.cp)))
657                         self.file.write("skyColor=\"%s %s %s, " % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp)))
658                         self.file.write("%s %s %s\" skyAngle=\"1.57, 1.57\" " %(round(mix0,self.cp), round(mix1,self.cp), round(mix2,self.cp)))
659                 # Paper - just Zen Color
660                 elif blending == 4:
661                         self.file.write("groundColor=\"%s %s %s\" " % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp)))
662                         self.file.write("skyColor=\"%s %s %s\" " % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp)))
663                 # Blend+Real+Paper - komplex gradient
664                 elif blending == 7:
665                         self.writeIndented("groundColor=\"%s %s %s, " % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp)))
666                         self.writeIndented("%s %s %s\" groundAngle=\"1.57, 1.57\" " %(round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp)))
667                         self.writeIndented("skyColor=\"%s %s %s, " % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp)))
668                         self.writeIndented("%s %s %s\" skyAngle=\"1.57, 1.57\" " %(round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp)))
669                 # Any Other two colors
670                 else:
671                         self.file.write("groundColor=\"%s %s %s\" " % (round(grd0,self.cp), round(grd1,self.cp), round(grd2,self.cp)))
672                         self.file.write("skyColor=\"%s %s %s\" " % (round(sky0,self.cp), round(sky1,self.cp), round(sky2,self.cp)))
673                 alltexture = len(alltextures)
674                 for i in xrange(alltexture):
675                         namemat = alltextures[i].name
676                         pic = alltextures[i].getImage()
677                         if (namemat == "back") and (pic != None):
678                                 self.file.write("\n\tbackUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1])
679                         elif (namemat == "bottom") and (pic != None):
680                                 self.writeIndented("bottomUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1])
681                         elif (namemat == "front") and (pic != None):
682                                 self.writeIndented("frontUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1])
683                         elif (namemat == "left") and (pic != None):
684                                 self.writeIndented("leftUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1])
685                         elif (namemat == "right") and (pic != None):
686                                 self.writeIndented("rightUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1])
687                         elif (namemat == "top") and (pic != None):
688                                 self.writeIndented("topUrl=\"%s\" " % pic.filename.split('/')[-1].split('\\')[-1])
689                 self.writeIndented("/>\n\n")
690
691 ##########################################################
692 # export routine
693 ##########################################################
694
695         def export(self, scene, world, alltextures,\
696                         EXPORT_APPLY_MODIFIERS = False,\
697                         EXPORT_TRI=                             False,\
698                 ):
699                 
700                 print "Info: starting X3D export to " + self.filename + "..."
701                 self.writeHeader()
702                 # self.writeScript()
703                 self.writeNavigationInfo(scene)
704                 self.writeBackground(world, alltextures)
705                 self.writeFog(world)
706                 self.proto = 0
707                 
708                 
709                 # COPIED FROM OBJ EXPORTER
710                 if EXPORT_APPLY_MODIFIERS:
711                         temp_mesh_name = '~tmp-mesh'
712                 
713                         # Get the container mesh. - used for applying modifiers and non mesh objects.
714                         containerMesh = meshName = tempMesh = None
715                         for meshName in Blender.NMesh.GetNames():
716                                 if meshName.startswith(temp_mesh_name):
717                                         tempMesh = Mesh.Get(meshName)
718                                         if not tempMesh.users:
719                                                 containerMesh = tempMesh
720                         if not containerMesh:
721                                 containerMesh = Mesh.New(temp_mesh_name)
722                 # -------------------------- 
723                 
724                 
725                 for ob_main in scene.objects.context:
726                         for ob, ob_mat in BPyObject.getDerivedObjects(ob_main):
727                                 objType=ob.type
728                                 objName=ob.name
729                                 self.matonly = 0
730                                 if objType == "Camera":
731                                         self.writeViewpoint(ob, ob_mat, scene)
732                                 elif objType in ("Mesh", "Curve", "Surf", "Text") :
733                                         if  EXPORT_APPLY_MODIFIERS or objType != 'Mesh':
734                                                 me= BPyMesh.getMeshFromObject(ob, containerMesh, EXPORT_APPLY_MODIFIERS, False, scene)
735                                         else:
736                                                 me = ob.getData(mesh=1)
737                                         
738                                         self.writeIndexedFaceSet(ob, me, ob_mat, world, EXPORT_TRI = EXPORT_TRI)
739                                 elif objType == "Lamp":
740                                         data= ob.data
741                                         datatype=data.type
742                                         if datatype == Lamp.Types.Lamp:
743                                                 self.writePointLight(ob, ob_mat, data, world)
744                                         elif datatype == Lamp.Types.Spot:
745                                                 self.writeSpotLight(ob, ob_mat, data, world)
746                                         elif datatype == Lamp.Types.Sun:
747                                                 self.writeDirectionalLight(ob, ob_mat, data, world)
748                                         else:
749                                                 self.writeDirectionalLight(ob, ob_mat, data, world)
750                                 # do you think x3d could document what to do with dummy objects?
751                                 #elif objType == "Empty" and objName != "Empty":
752                                 #       self.writeNode(ob, ob_mat)
753                                 else:
754                                         #print "Info: Ignoring [%s], object type [%s] not handle yet" % (object.name,object.getType)
755                                         pass
756                 
757                 self.file.write("\n</Scene>\n</X3D>")
758                 
759                 if EXPORT_APPLY_MODIFIERS:
760                         if containerMesh:
761                                 containerMesh.verts = None
762                 
763                 self.cleanup()
764                 
765 ##########################################################
766 # Utility methods
767 ##########################################################
768
769         def cleanup(self):
770                 self.file.close()
771                 self.texNames={}
772                 self.matNames={}
773                 self.indentLevel=0
774                 print "Info: finished X3D export to %s\n" % self.filename
775
776         def cleanStr(self, name, prefix='rsvd_'):
777                 """cleanStr(name,prefix) - try to create a valid VRML DEF name from object name"""
778
779                 newName=name[:]
780                 if len(newName) == 0:
781                         self.nNodeID+=1
782                         return "%s%d" % (prefix, self.nNodeID)
783                 
784                 if newName in self.namesReserved:
785                         newName='%s%s' % (prefix,newName)
786                 
787                 if newName[0].isdigit():
788                         newName='%s%s' % ('_',newName)
789
790                 for bad in [' ','"','#',"'",',','.','[','\\',']','{','}']:
791                         newName=newName.replace(bad,'_')
792                 return newName
793
794         def countIFSSetsNeeded(self, mesh, imageMap, sided, vColors):
795                 """
796                 countIFFSetsNeeded() - should look at a blender mesh to determine
797                 how many VRML IndexFaceSets or IndexLineSets are needed.  A
798                 new mesh created under the following conditions:
799                 
800                  o - split by UV Textures / one per mesh
801                  o - split by face, one sided and two sided
802                  o - split by smooth and flat faces
803                  o - split when faces only have 2 vertices * needs to be an IndexLineSet
804                 """
805                 
806                 imageNameMap={}
807                 faceMap={}
808                 nFaceIndx=0
809                 
810                 if mesh.faceUV:
811                         for face in mesh.faces:
812                                 sidename='';
813                                 if  face.mode & Mesh.FaceModes.TWOSIDE:
814                                         sidename='two'
815                                 else:
816                                         sidename='one'
817                                 
818                                 if sided.has_key(sidename):
819                                         sided[sidename]+=1
820                                 else:
821                                         sided[sidename]=1
822                                 
823                                 image = face.image
824                                 if image:
825                                         faceName="%s_%s" % (face.image.name, sidename);
826                                         try:
827                                                 imageMap[faceName].append(face)
828                                         except:
829                                                 imageMap[faceName]=[face.image.name,sidename,face]
830
831                         if self.verbose > 2:
832                                 for faceName in imageMap.iterkeys():
833                                         ifs=imageMap[faceName]
834                                         print "Debug: faceName=%s image=%s, solid=%s facecnt=%d" % \
835                                                   (faceName, ifs[0], ifs[1], len(ifs)-2)
836
837                 return len(imageMap)
838         
839         def faceToString(self,face):
840
841                 print "Debug: face.flag=0x%x (bitflags)" % face.flag
842                 if face.sel:
843                         print "Debug: face.sel=true"
844
845                 print "Debug: face.mode=0x%x (bitflags)" % face.mode
846                 if face.mode & Mesh.FaceModes.TWOSIDE:
847                         print "Debug: face.mode twosided"
848
849                 print "Debug: face.transp=0x%x (enum)" % face.transp
850                 if face.transp == Mesh.FaceTranspModes.SOLID:
851                         print "Debug: face.transp.SOLID"
852
853                 if face.image:
854                         print "Debug: face.image=%s" % face.image.name
855                 print "Debug: face.materialIndex=%d" % face.materialIndex 
856
857         def getVertexColorByIndx(self, mesh, indx):
858                 c = None
859                 for face in mesh.faces:
860                         j=0
861                         for vertex in face.v:
862                                 if vertex.index == indx:
863                                         c=face.col[j]
864                                         break
865                                 j=j+1
866                         if c: break
867                 return c
868
869         def meshToString(self,mesh):
870                 print "Debug: mesh.hasVertexUV=%d" % mesh.vertexColors
871                 print "Debug: mesh.faceUV=%d" % mesh.faceUV
872                 print "Debug: mesh.hasVertexColours=%d" % mesh.hasVertexColours()
873                 print "Debug: mesh.verts=%d" % len(mesh.verts)
874                 print "Debug: mesh.faces=%d" % len(mesh.faces)
875                 print "Debug: mesh.materials=%d" % len(mesh.materials)
876
877         def rgbToFS(self, c):
878                 s="%s %s %s" % (
879                         round(c.r/255.0,self.cp),
880                         round(c.g/255.0,self.cp),
881                         round(c.b/255.0,self.cp))
882                 return s
883
884         def computeDirection(self, mtx):
885                 x,y,z=(0,-1.0,0) # point down
886                 
887                 ax,ay,az = (mtx*MATWORLD).toEuler()
888                 
889                 ax *= DEG2RAD
890                 ay *= DEG2RAD
891                 az *= DEG2RAD
892                 # rot X
893                 x1=x
894                 y1=y*math.cos(ax)-z*math.sin(ax)
895                 z1=y*math.sin(ax)+z*math.cos(ax)
896
897                 # rot Y
898                 x2=x1*math.cos(ay)+z1*math.sin(ay)
899                 y2=y1
900                 z2=z1*math.cos(ay)-x1*math.sin(ay)
901
902                 # rot Z
903                 x3=x2*math.cos(az)-y2*math.sin(az)
904                 y3=x2*math.sin(az)+y2*math.cos(az)
905                 z3=z2
906
907                 return [x3,y3,z3]
908                 
909
910         # swap Y and Z to handle axis difference between Blender and VRML
911         #------------------------------------------------------------------------
912         def rotatePointForVRML(self, v):
913                 x = v[0]
914                 y = v[2]
915                 z = -v[1]
916                 
917                 vrmlPoint=[x, y, z]
918                 return vrmlPoint
919
920         # For writing well formed VRML code
921         #------------------------------------------------------------------------
922         def writeIndented(self, s, inc=0):
923                 if inc < 1:
924                         self.indentLevel = self.indentLevel + inc
925
926                 spaces=""
927                 for x in xrange(self.indentLevel):
928                         spaces = spaces + "\t"
929                 self.file.write(spaces + s)
930
931                 if inc > 0:
932                         self.indentLevel = self.indentLevel + inc
933
934         # Converts a Euler to three new Quaternions
935         # Angles of Euler are passed in as radians
936         #------------------------------------------------------------------------
937         def eulerToQuaternions(self, x, y, z):
938                 Qx = [math.cos(x/2), math.sin(x/2), 0, 0]
939                 Qy = [math.cos(y/2), 0, math.sin(y/2), 0]
940                 Qz = [math.cos(z/2), 0, 0, math.sin(z/2)]
941                 
942                 quaternionVec=[Qx,Qy,Qz]
943                 return quaternionVec
944         
945         # Multiply two Quaternions together to get a new Quaternion
946         #------------------------------------------------------------------------
947         def multiplyQuaternions(self, Q1, Q2):
948                 result = [((Q1[0] * Q2[0]) - (Q1[1] * Q2[1]) - (Q1[2] * Q2[2]) - (Q1[3] * Q2[3])),
949                                   ((Q1[0] * Q2[1]) + (Q1[1] * Q2[0]) + (Q1[2] * Q2[3]) - (Q1[3] * Q2[2])),
950                                   ((Q1[0] * Q2[2]) + (Q1[2] * Q2[0]) + (Q1[3] * Q2[1]) - (Q1[1] * Q2[3])),
951                                   ((Q1[0] * Q2[3]) + (Q1[3] * Q2[0]) + (Q1[1] * Q2[2]) - (Q1[2] * Q2[1]))]
952                 
953                 return result
954         
955         # Convert a Quaternion to an Angle Axis (ax, ay, az, angle)
956         # angle is in radians
957         #------------------------------------------------------------------------
958         def quaternionToAngleAxis(self, Qf):
959                 scale = math.pow(Qf[1],2) + math.pow(Qf[2],2) + math.pow(Qf[3],2)
960                 ax = Qf[1]
961                 ay = Qf[2]
962                 az = Qf[3]
963
964                 if scale > .0001:
965                         ax/=scale
966                         ay/=scale
967                         az/=scale
968                 
969                 angle = 2 * math.acos(Qf[0])
970                 
971                 result = [ax, ay, az, angle]
972                 return result
973
974 ##########################################################
975 # Callbacks, needed before Main
976 ##########################################################
977
978 def x3d_export(filename, \
979                 EXPORT_APPLY_MODIFIERS= False,\
980                 EXPORT_TRI=                             False,\
981                 EXPORT_GZIP=                    False,\
982         ):
983         
984         if EXPORT_GZIP:
985                 if not filename.lower().endswith('.x3dz'):
986                         filename = '.'.join(filename.split('.')[:-1]) + '.x3dz'
987         else:
988                 if not filename.lower().endswith('.x3d'):
989                         filename = '.'.join(filename.split('.')[:-1]) + '.x3d'
990         
991         
992         scene = Blender.Scene.GetCurrent()
993         world = scene.world
994         alltextures = Blender.Texture.Get()
995
996         wrlexport=x3d_class(filename)
997         wrlexport.export(\
998                 scene,\
999                 world,\
1000                 alltextures,\
1001                 \
1002                 EXPORT_APPLY_MODIFIERS = EXPORT_APPLY_MODIFIERS,\
1003                 EXPORT_TRI = EXPORT_TRI,\
1004                 )
1005
1006
1007 def x3d_export_ui(filename):
1008         if not filename.endswith(extension):
1009                 filename += extension
1010         #if _safeOverwrite and sys.exists(filename):
1011         #       result = Draw.PupMenu("File Already Exists, Overwrite?%t|Yes%x1|No%x0")
1012         #if(result != 1):
1013         #       return
1014         
1015         # Get user options
1016         EXPORT_APPLY_MODIFIERS = Draw.Create(1)
1017         EXPORT_TRI = Draw.Create(0)
1018         EXPORT_GZIP = Draw.Create( filename.lower().endswith('.x3dz') )
1019         
1020         # Get USER Options
1021         pup_block = [\
1022         ('Apply Modifiers', EXPORT_APPLY_MODIFIERS, 'Use transformed mesh data from each object.'),\
1023         ('Triangulate', EXPORT_TRI, 'Triangulate quads.'),\
1024         ('Compress', EXPORT_GZIP, 'GZip the resulting file, requires a full python install'),\
1025         ]
1026
1027         if not Draw.PupBlock('Export...', pup_block):
1028                 return
1029
1030         Blender.Window.EditMode(0)
1031         Blender.Window.WaitCursor(1)
1032         
1033         x3d_export(filename,\
1034                 EXPORT_APPLY_MODIFIERS = EXPORT_APPLY_MODIFIERS.val,\
1035                 EXPORT_TRI = EXPORT_TRI.val,\
1036                 EXPORT_GZIP = EXPORT_GZIP.val\
1037         )
1038         
1039         Blender.Window.WaitCursor(0)
1040
1041
1042
1043 #########################################################
1044 # main routine
1045 #########################################################
1046
1047
1048 if __name__ == '__main__':
1049         Blender.Window.FileSelector(x3d_export_ui,"Export X3D", Blender.Get('filename').replace('.blend', '.x3d'))
1050
1051