2.5: Top Menu
[blender.git] / release / scripts / vertexpaint_selfshadow_ao.py
1 #!BPY
2 """
3 Name: 'Self Shadow VCols (AO)...'
4 Blender: 245
5 Group: 'VertexPaint'
6 Tooltip: 'Generate Fake Ambient Occlusion with vertex colors.'
7 """
8
9 __author__ = "Campbell Barton aka ideasman42"
10 __url__ = ["www.blender.org", "blenderartists.org", "www.python.org"]
11 __version__ = "0.1"
12 __bpydoc__ = """\
13
14 Self Shadow
15
16 This usript uses the angles between faces to shade the mesh,
17 and optionaly blur the shading to remove artifacts from spesific edges.
18 """
19
20 # ***** BEGIN GPL LICENSE BLOCK *****
21 #
22 # Script copyright (C) Campbell J Barton
23 #
24 # This program is free software; you can redistribute it and/or
25 # modify it under the terms of the GNU General Public License
26 # as published by the Free Software Foundation; either version 2
27 # of the License, or (at your option) any later version.
28 #
29 # This program is distributed in the hope that it will be useful,
30 # but WITHOUT ANY WARRANTY; without even the implied warranty of
31 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32 # GNU General Public License for more details.
33 #
34 # You should have received a copy of the GNU General Public License
35 # along with this program; if not, write to the Free Software Foundation,
36 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
37 #
38 # ***** END GPL LICENCE BLOCK *****
39 # --------------------------------------------------------------------------
40
41 from Blender import Scene, Draw, sys, Window, Mathutils, Mesh
42 import bpy
43 import BPyMesh
44
45
46 def vertexFakeAO(me, PREF_BLUR_ITERATIONS, PREF_BLUR_RADIUS, PREF_MIN_EDLEN, PREF_CLAMP_CONCAVE, PREF_CLAMP_CONVEX, PREF_SHADOW_ONLY, PREF_SEL_ONLY):
47         Window.WaitCursor(1)
48         Ang= Mathutils.AngleBetweenVecs
49         
50         BPyMesh.meshCalcNormals(me)
51
52         vert_tone= [0.0] * len(me.verts)
53         vert_tone_count= [0] * len(me.verts)
54
55         min_tone=0
56         max_tone=0
57
58         for i, f in enumerate(me.faces):
59                 fc= f.cent
60                 fno = f.no
61                 
62                 for v in f.v:
63                         vno=v.no # get a scaled down normal.
64                         
65                         dot= vno.dot(v.co) - vno.dot(fc)
66                         vert_tone_count[v.index]+=1
67                         try:
68                                 a= Ang(vno, fno)
69                         except:
70                                 continue
71                         
72                         # Convex
73                         if dot>0:
74                                 a= min(PREF_CLAMP_CONVEX, a)
75                                 if not PREF_SHADOW_ONLY:
76                                         vert_tone[v.index] += a
77                         else:
78                                 a= min(PREF_CLAMP_CONCAVE, a)
79                                 vert_tone[v.index] -= a
80         
81         # average vert_tone_list into vert_tonef
82         for i, tones in enumerate(vert_tone):
83                 if vert_tone_count[i]:
84                         vert_tone[i] = vert_tone[i] / vert_tone_count[i]
85
86
87         # BLUR TONE
88         edge_lengths= [ ed.length for ed in me.edges]
89         
90         for i in xrange(PREF_BLUR_ITERATIONS):
91                 orig_vert_tone= list(vert_tone)
92                 for ii, ed in enumerate(me.edges):
93                         i1= ed.v1.index
94                         i2= ed.v2.index
95                         l= edge_lengths[ii]
96                         
97                         f=1.0
98                         if l > PREF_MIN_EDLEN and l < PREF_BLUR_RADIUS:
99                                 f= l/PREF_BLUR_RADIUS
100                                 
101                                 len_vert_tone_list_i1 = vert_tone_count[i1]
102                                 len_vert_tone_list_i2 = vert_tone_count[i2]
103                                         
104                                 if not len_vert_tone_list_i1: len_vert_tone_list_i1=1
105                                 if not len_vert_tone_list_i2: len_vert_tone_list_i2=1
106                                 
107                                 val1= (orig_vert_tone[i2]/len_vert_tone_list_i1)/ f
108                                 val2= (orig_vert_tone[i1]/len_vert_tone_list_i2)/ f
109                                 
110                                 vert_tone[i1]+= val1
111                                 vert_tone[i2]+= val2
112         
113
114         min_tone= min(vert_tone)
115         max_tone= max(vert_tone)
116         
117         #print min_tone, max_tone
118         
119         tone_range= max_tone-min_tone
120         if max_tone==min_tone:
121                 return
122         
123         for f in me.faces:
124                 if not PREF_SEL_ONLY or f.sel:
125                         f_col= f.col
126                         for i, v in enumerate(f):
127                                 col= f_col[i]
128                                 tone= vert_tone[v.index]
129                                 tone= (tone-min_tone)/tone_range
130                                 
131                                 col.r= int(tone*col.r)
132                                 col.g= int(tone*col.g)
133                                 col.b= int(tone*col.b)
134         
135         Window.WaitCursor(0)
136
137 def main():
138         sce= bpy.data.scenes.active
139         ob= sce.objects.active
140         
141         if not ob or ob.type != 'Mesh':
142                 Draw.PupMenu('Error, no active mesh object, aborting.')
143                 return
144         
145         me= ob.getData(mesh=1)
146         
147         PREF_BLUR_ITERATIONS= Draw.Create(1)    
148         PREF_BLUR_RADIUS= Draw.Create(0.05)
149         PREF_MIN_EDLEN= Draw.Create(0.01)
150         PREF_CLAMP_CONCAVE= Draw.Create(90)
151         PREF_CLAMP_CONVEX= Draw.Create(20)
152         PREF_SHADOW_ONLY= Draw.Create(0)
153         PREF_SEL_ONLY= Draw.Create(0)   
154         pup_block= [\
155         'Post AO Blur',\
156         ('  Iterations:', PREF_BLUR_ITERATIONS, 0, 40, 'Number times to blur the colors. (higher blurs more)'),\
157         ('  Blur Radius:', PREF_BLUR_RADIUS, 0.01, 40.0, 'How much distance effects blur transfur (higher blurs more).'),\
158         ('  Min EdgeLen:', PREF_MIN_EDLEN, 0.00001, 1.0, 'Minimim edge length to blur (very low values can cause errors).'),\
159         'Angle Clipping',\
160         ('  Highlight Angle:', PREF_CLAMP_CONVEX, 0, 180, 'Less then 180 limits the angle used in the tonal range.'),\
161         ('  Shadow Angle:', PREF_CLAMP_CONCAVE, 0, 180, 'Less then 180 limits the angle used in the tonal range.'),\
162         ('Shadow Only', PREF_SHADOW_ONLY, 'Dont calculate highlights for convex areas.'),\
163         ('Sel Faces Only', PREF_SEL_ONLY, 'Only apply to UV/Face selected faces (mix vpain/uvface select).'),\
164         ]
165         
166         if not Draw.PupBlock('SelfShadow...', pup_block):
167                 return
168         
169         PREF_BLUR_ITERATIONS= PREF_BLUR_ITERATIONS.val
170         PREF_BLUR_RADIUS= PREF_BLUR_RADIUS.val
171         PREF_MIN_EDLEN= PREF_MIN_EDLEN.val
172         PREF_CLAMP_CONCAVE= PREF_CLAMP_CONCAVE.val
173         PREF_CLAMP_CONVEX= PREF_CLAMP_CONVEX.val
174         PREF_SHADOW_ONLY= PREF_SHADOW_ONLY.val
175         PREF_SEL_ONLY= PREF_SEL_ONLY.val
176         
177         if not me.vertexColors:
178                 me.vertexColors= 1
179         
180         t= sys.time()
181         vertexFakeAO(me, PREF_BLUR_ITERATIONS, PREF_BLUR_RADIUS, PREF_MIN_EDLEN, PREF_CLAMP_CONCAVE, PREF_CLAMP_CONVEX, PREF_SHADOW_ONLY, PREF_SEL_ONLY)
182         
183         if ob.modifiers:
184                 me.update()
185         
186         print 'done in %.6f' % (sys.time()-t)
187 if __name__=='__main__':
188         main()
189