#!BPY """ Name: 'Sketch (.sk)...' Blender: 244 Group: 'Export' Tooltip: 'Export selected objects to the Sketch scene description language' """ __author__ = 'Kjell Magne Fauske' __version__ = "1.0.1" __url__ = ("Documentation, http://www.fauskes.net/code/blend2sketch/", "Author's homepage, http://www.fauskes.net/", "Sketch, http://www.frontiernet.net/~eugene.ressler/") __bpydoc__ = """\ This script exports the selected objects to the Sketch scene description language. Sketch is a small, simple system for producing line drawings of two- or three-dimensional solid objects and scenes. Sketch outputs PGF/TikZ or PSTricks code suitable for use in TeX/LaTeX documents. With this script you can create objects in Blender and use them in your Sketch drawings. Usage: Select the objects you want to export and invoke the script from the "File->Export" menu[1]. Alternatively you can load and run the script from inside Blender. Supported object types:
- Mesh
- Curve, Surf, MBall: Converted to mesh when exported
- Empties: Exported as a coordinates
- Material: Materials assigned to mesh and curve objects.
- Camera: The active camera will be used to set up a Sketch scene with a viewpoint similar to Blender's.
All drawable objects are exported as polygons or poly lines. Non-mesh objects like curves and surfaces will be converted to a mesh when exported. Each face in a mesh will be saved as a polygon. If the mesh does not has any faces, edges will be saved as lines. Materials: The exporter has basic support for materials. By default the material's RGB value is used as fill or draw color. An alternative is to specify polygon and line options directly by putting the values in a 'polyoptions' and 'lineoptions' property assigned to the material. You can use the Help->Property ID browser to set these values. Issues: - Meshes with faces and edge-only parts are not supported yet. Only the faces will be exported in this case. - No support for grouping and parenting. All objects are currently exported as independent blocks. I will probably add support for groups in a future version. - No support for fgons yet. [1] Requires you to put the script in Blender's scripts folder. Blender will then automatically detect the script. """ # Changelog: # 1.0.1 # - Fixed bug in names generated for lineoptions. Thanks Semmelb! # 1.0.0 - First public release # Ideas: # - Support groups. # - Flat shading. Should be implemented in Sketch, but is probably quite easy to # implement in the exporter as well. # - Draw meshes with both faces and 'standalone' edges # - FGONS support import Blender from Blender import sys as bsys from Blender import Mesh, Mathutils, Registry, Scene, Material # Start of configuration section ------- REG_KEY = 'sketch_export' # config options: EXPORT_MATERIALS = True OUTPUT_LANGUAGE = 'tikz' EXPORT_CAMERA = True STANDALONE = True USE_TWOSIDED_FLAG = True SPE_LINE_COLOR = False ONLY_PROPERTIES = False GLOBAL_COORDS = True TRANSFORM_VERTICES = True tooltips = { #'GLOBAL_COORDS' : 'Transform mesh vertices to global coordianates', 'EXPORT_MATERIALS' : 'Apply materials to mesh faces', 'EXPORT_CAMERA' : "Set Sketch's camera view similar to Blender's", 'OUTPUT_LANGUAGE' : 'Set to tikz or pstricks. Output language generated by Sketch', 'STANDALONE' : 'Output a standalone document', 'USE_TWOSIDED_FLAG' : 'Disable culling for objects with double sided option set', 'SPE_LINE_COLOR' : 'Use the specular color of the material as polygon line color', 'ONLY_PROPERTIES' : 'Use only properties for materials with the polyoptions and/or '\ 'lineoptions set', } def update_registry(): d = { 'EXPORT_MATERIALS' : EXPORT_MATERIALS, 'OUTPUT_LANGUAGE' : OUTPUT_LANGUAGE, 'USE_TWOSIDED_FLAG' : USE_TWOSIDED_FLAG, 'EXPORT_CAMERA' : EXPORT_CAMERA, 'STANDALONE' : STANDALONE, 'SPE_LINE_COLOR' : SPE_LINE_COLOR, 'ONLY_PROPERTIES' : ONLY_PROPERTIES } Registry.SetKey(REG_KEY, d, True) # Looking for a saved key in Blender.Registry dict: rd = Registry.GetKey(REG_KEY, True) if rd: try: OUTPUT_LANGUAGE = rd['OUTPUT_LANGUAGE'] EXPORT_MATERIALS = rd['EXPORT_MATERIALS'] USE_TWOSIDED_FLAG = rd['USE_TWOSIDED_FLAG'] EXPORT_CAMERA = rd['EXPORT_CAMERA'] STANDALONE = rd['STANDALONE'] SPE_LINE_COLOR = rd['SPE_LINE_COLOR'] ONLY_PROPERTIES = rd['ONLY_PROPERTIES'] except KeyError: print "Keyerror" update_registry() else: update_registry() # Start of GUI section ------------------------------------------------ from Blender import Draw def draw_GUI(): global EXPORT_MATERIALS, EXPORT_CAMERA, STANDALONE,USE_TWOSIDED_FLAG global OUTPUT_LANGUAGE, SPE_LINE_COLOR,ONLY_PROPERTIES format = Draw.Create(OUTPUT_LANGUAGE) mattog = Draw.Create(EXPORT_MATERIALS) cameratog = Draw.Create(EXPORT_CAMERA) standalonetog = Draw.Create(STANDALONE) doublesidedtog = Draw.Create(USE_TWOSIDED_FLAG) specoltog = Draw.Create(SPE_LINE_COLOR) onlyproptog = Draw.Create(ONLY_PROPERTIES) block = [] block.append(("Output format: ", format, 0, 30, tooltips["OUTPUT_LANGUAGE"])) block.append("Export:") block.append(("Materials", mattog, tooltips['EXPORT_MATERIALS'])) block.append(("Camera", cameratog, tooltips['EXPORT_CAMERA'])) block.append("Material options:") block.append(("Spe line color",specoltog,tooltips['SPE_LINE_COLOR'])) block.append(("Only properties",onlyproptog,tooltips['ONLY_PROPERTIES'])) block.append("Other options:") block.append(("Standalone", standalonetog, tooltips['STANDALONE'])) block.append(("Double sided", doublesidedtog, tooltips['USE_TWOSIDED_FLAG'])) retval = Blender.Draw.PupBlock("Blend2Sketch options", block) if retval: # set options if format in ['tikz','pstricks']: OUTPUT_LANGUAGE = format.val else: print "Invalid output format %s" % format EXPORT_MATERIALS = mattog.val USE_TWOSIDED_FLAG = doublesidedtog.val STANDALONE = standalonetog.val EXPORT_CAMERA = cameratog.val SPE_LINE_COLOR = specoltog.val ONLY_PROPERTIES = onlyproptog.val update_registry() return retval # End of GUI section ---------------------- # End of configuration section --------- used_materials = {} drawables = [] def sketchify(name): """Return a valid Sketch identifier""" return name.replace('.','_') def get_material(material, is_face): """Convert material to Sketch options""" if not material: return "" opts = "" mat_name = sketchify(material.name) if is_face: try: if material.properties['polyoptions']: opts = "%s_poly" % (mat_name) except: opts = "%s_poly" % mat_name else: try: if material.properties['lineoptions']: opts = "%s_line" % (mat_name) except: opts = "%s_line" % mat_name used_materials[mat_name] = material return opts def write_object(obj,f): s = "" name = obj.name defname = sketchify(obj.name) prop = obj.properties mtrx = obj.matrix.rotationPart() xvec,yvec,zvec = mtrx[0],mtrx[1],mtrx[2] x,y,z = obj.getLocation('worldspace') if obj.type == 'Empty': if obj.parent: # need to transform Empty to world coordinates #vec = Mathutils.Vector(obj.getLocation()) #nvec = vec*obj.matrixWorld #x,y,z = nvec.x, nvec.y, nvec.z x,y,z = obj.getLocation('worldspace') else: x,y,z = obj.loc s += "def %s (%s,%s,%s)\n" % (sketchify(name),x,y,z) elif obj.type in ['Mesh','Curve','Surf','MBall']: is_curve = (obj.type=='Curve') is_mesh = (obj.type == 'Mesh') drawables.append(sketchify(name)) # write mesh to file if TRANSFORM_VERTICES: # The transformation code is from the Blender Python API # documentation. mesh = Mesh.New() mesh.getFromObject(obj.name) verts = mesh.verts[:] mesh.transform(obj.matrix) else: if not is_mesh: mesh= Mesh.New() mesh.getFromObject(obj.name) else: # Ensure that we get a mesh of the Mesh type mesh = obj.getData(mesh=True) twosided = (mesh.mode & Mesh.Modes['TWOSIDED']) and USE_TWOSIDED_FLAG s += 'def %s_o (%f,%f,%f)\n' % (defname,x,y,z) s += 'def %s_x [%f,%f,%f]\n' % tuple([defname]+list(xvec)) s += 'def %s_y [%f,%f,%f]\n' % tuple([defname]+list(yvec)) s += 'def %s_z [%f,%f,%f]\n' % tuple([defname]+list(zvec)) polyoptsname = "%s_polyopts" % defname polyoptions = [] try: polyopts = prop['polyoptions'] polyoptions.append(polyopts) except: pass if twosided: polyoptions.append('cull=false') if polyoptions: s += 'def %s [%s]\n' % (polyoptsname,",".join(polyoptions)) else: polyoptsname = "" lineoptsname = "" try: lineopts = prop['lineoptions'] lineoptsname = "%s_lineopts" % defname s += 'def %s [%s]\n' % (lineoptsname,lineopts) except: lineoptsname = "" s += 'def %s {\n' % (defname) if EXPORT_MATERIALS: if is_mesh: materials = mesh.materials else: try: materials = obj.data.getMaterials() # does not work with MBall except: materials = [] if len(mesh.faces) > 0: # iterate over all faces in the mesh for face in mesh.faces: polyoptions= [] # each face is represented as a polygon if EXPORT_MATERIALS and len(materials): polymatopts = get_material(materials[face.mat],is_face=True) polyoptions.append(polymatopts) if polyoptsname: polyoptions.append(polyoptsname) if polyoptions: polyoptions = "[%s]" % ",".join(polyoptions) else: polyoptions = "" s += ' polygon%s' % polyoptions # iterate over the vertices in the mesh for vert in face.v: v = mesh.verts[vert.index] s += '(%f,%f,%f)' % tuple(v.co) s += '\n' else: prev = None connectededges = False lineoptions = [] if EXPORT_MATERIALS and materials: # pick first material for mat in materials: if mat: linematopts = get_material(mat, is_face=False) lineoptions.append(linematopts) break if lineoptsname: lineoptions.append(lineoptsname) if lineoptions: lineoptions = "[%s]" % ",".join(lineoptions) else: lineoptions = "" for edge in mesh.edges: if prev and (prev.v2.co == edge.v1.co): connectededges = True s += '(%f,%f,%f)' % tuple(edge.v2.co) else: if connectededges: s += '\n' connectededges=False s += ' line%s (%f,%f,%f)(%f,%f,%f)' % \ (lineoptions, edge.v1.co.x, edge.v1.co.y, edge.v1.co.z, edge.v2.co.x, edge.v2.co.y, edge.v2.co.z) prev = edge s += '\n' if TRANSFORM_VERTICES: # Restore vertices. mesh.verts = verts s += '}\n\n' return s def write_objects(filepath): """Write all selected objects to filepath""" # get all selected objects objects = Blender.Object.GetSelected() f = file(filepath, 'w') # write header to file f.write('# Generated by sketch_export.py v %s \n' % (__version__)) # iterate over each object # Todo: Should I group meshes and objects? s = "" s += "% Mesh section\n\n" for obj in objects: s += write_object(obj,f) if used_materials: c = "% Materials section \n" for material in used_materials.values(): mat_name = sketchify(material.name) polyopts = lineopts = opts = '' proponly = ONLY_PROPERTIES try: proponly = material.properties['onlyproperties'] if proponly and type(proponly) == str: proponly = proponly.lower() not in ('0','false') except: pass try: polyopts = material.properties['polyoptions'] except: pass try: lineopts = material.properties['lineoptions'] except: pass rgb = material.rgbCol spec = material.specCol alpha = material.alpha flags = material.getMode() if not (proponly and ( polyopts and lineopts )): c += "special |\definecolor{%s}{rgb}{%s,%s,%s}|[lay=under]\n" \ % tuple([mat_name]+rgb) if SPE_LINE_COLOR: spemat_name = "%s_l" % mat_name c += "special |\definecolor{%s}{rgb}{%s,%s,%s}|[lay=under]\n" \ % tuple([spemat_name]+spec) opts = "%s=%s" % (FILL_STR, mat_name) if SPE_LINE_COLOR: opts += ",%s=%s" % (LINE_STR, spemat_name) if alpha < 1.0: opts += ",%s=%s" % (OPACITY_STR,alpha) if polyopts and opts and not proponly: opts += ',' + polyopts else: opts = polyopts or opts c += "def %s_poly [%s]\n" % (mat_name,opts) opts = '' if not (lineopts and proponly): opts = "%s=%s" % (LINE_STR, mat_name) if lineopts and opts: opts += ','+lineopts else: opts = lineopts or opts c += "def %s_line [%s]\n" % (mat_name,opts) s = c+"\n"+s # Write a scene if drawables: s += "\ndef scene {\n" if EXPORT_CAMERA: scn = Blender.Scene.GetCurrent() cam = scn.objects.camera vec = Mathutils.Vector([0,0,-1]) direction = cam.matrix.rotationPart()*vec loc = cam.getLocation('worldspace') s += " put {view((%f,%f,%f),(%f,%f,%f),[0,0,1])}{\n" % \ tuple(list(loc)+list(direction)) for name in drawables: s += " {%s}\n" % name if EXPORT_CAMERA: s += " }\n" s += "}\n" if STANDALONE: s += "\n" s += "{scene}\n" if OUTPUT_LANGUAGE == 'tikz': s += "global {language tikz}\n" f.write(s) f.close() # Start of script ----------------------------------------------------- # Ensure that at leas one object is selected if len(Blender.Object.GetSelected()) == 0: # no objects selected. Print error message and quit Blender.Draw.PupMenu('ERROR: Please select at least one mesh') else: fname = bsys.makename(ext=".sk") retval = draw_GUI() # Set TikZ and PSTricks specific parameters if OUTPUT_LANGUAGE == 'tikz': FILL_STR = "fill" LINE_STR = "draw" OPACITY_STR = "fill opacity" else: FILL_STR = "fillcolor" LINE_STR = "linecolor" OPACITY_STR = "fill opacity" if retval: Blender.Window.FileSelector(write_objects, "Export SK", fname) pass