Some stabilization work
[vrm.git] / vrm.py
diff --git a/vrm.py b/vrm.py
index 871131e..c694b77 100755 (executable)
--- a/vrm.py
+++ b/vrm.py
@@ -34,12 +34,12 @@ Tooltip: 'Vector Rendering Method Export Script 0.3'
 #
 # Additional credits:
 #   Thanks to Emilio Aguirre for S2flender from which I took inspirations :)
 #
 # Additional credits:
 #   Thanks to Emilio Aguirre for S2flender from which I took inspirations :)
-#   Thanks to Anthony C. D'Agostino for the backface.py script   
+#   Thanks to Anthony C. D'Agostino for the original backface.py script   
 #
 # ---------------------------------------------------------------------
 
 import Blender
 #
 # ---------------------------------------------------------------------
 
 import Blender
-from Blender import Scene, Object, NMesh, Lamp, Camera
+from Blender import Scene, Object, Mesh, NMesh, Lamp, Camera
 from Blender.Mathutils import *
 from math import *
 
 from Blender.Mathutils import *
 from math import *
 
@@ -60,18 +60,16 @@ class Projector:
     parameter list.
     """
 
     parameter list.
     """
 
-    def __init__(self, cameraObj, obMesh, canvasSize):
+    def __init__(self, cameraObj, canvasRatio):
         """Calculate the projection matrix.
 
         The projection matrix depends, in this case, on the camera settings,
         and also on object transformation matrix.
         """
 
         """Calculate the projection matrix.
 
         The projection matrix depends, in this case, on the camera settings,
         and also on object transformation matrix.
         """
 
-        self.size = canvasSize
-
         camera = cameraObj.getData()
 
         camera = cameraObj.getData()
 
-        aspect = float(canvasSize[0])/float(canvasSize[1])
+        aspect = float(canvasRatio[0])/float(canvasRatio[1])
         near = camera.clipStart
         far = camera.clipEnd
 
         near = camera.clipStart
         far = camera.clipEnd
 
@@ -84,17 +82,19 @@ class Projector:
         else:
             m2 = self._calcPerspectiveMatrix(fovy, aspect, near, far) 
         
         else:
             m2 = self._calcPerspectiveMatrix(fovy, aspect, near, far) 
         
-        m1 = Matrix()
-        mP = Matrix()
 
         # View transformation
 
         # View transformation
-        cam = cameraObj.getInverseMatrix()
+        cam = Matrix(cameraObj.getInverseMatrix())
         cam.transpose() 
         cam.transpose() 
-
-        m1 = obMesh.getMatrix()
-        m1.transpose()
         
         
-        mP = cam * m1
+        # FIXME: remove the commented part, we used to pass object in local
+        # coordinates, but this is not very clean, we should apply modelview
+        # tranformations _before_ (at some other level).
+        #m1 = Matrix(obMesh.getMatrix())
+        #m1.transpose()
+        
+        #mP = cam * m1
+        mP = cam
         mP = m2  * mP
 
         self.projectionMatrix = mP
         mP = m2  * mP
 
         self.projectionMatrix = mP
@@ -111,23 +111,12 @@ class Projector:
         """
         
         # Note that we need the vertex expressed using homogeneous coordinates
         """
         
         # Note that we need the vertex expressed using homogeneous coordinates
-        p = self.projectionMatrix * Vector([v[0], v[1], v[2], 1.0])
-        
-        mW = self.size[0]/2
-        mH = self.size[1]/2
-        
-        if p[3]<=0:
-            p[0] = int(p[0]*mW)+mW
-            p[1] = int(p[1]*mH)+mH
-        else:
-            p[0] = int((p[0]/p[3])*mW)+mW
-            p[1] = int((p[1]/p[3])*mH)+mH
-            
-        # For now we want (0,0) in the top-left corner of the canvas
-        # Mirror and translate along y
-        p[1] *= -1
-        p[1] += self.size[1]
-    
+        p = self.projectionMatrix * Vector(v).resize4D()
+
+        if p[3]>0:
+            p[0] = p[0]/p[3]
+            p[1] = p[1]/p[3]
+
         return p
 
     ##
         return p
 
     ##
