X-Git-Url: https://git.ao2.it/vrm.git/blobdiff_plain/e393259b57de5b2e22bcdeb498839eee817a4432..159f92477d4654437623a3e564619519b9a1fdd1:/vrm.py diff --git a/vrm.py b/vrm.py index aa73b10..fc7cb5d 100755 --- a/vrm.py +++ b/vrm.py @@ -1,7 +1,7 @@ #!BPY """ Name: 'VRM' -Blender: 241 +Blender: 242 Group: 'Render' Tooltip: 'Vector Rendering Method script' """ @@ -57,7 +57,9 @@ __bpydoc__ = """\ # 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) +# - FIX the issue with negative scales in object tranformations! # # --------------------------------------------------------------------- # @@ -78,20 +80,23 @@ from math import * # Some global settings -PRINT_POLYGONS = True +class config: + polygons = dict() + polygons['FILL'] = True + polygons['STYLE'] = None + # 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 -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['MERGED_OBJECTS'] = True @@ -153,9 +158,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 v\n" % (framenumber, framestyle) ) + for obj in Objects: if(obj.getType() != 'Mesh'): @@ -554,7 +580,7 @@ class SVGVectorWriter(VectorWriter): self.file.write("\tstyle=\"fill:" + str_col + ";") self.file.write(opacity_string) - if POLYGON_EXPANSION_TRICK: + if config.polygons['EXPANSION_TRICK']: self.file.write(" stroke:" + str_col + ";") self.file.write(" stroke-width:" + str(stroke_width) + ";\n") self.file.write(" stroke-linecap:round;stroke-linejoin:round") @@ -575,8 +601,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 @@ -647,6 +671,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'] @@ -692,12 +722,25 @@ 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) + + print "Before" + + try: + renderedScene = self.doRenderScene(inputScene) + except: + self._SCENE.makeCurrent() + Scene.unlink(inputScene) + del inputScene + outputWriter.printCanvas(renderedScene, - doPrintPolygons = PRINT_POLYGONS, - doPrintEdges = PRINT_EDGES, - showHiddenEdges = SHOW_HIDDEN_EDGES) + doPrintPolygons = config.polygons['FILL'], + doPrintEdges = config.edges['SHOW'], + showHiddenEdges = config.edges['SHOW_HIDDEN']) + print "After" + # clear the rendered scene self._SCENE.makeCurrent() Scene.unlink(renderedScene) @@ -708,22 +751,13 @@ class Renderer: context.currentFrame(currentFrame) - 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) @@ -773,7 +807,7 @@ class Renderer: self._doEdgesStyle(mesh, edgeSelectionStyles[EDGE_STYLE]) - self._doProjection(mesh, proj) + self._doProjection(mesh, self.proj) # Update the object data, important! :) mesh.update() @@ -932,11 +966,17 @@ class Renderer: def _joinMeshObjectsInScene(self, scene): """Merge all the Mesh Objects in a scene into a single Mesh Object. """ + + 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() 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) for o in oList: @@ -971,6 +1011,11 @@ class Renderer: This step is done simply applying to the object its tranformation matrix and recalculating its normals. """ + # 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!" + mesh.transform(matrix, True) def _doObjectDepthSorting(self, mesh): @@ -1092,22 +1137,27 @@ class Renderer: # Diffuse component (add light.col for kd) kd = mat.getRef() * Vector(mat.getRGBCol()) Ip = light.getEnergy() - Idiff = Ip * kd * (N*L) - + #Idiff = Ip * kd * int(N*L > 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] @@ -1137,10 +1187,9 @@ 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. @@ -1170,38 +1219,41 @@ 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['MERGED_OBJECTS']) GUI.evtJoinObjsToggle = 2 # Render filled polygons - GUI.polygonsToggle = Draw.Create(PRINT_POLYGONS) + GUI.polygonsToggle = Draw.Create(config.polygons['FILL']) + 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 = edgeSelectionStyles.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 # Render Button @@ -1294,38 +1346,38 @@ 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['MERGED_OBJECTS'] = bool(GUI.joinObjsToggle.val) + elif evt == GUI.evtPolygonsToggle: - PRINT_POLYGONS = bool(GUI.polygonsToggle.val) + config.polygons['FILL'] = bool(GUI.polygonsToggle.val) + 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'] = edgeSelectionStyles.keys()[i] + elif evt == GUI.evtEdgeWidthSlider: - EDGES_WIDTH = float(GUI.edgeWidthSlider.val) + config.edges['WIDTH'] = float(GUI.edgeWidthSlider.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) @@ -1335,19 +1387,11 @@ class GUI: if evt: Draw.Redraw(1) - #GUI.conf_debug() + 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 + pprint(config) _init = staticmethod(_init) draw = staticmethod(draw) @@ -1371,11 +1415,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 +1430,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)