vertex paint script ported by - Keith "Wahooney" Boshoff
[blender.git] / release / scripts / io / vertexpaint_dirt.py
1 # bl_author = ["Campbell Barton aka ideasman42", "Keith Boshoff aka Wahooney"]
2 # bl_url = ["www.blender.org", "blenderartists.org", "www.python.org"]
3 # bl_version = "0.2"
4
5 # ***** BEGIN GPL LICENSE BLOCK *****
6 #
7 # Script copyright (C) Campbell J Barton
8 #
9 # This program is free software; you can redistribute it and/or
10 # modify it under the terms of the GNU General Public License
11 # as published by the Free Software Foundation; either version 2
12 # of the License, or (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with this program; if not, write to the Free Software Foundation,
21 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22 #
23 # ***** END GPL LICENCE BLOCK *****
24 # --------------------------------------------------------------------------
25
26 # History
27 #
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
31 #
32
33 import bpy
34 import Mathutils
35 import math
36 import time
37
38 from Mathutils import Vector
39 from bpy.props import *
40
41 def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, dirt_only, sel_only):
42 ##    Window.WaitCursor(1)
43
44     #BPyMesh.meshCalcNormals(me)
45
46     vert_tone= [0.0] * len(me.verts)
47     vert_tone_count= [0] * len(me.verts)
48
49     # create lookup table for each vertex's connected vertices (via edges)
50     con = [[] for i in range(len(me.verts))]
51
52     min_tone=180.0
53     max_tone=0.0
54
55     # add connected verts
56     for e in me.edges:
57         con[e.verts[0]].append(e.verts[1])
58         con[e.verts[1]].append(e.verts[0])
59
60     for v in me.verts:
61         vec = Vector()
62         no = v.normal
63         co = v.co
64
65         # get the direction of the vectors between the vertex and it's connected vertices
66         for c in con[v.index]:
67             vec += Vector(me.verts[c].co - co).normalize()
68
69         # normalize the vector by dividing by the number of connected verts
70         vec /= len(con[v.index])
71
72         # angle is the acos of the dot product between vert and connected verts normals
73         ang = math.acos(no.dot(vec))
74
75         # enforce min/max
76
77         vert_tone[v.index] = max(clamp_clean, min(clamp_dirt, ang))
78
79     # average vert_tone_list into vert_tonef
80 #    for i, tones in enumerate(vert_tone):
81 #        if vert_tone_count[i]:
82 #            vert_tone[i] = vert_tone[i] / vert_tone_count[i]
83
84     # Below we use edges to blur along so the edges need counting, not the faces
85     vert_tone_count=    [0] *    len(me.verts)
86     for ed in me.edges:
87         vert_tone_count[ed.verts[0]] += 1
88         vert_tone_count[ed.verts[1]] += 1
89
90
91     # Blur tone
92     blur        = blur_strength
93     blur_inv    = 1.0 - blur_strength
94
95     for i in range(blur_iterations):
96
97         # backup the original tones
98         orig_vert_tone= list(vert_tone)
99
100         for ed in me.edges:
101
102             i1 = ed.verts[0]
103             i2 = ed.verts[1]
104
105             val1 = (orig_vert_tone[i2]*blur) +  (orig_vert_tone[i1]*blur_inv)
106             val2 = (orig_vert_tone[i1]*blur) +  (orig_vert_tone[i2]*blur_inv)
107
108             # Apply the ton divided by the number of faces connected
109             vert_tone[i1] += val1 / max(vert_tone_count[i1], 1)
110             vert_tone[i2] += val2 / max(vert_tone_count[i2], 1)
111
112
113     min_tone= min(vert_tone)
114     max_tone= max(vert_tone)
115
116     print(min_tone)
117     print(max_tone)
118     print(clamp_clean)
119     print(clamp_dirt)
120
121     tone_range= max_tone-min_tone
122     if max_tone==min_tone:
123         return
124
125     for lay in me.vertex_colors:
126         if lay.active:
127             active_col_layer = lay.data
128
129     if not active_col_layer:
130         return('CANCELLED', )
131
132     for i, f in enumerate(me.faces):
133         if not sel_only or f.sel:
134             f_col = active_col_layer[i]
135
136             f_col = [f_col.color1, f_col.color2, f_col.color3, f_col.color4]
137
138             for j, v in enumerate(f.verts):
139                 col = f_col[j]
140                 tone = vert_tone[me.verts[v].index]
141                 tone = (tone-min_tone)/tone_range
142
143                 col[0] = tone*col[0]
144                 col[1] = tone*col[1]
145                 col[2] = tone*col[2]
146
147 ##    Window.WaitCursor(0)
148
149
150 class VertexPaintDirt(bpy.types.Operator):
151     '''This script uses the concavity of vertices to shade the mesh, and optionaly blur the shading to remove artifacts from spesific edges.'''
152
153     bl_idname = "mesh.vertex_paint_dirt"
154     bl_label = "Dirty Vertex Colors"
155     bl_register = True
156     bl_undo = True
157
158     blur_strength = FloatProperty(name="Blur Strength", description="Blur strength per iteration", default=1.0, min=0.01, max=1.0)
159     blur_iterations = IntProperty(name="Blur Iterations", description="Number times to blur the colors. (higher blurs more)", default=1, min=0, max=40)
160     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)
161     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)
162     dirt_only = BoolProperty(name="Dirt Only", description="Dont calculate cleans for convex areas", default=False)
163     sel_faces_only = BoolProperty(name="Selected Faces Only", description="Only apply to UV/Face selected faces (mix vpain/uvface select)", default=False)
164
165     def execute(self, context):
166         sce= context.scene
167         ob= context.object
168
169         if not ob or ob.type != 'MESH':
170             print('Error, no active mesh object, aborting.')
171             print(ob)
172             print(ob.type)
173             return('CANCELLED',)
174
175         me = ob.data
176
177         t = time.time()
178
179         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)
180
181         print('done in %.6f' % (time.time()-t))
182
183         return('FINISHED',)
184
185
186 bpy.ops.add(VertexPaintDirt)
187
188 if __name__ == "__main__":
189     bpy.ops.mesh.vertex_paint_dirt()