1 # ***** BEGIN GPL LICENSE BLOCK *****
3 # Script copyright (C) Campbell J Barton
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software Foundation,
17 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 # ***** END GPL LICENCE BLOCK *****
20 # --------------------------------------------------------------------------
26 # Originally written by Campbell Barton aka ideasman42
28 # 2009-11-01: * 2.5 port by Keith "Wahooney" Boshoff
29 # * Replaced old method with my own, speed is similar (about 0.001 sec on Suzanne)
30 # but results are far more accurate
34 def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, dirt_only):
35 from mathutils import Vector
38 #BPyMesh.meshCalcNormals(me)
40 vert_tone = [0.0] * len(me.vertices)
45 # create lookup table for each vertex's connected vertices (via edges)
48 con = [[] for i in range(len(me.vertices))]
52 con[e.vertices[0]].append(e.vertices[1])
53 con[e.vertices[1]].append(e.vertices[0])
55 for i, v in enumerate(me.vertices):
60 # get the direction of the vectors between the vertex and it's connected vertices
62 vec += (me.vertices[c].co - co).normalized()
64 # normalize the vector by dividing by the number of connected verts
72 # angle is the acos of the dot product between vert and connected verts normals
73 ang = acos(no.dot(vec))
76 ang = max(clamp_dirt, ang)
79 ang = min(clamp_clean, ang)
84 for i in range(blur_iterations):
85 # backup the original tones
86 orig_vert_tone = list(vert_tone)
88 # use connected verts look up for blurring
89 for j, c in enumerate(con):
91 vert_tone[j] += blur_strength * orig_vert_tone[v]
93 vert_tone[j] /= len(c) * blur_strength + 1
95 min_tone = min(vert_tone)
96 max_tone = max(vert_tone)
99 # print(min_tone * 2 * math.pi)
100 # print(max_tone * 2 * math.pi)
104 tone_range = max_tone - min_tone
109 active_col_layer = None
111 if len(me.vertex_colors):
112 for lay in me.vertex_colors:
114 active_col_layer = lay.data
116 bpy.ops.mesh.vertex_color_add()
117 me.vertex_colors[0].active = True
118 active_col_layer = me.vertex_colors[0].data
120 if not active_col_layer:
121 return('CANCELLED', )
123 for i, f in enumerate(me.faces):
124 if not me.use_paint_mask or f.select:
126 f_col = active_col_layer[i]
128 f_col = [f_col.color1, f_col.color2, f_col.color3, f_col.color4]
130 for j, v in enumerate(f.vertices):
132 tone = vert_tone[me.vertices[v].index]
133 tone = (tone - min_tone) / tone_range
136 tone = min(tone, 0.5)
139 col[0] = tone * col[0]
140 col[1] = tone * col[1]
141 col[2] = tone * col[2]
145 from bpy.types import Operator
146 from bpy.props import FloatProperty, IntProperty, BoolProperty
149 class VertexPaintDirt(Operator):
150 bl_idname = "paint.vertex_color_dirt"
151 bl_label = "Dirty Vertex Colors"
152 bl_options = {'REGISTER', 'UNDO'}
154 blur_strength = FloatProperty(name="Blur Strength", description="Blur strength per iteration", default=1.0, min=0.01, max=1.0)
155 blur_iterations = IntProperty(name="Blur Iterations", description="Number times to blur the colors. (higher blurs more)", default=1, min=0, max=40)
156 clean_angle = FloatProperty(name="Highlight Angle", description="Less then 90 limits the angle used in the tonal range", default=180.0, min=0.0, max=180.0)
157 dirt_angle = FloatProperty(name="Dirt Angle", description="Less then 90 limits the angle used in the tonal range", default=0.0, min=0.0, max=180.0)
158 dirt_only = BoolProperty(name="Dirt Only", description="Dont calculate cleans for convex areas", default=False)
160 def execute(self, context):
162 from math import radians
165 if not obj or obj.type != 'MESH':
166 self.report({'ERROR'}, "Error, no active mesh object, aborting")
173 applyVertexDirt(mesh, self.blur_iterations, self.blur_strength, radians(self.dirt_angle), radians(self.clean_angle), self.dirt_only)
175 print('Dirt calculated in %.6f' % (time.time() - t))