CM3D2 Converter.misc_MESH_MT_vertex_group_specials
1# 「プロパティ」エリア → 「メッシュデータ」タブ → 「頂点グループ」パネル → ▼ボタン 2import time 3import bpy 4import bmesh 5import mathutils 6from . import common 7from . import compat 8from .translations.pgettext_functions import * 9 10 11# メニュー等に項目追加 12def menu_func(self, context): 13 icon_id = common.kiss_icon() 14 self.layout.separator() 15 self.layout.operator('object.quick_transfer_vertex_group', icon_value=icon_id) 16 self.layout.operator('object.precision_transfer_vertex_group', icon_value=icon_id) 17 self.layout.separator() 18 self.layout.operator('object.quick_blur_vertex_group', icon_value=icon_id) 19 self.layout.operator('object.blur_vertex_group', icon_value=icon_id) 20 self.layout.separator() 21 self.layout.operator('object.multiply_vertex_group', icon_value=icon_id) 22 self.layout.separator() 23 self.layout.operator('object.remove_noassign_vertex_groups', icon_value=icon_id) 24 25 26@compat.BlRegister() 27class CNV_OT_quick_transfer_vertex_group(bpy.types.Operator): 28 bl_idname = 'object.quick_transfer_vertex_group' 29 bl_label = "クイック・ウェイト転送" 30 bl_description = "アクティブなメッシュに他の選択メッシュの頂点グループを高速で転送します" 31 bl_options = {'REGISTER', 'UNDO'} 32 33 is_remove_old_vertex_groups = bpy.props.BoolProperty(name="すでにある頂点グループを削除 (ロックで保護)", default=False) 34 is_source_select_vert_only = bpy.props.BoolProperty(name="選択頂点のみ(参照)", default=False) 35 is_target_select_vert_only = bpy.props.BoolProperty(name="選択頂点のみ(対象)", default=False) 36 items = [ 37 ('NEAREST', "最も近い頂点", "", 'VERTEXSEL', 1), 38 ('EDGEINTERP_NEAREST', "最も近い辺", "", 'EDGESEL', 2), 39 ('POLYINTERP_NEAREST', "最も近い面", "", 'FACESEL', 3), 40 ('POLYINTERP_VNORPROJ', "投影先", "", 'MOD_UVPROJECT', 4), 41 ] 42 vert_mapping = bpy.props.EnumProperty(items=items, name="参照要素", default='POLYINTERP_NEAREST') 43 is_clean = bpy.props.BoolProperty(name="転送後にクリーンを実行", default=True) 44 is_remove_noassign = bpy.props.BoolProperty(name="転送後に割り当てのない頂点グループを削除", default=True) 45 46 @classmethod 47 def poll(cls, context): 48 obs = context.selected_objects 49 if len(obs) < 2: 50 return False 51 52 active_ob = context.active_object 53 for ob in obs: 54 if ob.type != 'MESH': 55 return False 56 if ob.name != active_ob.name: 57 if not len(ob.vertex_groups): 58 return False 59 return True 60 61 def invoke(self, context, event): 62 return context.window_manager.invoke_props_dialog(self) 63 64 def draw(self, context): 65 self.layout.prop(self, 'is_remove_old_vertex_groups', icon='ERROR') 66 67 row = self.layout.row(align=True) 68 row.prop(self, 'is_source_select_vert_only', icon='UV_SYNC_SELECT') 69 row.prop(self, 'is_target_select_vert_only', icon='UV_SYNC_SELECT') 70 71 self.layout.prop(self, 'vert_mapping') 72 self.layout.prop(self, 'is_clean', icon='DISCLOSURE_TRI_DOWN') 73 self.layout.prop(self, 'is_remove_noassign', icon='X') 74 75 def execute(self, context): 76 class MyVertexGroupElement: 77 pass 78 79 target_ob = context.active_object 80 target_me = target_ob.data 81 82 pre_mode = target_ob.mode 83 bpy.ops.object.mode_set(mode='OBJECT') 84 85 original_source_obs = [] 86 for ob in context.selected_objects: 87 if ob.name != target_ob.name: 88 original_source_obs.append(ob) 89 90 compat.set_select(target_ob, False) 91 compat.set_active(context, original_source_obs[0]) 92 bpy.ops.object.duplicate(linked=False, mode='TRANSLATION') 93 if len(context.selected_objects) > 1: 94 bpy.ops.object.join() 95 join_source_ob = context.selected_objects[0] 96 join_source_me = join_source_ob.data 97 98 temp_source_ob = join_source_ob.copy() 99 temp_source_me = join_source_me.copy() 100 temp_source_ob.data = temp_source_me 101 compat.link(context.scene, temp_source_ob) 102 compat.set_select(temp_source_ob, True) 103 compat.set_select(join_source_ob, False) 104 compat.set_active(context, temp_source_ob) 105 if self.is_source_select_vert_only: 106 bpy.ops.object.mode_set(mode='EDIT') 107 bpy.ops.mesh.select_all(action='INVERT') 108 bpy.ops.mesh.delete(type='VERT') 109 bpy.ops.object.mode_set(mode='OBJECT') 110 111 compat.set_select(target_ob, True) 112 compat.set_active(context, target_ob) 113 114 if self.vert_mapping == 'POLYINTERP_VNORPROJ' and len(temp_source_me.polygons) == 0: 115 self.vert_mapping = 'EDGEINTERP_NEAREST' 116 self.report(type={'WARNING'}, message="面がひとつも存在しません、辺モードに変更します") 117 if self.vert_mapping == 'POLYINTERP_NEAREST' and len(temp_source_me.polygons) == 0: 118 self.vert_mapping = 'EDGEINTERP_NEAREST' 119 self.report(type={'WARNING'}, message="面がひとつも存在しません、辺モードに変更します") 120 if self.vert_mapping == 'EDGEINTERP_NEAREST' and len(temp_source_me.edges) == 0: 121 self.vert_mapping = 'NEAREST' 122 self.report(type={'WARNING'}, message="辺がひとつも存在しません、頂点モードに変更します") 123 if self.vert_mapping == 'NEAREST' and len(temp_source_me.vertices) == 0: 124 self.report(type={'ERROR'}, message="頂点がひとつも存在しません、中止します") 125 return {'CANCELLED'} 126 127 if self.is_remove_old_vertex_groups: 128 for vg in target_ob.vertex_groups[:]: 129 if not vg.lock_weight: 130 target_ob.vertex_groups.remove(vg) 131 132 old_vertex_groups = [] 133 for vert in target_me.vertices: 134 mvges = [] 135 for vge in vert.groups: 136 mvge = MyVertexGroupElement() 137 mvge.vertex_group = target_ob.vertex_groups[vge.group] 138 mvge.weight = vge.weight 139 mvges.append(mvge) 140 old_vertex_groups.append(mvges) 141 142 if self.is_remove_noassign: 143 pre_vertex_group_names = [vg.name for vg in target_ob.vertex_groups] 144 145 bpy.ops.object.data_transfer(use_reverse_transfer=True, use_freeze=False, data_type='VGROUP_WEIGHTS', use_create=True, vert_mapping=self.vert_mapping, use_auto_transform=False, use_object_transform=True, use_max_distance=False, ray_radius=0, layers_select_src='NAME', layers_select_dst='ALL', mix_mode='REPLACE', mix_factor=1) 146 if self.is_clean: 147 bpy.ops.object.vertex_group_clean(group_select_mode='ALL', limit=0.00000000001) 148 149 bpy.ops.object.mode_set(mode='EDIT') 150 bpy.ops.object.mode_set(mode='OBJECT') 151 152 if self.is_remove_noassign: 153 is_keeps = [False for i in range(len(target_ob.vertex_groups))] 154 for vert in target_me.vertices: 155 for vge in vert.groups: 156 if not is_keeps[vge.group]: 157 if 0.000001 < vge.weight: 158 is_keeps[vge.group] = True 159 copy_vertex_groups = target_ob.vertex_groups[:] 160 for i in range(len(copy_vertex_groups)): 161 if not is_keeps[i] and not copy_vertex_groups[i].lock_weight: 162 if copy_vertex_groups[i].name not in pre_vertex_group_names: 163 target_ob.vertex_groups.remove(copy_vertex_groups[i]) 164 165 if self.is_target_select_vert_only: 166 for vert in target_me.vertices: 167 if not vert.select: 168 for vg in target_ob.vertex_groups: 169 vg.remove([vert.index]) 170 for mvge in old_vertex_groups[vert.index]: 171 mvge.vertex_group.add([vert.index], mvge.weight, 'REPLACE') 172 173 for vert in target_me.vertices: 174 for vg in target_ob.vertex_groups: 175 if vg.lock_weight: 176 vg.remove([vert.index]) 177 for mvge in old_vertex_groups[vert.index]: 178 if mvge.vertex_group.lock_weight: 179 mvge.vertex_group.add([vert.index], mvge.weight, 'REPLACE') 180 181 common.remove_data([temp_source_ob, temp_source_me]) 182 compat.set_select(join_source_ob, True) 183 184 common.remove_data([join_source_ob, join_source_me]) 185 for ob in original_source_obs: 186 compat.set_select(ob, True) 187 188 compat.set_active(context, target_ob) 189 bpy.ops.object.mode_set(mode=pre_mode) 190 return {'FINISHED'} 191 192 193@compat.BlRegister() 194class CNV_OT_precision_transfer_vertex_group(bpy.types.Operator): 195 bl_idname = 'object.precision_transfer_vertex_group' 196 bl_label = "空間ぼかし・ウェイト転送" 197 bl_description = "アクティブなメッシュに他の選択メッシュの頂点グループを遠いほどぼかして転送します" 198 bl_options = {'REGISTER', 'UNDO'} 199 200 is_first_remove_all = bpy.props.BoolProperty(name="すでにある頂点グループを削除 (ロックで保護)", default=False) 201 subdivide_number = bpy.props.IntProperty(name="参照元の分割", default=1, min=0, max=10, soft_min=0, soft_max=10) 202 extend_range = bpy.props.FloatProperty(name="範囲倍率", default=1.1, min=1.0001, max=5.0, soft_min=1.0001, soft_max=5.0, step=10, precision=2) 203 is_remove_empty = bpy.props.BoolProperty(name="割り当てのない頂点グループを削除", default=True) 204 205 @classmethod 206 def poll(cls, context): 207 active_ob = context.active_object 208 obs = context.selected_objects 209 if len(obs) != 2: 210 return False 211 212 for ob in obs: 213 if ob.type != 'MESH': 214 return False 215 if ob.name != active_ob.name: 216 if len(ob.vertex_groups): 217 return True 218 return False 219 220 def invoke(self, context, event): 221 return context.window_manager.invoke_props_dialog(self) 222 223 def draw(self, context): 224 self.layout.prop(self, 'is_first_remove_all', icon='ERROR') 225 self.layout.prop(self, 'subdivide_number', icon='LATTICE_DATA') 226 self.layout.prop(self, 'extend_range', icon='PROP_ON') 227 self.layout.prop(self, 'is_remove_empty', icon='X') 228 229 def execute(self, context): 230 start_time = time.time() 231 232 target_ob = context.active_object 233 target_me = target_ob.data 234 235 pre_mode = target_ob.mode 236 bpy.ops.object.mode_set(mode='OBJECT') 237 238 for ob in context.selected_objects: 239 if ob.name != target_ob.name: 240 source_original_ob = ob 241 break 242 source_ob = source_original_ob.copy() 243 source_me = source_original_ob.data.copy() 244 source_ob.data = source_me 245 compat.link(context.scene, source_ob) 246 247 compat.set_select(target_ob, False) 248 compat.set_select(source_original_ob, False) 249 compat.set_active(context, source_ob) 250 try: 251 bpy.ops.object.mode_set(mode='EDIT') 252 bpy.ops.mesh.reveal() 253 bpy.ops.mesh.select_all(action='SELECT') 254 bpy.ops.mesh.subdivide(number_cuts=self.subdivide_number, smoothness=0.0, quadcorner='STRAIGHT_CUT', fractal=0.0, fractal_along_normal=0.0, seed=0) 255 bpy.ops.object.mode_set(mode='OBJECT') 256 257 if self.is_first_remove_all: 258 for vg in target_ob.vertex_groups[:]: 259 if not vg.lock_weight: 260 target_ob.vertex_groups.remove(vg) 261 262 kd = mathutils.kdtree.KDTree(len(source_me.vertices)) 263 for vert in source_me.vertices: 264 co = compat.mul(source_ob.matrix_world, vert.co) 265 kd.insert(co, vert.index) 266 kd.balance() 267 268 context.window_manager.progress_begin(0, len(target_me.vertices)) 269 progress_reduce = len(target_me.vertices) // 200 + 1 270 near_vert_data = [] 271 near_vert_multi_total = [] 272 near_vert_multi_total_append = near_vert_multi_total.append 273 for vert in target_me.vertices: 274 near_vert_data.append([]) 275 near_vert_data_append = near_vert_data[-1].append 276 277 target_co = compat.mul(target_ob.matrix_world, vert.co) 278 279 mini_co, mini_index, mini_dist = kd.find(target_co) 280 radius = mini_dist * self.extend_range 281 diff_radius = radius - mini_dist 282 283 multi_total = 0.0 284 for co, index, dist in kd.find_range(target_co, radius): 285 if 0 < diff_radius: 286 multi = (diff_radius - (dist - mini_dist)) / diff_radius 287 else: 288 multi = 1.0 289 near_vert_data_append((index, multi)) 290 multi_total += multi 291 near_vert_multi_total_append(multi_total) 292 293 if vert.index % progress_reduce == 0: 294 context.window_manager.progress_update(vert.index) 295 context.window_manager.progress_end() 296 297 context.window_manager.progress_begin(0, len(source_ob.vertex_groups)) 298 for source_vertex_group in source_ob.vertex_groups: 299 300 if source_vertex_group.name in target_ob.vertex_groups: 301 target_vertex_group = target_ob.vertex_groups[source_vertex_group.name] 302 else: 303 target_vertex_group = target_ob.vertex_groups.new(name=source_vertex_group.name) 304 305 is_waighted = False 306 307 source_weights = [] 308 source_weights_append = source_weights.append 309 for source_vert in source_me.vertices: 310 for elem in source_vert.groups: 311 if elem.group == source_vertex_group.index: 312 source_weights_append(elem.weight) 313 break 314 else: 315 source_weights_append(0.0) 316 317 for target_vert in target_me.vertices: 318 319 if 0 < near_vert_multi_total[target_vert.index]: 320 321 total_weight = [source_weights[i] * m for i, m in near_vert_data[target_vert.index]] 322 total_weight = sum(total_weight) 323 324 average_weight = total_weight / near_vert_multi_total[target_vert.index] 325 else: 326 average_weight = 0.0 327 328 if 0.000001 < average_weight: 329 target_vertex_group.add([target_vert.index], average_weight, 'REPLACE') 330 is_waighted = True 331 else: 332 if not self.is_first_remove_all: 333 target_vertex_group.remove([target_vert.index]) 334 335 context.window_manager.progress_update(source_vertex_group.index) 336 337 if not is_waighted and self.is_remove_empty: 338 target_ob.vertex_groups.remove(target_vertex_group) 339 context.window_manager.progress_end() 340 341 target_ob.vertex_groups.active_index = 0 342 finally: 343 common.remove_data([source_ob, source_me]) 344 compat.set_select(source_original_ob, True) 345 compat.set_select(target_ob, True) 346 compat.set_active(context, target_ob) 347 bpy.ops.object.mode_set(mode=pre_mode) 348 349 diff_time = time.time() - start_time 350 self.report(type={'INFO'}, message=f_tip_("{:.2f} Seconds", diff_time)) 351 return {'FINISHED'} 352 353 354@compat.BlRegister() 355class CNV_OT_quick_blur_vertex_group(bpy.types.Operator): 356 bl_idname = 'object.quick_blur_vertex_group' 357 bl_label = "頂点グループぼかし" 358 bl_description = "アクティブ、もしくは全ての頂点グループをぼかします" 359 bl_options = {'REGISTER', 'UNDO'} 360 361 items = [ 362 ('ACTIVE', "アクティブのみ", "", 'HAND', 1), 363 ('ALL', "全て", "", 'ARROW_LEFTRIGHT', 2), 364 ] 365 target = bpy.props.EnumProperty(items=items, name="対象", default='ALL') 366 strength = bpy.props.FloatProperty(name="強さ", default=1.0, min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, step=10, precision=3) 367 count = bpy.props.IntProperty(name="反復", default=1, min=1, max=256, soft_min=1, soft_max=256) 368 size = bpy.props.FloatProperty(name="拡大縮小", default=0.0, min=-1.0, max=1.0, soft_min=-1.0, soft_max=1.0, step=10, precision=3) 369 370 @classmethod 371 def poll(cls, context): 372 ob = context.active_object 373 if ob and ob.type == 'MESH': 374 return ob.vertex_groups.active 375 return False 376 377 def invoke(self, context, event): 378 return context.window_manager.invoke_props_dialog(self) 379 380 def draw(self, context): 381 self.layout.prop(self, 'target', icon='VIEWZOOM') 382 self.layout.prop(self, 'strength') 383 self.layout.prop(self, 'count') 384 self.layout.prop(self, 'size') 385 386 def execute(self, context): 387 target_ob = context.active_object 388 target_me = target_ob.data 389 390 pre_mode = target_ob.mode 391 bpy.ops.object.mode_set(mode='WEIGHT_PAINT') 392 393 pre_use_paint_mask_vertex = target_me.use_paint_mask_vertex 394 target_me.use_paint_mask_vertex = True 395 396 bpy.ops.paint.vert_select_all(action='SELECT') 397 # source='ALL'は2.8で削除された 398 bpy.ops.object.vertex_group_smooth(group_select_mode=self.target, factor=self.strength, repeat=self.count, expand=self.size) 399 400 target_me.use_paint_mask_vertex = pre_use_paint_mask_vertex 401 402 bpy.ops.object.mode_set(mode=pre_mode) 403 return {'FINISHED'} 404 405 406@compat.BlRegister() 407class CNV_OT_blur_vertex_group(bpy.types.Operator): 408 bl_idname = 'object.blur_vertex_group' 409 bl_label = "旧・頂点グループぼかし" 410 bl_description = "アクティブ、もしくは全ての頂点グループをぼかします" 411 bl_options = {'REGISTER', 'UNDO'} 412 413 items = [ 414 ('ACTIVE', "アクティブのみ", "", 'HAND', 1), 415 ('UP', "アクティブより上", "", 'TRIA_UP_BAR', 2), 416 ('DOWN', "アクティブより下", "", 'TRIA_DOWN_BAR', 3), 417 ('ALL', "全て", "", 'ARROW_LEFTRIGHT', 4), 418 ] 419 target = bpy.props.EnumProperty(items=items, name="対象", default='ACTIVE') 420 radius = bpy.props.FloatProperty(name="範囲倍率", default=3, min=0.1, max=50, soft_min=0.1, soft_max=50, step=50, precision=2) 421 strength = bpy.props.IntProperty(name="強さ", default=1, min=1, max=10, soft_min=1, soft_max=10) 422 items = [ 423 ('BOTH', "増減両方", "", 'AUTOMERGE_ON', 1), 424 ('ADD', "増加のみ", "", 'TRIA_UP', 2), 425 ('SUB', "減少のみ", "", 'TRIA_DOWN', 3), 426 ] 427 effect = bpy.props.EnumProperty(items=items, name="ぼかし効果", default='BOTH') 428 is_normalize = bpy.props.BoolProperty(name="他頂点グループも調節", default=True) 429 430 @classmethod 431 def poll(cls, context): 432 ob = context.active_object 433 if ob and ob.type == 'MESH': 434 return ob.vertex_groups.active 435 return False 436 437 def invoke(self, context, event): 438 return context.window_manager.invoke_props_dialog(self) 439 440 def draw(self, context): 441 self.layout.prop(self, 'target', icon='VIEWZOOM') 442 self.layout.prop(self, 'radius', icon='PROP_ON') 443 self.layout.prop(self, 'strength', icon='ARROW_LEFTRIGHT') 444 self.layout.prop(self, 'effect', icon='BRUSH_BLUR') 445 self.layout.prop(self, 'is_normalize', icon='GROUP') 446 447 def execute(self, context): 448 ob = context.active_object 449 me = ob.data 450 451 pre_mode = ob.mode 452 bpy.ops.object.mode_set(mode='OBJECT') 453 454 bm = bmesh.new() 455 bm.from_mesh(me) 456 edge_lengths = [e.calc_length() for e in bm.edges] 457 bm.free() 458 edge_lengths.sort() 459 average_edge_length = sum(edge_lengths) / len(edge_lengths) 460 center_index = int((len(edge_lengths) - 1) / 2.0) 461 average_edge_length = (average_edge_length + edge_lengths[center_index]) / 2 462 radius = average_edge_length * self.radius 463 464 context.window_manager.progress_begin(0, len(me.vertices)) 465 progress_reduce = len(me.vertices) // 200 + 1 466 near_vert_data = [] 467 kd = mathutils.kdtree.KDTree(len(me.vertices)) 468 for vert in me.vertices: 469 kd.insert(vert.co.copy(), vert.index) 470 kd.balance() 471 for vert in me.vertices: 472 new_vert_datum = [] 473 near_vert_data.append(new_vert_datum) 474 near_vert_data_append = new_vert_datum.append 475 for co, index, dist in kd.find_range(vert.co, radius): 476 multi = (radius - dist) / radius 477 near_vert_data_append((index, multi)) 478 if vert.index % progress_reduce == 0: 479 context.window_manager.progress_update(vert.index) 480 context.window_manager.progress_end() 481 482 target_vertex_groups = [] 483 if self.target == 'ACTIVE': 484 target_vertex_groups.append(ob.vertex_groups.active) 485 elif self.target == 'UP': 486 for vertex_group in ob.vertex_groups: 487 if vertex_group.index <= ob.vertex_groups.active_index: 488 target_vertex_groups.append(vertex_group) 489 elif self.target == 'DOWN': 490 for vertex_group in ob.vertex_groups: 491 if ob.vertex_groups.active_index <= vertex_group.index: 492 target_vertex_groups.append(vertex_group) 493 elif self.target == 'ALL': 494 for vertex_group in ob.vertex_groups: 495 target_vertex_groups.append(vertex_group) 496 497 progress_total = len(target_vertex_groups) * self.strength * len(me.vertices) 498 context.window_manager.progress_begin(0, progress_total) 499 progress_reduce = progress_total // 200 + 1 500 progress_count = 0 501 for strength_count in range(self.strength): 502 for vertex_group in target_vertex_groups: 503 504 weights = [] 505 weights_append = weights.append 506 for vert in me.vertices: 507 for elem in vert.groups: 508 if elem.group == vertex_group.index: 509 weights_append(elem.weight) 510 break 511 else: 512 weights_append(0.0) 513 514 for vert in me.vertices: 515 516 target_weight = weights[vert.index] 517 518 total_weight = 0.0 519 total_multi = 0.0 520 for index, multi in near_vert_data[vert.index]: 521 if self.effect == 'ADD': 522 if target_weight <= weights[index]: 523 total_weight += weights[index] * multi 524 total_multi += multi 525 elif self.effect == 'SUB': 526 if weights[index] <= target_weight: 527 total_weight += weights[index] * multi 528 total_multi += multi 529 else: 530 total_weight += weights[index] * multi 531 total_multi += multi 532 533 if 0 < total_multi: 534 average_weight = total_weight / total_multi 535 else: 536 average_weight = 0.0 537 538 if 0.001 < average_weight: 539 vertex_group.add([vert.index], average_weight, 'REPLACE') 540 else: 541 vertex_group.remove([vert.index]) 542 543 progress_count += 1 544 if progress_count % progress_reduce == 0: 545 context.window_manager.progress_update(progress_count) 546 547 if self.is_normalize: 548 549 other_weight_total = 0.0 550 for elem in vert.groups: 551 if elem.group != vertex_group.index: 552 other_weight_total += elem.weight 553 554 diff_weight = average_weight - target_weight 555 new_other_weight_total = other_weight_total - diff_weight 556 if 0 < other_weight_total: 557 other_weight_multi = new_other_weight_total / other_weight_total 558 else: 559 other_weight_multi = 0.0 560 561 for elem in vert.groups: 562 if elem.group != vertex_group.index: 563 vg = ob.vertex_groups[elem.group] 564 vg.add([vert.index], elem.weight * other_weight_multi, 'REPLACE') 565 566 context.window_manager.progress_end() 567 bpy.ops.object.mode_set(mode=pre_mode) 568 return {'FINISHED'} 569 570 571@compat.BlRegister() 572class CNV_OT_multiply_vertex_group(bpy.types.Operator): 573 bl_idname = 'object.multiply_vertex_group' 574 bl_label = "頂点グループに乗算" 575 bl_description = "頂点グループのウェイトに数値を乗算し、ウェイトの強度を増減させます" 576 bl_options = {'REGISTER', 'UNDO'} 577 578 items = [ 579 ('ACTIVE', "アクティブのみ", "", 'HAND', 1), 580 ('UP', "アクティブより上", "", 'TRIA_UP_BAR', 2), 581 ('DOWN', "アクティブより下", "", 'TRIA_DOWN_BAR', 3), 582 ('ALL', "全て", "", 'ARROW_LEFTRIGHT', 4), 583 ] 584 target = bpy.props.EnumProperty(items=items, name="対象", default='ACTIVE') 585 value = bpy.props.FloatProperty(name="倍率", default=1.1, min=0.1, max=10, soft_min=0.1, soft_max=10, step=10, precision=2) 586 is_normalize = bpy.props.BoolProperty(name="他頂点グループも調節", default=True) 587 588 @classmethod 589 def poll(cls, context): 590 ob = context.active_object 591 if ob: 592 if ob.type == 'MESH': 593 return ob.vertex_groups.active 594 return False 595 596 def invoke(self, context, event): 597 return context.window_manager.invoke_props_dialog(self) 598 599 def draw(self, context): 600 self.layout.prop(self, 'target', icon='VIEWZOOM') 601 self.layout.prop(self, 'value', icon='ARROW_LEFTRIGHT') 602 self.layout.prop(self, 'is_normalize', icon='GROUP') 603 604 def execute(self, context): 605 ob = context.active_object 606 me = ob.data 607 608 pre_mode = ob.mode 609 bpy.ops.object.mode_set(mode='OBJECT') 610 611 target_vertex_groups = [] 612 if self.target == 'ACTIVE': 613 target_vertex_groups.append(ob.vertex_groups.active) 614 elif self.target == 'UP': 615 for vertex_group in ob.vertex_groups: 616 if vertex_group.index <= ob.vertex_groups.active_index: 617 target_vertex_groups.append(vertex_group) 618 elif self.target == 'DOWN': 619 for vertex_group in ob.vertex_groups: 620 if ob.vertex_groups.active_index <= vertex_group.index: 621 target_vertex_groups.append(vertex_group) 622 elif self.target == 'ALL': 623 for vertex_group in ob.vertex_groups: 624 target_vertex_groups.append(vertex_group) 625 626 for vertex_group in target_vertex_groups: 627 for vert in me.vertices: 628 629 old_weight = -1 630 other_weight_total = 0.0 631 for elem in vert.groups: 632 if elem.group == vertex_group.index: 633 old_weight = elem.weight 634 else: 635 other_weight_total += elem.weight 636 if old_weight == -1: 637 continue 638 639 new_weight = old_weight * self.value 640 vertex_group.add([vert.index], new_weight, 'REPLACE') 641 642 if self.is_normalize: 643 644 diff_weight = new_weight - old_weight 645 646 new_other_weight_total = other_weight_total - diff_weight 647 if 0 < other_weight_total: 648 other_weight_multi = new_other_weight_total / other_weight_total 649 else: 650 other_weight_multi = 0.0 651 652 for elem in vert.groups: 653 if elem.group != vertex_group.index: 654 vg = ob.vertex_groups[elem.group] 655 vg.add([vert.index], elem.weight * other_weight_multi, 'REPLACE') 656 657 bpy.ops.object.mode_set(mode=pre_mode) 658 return {'FINISHED'} 659 660 661@compat.BlRegister() 662class CNV_OT_remove_noassign_vertex_groups(bpy.types.Operator): 663 bl_idname = 'object.remove_noassign_vertex_groups' 664 bl_label = "割り当てのない頂点グループを削除" 665 bl_description = "どの頂点にも割り当てられていない頂点グループを全て削除します" 666 bl_options = {'REGISTER', 'UNDO'} 667 668 threshold = bpy.props.FloatProperty(name="これ以下の影響は切り捨て", default=0.000001, min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, step=1, precision=10) 669 670 @classmethod 671 def poll(cls, context): 672 ob = context.active_object 673 if ob and ob.type == 'MESH': 674 return len(ob.vertex_groups) > 0 675 return False 676 677 def invoke(self, context, event): 678 return context.window_manager.invoke_props_dialog(self) 679 680 def draw(self, context): 681 self.layout.prop(self, 'threshold') 682 683 def execute(self, context): 684 ob = context.active_object 685 me = ob.data 686 687 is_keeps = [False for i in range(len(ob.vertex_groups))] 688 689 for vert in me.vertices: 690 for vge in vert.groups: 691 if not is_keeps[vge.group]: 692 if self.threshold < vge.weight: 693 is_keeps[vge.group] = True 694 695 copy_vertex_groups = ob.vertex_groups[:] 696 for i in range(len(copy_vertex_groups)): 697 if not is_keeps[i] and not copy_vertex_groups[i].lock_weight: 698 ob.vertex_groups.remove(copy_vertex_groups[i]) 699 700 return {'FINISHED'}
@compat.BlRegister()
class
CNV_OT_quick_transfer_vertex_group27@compat.BlRegister() 28class CNV_OT_quick_transfer_vertex_group(bpy.types.Operator): 29 bl_idname = 'object.quick_transfer_vertex_group' 30 bl_label = "クイック・ウェイト転送" 31 bl_description = "アクティブなメッシュに他の選択メッシュの頂点グループを高速で転送します" 32 bl_options = {'REGISTER', 'UNDO'} 33 34 is_remove_old_vertex_groups = bpy.props.BoolProperty(name="すでにある頂点グループを削除 (ロックで保護)", default=False) 35 is_source_select_vert_only = bpy.props.BoolProperty(name="選択頂点のみ(参照)", default=False) 36 is_target_select_vert_only = bpy.props.BoolProperty(name="選択頂点のみ(対象)", default=False) 37 items = [ 38 ('NEAREST', "最も近い頂点", "", 'VERTEXSEL', 1), 39 ('EDGEINTERP_NEAREST', "最も近い辺", "", 'EDGESEL', 2), 40 ('POLYINTERP_NEAREST', "最も近い面", "", 'FACESEL', 3), 41 ('POLYINTERP_VNORPROJ', "投影先", "", 'MOD_UVPROJECT', 4), 42 ] 43 vert_mapping = bpy.props.EnumProperty(items=items, name="参照要素", default='POLYINTERP_NEAREST') 44 is_clean = bpy.props.BoolProperty(name="転送後にクリーンを実行", default=True) 45 is_remove_noassign = bpy.props.BoolProperty(name="転送後に割り当てのない頂点グループを削除", default=True) 46 47 @classmethod 48 def poll(cls, context): 49 obs = context.selected_objects 50 if len(obs) < 2: 51 return False 52 53 active_ob = context.active_object 54 for ob in obs: 55 if ob.type != 'MESH': 56 return False 57 if ob.name != active_ob.name: 58 if not len(ob.vertex_groups): 59 return False 60 return True 61 62 def invoke(self, context, event): 63 return context.window_manager.invoke_props_dialog(self) 64 65 def draw(self, context): 66 self.layout.prop(self, 'is_remove_old_vertex_groups', icon='ERROR') 67 68 row = self.layout.row(align=True) 69 row.prop(self, 'is_source_select_vert_only', icon='UV_SYNC_SELECT') 70 row.prop(self, 'is_target_select_vert_only', icon='UV_SYNC_SELECT') 71 72 self.layout.prop(self, 'vert_mapping') 73 self.layout.prop(self, 'is_clean', icon='DISCLOSURE_TRI_DOWN') 74 self.layout.prop(self, 'is_remove_noassign', icon='X') 75 76 def execute(self, context): 77 class MyVertexGroupElement: 78 pass 79 80 target_ob = context.active_object 81 target_me = target_ob.data 82 83 pre_mode = target_ob.mode 84 bpy.ops.object.mode_set(mode='OBJECT') 85 86 original_source_obs = [] 87 for ob in context.selected_objects: 88 if ob.name != target_ob.name: 89 original_source_obs.append(ob) 90 91 compat.set_select(target_ob, False) 92 compat.set_active(context, original_source_obs[0]) 93 bpy.ops.object.duplicate(linked=False, mode='TRANSLATION') 94 if len(context.selected_objects) > 1: 95 bpy.ops.object.join() 96 join_source_ob = context.selected_objects[0] 97 join_source_me = join_source_ob.data 98 99 temp_source_ob = join_source_ob.copy() 100 temp_source_me = join_source_me.copy() 101 temp_source_ob.data = temp_source_me 102 compat.link(context.scene, temp_source_ob) 103 compat.set_select(temp_source_ob, True) 104 compat.set_select(join_source_ob, False) 105 compat.set_active(context, temp_source_ob) 106 if self.is_source_select_vert_only: 107 bpy.ops.object.mode_set(mode='EDIT') 108 bpy.ops.mesh.select_all(action='INVERT') 109 bpy.ops.mesh.delete(type='VERT') 110 bpy.ops.object.mode_set(mode='OBJECT') 111 112 compat.set_select(target_ob, True) 113 compat.set_active(context, target_ob) 114 115 if self.vert_mapping == 'POLYINTERP_VNORPROJ' and len(temp_source_me.polygons) == 0: 116 self.vert_mapping = 'EDGEINTERP_NEAREST' 117 self.report(type={'WARNING'}, message="面がひとつも存在しません、辺モードに変更します") 118 if self.vert_mapping == 'POLYINTERP_NEAREST' and len(temp_source_me.polygons) == 0: 119 self.vert_mapping = 'EDGEINTERP_NEAREST' 120 self.report(type={'WARNING'}, message="面がひとつも存在しません、辺モードに変更します") 121 if self.vert_mapping == 'EDGEINTERP_NEAREST' and len(temp_source_me.edges) == 0: 122 self.vert_mapping = 'NEAREST' 123 self.report(type={'WARNING'}, message="辺がひとつも存在しません、頂点モードに変更します") 124 if self.vert_mapping == 'NEAREST' and len(temp_source_me.vertices) == 0: 125 self.report(type={'ERROR'}, message="頂点がひとつも存在しません、中止します") 126 return {'CANCELLED'} 127 128 if self.is_remove_old_vertex_groups: 129 for vg in target_ob.vertex_groups[:]: 130 if not vg.lock_weight: 131 target_ob.vertex_groups.remove(vg) 132 133 old_vertex_groups = [] 134 for vert in target_me.vertices: 135 mvges = [] 136 for vge in vert.groups: 137 mvge = MyVertexGroupElement() 138 mvge.vertex_group = target_ob.vertex_groups[vge.group] 139 mvge.weight = vge.weight 140 mvges.append(mvge) 141 old_vertex_groups.append(mvges) 142 143 if self.is_remove_noassign: 144 pre_vertex_group_names = [vg.name for vg in target_ob.vertex_groups] 145 146 bpy.ops.object.data_transfer(use_reverse_transfer=True, use_freeze=False, data_type='VGROUP_WEIGHTS', use_create=True, vert_mapping=self.vert_mapping, use_auto_transform=False, use_object_transform=True, use_max_distance=False, ray_radius=0, layers_select_src='NAME', layers_select_dst='ALL', mix_mode='REPLACE', mix_factor=1) 147 if self.is_clean: 148 bpy.ops.object.vertex_group_clean(group_select_mode='ALL', limit=0.00000000001) 149 150 bpy.ops.object.mode_set(mode='EDIT') 151 bpy.ops.object.mode_set(mode='OBJECT') 152 153 if self.is_remove_noassign: 154 is_keeps = [False for i in range(len(target_ob.vertex_groups))] 155 for vert in target_me.vertices: 156 for vge in vert.groups: 157 if not is_keeps[vge.group]: 158 if 0.000001 < vge.weight: 159 is_keeps[vge.group] = True 160 copy_vertex_groups = target_ob.vertex_groups[:] 161 for i in range(len(copy_vertex_groups)): 162 if not is_keeps[i] and not copy_vertex_groups[i].lock_weight: 163 if copy_vertex_groups[i].name not in pre_vertex_group_names: 164 target_ob.vertex_groups.remove(copy_vertex_groups[i]) 165 166 if self.is_target_select_vert_only: 167 for vert in target_me.vertices: 168 if not vert.select: 169 for vg in target_ob.vertex_groups: 170 vg.remove([vert.index]) 171 for mvge in old_vertex_groups[vert.index]: 172 mvge.vertex_group.add([vert.index], mvge.weight, 'REPLACE') 173 174 for vert in target_me.vertices: 175 for vg in target_ob.vertex_groups: 176 if vg.lock_weight: 177 vg.remove([vert.index]) 178 for mvge in old_vertex_groups[vert.index]: 179 if mvge.vertex_group.lock_weight: 180 mvge.vertex_group.add([vert.index], mvge.weight, 'REPLACE') 181 182 common.remove_data([temp_source_ob, temp_source_me]) 183 compat.set_select(join_source_ob, True) 184 185 common.remove_data([join_source_ob, join_source_me]) 186 for ob in original_source_obs: 187 compat.set_select(ob, True) 188 189 compat.set_active(context, target_ob) 190 bpy.ops.object.mode_set(mode=pre_mode) 191 return {'FINISHED'}
is_remove_old_vertex_groups: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'すでにある頂点グループを削除 (ロックで保護)', 'default': False, 'attr': 'is_remove_old_vertex_groups'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': 'すでにある頂点グループを削除 (ロックで保護)', 'default': False, 'attr': 'is_remove_old_vertex_groups'}>
is_source_select_vert_only: <_PropertyDeferred, <built-in function BoolProperty>, {'name': '選択頂点のみ(参照)', 'default': False, 'attr': 'is_source_select_vert_only'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': '選択頂点のみ(参照)', 'default': False, 'attr': 'is_source_select_vert_only'}>
is_target_select_vert_only: <_PropertyDeferred, <built-in function BoolProperty>, {'name': '選択頂点のみ(対象)', 'default': False, 'attr': 'is_target_select_vert_only'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': '選択頂点のみ(対象)', 'default': False, 'attr': 'is_target_select_vert_only'}>
items =
[('NEAREST', '最も近い頂点', '', 'VERTEXSEL', 1), ('EDGEINTERP_NEAREST', '最も近い辺', '', 'EDGESEL', 2), ('POLYINTERP_NEAREST', '最も近い面', '', 'FACESEL', 3), ('POLYINTERP_VNORPROJ', '投影先', '', 'MOD_UVPROJECT', 4)]
vert_mapping: <_PropertyDeferred, <built-in function EnumProperty>, {'items': [('NEAREST', '最も近い頂点', '', 'VERTEXSEL', 1), ('EDGEINTERP_NEAREST', '最も近い辺', '', 'EDGESEL', 2), ('POLYINTERP_NEAREST', '最も近い面', '', 'FACESEL', 3), ('POLYINTERP_VNORPROJ', '投影先', '', 'MOD_UVPROJECT', 4)], 'name': '参照要素', 'default': 'POLYINTERP_NEAREST', 'attr': 'vert_mapping'}> =
<_PropertyDeferred, <built-in function EnumProperty>, {'items': [('NEAREST', '最も近い頂点', '', 'VERTEXSEL', 1), ('EDGEINTERP_NEAREST', '最も近い辺', '', 'EDGESEL', 2), ('POLYINTERP_NEAREST', '最も近い面', '', 'FACESEL', 3), ('POLYINTERP_VNORPROJ', '投影先', '', 'MOD_UVPROJECT', 4)], 'name': '参照要素', 'default': 'POLYINTERP_NEAREST', 'attr': 'vert_mapping'}>
is_clean: <_PropertyDeferred, <built-in function BoolProperty>, {'name': '転送後にクリーンを実行', 'default': True, 'attr': 'is_clean'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': '転送後にクリーンを実行', 'default': True, 'attr': 'is_clean'}>
is_remove_noassign: <_PropertyDeferred, <built-in function BoolProperty>, {'name': '転送後に割り当てのない頂点グループを削除', 'default': True, 'attr': 'is_remove_noassign'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': '転送後に割り当てのない頂点グループを削除', 'default': True, 'attr': 'is_remove_noassign'}>
@classmethod
def
poll(cls, context):
47 @classmethod 48 def poll(cls, context): 49 obs = context.selected_objects 50 if len(obs) < 2: 51 return False 52 53 active_ob = context.active_object 54 for ob in obs: 55 if ob.type != 'MESH': 56 return False 57 if ob.name != active_ob.name: 58 if not len(ob.vertex_groups): 59 return False 60 return True
def
draw(self, context):
65 def draw(self, context): 66 self.layout.prop(self, 'is_remove_old_vertex_groups', icon='ERROR') 67 68 row = self.layout.row(align=True) 69 row.prop(self, 'is_source_select_vert_only', icon='UV_SYNC_SELECT') 70 row.prop(self, 'is_target_select_vert_only', icon='UV_SYNC_SELECT') 71 72 self.layout.prop(self, 'vert_mapping') 73 self.layout.prop(self, 'is_clean', icon='DISCLOSURE_TRI_DOWN') 74 self.layout.prop(self, 'is_remove_noassign', icon='X')
def
execute(self, context):
76 def execute(self, context): 77 class MyVertexGroupElement: 78 pass 79 80 target_ob = context.active_object 81 target_me = target_ob.data 82 83 pre_mode = target_ob.mode 84 bpy.ops.object.mode_set(mode='OBJECT') 85 86 original_source_obs = [] 87 for ob in context.selected_objects: 88 if ob.name != target_ob.name: 89 original_source_obs.append(ob) 90 91 compat.set_select(target_ob, False) 92 compat.set_active(context, original_source_obs[0]) 93 bpy.ops.object.duplicate(linked=False, mode='TRANSLATION') 94 if len(context.selected_objects) > 1: 95 bpy.ops.object.join() 96 join_source_ob = context.selected_objects[0] 97 join_source_me = join_source_ob.data 98 99 temp_source_ob = join_source_ob.copy() 100 temp_source_me = join_source_me.copy() 101 temp_source_ob.data = temp_source_me 102 compat.link(context.scene, temp_source_ob) 103 compat.set_select(temp_source_ob, True) 104 compat.set_select(join_source_ob, False) 105 compat.set_active(context, temp_source_ob) 106 if self.is_source_select_vert_only: 107 bpy.ops.object.mode_set(mode='EDIT') 108 bpy.ops.mesh.select_all(action='INVERT') 109 bpy.ops.mesh.delete(type='VERT') 110 bpy.ops.object.mode_set(mode='OBJECT') 111 112 compat.set_select(target_ob, True) 113 compat.set_active(context, target_ob) 114 115 if self.vert_mapping == 'POLYINTERP_VNORPROJ' and len(temp_source_me.polygons) == 0: 116 self.vert_mapping = 'EDGEINTERP_NEAREST' 117 self.report(type={'WARNING'}, message="面がひとつも存在しません、辺モードに変更します") 118 if self.vert_mapping == 'POLYINTERP_NEAREST' and len(temp_source_me.polygons) == 0: 119 self.vert_mapping = 'EDGEINTERP_NEAREST' 120 self.report(type={'WARNING'}, message="面がひとつも存在しません、辺モードに変更します") 121 if self.vert_mapping == 'EDGEINTERP_NEAREST' and len(temp_source_me.edges) == 0: 122 self.vert_mapping = 'NEAREST' 123 self.report(type={'WARNING'}, message="辺がひとつも存在しません、頂点モードに変更します") 124 if self.vert_mapping == 'NEAREST' and len(temp_source_me.vertices) == 0: 125 self.report(type={'ERROR'}, message="頂点がひとつも存在しません、中止します") 126 return {'CANCELLED'} 127 128 if self.is_remove_old_vertex_groups: 129 for vg in target_ob.vertex_groups[:]: 130 if not vg.lock_weight: 131 target_ob.vertex_groups.remove(vg) 132 133 old_vertex_groups = [] 134 for vert in target_me.vertices: 135 mvges = [] 136 for vge in vert.groups: 137 mvge = MyVertexGroupElement() 138 mvge.vertex_group = target_ob.vertex_groups[vge.group] 139 mvge.weight = vge.weight 140 mvges.append(mvge) 141 old_vertex_groups.append(mvges) 142 143 if self.is_remove_noassign: 144 pre_vertex_group_names = [vg.name for vg in target_ob.vertex_groups] 145 146 bpy.ops.object.data_transfer(use_reverse_transfer=True, use_freeze=False, data_type='VGROUP_WEIGHTS', use_create=True, vert_mapping=self.vert_mapping, use_auto_transform=False, use_object_transform=True, use_max_distance=False, ray_radius=0, layers_select_src='NAME', layers_select_dst='ALL', mix_mode='REPLACE', mix_factor=1) 147 if self.is_clean: 148 bpy.ops.object.vertex_group_clean(group_select_mode='ALL', limit=0.00000000001) 149 150 bpy.ops.object.mode_set(mode='EDIT') 151 bpy.ops.object.mode_set(mode='OBJECT') 152 153 if self.is_remove_noassign: 154 is_keeps = [False for i in range(len(target_ob.vertex_groups))] 155 for vert in target_me.vertices: 156 for vge in vert.groups: 157 if not is_keeps[vge.group]: 158 if 0.000001 < vge.weight: 159 is_keeps[vge.group] = True 160 copy_vertex_groups = target_ob.vertex_groups[:] 161 for i in range(len(copy_vertex_groups)): 162 if not is_keeps[i] and not copy_vertex_groups[i].lock_weight: 163 if copy_vertex_groups[i].name not in pre_vertex_group_names: 164 target_ob.vertex_groups.remove(copy_vertex_groups[i]) 165 166 if self.is_target_select_vert_only: 167 for vert in target_me.vertices: 168 if not vert.select: 169 for vg in target_ob.vertex_groups: 170 vg.remove([vert.index]) 171 for mvge in old_vertex_groups[vert.index]: 172 mvge.vertex_group.add([vert.index], mvge.weight, 'REPLACE') 173 174 for vert in target_me.vertices: 175 for vg in target_ob.vertex_groups: 176 if vg.lock_weight: 177 vg.remove([vert.index]) 178 for mvge in old_vertex_groups[vert.index]: 179 if mvge.vertex_group.lock_weight: 180 mvge.vertex_group.add([vert.index], mvge.weight, 'REPLACE') 181 182 common.remove_data([temp_source_ob, temp_source_me]) 183 compat.set_select(join_source_ob, True) 184 185 common.remove_data([join_source_ob, join_source_me]) 186 for ob in original_source_obs: 187 compat.set_select(ob, True) 188 189 compat.set_active(context, target_ob) 190 bpy.ops.object.mode_set(mode=pre_mode) 191 return {'FINISHED'}
Inherited Members
- bpy_types.Operator
- as_keywords
- poll_message_set
- builtins.bpy_struct
- keys
- values
- get
- pop
- as_pointer
- keyframe_insert
- keyframe_delete
- driver_add
- driver_remove
- is_property_set
- property_unset
- is_property_readonly
- is_property_overridable_library
- property_overridable_library_set
- path_resolve
- path_from_id
- type_recast
- bl_rna_get_subclass_py
- bl_rna_get_subclass
- id_properties_ensure
- id_properties_clear
- id_properties_ui
- id_data
@compat.BlRegister()
class
CNV_OT_precision_transfer_vertex_group194@compat.BlRegister() 195class CNV_OT_precision_transfer_vertex_group(bpy.types.Operator): 196 bl_idname = 'object.precision_transfer_vertex_group' 197 bl_label = "空間ぼかし・ウェイト転送" 198 bl_description = "アクティブなメッシュに他の選択メッシュの頂点グループを遠いほどぼかして転送します" 199 bl_options = {'REGISTER', 'UNDO'} 200 201 is_first_remove_all = bpy.props.BoolProperty(name="すでにある頂点グループを削除 (ロックで保護)", default=False) 202 subdivide_number = bpy.props.IntProperty(name="参照元の分割", default=1, min=0, max=10, soft_min=0, soft_max=10) 203 extend_range = bpy.props.FloatProperty(name="範囲倍率", default=1.1, min=1.0001, max=5.0, soft_min=1.0001, soft_max=5.0, step=10, precision=2) 204 is_remove_empty = bpy.props.BoolProperty(name="割り当てのない頂点グループを削除", default=True) 205 206 @classmethod 207 def poll(cls, context): 208 active_ob = context.active_object 209 obs = context.selected_objects 210 if len(obs) != 2: 211 return False 212 213 for ob in obs: 214 if ob.type != 'MESH': 215 return False 216 if ob.name != active_ob.name: 217 if len(ob.vertex_groups): 218 return True 219 return False 220 221 def invoke(self, context, event): 222 return context.window_manager.invoke_props_dialog(self) 223 224 def draw(self, context): 225 self.layout.prop(self, 'is_first_remove_all', icon='ERROR') 226 self.layout.prop(self, 'subdivide_number', icon='LATTICE_DATA') 227 self.layout.prop(self, 'extend_range', icon='PROP_ON') 228 self.layout.prop(self, 'is_remove_empty', icon='X') 229 230 def execute(self, context): 231 start_time = time.time() 232 233 target_ob = context.active_object 234 target_me = target_ob.data 235 236 pre_mode = target_ob.mode 237 bpy.ops.object.mode_set(mode='OBJECT') 238 239 for ob in context.selected_objects: 240 if ob.name != target_ob.name: 241 source_original_ob = ob 242 break 243 source_ob = source_original_ob.copy() 244 source_me = source_original_ob.data.copy() 245 source_ob.data = source_me 246 compat.link(context.scene, source_ob) 247 248 compat.set_select(target_ob, False) 249 compat.set_select(source_original_ob, False) 250 compat.set_active(context, source_ob) 251 try: 252 bpy.ops.object.mode_set(mode='EDIT') 253 bpy.ops.mesh.reveal() 254 bpy.ops.mesh.select_all(action='SELECT') 255 bpy.ops.mesh.subdivide(number_cuts=self.subdivide_number, smoothness=0.0, quadcorner='STRAIGHT_CUT', fractal=0.0, fractal_along_normal=0.0, seed=0) 256 bpy.ops.object.mode_set(mode='OBJECT') 257 258 if self.is_first_remove_all: 259 for vg in target_ob.vertex_groups[:]: 260 if not vg.lock_weight: 261 target_ob.vertex_groups.remove(vg) 262 263 kd = mathutils.kdtree.KDTree(len(source_me.vertices)) 264 for vert in source_me.vertices: 265 co = compat.mul(source_ob.matrix_world, vert.co) 266 kd.insert(co, vert.index) 267 kd.balance() 268 269 context.window_manager.progress_begin(0, len(target_me.vertices)) 270 progress_reduce = len(target_me.vertices) // 200 + 1 271 near_vert_data = [] 272 near_vert_multi_total = [] 273 near_vert_multi_total_append = near_vert_multi_total.append 274 for vert in target_me.vertices: 275 near_vert_data.append([]) 276 near_vert_data_append = near_vert_data[-1].append 277 278 target_co = compat.mul(target_ob.matrix_world, vert.co) 279 280 mini_co, mini_index, mini_dist = kd.find(target_co) 281 radius = mini_dist * self.extend_range 282 diff_radius = radius - mini_dist 283 284 multi_total = 0.0 285 for co, index, dist in kd.find_range(target_co, radius): 286 if 0 < diff_radius: 287 multi = (diff_radius - (dist - mini_dist)) / diff_radius 288 else: 289 multi = 1.0 290 near_vert_data_append((index, multi)) 291 multi_total += multi 292 near_vert_multi_total_append(multi_total) 293 294 if vert.index % progress_reduce == 0: 295 context.window_manager.progress_update(vert.index) 296 context.window_manager.progress_end() 297 298 context.window_manager.progress_begin(0, len(source_ob.vertex_groups)) 299 for source_vertex_group in source_ob.vertex_groups: 300 301 if source_vertex_group.name in target_ob.vertex_groups: 302 target_vertex_group = target_ob.vertex_groups[source_vertex_group.name] 303 else: 304 target_vertex_group = target_ob.vertex_groups.new(name=source_vertex_group.name) 305 306 is_waighted = False 307 308 source_weights = [] 309 source_weights_append = source_weights.append 310 for source_vert in source_me.vertices: 311 for elem in source_vert.groups: 312 if elem.group == source_vertex_group.index: 313 source_weights_append(elem.weight) 314 break 315 else: 316 source_weights_append(0.0) 317 318 for target_vert in target_me.vertices: 319 320 if 0 < near_vert_multi_total[target_vert.index]: 321 322 total_weight = [source_weights[i] * m for i, m in near_vert_data[target_vert.index]] 323 total_weight = sum(total_weight) 324 325 average_weight = total_weight / near_vert_multi_total[target_vert.index] 326 else: 327 average_weight = 0.0 328 329 if 0.000001 < average_weight: 330 target_vertex_group.add([target_vert.index], average_weight, 'REPLACE') 331 is_waighted = True 332 else: 333 if not self.is_first_remove_all: 334 target_vertex_group.remove([target_vert.index]) 335 336 context.window_manager.progress_update(source_vertex_group.index) 337 338 if not is_waighted and self.is_remove_empty: 339 target_ob.vertex_groups.remove(target_vertex_group) 340 context.window_manager.progress_end() 341 342 target_ob.vertex_groups.active_index = 0 343 finally: 344 common.remove_data([source_ob, source_me]) 345 compat.set_select(source_original_ob, True) 346 compat.set_select(target_ob, True) 347 compat.set_active(context, target_ob) 348 bpy.ops.object.mode_set(mode=pre_mode) 349 350 diff_time = time.time() - start_time 351 self.report(type={'INFO'}, message=f_tip_("{:.2f} Seconds", diff_time)) 352 return {'FINISHED'}
is_first_remove_all: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'すでにある頂点グループを削除 (ロックで保護)', 'default': False, 'attr': 'is_first_remove_all'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': 'すでにある頂点グループを削除 (ロックで保護)', 'default': False, 'attr': 'is_first_remove_all'}>
subdivide_number: <_PropertyDeferred, <built-in function IntProperty>, {'name': '参照元の分割', 'default': 1, 'min': 0, 'max': 10, 'soft_min': 0, 'soft_max': 10, 'attr': 'subdivide_number'}> =
<_PropertyDeferred, <built-in function IntProperty>, {'name': '参照元の分割', 'default': 1, 'min': 0, 'max': 10, 'soft_min': 0, 'soft_max': 10, 'attr': 'subdivide_number'}>
extend_range: <_PropertyDeferred, <built-in function FloatProperty>, {'name': '範囲倍率', 'default': 1.1, 'min': 1.0001, 'max': 5.0, 'soft_min': 1.0001, 'soft_max': 5.0, 'step': 10, 'precision': 2, 'attr': 'extend_range'}> =
<_PropertyDeferred, <built-in function FloatProperty>, {'name': '範囲倍率', 'default': 1.1, 'min': 1.0001, 'max': 5.0, 'soft_min': 1.0001, 'soft_max': 5.0, 'step': 10, 'precision': 2, 'attr': 'extend_range'}>
is_remove_empty: <_PropertyDeferred, <built-in function BoolProperty>, {'name': '割り当てのない頂点グループを削除', 'default': True, 'attr': 'is_remove_empty'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': '割り当てのない頂点グループを削除', 'default': True, 'attr': 'is_remove_empty'}>
@classmethod
def
poll(cls, context):
206 @classmethod 207 def poll(cls, context): 208 active_ob = context.active_object 209 obs = context.selected_objects 210 if len(obs) != 2: 211 return False 212 213 for ob in obs: 214 if ob.type != 'MESH': 215 return False 216 if ob.name != active_ob.name: 217 if len(ob.vertex_groups): 218 return True 219 return False
def
execute(self, context):
230 def execute(self, context): 231 start_time = time.time() 232 233 target_ob = context.active_object 234 target_me = target_ob.data 235 236 pre_mode = target_ob.mode 237 bpy.ops.object.mode_set(mode='OBJECT') 238 239 for ob in context.selected_objects: 240 if ob.name != target_ob.name: 241 source_original_ob = ob 242 break 243 source_ob = source_original_ob.copy() 244 source_me = source_original_ob.data.copy() 245 source_ob.data = source_me 246 compat.link(context.scene, source_ob) 247 248 compat.set_select(target_ob, False) 249 compat.set_select(source_original_ob, False) 250 compat.set_active(context, source_ob) 251 try: 252 bpy.ops.object.mode_set(mode='EDIT') 253 bpy.ops.mesh.reveal() 254 bpy.ops.mesh.select_all(action='SELECT') 255 bpy.ops.mesh.subdivide(number_cuts=self.subdivide_number, smoothness=0.0, quadcorner='STRAIGHT_CUT', fractal=0.0, fractal_along_normal=0.0, seed=0) 256 bpy.ops.object.mode_set(mode='OBJECT') 257 258 if self.is_first_remove_all: 259 for vg in target_ob.vertex_groups[:]: 260 if not vg.lock_weight: 261 target_ob.vertex_groups.remove(vg) 262 263 kd = mathutils.kdtree.KDTree(len(source_me.vertices)) 264 for vert in source_me.vertices: 265 co = compat.mul(source_ob.matrix_world, vert.co) 266 kd.insert(co, vert.index) 267 kd.balance() 268 269 context.window_manager.progress_begin(0, len(target_me.vertices)) 270 progress_reduce = len(target_me.vertices) // 200 + 1 271 near_vert_data = [] 272 near_vert_multi_total = [] 273 near_vert_multi_total_append = near_vert_multi_total.append 274 for vert in target_me.vertices: 275 near_vert_data.append([]) 276 near_vert_data_append = near_vert_data[-1].append 277 278 target_co = compat.mul(target_ob.matrix_world, vert.co) 279 280 mini_co, mini_index, mini_dist = kd.find(target_co) 281 radius = mini_dist * self.extend_range 282 diff_radius = radius - mini_dist 283 284 multi_total = 0.0 285 for co, index, dist in kd.find_range(target_co, radius): 286 if 0 < diff_radius: 287 multi = (diff_radius - (dist - mini_dist)) / diff_radius 288 else: 289 multi = 1.0 290 near_vert_data_append((index, multi)) 291 multi_total += multi 292 near_vert_multi_total_append(multi_total) 293 294 if vert.index % progress_reduce == 0: 295 context.window_manager.progress_update(vert.index) 296 context.window_manager.progress_end() 297 298 context.window_manager.progress_begin(0, len(source_ob.vertex_groups)) 299 for source_vertex_group in source_ob.vertex_groups: 300 301 if source_vertex_group.name in target_ob.vertex_groups: 302 target_vertex_group = target_ob.vertex_groups[source_vertex_group.name] 303 else: 304 target_vertex_group = target_ob.vertex_groups.new(name=source_vertex_group.name) 305 306 is_waighted = False 307 308 source_weights = [] 309 source_weights_append = source_weights.append 310 for source_vert in source_me.vertices: 311 for elem in source_vert.groups: 312 if elem.group == source_vertex_group.index: 313 source_weights_append(elem.weight) 314 break 315 else: 316 source_weights_append(0.0) 317 318 for target_vert in target_me.vertices: 319 320 if 0 < near_vert_multi_total[target_vert.index]: 321 322 total_weight = [source_weights[i] * m for i, m in near_vert_data[target_vert.index]] 323 total_weight = sum(total_weight) 324 325 average_weight = total_weight / near_vert_multi_total[target_vert.index] 326 else: 327 average_weight = 0.0 328 329 if 0.000001 < average_weight: 330 target_vertex_group.add([target_vert.index], average_weight, 'REPLACE') 331 is_waighted = True 332 else: 333 if not self.is_first_remove_all: 334 target_vertex_group.remove([target_vert.index]) 335 336 context.window_manager.progress_update(source_vertex_group.index) 337 338 if not is_waighted and self.is_remove_empty: 339 target_ob.vertex_groups.remove(target_vertex_group) 340 context.window_manager.progress_end() 341 342 target_ob.vertex_groups.active_index = 0 343 finally: 344 common.remove_data([source_ob, source_me]) 345 compat.set_select(source_original_ob, True) 346 compat.set_select(target_ob, True) 347 compat.set_active(context, target_ob) 348 bpy.ops.object.mode_set(mode=pre_mode) 349 350 diff_time = time.time() - start_time 351 self.report(type={'INFO'}, message=f_tip_("{:.2f} Seconds", diff_time)) 352 return {'FINISHED'}
Inherited Members
- bpy_types.Operator
- as_keywords
- poll_message_set
- builtins.bpy_struct
- keys
- values
- items
- get
- pop
- as_pointer
- keyframe_insert
- keyframe_delete
- driver_add
- driver_remove
- is_property_set
- property_unset
- is_property_readonly
- is_property_overridable_library
- property_overridable_library_set
- path_resolve
- path_from_id
- type_recast
- bl_rna_get_subclass_py
- bl_rna_get_subclass
- id_properties_ensure
- id_properties_clear
- id_properties_ui
- id_data
@compat.BlRegister()
class
CNV_OT_quick_blur_vertex_group355@compat.BlRegister() 356class CNV_OT_quick_blur_vertex_group(bpy.types.Operator): 357 bl_idname = 'object.quick_blur_vertex_group' 358 bl_label = "頂点グループぼかし" 359 bl_description = "アクティブ、もしくは全ての頂点グループをぼかします" 360 bl_options = {'REGISTER', 'UNDO'} 361 362 items = [ 363 ('ACTIVE', "アクティブのみ", "", 'HAND', 1), 364 ('ALL', "全て", "", 'ARROW_LEFTRIGHT', 2), 365 ] 366 target = bpy.props.EnumProperty(items=items, name="対象", default='ALL') 367 strength = bpy.props.FloatProperty(name="強さ", default=1.0, min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, step=10, precision=3) 368 count = bpy.props.IntProperty(name="反復", default=1, min=1, max=256, soft_min=1, soft_max=256) 369 size = bpy.props.FloatProperty(name="拡大縮小", default=0.0, min=-1.0, max=1.0, soft_min=-1.0, soft_max=1.0, step=10, precision=3) 370 371 @classmethod 372 def poll(cls, context): 373 ob = context.active_object 374 if ob and ob.type == 'MESH': 375 return ob.vertex_groups.active 376 return False 377 378 def invoke(self, context, event): 379 return context.window_manager.invoke_props_dialog(self) 380 381 def draw(self, context): 382 self.layout.prop(self, 'target', icon='VIEWZOOM') 383 self.layout.prop(self, 'strength') 384 self.layout.prop(self, 'count') 385 self.layout.prop(self, 'size') 386 387 def execute(self, context): 388 target_ob = context.active_object 389 target_me = target_ob.data 390 391 pre_mode = target_ob.mode 392 bpy.ops.object.mode_set(mode='WEIGHT_PAINT') 393 394 pre_use_paint_mask_vertex = target_me.use_paint_mask_vertex 395 target_me.use_paint_mask_vertex = True 396 397 bpy.ops.paint.vert_select_all(action='SELECT') 398 # source='ALL'は2.8で削除された 399 bpy.ops.object.vertex_group_smooth(group_select_mode=self.target, factor=self.strength, repeat=self.count, expand=self.size) 400 401 target_me.use_paint_mask_vertex = pre_use_paint_mask_vertex 402 403 bpy.ops.object.mode_set(mode=pre_mode) 404 return {'FINISHED'}
target: <_PropertyDeferred, <built-in function EnumProperty>, {'items': [('ACTIVE', 'アクティブのみ', '', 'HAND', 1), ('ALL', '全て', '', 'ARROW_LEFTRIGHT', 2)], 'name': '対象', 'default': 'ALL', 'attr': 'target'}> =
<_PropertyDeferred, <built-in function EnumProperty>, {'items': [('ACTIVE', 'アクティブのみ', '', 'HAND', 1), ('ALL', '全て', '', 'ARROW_LEFTRIGHT', 2)], 'name': '対象', 'default': 'ALL', 'attr': 'target'}>
strength: <_PropertyDeferred, <built-in function FloatProperty>, {'name': '強さ', 'default': 1.0, 'min': 0.0, 'max': 1.0, 'soft_min': 0.0, 'soft_max': 1.0, 'step': 10, 'precision': 3, 'attr': 'strength'}> =
<_PropertyDeferred, <built-in function FloatProperty>, {'name': '強さ', 'default': 1.0, 'min': 0.0, 'max': 1.0, 'soft_min': 0.0, 'soft_max': 1.0, 'step': 10, 'precision': 3, 'attr': 'strength'}>
count: <_PropertyDeferred, <built-in function IntProperty>, {'name': '反復', 'default': 1, 'min': 1, 'max': 256, 'soft_min': 1, 'soft_max': 256, 'attr': 'count'}> =
<_PropertyDeferred, <built-in function IntProperty>, {'name': '反復', 'default': 1, 'min': 1, 'max': 256, 'soft_min': 1, 'soft_max': 256, 'attr': 'count'}>
size: <_PropertyDeferred, <built-in function FloatProperty>, {'name': '拡大縮小', 'default': 0.0, 'min': -1.0, 'max': 1.0, 'soft_min': -1.0, 'soft_max': 1.0, 'step': 10, 'precision': 3, 'attr': 'size'}> =
<_PropertyDeferred, <built-in function FloatProperty>, {'name': '拡大縮小', 'default': 0.0, 'min': -1.0, 'max': 1.0, 'soft_min': -1.0, 'soft_max': 1.0, 'step': 10, 'precision': 3, 'attr': 'size'}>
def
execute(self, context):
387 def execute(self, context): 388 target_ob = context.active_object 389 target_me = target_ob.data 390 391 pre_mode = target_ob.mode 392 bpy.ops.object.mode_set(mode='WEIGHT_PAINT') 393 394 pre_use_paint_mask_vertex = target_me.use_paint_mask_vertex 395 target_me.use_paint_mask_vertex = True 396 397 bpy.ops.paint.vert_select_all(action='SELECT') 398 # source='ALL'は2.8で削除された 399 bpy.ops.object.vertex_group_smooth(group_select_mode=self.target, factor=self.strength, repeat=self.count, expand=self.size) 400 401 target_me.use_paint_mask_vertex = pre_use_paint_mask_vertex 402 403 bpy.ops.object.mode_set(mode=pre_mode) 404 return {'FINISHED'}
Inherited Members
- bpy_types.Operator
- as_keywords
- poll_message_set
- builtins.bpy_struct
- keys
- values
- get
- pop
- as_pointer
- keyframe_insert
- keyframe_delete
- driver_add
- driver_remove
- is_property_set
- property_unset
- is_property_readonly
- is_property_overridable_library
- property_overridable_library_set
- path_resolve
- path_from_id
- type_recast
- bl_rna_get_subclass_py
- bl_rna_get_subclass
- id_properties_ensure
- id_properties_clear
- id_properties_ui
- id_data
@compat.BlRegister()
class
CNV_OT_blur_vertex_group407@compat.BlRegister() 408class CNV_OT_blur_vertex_group(bpy.types.Operator): 409 bl_idname = 'object.blur_vertex_group' 410 bl_label = "旧・頂点グループぼかし" 411 bl_description = "アクティブ、もしくは全ての頂点グループをぼかします" 412 bl_options = {'REGISTER', 'UNDO'} 413 414 items = [ 415 ('ACTIVE', "アクティブのみ", "", 'HAND', 1), 416 ('UP', "アクティブより上", "", 'TRIA_UP_BAR', 2), 417 ('DOWN', "アクティブより下", "", 'TRIA_DOWN_BAR', 3), 418 ('ALL', "全て", "", 'ARROW_LEFTRIGHT', 4), 419 ] 420 target = bpy.props.EnumProperty(items=items, name="対象", default='ACTIVE') 421 radius = bpy.props.FloatProperty(name="範囲倍率", default=3, min=0.1, max=50, soft_min=0.1, soft_max=50, step=50, precision=2) 422 strength = bpy.props.IntProperty(name="強さ", default=1, min=1, max=10, soft_min=1, soft_max=10) 423 items = [ 424 ('BOTH', "増減両方", "", 'AUTOMERGE_ON', 1), 425 ('ADD', "増加のみ", "", 'TRIA_UP', 2), 426 ('SUB', "減少のみ", "", 'TRIA_DOWN', 3), 427 ] 428 effect = bpy.props.EnumProperty(items=items, name="ぼかし効果", default='BOTH') 429 is_normalize = bpy.props.BoolProperty(name="他頂点グループも調節", default=True) 430 431 @classmethod 432 def poll(cls, context): 433 ob = context.active_object 434 if ob and ob.type == 'MESH': 435 return ob.vertex_groups.active 436 return False 437 438 def invoke(self, context, event): 439 return context.window_manager.invoke_props_dialog(self) 440 441 def draw(self, context): 442 self.layout.prop(self, 'target', icon='VIEWZOOM') 443 self.layout.prop(self, 'radius', icon='PROP_ON') 444 self.layout.prop(self, 'strength', icon='ARROW_LEFTRIGHT') 445 self.layout.prop(self, 'effect', icon='BRUSH_BLUR') 446 self.layout.prop(self, 'is_normalize', icon='GROUP') 447 448 def execute(self, context): 449 ob = context.active_object 450 me = ob.data 451 452 pre_mode = ob.mode 453 bpy.ops.object.mode_set(mode='OBJECT') 454 455 bm = bmesh.new() 456 bm.from_mesh(me) 457 edge_lengths = [e.calc_length() for e in bm.edges] 458 bm.free() 459 edge_lengths.sort() 460 average_edge_length = sum(edge_lengths) / len(edge_lengths) 461 center_index = int((len(edge_lengths) - 1) / 2.0) 462 average_edge_length = (average_edge_length + edge_lengths[center_index]) / 2 463 radius = average_edge_length * self.radius 464 465 context.window_manager.progress_begin(0, len(me.vertices)) 466 progress_reduce = len(me.vertices) // 200 + 1 467 near_vert_data = [] 468 kd = mathutils.kdtree.KDTree(len(me.vertices)) 469 for vert in me.vertices: 470 kd.insert(vert.co.copy(), vert.index) 471 kd.balance() 472 for vert in me.vertices: 473 new_vert_datum = [] 474 near_vert_data.append(new_vert_datum) 475 near_vert_data_append = new_vert_datum.append 476 for co, index, dist in kd.find_range(vert.co, radius): 477 multi = (radius - dist) / radius 478 near_vert_data_append((index, multi)) 479 if vert.index % progress_reduce == 0: 480 context.window_manager.progress_update(vert.index) 481 context.window_manager.progress_end() 482 483 target_vertex_groups = [] 484 if self.target == 'ACTIVE': 485 target_vertex_groups.append(ob.vertex_groups.active) 486 elif self.target == 'UP': 487 for vertex_group in ob.vertex_groups: 488 if vertex_group.index <= ob.vertex_groups.active_index: 489 target_vertex_groups.append(vertex_group) 490 elif self.target == 'DOWN': 491 for vertex_group in ob.vertex_groups: 492 if ob.vertex_groups.active_index <= vertex_group.index: 493 target_vertex_groups.append(vertex_group) 494 elif self.target == 'ALL': 495 for vertex_group in ob.vertex_groups: 496 target_vertex_groups.append(vertex_group) 497 498 progress_total = len(target_vertex_groups) * self.strength * len(me.vertices) 499 context.window_manager.progress_begin(0, progress_total) 500 progress_reduce = progress_total // 200 + 1 501 progress_count = 0 502 for strength_count in range(self.strength): 503 for vertex_group in target_vertex_groups: 504 505 weights = [] 506 weights_append = weights.append 507 for vert in me.vertices: 508 for elem in vert.groups: 509 if elem.group == vertex_group.index: 510 weights_append(elem.weight) 511 break 512 else: 513 weights_append(0.0) 514 515 for vert in me.vertices: 516 517 target_weight = weights[vert.index] 518 519 total_weight = 0.0 520 total_multi = 0.0 521 for index, multi in near_vert_data[vert.index]: 522 if self.effect == 'ADD': 523 if target_weight <= weights[index]: 524 total_weight += weights[index] * multi 525 total_multi += multi 526 elif self.effect == 'SUB': 527 if weights[index] <= target_weight: 528 total_weight += weights[index] * multi 529 total_multi += multi 530 else: 531 total_weight += weights[index] * multi 532 total_multi += multi 533 534 if 0 < total_multi: 535 average_weight = total_weight / total_multi 536 else: 537 average_weight = 0.0 538 539 if 0.001 < average_weight: 540 vertex_group.add([vert.index], average_weight, 'REPLACE') 541 else: 542 vertex_group.remove([vert.index]) 543 544 progress_count += 1 545 if progress_count % progress_reduce == 0: 546 context.window_manager.progress_update(progress_count) 547 548 if self.is_normalize: 549 550 other_weight_total = 0.0 551 for elem in vert.groups: 552 if elem.group != vertex_group.index: 553 other_weight_total += elem.weight 554 555 diff_weight = average_weight - target_weight 556 new_other_weight_total = other_weight_total - diff_weight 557 if 0 < other_weight_total: 558 other_weight_multi = new_other_weight_total / other_weight_total 559 else: 560 other_weight_multi = 0.0 561 562 for elem in vert.groups: 563 if elem.group != vertex_group.index: 564 vg = ob.vertex_groups[elem.group] 565 vg.add([vert.index], elem.weight * other_weight_multi, 'REPLACE') 566 567 context.window_manager.progress_end() 568 bpy.ops.object.mode_set(mode=pre_mode) 569 return {'FINISHED'}
items =
[('BOTH', '増減両方', '', 'AUTOMERGE_ON', 1), ('ADD', '増加のみ', '', 'TRIA_UP', 2), ('SUB', '減少のみ', '', 'TRIA_DOWN', 3)]
target: <_PropertyDeferred, <built-in function EnumProperty>, {'items': [('ACTIVE', 'アクティブのみ', '', 'HAND', 1), ('UP', 'アクティブより上', '', 'TRIA_UP_BAR', 2), ('DOWN', 'アクティブより下', '', 'TRIA_DOWN_BAR', 3), ('ALL', '全て', '', 'ARROW_LEFTRIGHT', 4)], 'name': '対象', 'default': 'ACTIVE', 'attr': 'target'}> =
<_PropertyDeferred, <built-in function EnumProperty>, {'items': [('ACTIVE', 'アクティブのみ', '', 'HAND', 1), ('UP', 'アクティブより上', '', 'TRIA_UP_BAR', 2), ('DOWN', 'アクティブより下', '', 'TRIA_DOWN_BAR', 3), ('ALL', '全て', '', 'ARROW_LEFTRIGHT', 4)], 'name': '対象', 'default': 'ACTIVE', 'attr': 'target'}>
radius: <_PropertyDeferred, <built-in function FloatProperty>, {'name': '範囲倍率', 'default': 3, 'min': 0.1, 'max': 50, 'soft_min': 0.1, 'soft_max': 50, 'step': 50, 'precision': 2, 'attr': 'radius'}> =
<_PropertyDeferred, <built-in function FloatProperty>, {'name': '範囲倍率', 'default': 3, 'min': 0.1, 'max': 50, 'soft_min': 0.1, 'soft_max': 50, 'step': 50, 'precision': 2, 'attr': 'radius'}>
strength: <_PropertyDeferred, <built-in function IntProperty>, {'name': '強さ', 'default': 1, 'min': 1, 'max': 10, 'soft_min': 1, 'soft_max': 10, 'attr': 'strength'}> =
<_PropertyDeferred, <built-in function IntProperty>, {'name': '強さ', 'default': 1, 'min': 1, 'max': 10, 'soft_min': 1, 'soft_max': 10, 'attr': 'strength'}>
effect: <_PropertyDeferred, <built-in function EnumProperty>, {'items': [('BOTH', '増減両方', '', 'AUTOMERGE_ON', 1), ('ADD', '増加のみ', '', 'TRIA_UP', 2), ('SUB', '減少のみ', '', 'TRIA_DOWN', 3)], 'name': 'ぼかし効果', 'default': 'BOTH', 'attr': 'effect'}> =
<_PropertyDeferred, <built-in function EnumProperty>, {'items': [('BOTH', '増減両方', '', 'AUTOMERGE_ON', 1), ('ADD', '増加のみ', '', 'TRIA_UP', 2), ('SUB', '減少のみ', '', 'TRIA_DOWN', 3)], 'name': 'ぼかし効果', 'default': 'BOTH', 'attr': 'effect'}>
is_normalize: <_PropertyDeferred, <built-in function BoolProperty>, {'name': '他頂点グループも調節', 'default': True, 'attr': 'is_normalize'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': '他頂点グループも調節', 'default': True, 'attr': 'is_normalize'}>
def
draw(self, context):
441 def draw(self, context): 442 self.layout.prop(self, 'target', icon='VIEWZOOM') 443 self.layout.prop(self, 'radius', icon='PROP_ON') 444 self.layout.prop(self, 'strength', icon='ARROW_LEFTRIGHT') 445 self.layout.prop(self, 'effect', icon='BRUSH_BLUR') 446 self.layout.prop(self, 'is_normalize', icon='GROUP')
def
execute(self, context):
448 def execute(self, context): 449 ob = context.active_object 450 me = ob.data 451 452 pre_mode = ob.mode 453 bpy.ops.object.mode_set(mode='OBJECT') 454 455 bm = bmesh.new() 456 bm.from_mesh(me) 457 edge_lengths = [e.calc_length() for e in bm.edges] 458 bm.free() 459 edge_lengths.sort() 460 average_edge_length = sum(edge_lengths) / len(edge_lengths) 461 center_index = int((len(edge_lengths) - 1) / 2.0) 462 average_edge_length = (average_edge_length + edge_lengths[center_index]) / 2 463 radius = average_edge_length * self.radius 464 465 context.window_manager.progress_begin(0, len(me.vertices)) 466 progress_reduce = len(me.vertices) // 200 + 1 467 near_vert_data = [] 468 kd = mathutils.kdtree.KDTree(len(me.vertices)) 469 for vert in me.vertices: 470 kd.insert(vert.co.copy(), vert.index) 471 kd.balance() 472 for vert in me.vertices: 473 new_vert_datum = [] 474 near_vert_data.append(new_vert_datum) 475 near_vert_data_append = new_vert_datum.append 476 for co, index, dist in kd.find_range(vert.co, radius): 477 multi = (radius - dist) / radius 478 near_vert_data_append((index, multi)) 479 if vert.index % progress_reduce == 0: 480 context.window_manager.progress_update(vert.index) 481 context.window_manager.progress_end() 482 483 target_vertex_groups = [] 484 if self.target == 'ACTIVE': 485 target_vertex_groups.append(ob.vertex_groups.active) 486 elif self.target == 'UP': 487 for vertex_group in ob.vertex_groups: 488 if vertex_group.index <= ob.vertex_groups.active_index: 489 target_vertex_groups.append(vertex_group) 490 elif self.target == 'DOWN': 491 for vertex_group in ob.vertex_groups: 492 if ob.vertex_groups.active_index <= vertex_group.index: 493 target_vertex_groups.append(vertex_group) 494 elif self.target == 'ALL': 495 for vertex_group in ob.vertex_groups: 496 target_vertex_groups.append(vertex_group) 497 498 progress_total = len(target_vertex_groups) * self.strength * len(me.vertices) 499 context.window_manager.progress_begin(0, progress_total) 500 progress_reduce = progress_total // 200 + 1 501 progress_count = 0 502 for strength_count in range(self.strength): 503 for vertex_group in target_vertex_groups: 504 505 weights = [] 506 weights_append = weights.append 507 for vert in me.vertices: 508 for elem in vert.groups: 509 if elem.group == vertex_group.index: 510 weights_append(elem.weight) 511 break 512 else: 513 weights_append(0.0) 514 515 for vert in me.vertices: 516 517 target_weight = weights[vert.index] 518 519 total_weight = 0.0 520 total_multi = 0.0 521 for index, multi in near_vert_data[vert.index]: 522 if self.effect == 'ADD': 523 if target_weight <= weights[index]: 524 total_weight += weights[index] * multi 525 total_multi += multi 526 elif self.effect == 'SUB': 527 if weights[index] <= target_weight: 528 total_weight += weights[index] * multi 529 total_multi += multi 530 else: 531 total_weight += weights[index] * multi 532 total_multi += multi 533 534 if 0 < total_multi: 535 average_weight = total_weight / total_multi 536 else: 537 average_weight = 0.0 538 539 if 0.001 < average_weight: 540 vertex_group.add([vert.index], average_weight, 'REPLACE') 541 else: 542 vertex_group.remove([vert.index]) 543 544 progress_count += 1 545 if progress_count % progress_reduce == 0: 546 context.window_manager.progress_update(progress_count) 547 548 if self.is_normalize: 549 550 other_weight_total = 0.0 551 for elem in vert.groups: 552 if elem.group != vertex_group.index: 553 other_weight_total += elem.weight 554 555 diff_weight = average_weight - target_weight 556 new_other_weight_total = other_weight_total - diff_weight 557 if 0 < other_weight_total: 558 other_weight_multi = new_other_weight_total / other_weight_total 559 else: 560 other_weight_multi = 0.0 561 562 for elem in vert.groups: 563 if elem.group != vertex_group.index: 564 vg = ob.vertex_groups[elem.group] 565 vg.add([vert.index], elem.weight * other_weight_multi, 'REPLACE') 566 567 context.window_manager.progress_end() 568 bpy.ops.object.mode_set(mode=pre_mode) 569 return {'FINISHED'}
Inherited Members
- bpy_types.Operator
- as_keywords
- poll_message_set
- builtins.bpy_struct
- keys
- values
- get
- pop
- as_pointer
- keyframe_insert
- keyframe_delete
- driver_add
- driver_remove
- is_property_set
- property_unset
- is_property_readonly
- is_property_overridable_library
- property_overridable_library_set
- path_resolve
- path_from_id
- type_recast
- bl_rna_get_subclass_py
- bl_rna_get_subclass
- id_properties_ensure
- id_properties_clear
- id_properties_ui
- id_data
@compat.BlRegister()
class
CNV_OT_multiply_vertex_group572@compat.BlRegister() 573class CNV_OT_multiply_vertex_group(bpy.types.Operator): 574 bl_idname = 'object.multiply_vertex_group' 575 bl_label = "頂点グループに乗算" 576 bl_description = "頂点グループのウェイトに数値を乗算し、ウェイトの強度を増減させます" 577 bl_options = {'REGISTER', 'UNDO'} 578 579 items = [ 580 ('ACTIVE', "アクティブのみ", "", 'HAND', 1), 581 ('UP', "アクティブより上", "", 'TRIA_UP_BAR', 2), 582 ('DOWN', "アクティブより下", "", 'TRIA_DOWN_BAR', 3), 583 ('ALL', "全て", "", 'ARROW_LEFTRIGHT', 4), 584 ] 585 target = bpy.props.EnumProperty(items=items, name="対象", default='ACTIVE') 586 value = bpy.props.FloatProperty(name="倍率", default=1.1, min=0.1, max=10, soft_min=0.1, soft_max=10, step=10, precision=2) 587 is_normalize = bpy.props.BoolProperty(name="他頂点グループも調節", default=True) 588 589 @classmethod 590 def poll(cls, context): 591 ob = context.active_object 592 if ob: 593 if ob.type == 'MESH': 594 return ob.vertex_groups.active 595 return False 596 597 def invoke(self, context, event): 598 return context.window_manager.invoke_props_dialog(self) 599 600 def draw(self, context): 601 self.layout.prop(self, 'target', icon='VIEWZOOM') 602 self.layout.prop(self, 'value', icon='ARROW_LEFTRIGHT') 603 self.layout.prop(self, 'is_normalize', icon='GROUP') 604 605 def execute(self, context): 606 ob = context.active_object 607 me = ob.data 608 609 pre_mode = ob.mode 610 bpy.ops.object.mode_set(mode='OBJECT') 611 612 target_vertex_groups = [] 613 if self.target == 'ACTIVE': 614 target_vertex_groups.append(ob.vertex_groups.active) 615 elif self.target == 'UP': 616 for vertex_group in ob.vertex_groups: 617 if vertex_group.index <= ob.vertex_groups.active_index: 618 target_vertex_groups.append(vertex_group) 619 elif self.target == 'DOWN': 620 for vertex_group in ob.vertex_groups: 621 if ob.vertex_groups.active_index <= vertex_group.index: 622 target_vertex_groups.append(vertex_group) 623 elif self.target == 'ALL': 624 for vertex_group in ob.vertex_groups: 625 target_vertex_groups.append(vertex_group) 626 627 for vertex_group in target_vertex_groups: 628 for vert in me.vertices: 629 630 old_weight = -1 631 other_weight_total = 0.0 632 for elem in vert.groups: 633 if elem.group == vertex_group.index: 634 old_weight = elem.weight 635 else: 636 other_weight_total += elem.weight 637 if old_weight == -1: 638 continue 639 640 new_weight = old_weight * self.value 641 vertex_group.add([vert.index], new_weight, 'REPLACE') 642 643 if self.is_normalize: 644 645 diff_weight = new_weight - old_weight 646 647 new_other_weight_total = other_weight_total - diff_weight 648 if 0 < other_weight_total: 649 other_weight_multi = new_other_weight_total / other_weight_total 650 else: 651 other_weight_multi = 0.0 652 653 for elem in vert.groups: 654 if elem.group != vertex_group.index: 655 vg = ob.vertex_groups[elem.group] 656 vg.add([vert.index], elem.weight * other_weight_multi, 'REPLACE') 657 658 bpy.ops.object.mode_set(mode=pre_mode) 659 return {'FINISHED'}
items =
[('ACTIVE', 'アクティブのみ', '', 'HAND', 1), ('UP', 'アクティブより上', '', 'TRIA_UP_BAR', 2), ('DOWN', 'アクティブより下', '', 'TRIA_DOWN_BAR', 3), ('ALL', '全て', '', 'ARROW_LEFTRIGHT', 4)]
target: <_PropertyDeferred, <built-in function EnumProperty>, {'items': [('ACTIVE', 'アクティブのみ', '', 'HAND', 1), ('UP', 'アクティブより上', '', 'TRIA_UP_BAR', 2), ('DOWN', 'アクティブより下', '', 'TRIA_DOWN_BAR', 3), ('ALL', '全て', '', 'ARROW_LEFTRIGHT', 4)], 'name': '対象', 'default': 'ACTIVE', 'attr': 'target'}> =
<_PropertyDeferred, <built-in function EnumProperty>, {'items': [('ACTIVE', 'アクティブのみ', '', 'HAND', 1), ('UP', 'アクティブより上', '', 'TRIA_UP_BAR', 2), ('DOWN', 'アクティブより下', '', 'TRIA_DOWN_BAR', 3), ('ALL', '全て', '', 'ARROW_LEFTRIGHT', 4)], 'name': '対象', 'default': 'ACTIVE', 'attr': 'target'}>
value: <_PropertyDeferred, <built-in function FloatProperty>, {'name': '倍率', 'default': 1.1, 'min': 0.1, 'max': 10, 'soft_min': 0.1, 'soft_max': 10, 'step': 10, 'precision': 2, 'attr': 'value'}> =
<_PropertyDeferred, <built-in function FloatProperty>, {'name': '倍率', 'default': 1.1, 'min': 0.1, 'max': 10, 'soft_min': 0.1, 'soft_max': 10, 'step': 10, 'precision': 2, 'attr': 'value'}>
is_normalize: <_PropertyDeferred, <built-in function BoolProperty>, {'name': '他頂点グループも調節', 'default': True, 'attr': 'is_normalize'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': '他頂点グループも調節', 'default': True, 'attr': 'is_normalize'}>
def
execute(self, context):
605 def execute(self, context): 606 ob = context.active_object 607 me = ob.data 608 609 pre_mode = ob.mode 610 bpy.ops.object.mode_set(mode='OBJECT') 611 612 target_vertex_groups = [] 613 if self.target == 'ACTIVE': 614 target_vertex_groups.append(ob.vertex_groups.active) 615 elif self.target == 'UP': 616 for vertex_group in ob.vertex_groups: 617 if vertex_group.index <= ob.vertex_groups.active_index: 618 target_vertex_groups.append(vertex_group) 619 elif self.target == 'DOWN': 620 for vertex_group in ob.vertex_groups: 621 if ob.vertex_groups.active_index <= vertex_group.index: 622 target_vertex_groups.append(vertex_group) 623 elif self.target == 'ALL': 624 for vertex_group in ob.vertex_groups: 625 target_vertex_groups.append(vertex_group) 626 627 for vertex_group in target_vertex_groups: 628 for vert in me.vertices: 629 630 old_weight = -1 631 other_weight_total = 0.0 632 for elem in vert.groups: 633 if elem.group == vertex_group.index: 634 old_weight = elem.weight 635 else: 636 other_weight_total += elem.weight 637 if old_weight == -1: 638 continue 639 640 new_weight = old_weight * self.value 641 vertex_group.add([vert.index], new_weight, 'REPLACE') 642 643 if self.is_normalize: 644 645 diff_weight = new_weight - old_weight 646 647 new_other_weight_total = other_weight_total - diff_weight 648 if 0 < other_weight_total: 649 other_weight_multi = new_other_weight_total / other_weight_total 650 else: 651 other_weight_multi = 0.0 652 653 for elem in vert.groups: 654 if elem.group != vertex_group.index: 655 vg = ob.vertex_groups[elem.group] 656 vg.add([vert.index], elem.weight * other_weight_multi, 'REPLACE') 657 658 bpy.ops.object.mode_set(mode=pre_mode) 659 return {'FINISHED'}
Inherited Members
- bpy_types.Operator
- as_keywords
- poll_message_set
- builtins.bpy_struct
- keys
- values
- get
- pop
- as_pointer
- keyframe_insert
- keyframe_delete
- driver_add
- driver_remove
- is_property_set
- property_unset
- is_property_readonly
- is_property_overridable_library
- property_overridable_library_set
- path_resolve
- path_from_id
- type_recast
- bl_rna_get_subclass_py
- bl_rna_get_subclass
- id_properties_ensure
- id_properties_clear
- id_properties_ui
- id_data
@compat.BlRegister()
class
CNV_OT_remove_noassign_vertex_groups662@compat.BlRegister() 663class CNV_OT_remove_noassign_vertex_groups(bpy.types.Operator): 664 bl_idname = 'object.remove_noassign_vertex_groups' 665 bl_label = "割り当てのない頂点グループを削除" 666 bl_description = "どの頂点にも割り当てられていない頂点グループを全て削除します" 667 bl_options = {'REGISTER', 'UNDO'} 668 669 threshold = bpy.props.FloatProperty(name="これ以下の影響は切り捨て", default=0.000001, min=0.0, max=1.0, soft_min=0.0, soft_max=1.0, step=1, precision=10) 670 671 @classmethod 672 def poll(cls, context): 673 ob = context.active_object 674 if ob and ob.type == 'MESH': 675 return len(ob.vertex_groups) > 0 676 return False 677 678 def invoke(self, context, event): 679 return context.window_manager.invoke_props_dialog(self) 680 681 def draw(self, context): 682 self.layout.prop(self, 'threshold') 683 684 def execute(self, context): 685 ob = context.active_object 686 me = ob.data 687 688 is_keeps = [False for i in range(len(ob.vertex_groups))] 689 690 for vert in me.vertices: 691 for vge in vert.groups: 692 if not is_keeps[vge.group]: 693 if self.threshold < vge.weight: 694 is_keeps[vge.group] = True 695 696 copy_vertex_groups = ob.vertex_groups[:] 697 for i in range(len(copy_vertex_groups)): 698 if not is_keeps[i] and not copy_vertex_groups[i].lock_weight: 699 ob.vertex_groups.remove(copy_vertex_groups[i]) 700 701 return {'FINISHED'}
threshold: <_PropertyDeferred, <built-in function FloatProperty>, {'name': 'これ以下の影響は切り捨て', 'default': 1e-06, 'min': 0.0, 'max': 1.0, 'soft_min': 0.0, 'soft_max': 1.0, 'step': 1, 'precision': 10, 'attr': 'threshold'}> =
<_PropertyDeferred, <built-in function FloatProperty>, {'name': 'これ以下の影響は切り捨て', 'default': 1e-06, 'min': 0.0, 'max': 1.0, 'soft_min': 0.0, 'soft_max': 1.0, 'step': 1, 'precision': 10, 'attr': 'threshold'}>
def
execute(self, context):
684 def execute(self, context): 685 ob = context.active_object 686 me = ob.data 687 688 is_keeps = [False for i in range(len(ob.vertex_groups))] 689 690 for vert in me.vertices: 691 for vge in vert.groups: 692 if not is_keeps[vge.group]: 693 if self.threshold < vge.weight: 694 is_keeps[vge.group] = True 695 696 copy_vertex_groups = ob.vertex_groups[:] 697 for i in range(len(copy_vertex_groups)): 698 if not is_keeps[i] and not copy_vertex_groups[i].lock_weight: 699 ob.vertex_groups.remove(copy_vertex_groups[i]) 700 701 return {'FINISHED'}
Inherited Members
- bpy_types.Operator
- as_keywords
- poll_message_set
- builtins.bpy_struct
- keys
- values
- items
- get
- pop
- as_pointer
- keyframe_insert
- keyframe_delete
- driver_add
- driver_remove
- is_property_set
- property_unset
- is_property_readonly
- is_property_overridable_library
- property_overridable_library_set
- path_resolve
- path_from_id
- type_recast
- bl_rna_get_subclass_py
- bl_rna_get_subclass
- id_properties_ensure
- id_properties_clear
- id_properties_ui
- id_data