6 Tooltip: 'Vector Rendering Method Export Script'
9 __author__ = "Antonio Ospite"
14 Render the scene and save the result in vector format.
17 # ---------------------------------------------------------------------
18 # Copyright (c) 2006 Antonio Ospite
20 # This program is free software; you can redistribute it and/or modify
21 # it under the terms of the GNU General Public License as published by
22 # the Free Software Foundation; either version 2 of the License, or
23 # (at your option) any later version.
25 # This program is distributed in the hope that it will be useful,
26 # but WITHOUT ANY WARRANTY; without even the implied warranty of
27 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 # GNU General Public License for more details.
30 # You should have received a copy of the GNU General Public License
31 # along with this program; if not, write to the Free Software
32 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
34 # ---------------------------------------------------------------------
37 # Thanks to Emilio Aguirre for S2flender from which I took inspirations :)
38 # Thanks to Nikola Radovanovic, the author of the original VRM script,
39 # the code you read here has been rewritten _almost_ entirely
40 # from scratch but Nikola gave me the idea, so I thank him publicly.
42 # ---------------------------------------------------------------------
44 # Things TODO for a next release:
45 # - Switch to the Mesh structure, should be considerably faster
46 # (partially done, but cannot sort faces, yet)
47 # - Use a better depth sorting algorithm
48 # - Review how selections are made (this script uses selection states of
49 # primitives to represent visibility infos)
50 # - Implement clipping of primitives and do handle object intersections.
51 # (for now only clipping for whole objects is supported).
52 # - Implement Edge Styles (silhouettes, contours, etc.)
53 # - Implement Edge coloring
54 # - Use multiple lighting sources in color calculation
55 # - Implement Shading Styles?
56 # - Use another representation for the 2D projection?
57 # Think to a way to merge adjacent polygons that have the same color.
58 # - Add other Vector Writers.
60 # ---------------------------------------------------------------------
64 # vrm-0.3.py - 2006-05-19
65 # * First release after code restucturing.
66 # Now the script offers a useful set of functionalities
67 # and it can render animations, too.
69 # ---------------------------------------------------------------------
72 from Blender import Scene, Object, Mesh, NMesh, Material, Lamp, Camera
73 from Blender.Mathutils import *
77 # Some global settings
80 SHOW_HIDDEN_EDGES = False
84 POLYGON_EXPANSION_TRICK = True
86 RENDER_ANIMATION = False
88 # Does not work in batch mode!!
89 #OPTIMIZE_FOR_SPACE = True
92 # ---------------------------------------------------------------------
94 ## Projections classes
96 # ---------------------------------------------------------------------
99 """Calculate the projection of an object given the camera.
101 A projector is useful to so some per-object transformation to obtain the
102 projection of an object given the camera.
104 The main method is #doProjection# see the method description for the
108 def __init__(self, cameraObj, canvasRatio):
109 """Calculate the projection matrix.
111 The projection matrix depends, in this case, on the camera settings.
112 TAKE CARE: This projector expects vertices in World Coordinates!
115 camera = cameraObj.getData()
117 aspect = float(canvasRatio[0])/float(canvasRatio[1])
118 near = camera.clipStart
121 scale = float(camera.scale)
123 fovy = atan(0.5/aspect/(camera.lens/32))
124 fovy = fovy * 360.0/pi
126 # What projection do we want?
128 #mP = self._calcOrthoMatrix(fovy, aspect, near, far, 17) #camera.scale)
129 mP = self._calcOrthoMatrix(fovy, aspect, near, far, scale)
131 mP = self._calcPerspectiveMatrix(fovy, aspect, near, far)
133 # View transformation
134 cam = Matrix(cameraObj.getInverseMatrix())
139 self.projectionMatrix = mP
145 def doProjection(self, v):
146 """Project the point on the view plane.
148 Given a vertex calculate the projection using the current projection
152 # Note that we have to work on the vertex using homogeneous coordinates
153 p = self.projectionMatrix * Vector(v).resize4D()
169 def _calcPerspectiveMatrix(self, fovy, aspect, near, far):
170 """Return a perspective projection matrix.
173 top = near * tan(fovy * pi / 360.0)
177 x = (2.0 * near) / (right-left)
178 y = (2.0 * near) / (top-bottom)
179 a = (right+left) / (right-left)
180 b = (top+bottom) / (top - bottom)
181 c = - ((far+near) / (far-near))
182 d = - ((2*far*near)/(far-near))
188 [0.0, 0.0, -1.0, 0.0])
192 def _calcOrthoMatrix(self, fovy, aspect , near, far, scale):
193 """Return an orthogonal projection matrix.
196 # The 11 in the formula was found emiprically
197 top = near * tan(fovy * pi / 360.0) * (scale * 11)
199 left = bottom * aspect
204 tx = -((right+left)/rl)
205 ty = -((top+bottom)/tb)
209 [2.0/rl, 0.0, 0.0, tx],
210 [0.0, 2.0/tb, 0.0, ty],
211 [0.0, 0.0, 2.0/fn, tz],
212 [0.0, 0.0, 0.0, 1.0])
217 # ---------------------------------------------------------------------
219 ## 2DObject representation class
221 # ---------------------------------------------------------------------
223 # TODO: a class to represent the needed properties of a 2D vector image
224 # For now just using a [N]Mesh structure.
227 # ---------------------------------------------------------------------
229 ## Vector Drawing Classes
231 # ---------------------------------------------------------------------
237 A class for printing output in a vectorial format.
239 Given a 2D representation of the 3D scene the class is responsible to
240 write it is a vector format.
242 Every subclasses of VectorWriter must have at last the following public
246 - printCanvas(self, scene,
247 doPrintPolygons=True, doPrintEdges=False, showHiddenEdges=False):
250 def __init__(self, fileName):
251 """Set the output file name and other properties"""
253 self.outputFileName = fileName
256 context = Scene.GetCurrent().getRenderingContext()
257 self.canvasSize = ( context.imageSizeX(), context.imageSizeY() )
261 self.animation = False
268 def open(self, startFrame=1, endFrame=1):
269 if startFrame != endFrame:
270 self.startFrame = startFrame
271 self.endFrame = endFrame
272 self.animation = True
274 self.file = open(self.outputFileName, "w")
275 print "Outputting to: ", self.outputFileName
283 def printCanvas(self, scene, doPrintPolygons=True, doPrintEdges=False,
284 showHiddenEdges=False):
285 """This is the interface for the needed printing routine.
292 class SVGVectorWriter(VectorWriter):
293 """A concrete class for writing SVG output.
296 def __init__(self, fileName):
297 """Simply call the parent Contructor.
299 VectorWriter.__init__(self, fileName)
306 def open(self, startFrame=1, endFrame=1):
307 """Do some initialization operations.
309 VectorWriter.open(self, startFrame, endFrame)
313 """Do some finalization operation.
317 # remember to call the close method of the parent
318 VectorWriter.close(self)
321 def printCanvas(self, scene, doPrintPolygons=True, doPrintEdges=False,
322 showHiddenEdges=False):
323 """Convert the scene representation to SVG.
326 Objects = scene.getChildren()
328 context = scene.getRenderingContext()
329 framenumber = context.currentFrame()
332 framestyle = "display:none"
334 framestyle = "display:block"
336 # Assign an id to this group so we can set properties on it using DOM
337 self.file.write("<g id=\"frame%d\" style=\"%s\">\n" %
338 (framenumber, framestyle) )
342 if(obj.getType() != 'Mesh'):
345 self.file.write("<g id=\"%s\">\n" % obj.getName())
347 mesh = obj.getData(mesh=1)
350 self._printPolygons(mesh)
353 self._printEdges(mesh, showHiddenEdges)
355 self.file.write("</g>\n")
357 self.file.write("</g>\n")
364 def _calcCanvasCoord(self, v):
365 """Convert vertex in scene coordinates to canvas coordinates.
368 pt = Vector([0, 0, 0])
370 mW = float(self.canvasSize[0])/2.0
371 mH = float(self.canvasSize[1])/2.0
373 # rescale to canvas size
374 pt[0] = v.co[0]*mW + mW
375 pt[1] = v.co[1]*mH + mH
378 # For now we want (0,0) in the top-left corner of the canvas.
379 # Mirror and translate along y
381 pt[1] += self.canvasSize[1]
385 def _printHeader(self):
386 """Print SVG header."""
388 self.file.write("<?xml version=\"1.0\"?>\n")
389 self.file.write("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n")
390 self.file.write("\t\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n")
391 self.file.write("<svg version=\"1.1\"\n")
392 self.file.write("\txmlns=\"http://www.w3.org/2000/svg\"\n")
393 self.file.write("\twidth=\"%d\" height=\"%d\" streamable=\"true\">\n\n" %
398 self.file.write("""\n<script><![CDATA[
402 /* FIXME: Use 1000 as interval as lower values gives problems */
403 timerID = setInterval("NextFrame()", 1000);
404 globalFrameCounter=%d;
408 currentElement = document.getElementById('frame'+globalFrameCounter)
409 previousElement = document.getElementById('frame'+(globalFrameCounter-1))
416 if (globalFrameCounter > globalEndFrame)
418 clearInterval(timerID)
424 previousElement.style.display="none";
426 currentElement.style.display="block";
427 globalFrameCounter++;
431 \n""" % (self.startFrame, self.endFrame, self.startFrame) )
433 def _printFooter(self):
434 """Print the SVG footer."""
436 self.file.write("\n</svg>\n")
438 def _printPolygons(self, mesh):
439 """Print the selected (visible) polygons.
442 if len(mesh.faces) == 0:
445 self.file.write("<g>\n")
447 for face in mesh.faces:
451 self.file.write("<polygon points=\"")
454 p = self._calcCanvasCoord(v)
455 self.file.write("%g,%g " % (p[0], p[1]))
457 # get rid of the last blank space, just cosmetics here.
458 self.file.seek(-1, 1)
459 self.file.write("\"\n")
461 # take as face color the first vertex color
462 # TODO: the average of vetrex colors?
465 color = [fcol.r, fcol.g, fcol.b, fcol.a]
467 color = [255, 255, 255, 255]
469 # use the stroke property to alleviate the "adjacent edges" problem,
470 # we simulate polygon expansion using borders,
471 # see http://www.antigrain.com/svg/index.html for more info
475 # Convert the color to the #RRGGBB form
476 str_col = "#%02X%02X%02X" % (color[0], color[1], color[2])
478 self.file.write("\tstyle=\"fill:" + str_col + ";")
479 if POLYGON_EXPANSION_TRICK:
480 self.file.write(" stroke:" + str_col + ";")
481 self.file.write(" stroke-width:" + str(stroke_width) + ";\n")
482 self.file.write(" stroke-linecap:round;stroke-linejoin:round")
483 self.file.write("\"/>\n")
485 self.file.write("</g>\n")
487 def _printEdges(self, mesh, showHiddenEdges=False):
488 """Print the wireframe using mesh edges.
491 stroke_width=EDGES_WIDTH
492 stroke_col = [0, 0, 0]
494 self.file.write("<g>\n")
498 hidden_stroke_style = ""
500 # Consider an edge selected if both vertices are selected
501 if e.v1.sel == 0 or e.v2.sel == 0:
502 if showHiddenEdges == False:
505 hidden_stroke_style = ";\n stroke-dasharray:3, 3"
507 p1 = self._calcCanvasCoord(e.v1)
508 p2 = self._calcCanvasCoord(e.v2)
510 self.file.write("<line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"\n"
511 % ( p1[0], p1[1], p2[0], p2[1] ) )
512 self.file.write(" style=\"stroke:rgb("+str(stroke_col[0])+","+str(stroke_col[1])+","+str(stroke_col[2])+");")
513 self.file.write(" stroke-width:"+str(stroke_width)+";\n")
514 self.file.write(" stroke-linecap:round;stroke-linejoin:round")
515 self.file.write(hidden_stroke_style)
516 self.file.write("\"/>\n")
518 self.file.write("</g>\n")
522 # ---------------------------------------------------------------------
526 # ---------------------------------------------------------------------
529 """Render a scene viewed from a given camera.
531 This class is responsible of the rendering process, transformation and
532 projection of the objects in the scene are invoked by the renderer.
534 The rendering is done using the active camera for the current scene.
538 """Make the rendering process only for the current scene by default.
540 We will work on a copy of the scene, be sure that the current scene do
541 not get modified in any way.
544 # Render the current Scene, this should be a READ-ONLY property
545 self._SCENE = Scene.GetCurrent()
547 # Use the aspect ratio of the scene rendering context
548 context = self._SCENE.getRenderingContext()
550 aspect_ratio = float(context.imageSizeX())/float(context.imageSizeY())
551 self.canvasRatio = (float(context.aspectRatioX())*aspect_ratio,
552 float(context.aspectRatioY())
555 # Render from the currently active camera
556 self.cameraObj = self._SCENE.getCurrentCamera()
558 # Get the list of lighting sources
559 obj_lst = self._SCENE.getChildren()
560 self.lights = [ o for o in obj_lst if o.getType() == 'Lamp']
562 if len(self.lights) == 0:
564 lobj = Object.New('Lamp')
566 self.lights.append(lobj)
573 def doRendering(self, outputWriter, animation=False):
574 """Render picture or animation and write it out.
577 - a Vector writer object that will be used to output the result.
578 - a flag to tell if we want to render an animation or only the
582 context = self._SCENE.getRenderingContext()
583 currentFrame = context.currentFrame()
585 # Handle the animation case
587 startFrame = currentFrame
588 endFrame = startFrame
591 startFrame = context.startFrame()
592 endFrame = context.endFrame()
593 outputWriter.open(startFrame, endFrame)
595 # Do the rendering process frame by frame
596 print "Start Rendering!"
597 for f in range(startFrame, endFrame+1):
598 context.currentFrame(f)
600 renderedScene = self.doRenderScene(self._SCENE)
601 outputWriter.printCanvas(renderedScene,
602 doPrintPolygons = PRINT_POLYGONS,
603 doPrintEdges = PRINT_EDGES,
604 showHiddenEdges = SHOW_HIDDEN_EDGES)
606 # clear the rendered scene
607 self._SCENE.makeCurrent()
608 Scene.unlink(renderedScene)
613 context.currentFrame(currentFrame)
616 def doRenderScene(self, inputScene):
617 """Control the rendering process.
619 Here we control the entire rendering process invoking the operation
620 needed to transform and project the 3D scene in two dimensions.
623 # Use some temporary workspace, a full copy of the scene
624 workScene = inputScene.copy(2)
626 # Get a projector for this scene.
627 # NOTE: the projector wants object in world coordinates,
628 # so we should apply modelview transformations _before_
629 # projection transformations
630 proj = Projector(self.cameraObj, self.canvasRatio)
632 # global processing of the scene
634 self._doConvertGeometricObjToMesh(workScene)
636 self._doSceneClipping(workScene)
638 # FIXME: does not work in batch mode!
639 #if OPTIMIZE_FOR_SPACE:
640 # self._joinMeshObjectsInScene(workScene)
642 self._doSceneDepthSorting(workScene)
644 # Per object activities
646 Objects = workScene.getChildren()
649 if obj.getType() != 'Mesh':
650 print "Only Mesh supported! - Skipping type:", obj.getType()
653 print "Rendering: ", obj.getName()
657 self._doModelToWorldCoordinates(mesh, obj.matrix)
659 self._doObjectDepthSorting(mesh)
661 self._doBackFaceCulling(mesh)
663 self._doColorAndLighting(mesh)
665 # TODO: 'style' can be a function that determine
666 # if an edge should be showed?
667 self._doEdgesStyle(mesh, style=None)
669 self._doProjection(mesh, proj)
671 # Update the object data, important! :)
683 def _getObjPosition(self, obj):
684 """Return the obj position in World coordinates.
686 return obj.matrix.translationPart()
688 def _cameraViewDirection(self):
689 """Get the View Direction form the camera matrix.
691 return Vector(self.cameraObj.matrix[2]).resize3D()
696 def _isFaceVisible(self, face):
697 """Determine if a face of an object is visible from the current camera.
699 The view vector is calculated from the camera location and one of the
700 vertices of the face (expressed in World coordinates, after applying
701 modelview transformations).
703 After those transformations we determine if a face is visible by
704 computing the angle between the face normal and the view vector, this
705 angle has to be between -90 and 90 degrees for the face to be visible.
706 This corresponds somehow to the dot product between the two, if it
707 results > 0 then the face is visible.
709 There is no need to normalize those vectors since we are only interested in
710 the sign of the cross product and not in the product value.
712 NOTE: here we assume the face vertices are in WorldCoordinates, so
713 please transform the object _before_ doing the test.
716 normal = Vector(face.no)
717 camPos = self._getObjPosition(self.cameraObj)
720 # View Vector in orthographics projections is the view Direction of
722 if self.cameraObj.data.getType() == 1:
723 view_vect = self._cameraViewDirection()
725 # View vector in perspective projections can be considered as
726 # the difference between the camera position and one point of
727 # the face, we choose the farthest point from the camera.
728 if self.cameraObj.data.getType() == 0:
729 vv = max( [ ((camPos - Vector(v.co)).length, (camPos - Vector(v.co))) for v in face] )
732 # if d > 0 the face is visible from the camera
733 d = view_vect * normal
743 def _doConvertGeometricObjToMesh(self, scene):
744 """Convert all "geometric" objects to mesh ones.
746 geometricObjTypes = ['Mesh', 'Surf', 'Curve', 'Text']
748 Objects = scene.getChildren()
749 objList = [ o for o in Objects if o.getType() in geometricObjTypes ]
752 obj = self._convertToRawMeshObj(obj)
754 scene.unlink(old_obj)
757 # XXX Workaround for Text and Curve which have some normals
758 # inverted when they are converted to Mesh, REMOVE that when
759 # blender will fix that!!
760 if old_obj.getType() in ['Curve', 'Text']:
761 me = obj.getData(mesh=1)
762 for f in me.faces: f.sel = 1;
763 for v in me.verts: v.sel = 1;
770 def _doSceneClipping(self, scene):
771 """Clip objects against the View Frustum.
773 For now clip away only objects according to their center position.
776 cpos = self._getObjPosition(self.cameraObj)
777 view_vect = self._cameraViewDirection()
779 near = self.cameraObj.data.clipStart
780 far = self.cameraObj.data.clipEnd
782 aspect = float(self.canvasRatio[0])/float(self.canvasRatio[1])
783 fovy = atan(0.5/aspect/(self.cameraObj.data.lens/32))
784 fovy = fovy * 360.0/pi
786 Objects = scene.getChildren()
788 if o.getType() != 'Mesh': continue;
790 obj_vect = Vector(cpos) - self._getObjPosition(o)
792 d = obj_vect*view_vect
793 theta = AngleBetweenVecs(obj_vect, view_vect)
795 # if the object is outside the view frustum, clip it away
796 if (d < near) or (d > far) or (theta > fovy):
799 def _doSceneDepthSorting(self, scene):
800 """Sort objects in the scene.
802 The object sorting is done accordingly to the object centers.
805 c = self._getObjPosition(self.cameraObj)
807 by_center_pos = (lambda o1, o2:
808 (o1.getType() == 'Mesh' and o2.getType() == 'Mesh') and
809 cmp((self._getObjPosition(o1) - Vector(c)).length,
810 (self._getObjPosition(o2) - Vector(c)).length)
813 # TODO: implement sorting by bounding box, if obj1.bb is inside obj2.bb,
814 # then ob1 goes farther than obj2, useful when obj2 has holes
817 Objects = scene.getChildren()
818 Objects.sort(by_center_pos)
825 def _joinMeshObjectsInScene(self, scene):
826 """Merge all the Mesh Objects in a scene into a single Mesh Object.
829 bigObj = Object.New('Mesh', 'BigOne')
832 oList = [o for o in scene.getChildren() if o.getType()=='Mesh']
843 def _convertToRawMeshObj(self, object):
844 """Convert geometry based object to a mesh object.
846 me = Mesh.New('RawMesh_'+object.name)
847 me.getFromObject(object.name)
849 newObject = Object.New('Mesh', 'RawMesh_'+object.name)
852 # If the object has no materials set a default material
854 me.materials = [Material.New()]
855 #for f in me.faces: f.mat = 0
857 newObject.setMatrix(object.getMatrix())
861 def _doModelToWorldCoordinates(self, mesh, matrix):
862 """Transform object coordinates to world coordinates.
864 This step is done simply applying to the object its tranformation
865 matrix and recalculating its normals.
867 mesh.transform(matrix, True)
869 def _doObjectDepthSorting(self, mesh):
870 """Sort faces in an object.
872 The faces in the object are sorted following the distance of the
873 vertices from the camera position.
875 c = self._getObjPosition(self.cameraObj)
877 # hackish sorting of faces
879 # Sort faces according to the max distance from the camera
880 by_max_vert_dist = (lambda f1, f2:
881 cmp(max([(Vector(v.co)-Vector(c)).length for v in f1]),
882 max([(Vector(v.co)-Vector(c)).length for v in f2])))
884 # Sort faces according to the min distance from the camera
885 by_min_vert_dist = (lambda f1, f2:
886 cmp(min([(Vector(v.co)-Vector(c)).length for v in f1]),
887 min([(Vector(v.co)-Vector(c)).length for v in f2])))
889 # Sort faces according to the avg distance from the camera
890 by_avg_vert_dist = (lambda f1, f2:
891 cmp(sum([(Vector(v.co)-Vector(c)).length for v in f1])/len(f1),
892 sum([(Vector(v.co)-Vector(c)).length for v in f2])/len(f2)))
894 mesh.faces.sort(by_max_vert_dist)
897 def _doBackFaceCulling(self, mesh):
898 """Simple Backface Culling routine.
900 At this level we simply do a visibility test face by face and then
901 select the vertices belonging to visible faces.
904 # Select all vertices, so edges can be displayed even if there are no
909 Mesh.Mode(Mesh.SelectModes['FACE'])
913 if self._isFaceVisible(f):
916 # Is this the correct way to propagate the face selection info to the
917 # vertices belonging to a face ??
918 # TODO: Using the Mesh module this should come for free. Right?
919 Mesh.Mode(Mesh.SelectModes['VERTEX'])
922 for v in f: v.sel = 0;
926 for v in f: v.sel = 1;
928 def _doColorAndLighting(self, mesh):
929 """Apply an Illumination model to the object.
931 The Illumination model used is the Phong one, it may be inefficient,
932 but I'm just learning about rendering and starting from Phong seemed
933 the most natural way.
936 # If the mesh has vertex colors already, use them,
937 # otherwise turn them on and do some calculations
938 if mesh.hasVertexColours():
940 mesh.hasVertexColours(True)
942 materials = mesh.materials
944 # TODO: use multiple lighting sources
945 light_obj = self.lights[0]
946 light_pos = self._getObjPosition(light_obj)
947 light = light_obj.data
949 camPos = self._getObjPosition(self.cameraObj)
951 # We do per-face color calculation (FLAT Shading), we can easily turn
952 # to a per-vertex calculation if we want to implement some shading
953 # technique. For an example see:
954 # http://www.miralab.unige.ch/papers/368.pdf
961 mat = materials[f.mat]
963 # A new default material
965 mat = Material.New('defMat')
967 L = Vector(light_pos).normalize()
969 V = (Vector(camPos) - Vector(f.v[0].co)).normalize()
971 N = Vector(f.no).normalize()
973 R = 2 * (N*L) * N - L
975 # TODO: Attenuation factor (not used for now)
976 a0 = 1; a1 = 0.0; a2 = 0.0
977 d = (Vector(f.v[0].co) - Vector(light_pos)).length
978 fd = min(1, 1.0/(a0 + a1*d + a2*d*d))
982 ka = mat.getAmb() * Vector([0.1, 0.1, 0.1])
985 # Diffuse component (add light.col for kd)
986 kd = mat.getRef() * Vector(mat.getRGBCol())
987 Ip = light.getEnergy()
988 Idiff = Ip * kd * (N*L)
991 ks = mat.getSpec() * Vector(mat.getSpecCol())
992 ns = mat.getHardness()
993 Ispec = Ip * ks * pow((V * R), ns)
996 ki = Vector([mat.getEmit()]*3)
998 I = ki + Iamb + Idiff + Ispec
1000 # Clamp I values between 0 and 1
1001 I = [ min(c, 1) for c in I]
1002 I = [ max(0, c) for c in I]
1003 tmp_col = [ int(c * 255.0) for c in I]
1005 vcol = NMesh.Col(tmp_col[0], tmp_col[1], tmp_col[2], 255)
1010 def _doEdgesStyle(self, mesh, style):
1011 """Process Mesh Edges.
1013 Examples of algorithms:
1016 given an edge if its adjacent faces have the same normal (that is
1017 they are complanar), than deselect it.
1020 given an edge if one its adjacent faces is frontfacing and the
1021 other is backfacing, than select it, else deselect.
1023 #print "\tTODO: _doEdgeStyle()"
1026 def _doProjection(self, mesh, projector):
1027 """Calculate the Projection for the object.
1029 # TODO: maybe using the object.transform() can be faster?
1031 for v in mesh.verts:
1032 p = projector.doProjection(v.co)
1039 # ---------------------------------------------------------------------
1043 # ---------------------------------------------------------------------
1045 def vectorize(filename):
1046 """The vectorizing process is as follows:
1048 - Instanciate the writer and the renderer
1051 from Blender import Window
1052 editmode = Window.EditMode()
1053 if editmode: Window.EditMode(0)
1055 writer = SVGVectorWriter(filename)
1057 renderer = Renderer()
1058 renderer.doRendering(writer, RENDER_ANIMATION)
1060 if editmode: Window.EditMode(1)
1062 def vectorize_gui(filename):
1065 I would like to keep that simple, really.
1067 Blender.Window.FileSelector (vectorize, 'Save SVG', filename)
1072 if __name__ == "__main__":
1074 basename = Blender.sys.basename(Blender.Get('filename'))
1075 outputfile = Blender.sys.splitext(basename)[0]+".svg"
1077 # with this trick we can run the script in batch mode
1079 vectorize_gui(outputfile)
1081 vectorize(outputfile)