Some improvements in the SVG writer
[vrm.git] / vrm.py
diff --git a/vrm.py b/vrm.py
index 871131e..d85b085 100755 (executable)
--- a/vrm.py
+++ b/vrm.py
@@ -34,7 +34,7 @@ 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   
 #
 # ---------------------------------------------------------------------
 
 #
 # ---------------------------------------------------------------------
 
@@ -84,14 +84,12 @@ 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 = Matrix(obMesh.getMatrix())
         m1.transpose()
         
         mP = cam * m1
         m1.transpose()
         
         mP = cam * m1
@@ -117,11 +115,11 @@ class Projector:
         mH = self.size[1]/2
         
         if p[3]<=0:
         mH = self.size[1]/2
         
         if p[3]<=0:
-            p[0] = int(p[0]*mW)+mW
-            p[1] = int(p[1]*mH)+mH
+            p[0] = round(p[0]*mW)+mW
+            p[1] = round(p[1]*mH)+mH
         else:
         else:
-            p[0] = int((p[0]/p[3])*mW)+mW
-            p[1] = int((p[1]/p[3])*mH)+mH
+            p[0] = round((p[0]/p[3])*mW)+mW
+            p[1] = round((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
             
         # For now we want (0,0) in the top-left corner of the canvas
         # Mirror and translate along y
@@ -181,7 +179,7 @@ class Projector:
 
 # ---------------------------------------------------------------------
 #
 
 # ---------------------------------------------------------------------
 #
-## Mesh representation class
+## Object representation class
 #
 # ---------------------------------------------------------------------
 
 #
 # ---------------------------------------------------------------------
 
@@ -259,12 +257,15 @@ class SVGVectorWriter(VectorWriter):
 
         self._printHeader()
         
 
         self._printHeader()
         
-        for obj in scene:
+        Objects = scene.getChildren()
+        for obj in Objects:
             self.file.write("<g>\n")
             
             self.file.write("<g>\n")
             
-            for face in obj.faces:
+            for face in obj.getData().faces:
                 self._printPolygon(face)
 
                 self._printPolygon(face)
 
+            self._printWireframe(obj.getData())
+            
             self.file.write("</g>\n")
         
         self._printFooter()
             self.file.write("</g>\n")
         
         self._printFooter()
@@ -277,7 +278,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 +291,60 @@ class SVGVectorWriter(VectorWriter):
         self.file.write("\n</svg>\n")
         self.file.close()
 
         self.file.write("\n</svg>\n")
         self.file.close()
 
-    def _printPolygon(self, face):
-        """Print our primitive, finally.
-
-        There is no color Handling for now, *FIX!*
+    def _printWireframe(self, mesh):
+        """Print the wireframe using mesh edges... is this the correct way?
         """
 
         """
 
-        stroke_width=1
+        print mesh.edges
+        print
+        print mesh.verts
         
         
-        self.file.write("<polygon points=\"")
+        stroke_width=0.5
+        stroke_col = [0, 0, 0]
+        
+        self.file.write("<g>\n")
 
 
-        i = 0
-        for v in face:
-            if i != 0:
-                self.file.write(", ")
+        for e in mesh.edges:
+            self.file.write("<line x1=\"%g\" y1=\"%g\" x2=\"%g\" y2=\"%g\"\n"
+                    % ( e.v1[0], e.v1[1], e.v2[0], e.v2[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("\"/>\n")
 
 
-            i+=1
+        self.file.write("</g>\n")
             
             
-            self.file.write("%g, %g" % (v[0], v[1]))
         
         
-        color = [ int(c*255) for c in face.col]
 
 
+    def _printPolygon(self, face):
+        """Print our primitive, finally.
+        """
+        
+        wireframe = False
+        
+        stroke_width=0.5
+        
+        self.file.write("<polygon points=\"")
+
+        for v in face:
+            self.file.write("%g,%g " % (v[0], v[1]))
+        
+        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
+        fcol = face.col[0]
+        color = [fcol.r, fcol.g, fcol.b]
+
+        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")
 
 
 # ---------------------------------------------------------------------
 
 
 # ---------------------------------------------------------------------
@@ -390,7 +421,7 @@ class Renderer:
         Objects = scene.getChildren()
         
         # A structure to store the transformed scene
         Objects = scene.getChildren()
         
         # A structure to store the transformed scene
-        newscene = []
+        newscene = Scene.New("flat"+scene.name)
         
         for obj in Objects:
             
         
         for obj in Objects:
             
@@ -402,8 +433,16 @@ class Renderer:
             proj = Projector(cameraObj, obj, self.canvasSize)
 
             # Let's store the transformed data
             proj = Projector(cameraObj, obj, self.canvasSize)
 
             # 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
+            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()
 
@@ -412,26 +451,33 @@ class Renderer:
             for face in meshfaces:
 
                 # if the face is visible flatten it on the "picture plane"
             for face in meshfaces:
 
                 # if the face is visible flatten it on the "picture plane"
-                if self._isFaceVisible(face, obj, cameraObj):
+                if self._isFaceVisible_old(face, obj, cameraObj):
                     
                     # Store transformed face
                     
                     # 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,21 +485,24 @@ 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 newscene
 
@@ -462,7 +511,7 @@ class Renderer:
     # Private Methods
     #
 
     # Private Methods
     #
 
-    def _isFaceVisible(self, face, obj, cameraObj):
+    def _isFaceVisible_old(self, face, obj, cameraObj):
         """Determine if the face is visible from the current camera.
 
         The following code is taken basicly from the original vrm script.
         """Determine if the face is visible from the current camera.
 
         The following code is taken basicly from the original vrm script.
@@ -508,19 +557,66 @@ class Renderer:
         c[1] += obj.LocY - camera.LocY
         c[2] += obj.LocZ - camera.LocZ
 
         c[1] += obj.LocY - camera.LocY
         c[2] += obj.LocZ - camera.LocZ
 
-        norm = Vector([0,0,0])
+        norm = [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]
         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))
+        #d = DotVecs(Vector(norm), Vector(a))
+
+        return (d<0)
+    
+    def _isFaceVisible(self, face, obj, cameraObj):
+        """Determine if the face is visible from the current camera.
+
+        The following code is taken basicly from the original vrm script.
+        """
+
+        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)
+
+        norm = m*Vector(face.no)
+
+        d = DotVecs(norm, a)
 
         return (d<0)
 
 
         return (d<0)
 
-    def _doClipping(face):
+
+    def _doClipping():
+        return
+
+
+    # Per object methods
+
+    def _doVisibleSurfaceDetermination(object):
+        return
+
+    def _doColorizing(object):
         return
 
         return
 
+    def _doStylizingEdges(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
+
+
 
 # ---------------------------------------------------------------------
 #
 
 # ---------------------------------------------------------------------
 #
@@ -529,10 +625,24 @@ 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:
+# FIXME: really hackish code, just to test if the other parts work
+def depthSorting(scene):
+
+    cameraObj = Scene.GetCurrent().getCurrentCamera()
+    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:
+
+        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 min z coordinate in a face
                 #cmp(min([v[2] for v in f1]), min([v[2] for v in f2])))
 
@@ -541,30 +651,48 @@ def zSorting(scene):
                 
                 # 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)))
                 
                 # 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()
+        mesh.faces.reverse()
+        mesh.update()
+        
+    # update the scene
+    for o in scene.getChildren():
+        scene.unlink(o)
+    for o in Objects:
+        scene.link(o)
     
     
-from Blender import sys
 def vectorize(filename):
 def vectorize(filename):
+    """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.
+     """
 
     print "Filename: %s" % filename
 
     print "Filename: %s" % filename
-    print
-    filename = filename.replace('/', sys.sep)
-    print filename
-    print
     
     
-    scene   = Scene.GetCurrent()
+    scene = Scene.GetCurrent()
     renderer = Renderer()
     renderer = Renderer()
-
+    
     flatScene = renderer.doRendering(scene)
     canvasSize = renderer.getCanvasSize()
 
     flatScene = renderer.doRendering(scene)
     canvasSize = renderer.getCanvasSize()
 
-    zSorting(flatScene)
+    depthSorting(flatScene)
 
     writer = SVGVectorWriter(filename, canvasSize)
     writer.printCanvas(flatScene)
 
     writer = SVGVectorWriter(filename, canvasSize)
     writer.printCanvas(flatScene)
-    
-try:
-    Blender.Window.FileSelector (vectorize, 'Save SVG', "proba.svg")
-except:
-    vectorize("proba.svg")
+
+    Blender.Scene.unlink(flatScene)
+    del flatScene
+
+# 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")
+    except:
+        vectorize("proba.svg")