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 = self.getNumMaterials(me)
561 # Vertex and Face colors trump materials and image textures
562 if (self.facecolors or self.vcolors):
565 self.writeShape(ob, me, 0, None)
567 self.writeShape(ob, me, -1, None)
569 self.writeShape(ob, me, -1, None)
570 # Do meshes with materials, possible with image textures
572 for matnum in range(len(maters)):
576 images = self.getImages(me, matnum)
579 self.writeShape(ob, me, matnum, image)
581 self.writeShape(ob, me, matnum, None)
583 self.writeShape(ob, me, matnum, None)
586 images = self.getImages(me, -1)
589 self.writeShape(ob, me, -1, image)
591 self.writeShape(ob, me, -1, None)
593 self.writeShape(ob, me, -1, None)
596 self.writeIndented("]\n", -1)
597 self.writeIndented("}\n", -1)
599 def getImages(self, me, matnum):
602 for face in me.faces:
603 if (matnum == -1) or (face.mat == matnum):
605 imName = self.cleanStr(face.image.name)
606 if not imageNames.has_key(imName):
607 images.append(face.image)
611 def getNumMaterials(self, me):
612 # Oh silly Blender, why do you sometimes have 'None' as
613 # a member of the me.materials array?
615 for mat in me.materials:
620 def writeCoordinates(self, me, meshName):
621 coordName = "coord_%s" % (meshName)
622 # look up coord name, use it if available
623 if self.coordNames.has_key(coordName):
624 self.writeIndented("coord USE %s\n" % coordName, 0)
625 self.coordNames[coordName]+=1
628 self.coordNames[coordName]=1
631 self.writeIndented("coord DEF %s Coordinate {\n" % (coordName), 1)
632 self.writeIndented("point [\n", 1)
633 meshVertexList = me.verts
635 for vertex in meshVertexList:
636 vrmlvert = blenvert = Mathutils.Vector(vertex.co)
637 if export_rotate_z_to_y.val:
638 vrmlvert = M_blen2vrml * vrmlvert
639 self.writeUnindented("%s %s %s\n " % \
643 self.writeIndented("]\n", -1)
644 self.writeIndented("}\n", -1)
645 self.writeIndented("\n")
647 def writeShape(self, ob, me, matnum, image):
648 # Note: at this point it is assumed for matnum!=-1 that the
649 # material in me.materials[matnum] is not equal to 'None'.
650 # Such validation should be performed by the function that
652 self.writeIndented("Shape {\n",1)
654 self.writeIndented("appearance Appearance {\n", 1)
656 mater = me.materials[matnum]
657 self.writeMaterial(mater, self.cleanStr(mater.name,''))
658 if (mater.mode & Blender.Material.Modes['TEXFACE']):
660 self.writeImageTexture(image.name, image.filename)
663 self.writeImageTexture(image.name, image.filename)
665 self.writeIndented("}\n", -1)
667 self.writeGeometry(ob, me, matnum, image)
669 self.writeIndented("}\n", -1)
671 def writeGeometry(self, ob, me, matnum, image):
673 #-- IndexedFaceSet or IndexedLineSet
674 meshName = self.cleanStr(me.name)
676 # check if object is wireframe only
678 ifStyle="IndexedLineSet"
680 # user selected BOUNDS=1, SOLID=3, SHARED=4, or TEXTURE=5
681 ifStyle="IndexedFaceSet"
683 self.writeIndented("geometry %s {\n" % ifStyle, 1)
685 if self.twosided == 1:
686 self.writeIndented("solid FALSE\n")
688 self.writeIndented("solid TRUE\n")
690 self.writeCoordinates(me, meshName)
691 self.writeCoordIndex(me, meshName, matnum, image)
692 self.writeTextureCoordinates(me, meshName, matnum, image)
694 self.writeFaceColors(me)
696 self.writeVertexColors(me)
697 self.writeIndented("}\n", -1)
699 def writeCoordIndex(self, me, meshName, matnum, image):
700 meshVertexList = me.verts
701 self.writeIndented("coordIndex [\n", 1)
703 for face in me.faces:
704 if (matnum == -1) or (face.mat == matnum):
705 if (image == None) or (face.image == image):
709 cordStr = cordStr + "%s " % indx
710 self.writeUnindented(cordStr + "-1, \n")
711 self.writeIndented("]\n", -1)
713 def writeTextureCoordinates(self, me, meshName, matnum, image):
721 for face in me.faces:
724 if (matnum == -1) or (face.mat == matnum):
725 if (face.image == image):
726 for i in range(len(face.verts)):
728 indexStr += "%s " % (j)
729 coordStr += "%s %s, " % \
730 (round(uv[0], self.tp), \
731 round(uv[1], self.tp))
734 texIndexList.append(indexStr)
735 texCoordList.append(coordStr)
737 self.writeIndented("texCoord TextureCoordinate {\n", 1)
738 self.writeIndented("point [\n", 1)
739 for coord in texCoordList:
740 self.writeUnindented("%s\n" % (coord))
741 self.writeIndented("]\n", -1)
742 self.writeIndented("}\n", -1)
744 self.writeIndented("texCoordIndex [\n", 1)
745 for ind in texIndexList:
746 self.writeUnindented("%s\n" % (ind))
747 self.writeIndented("]\n", -1)
749 def writeFaceColors(self, me):
750 self.writeIndented("colorPerVertex FALSE\n")
751 self.writeIndented("color Color {\n",1)
752 self.writeIndented("color [\n", 1)
754 for face in me.faces:
757 if self.verbose >= 2:
758 print "Debug: face.col r=%d g=%d b=%d" % (c.r, c.g, c.b)
760 aColor = self.rgbToFS(c)
761 self.writeUnindented("%s,\n" % aColor)
762 self.writeIndented("]\n",-1)
763 self.writeIndented("}\n",-1)
765 def writeVertexColors(self, me):
766 self.writeIndented("colorPerVertex TRUE\n")
767 self.writeIndented("color Color {\n",1)
768 self.writeIndented("color [\n\t\t\t\t\t\t", 1)
770 cols = [None] * len(me.verts)
772 for face in me.faces:
773 for vind in range(len(face.v)):
774 vertex = face.v[vind]
777 cols[i] = face.col[vind]
779 for i in range(len(me.verts)):
780 aColor = self.rgbToFS(cols[i])
781 self.writeUnindented("%s\n" % aColor)
783 self.writeIndented("\n", 0)
784 self.writeIndented("]\n",-1)
785 self.writeIndented("}\n",-1)
787 def writeMaterial(self, mat, matName):
788 # look up material name, use it if available
789 if self.matNames.has_key(matName):
790 self.writeIndented("material USE MA_%s\n" % matName)
791 self.matNames[matName]+=1
794 self.matNames[matName]=1
797 diffuseR, diffuseG, diffuseB = \
798 mat.rgbCol[0], mat.rgbCol[1],mat.rgbCol[2]
800 ambi = world.getAmb()
801 ambi0, ambi1, ambi2 = (ambi[0]*mat.amb) * 2, \
802 (ambi[1]*mat.amb) * 2, \
803 (ambi[2]*mat.amb) * 2
805 ambi0, ambi1, ambi2 = 0, 0, 0
806 emisR, emisG, emisB = (diffuseR*mat.emit+ambi0) / 2, \
807 (diffuseG*mat.emit+ambi1) / 2, \
808 (diffuseB*mat.emit+ambi2) / 2
810 shininess = mat.hard/512.0
811 specR = (mat.specCol[0]+0.001) / (1.25/(mat.getSpec()+0.001))
812 specG = (mat.specCol[1]+0.001) / (1.25/(mat.getSpec()+0.001))
813 specB = (mat.specCol[2]+0.001) / (1.25/(mat.getSpec()+0.001))
814 transp = 1 - mat.alpha
815 matFlags = mat.getMode()
816 if matFlags & Blender.Material.Modes['SHADELESS']:
819 specR = emitR = diffuseR
820 specG = emitG = diffuseG
821 specB = emitB = diffuseB
822 self.writeIndented("material DEF MA_%s Material {\n" % matName, 1)
823 self.writeIndented("diffuseColor %s %s %s\n" % \
824 (round(diffuseR,self.cp), \
825 round(diffuseG,self.cp), \
826 round(diffuseB,self.cp)))
827 self.writeIndented("ambientIntensity %s\n" % \
828 (round(ambient,self.cp)))
829 self.writeIndented("specularColor %s %s %s\n" % \
830 (round(specR,self.cp), \
831 round(specG,self.cp), \
832 round(specB,self.cp)))
833 self.writeIndented("emissiveColor %s %s %s\n" % \
834 (round(emisR,self.cp), \
835 round(emisG,self.cp), \
836 round(emisB,self.cp)))
837 self.writeIndented("shininess %s\n" % (round(shininess,self.cp)))
838 self.writeIndented("transparency %s\n" % (round(transp,self.cp)))
839 self.writeIndented("}\n",-1)
841 def writeImageTexture(self, name, filename):
842 if self.texNames.has_key(name):
843 self.writeIndented("texture USE %s\n" % self.cleanStr(name))
844 self.texNames[name] += 1
847 self.writeIndented("texture DEF %s ImageTexture {\n" % \
848 self.cleanStr(name), 1)
849 self.writeIndented('url "%s"\n' % \
850 filename.split("\\")[-1].split("/")[-1])
851 self.writeIndented("}\n",-1)
852 self.texNames[name] = 1
854 def writeBackground(self):
856 worldname = world.getName()
859 blending = world.getSkytype()
861 grd0, grd1, grd2 = grd[0], grd[1], grd[2]
863 sky0, sky1, sky2 = sky[0], sky[1], sky[2]
864 mix0, mix1, mix2 = grd[0]+sky[0], grd[1]+sky[1], grd[2]+sky[2]
865 mix0, mix1, mix2 = mix0/2, mix1/2, mix2/2
866 if worldname in self.namesStandard:
867 self.writeIndented("Background {\n",1)
869 self.writeIndented("DEF %s Background {\n" % \
870 self.secureName(worldname),1)
871 # No Skytype - just Hor color
873 self.writeIndented("groundColor %s %s %s\n" % \
874 (round(grd0,self.cp), \
875 round(grd1,self.cp), \
876 round(grd2,self.cp)))
877 self.writeIndented("skyColor %s %s %s\n" % \
878 (round(grd0,self.cp), \
879 round(grd1,self.cp), \
880 round(grd2,self.cp)))
883 self.writeIndented("groundColor [ %s %s %s, " % \
884 (round(grd0,self.cp), \
885 round(grd1,self.cp), \
886 round(grd2,self.cp)))
887 self.writeIndented("%s %s %s ]\n" % \
888 (round(mix0,self.cp), \
889 round(mix1,self.cp), \
890 round(mix2,self.cp)))
891 self.writeIndented("groundAngle [ 1.57, 1.57 ]\n")
892 self.writeIndented("skyColor [ %s %s %s, " % \
893 (round(sky0,self.cp), \
894 round(sky1,self.cp), \
895 round(sky2,self.cp)))
896 self.writeIndented("%s %s %s ]\n" % \
897 (round(mix0,self.cp), \
898 round(mix1,self.cp), \
899 round(mix2,self.cp)))
900 self.writeIndented("skyAngle [ 1.57, 1.57 ]\n")
901 # Blend+Real Gradient Inverse
903 self.writeIndented("groundColor [ %s %s %s, " % \
904 (round(sky0,self.cp), \
905 round(sky1,self.cp), \
906 round(sky2,self.cp)))
907 self.writeIndented("%s %s %s ]\n" % \
908 (round(mix0,self.cp), \
909 round(mix1,self.cp), \
910 round(mix2,self.cp)))
911 self.writeIndented("groundAngle [ 1.57, 1.57 ]\n")
912 self.writeIndented("skyColor [ %s %s %s, " % \
913 (round(grd0,self.cp), \
914 round(grd1,self.cp), \
915 round(grd2,self.cp)))
916 self.writeIndented("%s %s %s ]\n" % \
917 (round(mix0,self.cp), \
918 round(mix1,self.cp), \
919 round(mix2,self.cp)))
920 self.writeIndented("skyAngle [ 1.57, 1.57 ]\n")
921 # Paper - just Zen Color
923 self.writeIndented("groundColor %s %s %s\n" % \
924 (round(sky0,self.cp), \
925 round(sky1,self.cp), \
926 round(sky2,self.cp)))
927 self.writeIndented("skyColor %s %s %s\n" % \
928 (round(sky0,self.cp), \
929 round(sky1,self.cp), \
930 round(sky2,self.cp)))
931 # Blend+Real+Paper - komplex gradient
933 self.writeIndented("groundColor [ %s %s %s, " % \
934 (round(sky0,self.cp), \
935 round(sky1,self.cp), \
936 round(sky2,self.cp)))
937 self.writeIndented("%s %s %s ]\n" % \
938 (round(grd0,self.cp), \
939 round(grd1,self.cp), \
940 round(grd2,self.cp)))
941 self.writeIndented("groundAngle [ 1.57, 1.57 ]\n")
942 self.writeIndented("skyColor [ %s %s %s, " % \
943 (round(sky0,self.cp), \
944 round(sky1,self.cp), \
945 round(sky2,self.cp)))
946 self.writeIndented("%s %s %s ]\n" % \
947 (round(grd0,self.cp),
949 round(grd2,self.cp)))
950 self.writeIndented("skyAngle [ 1.57, 1.57 ]\n")
951 # Any Other two colors
953 self.writeIndented("groundColor %s %s %s\n" % \
954 (round(grd0,self.cp), \
955 round(grd1,self.cp), \
956 round(grd2,self.cp)))
957 self.writeIndented("skyColor %s %s %s\n" % \
958 (round(sky0,self.cp), \
959 round(sky1,self.cp), \
960 round(sky2,self.cp)))
961 alltexture = len(worldmat)
962 for i in xrange(alltexture):
963 namemat = worldmat[i].getName()
964 pic = worldmat[i].getImage()
967 pic_path= pic.filename.split('\\')[-1].split('/')[-1]
968 if namemat == "back":
969 self.writeIndented('backUrl "%s"\n' % pic_path)
970 elif namemat == "bottom":
971 self.writeIndented('bottomUrl "%s"\n' % pic_path)
972 elif namemat == "front":
973 self.writeIndented('frontUrl "%s"\n' % pic_path)
974 elif namemat == "left":
975 self.writeIndented('leftUrl "%s"\n' % pic_path)
976 elif namemat == "right":
977 self.writeIndented('rightUrl "%s"\n' % pic_path)
978 elif namemat == "top":
979 self.writeIndented('topUrl "%s"\n' % pic_path)
980 self.writeIndented("}",-1)
981 self.writeIndented("\n\n")
983 def writeLamp(self, ob):
985 laType = la.getType()
987 if laType == Lamp.Types.Lamp:
988 self.writePointLight(ob, la)
989 elif laType == Lamp.Types.Spot:
990 self.writeSpotLight(ob, la)
991 elif laType == Lamp.Types.Sun:
992 self.writeDirectionalLight(ob, la)
994 self.writeDirectionalLight(ob, la)
996 def writeObject(self, ob):
998 obname = self.cleanStr(ob.name)
1002 except AttributeError:
1003 print "Error: Unable to get type info for %s" % obname
1006 if self.verbose >= 1:
1007 print "++ Writing %s object %s (Blender name: %s)\n" % \
1008 (obtype, obname, ob.name)
1010 # Note: I am leaving empties out for now -- the original
1011 # script does some really weird stuff with empties
1012 if ( (obtype != "Camera") and \
1013 (obtype != "Mesh") and \
1014 (obtype != "Lamp") ):
1015 print "Info: Ignoring [%s], object type [%s] " \
1016 "not handle yet" % (obname, obtype)
1019 ob_matrix = Mathutils.Matrix(ob.getMatrix('worldspace'))
1020 if export_rotate_z_to_y.val:
1021 matrix = M_blen2vrml * ob_matrix * M_vrml2blen
1024 e = matrix.rotationPart().toEuler()
1026 v = matrix.translationPart()
1027 (axis, angle) = self.eulToVecRot(self.deg2rad(e.x), \
1028 self.deg2rad(e.y), \
1031 mrot = e.toMatrix().resize4x4()
1035 print "Warning: %s has degenerate transformation!" % (obname)
1038 diag = matrix * mrot
1043 if self.verbose >= 1:
1044 print " Transformation:\n" \
1045 " loc: %f %f %f\n" \
1046 " size: %f %f %f\n" \
1047 " Rot: (%f %f %f), %f\n" % \
1049 sizeX, sizeY, sizeZ, \
1050 axis[0], axis[1], axis[2], angle)
1052 self.writeIndented("DEF OB_%s Transform {\n" % (obname), 1)
1053 self.writeIndented("translation %f %f %f\n" % \
1056 self.writeIndented("rotation %f %f %f %f\n" % \
1057 (axis[0],axis[1],axis[2],angle) )
1059 self.writeIndented("scale %f %f %f\n" % \
1060 (sizeX, sizeY, sizeZ) )
1062 self.writeIndented("children [\n", 1)
1064 self.writeObData(ob)
1066 self.writeIndented("]\n", -1) # end object
1067 self.writeIndented("}\n", -1) # end object
1069 def writeObData(self, ob):
1071 obtype = ob.getType()
1073 if obtype == "Camera":
1074 self.writeViewpoint(ob)
1075 elif obtype == "Mesh":
1077 elif obtype == "Lamp":
1079 elif obtype == "Empty":
1083 ##########################################################
1085 ##########################################################
1087 def export(self, scene, world, worldmat):
1088 print "Info: starting VRML97 export to " + self.filename + "..."
1091 self.writeNavigationInfo(scene)
1092 self.writeBackground()
1096 if export_selection_only.val:
1097 allObj = list(scene.objects.context)
1099 allObj = list(scene.objects)
1102 for thisObj in allObj:
1103 self.writeObject(thisObj)
1105 if not export_selection_only.val:
1109 ##########################################################
1111 ##########################################################
1118 print "Info: finished VRML97 export to %s\n" % self.filename
1120 def cleanStr(self, name, prefix='rsvd_'):
1121 """cleanStr(name,prefix) - try to create a valid VRML DEF \
1122 name from object name"""
1125 if len(newName) == 0:
1127 return "%s%d" % (prefix, self.nNodeID)
1129 if newName in self.namesReserved:
1130 newName='%s%s' % (prefix,newName)
1132 if newName[0].isdigit():
1133 newName='%s%s' % ('_',newName)
1135 for bad in (' ','"','#',"'",',','.','[','\\',']','{','}'):
1136 newName=newName.replace(bad,'_')
1139 def rgbToFS(self, c):
1141 (round(c.r/255.0,self.cp), \
1142 round(c.g/255.0,self.cp), \
1143 round(c.b/255.0,self.cp))
1146 def rad2deg(self, v):
1147 return round(v*180.0/math.pi,4)
1149 def deg2rad(self, v):
1150 return (v*math.pi)/180.0;
1152 def eulToVecRot(self, RotX, RotY, RotZ):
1174 angle = 2 * math.acos(q0)
1175 if (math.fabs(angle) < 0.000001):
1176 axis = [1.0, 0.0, 0.0]
1178 sphi = 1.0/math.sqrt(1.0 - (q0*q0))
1179 axis = [q1 * sphi, q2 * sphi, q3 * sphi]
1181 a = Mathutils.Vector(axis)
1183 return ([a.x, a.y, a.z], angle)
1186 # For writing well formed VRML code
1187 #----------------------------------
1188 def writeIndented(self, s, inc=0):
1190 self.indentLevel = self.indentLevel + inc
1192 self.file.write( self.indentLevel*"\t" + s)
1195 self.indentLevel = self.indentLevel + inc
1197 # Sometimes better to not have too many
1198 # tab characters in a long list, for file size
1199 #----------------------------------
1200 def writeUnindented(self, s):
1203 ##########################################################
1204 # Callbacks, needed before Main
1205 ##########################################################
1207 def select_file(filename):
1208 if sys.exists(filename) and _safeOverwrite:
1210 Draw.PupMenu("File Already Exists, Overwrite?%t|Yes%x1|No%x0")
1214 if not filename.endswith(extension):
1215 filename += extension
1217 wrlexport=VRML2Export(filename)
1218 wrlexport.export(scene, world, worldmat)
1220 #########################################################
1221 # UI and Registry utilities
1222 #########################################################
1224 export_selection_only = Draw.Create(0)
1225 export_rotate_z_to_y = Draw.Create(1)
1226 export_compressed = Draw.Create(0)
1228 def save_to_registry():
1230 d['selection_only'] = export_selection_only.val
1231 d['rotate_z_to_y'] = export_rotate_z_to_y.val
1232 d['compressed'] = export_compressed.val
1233 Registry.SetKey('vrml97_export', d, True)
1235 def load_from_registry():
1236 d = Registry.GetKey('vrml97_export', True)
1239 export_selection_only.val = d['selection_only']
1240 export_rotate_z_to_y.val = d['rotate_z_to_y']
1241 export_compressed.val = d['compressed']
1242 except: save_to_registry() # If data is not valid, rewrite it.
1246 ('Selection Only', export_selection_only, 'Only export objects in visible selection. Else export whole scene.'),
1247 ('Rotate +Z to +Y', export_rotate_z_to_y, 'Rotate such that +Z axis (Blender up) becomes +Y (VRML up).'),
1248 ('Compress', export_compressed, 'Generate a .wrz file (normal VRML compressed by gzip).')
1250 return Draw.PupBlock('Export VRML 97...', pup_block)
1252 #########################################################
1254 #########################################################
1256 load_from_registry()
1258 # Note that show_popup must be done before Blender.Window.FileSelector,
1259 # because export_compressed affects the suggested extension of resulting
1264 if export_compressed.val:
1269 Blender.Window.FileSelector(select_file, "Export VRML97", \
1270 sys.makename(ext=extension))