@@ -181,7 +170,7 @@ class Projector:
 
 # ---------------------------------------------------------------------
 #
 
 # ---------------------------------------------------------------------
 #
-## Mesh representation class
+## Object representation class
 #
 # ---------------------------------------------------------------------
 
 #
 # ---------------------------------------------------------------------
 
@@ -209,13 +198,15 @@ class VectorWriter:
         - printCanvas(mesh) --- where mesh is as specified before.
     """
     
         - printCanvas(mesh) --- where mesh is as specified before.
     """
     
-    def __init__(self, fileName, canvasSize):
+    def __init__(self, fileName):
         """Open the file named #fileName# and set the canvas size."""
         
         self.file = open(fileName, "w")
         print "Outputting to: ", fileName
 
         """Open the file named #fileName# and set the canvas size."""
         
         self.file = open(fileName, "w")
         print "Outputting to: ", fileName
 
-        self.canvasSize = canvasSize
+
+        context = Scene.GetCurrent().getRenderingContext()
+        self.canvasSize = ( context.imageSizeX(), context.imageSizeY() )
     
 
     ##
     
 
     ##
@@ -245,29 +236,45 @@ class SVGVectorWriter(VectorWriter):
     Sorry.
     """
 
     Sorry.
     """
 
-    def __init__(self, file, canvasSize):
+    def __init__(self, file):
         """Simply call the parent Contructor."""
         """Simply call the parent Contructor."""
-        VectorWriter.__init__(self, file, canvasSize)
+        VectorWriter.__init__(self, file)
 
 
     ##
     # Public Methods
     #
 
 
     ##
     # Public Methods
     #
-    
-    def printCanvas(self, scene):
-        """Convert the scene representation to SVG."""
 
 
+    def open(self):
         self._printHeader()
         self._printHeader()
+
+    def close(self):
+        self._printFooter()
+
         
         
-        for obj in scene:
+    
+    def printCanvas(self, scene, doPrintPolygons=True, doPrintEdges=False, showHiddenEdges=False):
+        """Convert the scene representation to SVG."""
+
+        Objects = scene.getChildren()
+        for obj in Objects:
+
+            if(obj.getType() != 'Mesh'):
+                continue
+            #
+
             self.file.write("<g>\n")
             self.file.write("<g>\n")
+
             
             
-            for face in obj.faces:
-                self._printPolygon(face)
+            if doPrintPolygons:
+                for face in obj.getData().faces:
+                    self._printPolygon(face)
 
 
+            if doPrintEdges:
+                self._printEdges(obj.getData(), showHiddenEdges)
+            
             self.file.write("</g>\n")
         
             self.file.write("</g>\n")
         
-        self._printFooter()
     
     ##  
     # Private Methods
     
     ##  
     # Private Methods
@@ -277,7 +284,9 @@ class SVGVectorWriter(VectorWriter):
         """Print SVG header."""
 
         self.file.write("<?xml version=\"1.0\"?>\n")
         """Print SVG header."""
 
         self.file.write("<?xml version=\"1.0\"?>\n")
-        self.file.write("<svg version=\"1.2\"\n")
+        self.file.write("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n")
+        self.file.write("\t\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n")
+        self.file.write("<svg version=\"1.1\"\n")
         self.file.write("\txmlns=\"http://www.w3.org/2000/svg\"\n")
         self.file.write("\twidth=\"%d\" height=\"%d\" streamable=\"true\">\n\n" %
                 self.canvasSize)
         self.file.write("\txmlns=\"http://www.w3.org/2000/svg\"\n")
         self.file.write("\twidth=\"%d\" height=\"%d\" streamable=\"true\">\n\n" %
                 self.canvasSize)
@@ -288,32 +297,92 @@ class SVGVectorWriter(VectorWriter):
         self.file.write("\n</svg>\n")
         self.file.close()
 
         self.file.write("\n</svg>\n")
         self.file.close()
 
