#!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