kitou's picture
Upload 646 files
4d3af08
raw
history blame
15.5 kB
# 「3Dビュー」エリア → 「ウェイトペイント」モード → ツールシェルフ → 「ウェイトツール」パネル
import os, re, sys, bpy, time, bmesh, mathutils
from . import common
# メニュー等に項目追加
def menu_func(self, context):
icon_id = common.preview_collections['main']['KISS'].icon_id
box = self.layout.box()
column = box.column(align=False)
column.prop(context.active_object.data, 'use_paint_mask_vertex', icon='VERTEXSEL', text="Vertex selection mode")
column.operator('mesh.selected_mesh_vertex_group_blur', text="Blur the selected part", icon_value=icon_id)
column.operator('mesh.selected_mesh_vertex_group_calculation', text="Four arithmetic operation", icon_value=icon_id)
class selected_mesh_vertex_group_blur(bpy.types.Operator):
bl_idname = 'mesh.selected_mesh_vertex_group_blur'
bl_label = "Blur the vertex group of the selected part"
bl_description = "Blurs the vertex groups of the selected parts."
bl_options = {'REGISTER', 'UNDO'}
items = [
('LINER', "Linear", "", 'LINCURVE', 1),
('TRIGONOMETRIC', "Trigonometric", "", 'SMOOTHCURVE', 2),
]
smooth_method = bpy.props.EnumProperty(items=items, name="Damping type", default='TRIGONOMETRIC')
selection_blur_range_multi = bpy.props.FloatProperty(name="Blur Range", default=4.0, min=0.0, max=100.0, soft_min=0.0, soft_max=100.0, step=50, precision=1)
selection_blur_accuracy = bpy.props.IntProperty(name="Blur Accuracy", default=3, min=0, max=10, soft_min=1, soft_max=10)
items = [
('ALL', "All", "", 'COLLAPSEMENU', 1),
('ACTIVE', "Active", "", 'LAYER_ACTIVE', 2),
]
target_vertex_group = bpy.props.EnumProperty(items=items, name="Target vertex groups", default='ALL')
items = [
('NORMAL', "Normal", "", 'BRUSH_BLUR', 1),
('ADD', "Add", "", 'BRUSH_DARKEN', 2),
('SUB', "Sub", "", 'BRUSH_LIGHTEN', 3),
]
blur_mode = bpy.props.EnumProperty(items=items, name="Blur Mode", default='NORMAL')
blur_range_multi = bpy.props.FloatProperty(name="Blur Range", default=4.0, min=0.0, max=100.0, soft_min=0.0, soft_max=100.0, step=50, precision=1)
blur_count = bpy.props.IntProperty(name="Blur Amount", default=1, min=1, max=100, soft_min=1, soft_max=100)
is_vertex_group_limit_total = bpy.props.BoolProperty(name="Limit total weights.", default=True)
@classmethod
def poll(cls, context):
ob = context.active_object
if ob.type == 'MESH':
if len(ob.vertex_groups):
return len(ob.data.vertices) and len(ob.data.edges)
return False
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
self.layout.prop(self, 'smooth_method')
self.layout.label(text="Blur Selected", icon='UV_SYNC_SELECT')
self.layout.prop(self, 'selection_blur_range_multi', text="Range | Average of side lengths ×")
self.layout.prop(self, 'selection_blur_accuracy', text="Accuracy (number of steps)")
self.layout.label(text="Blur Vertex Group", icon='GROUP_VERTEX')
self.layout.prop(self, 'target_vertex_group', text="Target Group")
self.layout.prop(self, 'blur_mode', text="Mode")
self.layout.prop(self, 'blur_range_multi', text="Range | Average of side lengths ×")
self.layout.prop(self, 'blur_count', text="Blur Count")
self.layout.prop(self, 'is_vertex_group_limit_total', icon='IMGDISPLAY')
def execute(self, context):
class EmptyClass: pass
ob = context.active_object
me = ob.data
pre_mode = ob.mode
bpy.ops.object.mode_set(mode='OBJECT')
pre_selected_objects = context.selected_objects[:]
for selected_object in pre_selected_objects:
selected_object.select = False
ob.select = True
bpy.ops.object.duplicate(linked=False, mode='TRANSLATION')
selection_ob = context.active_object
selection_me = selection_ob.data
for v in selection_me.vertices:
if v.hide: v.hide, v.select = False, False
for e in selection_me.edges:
if e.hide: e.hide, e.select = False, False
for p in selection_me.polygons:
if p.hide: p.hide, p.select = False, False
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='INVERT')
if context.tool_settings.mesh_select_mode[0]:
bpy.ops.mesh.delete(type='VERT')
elif context.tool_settings.mesh_select_mode[1]:
bpy.ops.mesh.delete(type='EDGE')
elif context.tool_settings.mesh_select_mode[2]:
bpy.ops.mesh.delete(type='FACE')
bpy.ops.mesh.select_all(action='SELECT')
if 1 <= self.selection_blur_accuracy:
bpy.ops.mesh.subdivide(number_cuts=self.selection_blur_accuracy, smoothness=0, quadtri=False, quadcorner='INNERVERT', fractal=0, fractal_along_normal=0, seed=0)
bpy.ops.object.mode_set(mode='OBJECT')
selection_kd = mathutils.kdtree.KDTree(len(selection_me.vertices))
[selection_kd.insert(v.co, v.index) for v in selection_me.vertices]
selection_kd.balance()
common.remove_data([selection_ob, selection_me])
ob.select = True
context.scene.objects.active = ob
bm = bmesh.new()
bm.from_mesh(me)
edge_lengths = [e.calc_length() for e in bm.edges]
bm.free()
edge_lengths.sort()
edge_lengths_center_index = int( (len(edge_lengths) - 1) * 0.5 )
average_edge_length = edge_lengths[edge_lengths_center_index]
selection_blur_range = average_edge_length * self.selection_blur_range_multi
vert_selection_values = [None for v in me.vertices]
for vert in me.vertices:
co, index, dist = selection_kd.find(vert.co)
if dist <= selection_blur_range + 0.00001:
if 0 < selection_blur_range:
if self.smooth_method == 'TRIGONOMETRIC':
value = common.trigonometric_smooth(1.0 - (dist / selection_blur_range))
else:
value = 1.0 - (dist / selection_blur_range)
vert_selection_values[vert.index] = value
else:
vert_selection_values[vert.index] = 1.0
"""
# 頂点カラーで選択状態を確認
preview_vertex_color = me.vertex_colors.new()
for loop in me.loops:
v = vert_selection_values[loop.vertex_index]
if v != None:
preview_vertex_color.data[loop.index].color = (v, v, v)
else:
preview_vertex_color.data[loop.index].color = (0, 0, 0)
"""
kd = mathutils.kdtree.KDTree(len(me.vertices))
[kd.insert(v.co, v.index) for v in me.vertices]
kd.balance()
blur_range = average_edge_length * self.blur_range_multi
for i in range(self.blur_count):
pre_vert_weights = [[0.0 for vg in ob.vertex_groups] for v in me.vertices]
for vert in me.vertices:
for vge in vert.groups:
pre_vert_weights[vert.index][vge.group] = vge.weight
for vert in me.vertices:
selection_value = vert_selection_values[vert.index]
if selection_value == None: continue
near_infos = []
total_effect = 0.0
for co, index, dist in kd.find_range(vert.co, blur_range):
ec = EmptyClass()
ec.index = index
if 0 < blur_range:
raw_effect = 1.0 - (dist / blur_range)
if self.smooth_method == 'TRIGONOMETRIC': ec.effect = common.trigonometric_smooth(raw_effect)
else: ec.effect = raw_effect
else:
ec.effect = 1.0
total_effect += ec.effect
near_infos.append(ec)
new_vert_weight = [0.0 for vg in ob.vertex_groups]
for ec in near_infos:
pre_vert_weight = pre_vert_weights[ec.index]
weight_multi = ec.effect / total_effect
for vg_index, near_vert_pre_weight_value in enumerate(pre_vert_weight):
current_vert_pre_weight_value = pre_vert_weights[vert.index][vg_index]
if self.blur_mode == 'NORMAL':
send_weight_value = near_vert_pre_weight_value
elif self.blur_mode == 'ADD':
if current_vert_pre_weight_value < near_vert_pre_weight_value:
send_weight_value = near_vert_pre_weight_value
else:
send_weight_value = current_vert_pre_weight_value
elif self.blur_mode == 'SUB':
if near_vert_pre_weight_value < current_vert_pre_weight_value:
send_weight_value = near_vert_pre_weight_value
else:
send_weight_value = current_vert_pre_weight_value
new_vert_weight[vg_index] += send_weight_value * weight_multi
for vg in ob.vertex_groups:
if self.target_vertex_group == 'ACTIVE' and ob.vertex_groups.active.name != vg.name: continue
if vg.lock_weight: continue
pre_weight = pre_vert_weights[vert.index][vg.index]
new_weight = new_vert_weight[vg.index]
result_weight = (pre_weight * (1.0 - selection_value)) + (new_weight * selection_value)
if 0.0 < result_weight:
vg.add([vert.index], result_weight, 'REPLACE')
else:
vg.remove([vert.index])
if self.is_vertex_group_limit_total:
bpy.ops.object.vertex_group_limit_total(group_select_mode='ALL', limit=4)
bpy.ops.object.mode_set(mode=pre_mode)
for selected_object in pre_selected_objects:
selected_object.select = True
return {'FINISHED'}
class selected_mesh_vertex_group_calculation(bpy.types.Operator):
bl_idname = 'mesh.selected_mesh_vertex_group_calculation'
bl_label = "Four arithmetic operations on the vertex groups of the selection"
bl_description = "Applies four arithmetic operations to the vertex groups of selection."
bl_options = {'REGISTER', 'UNDO'}
items = [
('LINER', "Iinear", "", 'LINCURVE', 1),
('TRIGONOMETRIC', "Trigonometric", "", 'SMOOTHCURVE', 2),
]
smooth_method = bpy.props.EnumProperty(items=items, name="Smooth Methid", default='TRIGONOMETRIC')
selection_blur_range_multi = bpy.props.FloatProperty(name="Blur Range", default=4.0, min=0.0, max=100.0, soft_min=0.0, soft_max=100.0, step=50, precision=1)
selection_blur_accuracy = bpy.props.IntProperty(name="Blur Accuracy", default=3, min=0, max=10, soft_min=1, soft_max=10)
items = [
('ACTIVE', "Active", "", 'LAYER_ACTIVE', 1),
]
target_vertex_group = bpy.props.EnumProperty(items=items, name="Target Vertex Group", default='ACTIVE')
items = [
('ADD', "Add", "", 'ZOOMIN', 1),
('SUB', "Sub", "", 'ZOOMOUT', 2),
('MULTI', "Multi", "", 'X', 3),
('DIV', "Div", "", 'FULLSCREEN_EXIT', 4),
]
calculation_mode = bpy.props.EnumProperty(items=items, name="Arithmetic operation mode", default='ADD')
calculation_value = bpy.props.FloatProperty(name="Value", default=1.0, min=-100.0, max=100.0, soft_min=-100.0, soft_max=100.0, step=10, precision=1)
@classmethod
def poll(cls, context):
ob = context.active_object
if ob.type == 'MESH':
return bool(len(ob.vertex_groups))
return False
def draw(self, context):
self.layout.label(text="Blur the selection of the vertex groups.", icon='UV_SYNC_SELECT')
self.layout.prop(self, 'smooth_method')
self.layout.prop(self, 'selection_blur_range_multi', text="Range | Average of side length ×")
self.layout.prop(self, 'selection_blur_accuracy', text="Accuracy (Steps)")
self.layout.label(text="Four arithmetic operations", icon='BRUSH_ADD')
self.layout.prop(self, 'target_vertex_group', text="Target Group")
self.layout.prop(self, 'calculation_mode', text="Mode")
self.layout.prop(self, 'calculation_value', text="Value")
calculation_text = "Expression: Original weight "
if self.calculation_mode == 'ADD': calculation_text += "+"
elif self.calculation_mode == 'SUB': calculation_text += "-"
elif self.calculation_mode == 'MULTI': calculation_text += "×"
elif self.calculation_mode == 'DIV': calculation_text += "÷"
calculation_text += " " + str(round(self.calculation_value, 1))
self.layout.label(text=calculation_text)
def execute(self, context):
class EmptyClass: pass
if self.calculation_mode == 'DIV' and self.calculation_value == 0.0:
self.report(type={'ERROR'}, message="Cannot divide by zero, Aborting.")
return {'CANCELLED'}
ob = context.active_object
me = ob.data
pre_mode = ob.mode
bpy.ops.object.mode_set(mode='OBJECT')
pre_selected_objects = context.selected_objects[:]
for selected_object in pre_selected_objects:
selected_object.select = False
ob.select = True
bpy.ops.object.duplicate(linked=False, mode='TRANSLATION')
selection_ob = context.active_object
selection_me = selection_ob.data
for v in selection_me.vertices:
if v.hide: v.hide, v.select = False, False
for e in selection_me.edges:
if e.hide: e.hide, e.select = False, False
for p in selection_me.polygons:
if p.hide: p.hide, p.select = False, False
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='INVERT')
if context.tool_settings.mesh_select_mode[0]:
bpy.ops.mesh.delete(type='VERT')
elif context.tool_settings.mesh_select_mode[1]:
bpy.ops.mesh.delete(type='EDGE')
elif context.tool_settings.mesh_select_mode[2]:
bpy.ops.mesh.delete(type='FACE')
bpy.ops.mesh.select_all(action='SELECT')
if 1 <= self.selection_blur_accuracy:
bpy.ops.mesh.subdivide(number_cuts=self.selection_blur_accuracy, smoothness=0, quadtri=False, quadcorner='INNERVERT', fractal=0, fractal_along_normal=0, seed=0)
bpy.ops.object.mode_set(mode='OBJECT')
selection_kd = mathutils.kdtree.KDTree(len(selection_me.vertices))
[selection_kd.insert(v.co, v.index) for v in selection_me.vertices]
selection_kd.balance()
common.remove_data([selection_ob, selection_me])
ob.select = True
context.scene.objects.active = ob
bm = bmesh.new()
bm.from_mesh(me)
edge_lengths = [e.calc_length() for e in bm.edges]
bm.free()
edge_lengths.sort()
edge_lengths_center_index = int( (len(edge_lengths) - 1) * 0.5 )
average_edge_length = edge_lengths[edge_lengths_center_index]
selection_blur_range = average_edge_length * self.selection_blur_range_multi
vert_selection_values = [None for v in me.vertices]
for vert in me.vertices:
co, index, dist = selection_kd.find(vert.co)
if dist <= selection_blur_range + 0.00001:
if 0 < selection_blur_range:
if self.smooth_method == 'TRIGONOMETRIC':
value = common.trigonometric_smooth(1.0 - (dist / selection_blur_range))
else:
value = 1.0 - (dist / selection_blur_range)
vert_selection_values[vert.index] = value
else:
vert_selection_values[vert.index] = 1.0
"""
# 頂点カラーで選択状態を確認
preview_vertex_color = me.vertex_colors.new()
for loop in me.loops:
v = vert_selection_values[loop.vertex_index]
if v != None:
preview_vertex_color.data[loop.index].color = (v, v, v)
else:
preview_vertex_color.data[loop.index].color = (0, 0, 0)
"""
for vert in me.vertices:
effect = vert_selection_values[vert.index]
if effect == None: continue
pre_vert_weight = 0.0
for vge in vert.groups:
if ob.vertex_groups.active.index == vge.group:
pre_vert_weight = vge.weight
if self.calculation_mode == 'ADD':
new_vert_weight = pre_vert_weight + self.calculation_value
elif self.calculation_mode == 'SUB':
new_vert_weight = pre_vert_weight - self.calculation_value
elif self.calculation_mode == 'MULTI':
new_vert_weight = pre_vert_weight * self.calculation_value
elif self.calculation_mode == 'DIV':
new_vert_weight = pre_vert_weight / self.calculation_value
if new_vert_weight < 0.0:
new_vert_weight = 0.0
elif 1.0 < new_vert_weight:
new_vert_weight = 1.0
new_vert_weight = (pre_vert_weight * (1.0 - effect)) + (new_vert_weight * effect)
if 0.0 < new_vert_weight:
ob.vertex_groups.active.add([vert.index], new_vert_weight, 'REPLACE')
else:
ob.vertex_groups.active.remove([vert.index])
bpy.ops.object.mode_set(mode=pre_mode)
for selected_object in pre_selected_objects:
selected_object.select = True
return {'FINISHED'}