operators were copying the properties from the rna operator into the class instance.
[blender.git] / release / scripts / op / vertexpaint_dirt.py
1 # ***** BEGIN GPL LICENSE BLOCK *****
2 #
3 # Script copyright (C) Campbell J Barton
4 #
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.
9 #
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.
14 #
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 #
19 # ***** END GPL LICENCE BLOCK *****
20 # --------------------------------------------------------------------------
21
22 # History
23 #
24 # Originally written by Campbell Barton aka ideasman42
25 #
26 # 2009-11-01: * 2.5 port by Keith "Wahooney" Boshoff
27 #              * Replaced old method with my own, speed is similar (about 0.001 sec on Suzanne)
28 #               but results are far more accurate
29 #
30
31 import bpy
32 import Mathutils
33 import math
34 import time
35
36 from Mathutils import Vector
37 from bpy.props import *
38
39 def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, dirt_only):
40 ##    Window.WaitCursor(1)
41
42     #BPyMesh.meshCalcNormals(me)
43
44     vert_tone = [0.0] * len(me.verts)
45
46     min_tone =180.0
47     max_tone =0.0
48
49     # create lookup table for each vertex's connected vertices (via edges)
50     con = []
51
52     con = [[] for i in range(len(me.verts))]
53
54     # add connected verts
55     for e in me.edges:
56         con[e.verts[0]].append(e.verts[1])
57         con[e.verts[1]].append(e.verts[0])
58
59     for v in me.verts:
60         vec = Vector()
61         no = v.normal
62         co = v.co
63
64         # get the direction of the vectors between the vertex and it's connected vertices
65         for c in con[v.index]:
66             vec += Vector(me.verts[c].co - co).normalize()
67
68         # normalize the vector by dividing by the number of connected verts
69         vec /= len(con[v.index])
70
71         # angle is the acos of the dot product between vert and connected verts normals
72         ang = math.acos(no.dot(vec))
73
74         # enforce min/max
75         ang = max(clamp_dirt, ang)
76
77         if not dirt_only:
78             ang = min(clamp_clean, ang)
79
80         vert_tone[v.index] = ang
81
82     # blur tones
83     for i in range(blur_iterations):
84         # backup the original tones
85         orig_vert_tone = list(vert_tone)
86
87         # use connected verts look up for blurring
88         for j, c in enumerate(con):
89             for v in c:
90                 vert_tone[j] += blur_strength * orig_vert_tone[v]
91
92             vert_tone[j] /= len(c) * blur_strength + 1
93
94     min_tone = min(vert_tone)
95     max_tone = max(vert_tone)
96
97     # debug information
98     # print(min_tone * 2 * math.pi)
99     # print(max_tone * 2 * math.pi)
100     # print(clamp_clean)
101     # print(clamp_dirt)
102
103     tone_range = max_tone-min_tone
104
105     if not tone_range:
106         return
107
108     active_col_layer = None
109
110     if len(me.vertex_colors):
111         for lay in me.vertex_colors:
112             if lay.active:
113                 active_col_layer = lay.data
114     else:
115         bpy.ops.mesh.vertex_color_add()
116         me.vertex_colors[0].active = True
117         active_col_layer = me.vertex_colors[0].data
118
119     if not active_col_layer:
120         return('CANCELLED', )
121
122     for i, f in enumerate(me.faces):
123         if not me.use_paint_mask or f.selected:
124
125             f_col = active_col_layer[i]
126
127             f_col = [f_col.color1, f_col.color2, f_col.color3, f_col.color4]
128
129             for j, v in enumerate(f.verts):
130                 col = f_col[j]
131                 tone = vert_tone[me.verts[v].index]
132                 tone = (tone-min_tone)/tone_range
133
134                 if dirt_only:
135                     tone = min(tone, 0.5)
136                     tone *= 2
137
138                 col[0] = tone*col[0]
139                 col[1] = tone*col[1]
140                 col[2] = tone*col[2]
141
142 ##    Window.WaitCursor(0)
143
144 class VertexPaintDirt(bpy.types.Operator):
145
146     bl_idname = "mesh.vertex_paint_dirt"
147     bl_label = "Dirty Vertex Colors"
148     bl_register = True
149     bl_undo = True
150
151     blur_strength = FloatProperty(name = "Blur Strength", description = "Blur strength per iteration", default = 1.0, min = 0.01, max = 1.0)
152     blur_iterations = IntProperty(name = "Blur Iterations", description = "Number times to blur the colors. (higher blurs more)", default = 1, min = 0, max = 40)
153     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)
154     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)
155     dirt_only = BoolProperty(name= "Dirt Only", description = "Dont calculate cleans for convex areas", default = False)
156
157     def execute(self, context):
158         sce = context.scene
159         ob = context.object
160
161         if not ob or ob.type != 'MESH':
162             print('Error, no active mesh object, aborting.')
163             return('CANCELLED',)
164
165         me = ob.data
166
167         t = time.time()
168
169         applyVertexDirt(me, self.properties.blur_iterations, self.properties.blur_strength, math.radians(self.properties.dirt_angle), math.radians(self.properties.clean_angle), self.properties.dirt_only)
170
171         print('Dirt calculated in %.6f' % (time.time()-t))
172
173         return('FINISHED',)
174
175 bpy.ops.add(VertexPaintDirt)
176
177 if __name__ == "__main__":
178     bpy.ops.mesh.vertex_paint_dirt()