diff --git a/release/scripts/io/vertexpaint_dirt.py b/release/scripts/io/vertexpaint_dirt.py new file mode 100644 index 00000000000..9f55d3bb042 --- /dev/null +++ b/release/scripts/io/vertexpaint_dirt.py @@ -0,0 +1,189 @@ +# bl_author = ["Campbell Barton aka ideasman42", "Keith Boshoff aka Wahooney"] +# bl_url = ["www.blender.org", "blenderartists.org", "www.python.org"] +# bl_version = "0.2" + +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# Script copyright (C) Campbell J Barton +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# ***** END GPL LICENCE BLOCK ***** +# -------------------------------------------------------------------------- + +# History +# +# 2009-11-01: * 2.5 port by Keith "Wahooney" Boshoff +# * Replaced old method with my own, speed is similar (about 0.001 sec on Suzanne) +# but results are far more accurate +# + +import bpy +import Mathutils +import math +import time + +from Mathutils import Vector +from bpy.props import * + +def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, dirt_only, sel_only): +## Window.WaitCursor(1) + + #BPyMesh.meshCalcNormals(me) + + vert_tone= [0.0] * len(me.verts) + vert_tone_count= [0] * len(me.verts) + + # create lookup table for each vertex's connected vertices (via edges) + con = [[] for i in range(len(me.verts))] + + min_tone=180.0 + max_tone=0.0 + + # add connected verts + for e in me.edges: + con[e.verts[0]].append(e.verts[1]) + con[e.verts[1]].append(e.verts[0]) + + for v in me.verts: + vec = Vector() + no = v.normal + co = v.co + + # get the direction of the vectors between the vertex and it's connected vertices + for c in con[v.index]: + vec += Vector(me.verts[c].co - co).normalize() + + # normalize the vector by dividing by the number of connected verts + vec /= len(con[v.index]) + + # angle is the acos of the dot product between vert and connected verts normals + ang = math.acos(no.dot(vec)) + + # enforce min/max + + vert_tone[v.index] = max(clamp_clean, min(clamp_dirt, ang)) + + # average vert_tone_list into vert_tonef +# for i, tones in enumerate(vert_tone): +# if vert_tone_count[i]: +# vert_tone[i] = vert_tone[i] / vert_tone_count[i] + + # Below we use edges to blur along so the edges need counting, not the faces + vert_tone_count= [0] * len(me.verts) + for ed in me.edges: + vert_tone_count[ed.verts[0]] += 1 + vert_tone_count[ed.verts[1]] += 1 + + + # Blur tone + blur = blur_strength + blur_inv = 1.0 - blur_strength + + for i in range(blur_iterations): + + # backup the original tones + orig_vert_tone= list(vert_tone) + + for ed in me.edges: + + i1 = ed.verts[0] + i2 = ed.verts[1] + + val1 = (orig_vert_tone[i2]*blur) + (orig_vert_tone[i1]*blur_inv) + val2 = (orig_vert_tone[i1]*blur) + (orig_vert_tone[i2]*blur_inv) + + # Apply the ton divided by the number of faces connected + vert_tone[i1] += val1 / max(vert_tone_count[i1], 1) + vert_tone[i2] += val2 / max(vert_tone_count[i2], 1) + + + min_tone= min(vert_tone) + max_tone= max(vert_tone) + + print(min_tone) + print(max_tone) + print(clamp_clean) + print(clamp_dirt) + + tone_range= max_tone-min_tone + if max_tone==min_tone: + return + + for lay in me.vertex_colors: + if lay.active: + active_col_layer = lay.data + + if not active_col_layer: + return('CANCELLED', ) + + for i, f in enumerate(me.faces): + if not sel_only or f.sel: + f_col = active_col_layer[i] + + f_col = [f_col.color1, f_col.color2, f_col.color3, f_col.color4] + + for j, v in enumerate(f.verts): + col = f_col[j] + tone = vert_tone[me.verts[v].index] + tone = (tone-min_tone)/tone_range + + col[0] = tone*col[0] + col[1] = tone*col[1] + col[2] = tone*col[2] + +## Window.WaitCursor(0) + + +class VertexPaintDirt(bpy.types.Operator): + '''This script uses the concavity of vertices to shade the mesh, and optionaly blur the shading to remove artifacts from spesific edges.''' + + bl_idname = "mesh.vertex_paint_dirt" + bl_label = "Dirty Vertex Colors" + bl_register = True + bl_undo = True + + blur_strength = FloatProperty(name="Blur Strength", description="Blur strength per iteration", default=1.0, min=0.01, max=1.0) + blur_iterations = IntProperty(name="Blur Iterations", description="Number times to blur the colors. (higher blurs more)", default=1, min=0, max=40) + clean_angle = FloatProperty(name="Highlight Angle", description="Less then 90 limits the angle used in the tonal range", default=0.0, min=0.0, max=180.0) + dirt_angle = FloatProperty(name="Dirt Angle", description="Less then 90 limits the angle used in the tonal range", default=180.0, min=0.0, max=180.0) + dirt_only = BoolProperty(name="Dirt Only", description="Dont calculate cleans for convex areas", default=False) + sel_faces_only = BoolProperty(name="Selected Faces Only", description="Only apply to UV/Face selected faces (mix vpain/uvface select)", default=False) + + def execute(self, context): + sce= context.scene + ob= context.object + + if not ob or ob.type != 'MESH': + print('Error, no active mesh object, aborting.') + print(ob) + print(ob.type) + return('CANCELLED',) + + me = ob.data + + t = time.time() + + applyVertexDirt(me, self.blur_iterations, self.blur_strength, math.radians(self.dirt_angle), math.radians(self.clean_angle), self.dirt_only, self.sel_faces_only) + + print('done in %.6f' % (time.time()-t)) + + return('FINISHED',) + + +bpy.ops.add(VertexPaintDirt) + +if __name__ == "__main__": + bpy.ops.mesh.vertex_paint_dirt()