2 """ Registration info for Blender menus:
3 Name: 'VRML97 (.wrl)...'
6 Tooltip: 'Export to VRML97 file (.wrl)'
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"
16 This script exports to VRML97 format.
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.
27 #------------------------------------------------------------------------
28 # VRML97 exporter for blender 2.36 or above
30 # ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
46 # ***** END GPL LICENCE BLOCK *****
49 ####################################
50 # Library dependancies
51 ####################################
54 from Blender import Object, Mesh, Lamp, Draw, BGL, \
55 Image, Text, sys, Mathutils, Registry
56 from Blender.Scene import Render
60 ####################################
62 ####################################
64 scene = Blender.Scene.getCurrent()
65 world = Blender.World.GetCurrent()
66 worldmat = Blender.Texture.Get()
67 filename = Blender.Get('filename')
71 # Matrices below are used only when export_rotate_z_to_y.val:
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], \
81 M_vrml2blen = Mathutils.Matrix([1,0,0,0], \
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
100 if not hasattr(Blender.Object,'DrawTypes'):
101 Blender.Object.DrawTypes = DrawTypes()
103 ##########################################################
104 # Functions for writing output file
105 ##########################################################
109 def __init__(self, filename):
110 #--- public you can change these ---
122 # level of verbosity in console 0-none, 1-some, 2-most
124 rt = Blender.Get('rt')
134 # decimals for material color values 0.000 - 1.000
136 # decimals for vertex coordinate values 0.000 - n.000
138 # decimals for texture coordinate values 0.000 - 1.000
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")
151 self.namesReserved=[ "Anchor", "Appearance", "AudioClip",
152 "Background","Billboard", "Box",
153 "Collision", "Color", "ColorInterpolator",
154 "Cone", "Coordinate",
155 "CoordinateInterpolator", "Cylinder",
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",
187 self.namesFog=[ "", "LINEAR"," EXPONENTIAL", "" ]
189 ##########################################################
190 # Writing nodes routines
191 ##########################################################
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")
203 def writeInline(self):
204 inlines = Blender.Scene.Get()
205 allinlines = len(inlines)
206 if scene != inlines[0]:
209 for i in range(allinlines):
210 nameinline=inlines[i].getName()
211 if (nameinline not in self.namesStandard) and (i > 0):
212 self.writeIndented("DEF %s Inline {\n" % \
213 (self.cleanStr(nameinline)), 1)
214 nameinline = nameinline+".wrl"
215 self.writeIndented("url \"%s\" \n" % nameinline)
216 self.writeIndented("}\n", -1)
217 self.writeIndented("\n")
219 def writeScript(self):
220 textEditor = Blender.Text.Get()
221 alltext = len(textEditor)
222 for i in range(alltext):
223 nametext = textEditor[i].getName()
224 nlines = textEditor[i].getNLines()
225 if (self.proto == 1):
226 if (nametext == "proto" or nametext == "proto.js" or \
227 nametext == "proto.txt") and (nlines != None):
228 nalllines = len(textEditor[i].asLines())
229 alllines = textEditor[i].asLines()
230 for j in range(nalllines):
231 self.writeIndented(alllines[j] + "\n")
232 elif (self.proto == 0):
233 if (nametext == "route" or nametext == "route.js" or \
234 nametext == "route.txt") and (nlines != None):
235 nalllines = len(textEditor[i].asLines())
236 alllines = textEditor[i].asLines()
237 for j in range(nalllines):
238 self.writeIndented(alllines[j] + "\n")
239 self.writeIndented("\n")
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)
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")
263 mtype = world.getMistype()
264 mparam = world.getMist()
266 grd0, grd1, grd2 = grd[0], grd[1], grd[2]
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")
283 def writeNavigationInfo(self, scene):
285 allObj = list(scene.objects)
288 for thisObj in allObj:
290 if objType == "Camera":
291 vislimit = thisObj.data.getClipEnd()
292 elif objType == "Lamp":
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")
303 def writeSpotLight(self, object, lamp):
304 # Note: location and orientation are handled by the
305 # transform node above this object
307 ambi = world.getAmb()
308 ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5
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
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")
338 def writeDirectionalLight(self, object, lamp):
339 # Note: location and orientation are handled by the
340 # transform node above this object
342 ambi = world.getAmb()
343 ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5
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")
364 def writePointLight(self, object, lamp):
365 # Note: location is at origin because parent transform node
368 ambi = world.getAmb()
369 ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5
373 om = object.getMatrix()
374 intensity=min(lamp.energy/1.75,1.0)
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")
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:
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")
404 def secureName(self, name):
405 name = name + str(self.nodeID)
408 newname = "_" + str(self.nodeID)
409 return "%s" % (newname)
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)
421 return "%s" % (newname)
423 def classifyMesh(self, me, ob):
432 for face in me.faces:
433 if (face.mode & Mesh.FaceModes['HALO']):
435 if (face.mode & Mesh.FaceModes['BILLBOARD']):
437 if (face.mode & Mesh.FaceModes['OBCOL']):
439 if (face.mode & Mesh.FaceModes['SHAREDCOL']):
441 if (face.mode & Mesh.FaceModes['TILES']):
443 if not (face.mode & Mesh.FaceModes['DYNAMIC']):
445 if (face.mode & Mesh.FaceModes['TWOSIDE']):
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]
455 if (mat.mode & Blender.Material.Modes['VCOL_PAINT']):
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
466 ### The next few functions nest Collision/Billboard/Halo nodes.
467 ### For real mesh data export, jump down to writeMeshData()
469 def writeMesh(self, ob, normals = 0):
471 imageMap={} # set of used images
472 sided={} # 'one':cnt , 'two':cnt
473 vColors={} # 'multi':1
475 if (len(ob.modifiers) > 0):
477 me.getFromObject(ob.name)
478 # Careful with the name, the temporary mesh may
479 # reuse the default name for other meshes. So we
481 me.name = "MOD_%s" % (ob.name)
483 me = ob.getData(mesh = 1)
485 self.classifyMesh(me, ob)
488 self.writeCollisionMesh(me, ob, normals)
491 self.writeRegularMesh(me, ob, normals)
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")
499 self.writeRegularMesh(me, ob, normals)
501 self.writeIndented("]\n", -1)
502 self.writeIndented("}\n", -1)
504 def writeRegularMesh(self, me, ob, normals = 0):
506 self.writeBillboardMesh(me, ob, normals)
507 elif (self.halonode):
508 self.writeHaloMesh(me, ob, normals)
510 self.writeMeshData(me, ob, normals)
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")
517 self.writeMeshData(me, ob, normals)
519 self.writeIndented("]\n", -1)
520 self.writeIndented("}\n", -1)
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")
527 self.writeMeshData(me, ob, normals)
529 self.writeIndented("]\n", -1)
530 self.writeIndented("}\n", -1)
533 ### Here is where real mesh data is written
535 def writeMeshData(self, me, ob, normals = 0):
536 meshName = self.cleanStr(me.name)
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" % \
545 self.meshNames[meshName]=1
547 if (self.verbose == 1):
548 print " Writing Mesh %s (Blender mesh: %s)\n" % \
552 self.writeIndented("DEF ME_%s Group {\n" % meshName,1)
553 self.writeIndented("children [\n", 1)
558 maters = me.materials
559 nummats = len(me.materials)
561 # Vertex and Face colors trump materials and image textures
562 if (self.facecolors or self.vcolors):
564 self.writeShape(ob, me, 0, None)
566 self.writeShape(ob, me, -1, None)
568 # Do meshes with materials, possibly with image textures
570 for matnum in range(len(maters)):
573 images = self.getImages(me, matnum)
576 self.writeShape(ob, me, matnum, image)
578 self.writeShape(ob, me, matnum, None)
580 self.writeShape(ob, me, matnum, None)
583 images = self.getImages(me, -1)
586 self.writeShape(ob, me, -1, image)
588 self.writeShape(ob, me, -1, None)
590 self.writeShape(ob, me, -1, None)
593 self.writeIndented("]\n", -1)
594 self.writeIndented("}\n", -1)
596 def getImages(self, me, matnum):
599 for face in me.faces:
600 if (matnum == -1) or (face.mat == matnum):
602 imName = self.cleanStr(face.image.name)
603 if not imageNames.has_key(imName):
604 images.append(face.image)
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
616 self.coordNames[coordName]=1
619 self.writeIndented("coord DEF %s Coordinate {\n" % (coordName), 1)
620 self.writeIndented("point [\n", 1)
621 meshVertexList = me.verts
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 " % \
631 self.writeIndented("]\n", -1)
632 self.writeIndented("}\n", -1)
633 self.writeIndented("\n")
635 def testShape(self, ob, me, matnum, image):
636 if ( (matnum == -1) and (image == None) ):
637 if ( len(me.faces) > 0 ):
639 # Check if any faces the material or image
640 for face in me.faces:
642 if (f.image == image):
644 elif (image == None):
645 if (face.mat == matnum):
648 if ((face.image == image) and (face.mat == matnum)):
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
657 if ( not self.testShape(ob, me, matnum, image) ):
660 self.writeIndented("Shape {\n",1)
662 self.writeIndented("appearance Appearance {\n", 1)
664 mater = me.materials[matnum]
666 self.writeMaterial(mater, self.cleanStr(mater.name,''))
667 if (mater.mode & Blender.Material.Modes['TEXFACE']):
669 self.writeImageTexture(image.name, image.filename)
671 self.writeDefaultMaterial()
674 self.writeImageTexture(image.name, image.filename)
676 self.writeIndented("}\n", -1)
678 self.writeGeometry(ob, me, matnum, image)
680 self.writeIndented("}\n", -1)
684 def writeGeometry(self, ob, me, matnum, image):
686 #-- IndexedFaceSet or IndexedLineSet
687 meshName = self.cleanStr(me.name)
689 # check if object is wireframe only
691 ifStyle="IndexedLineSet"
693 # user selected BOUNDS=1, SOLID=3, SHARED=4, or TEXTURE=5
694 ifStyle="IndexedFaceSet"
696 self.writeIndented("geometry %s {\n" % ifStyle, 1)
698 if self.twosided == 1:
699 self.writeIndented("solid FALSE\n")
701 self.writeIndented("solid TRUE\n")
703 self.writeCoordinates(me, meshName)
704 self.writeCoordIndex(me, meshName, matnum, image)
705 self.writeTextureCoordinates(me, meshName, matnum, image)
707 self.writeFaceColors(me)
709 self.writeVertexColors(me)
710 self.writeIndented("}\n", -1)
712 def writeCoordIndex(self, me, meshName, matnum, image):
713 meshVertexList = me.verts
714 self.writeIndented("coordIndex [\n", 1)
716 for face in me.faces:
717 if (matnum == -1) or (face.mat == matnum):
718 if (image == None) or (face.image == image):
722 cordStr = cordStr + "%s " % indx
723 self.writeUnindented(cordStr + "-1, \n")
724 self.writeIndented("]\n", -1)
726 def writeTextureCoordinates(self, me, meshName, matnum, image):
734 for face in me.faces:
737 if (matnum == -1) or (face.mat == matnum):
738 if (face.image == image):
739 for i in range(len(face.verts)):
741 indexStr += "%s " % (j)
742 coordStr += "%s %s, " % \
743 (round(uv[0], self.tp), \
744 round(uv[1], self.tp))
747 texIndexList.append(indexStr)
748 texCoordList.append(coordStr)
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)
757 self.writeIndented("texCoordIndex [\n", 1)
758 for ind in texIndexList:
759 self.writeUnindented("%s\n" % (ind))
760 self.writeIndented("]\n", -1)
762 def writeFaceColors(self, me):
763 self.writeIndented("colorPerVertex FALSE\n")
764 self.writeIndented("color Color {\n",1)
765 self.writeIndented("color [\n", 1)
767 for face in me.faces:
770 if self.verbose >= 2:
771 print "Debug: face.col r=%d g=%d b=%d" % (c.r, c.g, c.b)
773 aColor = self.rgbToFS(c)
774 self.writeUnindented("%s,\n" % aColor)
775 self.writeIndented("]\n",-1)
776 self.writeIndented("}\n",-1)
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)
783 cols = [None] * len(me.verts)
785 for face in me.faces:
786 for vind in range(len(face.v)):
787 vertex = face.v[vind]
790 cols[i] = face.col[vind]
792 for i in range(len(me.verts)):
793 aColor = self.rgbToFS(cols[i])
794 self.writeUnindented("%s\n" % aColor)
796 self.writeIndented("\n", 0)
797 self.writeIndented("]\n",-1)
798 self.writeIndented("}\n",-1)
800 def writeDefaultMaterial(self):
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
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)
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
824 self.matNames[matName]=1
827 diffuseR, diffuseG, diffuseB = \
828 mat.rgbCol[0], mat.rgbCol[1],mat.rgbCol[2]
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
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
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']:
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)
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
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
884 def writeBackground(self):
886 worldname = world.getName()
889 blending = world.getSkytype()
891 grd0, grd1, grd2 = grd[0], grd[1], grd[2]
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)
899 self.writeIndented("DEF %s Background {\n" % \
900 self.secureName(worldname),1)
901 # No Skytype - just Hor color
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)))
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
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
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
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),
979 round(grd2,self.cp)))
980 self.writeIndented("skyAngle [ 1.57, 1.57 ]\n")
981 # Any Other two colors
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()
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")
1013 def writeLamp(self, ob):
1015 laType = la.getType()
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)
1024 self.writeDirectionalLight(ob, la)
1026 def writeObject(self, ob):
1028 obname = self.cleanStr(ob.name)
1032 except AttributeError:
1033 print "Error: Unable to get type info for %s" % obname
1036 if self.verbose >= 1:
1037 print "++ Writing %s object %s (Blender name: %s)\n" % \
1038 (obtype, obname, ob.name)
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)
1049 ob_matrix = Mathutils.Matrix(ob.getMatrix('worldspace'))
1050 if export_rotate_z_to_y.val:
1051 matrix = M_blen2vrml * ob_matrix * M_vrml2blen
1054 e = matrix.rotationPart().toEuler()
1056 v = matrix.translationPart()
1057 (axis, angle) = self.eulToVecRot(self.deg2rad(e.x), \
1058 self.deg2rad(e.y), \
1061 mrot = e.toMatrix().resize4x4()
1065 print "Warning: %s has degenerate transformation!" % (obname)
1068 diag = matrix * mrot
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" % \
1079 sizeX, sizeY, sizeZ, \
1080 axis[0], axis[1], axis[2], angle)
1082 self.writeIndented("DEF OB_%s Transform {\n" % (obname), 1)
1083 self.writeIndented("translation %f %f %f\n" % \
1086 self.writeIndented("rotation %f %f %f %f\n" % \
1087 (axis[0],axis[1],axis[2],angle) )
1089 self.writeIndented("scale %f %f %f\n" % \
1090 (sizeX, sizeY, sizeZ) )
1092 self.writeIndented("children [\n", 1)
1094 self.writeObData(ob)
1096 self.writeIndented("]\n", -1) # end object
1097 self.writeIndented("}\n", -1) # end object
1099 def writeObData(self, ob):
1101 obtype = ob.getType()
1103 if obtype == "Camera":
1104 self.writeViewpoint(ob)
1105 elif obtype == "Mesh":
1107 elif obtype == "Lamp":
1109 elif obtype == "Empty":
1113 ##########################################################
1115 ##########################################################
1117 def export(self, scene, world, worldmat):
1118 print "Info: starting VRML97 export to " + self.filename + "..."
1121 self.writeNavigationInfo(scene)
1122 self.writeBackground()
1126 if export_selection_only.val:
1127 allObj = list(scene.objects.context)
1129 allObj = list(scene.objects)
1132 for thisObj in allObj:
1133 self.writeObject(thisObj)
1135 if not export_selection_only.val:
1139 ##########################################################
1141 ##########################################################
1148 print "Info: finished VRML97 export to %s\n" % self.filename
1150 def cleanStr(self, name, prefix='rsvd_'):
1151 """cleanStr(name,prefix) - try to create a valid VRML DEF \
1152 name from object name"""
1155 if len(newName) == 0:
1157 return "%s%d" % (prefix, self.nNodeID)
1159 if newName in self.namesReserved:
1160 newName='%s%s' % (prefix,newName)
1162 if newName[0].isdigit():
1163 newName='%s%s' % ('_',newName)
1165 for bad in (' ','"','#',"'",',','.','[','\\',']','{','}'):
1166 newName=newName.replace(bad,'_')
1169 def rgbToFS(self, c):
1171 (round(c.r/255.0,self.cp), \
1172 round(c.g/255.0,self.cp), \
1173 round(c.b/255.0,self.cp))
1176 def rad2deg(self, v):
1177 return round(v*180.0/math.pi,4)
1179 def deg2rad(self, v):
1180 return (v*math.pi)/180.0;
1182 def eulToVecRot(self, RotX, RotY, RotZ):
1204 angle = 2 * math.acos(q0)
1205 if (math.fabs(angle) < 0.000001):
1206 axis = [1.0, 0.0, 0.0]
1208 sphi = 1.0/math.sqrt(1.0 - (q0*q0))
1209 axis = [q1 * sphi, q2 * sphi, q3 * sphi]
1211 a = Mathutils.Vector(axis)
1213 return ([a.x, a.y, a.z], angle)
1216 # For writing well formed VRML code
1217 #----------------------------------
1218 def writeIndented(self, s, inc=0):
1220 self.indentLevel = self.indentLevel + inc
1222 self.file.write( self.indentLevel*"\t" + s)
1225 self.indentLevel = self.indentLevel + inc
1227 # Sometimes better to not have too many
1228 # tab characters in a long list, for file size
1229 #----------------------------------
1230 def writeUnindented(self, s):
1233 ##########################################################
1234 # Callbacks, needed before Main
1235 ##########################################################
1237 def select_file(filename):
1238 if sys.exists(filename) and _safeOverwrite:
1240 Draw.PupMenu("File Already Exists, Overwrite?%t|Yes%x1|No%x0")
1244 if not filename.endswith(extension):
1245 filename += extension
1247 wrlexport=VRML2Export(filename)
1248 wrlexport.export(scene, world, worldmat)
1250 #########################################################
1251 # UI and Registry utilities
1252 #########################################################
1254 export_selection_only = Draw.Create(0)
1255 export_rotate_z_to_y = Draw.Create(1)
1256 export_compressed = Draw.Create(0)
1258 def save_to_registry():
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)
1265 def load_from_registry():
1266 d = Registry.GetKey('vrml97_export', True)
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.
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).')
1280 return Draw.PupBlock('Export VRML 97...', pup_block)
1282 #########################################################
1284 #########################################################
1286 load_from_registry()
1288 # Note that show_popup must be done before Blender.Window.FileSelector,
1289 # because export_compressed affects the suggested extension of resulting
1294 if export_compressed.val:
1299 Blender.Window.FileSelector(select_file, "Export VRML97", \
1300 sys.makename(ext=extension))