123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- # ***** BEGIN GPL LICENSE BLOCK *****
- #
- # Script copyright (C) Campbell J Barton
- #
- # This program is free software; you can redistribute it and/or
- # modify it under the terms of the GNU General Public License
- # as published by the Free Software Foundation; either version 2
- # of the License, or (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program; if not, write to the Free Software Foundation,
- # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- #
- # ***** END GPL LICENSE BLOCK *****
- # --------------------------------------------------------------------------
- # <pep8 compliant>
- def get_vcolor_layer_data(me):
- for lay in me.vertex_colors:
- if lay.active:
- return lay.data
- lay = me.vertex_colors.new()
- lay.active = True
- return lay.data
- def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, dirt_only):
- from mathutils import Vector
- from math import acos
- import array
- vert_tone = array.array("f", [0.0]) * len(me.vertices)
- # create lookup table for each vertex's connected vertices (via edges)
- con = [[] for i in range(len(me.vertices))]
- # add connected verts
- for e in me.edges:
- con[e.vertices[0]].append(e.vertices[1])
- con[e.vertices[1]].append(e.vertices[0])
- for i, v in enumerate(me.vertices):
- vec = Vector()
- no = v.normal
- co = v.co
- # get the direction of the vectors between the vertex and it's connected vertices
- for c in con[i]:
- vec += (me.vertices[c].co - co).normalized()
- # average the vector by dividing by the number of connected verts
- tot_con = len(con[i])
- if tot_con == 0:
- continue
- vec /= tot_con
- # angle is the acos() of the dot product between normal and connected verts.
- # > 90 degrees: convex
- # < 90 degrees: concave
- ang = acos(no.dot(vec))
- # enforce min/max
- ang = max(clamp_dirt, ang)
- if not dirt_only:
- ang = min(clamp_clean, ang)
- vert_tone[i] = ang
- # blur tones
- for i in range(blur_iterations):
- # backup the original tones
- orig_vert_tone = vert_tone[:]
- # use connected verts look up for blurring
- for j, c in enumerate(con):
- for v in c:
- vert_tone[j] += blur_strength * orig_vert_tone[v]
- vert_tone[j] /= len(c) * blur_strength + 1
- del orig_vert_tone
- min_tone = min(vert_tone)
- max_tone = max(vert_tone)
- tone_range = max_tone - min_tone
- if tone_range < 0.0001:
- # weak, don't cancel, see T43345
- tone_range = 0.0
- else:
- tone_range = 1.0 / tone_range
- active_col_layer = get_vcolor_layer_data(me)
- if not active_col_layer:
- return {'CANCELLED'}
- use_paint_mask = me.use_paint_mask
- for i, p in enumerate(me.polygons):
- if not use_paint_mask or p.select:
- for loop_index in p.loop_indices:
- loop = me.loops[loop_index]
- v = loop.vertex_index
- col = active_col_layer[loop_index].color
- tone = vert_tone[v]
- tone = (tone - min_tone) * tone_range
- if dirt_only:
- tone = min(tone, 0.5) * 2.0
- col[0] = tone * col[0]
- col[1] = tone * col[1]
- col[2] = tone * col[2]
- me.update()
- return {'FINISHED'}
- from bpy.types import Operator
- from bpy.props import FloatProperty, IntProperty, BoolProperty
- from math import pi
- class VertexPaintDirt(Operator):
- bl_idname = "paint.vertex_color_dirt"
- bl_label = "Dirty Vertex Colors"
- bl_options = {'REGISTER', 'UNDO'}
- blur_strength: FloatProperty(
- name="Blur Strength",
- description="Blur strength per iteration",
- min=0.01, max=1.0,
- default=1.0,
- )
- blur_iterations: IntProperty(
- name="Blur Iterations",
- description="Number of times to blur the colors (higher blurs more)",
- min=0, max=40,
- default=1,
- )
- clean_angle: FloatProperty(
- name="Highlight Angle",
- description="Less than 90 limits the angle used in the tonal range",
- min=0.0, max=pi,
- default=pi,
- unit='ROTATION',
- )
- dirt_angle: FloatProperty(
- name="Dirt Angle",
- description="Less than 90 limits the angle used in the tonal range",
- min=0.0, max=pi,
- default=0.0,
- unit='ROTATION',
- )
- dirt_only: BoolProperty(
- name="Dirt Only",
- description="Don't calculate cleans for convex areas",
- default=False,
- )
- @classmethod
- def poll(cls, context):
- obj = context.object
- return (obj and obj.type == 'MESH')
- def execute(self, context):
- obj = context.object
- mesh = obj.data
- ret = applyVertexDirt(mesh, self.blur_iterations, self.blur_strength, self.dirt_angle, self.clean_angle, self.dirt_only)
- return ret
- classes = (
- VertexPaintDirt,
- )
|