+    def _printEdges(self, mesh, showHiddenEdges=False):
+        """Print the wireframe using mesh edges... is this the correct way?
+        """
+
+        stroke_width=0.5
+        stroke_col = [0, 0, 0]
+        
+        self.file.write("<g>\n")
+
+        for e in mesh.edges:
+            
+            hidden_stroke_style = ""
+            
+            # And edge is selected if both vertives are selected
+            if e.v1.sel == 0 or e.v2.sel == 0:
+                if showHiddenEdges == False:
+                    continue
+                else:
+                    hidden_stroke_style = ";\n stroke-dasharray:3, 3"
+
+            p1 = self._calcCanvasCoord(e.v1)
+            p2 = self._calcCanvasCoord(e.v2)
+            
+            self.file.write("<line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"\n"
+                    % ( p1[0], p1[1], p2[0], p2[1] ) )
+            self.file.write(" style=\"stroke:rgb("+str(stroke_col[0])+","+str(stroke_col[1])+","+str(stroke_col[2])+");")
+            self.file.write(" stroke-width:"+str(stroke_width)+";\n")
+            self.file.write(" stroke-linecap:round;stroke-linejoin:round")
+            self.file.write(hidden_stroke_style)
+            self.file.write("\"/>\n")
+
+        self.file.write("</g>\n")
+            
+        
+
     def _printPolygon(self, face):
         """Print our primitive, finally.
     def _printPolygon(self, face):
         """Print our primitive, finally.
-
-        There is no color Handling for now, *FIX!*
         """
 
         """
 
-        stroke_width=1
+        wireframe = False
+        
+        stroke_width=0.5
         
         self.file.write("<polygon points=\"")
 
         
         self.file.write("<polygon points=\"")
 
-        i = 0
         for v in face:
         for v in face:
-            if i != 0:
-                self.file.write(", ")
-
-            i+=1
-            
-            self.file.write("%g, %g" % (v[0], v[1]))
+            p = self._calcCanvasCoord(v)
+            self.file.write("%g,%g " % (p[0], p[1]))
         
         
-        color = [ int(c*255) for c in face.col]
-
+        self.file.seek(-1,1) # get rid of the last space
         self.file.write("\"\n")
         self.file.write("\"\n")
+        
+        #take as face color the first vertex color
+        if face.col:
+            fcol = face.col[0]
+            color = [fcol.r, fcol.g, fcol.b]
+        else:
+            color = [ 255, 255, 255]
+
+        stroke_col = [0, 0, 0]
+        if not wireframe:
+            stroke_col = color
+
         self.file.write("\tstyle=\"fill:rgb("+str(color[0])+","+str(color[1])+","+str(color[2])+");")
         self.file.write("\tstyle=\"fill:rgb("+str(color[0])+","+str(color[1])+","+str(color[2])+");")
-        self.file.write(" stroke:rgb(0,0,0);")
+        self.file.write(" stroke:rgb("+str(stroke_col[0])+","+str(stroke_col[1])+","+str(stroke_col[2])+");")
         self.file.write(" stroke-width:"+str(stroke_width)+";\n")
         self.file.write(" stroke-width:"+str(stroke_width)+";\n")
-        self.file.write(" stroke-linecap:round;stroke-linejoin:round\"/>\n")
+        self.file.write(" stroke-linecap:round;stroke-linejoin:round")
+        self.file.write("\"/>\n")
+
+    def _calcCanvasCoord(self, v):
+
+        pt = Vector([0, 0, 0])
+        
+        mW = self.canvasSize[0]/2
+        mH = self.canvasSize[1]/2
+
+        # rescale to canvas size
+        pt[0] = round(v[0]*mW)+mW
+        pt[1] = round(v[1]*mH)+mH
+         
+        # For now we want (0,0) in the top-left corner of the canvas
+        # Mirror and translate along y
+        pt[1] *= -1
+        pt[1] += self.canvasSize[1]
+        
+        return pt
 
 
 # ---------------------------------------------------------------------
 
 
 # ---------------------------------------------------------------------
@@ -322,31 +391,10 @@ class SVGVectorWriter(VectorWriter):
 #
 # ---------------------------------------------------------------------
 
 #
 # ---------------------------------------------------------------------
 
