vertexpaint_dirt.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. #
  19. # ***** END GPL LICENSE BLOCK *****
  20. # --------------------------------------------------------------------------
  21. # <pep8 compliant>
  22. def get_vcolor_layer_data(me):
  23. for lay in me.vertex_colors:
  24. if lay.active:
  25. return lay.data
  26. lay = me.vertex_colors.new()
  27. lay.active = True
  28. return lay.data
  29. def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, dirt_only):
  30. from mathutils import Vector
  31. from math import acos
  32. import array
  33. vert_tone = array.array("f", [0.0]) * len(me.vertices)
  34. # create lookup table for each vertex's connected vertices (via edges)
  35. con = [[] for i in range(len(me.vertices))]
  36. # add connected verts
  37. for e in me.edges:
  38. con[e.vertices[0]].append(e.vertices[1])
  39. con[e.vertices[1]].append(e.vertices[0])
  40. for i, v in enumerate(me.vertices):
  41. vec = Vector()
  42. no = v.normal
  43. co = v.co
  44. # get the direction of the vectors between the vertex and it's connected vertices
  45. for c in con[i]:
  46. vec += (me.vertices[c].co - co).normalized()
  47. # average the vector by dividing by the number of connected verts
  48. tot_con = len(con[i])
  49. if tot_con == 0:
  50. continue
  51. vec /= tot_con
  52. # angle is the acos() of the dot product between normal and connected verts.
  53. # > 90 degrees: convex
  54. # < 90 degrees: concave
  55. ang = acos(no.dot(vec))
  56. # enforce min/max
  57. ang = max(clamp_dirt, ang)
  58. if not dirt_only:
  59. ang = min(clamp_clean, ang)
  60. vert_tone[i] = ang
  61. # blur tones
  62. for i in range(blur_iterations):
  63. # backup the original tones
  64. orig_vert_tone = vert_tone[:]
  65. # use connected verts look up for blurring
  66. for j, c in enumerate(con):
  67. for v in c:
  68. vert_tone[j] += blur_strength * orig_vert_tone[v]
  69. vert_tone[j] /= len(c) * blur_strength + 1
  70. del orig_vert_tone
  71. min_tone = min(vert_tone)
  72. max_tone = max(vert_tone)
  73. tone_range = max_tone - min_tone
  74. if tone_range < 0.0001:
  75. # weak, don't cancel, see T43345
  76. tone_range = 0.0
  77. else:
  78. tone_range = 1.0 / tone_range
  79. active_col_layer = get_vcolor_layer_data(me)
  80. if not active_col_layer:
  81. return {'CANCELLED'}
  82. use_paint_mask = me.use_paint_mask
  83. for i, p in enumerate(me.polygons):
  84. if not use_paint_mask or p.select:
  85. for loop_index in p.loop_indices:
  86. loop = me.loops[loop_index]
  87. v = loop.vertex_index
  88. col = active_col_layer[loop_index].color
  89. tone = vert_tone[v]
  90. tone = (tone - min_tone) * tone_range
  91. if dirt_only:
  92. tone = min(tone, 0.5) * 2.0
  93. col[0] = tone * col[0]
  94. col[1] = tone * col[1]
  95. col[2] = tone * col[2]
  96. me.update()
  97. return {'FINISHED'}
  98. from bpy.types import Operator
  99. from bpy.props import FloatProperty, IntProperty, BoolProperty
  100. from math import pi
  101. class VertexPaintDirt(Operator):
  102. bl_idname = "paint.vertex_color_dirt"
  103. bl_label = "Dirty Vertex Colors"
  104. bl_options = {'REGISTER', 'UNDO'}
  105. blur_strength: FloatProperty(
  106. name="Blur Strength",
  107. description="Blur strength per iteration",
  108. min=0.01, max=1.0,
  109. default=1.0,
  110. )
  111. blur_iterations: IntProperty(
  112. name="Blur Iterations",
  113. description="Number of times to blur the colors (higher blurs more)",
  114. min=0, max=40,
  115. default=1,
  116. )
  117. clean_angle: FloatProperty(
  118. name="Highlight Angle",
  119. description="Less than 90 limits the angle used in the tonal range",
  120. min=0.0, max=pi,
  121. default=pi,
  122. unit='ROTATION',
  123. )
  124. dirt_angle: FloatProperty(
  125. name="Dirt Angle",
  126. description="Less than 90 limits the angle used in the tonal range",
  127. min=0.0, max=pi,
  128. default=0.0,
  129. unit='ROTATION',
  130. )
  131. dirt_only: BoolProperty(
  132. name="Dirt Only",
  133. description="Don't calculate cleans for convex areas",
  134. default=False,
  135. )
  136. @classmethod
  137. def poll(cls, context):
  138. obj = context.object
  139. return (obj and obj.type == 'MESH')
  140. def execute(self, context):
  141. obj = context.object
  142. mesh = obj.data
  143. ret = applyVertexDirt(mesh, self.blur_iterations, self.blur_strength, self.dirt_angle, self.clean_angle, self.dirt_only)
  144. return ret
  145. classes = (
  146. VertexPaintDirt,
  147. )