6 Tooltip: 'Vector Rendering Method Export Script 0.3'
9 # ---------------------------------------------------------------------
10 # Copyright (c) 2006 Antonio Ospite
12 # This program is free software; you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation; either version 2 of the License, or
15 # (at your option) any later version.
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26 # ---------------------------------------------------------------------
28 # NOTE: I do not know who is the original author of 'vrm'.
29 # The present code is almost entirely rewritten from scratch,
30 # but if I have to give credits to anyone, please let me know,
31 # so I can update the copyright.
33 # ---------------------------------------------------------------------
36 # Thanks to Emilio Aguirre for S2flender from which I took inspirations :)
37 # Thanks to Anthony C. D'Agostino for the backface.py script
39 # ---------------------------------------------------------------------
42 from Blender import Scene, Object, NMesh, Lamp, Camera
43 from Blender.Mathutils import *
47 # ---------------------------------------------------------------------
49 ## Projections classes
51 # ---------------------------------------------------------------------
54 """Calculate the projection of an object given the camera.
56 A projector is useful to so some per-object transformation to obtain the
57 projection of an object given the camera.
59 The main method is #doProjection# see the method description for the
63 def __init__(self, cameraObj, obMesh, canvasSize):
64 """Calculate the projection matrix.
66 The projection matrix depends, in this case, on the camera settings,
67 and also on object transformation matrix.
70 self.size = canvasSize
72 camera = cameraObj.getData()
74 aspect = float(canvasSize[0])/float(canvasSize[1])
75 near = camera.clipStart
78 fovy = atan(0.5/aspect/(camera.lens/32))
81 # What projection do we want?
83 m2 = self._calcOrthoMatrix(fovy, aspect, near, far, 17) #camera.scale)
85 m2 = self._calcPerspectiveMatrix(fovy, aspect, near, far)
91 cam = cameraObj.getInverseMatrix()
94 m1 = obMesh.getMatrix()
100 self.projectionMatrix = mP
106 def doProjection(self, v):
107 """Project the point on the view plane.
109 Given a vertex calculate the projection using the current projection
113 # Note that we need the vertex expressed using homogeneous coordinates
114 p = self.projectionMatrix * Vector([v[0], v[1], v[2], 1.0])
120 p[0] = int(p[0]*mW)+mW
121 p[1] = int(p[1]*mH)+mH
123 p[0] = int((p[0]/p[3])*mW)+mW
124 p[1] = int((p[1]/p[3])*mH)+mH
126 # For now we want (0,0) in the top-left corner of the canvas
127 # Mirror and translate along y
137 def _calcPerspectiveMatrix(self, fovy, aspect, near, far):
138 """Return a perspective projection matrix."""
140 top = near * tan(fovy * pi / 360.0)
144 x = (2.0 * near) / (right-left)
145 y = (2.0 * near) / (top-bottom)
146 a = (right+left) / (right-left)
147 b = (top+bottom) / (top - bottom)
148 c = - ((far+near) / (far-near))
149 d = - ((2*far*near)/(far-near))
155 [0.0, 0.0, -1.0, 0.0])
159 def _calcOrthoMatrix(self, fovy, aspect , near, far, scale):
160 """Return an orthogonal projection matrix."""
162 top = near * tan(fovy * pi / 360.0) * (scale * 10)
164 left = bottom * aspect
169 tx = -((right+left)/rl)
170 ty = -((top+bottom)/tb)
174 [2.0/rl, 0.0, 0.0, tx],
175 [0.0, 2.0/tb, 0.0, ty],
176 [0.0, 0.0, 2.0/fn, tz],
177 [0.0, 0.0, 0.0, 1.0])
182 # ---------------------------------------------------------------------
184 ## Mesh representation class
186 # ---------------------------------------------------------------------
188 # TODO: a class to represent the needed properties of a 2D vector image
189 # Just use a NMesh structure?
192 # ---------------------------------------------------------------------
194 ## Vector Drawing Classes
196 # ---------------------------------------------------------------------
202 A class for printing output in a vectorial format.
204 Given a 2D representation of the 3D scene the class is responsible to
205 write it is a vector format.
207 Every subclasses of VectorWriter must have at last the following public
209 - printCanvas(mesh) --- where mesh is as specified before.
212 def __init__(self, fileName, canvasSize):
213 """Open the file named #fileName# and set the canvas size."""
215 self.file = open(fileName, "w")
216 print "Outputting to: ", fileName
218 self.canvasSize = canvasSize
225 def printCanvas(mesh):
241 class SVGVectorWriter(VectorWriter):
242 """A concrete class for writing SVG output.
244 The class does not support animations, yet.
248 def __init__(self, file, canvasSize):
249 """Simply call the parent Contructor."""
250 VectorWriter.__init__(self, file, canvasSize)
257 def printCanvas(self, scene):
258 """Convert the scene representation to SVG."""
263 self.file.write("<g>\n")
265 for face in obj.faces:
266 self._printPolygon(face)
268 self.file.write("</g>\n")
276 def _printHeader(self):
277 """Print SVG header."""
279 self.file.write("<?xml version=\"1.0\"?>\n")
280 self.file.write("<svg version=\"1.2\"\n")
281 self.file.write("\txmlns=\"http://www.w3.org/2000/svg\"\n")
282 self.file.write("\twidth=\"%d\" height=\"%d\" streamable=\"true\">\n\n" %
285 def _printFooter(self):
286 """Print the SVG footer."""
288 self.file.write("\n</svg>\n")
291 def _printPolygon(self, face):
292 """Print our primitive, finally.
294 There is no color Handling for now, *FIX!*
299 self.file.write("<polygon points=\"")
304 self.file.write(", ")
308 self.file.write("%g, %g" % (v[0], v[1]))
310 color = [ int(c*255) for c in face.col]
312 self.file.write("\"\n")
313 self.file.write("\tstyle=\"fill:rgb("+str(color[0])+","+str(color[1])+","+str(color[2])+");")
314 self.file.write(" stroke:rgb(0,0,0);")
315 self.file.write(" stroke-width:"+str(stroke_width)+";\n")
316 self.file.write(" stroke-linecap:round;stroke-linejoin:round\"/>\n")
319 # ---------------------------------------------------------------------
323 # ---------------------------------------------------------------------
325 def RotatePoint(PX,PY,PZ,AngleX,AngleY,AngleZ):
329 NewY = (PY * cos(AngleX))-(PZ * sin(AngleX))
330 NewZ = (PZ * cos(AngleX))+(PY * sin(AngleX))
334 NewZ = (PZ * cos(AngleY))-(PX * sin(AngleY))
335 NewX = (PX * cos(AngleY))+(PZ * sin(AngleY))
339 NewX = (PX * cos(AngleZ))-(PY * sin(AngleZ))
340 NewY = (PY * cos(AngleZ))+(PX * sin(AngleZ))
341 NewPoint.append(NewX)
342 NewPoint.append(NewY)
343 NewPoint.append(NewZ)
347 """Render a scene viewed from a given camera.
349 This class is responsible of the rendering process, hence transormation
350 and projection of the ojects in the scene are invoked by the renderer.
352 The user can optionally provide a specific camera for the rendering, see
353 the #doRendering# method for more informations.
357 """Set the canvas size to a defaulr value.
359 The only instance attribute here is the canvas size, which can be
360 queryed to the renderer by other entities.
362 self.canvasSize = (0.0, 0.0)
369 def getCanvasSize(self):
370 """Return the current canvas size read from Blender rendering context"""
371 return self.canvasSize
373 def doRendering(self, scene, cameraObj=None):
374 """Control the rendering process.
376 Here we control the entire rendering process invoking the operation
377 needed to transforma project the 3D scene in two dimensions.
380 scene --- the Blender Scene to render
381 cameraObj --- the camera object to use for the viewing processing
384 if cameraObj == None:
385 cameraObj = scene.getCurrentCamera()
387 context = scene.getRenderingContext()
388 self.canvasSize = (context.imageSizeX(), context.imageSizeY())
390 Objects = scene.getChildren()
392 # A structure to store the transformed scene
397 if (obj.getType() != "Mesh"):
398 print "Type:", obj.getType(), "\tSorry, only mesh Object supported!"
401 # Get a projector for this object
402 proj = Projector(cameraObj, obj, self.canvasSize)
404 # Let's store the transformed data
405 transformed_mesh = NMesh.New(obj.name)
407 # Store the materials
408 materials = obj.getData().getMaterials()
410 meshfaces = obj.getData().faces
412 for face in meshfaces:
414 # if the face is visible flatten it on the "picture plane"
415 if self._isFaceVisible(face, obj, cameraObj):
417 # Store transformed face
418 transformed_face = []
422 p = proj.doProjection(vert.co)
424 transformed_vert = NMesh.Vert(p[0], p[1], p[2])
425 transformed_face.append(transformed_vert)
427 newface = NMesh.Face(transformed_face)
429 # Per-face color calculation
430 # code taken mostly from the original vrm script
431 # TODO: understand the code and rewrite it clearly
433 fakelight = [10, 10, 15]
435 vektori = (norm[0]*fakelight[0]+norm[1]*fakelight[1]+norm[2]*fakelight[2])
436 vduzine = fabs(sqrt(pow(norm[0],2)+pow(norm[1],2)+pow(norm[2],2))*sqrt(pow(fakelight[0],2)+pow(fakelight[1],2)+pow(fakelight[2],2)))
437 intensity = floor(ambient + 200*acos(vektori/vduzine))/200
442 newface.col = materials[face.mat].getRGBCol()
444 newface.col = [0.5, 0.5, 0.5]
446 newface.col = [ (c>0) and (c-intensity) for c in newface.col]
448 transformed_mesh.addFace(newface)
450 # at the end of the loop on obj
452 #transformed_object = NMesh.PutRaw(transformed_mesh)
453 newscene.append(transformed_mesh)
455 # reverse the order (TODO: See how is the object order in NMesh)
465 def _isFaceVisible(self, face, obj, cameraObj):
466 """Determine if the face is visible from the current camera.
468 The following code is taken basicly from the original vrm script.
477 # translate and rotate according to the object matrix
478 # and then translate according to the camera position
482 #a = m*Vector(face[0]) - Vector(cameraObj.loc)
483 #b = m*Vector(face[1]) - Vector(cameraObj.loc)
484 #c = m*Vector(face[numvert-1]) - Vector(cameraObj.loc)
490 a = RotatePoint(a[0], a[1], a[2], obj.RotX, obj.RotY, obj.RotZ)
491 a[0] += obj.LocX - camera.LocX
492 a[1] += obj.LocY - camera.LocY
493 a[2] += obj.LocZ - camera.LocZ
498 b = RotatePoint(b[0], b[1], b[2], obj.RotX, obj.RotY, obj.RotZ)
499 b[0] += obj.LocX - camera.LocX
500 b[1] += obj.LocY - camera.LocY
501 b[2] += obj.LocZ - camera.LocZ
503 c.append(face[numvert-1][0])
504 c.append(face[numvert-1][1])
505 c.append(face[numvert-1][2])
506 c = RotatePoint(c[0], c[1], c[2], obj.RotX, obj.RotY, obj.RotZ)
507 c[0] += obj.LocX - camera.LocX
508 c[1] += obj.LocY - camera.LocY
509 c[2] += obj.LocZ - camera.LocZ
511 norm = Vector([0,0,0])
512 norm[0] = (b[1] - a[1])*(c[2] - a[2]) - (c[1] - a[1])*(b[2] - a[2])
513 norm[1] = -((b[0] - a[0])*(c[2] - a[2]) - (c[0] - a[0])*(b[2] - a[2]))
514 norm[2] = (b[0] - a[0])*(c[1] - a[1]) - (c[0] - a[0])*(b[1] - a[1])
516 d = norm[0]*a[0] + norm[1]*a[1] + norm[2]*a[2]
517 # d = DotVecs(norm, Vector(a))
521 def _doClipping(face):
525 # ---------------------------------------------------------------------
529 # ---------------------------------------------------------------------
532 # hackish sorting of faces according to the max z value of a vertex
535 o.faces.sort(lambda f1, f2:
536 # Sort faces according to the min z coordinate in a face
537 #cmp(min([v[2] for v in f1]), min([v[2] for v in f2])))
539 # Sort faces according to the max z coordinate in a face
540 cmp(max([v[2] for v in f1]), max([v[2] for v in f2])))
542 # Sort faces according to the avg z coordinate in a face
543 #cmp(sum([v[2] for v in f1])/len(f1), sum([v[2] for v in f2])/len(f2)))
546 from Blender import sys
547 def vectorize(filename):
549 print "Filename: %s" % filename
551 filename = filename.replace('/', sys.sep)
555 scene = Scene.GetCurrent()
556 renderer = Renderer()
558 flatScene = renderer.doRendering(scene)
559 canvasSize = renderer.getCanvasSize()
563 writer = SVGVectorWriter(filename, canvasSize)
564 writer.printCanvas(flatScene)
567 Blender.Window.FileSelector (vectorize, 'Save SVG', "proba.svg")
569 vectorize("proba.svg")