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.
25
# $Id: vrml97_export.py 16949 2008-10-06 17:11:10Z hos $
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 xrange(allinlines):
210
nameinline=inlines[i].getName()
211
if (nameinline not in self.namesStandard) and (i > 0):
212
self.writeIndented("DEF %s Inline {\n" % \
213
(self.cleanStr(nameinline)), 1)
214
nameinline = nameinline+".wrl"
215
self.writeIndented("url \"%s\" \n" % nameinline)
216
self.writeIndented("}\n", -1)
217
self.writeIndented("\n")
219
def writeScript(self):
220
textEditor = Blender.Text.Get()
221
alltext = len(textEditor)
222
for i in xrange(alltext):
223
nametext = textEditor[i].getName()
224
nlines = textEditor[i].getNLines()
225
if (self.proto == 1):
226
if (nametext == "proto" or nametext == "proto.js" or \
227
nametext == "proto.txt") and (nlines != None):
228
nalllines = len(textEditor[i].asLines())
229
alllines = textEditor[i].asLines()
230
for j in xrange(nalllines):
231
self.writeIndented(alllines[j] + "\n")
232
elif (self.proto == 0):
233
if (nametext == "route" or nametext == "route.js" or \
234
nametext == "route.txt") and (nlines != None):
235
nalllines = len(textEditor[i].asLines())
236
alllines = textEditor[i].asLines()
237
for j in xrange(nalllines):
238
self.writeIndented(alllines[j] + "\n")
239
self.writeIndented("\n")
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 xrange(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 xrange(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 xrange(len(face.v)):
787
vertex = face.v[vind]
790
cols[i] = face.col[vind]
792
for i in xrange(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))