-def RotatePoint(PX,PY,PZ,AngleX,AngleY,AngleZ):
-    
-    NewPoint = []
-    # Rotate X
-    NewY = (PY * cos(AngleX))-(PZ * sin(AngleX))
-    NewZ = (PZ * cos(AngleX))+(PY * sin(AngleX))
-    # Rotate Y
-    PZ = NewZ
-    PY = NewY
-    NewZ = (PZ * cos(AngleY))-(PX * sin(AngleY))
-    NewX = (PX * cos(AngleY))+(PZ * sin(AngleY))
-    PX = NewX
-    PZ = NewZ
-    # Rotate Z
-    NewX = (PX * cos(AngleZ))-(PY * sin(AngleZ))
-    NewY = (PY * cos(AngleZ))+(PX * sin(AngleZ))
-    NewPoint.append(NewX)
-    NewPoint.append(NewY)
-    NewPoint.append(NewZ)
-    return NewPoint
-
 class Renderer:
     """Render a scene viewed from a given camera.
     
 class Renderer:
     """Render a scene viewed from a given camera.
     
-    This class is responsible of the rendering process, hence transormation
+    This class is responsible of the rendering process, hence transformation
     and projection of the ojects in the scene are invoked by the renderer.
 
     The user can optionally provide a specific camera for the rendering, see
     and projection of the ojects in the scene are invoked by the renderer.
 
     The user can optionally provide a specific camera for the rendering, see
@@ -354,56 +402,133 @@ class Renderer:
     """
 
     def __init__(self):
     """
 
     def __init__(self):
-        """Set the canvas size to a defaulr value.
-        
-        The only instance attribute here is the canvas size, which can be
-        queryed to the renderer by other entities.
+        """Make the rendering process only for the current scene by default.
         """
         """
-        self.canvasSize = (0.0, 0.0)
+
+        # Render the current Scene set as a READ-ONLY property
+        self._SCENE = Scene.GetCurrent()
+        
+        # Use the aspect ratio of the scene rendering context
+        context = self._SCENE.getRenderingContext()
+        self.canvasRatio = (context.aspectRatioX(), context.aspectRatioY())
+
+        # Render from the currently active camera 
+        self.camera = self._SCENE.getCurrentCamera()
 
 
     ##
     # Public Methods
     #
 
 
 
     ##
     # Public Methods
     #
 
-    def getCanvasSize(self):
-        """Return the current canvas size read from Blender rendering context"""
-        return self.canvasSize
+    def doRendering(self, outputWriter, animation=0):
+        """Render picture or animation and write it out.
         
         
-    def doRendering(self, scene, cameraObj=None):
-        """Control the rendering process.
+        The parameters are:
+            - a Vector writer object than will be used to output the result.
+            - a flag to tell if we want to render an animation or the only
+              current frame.
+        """
         
         
-        Here we control the entire rendering process invoking the operation
-        needed to transforma project the 3D scene in two dimensions.
+        context = self._SCENE.getRenderingContext()
+        currentFrame = context.currentFrame()
 
 
-        Parameters:
-        scene --- the Blender Scene to render
-        cameraObj --- the camera object to use for the viewing processing
-        """
+        # Handle the animation case
+        if animation == 0:
+            startFrame = currentFrame
+            endFrame = startFrame
+        else:
+            startFrame = context.startFrame()
+            endFrame = context.endFrame()
+        
+        # Do the rendering process frame by frame
+        print "Start Rendering!"
+        for f in range(startFrame, endFrame+1):
+            context.currentFrame(f)
+            renderedScene = self.doRenderScene(self._SCENE)
+            outputWriter.printCanvas(renderedScene,
+                    doPrintPolygons=False, doPrintEdges=True, showHiddenEdges=True)
+            
+            # clear the rendered scene
+            self._SCENE.makeCurrent()
+            Scene.unlink(renderedScene)
+            del renderedScene
+
+        print "Done!"
+        context.currentFrame(currentFrame)
 
 
-        if cameraObj == None:
-            cameraObj = scene.getCurrentCamera()
+
+
+    def doRenderScene(self, inputScene):
+        """Control the rendering process.
         
         
-        context = scene.getRenderingContext()
-        self.canvasSize = (context.imageSizeX(), context.imageSizeY())
+        Here we control the entire rendering process invoking the operation
+        needed to transform and project the 3D scene in two dimensions.
+        """
         
         
-        Objects = scene.getChildren()
+        # Use some temporary workspace, a full copy of the scene
+        workScene = inputScene.copy(2)
+
+        # Get a projector for this scene.
+        # NOTE: the projector wants object in world coordinates,
+        # so we should apply modelview transformations _before_
+        # projection transformations
+        proj = Projector(self.camera, self.canvasRatio)
+            
+        # global processing of the scene
+        self._doDepthSorting(workScene)
         
         
-        # A structure to store the transformed scene
-        newscene = []
+        # Per object activities
+        Objects = workScene.getChildren()
         
         for obj in Objects:
             
         
         for obj in Objects:
             
