#!BPY
""" Registration info for Blender menus:
Name: 'VRML97 (.wrl)...'
Blender: 241
Group: 'Export'
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_export.py 16949 2008-10-06 17:11:10Z hos $
#
#------------------------------------------------------------------------
# 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, Registry
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 = ''

# Matrices below are used only when export_rotate_z_to_y.val:
#
# 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 xrange(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 xrange(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 xrange(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 xrange(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
		else:
			self.vcolors = 0
			
		# 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 = len(me.materials)

		# Vertex and Face colors trump materials and image textures
		if (self.facecolors or self.vcolors):
			if nummats > 0:
				self.writeShape(ob, me, 0, None)
			else:
				self.writeShape(ob, me, -1, None)

		# Do meshes with materials, possibly with image textures
		elif nummats > 0:
			for matnum in xrange(len(maters)):
				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 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:
			vrmlvert = blenvert = Mathutils.Vector(vertex.co)
			if export_rotate_z_to_y.val:
				vrmlvert = M_blen2vrml * vrmlvert
			self.writeUnindented("%s %s %s\n " % \
								 (vrmlvert[0], \
								  vrmlvert[1], \
								  vrmlvert[2]))
		self.writeIndented("]\n", -1)
		self.writeIndented("}\n", -1)
		self.writeIndented("\n")

	def testShape(self, ob, me, matnum, image):
		if ( (matnum == -1) and (image == None) ):
			if ( len(me.faces) > 0 ):
				return True
		# Check if any faces the material or image
		for face in me.faces:
			if (matnum == -1):
				if (f.image == image):
					return True
			elif (image == None):
				if (face.mat == matnum):
					return True
			else:
				if ((face.image == image) and (face.mat == matnum)):
					return True

		return False

	def writeShape(self, ob, me, matnum, image):
		# matnum == -1  means don't check the face.mat
		# image == None means don't check face.image

		if ( not self.testShape(ob, me, matnum, image) ):
			return False

		self.writeIndented("Shape {\n",1)

		self.writeIndented("appearance Appearance {\n", 1)
		if (matnum != -1):
			mater = me.materials[matnum]
			if (mater):
				self.writeMaterial(mater, self.cleanStr(mater.name,''))
				if (mater.mode & Blender.Material.Modes['TEXFACE']):
					if image != None:
						self.writeImageTexture(image.name, image.filename)
			else:
				self.writeDefaultMaterial()	
		else:
			if image != None:
				self.writeImageTexture(image.name, image.filename)

		self.writeIndented("}\n", -1)

		self.writeGeometry(ob, me, matnum, image)

		self.writeIndented("}\n", -1)

		return True

	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 xrange(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 xrange(len(face.v)):
				vertex = face.v[vind]
				i = vertex.index
				if cols[i] == None:
					cols[i] = face.col[vind]
					
		for i in xrange(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 writeDefaultMaterial(self):
		matName = "default"

		# 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
		self.writeIndented("material DEF MA_%s Material {\n" % matName, 1)
		self.writeIndented("diffuseColor 0.8 0.8 0.8\n")
		self.writeIndented("specularColor 1.0 1.0 1.0\n")
		self.writeIndented("shininess 0.5\n")
		self.writeIndented("transparency 0.0\n")
		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'))
		if export_rotate_z_to_y.val:
			matrix = M_blen2vrml * ob_matrix * M_vrml2blen
		else:
			matrix = ob_matrix
		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 export_selection_only.val:
			allObj = list(scene.objects.context)
		else:
			allObj = list(scene.objects)
			self.writeInline()

		for thisObj in allObj:
			self.writeObject(thisObj)

		if not export_selection_only.val:
			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)

#########################################################
# UI and Registry utilities
#########################################################

export_selection_only = Draw.Create(0)
export_rotate_z_to_y = Draw.Create(1)
export_compressed = Draw.Create(0)

def save_to_registry():
	d = {}
	d['selection_only'] = export_selection_only.val
	d['rotate_z_to_y'] = export_rotate_z_to_y.val
	d['compressed'] = export_compressed.val
	Registry.SetKey('vrml97_export', d, True)

def load_from_registry():
	d = Registry.GetKey('vrml97_export', True)
	if d:
		try:
			export_selection_only.val = d['selection_only']
			export_rotate_z_to_y.val = d['rotate_z_to_y']
			export_compressed.val = d['compressed']
		except: save_to_registry() # If data is not valid, rewrite it.

def show_popup():
	pup_block = [
		('Selection Only', export_selection_only, 'Only export objects in visible selection. Else export whole scene.'),
		('Rotate +Z to +Y', export_rotate_z_to_y, 'Rotate such that +Z axis (Blender up) becomes +Y (VRML up).'),
		('Compress', export_compressed, 'Generate a .wrz file (normal VRML compressed by gzip).')
		]
	return Draw.PupBlock('Export VRML 97...', pup_block) 

#########################################################
# main routine
#########################################################

load_from_registry()

# Note that show_popup must be done before Blender.Window.FileSelector,
# because export_compressed affects the suggested extension of resulting
# file.

if show_popup():
	save_to_registry()
	if export_compressed.val:
		extension=".wrz"
		from gzip import *
	else:
		extension=".wrl"
	Blender.Window.FileSelector(select_file, "Export VRML97", \
								sys.makename(ext=extension))
