blender/release/scripts/vrml97_export.py
Chris Want 5e5dc2e938 Reverting vrml97_export.py to it's state at revision 14751 because:
1) Bug #6692 is actually a feature request. While I do think that
Michalis' patch is a worthy change, we don't have time to test
this at this stage in the release cycle (and svn is supposedly
closed to new features anyways). I'm sorry Michalis, this will
have to wait until after release.

2) I have looked at bug #8814 a while ago, but I have not yet found
a good solution (and perhaps the solution is to *not* export faces 
without materials assigned). I don't have time to assess whether
revision 14774 presents a worthwhile change or not, and I have no
time to test before release. Also I don't like that the 'fix' for
#8814 is mixed with a fix for a previous faulty commit.

3) Again, another commit to a script I maintain without consulting
me first. Not cool!

Chris
2008-05-10 14:05:35 +00:00

1239 lines
36 KiB
Python

#!BPY
""" Registration info for Blender menus:
Name: 'VRML97 (.wrl)...'
Blender: 241
Group: 'Export'
Submenu: 'All Objects...' all
Submenu: 'All Objects compressed...' comp
Submenu: 'Selected Objects...' selected
Tooltip: 'Export to VRML97 file (.wrl)'
"""
__author__ = ("Rick Kimball", "Ken Miller", "Steve Matthews", "Bart")
__url__ = ["blender", "blenderartists.org",
"Author's (Rick) homepage, http://kimballsoftware.com/blender",
"Author's (Bart) homepage, http://www.neeneenee.de/vrml"]
__email__ = ["Bart, bart:neeneenee*de"]
__version__ = "2006/01/17"
__bpydoc__ = """\
This script exports to VRML97 format.
Usage:
Run this script from "File->Export" menu. A pop-up will ask whether you
want to export only selected or all relevant objects.
"""
# $Id$
#
#------------------------------------------------------------------------
# VRML97 exporter for blender 2.36 or above
#
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****
#
####################################
# Library dependancies
####################################
import Blender
from Blender import Object, Mesh, Lamp, Draw, BGL, \
Image, Text, sys, Mathutils
from Blender.Scene import Render
import math
####################################
# Global Variables
####################################
scene = Blender.Scene.getCurrent()
world = Blender.World.GetCurrent()
worldmat = Blender.Texture.Get()
filename = Blender.Get('filename')
_safeOverwrite = True
extension = ''
ARG=''
# Blender is Z up, VRML is Y up, both are right hand coordinate
# systems, so to go from Blender coords to VRML coords we rotate
# by 90 degrees around the X axis. In matrix notation, we have a
# matrix, and it's inverse, as:
M_blen2vrml = Mathutils.Matrix([1,0,0,0], \
[0,0,1,0], \
[0,-1,0,0], \
[0,0,0,1])
M_vrml2blen = Mathutils.Matrix([1,0,0,0], \
[0,0,-1,0], \
[0,1,0,0], \
[0,0,0,1])
class DrawTypes:
"""Object DrawTypes enum values
BOUNDS - draw only the bounding box of the object
WIRE - draw object as a wire frame
SOLID - draw object with flat shading
SHADED - draw object with OpenGL shading
"""
BOUNDBOX = 1
WIRE = 2
SOLID = 3
SHADED = 4
TEXTURE = 5
if not hasattr(Blender.Object,'DrawTypes'):
Blender.Object.DrawTypes = DrawTypes()
##########################################################
# Functions for writing output file
##########################################################
class VRML2Export:
def __init__(self, filename):
#--- public you can change these ---
self.wire = 0
self.proto = 1
self.facecolors = 0
self.vcolors = 0
self.billnode = 0
self.halonode = 0
self.collnode = 0
self.tilenode = 0
self.wire = 0
self.twosided = 0
# level of verbosity in console 0-none, 1-some, 2-most
try:
rt = Blender.Get('rt')
if (rt == 42):
self.verbose = 1
elif (rt == 43):
self.verbose = 2
else:
self.verbose = 0
except:
self.verbose = 0
# decimals for material color values 0.000 - 1.000
self.cp=7
# decimals for vertex coordinate values 0.000 - n.000
self.vp=7
# decimals for texture coordinate values 0.000 - 1.000
self.tp=7
#--- class private don't touch ---
self.texNames={} # dictionary of textureNames
self.matNames={} # dictionary of materialNames
self.meshNames={} # dictionary of meshNames
self.coordNames={} # dictionary of coordNames
self.indentLevel=0 # keeps track of current indenting
self.filename=filename
self.file = open(filename, "w")
self.bNav=0
self.nodeID=0
self.namesReserved=[ "Anchor", "Appearance", "AudioClip",
"Background","Billboard", "Box",
"Collision", "Color", "ColorInterpolator",
"Cone", "Coordinate",
"CoordinateInterpolator", "Cylinder",
"CylinderSensor",
"DirectionalLight",
"ElevationGrid", "Extrustion",
"Fog", "FontStyle", "Group",
"ImageTexture", "IndexedFaceSet",
"IndexedLineSet", "Inline",
"LOD", "Material", "MovieTexture",
"NavigationInfo", "Normal",
"NormalInterpolator",
"OrientationInterpolator", "PixelTexture",
"PlaneSensor", "PointLight", "PointSet",
"PositionInterpolator", "ProxmimitySensor",
"ScalarInterpolator", "Script", "Shape",
"Sound", "Sphere", "SphereSensor",
"SpotLight", "Switch", "Text",
"TextureCoordinate", "TextureTransform",
"TimeSensor", "TouchSensor", "Transform",
"Viewpoint", "VisibilitySensor", "WorldInfo" ]
self.namesStandard=[ "Empty", "Empty.000", "Empty.001",
"Empty.002", "Empty.003", "Empty.004",
"Empty.005", "Empty.006", "Empty.007",
"Empty.008", "Empty.009", "Empty.010",
"Empty.011", "Empty.012",
"Scene.001", "Scene.002", "Scene.003",
"Scene.004", "Scene.005", "Scene.06",
"Scene.013", "Scene.006", "Scene.007",
"Scene.008", "Scene.009", "Scene.010",
"Scene.011","Scene.012",
"World", "World.000", "World.001",
"World.002", "World.003", "World.004",
"World.005" ]
self.namesFog=[ "", "LINEAR"," EXPONENTIAL", "" ]
##########################################################
# Writing nodes routines
##########################################################
def writeHeader(self):
bfile = sys.expandpath(Blender.Get('filename'))
self.file.write("#VRML V2.0 utf8\n\n")
self.file.write("# This file was authored with Blender " \
"(http://www.blender.org/)\n")
self.file.write("# Blender version %s\n" % Blender.Get('version'))
self.file.write("# Blender file %s\n" % sys.basename(bfile))
self.file.write("# Exported using VRML97 exporter " \
"v1.55 (2006/01/17)\n\n")
def writeInline(self):
inlines = Blender.Scene.Get()
allinlines = len(inlines)
if scene != inlines[0]:
return
else:
for i in range(allinlines):
nameinline=inlines[i].getName()
if (nameinline not in self.namesStandard) and (i > 0):
self.writeIndented("DEF %s Inline {\n" % \
(self.cleanStr(nameinline)), 1)
nameinline = nameinline+".wrl"
self.writeIndented("url \"%s\" \n" % nameinline)
self.writeIndented("}\n", -1)
self.writeIndented("\n")
def writeScript(self):
textEditor = Blender.Text.Get()
alltext = len(textEditor)
for i in range(alltext):
nametext = textEditor[i].getName()
nlines = textEditor[i].getNLines()
if (self.proto == 1):
if (nametext == "proto" or nametext == "proto.js" or \
nametext == "proto.txt") and (nlines != None):
nalllines = len(textEditor[i].asLines())
alllines = textEditor[i].asLines()
for j in range(nalllines):
self.writeIndented(alllines[j] + "\n")
elif (self.proto == 0):
if (nametext == "route" or nametext == "route.js" or \
nametext == "route.txt") and (nlines != None):
nalllines = len(textEditor[i].asLines())
alllines = textEditor[i].asLines()
for j in range(nalllines):
self.writeIndented(alllines[j] + "\n")
self.writeIndented("\n")
def writeViewpoint(self, thisObj):
# NOTE: The transform node above this will take care of
# the position and orientation of the camera
context = scene.getRenderingContext()
ratio = float(context.imageSizeY()) / float(context.imageSizeX())
temp = ratio * 16 / thisObj.data.getLens()
lens = 2 * math.atan(temp)
lens = min(lens, math.pi)
self.writeIndented("DEF %s Viewpoint {\n" % \
(self.cleanStr(thisObj.name)), 1)
self.writeIndented('description "%s" \n' % thisObj.name)
self.writeIndented("position 0.0 0.0 0.0\n")
# Need camera to point to -y in local space to accomodate
# the transforma node above
self.writeIndented("orientation 1.0 0.0 0.0 %f\n" % (-math.pi/2.0))
self.writeIndented("fieldOfView %.3f\n" % (lens))
self.writeIndented("}\n", -1)
self.writeIndented("\n")
def writeFog(self):
if world:
mtype = world.getMistype()
mparam = world.getMist()
grd = world.getHor()
grd0, grd1, grd2 = grd[0], grd[1], grd[2]
else:
return
if (mtype == 1 or mtype == 2):
self.writeIndented("Fog {\n",1)
self.writeIndented('fogType "%s"\n' % self.namesFog[mtype])
self.writeIndented("color %s %s %s\n" % \
(round(grd0,self.cp), \
round(grd1,self.cp), \
round(grd2,self.cp)))
self.writeIndented("visibilityRange %s\n" % \
round(mparam[2],self.cp))
self.writeIndented("}\n",-1)
self.writeIndented("\n")
else:
return
def writeNavigationInfo(self, scene):
allObj = []
allObj = list(scene.objects)
headlight = "TRUE"
vislimit = 0.0
for thisObj in allObj:
objType=thisObj.type
if objType == "Camera":
vislimit = thisObj.data.getClipEnd()
elif objType == "Lamp":
headlight = "FALSE"
self.writeIndented("NavigationInfo {\n",1)
self.writeIndented("headlight %s\n" % headlight)
self.writeIndented("visibilityLimit %s\n" % \
(round(vislimit,self.cp)))
self.writeIndented("type [\"EXAMINE\", \"ANY\"]\n")
self.writeIndented("avatarSize [0.25, 1.75, 0.75]\n")
self.writeIndented("} \n",-1)
self.writeIndented(" \n")
def writeSpotLight(self, object, lamp):
# Note: location and orientation are handled by the
# transform node above this object
if world:
ambi = world.getAmb()
ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5
else:
ambi = 0
ambientIntensity = 0
# compute cutoff and beamwidth
intensity=min(lamp.energy/1.75,1.0)
beamWidth=((lamp.spotSize*math.pi)/180.0)*.37;
cutOffAngle=beamWidth*1.3
radius = lamp.dist*math.cos(beamWidth)
self.writeIndented("DEF %s SpotLight {\n" % \
self.cleanStr(object.name),1)
self.writeIndented("radius %s\n" % (round(radius,self.cp)))
self.writeIndented("ambientIntensity %s\n" % \
(round(ambientIntensity,self.cp)))
self.writeIndented("intensity %s\n" % (round(intensity,self.cp)))
self.writeIndented("color %s %s %s\n" % \
(round(lamp.col[0],self.cp), \
round(lamp.col[1],self.cp), \
round(lamp.col[2],self.cp)))
self.writeIndented("beamWidth %s\n" % (round(beamWidth,self.cp)))
self.writeIndented("cutOffAngle %s\n" % \
(round(cutOffAngle,self.cp)))
# Note: point down -Y axis, transform node above will rotate
self.writeIndented("direction 0.0 -1.0 0.0\n")
self.writeIndented("location 0.0 0.0 0.0\n")
self.writeIndented("}\n",-1)
self.writeIndented("\n")
def writeDirectionalLight(self, object, lamp):
# Note: location and orientation are handled by the
# transform node above this object
if world:
ambi = world.getAmb()
ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5
else:
ambi = 0
ambientIntensity = 0
intensity=min(lamp.energy/1.75,1.0)
self.writeIndented("DEF %s DirectionalLight {\n" % \
self.cleanStr(object.name),1)
self.writeIndented("ambientIntensity %s\n" % \
(round(ambientIntensity,self.cp)))
self.writeIndented("color %s %s %s\n" % \
(round(lamp.col[0],self.cp), \
round(lamp.col[1],self.cp), \
round(lamp.col[2],self.cp)))
self.writeIndented("intensity %s\n" % \
(round(intensity,self.cp)))
# Note: point down -Y axis, transform node above will rotate
self.writeIndented("direction 0.0 -1.0 0.0\n")
self.writeIndented("}\n",-1)
self.writeIndented("\n")
def writePointLight(self, object, lamp):
# Note: location is at origin because parent transform node
# takes care of this
if world:
ambi = world.getAmb()
ambientIntensity = ((float(ambi[0] + ambi[1] + ambi[2]))/3)/2.5
else:
ambi = 0
ambientIntensity = 0
om = object.getMatrix()
intensity=min(lamp.energy/1.75,1.0)
radius = lamp.dist
self.writeIndented("DEF %s PointLight {\n" % \
self.cleanStr(object.name),1)
self.writeIndented("ambientIntensity %s\n" % \
(round(ambientIntensity,self.cp)))
self.writeIndented("color %s %s %s\n" % \
(round(lamp.col[0],self.cp), \
round(lamp.col[1],self.cp), \
round(lamp.col[2],self.cp)))
self.writeIndented("intensity %s\n" % (round(intensity,self.cp)))
self.writeIndented("location 0.0 0.0 0.0\n")
self.writeIndented("radius %s\n" % radius )
self.writeIndented("}\n",-1)
self.writeIndented("\n")
def writeNode(self, thisObj):
# Note: location and orientation are handled by the
# transform node above this object
objectname=str(thisObj.getName())
if objectname in self.namesStandard:
return
else:
self.writeIndented("%s {\n" % objectname,1)
# May need to check that the direction is done right
self.writeIndented("direction 0.0 -1.0 0.0\n")
self.writeIndented("location 0.0 0.0 0.0\n")
self.writeIndented("}\n",-1)
self.writeIndented("\n")
def secureName(self, name):
name = name + str(self.nodeID)
self.nodeID += 1
if len(name) <= 3:
newname = "_" + str(self.nodeID)
return "%s" % (newname)
else:
for bad in ['"','#',"'",',','.','[','\\',']','{','}']:
name=name.replace(bad,'_')
if name in self.namesReserved:
newname = name[0:3] + "_" + str(self.nodeID)
return "%s" % (newname)
elif name[0].isdigit():
newname = "_" + name + str(self.nodeID)
return "%s" % (newname)
else:
newname = name
return "%s" % (newname)
def classifyMesh(self, me, ob):
self.halonode = 0
self.billnode = 0
self.facecolors = 0
self.vcolors = 0
self.tilenode = 0
self.colnode = 0
self.wire = 0
if me.faceUV:
for face in me.faces:
if (face.mode & Mesh.FaceModes['HALO']):
self.halonode = 1
if (face.mode & Mesh.FaceModes['BILLBOARD']):
self.billnode = 1
if (face.mode & Mesh.FaceModes['OBCOL']):
self.facecolors = 1
if (face.mode & Mesh.FaceModes['SHAREDCOL']):
self.vcolors = 1
if (face.mode & Mesh.FaceModes['TILES']):
self.tilenode = 1
if not (face.mode & Mesh.FaceModes['DYNAMIC']):
self.collnode = 1
if (face.mode & Mesh.FaceModes['TWOSIDE']):
self.twosided = 1
# Bit of a crufty trick, but if mesh has vertex colors
# (as a non-face property) and if first material has
# vcol paint set, we export the vertex colors
if (me.vertexColors):
if len(me.materials) > 0:
mat = me.materials[0]
if mat:
if (mat.mode & Blender.Material.Modes['VCOL_PAINT']):
self.vcolors = 1
# check if object is wireframe only
if ob.drawType == Blender.Object.DrawTypes.WIRE:
# user selected WIRE=2 on the Drawtype=Wire on (F9) Edit page
self.wire = 1
###
### The next few functions nest Collision/Billboard/Halo nodes.
### For real mesh data export, jump down to writeMeshData()
###
def writeMesh(self, ob, normals = 0):
imageMap={} # set of used images
sided={} # 'one':cnt , 'two':cnt
vColors={} # 'multi':1
if (len(ob.modifiers) > 0):
me = Mesh.New()
me.getFromObject(ob.name)
# Careful with the name, the temporary mesh may
# reuse the default name for other meshes. So we
# pick our own name.
me.name = "MOD_%s" % (ob.name)
else:
me = ob.getData(mesh = 1)
self.classifyMesh(me, ob)
if (self.collnode):
self.writeCollisionMesh(me, ob, normals)
return
else:
self.writeRegularMesh(me, ob, normals)
return
def writeCollisionMesh(self, me, ob, normals = 0):
self.writeIndented("Collision {\n",1)
self.writeIndented("collide FALSE\n")
self.writeIndented("children [\n")
self.writeRegularMesh(me, ob, normals)
self.writeIndented("]\n", -1)
self.writeIndented("}\n", -1)
def writeRegularMesh(self, me, ob, normals = 0):
if (self.billnode):
self.writeBillboardMesh(me, ob, normals)
elif (self.halonode):
self.writeHaloMesh(me, ob, normals)
else:
self.writeMeshData(me, ob, normals)
def writeBillboardMesh(self, me, ob, normals = 0):
self.writeIndented("Billboard {\n",1)
self.writeIndented("axisOfRotation 0 1 0\n")
self.writeIndented("children [\n")
self.writeMeshData(me, ob, normals)
self.writeIndented("]\n", -1)
self.writeIndented("}\n", -1)
def writeHaloMesh(self, me, ob, normals = 0):
self.writeIndented("Billboard {\n",1)
self.writeIndented("axisOfRotation 0 0 0\n")
self.writeIndented("children [\n")
self.writeMeshData(me, ob, normals)
self.writeIndented("]\n", -1)
self.writeIndented("}\n", -1)
###
### Here is where real mesh data is written
###
def writeMeshData(self, me, ob, normals = 0):
meshName = self.cleanStr(me.name)
if self.meshNames.has_key(meshName):
self.writeIndented("USE ME_%s\n" % meshName, 0)
self.meshNames[meshName]+=1
if (self.verbose == 1):
print " Using Mesh %s (Blender mesh: %s)\n" % \
(meshName, me.name)
return
self.meshNames[meshName]=1
if (self.verbose == 1):
print " Writing Mesh %s (Blender mesh: %s)\n" % \
(meshName, me.name)
return
self.writeIndented("DEF ME_%s Group {\n" % meshName,1)
self.writeIndented("children [\n", 1)
hasImageTexture = 0
issmooth = 0
maters = me.materials
nummats = self.getNumMaterials(me)
# Vertex and Face colors trump materials and image textures
if (self.facecolors or self.vcolors):
if nummats > 0:
if maters[0]:
self.writeShape(ob, me, 0, None)
else:
self.writeShape(ob, me, -1, None)
else:
self.writeShape(ob, me, -1, None)
# Do meshes with materials, possible with image textures
elif nummats > 0:
for matnum in range(len(maters)):
if maters[matnum]:
images = []
if me.faceUV:
images = self.getImages(me, matnum)
if len(images) > 0:
for image in images:
self.writeShape(ob, me, matnum, image)
else:
self.writeShape(ob, me, matnum, None)
else:
self.writeShape(ob, me, matnum, None)
else:
if me.faceUV:
images = self.getImages(me, -1)
if len(images) > 0:
for image in images:
self.writeShape(ob, me, -1, image)
else:
self.writeShape(ob, me, -1, None)
else:
self.writeShape(ob, me, -1, None)
self.writeIndented("]\n", -1)
self.writeIndented("}\n", -1)
def getImages(self, me, matnum):
imageNames = {}
images = []
for face in me.faces:
if (matnum == -1) or (face.mat == matnum):
if (face.image):
imName = self.cleanStr(face.image.name)
if not imageNames.has_key(imName):
images.append(face.image)
imageNames[imName]=1
return images
def getNumMaterials(self, me):
# Oh silly Blender, why do you sometimes have 'None' as
# a member of the me.materials array?
num = 0
for mat in me.materials:
if mat:
num = num + 1
return num
def writeCoordinates(self, me, meshName):
coordName = "coord_%s" % (meshName)
# look up coord name, use it if available
if self.coordNames.has_key(coordName):
self.writeIndented("coord USE %s\n" % coordName, 0)
self.coordNames[coordName]+=1
return;
self.coordNames[coordName]=1
#-- vertices
self.writeIndented("coord DEF %s Coordinate {\n" % (coordName), 1)
self.writeIndented("point [\n", 1)
meshVertexList = me.verts
for vertex in meshVertexList:
blenvert = Mathutils.Vector(vertex.co)
vrmlvert = M_blen2vrml * blenvert
self.writeUnindented("%s %s %s\n " % \
(vrmlvert[0], \
vrmlvert[1], \
vrmlvert[2]))
self.writeIndented("]\n", -1)
self.writeIndented("}\n", -1)
self.writeIndented("\n")
def writeShape(self, ob, me, matnum, image):
# Note: at this point it is assumed for matnum!=-1 that the
# material in me.materials[matnum] is not equal to 'None'.
# Such validation should be performed by the function that
# calls this one.
self.writeIndented("Shape {\n",1)
self.writeIndented("appearance Appearance {\n", 1)
if (matnum != -1):
mater = me.materials[matnum]
self.writeMaterial(mater, self.cleanStr(mater.name,''))
if (mater.mode & Blender.Material.Modes['TEXFACE']):
if image != None:
self.writeImageTexture(image.name, image.filename)
else:
if image != None:
self.writeImageTexture(image.name, image.filename)
self.writeIndented("}\n", -1)
self.writeGeometry(ob, me, matnum, image)
self.writeIndented("}\n", -1)
def writeGeometry(self, ob, me, matnum, image):
#-- IndexedFaceSet or IndexedLineSet
meshName = self.cleanStr(me.name)
# check if object is wireframe only
if (self.wire):
ifStyle="IndexedLineSet"
else:
# user selected BOUNDS=1, SOLID=3, SHARED=4, or TEXTURE=5
ifStyle="IndexedFaceSet"
self.writeIndented("geometry %s {\n" % ifStyle, 1)
if not self.wire:
if self.twosided == 1:
self.writeIndented("solid FALSE\n")
else:
self.writeIndented("solid TRUE\n")
self.writeCoordinates(me, meshName)
self.writeCoordIndex(me, meshName, matnum, image)
self.writeTextureCoordinates(me, meshName, matnum, image)
if self.facecolors:
self.writeFaceColors(me)
elif self.vcolors:
self.writeVertexColors(me)
self.writeIndented("}\n", -1)
def writeCoordIndex(self, me, meshName, matnum, image):
meshVertexList = me.verts
self.writeIndented("coordIndex [\n", 1)
coordIndexList=[]
for face in me.faces:
if (matnum == -1) or (face.mat == matnum):
if (image == None) or (face.image == image):
cordStr=""
for v in face.verts:
indx=v.index
cordStr = cordStr + "%s " % indx
self.writeUnindented(cordStr + "-1, \n")
self.writeIndented("]\n", -1)
def writeTextureCoordinates(self, me, meshName, matnum, image):
if (image == None):
return
texCoordList=[]
texIndexList=[]
j=0
for face in me.faces:
coordStr = ""
indexStr = ""
if (matnum == -1) or (face.mat == matnum):
if (face.image == image):
for i in range(len(face.verts)):
uv = face.uv[i]
indexStr += "%s " % (j)
coordStr += "%s %s, " % \
(round(uv[0], self.tp), \
round(uv[1], self.tp))
j=j+1
indexStr += "-1"
texIndexList.append(indexStr)
texCoordList.append(coordStr)
self.writeIndented("texCoord TextureCoordinate {\n", 1)
self.writeIndented("point [\n", 1)
for coord in texCoordList:
self.writeUnindented("%s\n" % (coord))
self.writeIndented("]\n", -1)
self.writeIndented("}\n", -1)
self.writeIndented("texCoordIndex [\n", 1)
for ind in texIndexList:
self.writeUnindented("%s\n" % (ind))
self.writeIndented("]\n", -1)
def writeFaceColors(self, me):
self.writeIndented("colorPerVertex FALSE\n")
self.writeIndented("color Color {\n",1)
self.writeIndented("color [\n", 1)
for face in me.faces:
if face.col:
c=face.col[0]
if self.verbose >= 2:
print "Debug: face.col r=%d g=%d b=%d" % (c.r, c.g, c.b)
aColor = self.rgbToFS(c)
self.writeUnindented("%s,\n" % aColor)
self.writeIndented("]\n",-1)
self.writeIndented("}\n",-1)
def writeVertexColors(self, me):
self.writeIndented("colorPerVertex TRUE\n")
self.writeIndented("color Color {\n",1)
self.writeIndented("color [\n\t\t\t\t\t\t", 1)
cols = [None] * len(me.verts)
for face in me.faces:
for vind in range(len(face.v)):
vertex = face.v[vind]
i = vertex.index
if cols[i] == None:
cols[i] = face.col[vind]
for i in range(len(me.verts)):
aColor = self.rgbToFS(cols[i])
self.writeUnindented("%s\n" % aColor)
self.writeIndented("\n", 0)
self.writeIndented("]\n",-1)
self.writeIndented("}\n",-1)
def writeMaterial(self, mat, matName):
# look up material name, use it if available
if self.matNames.has_key(matName):
self.writeIndented("material USE MA_%s\n" % matName)
self.matNames[matName]+=1
return;
self.matNames[matName]=1
ambient = mat.amb/3
diffuseR, diffuseG, diffuseB = \
mat.rgbCol[0], mat.rgbCol[1],mat.rgbCol[2]
if world:
ambi = world.getAmb()
ambi0, ambi1, ambi2 = (ambi[0]*mat.amb) * 2, \
(ambi[1]*mat.amb) * 2, \
(ambi[2]*mat.amb) * 2
else:
ambi0, ambi1, ambi2 = 0, 0, 0
emisR, emisG, emisB = (diffuseR*mat.emit+ambi0) / 2, \
(diffuseG*mat.emit+ambi1) / 2, \
(diffuseB*mat.emit+ambi2) / 2
shininess = mat.hard/512.0
specR = (mat.specCol[0]+0.001) / (1.25/(mat.getSpec()+0.001))
specG = (mat.specCol[1]+0.001) / (1.25/(mat.getSpec()+0.001))
specB = (mat.specCol[2]+0.001) / (1.25/(mat.getSpec()+0.001))
transp = 1 - mat.alpha
matFlags = mat.getMode()
if matFlags & Blender.Material.Modes['SHADELESS']:
ambient = 1
shine = 1
specR = emitR = diffuseR
specG = emitG = diffuseG
specB = emitB = diffuseB
self.writeIndented("material DEF MA_%s Material {\n" % matName, 1)
self.writeIndented("diffuseColor %s %s %s\n" % \
(round(diffuseR,self.cp), \
round(diffuseG,self.cp), \
round(diffuseB,self.cp)))
self.writeIndented("ambientIntensity %s\n" % \
(round(ambient,self.cp)))
self.writeIndented("specularColor %s %s %s\n" % \
(round(specR,self.cp), \
round(specG,self.cp), \
round(specB,self.cp)))
self.writeIndented("emissiveColor %s %s %s\n" % \
(round(emisR,self.cp), \
round(emisG,self.cp), \
round(emisB,self.cp)))
self.writeIndented("shininess %s\n" % (round(shininess,self.cp)))
self.writeIndented("transparency %s\n" % (round(transp,self.cp)))
self.writeIndented("}\n",-1)
def writeImageTexture(self, name, filename):
if self.texNames.has_key(name):
self.writeIndented("texture USE %s\n" % self.cleanStr(name))
self.texNames[name] += 1
return
else:
self.writeIndented("texture DEF %s ImageTexture {\n" % \
self.cleanStr(name), 1)
self.writeIndented('url "%s"\n' % \
filename.split("\\")[-1].split("/")[-1])
self.writeIndented("}\n",-1)
self.texNames[name] = 1
def writeBackground(self):
if world:
worldname = world.getName()
else:
return
blending = world.getSkytype()
grd = world.getHor()
grd0, grd1, grd2 = grd[0], grd[1], grd[2]
sky = world.getZen()
sky0, sky1, sky2 = sky[0], sky[1], sky[2]
mix0, mix1, mix2 = grd[0]+sky[0], grd[1]+sky[1], grd[2]+sky[2]
mix0, mix1, mix2 = mix0/2, mix1/2, mix2/2
if worldname in self.namesStandard:
self.writeIndented("Background {\n",1)
else:
self.writeIndented("DEF %s Background {\n" % \
self.secureName(worldname),1)
# No Skytype - just Hor color
if blending == 0:
self.writeIndented("groundColor %s %s %s\n" % \
(round(grd0,self.cp), \
round(grd1,self.cp), \
round(grd2,self.cp)))
self.writeIndented("skyColor %s %s %s\n" % \
(round(grd0,self.cp), \
round(grd1,self.cp), \
round(grd2,self.cp)))
# Blend Gradient
elif blending == 1:
self.writeIndented("groundColor [ %s %s %s, " % \
(round(grd0,self.cp), \
round(grd1,self.cp), \
round(grd2,self.cp)))
self.writeIndented("%s %s %s ]\n" % \
(round(mix0,self.cp), \
round(mix1,self.cp), \
round(mix2,self.cp)))
self.writeIndented("groundAngle [ 1.57, 1.57 ]\n")
self.writeIndented("skyColor [ %s %s %s, " % \
(round(sky0,self.cp), \
round(sky1,self.cp), \
round(sky2,self.cp)))
self.writeIndented("%s %s %s ]\n" % \
(round(mix0,self.cp), \
round(mix1,self.cp), \
round(mix2,self.cp)))
self.writeIndented("skyAngle [ 1.57, 1.57 ]\n")
# Blend+Real Gradient Inverse
elif blending == 3:
self.writeIndented("groundColor [ %s %s %s, " % \
(round(sky0,self.cp), \
round(sky1,self.cp), \
round(sky2,self.cp)))
self.writeIndented("%s %s %s ]\n" % \
(round(mix0,self.cp), \
round(mix1,self.cp), \
round(mix2,self.cp)))
self.writeIndented("groundAngle [ 1.57, 1.57 ]\n")
self.writeIndented("skyColor [ %s %s %s, " % \
(round(grd0,self.cp), \
round(grd1,self.cp), \
round(grd2,self.cp)))
self.writeIndented("%s %s %s ]\n" % \
(round(mix0,self.cp), \
round(mix1,self.cp), \
round(mix2,self.cp)))
self.writeIndented("skyAngle [ 1.57, 1.57 ]\n")
# Paper - just Zen Color
elif blending == 4:
self.writeIndented("groundColor %s %s %s\n" % \
(round(sky0,self.cp), \
round(sky1,self.cp), \
round(sky2,self.cp)))
self.writeIndented("skyColor %s %s %s\n" % \
(round(sky0,self.cp), \
round(sky1,self.cp), \
round(sky2,self.cp)))
# Blend+Real+Paper - komplex gradient
elif blending == 7:
self.writeIndented("groundColor [ %s %s %s, " % \
(round(sky0,self.cp), \
round(sky1,self.cp), \
round(sky2,self.cp)))
self.writeIndented("%s %s %s ]\n" % \
(round(grd0,self.cp), \
round(grd1,self.cp), \
round(grd2,self.cp)))
self.writeIndented("groundAngle [ 1.57, 1.57 ]\n")
self.writeIndented("skyColor [ %s %s %s, " % \
(round(sky0,self.cp), \
round(sky1,self.cp), \
round(sky2,self.cp)))
self.writeIndented("%s %s %s ]\n" % \
(round(grd0,self.cp),
round(grd1,self.cp),
round(grd2,self.cp)))
self.writeIndented("skyAngle [ 1.57, 1.57 ]\n")
# Any Other two colors
else:
self.writeIndented("groundColor %s %s %s\n" % \
(round(grd0,self.cp), \
round(grd1,self.cp), \
round(grd2,self.cp)))
self.writeIndented("skyColor %s %s %s\n" % \
(round(sky0,self.cp), \
round(sky1,self.cp), \
round(sky2,self.cp)))
alltexture = len(worldmat)
for i in xrange(alltexture):
namemat = worldmat[i].getName()
pic = worldmat[i].getImage()
if pic:
# Stripped path.
pic_path= pic.filename.split('\\')[-1].split('/')[-1]
if namemat == "back":
self.writeIndented('backUrl "%s"\n' % pic_path)
elif namemat == "bottom":
self.writeIndented('bottomUrl "%s"\n' % pic_path)
elif namemat == "front":
self.writeIndented('frontUrl "%s"\n' % pic_path)
elif namemat == "left":
self.writeIndented('leftUrl "%s"\n' % pic_path)
elif namemat == "right":
self.writeIndented('rightUrl "%s"\n' % pic_path)
elif namemat == "top":
self.writeIndented('topUrl "%s"\n' % pic_path)
self.writeIndented("}",-1)
self.writeIndented("\n\n")
def writeLamp(self, ob):
la = ob.data
laType = la.getType()
if laType == Lamp.Types.Lamp:
self.writePointLight(ob, la)
elif laType == Lamp.Types.Spot:
self.writeSpotLight(ob, la)
elif laType == Lamp.Types.Sun:
self.writeDirectionalLight(ob, la)
else:
self.writeDirectionalLight(ob, la)
def writeObject(self, ob):
obname = self.cleanStr(ob.name)
try:
obtype=ob.getType()
except AttributeError:
print "Error: Unable to get type info for %s" % obname
return
if self.verbose >= 1:
print "++ Writing %s object %s (Blender name: %s)\n" % \
(obtype, obname, ob.name)
# Note: I am leaving empties out for now -- the original
# script does some really weird stuff with empties
if ( (obtype != "Camera") and \
(obtype != "Mesh") and \
(obtype != "Lamp") ):
print "Info: Ignoring [%s], object type [%s] " \
"not handle yet" % (obname, obtype)
return
ob_matrix = Mathutils.Matrix(ob.getMatrix('worldspace'))
matrix = M_blen2vrml * ob_matrix * M_vrml2blen
e = matrix.rotationPart().toEuler()
v = matrix.translationPart()
(axis, angle) = self.eulToVecRot(self.deg2rad(e.x), \
self.deg2rad(e.y), \
self.deg2rad(e.z))
mrot = e.toMatrix().resize4x4()
try:
mrot.invert()
except:
print "Warning: %s has degenerate transformation!" % (obname)
return
diag = matrix * mrot
sizeX = diag[0][0]
sizeY = diag[1][1]
sizeZ = diag[2][2]
if self.verbose >= 1:
print " Transformation:\n" \
" loc: %f %f %f\n" \
" size: %f %f %f\n" \
" Rot: (%f %f %f), %f\n" % \
(v.x, v.y, v.z, \
sizeX, sizeY, sizeZ, \
axis[0], axis[1], axis[2], angle)
self.writeIndented("DEF OB_%s Transform {\n" % (obname), 1)
self.writeIndented("translation %f %f %f\n" % \
(v.x, v.y, v.z) )
self.writeIndented("rotation %f %f %f %f\n" % \
(axis[0],axis[1],axis[2],angle) )
self.writeIndented("scale %f %f %f\n" % \
(sizeX, sizeY, sizeZ) )
self.writeIndented("children [\n", 1)
self.writeObData(ob)
self.writeIndented("]\n", -1) # end object
self.writeIndented("}\n", -1) # end object
def writeObData(self, ob):
obtype = ob.getType()
if obtype == "Camera":
self.writeViewpoint(ob)
elif obtype == "Mesh":
self.writeMesh(ob)
elif obtype == "Lamp":
self.writeLamp(ob)
elif obtype == "Empty":
self.writeNode(ob)
##########################################################
# export routine
##########################################################
def export(self, scene, world, worldmat):
print "Info: starting VRML97 export to " + self.filename + "..."
self.writeHeader()
self.writeScript()
self.writeNavigationInfo(scene)
self.writeBackground()
self.writeFog()
self.proto = 0
allObj = []
if ARG == 'selected':
allObj = list(scene.objects.context)
else:
allObj = list(scene.objects)
self.writeInline()
for thisObj in allObj:
self.writeObject(thisObj)
if ARG != 'selected':
self.writeScript()
self.cleanup()
##########################################################
# Utility methods
##########################################################
def cleanup(self):
self.file.close()
self.texNames={}
self.matNames={}
self.indentLevel=0
print "Info: finished VRML97 export to %s\n" % self.filename
def cleanStr(self, name, prefix='rsvd_'):
"""cleanStr(name,prefix) - try to create a valid VRML DEF \
name from object name"""
newName=name[:]
if len(newName) == 0:
self.nNodeID+=1
return "%s%d" % (prefix, self.nNodeID)
if newName in self.namesReserved:
newName='%s%s' % (prefix,newName)
if newName[0].isdigit():
newName='%s%s' % ('_',newName)
for bad in (' ','"','#',"'",',','.','[','\\',']','{','}'):
newName=newName.replace(bad,'_')
return newName
def rgbToFS(self, c):
s = "%s %s %s" % \
(round(c.r/255.0,self.cp), \
round(c.g/255.0,self.cp), \
round(c.b/255.0,self.cp))
return s
def rad2deg(self, v):
return round(v*180.0/math.pi,4)
def deg2rad(self, v):
return (v*math.pi)/180.0;
def eulToVecRot(self, RotX, RotY, RotZ):
ti = RotX*0.5
tj = RotY*0.5
th = RotZ*0.5
ci = math.cos(ti)
cj = math.cos(tj)
ch = math.cos(th)
si = math.sin(ti)
sj = math.sin(tj)
sh = math.sin(th)
cc = ci*ch
cs = ci*sh
sc = si*ch
ss = si*sh
q0 = cj*cc + sj*ss
q1 = cj*sc - sj*cs
q2 = cj*ss + sj*cc
q3 = cj*cs - sj*sc
angle = 2 * math.acos(q0)
if (math.fabs(angle) < 0.000001):
axis = [1.0, 0.0, 0.0]
else:
sphi = 1.0/math.sqrt(1.0 - (q0*q0))
axis = [q1 * sphi, q2 * sphi, q3 * sphi]
a = Mathutils.Vector(axis)
a.normalize()
return ([a.x, a.y, a.z], angle)
# For writing well formed VRML code
#----------------------------------
def writeIndented(self, s, inc=0):
if inc < 1:
self.indentLevel = self.indentLevel + inc
self.file.write( self.indentLevel*"\t" + s)
if inc > 0:
self.indentLevel = self.indentLevel + inc
# Sometimes better to not have too many
# tab characters in a long list, for file size
#----------------------------------
def writeUnindented(self, s):
self.file.write(s)
##########################################################
# Callbacks, needed before Main
##########################################################
def select_file(filename):
if sys.exists(filename) and _safeOverwrite:
result = \
Draw.PupMenu("File Already Exists, Overwrite?%t|Yes%x1|No%x0")
if(result != 1):
return
if not filename.endswith(extension):
filename += extension
wrlexport=VRML2Export(filename)
wrlexport.export(scene, world, worldmat)
#########################################################
# main routine
#########################################################
try:
ARG = __script__['arg'] # user selected argument
except:
print "older version"
if Blender.Get('version') < 235:
print "Warning: VRML97 export failed, wrong blender version!"
print " You aren't running blender version 2.35 or greater"
print " download a newer version from http://blender3d.org/"
else:
if ARG == 'comp':
extension=".wrz"
from gzip import *
else:
extension=".wrl"
Blender.Window.FileSelector(select_file, "Export VRML97", \
sys.makename(ext=extension))