-            if (obj.getType() != "Mesh"):
+            if (obj.getType() != 'Mesh'):
                 print "Type:", obj.getType(), "\tSorry, only mesh Object supported!"
                 continue
                 print "Type:", obj.getType(), "\tSorry, only mesh Object supported!"
                 continue
+            #
+
+            self._doModelViewTransformations(obj)
+
+            self._doBackFaceCulling(obj)
+            
+            self._doColorAndLighting(obj)
+
+            # 'style' can be a function that determine
+            # if an edge should be showed?
+            self._doEdgesStyle(obj, style=None)
+           
+            self._doProjection(obj, proj)
+
+        return workScene
+
 
 
+    def oldRenderScene(scene):
+        
+        # Per object activities
+        Objects = workScene.getChildren()
+        
+        for obj in Objects:
+            
+            if (obj.getType() != 'Mesh'):
+                print "Type:", obj.getType(), "\tSorry, only mesh Object supported!"
+                continue
+            
             # Get a projector for this object
             # Get a projector for this object
-            proj = Projector(cameraObj, obj, self.canvasSize)
+            proj = Projector(self.camera, obj, self.canvasSize)
 
             # Let's store the transformed data
 
             # Let's store the transformed data
-            transformed_mesh = NMesh.New(obj.name)
+            transformed_mesh = NMesh.New("flat"+obj.name)
+            transformed_mesh.hasVertexColours(1)
+
+            # process Edges
+            self._doProcessEdges(obj)
+            
+            for v in obj.getData().verts:
+                transformed_mesh.verts.append(v)
+            transformed_mesh.edges = self._processEdges(obj.getData().edges)
+            #print transformed_mesh.edges
 
 
+            
             # Store the materials
             materials = obj.getData().getMaterials()
 
             # Store the materials
             materials = obj.getData().getMaterials()
 
@@ -415,23 +540,30 @@ class Renderer:
                 if self._isFaceVisible(face, obj, cameraObj):
                     
                     # Store transformed face
                 if self._isFaceVisible(face, obj, cameraObj):
                     
                     # Store transformed face
-                    transformed_face = []
+                    newface = NMesh.Face()
 
                     for vert in face:
 
                         p = proj.doProjection(vert.co)
 
 
                     for vert in face:
 
                         p = proj.doProjection(vert.co)
 
-                        transformed_vert = NMesh.Vert(p[0], p[1], p[2])
-                        transformed_face.append(transformed_vert)
+                        tmp_vert = NMesh.Vert(p[0], p[1], p[2])
 
 
-                    newface = NMesh.Face(transformed_face)
+                        # Add the vert to the mesh
+                        transformed_mesh.verts.append(tmp_vert)
+                        
+                        newface.v.append(tmp_vert)
+                        
                     
                     # Per-face color calculation
                     # code taken mostly from the original vrm script
                     # TODO: understand the code and rewrite it clearly
                     
                     # Per-face color calculation
                     # code taken mostly from the original vrm script
                     # TODO: understand the code and rewrite it clearly
