X-Git-Url: https://git.ao2.it/vrm.git/blobdiff_plain/e393259b57de5b2e22bcdeb498839eee817a4432..50d03fe3eb45bdc4a82144565911695a58e82f85:/vrm.py diff --git a/vrm.py b/vrm.py index aa73b10..f7382a7 100755 --- a/vrm.py +++ b/vrm.py @@ -1,13 +1,13 @@ #!BPY """ Name: 'VRM' -Blender: 241 +Blender: 242 Group: 'Render' Tooltip: 'Vector Rendering Method script' """ __author__ = "Antonio Ospite" -__url__ = ["http://vrm.projects.blender.org"] +__url__ = ["http://projects.blender.org/projects/vrm"] __version__ = "0.3" __bpydoc__ = """\ @@ -42,22 +42,24 @@ __bpydoc__ = """\ # --------------------------------------------------------------------- # # Things TODO for a next release: -# - Switch to the Mesh structure, should be considerably faster -# (partially done, but with Mesh we cannot sort faces, yet) +# - Use multiple lighting sources in color calculation, +# (this is part of the "shading refactor") and use light color! +# - FIX the issue with negative scales in object tranformations! # - Use a better depth sorting algorithm +# - Implement clipping of primitives and do handle object intersections. +# (for now only clipping away whole objects is supported). # - Review how selections are made (this script uses selection states of # primitives to represent visibility infos) -# - Implement clipping of primitives and do handle object intersections. -# (for now only clipping for whole objects is supported). -# - Implement Edge Styles (silhouettes, contours, etc.) (partially done). -# - Implement Edge coloring -# - Use multiple lighting sources in color calculation -# - Implement Shading Styles? (for now we use Flat Shading). # - Use a data structure other than Mesh to represent the 2D image? -# Think to a way to merge adjacent polygons that have the same color. +# Think to a way to merge (adjacent) polygons that have the same color. # Or a way to use paths for silhouettes and contours. -# - Add Vector Writers other that SVG. -# - Consider SMIL for animation handling instead of ECMA Script? +# - Consider SMIL for animation handling instead of ECMA Script? (Firefox do +# not support SMIL for animations) +# - Switch to the Mesh structure, should be considerably faster +# (partially done, but with Mesh we cannot sort faces, yet) +# - Implement Edge Styles (silhouettes, contours, etc.) (partially done). +# - Implement Shading Styles? (for now we use Flat Shading) (partially done). +# - Add Vector Writers other than SVG. # # --------------------------------------------------------------------- # @@ -78,20 +80,24 @@ from math import * # Some global settings -PRINT_POLYGONS = True +class config: + polygons = dict() + polygons['SHOW'] = True + polygons['SHADING'] = 'TOON' + # Hidden to the user for now + polygons['EXPANSION_TRICK'] = True -POLYGON_EXPANSION_TRICK = True # Hidden to the user for now + edges = dict() + edges['SHOW'] = True + edges['SHOW_HIDDEN'] = False + edges['STYLE'] = 'SILHOUETTE' + edges['WIDTH'] = 2 + edges['COLOR'] = [0, 0, 0] -PRINT_EDGES = False -SHOW_HIDDEN_EDGES = False -EDGE_STYLE = 'silhouette' -EDGES_WIDTH = 0.5 - -RENDER_ANIMATION = False - -OPTIMIZE_FOR_SPACE = True - -OUTPUT_FORMAT = 'SVG' + output = dict() + output['FORMAT'] = 'SVG' + output['ANIMATION'] = False + output['JOIN_OBJECTS'] = True @@ -115,10 +121,10 @@ class MeshUtils: return adjface_list - def isVisibleEdge(e, mesh): - """Normal edge selection rule. + def isMeshEdge(e, mesh): + """Mesh edge rule. - An edge is visible if _any_ of its adjacent faces is selected. + A mesh edge is visible if _any_ of its adjacent faces is selected. Note: if the edge has no adjacent faces we want to show it as well, useful for "edge only" portion of objects. """ @@ -153,9 +159,29 @@ class MeshUtils: else: return False + def toonShading(u): + + levels = 2 + texels = 2*levels - 1 + map = [0.0] + [(i)/float(texels-1) for i in range(1, texels-1) ] + [1.0] + + v = 1.0 + for i in range(0, len(map)-1): + pivot = (map[i]+map[i+1])/2.0 + j = int(u>pivot) + + v = map[i+j] + + if v0: + # Perspective division + if p[3] != 0: p[0] = p[0]/p[3] p[1] = p[1]/p[3] + p[2] = p[2]/p[3] # restore the size p[3] = 1.0 @@ -232,6 +259,7 @@ class Projector: return p + ## # Private methods # @@ -408,6 +436,7 @@ class SVGVectorWriter(VectorWriter): self.file.write("\n" % (framenumber, framestyle) ) + for obj in Objects: if(obj.getType() != 'Mesh'): @@ -506,7 +535,7 @@ class SVGVectorWriter(VectorWriter): self.file.write("\n\n") - def _printPolygons(self, mesh): + def _printPolygons(self, mesh): """Print the selected (visible) polygons. """ @@ -519,9 +548,12 @@ class SVGVectorWriter(VectorWriter): if not face.sel: continue - self.file.write("\n") @@ -566,8 +596,8 @@ class SVGVectorWriter(VectorWriter): """Print the wireframe using mesh edges. """ - stroke_width=EDGES_WIDTH - stroke_col = [0, 0, 0] + stroke_width = config.edges['WIDTH'] + stroke_col = config.edges['COLOR'] self.file.write("\n") @@ -575,8 +605,6 @@ class SVGVectorWriter(VectorWriter): hidden_stroke_style = "" - # We consider an edge visible if _both_ its vertices are selected, - # hence an edge is hidden if _any_ of its vertices is deselected. if e.sel == 0: if showHiddenEdges == False: continue @@ -604,17 +632,19 @@ class SVGVectorWriter(VectorWriter): # # --------------------------------------------------------------------- -# A dictionary to collect all the different edge styles and their edge -# selection criteria -edgeSelectionStyles = { - 'normal': MeshUtils.isVisibleEdge, - 'silhouette': MeshUtils.isSilhouetteEdge - } +# A dictionary to collect different shading style methods +shadingStyles = dict() +shadingStyles['FLAT'] = None +shadingStyles['TOON'] = None + +# A dictionary to collect different edge style methods +edgeStyles = dict() +edgeStyles['MESH'] = MeshUtils.isMeshEdge +edgeStyles['SILHOUETTE'] = MeshUtils.isSilhouetteEdge # A dictionary to collect the supported output formats -outputWriters = { - 'SVG': SVGVectorWriter, - } +outputWriters = dict() +outputWriters['SVG'] = SVGVectorWriter class Renderer: @@ -647,6 +677,12 @@ class Renderer: # Render from the currently active camera self.cameraObj = self._SCENE.getCurrentCamera() + # Get a projector for this camera. + # NOTE: the projector wants object in world coordinates, + # so we should remember to apply modelview transformations + # _before_ we do projection transformations. + self.proj = Projector(self.cameraObj, self.canvasRatio) + # Get the list of lighting sources obj_lst = self._SCENE.getChildren() self.lights = [ o for o in obj_lst if o.getType() == 'Lamp'] @@ -675,11 +711,11 @@ class Renderer: """ context = self._SCENE.getRenderingContext() - currentFrame = context.currentFrame() + origCurrentFrame = context.currentFrame() # Handle the animation case if not animation: - startFrame = currentFrame + startFrame = origCurrentFrame endFrame = startFrame outputWriter.open() else: @@ -692,88 +728,85 @@ class Renderer: for f in range(startFrame, endFrame+1): context.currentFrame(f) - renderedScene = self.doRenderScene(self._SCENE) + # Use some temporary workspace, a full copy of the scene + inputScene = self._SCENE.copy(2) + # And Set our camera accordingly + self.cameraObj = inputScene.getCurrentCamera() + + try: + renderedScene = self.doRenderScene(inputScene) + except : + print "There was an error! Aborting." + import traceback + print traceback.print_exc() + + self._SCENE.makeCurrent() + Scene.unlink(inputScene) + del inputScene + return + outputWriter.printCanvas(renderedScene, - doPrintPolygons = PRINT_POLYGONS, - doPrintEdges = PRINT_EDGES, - showHiddenEdges = SHOW_HIDDEN_EDGES) + doPrintPolygons = config.polygons['SHOW'], + doPrintEdges = config.edges['SHOW'], + showHiddenEdges = config.edges['SHOW_HIDDEN']) # clear the rendered scene self._SCENE.makeCurrent() - Scene.unlink(renderedScene) - del renderedScene + #Scene.unlink(renderedScene) + #del renderedScene outputWriter.close() print "Done!" - context.currentFrame(currentFrame) + context.currentFrame(origCurrentFrame) - def doRenderScene(self, inputScene): + def doRenderScene(self, workScene): """Control the rendering process. Here we control the entire rendering process invoking the operation needed to transform and project the 3D scene in two dimensions. """ - # 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.cameraObj, self.canvasRatio) - # global processing of the scene - self._doConvertGeometricObjToMesh(workScene) - self._doSceneClipping(workScene) + self._doConvertGeometricObjToMesh(workScene) - # XXX: Joining objects does not work in batch mode!! - # Do not touch the following if, please :) - - global OPTIMIZE_FOR_SPACE - if Blender.mode == 'background': - print "\nWARNING! Joining objects not supported in background mode!\n" - OPTIMIZE_FOR_SPACE = False - - if OPTIMIZE_FOR_SPACE: + if config.output['JOIN_OBJECTS']: self._joinMeshObjectsInScene(workScene) - self._doSceneDepthSorting(workScene) # Per object activities Objects = workScene.getChildren() for obj in Objects: - + if obj.getType() != 'Mesh': print "Only Mesh supported! - Skipping type:", obj.getType() continue print "Rendering: ", obj.getName() - mesh = obj.getData() + mesh = obj.getData(mesh=1) - self._doModelToWorldCoordinates(mesh, obj.matrix) + self._doModelingTransformation(mesh, obj.matrix) - self._doObjectDepthSorting(mesh) - - # We use both Mesh and NMesh because for depth sorting we change - # face order and Mesh class don't let us to do that. - mesh.update() - mesh = obj.getData(mesh=1) - self._doBackFaceCulling(mesh) - - self._doColorAndLighting(mesh) - self._doEdgesStyle(mesh, edgeSelectionStyles[EDGE_STYLE]) + self._doPerVertexLighting(mesh) + + # Do "projection" now so we perform further processing + # in Normalized View Coordinates + self._doProjection(mesh, self.proj) + + self._doViewFrustumClipping(mesh) + + self._doMeshDepthSorting(mesh) + + self._doEdgesStyle(mesh, edgeStyles[config.edges['STYLE']]) - self._doProjection(mesh, proj) # Update the object data, important! :) mesh.update() @@ -792,7 +825,7 @@ class Renderer: """ return obj.matrix.translationPart() - def _cameraViewDirection(self): + def _cameraViewVector(self): """Get the View Direction form the camera matrix. """ return Vector(self.cameraObj.matrix[2]).resize3D() @@ -827,7 +860,7 @@ class Renderer: # View Vector in orthographics projections is the view Direction of # the camera if self.cameraObj.data.getType() == 1: - view_vect = self._cameraViewDirection() + view_vect = self._cameraViewVector() # View vector in perspective projections can be considered as # the difference between the camera position and one point of @@ -836,6 +869,7 @@ class Renderer: vv = max( [ ((camPos - Vector(v.co)).length, (camPos - Vector(v.co))) for v in face] ) view_vect = vv[1] + # if d > 0 the face is visible from the camera d = view_vect * normal @@ -847,41 +881,14 @@ class Renderer: # Scene methods - def _doConvertGeometricObjToMesh(self, scene): - """Convert all "geometric" objects to mesh ones. - """ - geometricObjTypes = ['Mesh', 'Surf', 'Curve', 'Text'] - - Objects = scene.getChildren() - objList = [ o for o in Objects if o.getType() in geometricObjTypes ] - for obj in objList: - old_obj = obj - obj = self._convertToRawMeshObj(obj) - scene.link(obj) - scene.unlink(old_obj) - - - # XXX Workaround for Text and Curve which have some normals - # inverted when they are converted to Mesh, REMOVE that when - # blender will fix that!! - if old_obj.getType() in ['Curve', 'Text']: - me = obj.getData(mesh=1) - for f in me.faces: f.sel = 1; - for v in me.verts: v.sel = 1; - me.remDoubles(0) - me.triangleToQuad() - me.recalcNormals() - me.update() - - def _doSceneClipping(self, scene): - """Clip objects against the View Frustum. + """Clip whole objects against the View Frustum. For now clip away only objects according to their center position. """ cpos = self._getObjPosition(self.cameraObj) - view_vect = self._cameraViewDirection() + view_vect = self._cameraViewVector() near = self.cameraObj.data.clipStart far = self.cameraObj.data.clipEnd @@ -903,6 +910,33 @@ class Renderer: if (d < near) or (d > far) or (theta > fovy): scene.unlink(o) + def _doConvertGeometricObjToMesh(self, scene): + """Convert all "geometric" objects to mesh ones. + """ + geometricObjTypes = ['Mesh', 'Surf', 'Curve', 'Text'] + + Objects = scene.getChildren() + objList = [ o for o in Objects if o.getType() in geometricObjTypes ] + for obj in objList: + old_obj = obj + obj = self._convertToRawMeshObj(obj) + scene.link(obj) + scene.unlink(old_obj) + + + # XXX Workaround for Text and Curve which have some normals + # inverted when they are converted to Mesh, REMOVE that when + # blender will fix that!! + if old_obj.getType() in ['Curve', 'Text']: + me = obj.getData(mesh=1) + for f in me.faces: f.sel = 1; + for v in me.verts: v.sel = 1; + me.remDoubles(0) + me.triangleToQuad() + me.recalcNormals() + me.update() + + def _doSceneDepthSorting(self, scene): """Sort objects in the scene. @@ -932,13 +966,28 @@ class Renderer: def _joinMeshObjectsInScene(self, scene): """Merge all the Mesh Objects in a scene into a single Mesh Object. """ - mesh = Mesh.New() + + oList = [o for o in scene.getChildren() if o.getType()=='Mesh'] + + # FIXME: Object.join() do not work if the list contains 1 object + if len(oList) == 1: + return + + mesh = Mesh.New('BigOne') bigObj = Object.New('Mesh', 'BigOne') bigObj.link(mesh) - oList = [o for o in scene.getChildren() if o.getType()=='Mesh'] - bigObj.join(oList) scene.link(bigObj) + + try: + bigObj.join(oList) + except RuntimeError: + print "\nCan't Join Objects\n" + scene.unlink(bigObj) + return + except TypeError: + print "Objects Type error?" + for o in oList: scene.unlink(o) @@ -965,41 +1014,18 @@ class Renderer: return newObject - def _doModelToWorldCoordinates(self, mesh, matrix): + def _doModelingTransformation(self, mesh, matrix): """Transform object coordinates to world coordinates. This step is done simply applying to the object its tranformation matrix and recalculating its normals. """ - mesh.transform(matrix, True) - - def _doObjectDepthSorting(self, mesh): - """Sort faces in an object. - - The faces in the object are sorted following the distance of the - vertices from the camera position. - """ - c = self._getObjPosition(self.cameraObj) + # XXX FIXME: blender do not transform normals in the right way when + # there are negative scale values + if matrix[0][0] < 0 or matrix[1][1] < 0 or matrix[2][2] < 0: + print "WARNING: Negative scales, expect incorrect results!" - # hackish sorting of faces - - # Sort faces according to the max distance from the camera - by_max_vert_dist = (lambda f1, f2: - cmp(max([(Vector(v.co)-Vector(c)).length for v in f1]), - max([(Vector(v.co)-Vector(c)).length for v in f2]))) - - # Sort faces according to the min distance from the camera - by_min_vert_dist = (lambda f1, f2: - cmp(min([(Vector(v.co)-Vector(c)).length for v in f1]), - min([(Vector(v.co)-Vector(c)).length for v in f2]))) - - # Sort faces according to the avg distance from the camera - by_avg_vert_dist = (lambda f1, f2: - cmp(sum([(Vector(v.co)-Vector(c)).length for v in f1])/len(f1), - sum([(Vector(v.co)-Vector(c)).length for v in f2])/len(f2))) - - mesh.faces.sort(by_max_vert_dist) - mesh.faces.reverse() + mesh.transform(matrix, True) def _doBackFaceCulling(self, mesh): """Simple Backface Culling routine. @@ -1020,22 +1046,10 @@ class Renderer: if self._isFaceVisible(f): f.sel = 1 - # Is this the correct way to propagate the face selection info to the - # vertices belonging to a face ?? - # TODO: Using the Mesh module this should come for free. Right? - #Mesh.Mode(Mesh.SelectModes['VERTEX']) - #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; - - def _doColorAndLighting(self, mesh): - """Apply an Illumination model to the object. + def _doPerVertexLighting(self, mesh): + """Apply an Illumination ans shading model to the object. - The Illumination model used is the Phong one, it may be inefficient, + The model used is the Phong one, it may be inefficient, but I'm just learning about rendering and starting from Phong seemed the most natural way. """ @@ -1054,7 +1068,7 @@ class Renderer: light = light_obj.data camPos = self._getObjPosition(self.cameraObj) - + # We do per-face color calculation (FLAT Shading), we can easily turn # to a per-vertex calculation if we want to implement some shading # technique. For an example see: @@ -1073,16 +1087,16 @@ class Renderer: L = Vector(light_pos).normalize() - V = (Vector(camPos) - Vector(f.v[0].co)).normalize() + V = (Vector(camPos) - Vector(f.cent)).normalize() N = Vector(f.no).normalize() R = 2 * (N*L) * N - L # TODO: Attenuation factor (not used for now) - a0 = 1; a1 = 0.0; a2 = 0.0 + a0 = 1.0; a1 = 0.0; a2 = 1.0 d = (Vector(f.v[0].co) - Vector(light_pos)).length - fd = min(1, 1.0/(a0 + a1*d + a2*d*d)) + fd = min(1, 1.0/(a0 + a1*d + a2*(d*d))) # Ambient component Ia = 1.0 @@ -1092,17 +1106,22 @@ class Renderer: # Diffuse component (add light.col for kd) kd = mat.getRef() * Vector(mat.getRGBCol()) Ip = light.getEnergy() - Idiff = Ip * kd * (N*L) + if config.polygons['SHADING'] == 'FLAT': + Idiff = Ip * kd * max(0, (N*L)) + elif config.polygons['SHADING'] == 'TOON': + Idiff = Ip * kd * MeshUtils.toonShading(N*L) + # Specular component ks = mat.getSpec() * Vector(mat.getSpecCol()) ns = mat.getHardness() - Ispec = Ip * ks * pow((V * R), ns) + Ispec = Ip * ks * pow(max(0, (V*R)), ns) # Emissive component ki = Vector([mat.getEmit()]*3) - I = ki + Iamb + Idiff + Ispec + I = ki + Iamb + (Idiff + Ispec) + # Set Alpha component I = list(I) @@ -1111,6 +1130,8 @@ class Renderer: # Clamp I values between 0 and 1 I = [ min(c, 1) for c in I] I = [ max(0, c) for c in I] + + # Convert to a value between 0 and 255 tmp_col = [ int(c * 255.0) for c in I] for c in f.col: @@ -1119,6 +1140,93 @@ class Renderer: c.b = tmp_col[2] c.a = tmp_col[3] + def _doProjection(self, mesh, projector): + """Apply Viewing and Projection tranformations. + """ + + for v in mesh.verts: + p = projector.doProjection(v.co) + v.co[0] = p[0] + v.co[1] = p[1] + v.co[2] = p[2] + + # We could reeset Camera matrix, since now + # we are in Normalized Viewing Coordinates, + # but doung that would affect World Coordinate + # processing for other objects + + #self.cameraObj.data.type = 1 + #self.cameraObj.data.scale = 2.0 + #m = Matrix().identity() + #self.cameraObj.setMatrix(m) + + def _doViewFrustumClipping(self, mesh): + """Clip faces against the View Frustum. + """ + + def test_extensions(self, f1, f2): + for v1, v2 in [ (v1, v2) for v1 in f1 for v2 in f2 ]: + pass + + def depth_sort(self, faces): + return + + + def _doMeshDepthSorting(self, mesh): + """Sort faces in an object. + + The faces in the object are sorted following the distance of the + vertices from the camera position. + """ + if len(mesh.faces) == 0: + return + + #c = self._getObjPosition(self.cameraObj) + + # In NVC + c = [0, 0, 1] + + # hackish sorting of faces + + # Sort faces according to the max distance from the camera + by_max_vert_dist = (lambda f1, f2: + cmp(max([(Vector(v.co)-Vector(c)).length for v in f2]), + max([(Vector(v.co)-Vector(c)).length for v in f1]))) + + # Sort faces according to the min distance from the camera + by_min_vert_dist = (lambda f1, f2: + cmp(min([(Vector(v.co)-Vector(c)).length for v in f1]), + min([(Vector(v.co)-Vector(c)).length for v in f2]))) + + # Sort faces according to the avg distance from the camera + by_avg_vert_dist = (lambda f1, f2: + cmp(sum([(Vector(v.co)-Vector(c)).length for v in f1])/len(f1), + sum([(Vector(v.co)-Vector(c)).length for v in f2])/len(f2))) + + + # FIXME: using NMesh to sort faces. We should avoid that! + nmesh = NMesh.GetRaw(mesh.name) + nmesh.faces.sort(by_max_vert_dist) + #nmesh.faces.reverse() + + # Depth sort tests + + self.depth_sort(nmesh.faces) + + + mesh.faces.delete(1, range(0, len(mesh.faces))) + + for i,f in enumerate(nmesh.faces): + fv = [v.index for v in f.v] + mesh.faces.extend(fv) + mesh.faces[i].mat = f.mat + mesh.faces[i].sel = f.sel + for i,c in enumerate(mesh.faces[i].col): + c.r = f.col[i].r + c.g = f.col[i].g + c.b = f.col[i].b + c.a = f.col[i].a + def _doEdgesStyle(self, mesh, edgestyleSelect): """Process Mesh Edges accroding to a given selection style. @@ -1137,22 +1245,10 @@ class Renderer: for e in mesh.edges: + e.sel = 0 if edgestyleSelect(e, mesh): e.sel = 1 - else: - e.sel = 0 - def _doProjection(self, mesh, projector): - """Calculate the Projection for the object. - """ - # TODO: maybe using the object.transform() can be faster? - - for v in mesh.verts: - p = projector.doProjection(v.co) - v.co[0] = p[0] - v.co[1] = p[1] - v.co[2] = p[2] - # --------------------------------------------------------------------- @@ -1170,40 +1266,54 @@ class GUI: def _init(): # Output Format menu - default_value = outputWriters.keys().index(OUTPUT_FORMAT)+1 + output_format = config.output['FORMAT'] + default_value = outputWriters.keys().index(output_format)+1 GUI.outFormatMenu = Draw.Create(default_value) GUI.evtOutFormatMenu = 0 # Animation toggle button - GUI.animToggle = Draw.Create(RENDER_ANIMATION) + GUI.animToggle = Draw.Create(config.output['ANIMATION']) GUI.evtAnimToggle = 1 # Join Objects toggle button - GUI.joinObjsToggle = Draw.Create(OPTIMIZE_FOR_SPACE) + GUI.joinObjsToggle = Draw.Create(config.output['JOIN_OBJECTS']) GUI.evtJoinObjsToggle = 2 # Render filled polygons - GUI.polygonsToggle = Draw.Create(PRINT_POLYGONS) + GUI.polygonsToggle = Draw.Create(config.polygons['SHOW']) + + # Shading Style menu + shading_style = config.polygons['SHADING'] + default_value = shadingStyles.keys().index(shading_style)+1 + GUI.shadingStyleMenu = Draw.Create(default_value) + GUI.evtShadingStyleMenu = 21 + GUI.evtPolygonsToggle = 3 - # We hide the POLYGON_EXPANSION_TRICK, for now + # We hide the config.polygons['EXPANSION_TRICK'], for now # Render polygon edges - GUI.showEdgesToggle = Draw.Create(PRINT_EDGES) + GUI.showEdgesToggle = Draw.Create(config.edges['SHOW']) GUI.evtShowEdgesToggle = 4 # Render hidden edges - GUI.showHiddenEdgesToggle = Draw.Create(SHOW_HIDDEN_EDGES) + GUI.showHiddenEdgesToggle = Draw.Create(config.edges['SHOW_HIDDEN']) GUI.evtShowHiddenEdgesToggle = 5 # Edge Style menu - default_value = edgeSelectionStyles.keys().index(EDGE_STYLE)+1 + edge_style = config.edges['STYLE'] + default_value = edgeStyles.keys().index(edge_style)+1 GUI.edgeStyleMenu = Draw.Create(default_value) GUI.evtEdgeStyleMenu = 6 # Edge Width slider - GUI.edgeWidthSlider = Draw.Create(EDGES_WIDTH) + GUI.edgeWidthSlider = Draw.Create(config.edges['WIDTH']) GUI.evtEdgeWidthSlider = 7 + # Edge Color Picker + c = config.edges['COLOR'] + GUI.edgeColorPicker = Draw.Create(c[0]/255.0, c[1]/255.0, c[2]/255.0) + GUI.evtEdgeColorPicker = 71 + # Render Button GUI.evtRenderButton = 8 @@ -1255,30 +1365,45 @@ class GUI: 200, 285, 160, 18, GUI.polygonsToggle.val, "Render filled polygons") + if GUI.polygonsToggle.val == 1: + + # Polygon Shading Style + shadingStyleMenuStruct = "Shading Style %t" + for t in shadingStyles.keys(): + shadingStyleMenuStruct = shadingStyleMenuStruct + "|%s" % t.lower() + GUI.shadingStyleMenu = Draw.Menu(shadingStyleMenuStruct, GUI.evtShadingStyleMenu, + 200, 260, 160, 18, GUI.shadingStyleMenu.val, + "Choose the shading style") + + # Render Edges GUI.showEdgesToggle = Draw.Toggle("Show Edges", GUI.evtShowEdgesToggle, - 200, 260, 160, 18, GUI.showEdgesToggle.val, + 200, 235, 160, 18, GUI.showEdgesToggle.val, "Render polygon edges") if GUI.showEdgesToggle.val == 1: # Edge Style edgeStyleMenuStruct = "Edge Style %t" - for t in edgeSelectionStyles.keys(): - edgeStyleMenuStruct = edgeStyleMenuStruct + "|%s" % t + for t in edgeStyles.keys(): + edgeStyleMenuStruct = edgeStyleMenuStruct + "|%s" % t.lower() GUI.edgeStyleMenu = Draw.Menu(edgeStyleMenuStruct, GUI.evtEdgeStyleMenu, - 200, 235, 160, 18, GUI.edgeStyleMenu.val, + 200, 210, 160, 18, GUI.edgeStyleMenu.val, "Choose the edge style") # Edge size GUI.edgeWidthSlider = Draw.Slider("Width: ", GUI.evtEdgeWidthSlider, - 200, 210, 160, 18, GUI.edgeWidthSlider.val, + 200, 185, 140, 18, GUI.edgeWidthSlider.val, 0.0, 10.0, 0, "Change Edge Width") + # Edge Color + GUI.edgeColorPicker = Draw.ColorPicker(GUI.evtEdgeColorPicker, + 342, 185, 18, 18, GUI.edgeColorPicker.val, "Choose Edge Color") + # Show Hidden Edges GUI.showHiddenEdgesToggle = Draw.Toggle("Show Hidden Edges", GUI.evtShowHiddenEdgesToggle, - 200, 185, 160, 18, GUI.showHiddenEdgesToggle.val, + 200, 160, 160, 18, GUI.showHiddenEdgesToggle.val, "Render hidden edges as dashed lines") glRasterPos2i(10, 160) @@ -1294,38 +1419,45 @@ class GUI: Draw.Redraw(1) def button_event(evt): - global PRINT_POLYGONS - global POLYGON_EXPANSION_TRICK - global PRINT_EDGES - global SHOW_HIDDEN_EDGES - global EDGE_STYLE - global EDGES_WIDTH - global RENDER_ANIMATION - global OPTIMIZE_FOR_SPACE - global OUTPUT_FORMAT if evt == GUI.evtExitButton: Draw.Exit() + elif evt == GUI.evtOutFormatMenu: i = GUI.outFormatMenu.val - 1 - OUTPUT_FORMAT = outputWriters.keys()[i] + config.output['FORMAT']= outputWriters.keys()[i] + elif evt == GUI.evtAnimToggle: - RENDER_ANIMATION = bool(GUI.animToggle.val) + config.outpur['ANIMATION'] = bool(GUI.animToggle.val) + elif evt == GUI.evtJoinObjsToggle: - OPTIMIZE_FOR_SPACE = bool(GUI.joinObjsToggle.val) + config.output['JOIN_OBJECTS'] = bool(GUI.joinObjsToggle.val) + elif evt == GUI.evtPolygonsToggle: - PRINT_POLYGONS = bool(GUI.polygonsToggle.val) + config.polygons['SHOW'] = bool(GUI.polygonsToggle.val) + + elif evt == GUI.evtShadingStyleMenu: + i = GUI.shadingStyleMenu.val - 1 + config.polygons['SHADING'] = shadingStyles.keys()[i] + elif evt == GUI.evtShowEdgesToggle: - PRINT_EDGES = bool(GUI.showEdgesToggle.val) + config.edges['SHOW'] = bool(GUI.showEdgesToggle.val) + elif evt == GUI.evtShowHiddenEdgesToggle: - SHOW_HIDDEN_EDGES = bool(GUI.showHiddenEdgesToggle.val) + config.edges['SHOW_HIDDEN'] = bool(GUI.showHiddenEdgesToggle.val) + elif evt == GUI.evtEdgeStyleMenu: i = GUI.edgeStyleMenu.val - 1 - EDGE_STYLE = edgeSelectionStyles.keys()[i] + config.edges['STYLE'] = edgeStyles.keys()[i] + elif evt == GUI.evtEdgeWidthSlider: - EDGES_WIDTH = float(GUI.edgeWidthSlider.val) + config.edges['WIDTH'] = float(GUI.edgeWidthSlider.val) + + elif evt == GUI.evtEdgeColorPicker: + config.edges['COLOR'] = [int(c*255.0) for c in GUI.edgeColorPicker.val] + elif evt == GUI.evtRenderButton: - label = "Save %s" % OUTPUT_FORMAT + label = "Save %s" % config.output['FORMAT'] # Show the File Selector global outputfile Blender.Window.FileSelector(vectorize, label, outputfile) @@ -1338,16 +1470,11 @@ class GUI: #GUI.conf_debug() def conf_debug(): - print - print "PRINT_POLYGONS:", PRINT_POLYGONS - print "POLYGON_EXPANSION_TRICK:", POLYGON_EXPANSION_TRICK - print "PRINT_EDGES:", PRINT_EDGES - print "SHOW_HIDDEN_EDGES:", SHOW_HIDDEN_EDGES - print "EDGE_STYLE:", EDGE_STYLE - print "EDGES_WIDTH:", EDGES_WIDTH - print "RENDER_ANIMATION:", RENDER_ANIMATION - print "OPTIMIZE_FOR_SPACE:", OPTIMIZE_FOR_SPACE - print "OUTPUT_FORMAT:", OUTPUT_FORMAT + from pprint import pprint + print "\nConfig" + pprint(config.output) + pprint(config.polygons) + pprint(config.edges) _init = staticmethod(_init) draw = staticmethod(draw) @@ -1371,11 +1498,11 @@ def vectorize(filename): editmode = Window.EditMode() if editmode: Window.EditMode(0) - actualWriter = outputWriters[OUTPUT_FORMAT] + actualWriter = outputWriters[config.output['FORMAT']] writer = actualWriter(filename) renderer = Renderer() - renderer.doRendering(writer, RENDER_ANIMATION) + renderer.doRendering(writer, config.output['ANIMATION']) if editmode: Window.EditMode(1) @@ -1386,7 +1513,7 @@ if __name__ == "__main__": outputfile = "" basename = Blender.sys.basename(Blender.Get('filename')) if basename != "": - outputfile = Blender.sys.splitext(basename)[0] + "." + str(OUTPUT_FORMAT).lower() + outputfile = Blender.sys.splitext(basename)[0] + "." + str(config.output['FORMAT']).lower() if Blender.mode == 'background': vectorize(outputfile)