forked from bartvdbraak/blender
1cf0358a33
also renamed Polygon helper property from 'loops' to loop_indices
192 lines
5.5 KiB
Python
192 lines
5.5 KiB
Python
# ***** 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 applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, dirt_only):
|
|
from mathutils import Vector
|
|
from math import acos
|
|
|
|
vert_tone = [0.0] * len(me.vertices)
|
|
|
|
min_tone = 180.0
|
|
max_tone = 0.0
|
|
|
|
# create lookup table for each vertex's connected vertices (via edges)
|
|
con = []
|
|
|
|
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()
|
|
|
|
# normalize 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 vert and connected verts normals
|
|
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 = list(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
|
|
|
|
min_tone = min(vert_tone)
|
|
max_tone = max(vert_tone)
|
|
|
|
# debug information
|
|
# print(min_tone * 2 * math.pi)
|
|
# print(max_tone * 2 * math.pi)
|
|
# print(clamp_clean)
|
|
# print(clamp_dirt)
|
|
|
|
tone_range = max_tone - min_tone
|
|
|
|
if not tone_range:
|
|
return {'CANCELLED'}
|
|
|
|
active_col_layer = None
|
|
|
|
if len(me.vertex_colors):
|
|
for lay in me.vertex_colors:
|
|
if lay.active:
|
|
active_col_layer = lay.data
|
|
else:
|
|
bpy.ops.mesh.vertex_color_add()
|
|
me.vertex_colors[0].active = True
|
|
active_col_layer = me.vertex_colors[0].data
|
|
|
|
if not active_col_layer:
|
|
return
|
|
|
|
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)
|
|
tone *= 2
|
|
|
|
col[0] = tone * col[0]
|
|
col[1] = tone * col[1]
|
|
col[2] = tone * col[2]
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
import bpy
|
|
from bpy.types import Operator
|
|
from bpy.props import FloatProperty, IntProperty, BoolProperty
|
|
|
|
|
|
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=180.0,
|
|
default=180.0,
|
|
)
|
|
dirt_angle = FloatProperty(
|
|
name="Dirt Angle",
|
|
description="Less than 90 limits the angle used in the tonal range",
|
|
min=0.0, max=180.0,
|
|
default=0.0,
|
|
)
|
|
dirt_only = BoolProperty(
|
|
name="Dirt Only",
|
|
description="Don't calculate cleans for convex areas",
|
|
default=False,
|
|
)
|
|
|
|
def execute(self, context):
|
|
import time
|
|
from math import radians
|
|
obj = context.object
|
|
|
|
if not obj or obj.type != 'MESH':
|
|
self.report({'ERROR'}, "Error, no active mesh object, aborting")
|
|
return {'CANCELLED'}
|
|
|
|
mesh = obj.data
|
|
|
|
t = time.time()
|
|
|
|
ret = applyVertexDirt(mesh, self.blur_iterations, self.blur_strength, radians(self.dirt_angle), radians(self.clean_angle), self.dirt_only)
|
|
|
|
print('Dirt calculated in %.6f' % (time.time() - t))
|
|
|
|
return ret
|