-                    ambient = -250
-                    fakelight = [10, 10, 15]
-                    norm = face.normal
+                    ambient = -150
+                    
+                    fakelight = Object.Get("Lamp").loc
+                    if fakelight == None:
+                        fakelight = [1.0, 1.0, -0.3]
+
+                    norm = Vector(face.no)
                     vektori = (norm[0]*fakelight[0]+norm[1]*fakelight[1]+norm[2]*fakelight[2])
                     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)))
                     intensity = floor(ambient + 200*acos(vektori/vduzine))/200
                     vektori = (norm[0]*fakelight[0]+norm[1]*fakelight[1]+norm[2]*fakelight[2])
                     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)))
                     intensity = floor(ambient + 200*acos(vektori/vduzine))/200
@@ -439,88 +571,203 @@ class Renderer:
                         intensity = 0
 
                     if materials:
                         intensity = 0
 
                     if materials:
-                        newface.col = materials[face.mat].getRGBCol()
+                        tmp_col = materials[face.mat].getRGBCol()
                     else:
                     else:
-                        newface.col = [0.5, 0.5, 0.5]
+                        tmp_col = [0.5, 0.5, 0.5]
                         
                         
-                    newface.col = [ (c>0) and (c-intensity) for c in newface.col]
+                    tmp_col = [ (c>intensity) and int(round((c-intensity)*10)*25.5) for c in tmp_col ]
+
+                    vcol = NMesh.Col(tmp_col[0], tmp_col[1], tmp_col[2])
+                    newface.col = [vcol, vcol, vcol, 255]
                     
                     transformed_mesh.addFace(newface)
 
             # at the end of the loop on obj
             
                     
                     transformed_mesh.addFace(newface)
 
             # at the end of the loop on obj
             
-            #transformed_object = NMesh.PutRaw(transformed_mesh)
-            newscene.append(transformed_mesh)
+            transformed_obj = Object.New(obj.getType(), "flat"+obj.name)
+            transformed_obj.link(transformed_mesh)
+            transformed_obj.loc = obj.loc
+            newscene.link(transformed_obj)
 
 
-        # reverse the order (TODO: See how is the object order in NMesh)
-        #newscene.reverse()
         
         
-        return newscene
+        return workScene
 
 
     ##
     # Private Methods
     #
 
 
 
     ##
     # Private Methods
     #
 
-    def _isFaceVisible(self, face, obj, cameraObj):
-        """Determine if the face is visible from the current camera.
+    # Faces methods
 
 
-        The following code is taken basicly from the original vrm script.
+    def _isFaceVisible(self, face, obj, camObj):
+        """Determine if a face of an object is visible from a given camera.
+        
+        The normals need to be transformed, but note that we should apply only the
+        rotation part of the tranformation matrix, since the normals are
+        normalized and they can be intended as starting from the origin.
+
+        The view vector is calculated from the camera location and one of the
+        vertices of the face (expressed in World coordinates, after applying
+        modelview transformations).
+
+        After those transformations we determine if a face is visible by computing
+        the angle between the face normal and the view vector, this angle
+        corresponds somehow to the dot product between the two. If the product
+        results <= 0 then the angle between the two vectors is less that 90
+        degrees and then the face is visible.
+
+        There is no need to normalize those vectors since we are only interested in
+        the sign of the cross product and not in the product value.
         """
 
         """
 
