X-Git-Url: https://git.ao2.it/vrm.git/blobdiff_plain/c71b2b3970d0bdcd1df68026aa3315141b42ded0..159f92477d4654437623a3e564619519b9a1fdd1:/vrm.py diff --git a/vrm.py b/vrm.py index 312d8af..fc7cb5d 100755 --- a/vrm.py +++ b/vrm.py @@ -1,13 +1,13 @@ #!BPY """ Name: 'VRM' -Blender: 241 -Group: 'Export' -Tooltip: 'Vector Rendering Method Export Script' +Blender: 242 +Group: 'Render' +Tooltip: 'Vector Rendering Method script' """ __author__ = "Antonio Ospite" -__url__ = ["blender"] +__url__ = ["http://vrm.projects.blender.org"] __version__ = "0.3" __bpydoc__ = """\ @@ -43,19 +43,23 @@ __bpydoc__ = """\ # # Things TODO for a next release: # - Switch to the Mesh structure, should be considerably faster -# (partially done, but cannot sort faces, yet) +# (partially done, but with Mesh we cannot sort faces, yet) # - Use a better depth sorting algorithm # - 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.) +# - Implement Edge Styles (silhouettes, contours, etc.) (partially done). # - Implement Edge coloring # - Use multiple lighting sources in color calculation -# - Implement Shading Styles? -# - Use another representation for the 2D projection? +# - 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. -# - Add other Vector Writers. +# 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? (Firefox do +# not support SMIL for animations) +# - FIX the issue with negative scales in object tranformations! # # --------------------------------------------------------------------- # @@ -75,18 +79,109 @@ from math import * # Some global settings -PRINT_POLYGONS = True -PRINT_EDGES = False -SHOW_HIDDEN_EDGES = False -EDGES_WIDTH = 0.5 +class config: + polygons = dict() + polygons['FILL'] = True + polygons['STYLE'] = None + # Hidden to the user for now + polygons['EXPANSION_TRICK'] = True -POLYGON_EXPANSION_TRICK = True + edges = dict() + edges['SHOW'] = True + edges['SHOW_HIDDEN'] = False + edges['STYLE'] = 'silhouette' + edges['WIDTH'] = 2 -RENDER_ANIMATION = False + output = dict() + output['FORMAT'] = 'SVG' + output['ANIMATION'] = False + output['MERGED_OBJECTS'] = True + + + +# --------------------------------------------------------------------- +# +## Utility Mesh class +# +# --------------------------------------------------------------------- +class MeshUtils: + + def getEdgeAdjacentFaces(edge, mesh): + """Get the faces adjacent to a given edge. + + There can be 0, 1 or more (usually 2) faces adjacent to an edge. + """ + adjface_list = [] + + for f in mesh.faces: + if (edge.v1 in f.v) and (edge.v2 in f.v): + adjface_list.append(f) + + return adjface_list + + def isVisibleEdge(e, mesh): + """Normal edge selection rule. + + An 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. + """ + + adjacent_faces = MeshUtils.getEdgeAdjacentFaces(e, mesh) + + if len(adjacent_faces) == 0: + return True + + selected_faces = [f for f in adjacent_faces if f.sel] + + if len(selected_faces) != 0: + return True + else: + return False + + def isSilhouetteEdge(e, mesh): + """Silhuette selection rule. + + An edge is a silhuette edge if it is shared by two faces with + different selection status or if it is a boundary edge of a selected + face. + """ + + adjacent_faces = MeshUtils.getEdgeAdjacentFaces(e, mesh) + + if ((len(adjacent_faces) == 1 and adjacent_faces[0].sel == 1) or + (len(adjacent_faces) == 2 and + adjacent_faces[0].sel != adjacent_faces[1].sel) + ): + return True + 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 v\n" % (framenumber, framestyle) ) + for obj in Objects: if(obj.getType() != 'Mesh'): @@ -446,7 +543,7 @@ class SVGVectorWriter(VectorWriter): for face in mesh.faces: if not face.sel: - continue + continue self.file.write(" 0.5) + 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((V*R), ns) # Emissive component ki = Vector([mat.getEmit()]*3) I = ki + Iamb + Idiff + Ispec + + # Set Alpha component + I = list(I) + I.append(mat.getAlpha()) + + # Toon shading + #I = [MeshUtils.toonShading(c) for c in I] + # Clamp I values between 0 and 1 I = [ min(c, 1) for c in I] I = [ max(0, c) for c in I] tmp_col = [ int(c * 255.0) for c in I] - vcol = NMesh.Col(tmp_col[0], tmp_col[1], tmp_col[2], 255) - f.col = [] - for v in f.v: - f.col.append(vcol) + for c in f.col: + c.r = tmp_col[0] + c.g = tmp_col[1] + c.b = tmp_col[2] + c.a = tmp_col[3] - def _doEdgesStyle(self, mesh, style): - """Process Mesh Edges. + def _doEdgesStyle(self, mesh, edgestyleSelect): + """Process Mesh Edges accroding to a given selection style. Examples of algorithms: @@ -1016,9 +1182,15 @@ class Renderer: given an edge if one its adjacent faces is frontfacing and the other is backfacing, than select it, else deselect. """ - #print "\tTODO: _doEdgeStyle()" - return + Mesh.Mode(Mesh.SelectModes['EDGE']) + + for e in mesh.edges: + + e.sel = 0 + if edgestyleSelect(e, mesh): + e.sel = 1 + def _doProjection(self, mesh, projector): """Calculate the Projection for the object. """ @@ -1034,44 +1206,233 @@ class Renderer: # --------------------------------------------------------------------- # -## Main Program +## GUI Class and Main Program # # --------------------------------------------------------------------- + +from Blender import BGL, Draw +from Blender.BGL import * + +class GUI: + + def _init(): + + # Output Format menu + 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(config.output['ANIMATION']) + GUI.evtAnimToggle = 1 + + # Join Objects toggle button + GUI.joinObjsToggle = Draw.Create(config.output['MERGED_OBJECTS']) + GUI.evtJoinObjsToggle = 2 + + # Render filled polygons + GUI.polygonsToggle = Draw.Create(config.polygons['FILL']) + + GUI.evtPolygonsToggle = 3 + # We hide the config.polygons['EXPANSION_TRICK'], for now + + # Render polygon edges + GUI.showEdgesToggle = Draw.Create(config.edges['SHOW']) + GUI.evtShowEdgesToggle = 4 + + # Render hidden edges + GUI.showHiddenEdgesToggle = Draw.Create(config.edges['SHOW_HIDDEN']) + GUI.evtShowHiddenEdgesToggle = 5 + + # Edge Style menu + edge_style = config.edges['STYLE'] + default_value = edgeSelectionStyles.keys().index(edge_style)+1 + GUI.edgeStyleMenu = Draw.Create(default_value) + GUI.evtEdgeStyleMenu = 6 + + # Edge Width slider + GUI.edgeWidthSlider = Draw.Create(config.edges['WIDTH']) + GUI.evtEdgeWidthSlider = 7 + + # Render Button + GUI.evtRenderButton = 8 + + # Exit Button + GUI.evtExitButton = 9 + + def draw(): + + # initialize static members + GUI._init() + + glClear(GL_COLOR_BUFFER_BIT) + glColor3f(0.0, 0.0, 0.0) + glRasterPos2i(10, 350) + Draw.Text("VRM: Vector Rendering Method script.") + glRasterPos2i(10, 335) + Draw.Text("Press Q or ESC to quit.") + + # Build the output format menu + glRasterPos2i(10, 310) + Draw.Text("Select the output Format:") + outMenuStruct = "Output Format %t" + for t in outputWriters.keys(): + outMenuStruct = outMenuStruct + "|%s" % t + GUI.outFormatMenu = Draw.Menu(outMenuStruct, GUI.evtOutFormatMenu, + 10, 285, 160, 18, GUI.outFormatMenu.val, "Choose the Output Format") + + # Animation toggle + GUI.animToggle = Draw.Toggle("Animation", GUI.evtAnimToggle, + 10, 260, 160, 18, GUI.animToggle.val, + "Toggle rendering of animations") + + # Join Objects toggle + GUI.joinObjsToggle = Draw.Toggle("Join objects", GUI.evtJoinObjsToggle, + 10, 235, 160, 18, GUI.joinObjsToggle.val, + "Join objects in the rendered file") + + # Render Button + Draw.Button("Render", GUI.evtRenderButton, 10, 210-25, 75, 25+18, + "Start Rendering") + Draw.Button("Exit", GUI.evtExitButton, 95, 210-25, 75, 25+18, "Exit!") + + # Rendering Styles + glRasterPos2i(200, 310) + Draw.Text("Rendering Style:") + + # Render Polygons + GUI.polygonsToggle = Draw.Toggle("Filled Polygons", GUI.evtPolygonsToggle, + 200, 285, 160, 18, GUI.polygonsToggle.val, + "Render filled polygons") + + # Render Edges + GUI.showEdgesToggle = Draw.Toggle("Show Edges", GUI.evtShowEdgesToggle, + 200, 260, 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 + GUI.edgeStyleMenu = Draw.Menu(edgeStyleMenuStruct, GUI.evtEdgeStyleMenu, + 200, 235, 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, + 0.0, 10.0, 0, "Change Edge Width") + + # Show Hidden Edges + GUI.showHiddenEdgesToggle = Draw.Toggle("Show Hidden Edges", + GUI.evtShowHiddenEdgesToggle, + 200, 185, 160, 18, GUI.showHiddenEdgesToggle.val, + "Render hidden edges as dashed lines") + + glRasterPos2i(10, 160) + Draw.Text("Antonio Ospite (c) 2006") + + def event(evt, val): + + if evt == Draw.ESCKEY or evt == Draw.QKEY: + Draw.Exit() + else: + return + + Draw.Redraw(1) + + def button_event(evt): + + if evt == GUI.evtExitButton: + Draw.Exit() + + elif evt == GUI.evtOutFormatMenu: + i = GUI.outFormatMenu.val - 1 + config.output['FORMAT']= outputWriters.keys()[i] + + elif evt == GUI.evtAnimToggle: + config.outpur['ANIMATION'] = bool(GUI.animToggle.val) + + elif evt == GUI.evtJoinObjsToggle: + config.output['MERGED_OBJECTS'] = bool(GUI.joinObjsToggle.val) + + elif evt == GUI.evtPolygonsToggle: + config.polygons['FILL'] = bool(GUI.polygonsToggle.val) + + elif evt == GUI.evtShowEdgesToggle: + config.edges['SHOW'] = bool(GUI.showEdgesToggle.val) + + elif evt == GUI.evtShowHiddenEdgesToggle: + config.edges['SHOW_HIDDEN'] = bool(GUI.showHiddenEdgesToggle.val) + + elif evt == GUI.evtEdgeStyleMenu: + i = GUI.edgeStyleMenu.val - 1 + config.edges['STYLE'] = edgeSelectionStyles.keys()[i] + + elif evt == GUI.evtEdgeWidthSlider: + config.edges['WIDTH'] = float(GUI.edgeWidthSlider.val) + + elif evt == GUI.evtRenderButton: + label = "Save %s" % config.output['FORMAT'] + # Show the File Selector + global outputfile + Blender.Window.FileSelector(vectorize, label, outputfile) + + else: + print "Event: %d not handled!" % evt + + if evt: + Draw.Redraw(1) + GUI.conf_debug() + + def conf_debug(): + from pprint import pprint + pprint(config) + + _init = staticmethod(_init) + draw = staticmethod(draw) + event = staticmethod(event) + button_event = staticmethod(button_event) + conf_debug = staticmethod(conf_debug) + +# A wrapper function for the vectorizing process def vectorize(filename): """The vectorizing process is as follows: - Instanciate the writer and the renderer - Render! """ + + if filename == "": + print "\nERROR: invalid file name!" + return + from Blender import Window editmode = Window.EditMode() if editmode: Window.EditMode(0) - writer = SVGVectorWriter(filename) + 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) -def vectorize_gui(filename): - """Draw the gui. - - I would like to keep that simple, really. - """ - Blender.Window.FileSelector (vectorize, 'Save SVG', filename) - Blender.Redraw() - # Here the main if __name__ == "__main__": + outputfile = "" basename = Blender.sys.basename(Blender.Get('filename')) - outputfile = Blender.sys.splitext(basename)[0]+".svg" + if basename != "": + outputfile = Blender.sys.splitext(basename)[0] + "." + str(config.output['FORMAT']).lower() - # with this trick we can run the script in batch mode - try: - vectorize_gui(outputfile) - except: + if Blender.mode == 'background': vectorize(outputfile) + else: + Draw.Register(GUI.draw, GUI.event, GUI.button_event)