#!BPY """Registration info for Blender menus: Name: 'KML' Blender: 232 Group: 'Export' Tooltip: 'Export to Goole Earth (.kml)' """ #----------------------------------------------------------------- # KML exporter for blender 2.32 or above # ---------------------------------------------------------------- # # ynniv: ynniv-kml@ynniv.com # # Remember kids, only you can prevent proprietary systems! # # This hack uses less than 50% of the original script, # so if its broken, its my fault! # The lat/lon of the Four Seasons Hotel in Atlanta # (HQ of Optimi corporation, my employer: www.optimi.com) xOffset = -84.385278 # longitude yOffset = 33.786211 # latitude zOffset = 600 # meters scale = 200 # meters per blender unit #### KML export is a Hack of: # # #----------------------------------------------------------------- # X3D exporter for blender 2.32 or above # It may work with earlier versions # # Version 0.16 - June 12, 2004 # Version 0.15 - May 22, 2004 # Version 0.14 - May 18, 2004 # Version 0.13 - May 15, 2004 # Version 0.12 - April 5, 2004 # Version 0.11 - March 28, 2004 # Version 0.1 - March 27, 2004 # # Source: http://www.bitbucket.ca/~acheater/blender/ # # ***** BEGIN GPL LICENSE BLOCK ***** # # Copyright (C) 2004: Adrian Cheater acheater@bitbucket.ca # # 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 ***** #################################### # Global Variables #################################### # Public, you may change these _askQuestions = True # If true, will prompt user for next two questions # These are settings which only matter if the line above is false _exportSelected = True # If false, exports the entire scene _safeOverwrite = True # If false, will overwrite files without asking. # Private, don't change these _doc = None _fatalError = False #################################### # Library dependancies #################################### import sys from math import * from os.path import exists, join try: import Blender from Blender import Object, NMesh, Lamp, Draw, BGL from Blender.Mathutils import * except: print "Fatal Error! Unable to find Blender modules!" print "Are you running this script from within blender?" _fatalError = True try: import xml from xml.dom.minidom import Document, DocumentType, Element, Comment except: Draw.PupMenu("Fatal Error! See console for details%t") print """\n\n *************************************** Fatal Error!! Couldn't find XML package. You may need to install Python Please visit http://www.bitbucket.ca/~acheater/blender/install.html for further instructions ***************************************\n\n""" _fatalError = True ################################### # Constant calculations ################################### # these calculate the scaling from blender x,y,z to # Google Maps latitude,longitude,altitude lat = radians(yOffset) latLength = 111412.84 * cos(1 * lat) + \ -93.5 * cos(3 * lat) + \ 0.118 * cos(5 * lat) lonLength = 111132.92 + \ -559.82 * cos(2 * lat) + \ 1.175 * cos(4 * lat) + \ -0.0023 * cos(6 * lat) # xScale = 0.01 xScale = scale / latLength # yScale = 0.01 yScale = scale / lonLength zScale = scale ########################################################## # Callbacks, needed before Main ########################################################## def select_file(filename): if exists(filename) and _safeOverwrite: result = Draw.PupMenu("File Already Exists, Overwrite?%t|Yes%x1|No%x0") if(result != 1): return export_file(filename) def askExportSelected(): result = Draw.PupMenu("Export...%t|All Objects%x0|Selected Objects%x1") if(result == 1): return True return False def createKMLPath(): filename = Blender.Get('filename') print filename if filename.find('.') != -1: filename = filename.split('.')[0] filename += ".kml" return filename ######################################## # Main ######################################## if(not _fatalError): if Blender.Get('version') < 232: print "Warning: this exporter may not function with versions of blender" print "older than 2.32. It if fails please download the latest version" print "from http://blender.org" if sys.hexversion < 0x020300F0: print "NOTICE: Python 2.2 or older, applying Document.writexml patch" Document.writexml = patch2_2_Document_writexml DocumentType.writexml = patch2_2_DocumentType_writexml if(_askQuestions): _exportSelected = askExportSelected() Blender.Window.FileSelector(select_file,"Export",createKMLPath()) ########################################################### # Functions for writing output file ########################################################## def export_file(filename): global _doc _doc = Document() print "*** Exporting KML to:", filename writeDocument(_doc) writeToXML(filename) print "*** Done" def writeToXML(filename): file = open(filename,"w") _doc.writexml(file,""," ","\n") def findFile(paths,filename): for path in paths: if exists(join(path,filename)): return join(path,filename) return None ######################################################## # Functions for writing KML scene structure ######################################################## def writeDocument(doc): writeHeader(doc) writeBody(doc) def writeHeader(doc): writeComments(doc) writeDocType(doc) def writeBody(doc): root = doc.createElement("kml") doc.appendChild(root) scene = _doc.createElement("Document") writeScene(scene) root.appendChild(scene) def writeComments(doc): doc.appendChild(Comment("This file was authored with Blender\ (http://www.blender.org/)")) doc.appendChild(Comment("Exported using the KMLExporter, a hack of [ X3DExporter, written by\ Adrian Cheater (http://www.bitbucket.ca/~acheater/blender) ], hacked by Vincent Fiano (http://ynniv.com)")) def writeDocType(doc): docType = DocumentType("kml") docType.publicId = "SYSTEM" docType.systemId = "kml.dtd" doc.appendChild(docType) def writeScene(scene): scene.appendChild(textElement("Visibility", "1")) scene.appendChild(textElement("DocumentSource", "Blender KML Export")) folder = _doc.createElement("Folder") folder.appendChild(textElement("name", "Optimi")) objects = getObjectList() for obj in objects: if obj.getType() == "Mesh": writeMesh(scene,obj) writeNavigationInfo(folder) def getObjectList(): objects = None if(_exportSelected): objects = Object.GetSelected() if(not objects): print "No objects selected: Exporting entire scene" objects = Object.Get() return objects def writeMesh(scene,obj): mesh = obj.getData() mesh.transform(obj.matrix) scene.appendChild(createMesh(mesh)) def writeNavigationInfo(scene): pass ############################################################# # Functions for converting Blender objects to XML # The create* functions will return completed element tags/trees # The get* functions perform formatting/data conversion ############################################################# ############################################################# # Functions for exporting Mesh Objects ############################################################# def createMesh(mesh): print "Exporting Mesh : \"%s\" %i vertices, %i faces" %\ (mesh.name,len(mesh.verts),len(mesh.faces)) if(mesh.getMode() & NMesh.Modes['AUTOSMOOTH']): print "Warning: Autosmoothing is not supported." print "Export will behave as if this feature were off" placemark = _doc.createElement("Placemark") if( mesh.materials ): placemark.appendChild(createStyle(mesh.materials[0])) name = _doc.createElement("name") name.appendChild(textNode(mesh.name)) placemark.appendChild(name) placemark.appendChild(writeGeometryCollection(mesh)) return placemark ########################################################## # Functions for exporting mesh data ########################################################## def writeGeometryCollection(mesh): gc = _doc.createElement("MultiGeometry") for face in mesh.faces: writePolygon(gc, face) return gc def writePolygon(gc, face): polygon = _doc.createElement("Polygon") gc.appendChild(polygon) polygon.appendChild(textElement("altitudeMode", "absolute")) outerBoundaryIs = _doc.createElement("outerBoundaryIs") polygon.appendChild(outerBoundaryIs) linearRing = _doc.createElement("LinearRing") outerBoundaryIs.appendChild(linearRing) coordinates = _doc.createElement("coordinates") linearRing.appendChild(coordinates) # make sure we have a rectangle while (len(face.v) < 4): face.v.append(face.v[len(face.v) - 1]) s = "" for vert in face.v: s += "%.14f,%.14f,%.14f\n" % (vert.co[0] * xScale + xOffset, vert.co[1] * yScale + yOffset, vert.co[2] * zScale + zOffset) coordinates.appendChild(textNode(s)) ############################################################ # Functions for exporting material data ############################################################ def createStyle(mat): style = _doc.createElement("Style") style.setAttribute("id", mat.name) linestyle = _doc.createElement("LineStyle") style.appendChild(linestyle) linestyle.appendChild(textElement("color", "0")) polystyle = _doc.createElement("PolyStyle") style.appendChild(polystyle) polystyle.appendChild(textElement("poly_outline", "00000000")) polystyle.appendChild(textElement("poly_fill", "1")) poly_color = _doc.createElement("color") poly_color.appendChild(textNode(getIntColorWithAlpha(getDiffuse(mat), mat.alpha))) polystyle.appendChild(poly_color) return style def textElement(name, value): tn = _doc.createElement(name) tn.appendChild(textNode(value)) return tn def textNode(t): return _doc.createTextNode(t) def getIntColorWithAlpha(c, alpha): return "%02X%02X%02X%02X" % (alpha * 255, c[2] * 255, c[1] * 255, c[0] * 255) # the following get color functions are a best guess # as to how to convert blender's color model to KML's def getDiffuse(mat): dCol = mat.rgbCol for c in range(3): dCol[c] *= mat.ref return dCol ######################################## # Python 2.2 writexml patches # Function borrowed from Python 2.3 code ######################################## def patch2_2_Document_writexml(self, writer, indent="", addindent="",\ newl="",encoding = None): if encoding is None: writer.write('\n') else: writer.write('\n' % encoding) for node in self.childNodes: node.writexml(writer, indent, addindent, newl) def patch2_2_DocumentType_writexml(self, writer, indent="", addindent="",\ newl=""): writer.write("\n")