-        camera = cameraObj
-
-        numvert = len(face)
-
-        # backface culling
-
-        # translate and rotate according to the object matrix
-        # and then translate according to the camera position
-        #m = obj.getMatrix()
-        #m.transpose()
-        
-        #a = m*Vector(face[0]) - Vector(cameraObj.loc)
-        #b = m*Vector(face[1]) - Vector(cameraObj.loc)
-        #c = m*Vector(face[numvert-1]) - Vector(cameraObj.loc)
-        
-        a = []
-        a.append(face[0][0])
-        a.append(face[0][1])
-        a.append(face[0][2])
-        a = RotatePoint(a[0], a[1], a[2], obj.RotX, obj.RotY, obj.RotZ)
-        a[0] += obj.LocX - camera.LocX
-        a[1] += obj.LocY - camera.LocY
-        a[2] += obj.LocZ - camera.LocZ
-        b = []
-        b.append(face[1][0])
-        b.append(face[1][1])
-        b.append(face[1][2])
-        b = RotatePoint(b[0], b[1], b[2], obj.RotX, obj.RotY, obj.RotZ)
-        b[0] += obj.LocX - camera.LocX
-        b[1] += obj.LocY - camera.LocY
-        b[2] += obj.LocZ - camera.LocZ
-        c = []
-        c.append(face[numvert-1][0])
-        c.append(face[numvert-1][1])
-        c.append(face[numvert-1][2])
-        c = RotatePoint(c[0], c[1], c[2], obj.RotX, obj.RotY, obj.RotZ)
-        c[0] += obj.LocX - camera.LocX
-        c[1] += obj.LocY - camera.LocY
-        c[2] += obj.LocZ - camera.LocZ
-
-        norm = Vector([0,0,0])
-        norm[0] = (b[1] - a[1])*(c[2] - a[2]) - (c[1] - a[1])*(b[2] - a[2])
-        norm[1] = -((b[0] - a[0])*(c[2] - a[2]) - (c[0] - a[0])*(b[2] - a[2]))
-        norm[2] = (b[0] - a[0])*(c[1] - a[1]) - (c[0] - a[0])*(b[1] - a[1])
-
-        d = norm[0]*a[0] + norm[1]*a[1] + norm[2]*a[2]
-        # d = DotVecs(norm, Vector(a))
-
-        return (d<0)
-
-    def _doClipping(face):
+        # The transformation matrix of the object
+        mObj = Matrix(obj.getMatrix())
+        mObj.transpose()
+
+        # The normal after applying the current object rotation
+        #normal = mObj.rotationPart() * Vector(face.no)
+        normal = Vector(face.no)
+
+        # View vector in orthographics projections can be considered simply s the
+        # camera position
+        #view_vect = Vector(camObj.loc)
+
+        # View vector as in perspective projections
+        # it is the dofference between the camera position and
+        # one point of the face, we choose the first point,
+        # but maybe a better choice may be the farthest point from the camera.
+        point = Vector(face[0].co)
+        #point = mObj * point.resize4D()
+        #point.resize3D()
+        view_vect = Vector(camObj.loc) - point
+        
+
+        # if d <= 0 the face is visible from the camera
+        d = view_vect * normal
+        
+        if d <= 0:
+            return False
+        else:
+            return True
+
+
+    # Scene methods
+
+    def _doClipping():
+        return
+
+    def _doDepthSorting(self, scene):
+
+        cameraObj = self.camera
+        Objects = scene.getChildren()
+
+        Objects.sort(lambda obj1, obj2: 
+                cmp(Vector(Vector(cameraObj.loc) - Vector(obj1.loc)).length,
+                    Vector(Vector(cameraObj.loc) - Vector(obj2.loc)).length
+                    )
+                )
+        
+        # hackish sorting of faces according to the max z value of a vertex
+        for o in Objects:
+
+            if (o.getType() != 'Mesh'):
+                continue
+            #
+
+            mesh = o.data
+            mesh.faces.sort(
+                lambda f1, f2:
+                    # Sort faces according to the min z coordinate in a face
+                    #cmp(min([v[2] for v in f1]), min([v[2] for v in f2])))
+
+                    # Sort faces according to the max z coordinate in a face
+                    cmp(max([v[2] for v in f1]), max([v[2] for v in f2])))
+                    
+                    # Sort faces according to the avg z coordinate in a face
+                    #cmp(sum([v[2] for v in f1])/len(f1), sum([v[2] for v in f2])/len(f2)))
+            mesh.faces.reverse()
+            mesh.update()
+            
+        # update the scene
+        # FIXME: check if it is correct
+        scene.update()
+        #for o in scene.getChildren():
+        #     scene.unlink(o)
+        #for o in Objects:
+        #   scene.link(o)
+
+    # Per object methods
+
+    def _doModelViewTransformations(self, object):
+        if(object.getType() != 'Mesh'):
+            return
+        
+        matMV = object.matrix
+        mesh = object.data
+        mesh.transform(matMV, True)
+        mesh.update()
+
+
+    def _doBackFaceCulling(self, object):
+        if(object.getType() != 'Mesh'):
+            return
+        
+        print "doing Backface Culling"
+        mesh = object.data
+        
+        # Select all vertices, so edges without faces can be displayed
+        for v in mesh.verts:
+            v.sel = 1
+        
+        Mesh.Mode(Mesh.SelectModes['FACE'])
+        # Loop on faces
+        for f in mesh.faces:
+            f.sel = 0
+            if self._isFaceVisible(f, object, self.camera):
+                f.sel = 1
+
+        for f in mesh.faces:
+            if not f.sel:
+                for v in f:
+                    v.sel = 0
+
+        for f in mesh.faces:
+            if f.sel:
+                for v in f:
+                    v.sel = 1
+
+        mesh.update()
+
+        
+
+        #Mesh.Mode(Mesh.SelectModes['VERTEX'])
+
+    def _doColorAndLighting(self, object):
         return
 
         return
 
