Any
/
vae
/com3d
/[CM3D2]English Mod Tools Pack
/CM3D2 Converter (English)
/misc_VIEW3D_PT_tools_weightpaint.py
# 「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) | |
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) | |
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'} | |