+    def _doEdgesStyle(self, object, style):
+        """Process Mesh Edges. (For now copy the edge data, in next version it
+        can be a place where recognize silouhettes and/or contours).
+
+        input: an edge list
+        return: a processed edge list
+        """
+        return
+
+    def _doProjection(self, object, projector):
+
+        if(object.getType() != 'Mesh'):
+            return
+        
+        mesh = object.data
+        for v in mesh.verts:
+            p = projector.doProjection(v.co)
+            v[0] = p[0]
+            v[1] = p[1]
+            v[2] = p[2]
+        mesh.update()
+
+
 
 # ---------------------------------------------------------------------
 #
 
 # ---------------------------------------------------------------------
 #
@@ -529,42 +776,42 @@ class Renderer:
 # ---------------------------------------------------------------------
 
 
 # ---------------------------------------------------------------------
 
 
-# hackish sorting of faces according to the max z value of a vertex
-def zSorting(scene):
-    for o in scene:
-        o.faces.sort(lambda f1, f2:
-                # Sort faces according to the min z coordinate in a face
-                #cmp(min([v[2] for v in f1]), min([v[2] for v in f2])))
-
-                # Sort faces according to the max z coordinate in a face
-                cmp(max([v[2] for v in f1]), max([v[2] for v in f2])))
-                
-                # Sort faces according to the avg z coordinate in a face
-                #cmp(sum([v[2] for v in f1])/len(f1), sum([v[2] for v in f2])/len(f2)))
-        o.faces.reverse()
+# FIXME: really hackish code, just to test if the other parts work
     
     
-from Blender import sys
 def vectorize(filename):
 def vectorize(filename):
-
-    print "Filename: %s" % filename
-    print
-    filename = filename.replace('/', sys.sep)
-    print filename
-    print
+    """The vectorizing process is as follows:
+     
+     - Open the writer
+     - Render the scene
+     - Close the writer
+     
+     If you want to render an animation the second pass should be
+     repeated for any frame, and the frame number should be passed to the
+     renderer.
+     """
+    writer = SVGVectorWriter(filename)
+    
+    writer.open()
     
     
-    scene   = Scene.GetCurrent()
     renderer = Renderer()
     renderer = Renderer()
+    renderer.doRendering(writer)
 
 
-    flatScene = renderer.doRendering(scene)
-    canvasSize = renderer.getCanvasSize()
+    writer.close()
+
+
+# Here the main
+if __name__ == "__main__":
+    # with this trick we can run the script in batch mode
+    try:
+        Blender.Window.FileSelector (vectorize, 'Save SVG', "proba.svg")
+        Blender.Redraw()
+    except:
+        from Blender import Window
+        editmode = Window.EditMode()
+        if editmode: Window.EditMode(0)
+
+        vectorize("proba.svg")
+        if editmode: Window.EditMode(1) 
 
 
-    zSorting(flatScene)
 
 
-    writer = SVGVectorWriter(filename, canvasSize)
-    writer.printCanvas(flatScene)
-    
-try:
-    Blender.Window.FileSelector (vectorize, 'Save SVG', "proba.svg")
-except:
-    vectorize("proba.svg")