CM3D2 Converter.misc_MESH_MT_shape_key_specials
1# 「プロパティ」エリア → 「メッシュデータ」タブ → 「シェイプキー」パネル → ▼ボタン 2import time 3import bpy 4import bmesh 5import mathutils 6import traceback 7from . import common 8from . import compat 9from . import model_export 10from .translations.pgettext_functions import * 11 12 13# メニュー等に項目追加 14def menu_func(self, context): 15 icon_id = common.kiss_icon() 16 self.layout.separator() 17 sub = self.layout.column() 18 self.layout.label(text="CM3D2 Converter", icon_value=icon_id) 19 if not compat.IS_LEGACY: 20 sub.separator() 21 sub.operator('object.change_base_shape_key', icon='SHAPEKEY_DATA') 22 sub.operator('object.multiply_shape_key', icon=compat.icon('CON_SIZELIKE')) 23 sub.operator('object.blur_shape_key', icon='MOD_SMOOTH') 24 sub.separator() 25 sub.operator('object.copy_shape_key_values', icon='COPYDOWN') 26 sub.separator() 27 sub.operator('object.quick_shape_key_transfer', icon=compat.icon('MOD_DATA_TRANSFER')) 28 sub.operator('object.precision_shape_key_transfer', icon='MOD_MESHDEFORM') 29 sub.operator('object.weighted_shape_key_transfer', icon='MOD_VERTEX_WEIGHT') 30 sub.separator() 31 else: 32 sub.separator() 33 sub.operator('object.change_base_shape_key', icon='SHAPEKEY_DATA') 34 sub.operator('object.multiply_shape_key', icon_value=icon_id) 35 sub.operator('object.blur_shape_key', icon_value=icon_id) 36 sub.separator() 37 sub.operator('object.copy_shape_key_values', icon_value=icon_id) 38 sub.separator() 39 sub.operator('object.quick_shape_key_transfer', icon_value=icon_id) 40 sub.operator('object.precision_shape_key_transfer', icon_value=icon_id) 41 sub.operator('object.weighted_shape_key_transfer', icon_value=icon_id) 42 sub.separator() 43 44 45 46class transfer_shape_key_iter: 47 index = -1 48 49 target_ob = None 50 source_ob = None 51 52 binded_shape_key = None 53 binded_shape_key_data = None 54 55 #target_mat = None 56 #source_mat = None 57 58 source_iter = None 59 60 source_shape_key_data = None 61 target_shape_key_data = None 62 63 def __init__(self, target_ob, source_ob, binded_shape_key=None): 64 self.target_ob = target_ob 65 self.source_ob = source_ob 66 self.binded_shape_key = binded_shape_key or self.source_ob.data.shape_keys.key_blocks[0] 67 68 def __iter__(self): 69 self.index = -1 70 if self.source_iter: 71 self.source_iter = iter(self.source_iter) 72 73 #self.target_mat = self.target_ob.matrix_world 74 #self.source_mat = self.source_ob.matrix_world 75 76 if self.source_ob and self.source_ob.data.shape_keys: 77 binded_index = self.source_ob.data.shape_keys.key_blocks.find(self.binded_shape_key.name) 78 #self.binded_shape_key_data = bmesh.new(use_operators=False) 79 #self.binded_shape_key_data.from_mesh(self.source_ob.data, use_shape_key=True, shape_key_index=binded_index) 80 #self.binded_shape_key_data.verts.ensure_lookup_table() 81 self.binded_shape_key_data = self.binded_shape_key.data 82 self.source_iter = iter(self.source_ob.data.shape_keys.key_blocks) 83 return self 84 85 def __next__(self): 86 target_me = self.target_ob.data 87 source_me = self.source_ob.data 88 89 target_shape_key = None 90 source_shape_key = next(self.source_iter, None) 91 if not source_shape_key: 92 raise StopIteration 93 94 self.index += 1 95 96 if target_me.shape_keys: 97 if source_shape_key.name in target_me.shape_keys.key_blocks: 98 target_shape_key = target_me.shape_keys.key_blocks[source_shape_key.name] 99 else: 100 target_shape_key = self.target_ob.shape_key_add(name=source_shape_key.name, from_mix=False) 101 else: 102 target_shape_key = self.target_ob.shape_key_add(name=source_shape_key.name, from_mix=False) 103 104 relative_key_name = source_shape_key.relative_key.name 105 106 rel_key = target_me.shape_keys.key_blocks.get(relative_key_name) 107 if rel_key: 108 target_shape_key.relative_key = rel_key 109 110 if not self.target_ob.active_shape_key_index == 0: 111 target_me.shape_keys.key_blocks[self.target_ob.active_shape_key_index].value = 0.0 112 if not self.source_ob.active_shape_key_index == 0: 113 source_me.shape_keys.key_blocks[self.source_ob.active_shape_key_index].value = 0.0 114 115 target_index = target_me.shape_keys.key_blocks.find(target_shape_key.name) 116 source_index = source_me.shape_keys.key_blocks.find(source_shape_key.name) 117 118 self.target_ob.active_shape_key_index = target_index 119 self.source_ob.active_shape_key_index = source_index 120 121 target_shape_key.value = 1.0 122 source_shape_key.value = 1.0 123 124 #source_shape_key_data = [compat.mul3(self.source_mat, source_shape_key.data[v.index].co, self.target_mat) - compat.mul3(self.source_mat, source_me.vertices[v.index].co, self.target_mat) for v in source_me.vertices] 125 #for i, v in enumerate(self.source_bind_data): 126 # shape_co = compat.mul3(self.source_mat, source_shape_key.data[i].co, self.target_mat) 127 # mesh_co = compat.mul3(self.source_mat, self.source_bind_data[i].co, self.target_mat) 128 # self.source_shape_key_data[i] = shape_co - mesh_co 129 130 #self.target_shape_key_data = bmesh.from_edit_mesh(self.target_ob.data) 131 #self.source_shape_key_data = bmesh.from_edit_mesh(self.source_ob.data) 132 #self.source_shape_key_data = bmesh.new(use_operators=False) 133 #self.source_shape_key_data.from_mesh(self.source_ob.data, use_shape_key=True, shape_key_index=source_index) 134 135 #self.target_shape_key_data.verts.ensure_lookup_table() 136 #self.source_shape_key_data.verts.ensure_lookup_table() 137 138 self.source_shape_key_data = source_shape_key.data 139 self.target_shape_key_data = target_shape_key.data 140 141 142 return self.index, target_shape_key, self.binded_shape_key_data, self.source_shape_key_data, self.target_shape_key_data 143 144 # update() will free resources for the current iteration of a loop, but not the loop itself. 145 def update(self, destructive=False): 146 pass 147 #if self.target_shape_key_data and self.target_shape_key_data.is_valid: 148 #bmesh.update_edit_mesh(self.target_ob.data, loop_triangles=True, destructive=destructive) 149 #self.target_shape_key_data.free() 150 #pass 151 152 #if self.source_shape_key_data and self.source_shape_key_data.is_valid: 153 #bmesh.update_edit_mesh(self.source_ob.data, loop_triangles=True, destructive=destructive) 154 #self.source_shape_key_data.free() 155 #pass 156 157 # free() will release all resources for the loop, leaving it unable to run unless iter() is used again. 158 def free(self, destructive=False): 159 pass 160 #self.update() 161 #if self.binded_shape_key_data and self.binded_shape_key_data.is_valid: 162 #bmesh.update_edit_mesh(self.source_ob.data, loop_triangles=True, destructive=destructive) 163 #self.binded_shape_key_data.free() 164 165 166 167class shape_key_transfer_op: 168 is_first_remove_all = bpy.props.BoolProperty(name="最初に全シェイプキーを削除", default=True) 169 is_remove_empty = bpy.props.BoolProperty(name="変形のないシェイプキーを削除", default=True) 170 is_bind_current_mix = bpy.props.BoolProperty(name="Bind to current source mix", default=False) 171 subdivide_number = bpy.props.IntProperty (name="参照元の分割", default=1, min=0, max=10, soft_min=0, soft_max=10) 172 173 target_ob = None 174 source_ob = None 175 og_source_ob = None 176 177 _start_time = 0 178 _timer = None 179 180 is_finished = False 181 is_canceled = False 182 183 pre_mode = None 184 pre_selected = None 185 186 binded_shape_key = None 187 kd = None 188 is_shapeds = {} 189 190 191 def draw(self, context): 192 self.layout.prop(self, 'is_first_remove_all', icon='ERROR' ) 193 self.layout.prop(self, 'subdivide_number' , icon='LATTICE_DATA' ) 194 self.layout.prop(self, 'is_remove_empty' , icon='X' ) 195 self.layout.prop(self, 'is_bind_current_mix', icon='AUTOMERGE_OFF') 196 197 def execute(self, context): 198 self.pre_selected = list(context.selected_objects) 199 self.target_ob, self.source_ob, self.og_source_ob = common.get_target_and_source_ob(context, copySource=True) 200 201 compat.set_hide(self.og_source_ob, True) 202 203 self._start_time = time.time() 204 self._timer = None 205 self.is_finished = False 206 self.is_canceled = False 207 self.pre_mode = self.target_ob.mode 208 209 self.binded_shape_key = None 210 self.source_bind_data = None 211 self.kd = None 212 self.is_shapeds = {} 213 214 bpy.ops.object.mode_set(mode='OBJECT') 215 216 try: 217 compat.link(context.scene, self.source_ob) 218 self.prepare(context) 219 220 except: 221 self.is_canceled = True 222 traceback.print_exc() 223 self.report(type={'ERROR'}, message="Error while preparing shapekey transfer.") 224 self.cancel(context) 225 return {'FINISHED'} 226 227 else: 228 wm = context.window_manager 229 self._timer = wm.event_timer_add(1.0/60.0, window=context.window) 230 wm.modal_handler_add(self) 231 self.report(type={'INFO'}, message="Press ESC to cancel shape key transfer") 232 233 compat.set_active(context, self.target_ob) 234 return {'RUNNING_MODAL'} 235 236 def modal(self, context, event): 237 if event.type == 'ESC': 238 self.is_canceled = 'WARNING' 239 if not event.type == 'TIMER': 240 return {'PASS_THROUGH'} 241 242 #print("Run Modal") 243 244 if self.is_canceled: 245 #print("Canceled") 246 try: 247 self.cancel(context) 248 except: 249 traceback.print_exc() 250 self.report(type={'ERROR'}, message="Error while canceling shapekey transfer.") 251 finally: 252 return {'FINISHED'} 253 254 if not self.is_canceled and not self.is_finished: 255 #print("Loop") 256 try: 257 self.is_finished = self.loop(context) 258 except: 259 self.is_canceled = True 260 traceback.print_exc() 261 self.report(type={'ERROR'}, message="Error while performing shapekey transfer.") 262 finally: 263 return {'PASS_THROUGH'} 264 265 else: 266 #print("Finish") 267 try: 268 self.finish(context) 269 except: 270 self.is_canceled = True 271 traceback.print_exc() 272 self.report(type={'ERROR'}, message="Error while finishing shapekey transfer.") 273 return {'PASS_THROUGH'} 274 else: 275 self.cleanup(context) 276 diff_time = time.time() - self._start_time 277 self.report(type={'INFO'}, message=f_tip_("{:.2f} Seconds", diff_time)) 278 return {'FINISHED'} 279 280 def prepare(self, context): 281 target_ob = self.target_ob 282 source_ob = self.source_ob 283 284 for ob in self.pre_selected: 285 compat.set_select(ob, False) 286 287 compat.set_active(context, source_ob) 288 #compat.set_select(source_og_ob, False) 289 #compat.set_select(target_ob, False) 290 291 # transform source's mesh now so theres no need to worry about it later 292 matrix_source_to_target = compat.mul(target_ob.matrix_world.inverted_safe(), source_ob.matrix_world) 293 source_ob.data.transform(matrix_source_to_target, shape_keys=True) 294 source_ob.matrix_world = target_ob.matrix_world 295 296 bpy.ops.object.mode_set(mode='EDIT') 297 bpy.ops.mesh.reveal() 298 bpy.ops.mesh.select_all(action='SELECT') 299 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) 300 bpy.ops.object.mode_set(mode='OBJECT') 301 302 if self.is_first_remove_all: 303 try: 304 target_ob.active_shape_key_index = 1 305 bpy.ops.object.shape_key_remove(all=True) 306 except: 307 pass 308 finally: 309 target_ob.active_shape_key_index = 0 310 else: 311 if target_ob.data.shape_keys: 312 for i, key in enumerate(target_ob.data.shape_keys.key_blocks): 313 if i == 0: 314 continue 315 else: 316 key.value = 0.0 317 target_ob.active_shape_key_index = 0 318 319 if self.is_bind_current_mix: 320 source_basis = source_ob.data.shape_keys.key_blocks[0] 321 322 old_basis = target_ob.data.shape_keys and next(iter(target_ob.data.shape_keys.key_blocks), False) or target_ob.shape_key_add() 323 old_basis.name = "__old_basis__" + old_basis.name 324 new_basis = target_ob.shape_key_add(name=source_basis.name) 325 326 self.binded_shape_key = source_ob.shape_key_add(name="__bind_shape_key", from_mix=True) 327 self.source_bind_data = self.binded_shape_key.data 328 329 compat.set_active(context, target_ob) 330 target_ob.active_shape_key_index = target_ob.data.shape_keys.key_blocks.find(new_basis.name) 331 # TOP指定でindex=1になるケースは、さらにもう一度UP 332 bpy.ops.object.shape_key_move(type='TOP') 333 if target_ob.active_shape_key_index == 1: 334 bpy.ops.object.shape_key_move(type='UP') 335 336 old_basis.relative_key = new_basis 337 338 source_ob.active_shape_key_index = source_ob.data.shape_keys.key_blocks.find(self.binded_shape_key.name) 339 340 else: 341 source_ob.active_shape_key_index = 0 342 self.source_bind_data = source_ob.data.vertices 343 344 #print(len(source_ob.data.vertices), len(self.source_bind_data)) 345 self.kd = mathutils.kdtree.KDTree(len(self.source_bind_data)) 346 for index, vert in enumerate(self.source_bind_data): 347 co = compat.mul(source_ob.matrix_world, vert.co) 348 self.kd.insert(co, index) 349 self.kd.balance() 350 351 for i, key in enumerate(source_ob.data.shape_keys.key_blocks): 352 if i == 0: 353 continue 354 else: 355 key.value = 0.0 356 357 def finish(self, context): 358 target_me = self.target_ob.data 359 360 if self.is_remove_empty: 361 for source_shape_key_name, is_shaped in reversed( list(self.is_shapeds.items()) ): 362 if not is_shaped: 363 target_shape_key = target_me.shape_keys.key_blocks.get(source_shape_key_name) 364 if not target_shape_key: 365 continue 366 key_blocks_values = target_me.shape_keys.key_blocks.values() 367 is_used = False 368 for key in key_blocks_values: 369 if key.relative_key == target_shape_key: 370 is_used = True 371 break 372 if not is_used: 373 self.target_ob.shape_key_remove(target_shape_key) 374 375 self.target_ob.active_shape_key_index = 0 376 377 def cancel(self, context): 378 report_type = (self.is_canceled == 'WARNING' and 'WARNING') or 'ERROR' 379 self.report(type={report_type}, message="Shape key transfer canceled. Results may not be as expected. Use Undo / Ctrl Z to revert changes") 380 self.cleanup(context) 381 382 def cleanup(self, context): 383 #compat.set_select(source_original_ob, True) 384 if self.target_ob: 385 #compat.set_select(target_ob, True) 386 compat.set_active(context, self.target_ob) 387 388 if self.og_source_ob: 389 compat.set_hide(self.og_source_ob, False) 390 391 source_me = self.source_ob and self.source_ob.data 392 if source_me: 393 common.remove_data([self.source_ob, source_me]) 394 elif self.source_ob: 395 common.remove_data([self.source_ob]) 396 397 if self._timer: 398 wm = context.window_manager 399 wm.event_timer_remove(self._timer) 400 401 if self.pre_mode: 402 bpy.ops.object.mode_set(mode=self.pre_mode) 403 404 if self.pre_selected: 405 for ob in self.pre_selected: 406 compat.set_select(ob, True) 407 408 self.target_ob = None 409 self.source_ob = None 410 411 self._timer = None 412 413 self.pre_mode = None 414 self.pre_selected = None 415 416 self.binded_shape_key = None 417 self.kd = None 418 self.is_shapeds = {} 419 420 421 422@compat.BlRegister() 423class CNV_OT_quick_shape_key_transfer(shape_key_transfer_op, bpy.types.Operator): 424 bl_idname = 'object.quick_shape_key_transfer' 425 bl_label = "クイック・シェイプキー転送" 426 bl_description = "アクティブなメッシュに他の選択メッシュのシェイプキーを高速で転送します" 427 bl_options = {'REGISTER', 'UNDO'} 428 429 step_size = bpy.props.IntProperty(name="Step Size (low = quality, high = speed)", default=1, min=1, max=100, soft_min=1, soft_max=10, step=1) 430 431 near_vert_indexs = [] 432 my_iter = None 433 434 @classmethod 435 def poll(cls, context): 436 obs = context.selected_objects 437 if len(obs) == 2: 438 active_ob = context.active_object 439 for ob in obs: 440 if ob.type != 'MESH': 441 return False 442 if ob.data.shape_keys and ob.name != active_ob.name: 443 return True 444 return False 445 446 def invoke(self, context, event): 447 return context.window_manager.invoke_props_dialog(self) 448 449 def draw(self, context): 450 shape_key_transfer_op.draw(self, context) 451 self.layout.prop(self, 'step_size') 452 453 def prepare(self, context): 454 shape_key_transfer_op.prepare(self, context) 455 456 target_me = self.target_ob.data 457 source_me = self.source_ob.data 458 459 self.near_vert_indexs = list( range(len(target_me.vertices)) ) 460 461 for v in target_me.vertices: 462 near_co = compat.mul(self.target_ob.matrix_world, v.co) #v.co 463 self.near_vert_indexs[v.index] = self.kd.find(near_co)[1] 464 465 self.my_iter = iter( transfer_shape_key_iter(self.target_ob, self.source_ob, self.binded_shape_key) ) 466 context.window_manager.progress_begin( 0, len(source_me.shape_keys.key_blocks) * len(target_me.vertices) ) 467 context.window_manager.progress_update( 0 ) 468 469 def loop(self, context): 470 source_shape_key_index, target_shape_key, binded_shape_key_data, source_shape_key_data, target_shape_key_data = next(self.my_iter, (-1, None, None, None, None)) 471 #print(source_shape_key_index, target_shape_key, binded_shape_key_data, source_shape_key_data, target_shape_key_data) 472 if not target_shape_key: 473 context.window_manager.progress_end() 474 return True 475 476 progress = source_shape_key_index * len(self.target_ob.data.vertices) 477 478 def check(index): 479 near_vert_index = self.near_vert_indexs[index] 480 near_shape_co = source_shape_key_data[near_vert_index].co - binded_shape_key_data[near_vert_index].co 481 482 context.window_manager.progress_update( progress + index ) 483 484 if abs(near_shape_co.length) > 2e-126: # 2e-126 is the smallest float != 0 485 target_shape_key_data[index].co += near_shape_co 486 return True 487 488 is_changed = False 489 just_changed = False 490 found_more = False 491 for i in range(0, len(target_shape_key_data), self.step_size): 492 493 if check(i) or found_more: 494 is_changed = True 495 found_more = False 496 if not just_changed: 497 for j in range(i-self.step_size+1, i): 498 if j < len(target_shape_key_data) and j > 0: 499 found_more = check(j) or found_more 500 for k in range(i+1, i+self.step_size): 501 if k < len(target_shape_key_data) and k > 0: 502 found_more = check(k) or found_more 503 just_changed = True 504 else: 505 just_changed = False 506 507 if not self.is_shapeds.get(target_shape_key.name): 508 self.is_shapeds[target_shape_key.name] = is_changed 509 self.my_iter.update() # only call this when done with current iteration. 510 511 def cleanup(self, context): 512 self.near_vert_indexs = [] 513 self.my_iter.free() 514 self.my_iter = None 515 shape_key_transfer_op.cleanup(self, context) 516 517 518 519@compat.BlRegister() 520class CNV_OT_precision_shape_key_transfer(shape_key_transfer_op, bpy.types.Operator): 521 bl_idname = 'object.precision_shape_key_transfer' 522 bl_label = "空間ぼかし・シェイプキー転送" 523 bl_description = "アクティブなメッシュに他の選択メッシュのシェイプキーを遠いほどぼかして転送します" 524 bl_options = {'REGISTER', 'UNDO'} 525 526 step_size = bpy.props.IntProperty(name="Step Size (low = quality, high = speed)", default=1, min=1, max=100, soft_min=1, soft_max=10, step=1) 527 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) 528 529 530 near_vert_data = [] 531 near_vert_multi_total = [] 532 my_iter = None 533 534 #source_raw_data = None 535 #binded_raw_data = None 536 #target_raw_data = None 537 538 @classmethod 539 def poll(cls, context): 540 obs = context.selected_objects 541 if len(obs) == 2: 542 active_ob = context.active_object 543 for ob in obs: 544 if ob.type != 'MESH': 545 return False 546 if ob.data.shape_keys and ob.name != active_ob.name: 547 return True 548 return False 549 550 def invoke(self, context, event): 551 return context.window_manager.invoke_props_dialog(self) 552 553 def draw(self, context): 554 shape_key_transfer_op.draw(self, context) 555 self.layout.prop(self, 'step_size') 556 self.layout.prop(self, 'extend_range', icon='PROP_ON') 557 558 def xexecute(self, context): 559 start_time = time.time() 560 561 target_ob, source_ob = common.get_target_and_source_ob(context, copySource=True) 562 target_me = target_ob.data 563 source_me = source_ob.ldata 564 565 pre_mode = target_ob.mode 566 bpy.ops.object.mode_set(mode='OBJECT') 567 568 try: 569 kd = self.prepare_sks_transfer(context, target_ob, source_ob) 570 571 context.window_manager.progress_begin(0, len(target_me.vertices)) 572 progress_reduce = len(target_me.vertices) // 200 + 1 573 near_vert_data = [] 574 near_vert_multi_total = [] 575 near_vert_multi_total_append = near_vert_multi_total.append 576 577 mat1, mat2 = source_ob.matrix_world, target_ob.matrix_world 578 source_shape_key_data = [compat.mul3(mat1, source_shape_key.data[v.index].co, mat2) - compat.mul3(mat1, source_me.vertices[v.index].co, mat2) for v in source_me.vertices] 579 580 for vert in target_me.vertices: 581 new_vert_data = [] 582 near_vert_data.append(new_vert_data) 583 near_vert_data_append = new_vert_data.append 584 585 target_co = compat.mul(target_ob.matrix_world, vert.co) 586 mini_co, mini_index, mini_dist = kd.find(target_co) 587 radius = mini_dist * self.extend_range 588 diff_radius = radius - mini_dist 589 590 multi_total = 0.0 591 for co, index, dist in kd.find_range(target_co, radius): 592 if 0 < diff_radius: 593 multi = (diff_radius - (dist - mini_dist)) / diff_radius 594 else: 595 multi = 1.0 596 near_vert_data_append((index, multi)) 597 multi_total += multi 598 near_vert_multi_total_append(multi_total) 599 600 if vert.index % progress_reduce == 0: 601 context.window_manager.progress_update(vert.index) 602 context.window_manager.progress_end() 603 604 is_shapeds = {} 605 context.window_manager.progress_begin(0, len(source_me.shape_keys.key_blocks) * len(target_me.vertices)) 606 context.window_manager.progress_update(0) 607 608 for source_shape_key_index, source_shape_key, target_shape_key in self.enumerate_transfer_sks(context, target_ob, source_ob): 609 for target_vert in target_me.vertices: 610 611 if 0 < near_vert_multi_total[target_vert.index]: 612 613 total_diff_co = mathutils.Vector((0, 0, 0)) 614 615 for near_index, near_multi in near_vert_data[target_vert.index]: 616 total_diff_co += source_shape_key_data[near_index] * near_multi 617 618 average_diff_co = total_diff_co / near_vert_multi_total[target_vert.index] 619 620 else: 621 average_diff_co = mathutils.Vector((0, 0, 0)) 622 623 target_shape_key.data[target_vert.index].co = target_me.vertices[target_vert.index].co + average_diff_co 624 is_shapeds[target_shape_key.name] = is_shapeds.get(target_shape_key.name) or 0.01 < average_diff_co.length 625 626 context.window_manager.progress_update((source_shape_key_index+1) * (target_me.vertices+1)) 627 #context.window_manager.progress_update(source_shape_key_index) 628 context.window_manager.progress_end() 629 630 self.finish_sks_transfer(context, target_ob, source_ob, is_shapeds) 631 632 except: 633 traceback.print_exc() 634 self.report(type={'ERROR'}, message="Error while transfering shapekeys. Results may not be as expected. Use Undo / Ctrl Z to revert changes") 635 636 finally: 637 self.cleanup_sks_transfer(context, target_ob, source_ob, pre_mode) 638 639 diff_time = time.time() - start_time 640 self.report(type={'INFO'}, message=f_tip_("{:.2f} Seconds", diff_time)) 641 return {'FINISHED'} 642 643 def prepare(self, context): 644 shape_key_transfer_op.prepare(self, context) 645 646 target_me = self.target_ob.data 647 source_me = self.source_ob.data 648 649 context.window_manager.progress_begin(0, len(target_me.vertices)) 650 progress_reduce = len(target_me.vertices) // 200 + 1 651 self.near_vert_data = [] 652 self.near_vert_multi_total = [] 653 near_vert_multi_total_append = self.near_vert_multi_total.append 654 655 for vert in target_me.vertices: 656 new_vert_data = [] 657 self.near_vert_data.append(new_vert_data) 658 self.near_vert_data_append = new_vert_data.append 659 660 target_co = compat.mul(self.target_ob.matrix_world, vert.co) #vert.co 661 mini_co, mini_index, mini_dist = self.kd.find(target_co) 662 radius = mini_dist * self.extend_range 663 diff_radius = radius - mini_dist 664 665 multi_total = 0.0 666 for co, index, dist in self.kd.find_range(target_co, radius): 667 if 0 < diff_radius: 668 multi = (diff_radius - (dist - mini_dist)) / diff_radius 669 else: 670 multi = 1.0 671 self.near_vert_data_append((index, multi)) 672 multi_total += multi 673 near_vert_multi_total_append(multi_total) 674 675 if vert.index % progress_reduce == 0: 676 context.window_manager.progress_update(vert.index) 677 context.window_manager.progress_end() 678 679 self.my_iter = iter(transfer_shape_key_iter(self.target_ob, self.source_ob, binded_shape_key=self.binded_shape_key)) 680 681 #self.source_raw_data = numpy.ndarray(shape=(len(source_me.vertices), 3), dtype=float, order='C') 682 #self.target_raw_data = numpy.ndarray(shape=(len(target_me.vertices), 3), dtype=float, order='C') 683 684 #self.binded_raw_data = numpy.ndarray(shape=len(self.source_bind_data)*3, dtype=float, order='C') 685 #self.source_bind_data.foreach_get('co', self.binded_raw_data) 686 #self.binded_raw_data.resize(self.binded_raw_data.size//3, 3) 687 688 context.window_manager.progress_begin(0, len(source_me.shape_keys.key_blocks) * len(target_me.vertices)) 689 context.window_manager.progress_update(0) 690 691 def loop(self, context): 692 #bpy.ops.object.mode_set(mode='OBJECT') 693 source_shape_key_index, target_shape_key, binded_shape_key_data, source_shape_key_data, target_shape_key_data = next(self.my_iter, (-1, None, None, None, None)) 694 if not target_shape_key: 695 context.window_manager.progress_end() 696 return True 697 698 #print("Loop for " + target_shape_key.name) 699 700 #context.window_manager.progress_begin( 0, len(self.source_ob.shape_keys.key_blocks) * len(target_ob.data.vertices) ) 701 progress = source_shape_key_index * len(self.target_ob.data.vertices) 702 #context.window_manager.progress_update( progress ) 703 704 diff_data = [None] * len(source_shape_key_data) 705 near_diff_co = mathutils.Vector.Fill(3, 0) # Creates a vector of length 3 filled with 0's 706 def check(index, near_diff_co=near_diff_co): 707 near_diff_co.zero() # This should be faster than creating a new vector every time 708 709 if self.near_vert_multi_total[index] > 0: 710 for near_index, near_multi in self.near_vert_data[index]: 711 diff_data[near_index] = diff_data[near_index] or source_shape_key_data[near_index].co - binded_shape_key_data[near_index].co 712 near_diff_co += diff_data[near_index] * near_multi 713 714 near_diff_co /= self.near_vert_multi_total[index] 715 716 context.window_manager.progress_update( progress + index ) 717 718 if near_diff_co.length > 2e-126: # 2e-126 is the smallest float != 0 719 target_shape_key_data[index].co += near_diff_co 720 return True 721 722 is_changed = False 723 just_changed = False 724 if self.step_size > 1: 725 found_more = False 726 for i in range(0, len(target_shape_key_data), self.step_size): 727 728 if check(i) or found_more: 729 is_changed = True 730 found_more = False 731 if not just_changed: 732 for j in range(i-self.step_size+1, i): 733 if j < len(target_shape_key_data) and j > 0: 734 found_more = check(j) or found_more 735 for k in range(i+1, i+self.step_size): 736 if k < len(target_shape_key_data) and k > 0: 737 found_more = check(k) or found_more 738 just_changed = True 739 else: 740 just_changed = False 741 742 else: # if self.step_size == 1: 743 for index, binded_vert, source_vert in zip(range(len(diff_data)), binded_shape_key_data, source_shape_key_data): 744 diff_data[index] = source_vert.co - binded_vert.co 745 if diff_data[index].length > 2e-126: 746 just_changed = True 747 748 if just_changed: 749 for target_vert, near_indices, near_total in zip(target_shape_key_data, self.near_vert_data, self.near_vert_multi_total): 750 near_diff_co.zero() # This should be faster than creating a new vector every time 751 752 if near_total > 0: 753 for near_index, near_multi in near_indices: 754 near_diff_co += diff_data[near_index] * near_multi 755 756 near_diff_co /= near_total 757 758 if near_diff_co.length > 2e-126: # 2e-126 is the smallest float != 0 759 target_vert.co += near_diff_co 760 is_changed = True 761 762 progress += 1 763 context.window_manager.progress_update( progress ) 764 else: 765 context.window_manager.progress_update( progress + len(target_shape_key_data) ) 766 767 self.is_shapeds[target_shape_key.name] = self.is_shapeds.get(target_shape_key.name) or is_changed 768 self.my_iter.update() # only call this when done with current iteration. 769 #bpy.ops.object.mode_set(mode='SCULPT') # Preview shape keys while transfering 770 771 def cleanup(self, context): 772 self.near_vert_data = [] 773 self.near_vert_multi_total = [] 774 self.my_iter = None 775 #self.source_raw_data = None 776 #self.binded_raw_data = None 777 #self.target_raw_data = None 778 shape_key_transfer_op.cleanup(self, context) 779 780 781""" 782global matched_vgroups 783global is_vgroups_used 784 785matched_vgroups = [] 786is_vgroups_used = {} 787 788#@compat.BlRegister() 789class CNV_PT_vgroups_selector(bpy.types.Panel): 790 bl_label = "Vertex Groups Selector" 791 bl_options = {'DEFAULT_CLOSED'} 792 bl_region_type = 'WINDOW' 793 bl_space_type = 'PROPERTIES' 794 795 bools1 = bpy.props.BoolProperty(name="Bools 1", default=False) 796 bools2 = bpy.props.BoolProperty(name="Bools 2", default=False) 797 bools3 = bpy.props.BoolProperty(name="Bools 3", default=False) 798 bools4 = bpy.props.BoolProperty(name="Bools 4", default=False) 799 bools5 = bpy.props.BoolProperty(name="Bools 5", default=False) 800 bools6 = bpy.props.BoolProperty(name="Bools 6", default=False) 801 802 803 keys = [] 804 805 def draw(self, context): 806 target_ob, source_ob = common.get_target_and_source_ob(context) 807 matched_vgroups = common.values_of_matched_keys(target_ob.vertex_groups, source_ob.vertex_groups) 808 print(f_("len(matched) = {length}", length=len(matched_vgroups))) 809 armature = target_ob.find_armature() or source_ob.find_armature() 810 armature = armature and armature.data 811 bone_data_ob = (target_ob.get("LocalBoneData:0") and target_ob) or (source_ob.get("LocalBoneData:0") and source_ob) or False 812 if bone_data_ob: 813 local_bone_data = model_export.CNV_OT_export_cm3d2_model.local_bone_data_parser(model_export.CNV_OT_export_cm3d2_model.indexed_data_generator(bone_data_ob, prefix="LocalBoneData:")) 814 local_bone_names = [ bone['name'] for bone in local_bone_data ] 815 for target_vg, source_vg in matched_vgroups: 816 vg_name = target_vg.name 817 is_used = True 818 if armature: 819 is_used = bool( armature.get(vg_name, False) ) 820 elif bone_data_ob: 821 is_used = bool( vg_name in local_bone_names ) 822 823 print(vg_name) 824 is_vgroups_used[vg_name] = bpy.props.BoolProperty(name=vg_name, default=is_used) 825 self.layout.prop( self, vg_name ) 826 827 def __getattr__(self, attr): 828 print(f_("get attr {key}", key=attr)) 829 if attr == 'matched_vgroups': 830 return matched_vgroups 831 if attr == 'is_vgroups_used': 832 return is_vgroups_used 833 if attr == 'layout': 834 return bpy.types.Panel.__getattribute__(self, attr) 835 return is_vgroups_used[attr] 836 837 def __setattr__(self, attr, value): 838 if attr == 'matched_vgroups': 839 matched_vgroups = value 840 return 841 if attr == 'is_vgroups_used': 842 is_vgroups_used = value 843 return 844 if attr == 'layout': 845 bpy.types.Panel.__setattribute__(self, attr, value) 846 print(f_("set attr {key} = {val}", key=attr, val=value)) 847 is_vgroups_used[attr] = value 848""" 849 850@compat.BlRegister() 851class CNV_UL_vgroups_selector(bpy.types.UIList): 852 bl_label = "Vertex Groups Selector" 853 bl_options = {'DEFAULT_CLOSED'} 854 bl_region_type = 'WINDOW' 855 bl_space_type = 'PROPERTIES' 856 857 # Constants (flags) 858 # Be careful not to shadow FILTER_ITEM! 859 VGROUP_EMPTY = 1 << 1 860 VGROUP_DEFORM = 1 << 0 861 862 armature = None 863 local_bone_names = None 864 cached_values = {} 865 866 expanded_layout = False 867 868 # Custom properties, saved with .blend file. 869 use_filter_name_reverse = bpy.props.BoolProperty( 870 name="Reverse Name", 871 default=False, 872 options=set(), 873 description="Reverse name filtering", 874 ) 875 use_filter_deform = bpy.props.BoolProperty( 876 name="Only Deform", 877 default=False, 878 options=set(), 879 description="Only show deforming vertex groups", 880 ) 881 use_filter_deform_reverse = bpy.props.BoolProperty( 882 name="Other", 883 default=False, 884 options=set(), 885 description="Only show non-deforming vertex groups", 886 ) 887 use_filter_empty = bpy.props.BoolProperty( 888 name="Filter Empty", 889 default=False, 890 options=set(), 891 description="Whether to filter empty vertex groups", 892 ) 893 use_filter_empty_reverse = bpy.props.BoolProperty( 894 name="Reverse Empty", 895 default=False, 896 options=set(), 897 description="Reverse empty filtering", 898 ) 899 900 # This allows us to have mutually exclusive options, which are also all disable-able! 901 def _gen_order_update(name1, name2): 902 def _u(self, ctxt): 903 if (getattr(self, name1)): 904 setattr(self, name2, False) 905 return _u 906 use_order_name = bpy.props.BoolProperty( 907 name="Name", default=False, options=set(), 908 description="Sort groups by their name (case-insensitive)", 909 update=_gen_order_update("use_order_name", "use_order_importance"), 910 ) 911 use_order_importance = bpy.props.BoolProperty( 912 name="Importance", 913 default=False, 914 options=set(), 915 description="Sort groups by their average weight in the mesh", 916 update=_gen_order_update("use_order_importance", "use_order_name"), 917 ) 918 use_filter_orderby_invert = bpy.props.BoolProperty( 919 name="Order by Invert", 920 default=False, 921 options=set(), 922 description="Invert the sort by order" 923 ) 924 925 # Usual draw item function. 926 def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag): 927 # Just in case, we do not use it here! 928 self.use_filter_invert = False 929 930 # assert(isinstance(item, bpy.types.VertexGroup) 931 #vgroup = getattr(data, 'matched_vgroups')[item.index] 932 if self.layout_type in {'DEFAULT', 'COMPACT'}: 933 # Here we use one feature of new filtering feature: it can pass data to draw_item, through flt_flag 934 # parameter, which contains exactly what filter_items set in its filter list for this item! 935 # In this case, we show empty groups grayed out. 936 cached_value = self.cached_values.get(item.name, None) 937 if (cached_value != None) and (cached_value != item.value): 938 item.preferred = item.value 939 940 if self.use_filter_deform: 941 item.value = bool(flt_flag & self.VGROUP_DEFORM) and item.preferred 942 else: 943 item.value = item.preferred 944 945 self.cached_values[item.name] = item.value 946 947 if flt_flag & (self.VGROUP_EMPTY | self.VGROUP_DEFORM): 948 col = layout.column() 949 col.enabled = False 950 col.alignment = 'LEFT' 951 col.prop(item, "value", text=item.name, emboss=False, icon_value=icon) 952 else: 953 layout.prop(item, "value", text=item.name, icon_value=icon) 954 955 icon = 'RADIOBUT_ON' if item.preferred else 'RADIOBUT_OFF' 956 layout.prop(item, "preferred", text="", icon=compat.icon(icon), emboss=False) 957 elif self.layout_type in {'GRID'}: 958 layout.alignment = 'CENTER' 959 if flt_flag & self.VGROUP_EMPTY: 960 layout.enabled = False 961 layout.label(text="", icon_value=icon) 962 963 def draw_filter(self, context, layout): 964 # Nothing much to say here, it's usual UI code... 965 row = layout.row() 966 if not self.expanded_layout: 967 layout.active = True 968 layout.enabled = True 969 row.active = True 970 row.enabled = True 971 self.expanded_layout = True 972 973 subrow = row.row(align=True) 974 subrow.prop(self, "filter_name", text="") 975 icon = 'ZOOM_OUT' if self.use_filter_name_reverse else 'ZOOM_IN' 976 subrow.prop(self, "use_filter_name_reverse", text="", icon=icon) 977 978 subrow = row.row(align=True) 979 subrow.prop(self, "use_filter_deform", toggle=True) 980 icon = 'ZOOM_OUT' if self.use_filter_deform_reverse else 'ZOOM_IN' 981 subrow.prop(self, "use_filter_deform_reverse", text="", icon=icon) 982 983 #subrow = row.row(align=True) 984 #subrow.prop(self, "use_filter_empty", toggle=True) 985 #icon = 'ZOOM_OUT' if self.use_filter_empty_reverse else 'ZOOM_IN' 986 #subrow.prop(self, "use_filter_empty_reverse", text="", icon=icon) 987 988 row = layout.row(align=True) 989 row.label(text="Order by:") 990 row.prop(self, "use_order_name", toggle=True) 991 #row.prop(self, "use_order_importance", toggle=True) 992 icon = 'TRIA_UP' if self.use_filter_orderby_invert else 'TRIA_DOWN' 993 row.prop(self, "use_filter_orderby_invert", text="", icon=icon) 994 995 def filter_items_empty_vgroups(self, context, vgroups): 996 # This helper function checks vgroups to find out whether they are empty, and what's their average weights. 997 # TODO: This should be RNA helper actually (a vgroup prop like "raw_data: ((vidx, vweight), etc.)"). 998 # Too slow for python! 999 obj_data = context.active_object.data 1000 ret = {vg.index: [True, 0.0] for vg in vgroups} 1001 if hasattr(obj_data, "vertices"): # Mesh data 1002 if obj_data.is_editmode: 1003 import bmesh 1004 bm = bmesh.from_edit_mesh(obj_data) 1005 # only ever one deform weight layer 1006 dvert_lay = bm.verts.layers.deform.active 1007 fact = 1 / len(bm.verts) 1008 if dvert_lay: 1009 for v in bm.verts: 1010 for vg_idx, vg_weight in v[dvert_lay].items(): 1011 ret[vg_idx][0] = False 1012 ret[vg_idx][1] += vg_weight * fact 1013 else: 1014 fact = 1 / len(obj_data.vertices) 1015 for v in obj_data.vertices: 1016 for vg in v.groups: 1017 ret[vg.group][0] = False 1018 ret[vg.group][1] += vg.weight * fact 1019 elif hasattr(obj_data, "points"): # Lattice data 1020 # XXX no access to lattice editdata? 1021 fact = 1 / len(obj_data.points) 1022 for v in obj_data.points: 1023 for vg in v.groups: 1024 ret[vg.group][0] = False 1025 ret[vg.group][1] += vg.weight * fact 1026 return ret 1027 1028 def filter_items(self, context, data, propname): 1029 # This function gets the collection property (as the usual tuple (data, propname)), and must return two lists: 1030 # * The first one is for filtering, it must contain 32bit integers were self.bitflag_filter_item marks the 1031 # matching item as filtered (i.e. to be shown), and 31 other bits are free for custom needs. Here we use the 1032 # first one to mark VGROUP_EMPTY. 1033 # * The second one is for reordering, it must return a list containing the new indices of the items (which 1034 # gives us a mapping org_idx -> new_idx). 1035 # Please note that the default UI_UL_list defines helper functions for common tasks (see its doc for more info). 1036 # If you do not make filtering and/or ordering, return empty list(s) (this will be more efficient than 1037 # returning full lists doing nothing!). 1038 items = getattr(data, propname) 1039 1040 if self.armature == None: 1041 target_ob, source_ob = common.get_target_and_source_ob(context) 1042 armature_ob = target_ob.find_armature() or source_ob.find_armature() 1043 self.armature = armature_ob and armature_ob.data or False 1044 1045 if not self.local_bone_names: 1046 target_ob, source_ob = common.get_target_and_source_ob(context) 1047 bone_data_ob = (target_ob.get("LocalBoneData:0") and target_ob) or (source_ob.get("LocalBoneData:0") and source_ob) or None 1048 if bone_data_ob: 1049 local_bone_data = model_export.CNV_OT_export_cm3d2_model.local_bone_data_parser(model_export.CNV_OT_export_cm3d2_model.indexed_data_generator(bone_data_ob, prefix="LocalBoneData:")) 1050 self.local_bone_names = [ bone['name'] for bone in local_bone_data ] 1051 1052 if not self.cached_values: 1053 self.cached_values = { item.name: item.value for item in items } 1054 #vgroups = [ getattr(data, 'matched_vgroups')[item.index][0] for item in items ] 1055 helper_funcs = bpy.types.UI_UL_list 1056 1057 # Default return values. 1058 flt_flags = [] 1059 flt_neworder = [] 1060 1061 # Pre-compute of vgroups data, CPU-intensive. :/ 1062 #vgroups_empty = self.filter_items_empty_vgroups(context, vgroups) 1063 1064 # Filtering by name 1065 if self.filter_name: 1066 flt_flags = helper_funcs.filter_items_by_name(self.filter_name, self.bitflag_filter_item, items, "name", 1067 reverse=self.use_filter_name_reverse) 1068 if not flt_flags: 1069 flt_flags = [self.bitflag_filter_item] * len(items) 1070 1071 for idx, vg in enumerate(items): 1072 # Filter by deform. 1073 if self.use_filter_deform: 1074 flt_flags[idx] |= self.VGROUP_DEFORM 1075 if self.use_filter_deform: 1076 if self.armature and self.armature.get(vg.name): 1077 if not self.use_filter_deform_reverse: 1078 flt_flags[idx] &= ~self.VGROUP_DEFORM 1079 elif bone_data_ob and (vg.name in self.local_bone_names): 1080 if not self.use_filter_deform_reverse: 1081 flt_flags[idx] &= ~self.VGROUP_DEFORM 1082 elif self.use_filter_deform_reverse or (not self.armature and not self.local_bone_names): 1083 flt_flags[idx] &= ~self.VGROUP_DEFORM 1084 else: 1085 flt_flags[idx] &= ~self.VGROUP_DEFORM 1086 1087 # Filter by emptiness. 1088 #if vgroups_empty[vg.index][0]: 1089 # flt_flags[idx] |= self.VGROUP_EMPTY 1090 # if self.use_filter_empty and self.use_filter_empty_reverse: 1091 # flt_flags[idx] &= ~self.bitflag_filter_item 1092 #elif self.use_filter_empty and not self.use_filter_empty_reverse: 1093 # flt_flags[idx] &= ~self.bitflag_filter_item 1094 1095 # Reorder by name or average weight. 1096 if self.use_order_name: 1097 flt_neworder = helper_funcs.sort_items_by_name(vgroups, "name") 1098 #elif self.use_order_importance: 1099 # _sort = [(idx, vgroups_empty[vg.index][1]) for idx, vg in enumerate(vgroups)] 1100 # flt_neworder = helper_funcs.sort_items_helper(_sort, lambda e: e[1], True) 1101 1102 return flt_flags, flt_neworder 1103 1104 1105#@compat.BlRegister() 1106#class CNV_BoolCollectionItem(bpy.types.PropertyGroup): 1107# bl_label = "CNV_BoolCollectionItem" 1108# bl_region_type = 'WINDOW' 1109# bl_space_type = 'PROPERTIES' 1110# 1111# name = bpy.props.StringProperty(name="Name", default="Unknown") 1112# value = bpy.props.BoolProperty(name="Value", default=True) 1113# index = bpy.props.IntProperty(name="Index", default=-1) 1114# preferred = bpy.props.BoolProperty(name="Prefered", default=True) 1115 1116 1117@compat.BlRegister() 1118class CNV_OT_weighted_shape_key_transfer(shape_key_transfer_op, bpy.types.Operator): 1119 bl_idname = 'object.weighted_shape_key_transfer' 1120 bl_label = "Weighted shape key transfer" 1121 bl_description = "Transfers the shape keys of other selected mesh to the active mesh, using matching vertex groups as masks" 1122 bl_options = {'REGISTER', 'UNDO'} 1123 1124 step_size = bpy.props.IntProperty(name="Step Size (low = quality, high = speed)", default=1, min=1, max=100, soft_min=1, soft_max=10, step=1) 1125 extend_range = bpy.props.FloatProperty(name="Range magnification", default=1.1, min=1.0001, max=5.0, soft_min=1.0001, soft_max=5.0, step=10, precision=2) 1126 1127 near_vert_data = [] 1128 near_vert_multi_total = [] 1129 my_iter = None 1130 1131 matched_vgroups = [] 1132 using_vgroups = bpy.props.CollectionProperty(type=common.CNV_SelectorItem) 1133 active_vgroup = bpy.props.IntProperty(name="Active Vertex Group") 1134 1135 #armature = bpy.props.PointerProperty(type=bpy.types.ID) 1136 #bone_data_ob = bpy.props.PointerProperty(type=bpy.types.ID) 1137 armature = None 1138 bone_data_ob = None 1139 1140 @classmethod 1141 def poll(cls, context): 1142 obs = context.selected_objects 1143 if len(obs) == 2: 1144 active_ob = context.active_object 1145 for ob in obs: 1146 if ob.type != 'MESH': 1147 return False 1148 if ob.data.shape_keys and ob.name != active_ob.name: 1149 return True 1150 return False 1151 1152 def draw(self, context): 1153 CNV_OT_precision_shape_key_transfer.draw(self, context) 1154 target_ob, source_ob = common.get_target_and_source_ob(context) 1155 1156 self.matched_vgroups = common.values_of_matched_keys(target_ob.vertex_groups, source_ob.vertex_groups) 1157 print(f_("len(matched) = {length}", length=len(self.matched_vgroups))) 1158 armature_ob = target_ob.find_armature() or source_ob.find_armature() 1159 self.armature = armature_ob and armature_ob.data 1160 self.bone_data_ob = (target_ob.get("LocalBoneData:0") and target_ob) or (source_ob.get("LocalBoneData:0") and source_ob) or None 1161 1162 for index, vgs in enumerate(self.matched_vgroups): 1163 target_vg, source_vg = vgs 1164 vg_name = target_vg.name 1165 if self.using_vgroups.get(vg_name): 1166 continue 1167 1168 print(index, vg_name) 1169 new_prop = self.using_vgroups.add() 1170 new_prop.name = vg_name 1171 new_prop.index = index 1172 new_prop.value = True 1173 1174 self.layout.template_list("CNV_UL_vgroups_selector", "", self, "using_vgroups", self, "active_vgroup") 1175 self.layout.label(text="Show filters", icon='FILE_PARENT') 1176 1177 def prepare(self, context): 1178 shape_key_transfer_op.prepare(self, context) 1179 1180 target_me = self.target_ob.data 1181 source_me = self.source_ob.data 1182 1183 self.matched_vgroups = [ ( self.target_ob.vertex_groups.get(vg.name), self.source_ob.vertex_groups.get(vg.name) ) for vg in self.using_vgroups] 1184 1185 context.window_manager.progress_begin(0, len(target_me.vertices)) 1186 progress_reduce = len(target_me.vertices) // 200 + 1 1187 self.near_vert_data = [] 1188 self.near_vert_multi_total = [] 1189 near_vert_multi_total_append = self.near_vert_multi_total.append 1190 1191 for vert in target_me.vertices: 1192 new_vert_data = [] 1193 self.near_vert_data.append(new_vert_data) 1194 self.near_vert_data_append = new_vert_data.append 1195 1196 target_co = compat.mul(self.target_ob.matrix_world, vert.co) #vert.co 1197 mini_co, mini_index, mini_dist = self.kd.find(target_co) 1198 radius = mini_dist * self.extend_range 1199 diff_radius = radius - mini_dist 1200 1201 multi_total = 0.0 1202 for co, index, dist in self.kd.find_range(target_co, radius): 1203 if 0 < diff_radius: 1204 multi = (diff_radius - (dist - mini_dist)) / diff_radius 1205 else: 1206 multi = 1.0 1207 1208 avg_weight_match = 0 1209 for target_vg, source_vg in self.matched_vgroups: 1210 target_weight = 0 1211 try: 1212 target_weight = target_vg.weight(vert.index) 1213 except: 1214 pass 1215 source_weight = 0 1216 try: 1217 source_weight = source_vg.weight(index) 1218 except: 1219 pass 1220 avg_weight_match += -abs(source_weight - target_weight) + target_weight 1221 if avg_weight_match > 1: 1222 avg_weight_match = 1 1223 elif avg_weight_match < 0: 1224 avg_weight_match = 0 1225 1226 multi *= avg_weight_match 1227 self.near_vert_data_append((index, multi)) 1228 multi_total += multi 1229 near_vert_multi_total_append(multi_total) 1230 1231 if vert.index % progress_reduce == 0: 1232 context.window_manager.progress_update(vert.index) 1233 context.window_manager.progress_end() 1234 1235 self.my_iter = iter(transfer_shape_key_iter(self.target_ob, self.source_ob, binded_shape_key=self.binded_shape_key)) 1236 1237 #self.source_raw_data = numpy.ndarray(shape=(len(source_me.vertices), 3), dtype=float, order='C') 1238 #self.target_raw_data = numpy.ndarray(shape=(len(target_me.vertices), 3), dtype=float, order='C') 1239 1240 #self.binded_raw_data = numpy.ndarray(shape=len(self.source_bind_data)*3, dtype=float, order='C') 1241 #self.source_bind_data.foreach_get('co', self.binded_raw_data) 1242 #self.binded_raw_data.resize(self.binded_raw_data.size//3, 3) 1243 1244 context.window_manager.progress_begin(0, len(source_me.shape_keys.key_blocks) * len(target_me.vertices)) 1245 context.window_manager.progress_update(0) 1246 1247 invoke = CNV_OT_precision_shape_key_transfer.invoke 1248 loop = CNV_OT_precision_shape_key_transfer.loop 1249 cleanup = CNV_OT_precision_shape_key_transfer.cleanup 1250 1251 1252 1253@compat.BlRegister() 1254class CNV_OT_multiply_shape_key(bpy.types.Operator): 1255 bl_idname = 'object.multiply_shape_key' 1256 bl_label = "シェイプキーの変形に乗算" 1257 bl_description = "シェイプキーの変形に数値を乗算し、変形の強度を増減させます" 1258 bl_options = {'REGISTER', 'UNDO'} 1259 1260 multi = bpy.props.FloatProperty(name="倍率", description="シェイプキーの拡大率です", default=1.1, min=-10, max=10, soft_min=-10, soft_max=10, step=10, precision=2) 1261 items = [ 1262 ('ACTIVE', "アクティブのみ", "", 'HAND', 1), 1263 ('UP', "アクティブより上", "", 'TRIA_UP_BAR', 2), 1264 ('DOWN', "アクティブより下", "", 'TRIA_DOWN_BAR', 3), 1265 ('ALL', "全て", "", 'ARROW_LEFTRIGHT', 4), 1266 ] 1267 mode = bpy.props.EnumProperty(items=items, name="対象", default='ACTIVE') 1268 1269 @classmethod 1270 def poll(cls, context): 1271 if context.active_object: 1272 ob = context.active_object 1273 if ob.type == 'MESH': 1274 return ob.active_shape_key 1275 return False 1276 1277 def invoke(self, context, event): 1278 return context.window_manager.invoke_props_dialog(self) 1279 1280 def draw(self, context): 1281 self.layout.prop(self, 'multi', icon='ARROW_LEFTRIGHT') 1282 self.layout.prop(self, 'mode', icon='VIEWZOOM') 1283 1284 def execute(self, context): 1285 ob = context.active_object 1286 me = ob.data 1287 shape_keys = me.shape_keys 1288 pre_mode = ob.mode 1289 bpy.ops.object.mode_set(mode='OBJECT') 1290 1291 target_shapes = [] 1292 if self.mode == 'ACTIVE': 1293 target_shapes.append(ob.active_shape_key) 1294 elif self.mode == 'UP': 1295 for index, key_block in enumerate(shape_keys.key_blocks): 1296 if index <= ob.active_shape_key_index: 1297 target_shapes.append(key_block) 1298 elif self.mode == 'UP': 1299 for index, key_block in enumerate(shape_keys.key_blocks): 1300 if ob.active_shape_key_index <= index: 1301 target_shapes.append(key_block) 1302 elif self.mode == 'ALL': 1303 for key_block in shape_keys.key_blocks: 1304 target_shapes.append(key_block) 1305 1306 for shape in target_shapes: 1307 data = shape.data 1308 for i, vert in enumerate(me.vertices): 1309 diff = data[i].co - vert.co 1310 diff *= self.multi 1311 data[i].co = vert.co + diff 1312 bpy.ops.object.mode_set(mode=pre_mode) 1313 return {'FINISHED'} 1314 1315 1316 1317@compat.BlRegister() 1318class CNV_OT_blur_shape_key(bpy.types.Operator): 1319 bl_idname = 'object.blur_shape_key' 1320 bl_label = "シェイプキーぼかし" 1321 bl_description = "アクティブ、もしくは全てのシェイプキーをぼかします" 1322 bl_options = {'REGISTER', 'UNDO'} 1323 1324 items = [ 1325 ('ACTIVE', "アクティブのみ", "", 'HAND', 1), 1326 ('UP', "アクティブより上", "", 'TRIA_UP_BAR', 2), 1327 ('DOWN', "アクティブより下", "", 'TRIA_DOWN_BAR', 3), 1328 ('ALL', "全て", "", 'ARROW_LEFTRIGHT', 4), 1329 ] 1330 target = bpy.props.EnumProperty(items=items, name="対象", default='ACTIVE') 1331 radius = bpy.props.FloatProperty(name="範囲倍率", default=3, min=0.1, max=50, soft_min=0.1, soft_max=50, step=50, precision=2) 1332 strength = bpy.props.IntProperty(name="強さ", default=1, min=1, max=10, soft_min=1, soft_max=10) 1333 items = [ 1334 ('BOTH', "増減両方", "", 'AUTOMERGE_ON', 1), 1335 ('ADD', "増加のみ", "", 'TRIA_UP', 2), 1336 ('SUB', "減少のみ", "", 'TRIA_DOWN', 3), 1337 ] 1338 effect = bpy.props.EnumProperty(items=items, name="ぼかし効果", default='BOTH') 1339 items = [ 1340 ('LINER', "ライナー", "", 'LINCURVE', 1), 1341 ('SMOOTH1', "スムーズ1", "", 'SMOOTHCURVE', 2), 1342 ('SMOOTH2', "スムーズ2", "", 'SMOOTHCURVE', 3), 1343 ] 1344 blend = bpy.props.EnumProperty(items=items, name="減衰タイプ", default='LINER') 1345 1346 @classmethod 1347 def poll(cls, context): 1348 ob = context.active_object 1349 if ob and ob.type == 'MESH': 1350 me = ob.data 1351 return me.shape_keys 1352 return False 1353 1354 def invoke(self, context, event): 1355 return context.window_manager.invoke_props_dialog(self) 1356 1357 def draw(self, context): 1358 self.layout.prop(self, 'target', icon='VIEWZOOM') 1359 self.layout.prop(self, 'radius', icon='RADIOBUT_OFF') 1360 self.layout.prop(self, 'strength', icon='ARROW_LEFTRIGHT') 1361 self.layout.prop(self, 'effect', icon='BRUSH_BLUR') 1362 self.layout.prop(self, 'blend', icon='IPO_SINE') 1363 1364 def execute(self, context): 1365 ob = context.active_object 1366 me = ob.data 1367 1368 pre_mode = ob.mode 1369 bpy.ops.object.mode_set(mode='OBJECT') 1370 1371 bm = bmesh.new() 1372 bm.from_mesh(me) 1373 edge_lengths = [e.calc_length() for e in bm.edges] 1374 bm.free() 1375 1376 edge_lengths.sort() 1377 average_edge_length = sum(edge_lengths) / len(edge_lengths) 1378 center_index = int((len(edge_lengths) - 1) / 2.0) 1379 average_edge_length = (average_edge_length + edge_lengths[center_index]) / 2 1380 radius = average_edge_length * self.radius 1381 1382 context.window_manager.progress_begin(0, len(me.vertices)) 1383 progress_reduce = len(me.vertices) // 200 + 1 1384 near_vert_data = [] 1385 kd = mathutils.kdtree.KDTree(len(me.vertices)) 1386 for vert in me.vertices: 1387 kd.insert(vert.co.copy(), vert.index) 1388 kd.balance() 1389 for vert in me.vertices: 1390 near_vert_data.append([]) 1391 near_vert_data_append = near_vert_data[-1].append 1392 for co, index, dist in kd.find_range(vert.co, radius): 1393 multi = (radius - dist) / radius 1394 if self.blend == 'SMOOTH1': 1395 multi = common.in_out_quad_blend(multi) 1396 elif self.blend == 'SMOOTH2': 1397 multi = common.bezier_blend(multi) 1398 near_vert_data_append((index, multi)) 1399 if vert.index % progress_reduce == 0: 1400 context.window_manager.progress_update(vert.index) 1401 context.window_manager.progress_end() 1402 1403 target_shape_keys = [] 1404 if self.target == 'ACTIVE': 1405 target_shape_keys.append(ob.active_shape_key) 1406 elif self.target == 'UP': 1407 for index, shape_key in enumerate(me.shape_keys.key_blocks): 1408 if index <= ob.active_shape_key_index: 1409 target_shape_keys.append(shape_key) 1410 elif self.target == 'DOWN': 1411 for index, shape_key in enumerate(me.shape_keys.key_blocks): 1412 if ob.active_shape_key_index <= index: 1413 target_shape_keys.append(shape_key) 1414 elif self.target == 'ALL': 1415 for index, shape_key in enumerate(me.shape_keys.key_blocks): 1416 target_shape_keys.append(shape_key) 1417 1418 progress_total = len(target_shape_keys) * self.strength * len(me.vertices) 1419 context.window_manager.progress_begin(0, progress_total) 1420 progress_reduce = progress_total // 200 + 1 1421 progress_count = 0 1422 for strength_count in range(self.strength): 1423 for shape_key in target_shape_keys: 1424 1425 shapes = [] 1426 shapes_append = shapes.append 1427 for index, vert in enumerate(me.vertices): 1428 co = shape_key.data[index].co - vert.co 1429 shapes_append(co) 1430 1431 for vert in me.vertices: 1432 1433 target_shape = shapes[vert.index] 1434 1435 total_shape = mathutils.Vector() 1436 total_multi = 0.0 1437 for index, multi in near_vert_data[vert.index]: 1438 co = shapes[index] 1439 if self.effect == 'ADD': 1440 if target_shape.length <= co.length: 1441 total_shape += co * multi 1442 total_multi += multi 1443 elif self.effect == 'SUB': 1444 if co.length <= target_shape.length: 1445 total_shape += co * multi 1446 total_multi += multi 1447 else: 1448 total_shape += co * multi 1449 total_multi += multi 1450 1451 if 0 < total_multi: 1452 average_shape = total_shape / total_multi 1453 else: 1454 average_shape = mathutils.Vector() 1455 1456 shape_key.data[vert.index].co = vert.co + average_shape 1457 1458 progress_count += 1 1459 if progress_count % progress_reduce == 0: 1460 context.window_manager.progress_update(progress_count) 1461 1462 context.window_manager.progress_end() 1463 bpy.ops.object.mode_set(mode=pre_mode) 1464 return {'FINISHED'} 1465 1466 1467 1468@compat.BlRegister() 1469class CNV_OT_change_base_shape_key(bpy.types.Operator): 1470 bl_idname = 'object.change_base_shape_key' 1471 bl_label = "このシェイプキーをベースに" 1472 bl_description = "アクティブなシェイプキーを他のシェイプキーのベースにします" 1473 bl_options = {'REGISTER', 'UNDO'} 1474 1475 is_deform_mesh = bpy.props.BoolProperty(name="素メッシュを調整", default=True) 1476 is_deform_other_shape = bpy.props.BoolProperty(name="他シェイプを調整", default=True) 1477 1478 @classmethod 1479 def poll(cls, context): 1480 ob = context.active_object 1481 return ob and ob.type == 'MESH' and 1 <= ob.active_shape_key_index 1482 1483 def invoke(self, context, event): 1484 return context.window_manager.invoke_props_dialog(self) 1485 1486 def draw(self, context): 1487 self.layout.prop(self, 'is_deform_mesh', icon='MESH_DATA') 1488 self.layout.prop(self, 'is_deform_other_shape', icon='SHAPEKEY_DATA') 1489 1490 def execute(self, context): 1491 ob = context.active_object 1492 me = ob.data 1493 1494 pre_mode = ob.mode 1495 bpy.ops.object.mode_set(mode='OBJECT') 1496 1497 target_shape_key = ob.active_shape_key 1498 old_shape_key = me.shape_keys.key_blocks[0] 1499 1500 # TOP指定でindex=1になるケースは、さらにもう一度UP 1501 bpy.ops.object.shape_key_move(type='TOP') 1502 if ob.active_shape_key_index == 1: 1503 bpy.ops.object.shape_key_move(type='UP') 1504 1505 target_shape_key.relative_key = target_shape_key 1506 old_shape_key.relative_key = target_shape_key 1507 1508 if self.is_deform_mesh: 1509 for vert in me.vertices: 1510 vert.co = target_shape_key.data[vert.index].co.copy() 1511 1512 if self.is_deform_other_shape: 1513 for shape_key in me.shape_keys.key_blocks: 1514 if shape_key.name == target_shape_key.name or shape_key.name == old_shape_key.name: 1515 continue 1516 if shape_key.relative_key.name == old_shape_key.name: 1517 shape_key.relative_key = target_shape_key 1518 for vert in me.vertices: 1519 diff_co = target_shape_key.data[vert.index].co - old_shape_key.data[vert.index].co 1520 shape_key.data[vert.index].co = shape_key.data[vert.index].co + diff_co 1521 1522 bpy.ops.object.mode_set(mode=pre_mode) 1523 return {'FINISHED'} 1524 1525 1526 1527@compat.BlRegister() 1528class CNV_OT_copy_shape_key_values(bpy.types.Operator): 1529 bl_idname = 'object.copy_shape_key_values' 1530 bl_label = "Copy shape key values" 1531 bl_description = "Copy the shape key values from the other selected mesh" 1532 bl_options = {'REGISTER', 'UNDO'} 1533 1534 use_drivers = bpy.props.BoolProperty(name="Apply as drivers", default=False) 1535 1536 @classmethod 1537 def poll(cls, context): 1538 obs = context.selected_objects 1539 if len(obs) == 2: 1540 active_ob = context.active_object 1541 for ob in obs: 1542 if ob.type != 'MESH': 1543 return False 1544 if ob.data.shape_keys and ob.name != active_ob.name: 1545 return True 1546 return False 1547 1548 def invoke(self, context, event): 1549 return context.window_manager.invoke_props_dialog(self) 1550 1551 def draw(self, context): 1552 self.layout.prop(self, 'use_drivers', icon='DRIVER') 1553 1554 def execute(self, context): 1555 target_ob, source_ob = common.get_target_and_source_ob(context) 1556 1557 source_sks = source_ob.data.shape_keys.key_blocks 1558 target_sks = target_ob.data.shape_keys.key_blocks 1559 for source_sk, target_sk in common.values_of_matched_keys(source_sks, target_sks): 1560 if not self.use_drivers: 1561 target_sk.value = source_sk.value 1562 else: 1563 driver = target_sk.driver_add('value').driver 1564 driver.type = 'AVERAGE' 1565 1566 driver_var = driver.variables.new() if len(driver.variables) < 1 else driver.variables[0] 1567 driver_var.type = 'SINGLE_PROP' 1568 1569 driver_target = driver_var.targets[0] 1570 driver_target.id_type = 'KEY' 1571 driver_target.id = source_sk.id_data 1572 driver_target.data_path = source_sk.path_from_id('value') 1573 1574 1575 return {'FINISHED'}
class
transfer_shape_key_iter:
47class transfer_shape_key_iter: 48 index = -1 49 50 target_ob = None 51 source_ob = None 52 53 binded_shape_key = None 54 binded_shape_key_data = None 55 56 #target_mat = None 57 #source_mat = None 58 59 source_iter = None 60 61 source_shape_key_data = None 62 target_shape_key_data = None 63 64 def __init__(self, target_ob, source_ob, binded_shape_key=None): 65 self.target_ob = target_ob 66 self.source_ob = source_ob 67 self.binded_shape_key = binded_shape_key or self.source_ob.data.shape_keys.key_blocks[0] 68 69 def __iter__(self): 70 self.index = -1 71 if self.source_iter: 72 self.source_iter = iter(self.source_iter) 73 74 #self.target_mat = self.target_ob.matrix_world 75 #self.source_mat = self.source_ob.matrix_world 76 77 if self.source_ob and self.source_ob.data.shape_keys: 78 binded_index = self.source_ob.data.shape_keys.key_blocks.find(self.binded_shape_key.name) 79 #self.binded_shape_key_data = bmesh.new(use_operators=False) 80 #self.binded_shape_key_data.from_mesh(self.source_ob.data, use_shape_key=True, shape_key_index=binded_index) 81 #self.binded_shape_key_data.verts.ensure_lookup_table() 82 self.binded_shape_key_data = self.binded_shape_key.data 83 self.source_iter = iter(self.source_ob.data.shape_keys.key_blocks) 84 return self 85 86 def __next__(self): 87 target_me = self.target_ob.data 88 source_me = self.source_ob.data 89 90 target_shape_key = None 91 source_shape_key = next(self.source_iter, None) 92 if not source_shape_key: 93 raise StopIteration 94 95 self.index += 1 96 97 if target_me.shape_keys: 98 if source_shape_key.name in target_me.shape_keys.key_blocks: 99 target_shape_key = target_me.shape_keys.key_blocks[source_shape_key.name] 100 else: 101 target_shape_key = self.target_ob.shape_key_add(name=source_shape_key.name, from_mix=False) 102 else: 103 target_shape_key = self.target_ob.shape_key_add(name=source_shape_key.name, from_mix=False) 104 105 relative_key_name = source_shape_key.relative_key.name 106 107 rel_key = target_me.shape_keys.key_blocks.get(relative_key_name) 108 if rel_key: 109 target_shape_key.relative_key = rel_key 110 111 if not self.target_ob.active_shape_key_index == 0: 112 target_me.shape_keys.key_blocks[self.target_ob.active_shape_key_index].value = 0.0 113 if not self.source_ob.active_shape_key_index == 0: 114 source_me.shape_keys.key_blocks[self.source_ob.active_shape_key_index].value = 0.0 115 116 target_index = target_me.shape_keys.key_blocks.find(target_shape_key.name) 117 source_index = source_me.shape_keys.key_blocks.find(source_shape_key.name) 118 119 self.target_ob.active_shape_key_index = target_index 120 self.source_ob.active_shape_key_index = source_index 121 122 target_shape_key.value = 1.0 123 source_shape_key.value = 1.0 124 125 #source_shape_key_data = [compat.mul3(self.source_mat, source_shape_key.data[v.index].co, self.target_mat) - compat.mul3(self.source_mat, source_me.vertices[v.index].co, self.target_mat) for v in source_me.vertices] 126 #for i, v in enumerate(self.source_bind_data): 127 # shape_co = compat.mul3(self.source_mat, source_shape_key.data[i].co, self.target_mat) 128 # mesh_co = compat.mul3(self.source_mat, self.source_bind_data[i].co, self.target_mat) 129 # self.source_shape_key_data[i] = shape_co - mesh_co 130 131 #self.target_shape_key_data = bmesh.from_edit_mesh(self.target_ob.data) 132 #self.source_shape_key_data = bmesh.from_edit_mesh(self.source_ob.data) 133 #self.source_shape_key_data = bmesh.new(use_operators=False) 134 #self.source_shape_key_data.from_mesh(self.source_ob.data, use_shape_key=True, shape_key_index=source_index) 135 136 #self.target_shape_key_data.verts.ensure_lookup_table() 137 #self.source_shape_key_data.verts.ensure_lookup_table() 138 139 self.source_shape_key_data = source_shape_key.data 140 self.target_shape_key_data = target_shape_key.data 141 142 143 return self.index, target_shape_key, self.binded_shape_key_data, self.source_shape_key_data, self.target_shape_key_data 144 145 # update() will free resources for the current iteration of a loop, but not the loop itself. 146 def update(self, destructive=False): 147 pass 148 #if self.target_shape_key_data and self.target_shape_key_data.is_valid: 149 #bmesh.update_edit_mesh(self.target_ob.data, loop_triangles=True, destructive=destructive) 150 #self.target_shape_key_data.free() 151 #pass 152 153 #if self.source_shape_key_data and self.source_shape_key_data.is_valid: 154 #bmesh.update_edit_mesh(self.source_ob.data, loop_triangles=True, destructive=destructive) 155 #self.source_shape_key_data.free() 156 #pass 157 158 # free() will release all resources for the loop, leaving it unable to run unless iter() is used again. 159 def free(self, destructive=False): 160 pass 161 #self.update() 162 #if self.binded_shape_key_data and self.binded_shape_key_data.is_valid: 163 #bmesh.update_edit_mesh(self.source_ob.data, loop_triangles=True, destructive=destructive) 164 #self.binded_shape_key_data.free()
def
update(self, destructive=False):
146 def update(self, destructive=False): 147 pass 148 #if self.target_shape_key_data and self.target_shape_key_data.is_valid: 149 #bmesh.update_edit_mesh(self.target_ob.data, loop_triangles=True, destructive=destructive) 150 #self.target_shape_key_data.free() 151 #pass 152 153 #if self.source_shape_key_data and self.source_shape_key_data.is_valid: 154 #bmesh.update_edit_mesh(self.source_ob.data, loop_triangles=True, destructive=destructive) 155 #self.source_shape_key_data.free() 156 #pass
class
shape_key_transfer_op:
168class shape_key_transfer_op: 169 is_first_remove_all = bpy.props.BoolProperty(name="最初に全シェイプキーを削除", default=True) 170 is_remove_empty = bpy.props.BoolProperty(name="変形のないシェイプキーを削除", default=True) 171 is_bind_current_mix = bpy.props.BoolProperty(name="Bind to current source mix", default=False) 172 subdivide_number = bpy.props.IntProperty (name="参照元の分割", default=1, min=0, max=10, soft_min=0, soft_max=10) 173 174 target_ob = None 175 source_ob = None 176 og_source_ob = None 177 178 _start_time = 0 179 _timer = None 180 181 is_finished = False 182 is_canceled = False 183 184 pre_mode = None 185 pre_selected = None 186 187 binded_shape_key = None 188 kd = None 189 is_shapeds = {} 190 191 192 def draw(self, context): 193 self.layout.prop(self, 'is_first_remove_all', icon='ERROR' ) 194 self.layout.prop(self, 'subdivide_number' , icon='LATTICE_DATA' ) 195 self.layout.prop(self, 'is_remove_empty' , icon='X' ) 196 self.layout.prop(self, 'is_bind_current_mix', icon='AUTOMERGE_OFF') 197 198 def execute(self, context): 199 self.pre_selected = list(context.selected_objects) 200 self.target_ob, self.source_ob, self.og_source_ob = common.get_target_and_source_ob(context, copySource=True) 201 202 compat.set_hide(self.og_source_ob, True) 203 204 self._start_time = time.time() 205 self._timer = None 206 self.is_finished = False 207 self.is_canceled = False 208 self.pre_mode = self.target_ob.mode 209 210 self.binded_shape_key = None 211 self.source_bind_data = None 212 self.kd = None 213 self.is_shapeds = {} 214 215 bpy.ops.object.mode_set(mode='OBJECT') 216 217 try: 218 compat.link(context.scene, self.source_ob) 219 self.prepare(context) 220 221 except: 222 self.is_canceled = True 223 traceback.print_exc() 224 self.report(type={'ERROR'}, message="Error while preparing shapekey transfer.") 225 self.cancel(context) 226 return {'FINISHED'} 227 228 else: 229 wm = context.window_manager 230 self._timer = wm.event_timer_add(1.0/60.0, window=context.window) 231 wm.modal_handler_add(self) 232 self.report(type={'INFO'}, message="Press ESC to cancel shape key transfer") 233 234 compat.set_active(context, self.target_ob) 235 return {'RUNNING_MODAL'} 236 237 def modal(self, context, event): 238 if event.type == 'ESC': 239 self.is_canceled = 'WARNING' 240 if not event.type == 'TIMER': 241 return {'PASS_THROUGH'} 242 243 #print("Run Modal") 244 245 if self.is_canceled: 246 #print("Canceled") 247 try: 248 self.cancel(context) 249 except: 250 traceback.print_exc() 251 self.report(type={'ERROR'}, message="Error while canceling shapekey transfer.") 252 finally: 253 return {'FINISHED'} 254 255 if not self.is_canceled and not self.is_finished: 256 #print("Loop") 257 try: 258 self.is_finished = self.loop(context) 259 except: 260 self.is_canceled = True 261 traceback.print_exc() 262 self.report(type={'ERROR'}, message="Error while performing shapekey transfer.") 263 finally: 264 return {'PASS_THROUGH'} 265 266 else: 267 #print("Finish") 268 try: 269 self.finish(context) 270 except: 271 self.is_canceled = True 272 traceback.print_exc() 273 self.report(type={'ERROR'}, message="Error while finishing shapekey transfer.") 274 return {'PASS_THROUGH'} 275 else: 276 self.cleanup(context) 277 diff_time = time.time() - self._start_time 278 self.report(type={'INFO'}, message=f_tip_("{:.2f} Seconds", diff_time)) 279 return {'FINISHED'} 280 281 def prepare(self, context): 282 target_ob = self.target_ob 283 source_ob = self.source_ob 284 285 for ob in self.pre_selected: 286 compat.set_select(ob, False) 287 288 compat.set_active(context, source_ob) 289 #compat.set_select(source_og_ob, False) 290 #compat.set_select(target_ob, False) 291 292 # transform source's mesh now so theres no need to worry about it later 293 matrix_source_to_target = compat.mul(target_ob.matrix_world.inverted_safe(), source_ob.matrix_world) 294 source_ob.data.transform(matrix_source_to_target, shape_keys=True) 295 source_ob.matrix_world = target_ob.matrix_world 296 297 bpy.ops.object.mode_set(mode='EDIT') 298 bpy.ops.mesh.reveal() 299 bpy.ops.mesh.select_all(action='SELECT') 300 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) 301 bpy.ops.object.mode_set(mode='OBJECT') 302 303 if self.is_first_remove_all: 304 try: 305 target_ob.active_shape_key_index = 1 306 bpy.ops.object.shape_key_remove(all=True) 307 except: 308 pass 309 finally: 310 target_ob.active_shape_key_index = 0 311 else: 312 if target_ob.data.shape_keys: 313 for i, key in enumerate(target_ob.data.shape_keys.key_blocks): 314 if i == 0: 315 continue 316 else: 317 key.value = 0.0 318 target_ob.active_shape_key_index = 0 319 320 if self.is_bind_current_mix: 321 source_basis = source_ob.data.shape_keys.key_blocks[0] 322 323 old_basis = target_ob.data.shape_keys and next(iter(target_ob.data.shape_keys.key_blocks), False) or target_ob.shape_key_add() 324 old_basis.name = "__old_basis__" + old_basis.name 325 new_basis = target_ob.shape_key_add(name=source_basis.name) 326 327 self.binded_shape_key = source_ob.shape_key_add(name="__bind_shape_key", from_mix=True) 328 self.source_bind_data = self.binded_shape_key.data 329 330 compat.set_active(context, target_ob) 331 target_ob.active_shape_key_index = target_ob.data.shape_keys.key_blocks.find(new_basis.name) 332 # TOP指定でindex=1になるケースは、さらにもう一度UP 333 bpy.ops.object.shape_key_move(type='TOP') 334 if target_ob.active_shape_key_index == 1: 335 bpy.ops.object.shape_key_move(type='UP') 336 337 old_basis.relative_key = new_basis 338 339 source_ob.active_shape_key_index = source_ob.data.shape_keys.key_blocks.find(self.binded_shape_key.name) 340 341 else: 342 source_ob.active_shape_key_index = 0 343 self.source_bind_data = source_ob.data.vertices 344 345 #print(len(source_ob.data.vertices), len(self.source_bind_data)) 346 self.kd = mathutils.kdtree.KDTree(len(self.source_bind_data)) 347 for index, vert in enumerate(self.source_bind_data): 348 co = compat.mul(source_ob.matrix_world, vert.co) 349 self.kd.insert(co, index) 350 self.kd.balance() 351 352 for i, key in enumerate(source_ob.data.shape_keys.key_blocks): 353 if i == 0: 354 continue 355 else: 356 key.value = 0.0 357 358 def finish(self, context): 359 target_me = self.target_ob.data 360 361 if self.is_remove_empty: 362 for source_shape_key_name, is_shaped in reversed( list(self.is_shapeds.items()) ): 363 if not is_shaped: 364 target_shape_key = target_me.shape_keys.key_blocks.get(source_shape_key_name) 365 if not target_shape_key: 366 continue 367 key_blocks_values = target_me.shape_keys.key_blocks.values() 368 is_used = False 369 for key in key_blocks_values: 370 if key.relative_key == target_shape_key: 371 is_used = True 372 break 373 if not is_used: 374 self.target_ob.shape_key_remove(target_shape_key) 375 376 self.target_ob.active_shape_key_index = 0 377 378 def cancel(self, context): 379 report_type = (self.is_canceled == 'WARNING' and 'WARNING') or 'ERROR' 380 self.report(type={report_type}, message="Shape key transfer canceled. Results may not be as expected. Use Undo / Ctrl Z to revert changes") 381 self.cleanup(context) 382 383 def cleanup(self, context): 384 #compat.set_select(source_original_ob, True) 385 if self.target_ob: 386 #compat.set_select(target_ob, True) 387 compat.set_active(context, self.target_ob) 388 389 if self.og_source_ob: 390 compat.set_hide(self.og_source_ob, False) 391 392 source_me = self.source_ob and self.source_ob.data 393 if source_me: 394 common.remove_data([self.source_ob, source_me]) 395 elif self.source_ob: 396 common.remove_data([self.source_ob]) 397 398 if self._timer: 399 wm = context.window_manager 400 wm.event_timer_remove(self._timer) 401 402 if self.pre_mode: 403 bpy.ops.object.mode_set(mode=self.pre_mode) 404 405 if self.pre_selected: 406 for ob in self.pre_selected: 407 compat.set_select(ob, True) 408 409 self.target_ob = None 410 self.source_ob = None 411 412 self._timer = None 413 414 self.pre_mode = None 415 self.pre_selected = None 416 417 self.binded_shape_key = None 418 self.kd = None 419 self.is_shapeds = {}
is_first_remove_all =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': '最初に全シェイプキーを削除', 'default': True, 'attr': 'is_first_remove_all'}>
is_remove_empty =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': '変形のないシェイプキーを削除', 'default': True, 'attr': 'is_remove_empty'}>
is_bind_current_mix =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Bind to current source mix', 'default': False, 'attr': 'is_bind_current_mix'}>
subdivide_number =
<_PropertyDeferred, <built-in function IntProperty>, {'name': '参照元の分割', 'default': 1, 'min': 0, 'max': 10, 'soft_min': 0, 'soft_max': 10, 'attr': 'subdivide_number'}>
def
execute(self, context):
198 def execute(self, context): 199 self.pre_selected = list(context.selected_objects) 200 self.target_ob, self.source_ob, self.og_source_ob = common.get_target_and_source_ob(context, copySource=True) 201 202 compat.set_hide(self.og_source_ob, True) 203 204 self._start_time = time.time() 205 self._timer = None 206 self.is_finished = False 207 self.is_canceled = False 208 self.pre_mode = self.target_ob.mode 209 210 self.binded_shape_key = None 211 self.source_bind_data = None 212 self.kd = None 213 self.is_shapeds = {} 214 215 bpy.ops.object.mode_set(mode='OBJECT') 216 217 try: 218 compat.link(context.scene, self.source_ob) 219 self.prepare(context) 220 221 except: 222 self.is_canceled = True 223 traceback.print_exc() 224 self.report(type={'ERROR'}, message="Error while preparing shapekey transfer.") 225 self.cancel(context) 226 return {'FINISHED'} 227 228 else: 229 wm = context.window_manager 230 self._timer = wm.event_timer_add(1.0/60.0, window=context.window) 231 wm.modal_handler_add(self) 232 self.report(type={'INFO'}, message="Press ESC to cancel shape key transfer") 233 234 compat.set_active(context, self.target_ob) 235 return {'RUNNING_MODAL'}
def
modal(self, context, event):
237 def modal(self, context, event): 238 if event.type == 'ESC': 239 self.is_canceled = 'WARNING' 240 if not event.type == 'TIMER': 241 return {'PASS_THROUGH'} 242 243 #print("Run Modal") 244 245 if self.is_canceled: 246 #print("Canceled") 247 try: 248 self.cancel(context) 249 except: 250 traceback.print_exc() 251 self.report(type={'ERROR'}, message="Error while canceling shapekey transfer.") 252 finally: 253 return {'FINISHED'} 254 255 if not self.is_canceled and not self.is_finished: 256 #print("Loop") 257 try: 258 self.is_finished = self.loop(context) 259 except: 260 self.is_canceled = True 261 traceback.print_exc() 262 self.report(type={'ERROR'}, message="Error while performing shapekey transfer.") 263 finally: 264 return {'PASS_THROUGH'} 265 266 else: 267 #print("Finish") 268 try: 269 self.finish(context) 270 except: 271 self.is_canceled = True 272 traceback.print_exc() 273 self.report(type={'ERROR'}, message="Error while finishing shapekey transfer.") 274 return {'PASS_THROUGH'} 275 else: 276 self.cleanup(context) 277 diff_time = time.time() - self._start_time 278 self.report(type={'INFO'}, message=f_tip_("{:.2f} Seconds", diff_time)) 279 return {'FINISHED'}
def
prepare(self, context):
281 def prepare(self, context): 282 target_ob = self.target_ob 283 source_ob = self.source_ob 284 285 for ob in self.pre_selected: 286 compat.set_select(ob, False) 287 288 compat.set_active(context, source_ob) 289 #compat.set_select(source_og_ob, False) 290 #compat.set_select(target_ob, False) 291 292 # transform source's mesh now so theres no need to worry about it later 293 matrix_source_to_target = compat.mul(target_ob.matrix_world.inverted_safe(), source_ob.matrix_world) 294 source_ob.data.transform(matrix_source_to_target, shape_keys=True) 295 source_ob.matrix_world = target_ob.matrix_world 296 297 bpy.ops.object.mode_set(mode='EDIT') 298 bpy.ops.mesh.reveal() 299 bpy.ops.mesh.select_all(action='SELECT') 300 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) 301 bpy.ops.object.mode_set(mode='OBJECT') 302 303 if self.is_first_remove_all: 304 try: 305 target_ob.active_shape_key_index = 1 306 bpy.ops.object.shape_key_remove(all=True) 307 except: 308 pass 309 finally: 310 target_ob.active_shape_key_index = 0 311 else: 312 if target_ob.data.shape_keys: 313 for i, key in enumerate(target_ob.data.shape_keys.key_blocks): 314 if i == 0: 315 continue 316 else: 317 key.value = 0.0 318 target_ob.active_shape_key_index = 0 319 320 if self.is_bind_current_mix: 321 source_basis = source_ob.data.shape_keys.key_blocks[0] 322 323 old_basis = target_ob.data.shape_keys and next(iter(target_ob.data.shape_keys.key_blocks), False) or target_ob.shape_key_add() 324 old_basis.name = "__old_basis__" + old_basis.name 325 new_basis = target_ob.shape_key_add(name=source_basis.name) 326 327 self.binded_shape_key = source_ob.shape_key_add(name="__bind_shape_key", from_mix=True) 328 self.source_bind_data = self.binded_shape_key.data 329 330 compat.set_active(context, target_ob) 331 target_ob.active_shape_key_index = target_ob.data.shape_keys.key_blocks.find(new_basis.name) 332 # TOP指定でindex=1になるケースは、さらにもう一度UP 333 bpy.ops.object.shape_key_move(type='TOP') 334 if target_ob.active_shape_key_index == 1: 335 bpy.ops.object.shape_key_move(type='UP') 336 337 old_basis.relative_key = new_basis 338 339 source_ob.active_shape_key_index = source_ob.data.shape_keys.key_blocks.find(self.binded_shape_key.name) 340 341 else: 342 source_ob.active_shape_key_index = 0 343 self.source_bind_data = source_ob.data.vertices 344 345 #print(len(source_ob.data.vertices), len(self.source_bind_data)) 346 self.kd = mathutils.kdtree.KDTree(len(self.source_bind_data)) 347 for index, vert in enumerate(self.source_bind_data): 348 co = compat.mul(source_ob.matrix_world, vert.co) 349 self.kd.insert(co, index) 350 self.kd.balance() 351 352 for i, key in enumerate(source_ob.data.shape_keys.key_blocks): 353 if i == 0: 354 continue 355 else: 356 key.value = 0.0
def
finish(self, context):
358 def finish(self, context): 359 target_me = self.target_ob.data 360 361 if self.is_remove_empty: 362 for source_shape_key_name, is_shaped in reversed( list(self.is_shapeds.items()) ): 363 if not is_shaped: 364 target_shape_key = target_me.shape_keys.key_blocks.get(source_shape_key_name) 365 if not target_shape_key: 366 continue 367 key_blocks_values = target_me.shape_keys.key_blocks.values() 368 is_used = False 369 for key in key_blocks_values: 370 if key.relative_key == target_shape_key: 371 is_used = True 372 break 373 if not is_used: 374 self.target_ob.shape_key_remove(target_shape_key) 375 376 self.target_ob.active_shape_key_index = 0
def
cleanup(self, context):
383 def cleanup(self, context): 384 #compat.set_select(source_original_ob, True) 385 if self.target_ob: 386 #compat.set_select(target_ob, True) 387 compat.set_active(context, self.target_ob) 388 389 if self.og_source_ob: 390 compat.set_hide(self.og_source_ob, False) 391 392 source_me = self.source_ob and self.source_ob.data 393 if source_me: 394 common.remove_data([self.source_ob, source_me]) 395 elif self.source_ob: 396 common.remove_data([self.source_ob]) 397 398 if self._timer: 399 wm = context.window_manager 400 wm.event_timer_remove(self._timer) 401 402 if self.pre_mode: 403 bpy.ops.object.mode_set(mode=self.pre_mode) 404 405 if self.pre_selected: 406 for ob in self.pre_selected: 407 compat.set_select(ob, True) 408 409 self.target_ob = None 410 self.source_ob = None 411 412 self._timer = None 413 414 self.pre_mode = None 415 self.pre_selected = None 416 417 self.binded_shape_key = None 418 self.kd = None 419 self.is_shapeds = {}
@compat.BlRegister()
class
CNV_OT_quick_shape_key_transfer423@compat.BlRegister() 424class CNV_OT_quick_shape_key_transfer(shape_key_transfer_op, bpy.types.Operator): 425 bl_idname = 'object.quick_shape_key_transfer' 426 bl_label = "クイック・シェイプキー転送" 427 bl_description = "アクティブなメッシュに他の選択メッシュのシェイプキーを高速で転送します" 428 bl_options = {'REGISTER', 'UNDO'} 429 430 step_size = bpy.props.IntProperty(name="Step Size (low = quality, high = speed)", default=1, min=1, max=100, soft_min=1, soft_max=10, step=1) 431 432 near_vert_indexs = [] 433 my_iter = None 434 435 @classmethod 436 def poll(cls, context): 437 obs = context.selected_objects 438 if len(obs) == 2: 439 active_ob = context.active_object 440 for ob in obs: 441 if ob.type != 'MESH': 442 return False 443 if ob.data.shape_keys and ob.name != active_ob.name: 444 return True 445 return False 446 447 def invoke(self, context, event): 448 return context.window_manager.invoke_props_dialog(self) 449 450 def draw(self, context): 451 shape_key_transfer_op.draw(self, context) 452 self.layout.prop(self, 'step_size') 453 454 def prepare(self, context): 455 shape_key_transfer_op.prepare(self, context) 456 457 target_me = self.target_ob.data 458 source_me = self.source_ob.data 459 460 self.near_vert_indexs = list( range(len(target_me.vertices)) ) 461 462 for v in target_me.vertices: 463 near_co = compat.mul(self.target_ob.matrix_world, v.co) #v.co 464 self.near_vert_indexs[v.index] = self.kd.find(near_co)[1] 465 466 self.my_iter = iter( transfer_shape_key_iter(self.target_ob, self.source_ob, self.binded_shape_key) ) 467 context.window_manager.progress_begin( 0, len(source_me.shape_keys.key_blocks) * len(target_me.vertices) ) 468 context.window_manager.progress_update( 0 ) 469 470 def loop(self, context): 471 source_shape_key_index, target_shape_key, binded_shape_key_data, source_shape_key_data, target_shape_key_data = next(self.my_iter, (-1, None, None, None, None)) 472 #print(source_shape_key_index, target_shape_key, binded_shape_key_data, source_shape_key_data, target_shape_key_data) 473 if not target_shape_key: 474 context.window_manager.progress_end() 475 return True 476 477 progress = source_shape_key_index * len(self.target_ob.data.vertices) 478 479 def check(index): 480 near_vert_index = self.near_vert_indexs[index] 481 near_shape_co = source_shape_key_data[near_vert_index].co - binded_shape_key_data[near_vert_index].co 482 483 context.window_manager.progress_update( progress + index ) 484 485 if abs(near_shape_co.length) > 2e-126: # 2e-126 is the smallest float != 0 486 target_shape_key_data[index].co += near_shape_co 487 return True 488 489 is_changed = False 490 just_changed = False 491 found_more = False 492 for i in range(0, len(target_shape_key_data), self.step_size): 493 494 if check(i) or found_more: 495 is_changed = True 496 found_more = False 497 if not just_changed: 498 for j in range(i-self.step_size+1, i): 499 if j < len(target_shape_key_data) and j > 0: 500 found_more = check(j) or found_more 501 for k in range(i+1, i+self.step_size): 502 if k < len(target_shape_key_data) and k > 0: 503 found_more = check(k) or found_more 504 just_changed = True 505 else: 506 just_changed = False 507 508 if not self.is_shapeds.get(target_shape_key.name): 509 self.is_shapeds[target_shape_key.name] = is_changed 510 self.my_iter.update() # only call this when done with current iteration. 511 512 def cleanup(self, context): 513 self.near_vert_indexs = [] 514 self.my_iter.free() 515 self.my_iter = None 516 shape_key_transfer_op.cleanup(self, context)
step_size: <_PropertyDeferred, <built-in function IntProperty>, {'name': 'Step Size (low = quality, high = speed)', 'default': 1, 'min': 1, 'max': 100, 'soft_min': 1, 'soft_max': 10, 'step': 1, 'attr': 'step_size'}> =
<_PropertyDeferred, <built-in function IntProperty>, {'name': 'Step Size (low = quality, high = speed)', 'default': 1, 'min': 1, 'max': 100, 'soft_min': 1, 'soft_max': 10, 'step': 1, 'attr': 'step_size'}>
def
prepare(self, context):
454 def prepare(self, context): 455 shape_key_transfer_op.prepare(self, context) 456 457 target_me = self.target_ob.data 458 source_me = self.source_ob.data 459 460 self.near_vert_indexs = list( range(len(target_me.vertices)) ) 461 462 for v in target_me.vertices: 463 near_co = compat.mul(self.target_ob.matrix_world, v.co) #v.co 464 self.near_vert_indexs[v.index] = self.kd.find(near_co)[1] 465 466 self.my_iter = iter( transfer_shape_key_iter(self.target_ob, self.source_ob, self.binded_shape_key) ) 467 context.window_manager.progress_begin( 0, len(source_me.shape_keys.key_blocks) * len(target_me.vertices) ) 468 context.window_manager.progress_update( 0 )
def
loop(self, context):
470 def loop(self, context): 471 source_shape_key_index, target_shape_key, binded_shape_key_data, source_shape_key_data, target_shape_key_data = next(self.my_iter, (-1, None, None, None, None)) 472 #print(source_shape_key_index, target_shape_key, binded_shape_key_data, source_shape_key_data, target_shape_key_data) 473 if not target_shape_key: 474 context.window_manager.progress_end() 475 return True 476 477 progress = source_shape_key_index * len(self.target_ob.data.vertices) 478 479 def check(index): 480 near_vert_index = self.near_vert_indexs[index] 481 near_shape_co = source_shape_key_data[near_vert_index].co - binded_shape_key_data[near_vert_index].co 482 483 context.window_manager.progress_update( progress + index ) 484 485 if abs(near_shape_co.length) > 2e-126: # 2e-126 is the smallest float != 0 486 target_shape_key_data[index].co += near_shape_co 487 return True 488 489 is_changed = False 490 just_changed = False 491 found_more = False 492 for i in range(0, len(target_shape_key_data), self.step_size): 493 494 if check(i) or found_more: 495 is_changed = True 496 found_more = False 497 if not just_changed: 498 for j in range(i-self.step_size+1, i): 499 if j < len(target_shape_key_data) and j > 0: 500 found_more = check(j) or found_more 501 for k in range(i+1, i+self.step_size): 502 if k < len(target_shape_key_data) and k > 0: 503 found_more = check(k) or found_more 504 just_changed = True 505 else: 506 just_changed = False 507 508 if not self.is_shapeds.get(target_shape_key.name): 509 self.is_shapeds[target_shape_key.name] = is_changed 510 self.my_iter.update() # only call this when done with current iteration.
Inherited Members
- shape_key_transfer_op
- is_first_remove_all
- is_remove_empty
- is_bind_current_mix
- subdivide_number
- target_ob
- source_ob
- og_source_ob
- is_finished
- is_canceled
- pre_mode
- pre_selected
- binded_shape_key
- kd
- is_shapeds
- execute
- modal
- finish
- cancel
- 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_precision_shape_key_transfer520@compat.BlRegister() 521class CNV_OT_precision_shape_key_transfer(shape_key_transfer_op, bpy.types.Operator): 522 bl_idname = 'object.precision_shape_key_transfer' 523 bl_label = "空間ぼかし・シェイプキー転送" 524 bl_description = "アクティブなメッシュに他の選択メッシュのシェイプキーを遠いほどぼかして転送します" 525 bl_options = {'REGISTER', 'UNDO'} 526 527 step_size = bpy.props.IntProperty(name="Step Size (low = quality, high = speed)", default=1, min=1, max=100, soft_min=1, soft_max=10, step=1) 528 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) 529 530 531 near_vert_data = [] 532 near_vert_multi_total = [] 533 my_iter = None 534 535 #source_raw_data = None 536 #binded_raw_data = None 537 #target_raw_data = None 538 539 @classmethod 540 def poll(cls, context): 541 obs = context.selected_objects 542 if len(obs) == 2: 543 active_ob = context.active_object 544 for ob in obs: 545 if ob.type != 'MESH': 546 return False 547 if ob.data.shape_keys and ob.name != active_ob.name: 548 return True 549 return False 550 551 def invoke(self, context, event): 552 return context.window_manager.invoke_props_dialog(self) 553 554 def draw(self, context): 555 shape_key_transfer_op.draw(self, context) 556 self.layout.prop(self, 'step_size') 557 self.layout.prop(self, 'extend_range', icon='PROP_ON') 558 559 def xexecute(self, context): 560 start_time = time.time() 561 562 target_ob, source_ob = common.get_target_and_source_ob(context, copySource=True) 563 target_me = target_ob.data 564 source_me = source_ob.ldata 565 566 pre_mode = target_ob.mode 567 bpy.ops.object.mode_set(mode='OBJECT') 568 569 try: 570 kd = self.prepare_sks_transfer(context, target_ob, source_ob) 571 572 context.window_manager.progress_begin(0, len(target_me.vertices)) 573 progress_reduce = len(target_me.vertices) // 200 + 1 574 near_vert_data = [] 575 near_vert_multi_total = [] 576 near_vert_multi_total_append = near_vert_multi_total.append 577 578 mat1, mat2 = source_ob.matrix_world, target_ob.matrix_world 579 source_shape_key_data = [compat.mul3(mat1, source_shape_key.data[v.index].co, mat2) - compat.mul3(mat1, source_me.vertices[v.index].co, mat2) for v in source_me.vertices] 580 581 for vert in target_me.vertices: 582 new_vert_data = [] 583 near_vert_data.append(new_vert_data) 584 near_vert_data_append = new_vert_data.append 585 586 target_co = compat.mul(target_ob.matrix_world, vert.co) 587 mini_co, mini_index, mini_dist = kd.find(target_co) 588 radius = mini_dist * self.extend_range 589 diff_radius = radius - mini_dist 590 591 multi_total = 0.0 592 for co, index, dist in kd.find_range(target_co, radius): 593 if 0 < diff_radius: 594 multi = (diff_radius - (dist - mini_dist)) / diff_radius 595 else: 596 multi = 1.0 597 near_vert_data_append((index, multi)) 598 multi_total += multi 599 near_vert_multi_total_append(multi_total) 600 601 if vert.index % progress_reduce == 0: 602 context.window_manager.progress_update(vert.index) 603 context.window_manager.progress_end() 604 605 is_shapeds = {} 606 context.window_manager.progress_begin(0, len(source_me.shape_keys.key_blocks) * len(target_me.vertices)) 607 context.window_manager.progress_update(0) 608 609 for source_shape_key_index, source_shape_key, target_shape_key in self.enumerate_transfer_sks(context, target_ob, source_ob): 610 for target_vert in target_me.vertices: 611 612 if 0 < near_vert_multi_total[target_vert.index]: 613 614 total_diff_co = mathutils.Vector((0, 0, 0)) 615 616 for near_index, near_multi in near_vert_data[target_vert.index]: 617 total_diff_co += source_shape_key_data[near_index] * near_multi 618 619 average_diff_co = total_diff_co / near_vert_multi_total[target_vert.index] 620 621 else: 622 average_diff_co = mathutils.Vector((0, 0, 0)) 623 624 target_shape_key.data[target_vert.index].co = target_me.vertices[target_vert.index].co + average_diff_co 625 is_shapeds[target_shape_key.name] = is_shapeds.get(target_shape_key.name) or 0.01 < average_diff_co.length 626 627 context.window_manager.progress_update((source_shape_key_index+1) * (target_me.vertices+1)) 628 #context.window_manager.progress_update(source_shape_key_index) 629 context.window_manager.progress_end() 630 631 self.finish_sks_transfer(context, target_ob, source_ob, is_shapeds) 632 633 except: 634 traceback.print_exc() 635 self.report(type={'ERROR'}, message="Error while transfering shapekeys. Results may not be as expected. Use Undo / Ctrl Z to revert changes") 636 637 finally: 638 self.cleanup_sks_transfer(context, target_ob, source_ob, pre_mode) 639 640 diff_time = time.time() - start_time 641 self.report(type={'INFO'}, message=f_tip_("{:.2f} Seconds", diff_time)) 642 return {'FINISHED'} 643 644 def prepare(self, context): 645 shape_key_transfer_op.prepare(self, context) 646 647 target_me = self.target_ob.data 648 source_me = self.source_ob.data 649 650 context.window_manager.progress_begin(0, len(target_me.vertices)) 651 progress_reduce = len(target_me.vertices) // 200 + 1 652 self.near_vert_data = [] 653 self.near_vert_multi_total = [] 654 near_vert_multi_total_append = self.near_vert_multi_total.append 655 656 for vert in target_me.vertices: 657 new_vert_data = [] 658 self.near_vert_data.append(new_vert_data) 659 self.near_vert_data_append = new_vert_data.append 660 661 target_co = compat.mul(self.target_ob.matrix_world, vert.co) #vert.co 662 mini_co, mini_index, mini_dist = self.kd.find(target_co) 663 radius = mini_dist * self.extend_range 664 diff_radius = radius - mini_dist 665 666 multi_total = 0.0 667 for co, index, dist in self.kd.find_range(target_co, radius): 668 if 0 < diff_radius: 669 multi = (diff_radius - (dist - mini_dist)) / diff_radius 670 else: 671 multi = 1.0 672 self.near_vert_data_append((index, multi)) 673 multi_total += multi 674 near_vert_multi_total_append(multi_total) 675 676 if vert.index % progress_reduce == 0: 677 context.window_manager.progress_update(vert.index) 678 context.window_manager.progress_end() 679 680 self.my_iter = iter(transfer_shape_key_iter(self.target_ob, self.source_ob, binded_shape_key=self.binded_shape_key)) 681 682 #self.source_raw_data = numpy.ndarray(shape=(len(source_me.vertices), 3), dtype=float, order='C') 683 #self.target_raw_data = numpy.ndarray(shape=(len(target_me.vertices), 3), dtype=float, order='C') 684 685 #self.binded_raw_data = numpy.ndarray(shape=len(self.source_bind_data)*3, dtype=float, order='C') 686 #self.source_bind_data.foreach_get('co', self.binded_raw_data) 687 #self.binded_raw_data.resize(self.binded_raw_data.size//3, 3) 688 689 context.window_manager.progress_begin(0, len(source_me.shape_keys.key_blocks) * len(target_me.vertices)) 690 context.window_manager.progress_update(0) 691 692 def loop(self, context): 693 #bpy.ops.object.mode_set(mode='OBJECT') 694 source_shape_key_index, target_shape_key, binded_shape_key_data, source_shape_key_data, target_shape_key_data = next(self.my_iter, (-1, None, None, None, None)) 695 if not target_shape_key: 696 context.window_manager.progress_end() 697 return True 698 699 #print("Loop for " + target_shape_key.name) 700 701 #context.window_manager.progress_begin( 0, len(self.source_ob.shape_keys.key_blocks) * len(target_ob.data.vertices) ) 702 progress = source_shape_key_index * len(self.target_ob.data.vertices) 703 #context.window_manager.progress_update( progress ) 704 705 diff_data = [None] * len(source_shape_key_data) 706 near_diff_co = mathutils.Vector.Fill(3, 0) # Creates a vector of length 3 filled with 0's 707 def check(index, near_diff_co=near_diff_co): 708 near_diff_co.zero() # This should be faster than creating a new vector every time 709 710 if self.near_vert_multi_total[index] > 0: 711 for near_index, near_multi in self.near_vert_data[index]: 712 diff_data[near_index] = diff_data[near_index] or source_shape_key_data[near_index].co - binded_shape_key_data[near_index].co 713 near_diff_co += diff_data[near_index] * near_multi 714 715 near_diff_co /= self.near_vert_multi_total[index] 716 717 context.window_manager.progress_update( progress + index ) 718 719 if near_diff_co.length > 2e-126: # 2e-126 is the smallest float != 0 720 target_shape_key_data[index].co += near_diff_co 721 return True 722 723 is_changed = False 724 just_changed = False 725 if self.step_size > 1: 726 found_more = False 727 for i in range(0, len(target_shape_key_data), self.step_size): 728 729 if check(i) or found_more: 730 is_changed = True 731 found_more = False 732 if not just_changed: 733 for j in range(i-self.step_size+1, i): 734 if j < len(target_shape_key_data) and j > 0: 735 found_more = check(j) or found_more 736 for k in range(i+1, i+self.step_size): 737 if k < len(target_shape_key_data) and k > 0: 738 found_more = check(k) or found_more 739 just_changed = True 740 else: 741 just_changed = False 742 743 else: # if self.step_size == 1: 744 for index, binded_vert, source_vert in zip(range(len(diff_data)), binded_shape_key_data, source_shape_key_data): 745 diff_data[index] = source_vert.co - binded_vert.co 746 if diff_data[index].length > 2e-126: 747 just_changed = True 748 749 if just_changed: 750 for target_vert, near_indices, near_total in zip(target_shape_key_data, self.near_vert_data, self.near_vert_multi_total): 751 near_diff_co.zero() # This should be faster than creating a new vector every time 752 753 if near_total > 0: 754 for near_index, near_multi in near_indices: 755 near_diff_co += diff_data[near_index] * near_multi 756 757 near_diff_co /= near_total 758 759 if near_diff_co.length > 2e-126: # 2e-126 is the smallest float != 0 760 target_vert.co += near_diff_co 761 is_changed = True 762 763 progress += 1 764 context.window_manager.progress_update( progress ) 765 else: 766 context.window_manager.progress_update( progress + len(target_shape_key_data) ) 767 768 self.is_shapeds[target_shape_key.name] = self.is_shapeds.get(target_shape_key.name) or is_changed 769 self.my_iter.update() # only call this when done with current iteration. 770 #bpy.ops.object.mode_set(mode='SCULPT') # Preview shape keys while transfering 771 772 def cleanup(self, context): 773 self.near_vert_data = [] 774 self.near_vert_multi_total = [] 775 self.my_iter = None 776 #self.source_raw_data = None 777 #self.binded_raw_data = None 778 #self.target_raw_data = None 779 shape_key_transfer_op.cleanup(self, context)
step_size: <_PropertyDeferred, <built-in function IntProperty>, {'name': 'Step Size (low = quality, high = speed)', 'default': 1, 'min': 1, 'max': 100, 'soft_min': 1, 'soft_max': 10, 'step': 1, 'attr': 'step_size'}> =
<_PropertyDeferred, <built-in function IntProperty>, {'name': 'Step Size (low = quality, high = speed)', 'default': 1, 'min': 1, 'max': 100, 'soft_min': 1, 'soft_max': 10, 'step': 1, 'attr': 'step_size'}>
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'}>
def
xexecute(self, context):
559 def xexecute(self, context): 560 start_time = time.time() 561 562 target_ob, source_ob = common.get_target_and_source_ob(context, copySource=True) 563 target_me = target_ob.data 564 source_me = source_ob.ldata 565 566 pre_mode = target_ob.mode 567 bpy.ops.object.mode_set(mode='OBJECT') 568 569 try: 570 kd = self.prepare_sks_transfer(context, target_ob, source_ob) 571 572 context.window_manager.progress_begin(0, len(target_me.vertices)) 573 progress_reduce = len(target_me.vertices) // 200 + 1 574 near_vert_data = [] 575 near_vert_multi_total = [] 576 near_vert_multi_total_append = near_vert_multi_total.append 577 578 mat1, mat2 = source_ob.matrix_world, target_ob.matrix_world 579 source_shape_key_data = [compat.mul3(mat1, source_shape_key.data[v.index].co, mat2) - compat.mul3(mat1, source_me.vertices[v.index].co, mat2) for v in source_me.vertices] 580 581 for vert in target_me.vertices: 582 new_vert_data = [] 583 near_vert_data.append(new_vert_data) 584 near_vert_data_append = new_vert_data.append 585 586 target_co = compat.mul(target_ob.matrix_world, vert.co) 587 mini_co, mini_index, mini_dist = kd.find(target_co) 588 radius = mini_dist * self.extend_range 589 diff_radius = radius - mini_dist 590 591 multi_total = 0.0 592 for co, index, dist in kd.find_range(target_co, radius): 593 if 0 < diff_radius: 594 multi = (diff_radius - (dist - mini_dist)) / diff_radius 595 else: 596 multi = 1.0 597 near_vert_data_append((index, multi)) 598 multi_total += multi 599 near_vert_multi_total_append(multi_total) 600 601 if vert.index % progress_reduce == 0: 602 context.window_manager.progress_update(vert.index) 603 context.window_manager.progress_end() 604 605 is_shapeds = {} 606 context.window_manager.progress_begin(0, len(source_me.shape_keys.key_blocks) * len(target_me.vertices)) 607 context.window_manager.progress_update(0) 608 609 for source_shape_key_index, source_shape_key, target_shape_key in self.enumerate_transfer_sks(context, target_ob, source_ob): 610 for target_vert in target_me.vertices: 611 612 if 0 < near_vert_multi_total[target_vert.index]: 613 614 total_diff_co = mathutils.Vector((0, 0, 0)) 615 616 for near_index, near_multi in near_vert_data[target_vert.index]: 617 total_diff_co += source_shape_key_data[near_index] * near_multi 618 619 average_diff_co = total_diff_co / near_vert_multi_total[target_vert.index] 620 621 else: 622 average_diff_co = mathutils.Vector((0, 0, 0)) 623 624 target_shape_key.data[target_vert.index].co = target_me.vertices[target_vert.index].co + average_diff_co 625 is_shapeds[target_shape_key.name] = is_shapeds.get(target_shape_key.name) or 0.01 < average_diff_co.length 626 627 context.window_manager.progress_update((source_shape_key_index+1) * (target_me.vertices+1)) 628 #context.window_manager.progress_update(source_shape_key_index) 629 context.window_manager.progress_end() 630 631 self.finish_sks_transfer(context, target_ob, source_ob, is_shapeds) 632 633 except: 634 traceback.print_exc() 635 self.report(type={'ERROR'}, message="Error while transfering shapekeys. Results may not be as expected. Use Undo / Ctrl Z to revert changes") 636 637 finally: 638 self.cleanup_sks_transfer(context, target_ob, source_ob, pre_mode) 639 640 diff_time = time.time() - start_time 641 self.report(type={'INFO'}, message=f_tip_("{:.2f} Seconds", diff_time)) 642 return {'FINISHED'}
def
prepare(self, context):
644 def prepare(self, context): 645 shape_key_transfer_op.prepare(self, context) 646 647 target_me = self.target_ob.data 648 source_me = self.source_ob.data 649 650 context.window_manager.progress_begin(0, len(target_me.vertices)) 651 progress_reduce = len(target_me.vertices) // 200 + 1 652 self.near_vert_data = [] 653 self.near_vert_multi_total = [] 654 near_vert_multi_total_append = self.near_vert_multi_total.append 655 656 for vert in target_me.vertices: 657 new_vert_data = [] 658 self.near_vert_data.append(new_vert_data) 659 self.near_vert_data_append = new_vert_data.append 660 661 target_co = compat.mul(self.target_ob.matrix_world, vert.co) #vert.co 662 mini_co, mini_index, mini_dist = self.kd.find(target_co) 663 radius = mini_dist * self.extend_range 664 diff_radius = radius - mini_dist 665 666 multi_total = 0.0 667 for co, index, dist in self.kd.find_range(target_co, radius): 668 if 0 < diff_radius: 669 multi = (diff_radius - (dist - mini_dist)) / diff_radius 670 else: 671 multi = 1.0 672 self.near_vert_data_append((index, multi)) 673 multi_total += multi 674 near_vert_multi_total_append(multi_total) 675 676 if vert.index % progress_reduce == 0: 677 context.window_manager.progress_update(vert.index) 678 context.window_manager.progress_end() 679 680 self.my_iter = iter(transfer_shape_key_iter(self.target_ob, self.source_ob, binded_shape_key=self.binded_shape_key)) 681 682 #self.source_raw_data = numpy.ndarray(shape=(len(source_me.vertices), 3), dtype=float, order='C') 683 #self.target_raw_data = numpy.ndarray(shape=(len(target_me.vertices), 3), dtype=float, order='C') 684 685 #self.binded_raw_data = numpy.ndarray(shape=len(self.source_bind_data)*3, dtype=float, order='C') 686 #self.source_bind_data.foreach_get('co', self.binded_raw_data) 687 #self.binded_raw_data.resize(self.binded_raw_data.size//3, 3) 688 689 context.window_manager.progress_begin(0, len(source_me.shape_keys.key_blocks) * len(target_me.vertices)) 690 context.window_manager.progress_update(0)
def
loop(self, context):
692 def loop(self, context): 693 #bpy.ops.object.mode_set(mode='OBJECT') 694 source_shape_key_index, target_shape_key, binded_shape_key_data, source_shape_key_data, target_shape_key_data = next(self.my_iter, (-1, None, None, None, None)) 695 if not target_shape_key: 696 context.window_manager.progress_end() 697 return True 698 699 #print("Loop for " + target_shape_key.name) 700 701 #context.window_manager.progress_begin( 0, len(self.source_ob.shape_keys.key_blocks) * len(target_ob.data.vertices) ) 702 progress = source_shape_key_index * len(self.target_ob.data.vertices) 703 #context.window_manager.progress_update( progress ) 704 705 diff_data = [None] * len(source_shape_key_data) 706 near_diff_co = mathutils.Vector.Fill(3, 0) # Creates a vector of length 3 filled with 0's 707 def check(index, near_diff_co=near_diff_co): 708 near_diff_co.zero() # This should be faster than creating a new vector every time 709 710 if self.near_vert_multi_total[index] > 0: 711 for near_index, near_multi in self.near_vert_data[index]: 712 diff_data[near_index] = diff_data[near_index] or source_shape_key_data[near_index].co - binded_shape_key_data[near_index].co 713 near_diff_co += diff_data[near_index] * near_multi 714 715 near_diff_co /= self.near_vert_multi_total[index] 716 717 context.window_manager.progress_update( progress + index ) 718 719 if near_diff_co.length > 2e-126: # 2e-126 is the smallest float != 0 720 target_shape_key_data[index].co += near_diff_co 721 return True 722 723 is_changed = False 724 just_changed = False 725 if self.step_size > 1: 726 found_more = False 727 for i in range(0, len(target_shape_key_data), self.step_size): 728 729 if check(i) or found_more: 730 is_changed = True 731 found_more = False 732 if not just_changed: 733 for j in range(i-self.step_size+1, i): 734 if j < len(target_shape_key_data) and j > 0: 735 found_more = check(j) or found_more 736 for k in range(i+1, i+self.step_size): 737 if k < len(target_shape_key_data) and k > 0: 738 found_more = check(k) or found_more 739 just_changed = True 740 else: 741 just_changed = False 742 743 else: # if self.step_size == 1: 744 for index, binded_vert, source_vert in zip(range(len(diff_data)), binded_shape_key_data, source_shape_key_data): 745 diff_data[index] = source_vert.co - binded_vert.co 746 if diff_data[index].length > 2e-126: 747 just_changed = True 748 749 if just_changed: 750 for target_vert, near_indices, near_total in zip(target_shape_key_data, self.near_vert_data, self.near_vert_multi_total): 751 near_diff_co.zero() # This should be faster than creating a new vector every time 752 753 if near_total > 0: 754 for near_index, near_multi in near_indices: 755 near_diff_co += diff_data[near_index] * near_multi 756 757 near_diff_co /= near_total 758 759 if near_diff_co.length > 2e-126: # 2e-126 is the smallest float != 0 760 target_vert.co += near_diff_co 761 is_changed = True 762 763 progress += 1 764 context.window_manager.progress_update( progress ) 765 else: 766 context.window_manager.progress_update( progress + len(target_shape_key_data) ) 767 768 self.is_shapeds[target_shape_key.name] = self.is_shapeds.get(target_shape_key.name) or is_changed 769 self.my_iter.update() # only call this when done with current iteration. 770 #bpy.ops.object.mode_set(mode='SCULPT') # Preview shape keys while transfering
Inherited Members
- shape_key_transfer_op
- is_first_remove_all
- is_remove_empty
- is_bind_current_mix
- subdivide_number
- target_ob
- source_ob
- og_source_ob
- is_finished
- is_canceled
- pre_mode
- pre_selected
- binded_shape_key
- kd
- is_shapeds
- execute
- modal
- finish
- cancel
- 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_UL_vgroups_selector851@compat.BlRegister() 852class CNV_UL_vgroups_selector(bpy.types.UIList): 853 bl_label = "Vertex Groups Selector" 854 bl_options = {'DEFAULT_CLOSED'} 855 bl_region_type = 'WINDOW' 856 bl_space_type = 'PROPERTIES' 857 858 # Constants (flags) 859 # Be careful not to shadow FILTER_ITEM! 860 VGROUP_EMPTY = 1 << 1 861 VGROUP_DEFORM = 1 << 0 862 863 armature = None 864 local_bone_names = None 865 cached_values = {} 866 867 expanded_layout = False 868 869 # Custom properties, saved with .blend file. 870 use_filter_name_reverse = bpy.props.BoolProperty( 871 name="Reverse Name", 872 default=False, 873 options=set(), 874 description="Reverse name filtering", 875 ) 876 use_filter_deform = bpy.props.BoolProperty( 877 name="Only Deform", 878 default=False, 879 options=set(), 880 description="Only show deforming vertex groups", 881 ) 882 use_filter_deform_reverse = bpy.props.BoolProperty( 883 name="Other", 884 default=False, 885 options=set(), 886 description="Only show non-deforming vertex groups", 887 ) 888 use_filter_empty = bpy.props.BoolProperty( 889 name="Filter Empty", 890 default=False, 891 options=set(), 892 description="Whether to filter empty vertex groups", 893 ) 894 use_filter_empty_reverse = bpy.props.BoolProperty( 895 name="Reverse Empty", 896 default=False, 897 options=set(), 898 description="Reverse empty filtering", 899 ) 900 901 # This allows us to have mutually exclusive options, which are also all disable-able! 902 def _gen_order_update(name1, name2): 903 def _u(self, ctxt): 904 if (getattr(self, name1)): 905 setattr(self, name2, False) 906 return _u 907 use_order_name = bpy.props.BoolProperty( 908 name="Name", default=False, options=set(), 909 description="Sort groups by their name (case-insensitive)", 910 update=_gen_order_update("use_order_name", "use_order_importance"), 911 ) 912 use_order_importance = bpy.props.BoolProperty( 913 name="Importance", 914 default=False, 915 options=set(), 916 description="Sort groups by their average weight in the mesh", 917 update=_gen_order_update("use_order_importance", "use_order_name"), 918 ) 919 use_filter_orderby_invert = bpy.props.BoolProperty( 920 name="Order by Invert", 921 default=False, 922 options=set(), 923 description="Invert the sort by order" 924 ) 925 926 # Usual draw item function. 927 def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag): 928 # Just in case, we do not use it here! 929 self.use_filter_invert = False 930 931 # assert(isinstance(item, bpy.types.VertexGroup) 932 #vgroup = getattr(data, 'matched_vgroups')[item.index] 933 if self.layout_type in {'DEFAULT', 'COMPACT'}: 934 # Here we use one feature of new filtering feature: it can pass data to draw_item, through flt_flag 935 # parameter, which contains exactly what filter_items set in its filter list for this item! 936 # In this case, we show empty groups grayed out. 937 cached_value = self.cached_values.get(item.name, None) 938 if (cached_value != None) and (cached_value != item.value): 939 item.preferred = item.value 940 941 if self.use_filter_deform: 942 item.value = bool(flt_flag & self.VGROUP_DEFORM) and item.preferred 943 else: 944 item.value = item.preferred 945 946 self.cached_values[item.name] = item.value 947 948 if flt_flag & (self.VGROUP_EMPTY | self.VGROUP_DEFORM): 949 col = layout.column() 950 col.enabled = False 951 col.alignment = 'LEFT' 952 col.prop(item, "value", text=item.name, emboss=False, icon_value=icon) 953 else: 954 layout.prop(item, "value", text=item.name, icon_value=icon) 955 956 icon = 'RADIOBUT_ON' if item.preferred else 'RADIOBUT_OFF' 957 layout.prop(item, "preferred", text="", icon=compat.icon(icon), emboss=False) 958 elif self.layout_type in {'GRID'}: 959 layout.alignment = 'CENTER' 960 if flt_flag & self.VGROUP_EMPTY: 961 layout.enabled = False 962 layout.label(text="", icon_value=icon) 963 964 def draw_filter(self, context, layout): 965 # Nothing much to say here, it's usual UI code... 966 row = layout.row() 967 if not self.expanded_layout: 968 layout.active = True 969 layout.enabled = True 970 row.active = True 971 row.enabled = True 972 self.expanded_layout = True 973 974 subrow = row.row(align=True) 975 subrow.prop(self, "filter_name", text="") 976 icon = 'ZOOM_OUT' if self.use_filter_name_reverse else 'ZOOM_IN' 977 subrow.prop(self, "use_filter_name_reverse", text="", icon=icon) 978 979 subrow = row.row(align=True) 980 subrow.prop(self, "use_filter_deform", toggle=True) 981 icon = 'ZOOM_OUT' if self.use_filter_deform_reverse else 'ZOOM_IN' 982 subrow.prop(self, "use_filter_deform_reverse", text="", icon=icon) 983 984 #subrow = row.row(align=True) 985 #subrow.prop(self, "use_filter_empty", toggle=True) 986 #icon = 'ZOOM_OUT' if self.use_filter_empty_reverse else 'ZOOM_IN' 987 #subrow.prop(self, "use_filter_empty_reverse", text="", icon=icon) 988 989 row = layout.row(align=True) 990 row.label(text="Order by:") 991 row.prop(self, "use_order_name", toggle=True) 992 #row.prop(self, "use_order_importance", toggle=True) 993 icon = 'TRIA_UP' if self.use_filter_orderby_invert else 'TRIA_DOWN' 994 row.prop(self, "use_filter_orderby_invert", text="", icon=icon) 995 996 def filter_items_empty_vgroups(self, context, vgroups): 997 # This helper function checks vgroups to find out whether they are empty, and what's their average weights. 998 # TODO: This should be RNA helper actually (a vgroup prop like "raw_data: ((vidx, vweight), etc.)"). 999 # Too slow for python! 1000 obj_data = context.active_object.data 1001 ret = {vg.index: [True, 0.0] for vg in vgroups} 1002 if hasattr(obj_data, "vertices"): # Mesh data 1003 if obj_data.is_editmode: 1004 import bmesh 1005 bm = bmesh.from_edit_mesh(obj_data) 1006 # only ever one deform weight layer 1007 dvert_lay = bm.verts.layers.deform.active 1008 fact = 1 / len(bm.verts) 1009 if dvert_lay: 1010 for v in bm.verts: 1011 for vg_idx, vg_weight in v[dvert_lay].items(): 1012 ret[vg_idx][0] = False 1013 ret[vg_idx][1] += vg_weight * fact 1014 else: 1015 fact = 1 / len(obj_data.vertices) 1016 for v in obj_data.vertices: 1017 for vg in v.groups: 1018 ret[vg.group][0] = False 1019 ret[vg.group][1] += vg.weight * fact 1020 elif hasattr(obj_data, "points"): # Lattice data 1021 # XXX no access to lattice editdata? 1022 fact = 1 / len(obj_data.points) 1023 for v in obj_data.points: 1024 for vg in v.groups: 1025 ret[vg.group][0] = False 1026 ret[vg.group][1] += vg.weight * fact 1027 return ret 1028 1029 def filter_items(self, context, data, propname): 1030 # This function gets the collection property (as the usual tuple (data, propname)), and must return two lists: 1031 # * The first one is for filtering, it must contain 32bit integers were self.bitflag_filter_item marks the 1032 # matching item as filtered (i.e. to be shown), and 31 other bits are free for custom needs. Here we use the 1033 # first one to mark VGROUP_EMPTY. 1034 # * The second one is for reordering, it must return a list containing the new indices of the items (which 1035 # gives us a mapping org_idx -> new_idx). 1036 # Please note that the default UI_UL_list defines helper functions for common tasks (see its doc for more info). 1037 # If you do not make filtering and/or ordering, return empty list(s) (this will be more efficient than 1038 # returning full lists doing nothing!). 1039 items = getattr(data, propname) 1040 1041 if self.armature == None: 1042 target_ob, source_ob = common.get_target_and_source_ob(context) 1043 armature_ob = target_ob.find_armature() or source_ob.find_armature() 1044 self.armature = armature_ob and armature_ob.data or False 1045 1046 if not self.local_bone_names: 1047 target_ob, source_ob = common.get_target_and_source_ob(context) 1048 bone_data_ob = (target_ob.get("LocalBoneData:0") and target_ob) or (source_ob.get("LocalBoneData:0") and source_ob) or None 1049 if bone_data_ob: 1050 local_bone_data = model_export.CNV_OT_export_cm3d2_model.local_bone_data_parser(model_export.CNV_OT_export_cm3d2_model.indexed_data_generator(bone_data_ob, prefix="LocalBoneData:")) 1051 self.local_bone_names = [ bone['name'] for bone in local_bone_data ] 1052 1053 if not self.cached_values: 1054 self.cached_values = { item.name: item.value for item in items } 1055 #vgroups = [ getattr(data, 'matched_vgroups')[item.index][0] for item in items ] 1056 helper_funcs = bpy.types.UI_UL_list 1057 1058 # Default return values. 1059 flt_flags = [] 1060 flt_neworder = [] 1061 1062 # Pre-compute of vgroups data, CPU-intensive. :/ 1063 #vgroups_empty = self.filter_items_empty_vgroups(context, vgroups) 1064 1065 # Filtering by name 1066 if self.filter_name: 1067 flt_flags = helper_funcs.filter_items_by_name(self.filter_name, self.bitflag_filter_item, items, "name", 1068 reverse=self.use_filter_name_reverse) 1069 if not flt_flags: 1070 flt_flags = [self.bitflag_filter_item] * len(items) 1071 1072 for idx, vg in enumerate(items): 1073 # Filter by deform. 1074 if self.use_filter_deform: 1075 flt_flags[idx] |= self.VGROUP_DEFORM 1076 if self.use_filter_deform: 1077 if self.armature and self.armature.get(vg.name): 1078 if not self.use_filter_deform_reverse: 1079 flt_flags[idx] &= ~self.VGROUP_DEFORM 1080 elif bone_data_ob and (vg.name in self.local_bone_names): 1081 if not self.use_filter_deform_reverse: 1082 flt_flags[idx] &= ~self.VGROUP_DEFORM 1083 elif self.use_filter_deform_reverse or (not self.armature and not self.local_bone_names): 1084 flt_flags[idx] &= ~self.VGROUP_DEFORM 1085 else: 1086 flt_flags[idx] &= ~self.VGROUP_DEFORM 1087 1088 # Filter by emptiness. 1089 #if vgroups_empty[vg.index][0]: 1090 # flt_flags[idx] |= self.VGROUP_EMPTY 1091 # if self.use_filter_empty and self.use_filter_empty_reverse: 1092 # flt_flags[idx] &= ~self.bitflag_filter_item 1093 #elif self.use_filter_empty and not self.use_filter_empty_reverse: 1094 # flt_flags[idx] &= ~self.bitflag_filter_item 1095 1096 # Reorder by name or average weight. 1097 if self.use_order_name: 1098 flt_neworder = helper_funcs.sort_items_by_name(vgroups, "name") 1099 #elif self.use_order_importance: 1100 # _sort = [(idx, vgroups_empty[vg.index][1]) for idx, vg in enumerate(vgroups)] 1101 # flt_neworder = helper_funcs.sort_items_helper(_sort, lambda e: e[1], True) 1102 1103 return flt_flags, flt_neworder
use_filter_name_reverse: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Reverse Name', 'default': False, 'options': set(), 'description': 'Reverse name filtering', 'attr': 'use_filter_name_reverse'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Reverse Name', 'default': False, 'options': set(), 'description': 'Reverse name filtering', 'attr': 'use_filter_name_reverse'}>
use_filter_deform: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Only Deform', 'default': False, 'options': set(), 'description': 'Only show deforming vertex groups', 'attr': 'use_filter_deform'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Only Deform', 'default': False, 'options': set(), 'description': 'Only show deforming vertex groups', 'attr': 'use_filter_deform'}>
use_filter_deform_reverse: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Other', 'default': False, 'options': set(), 'description': 'Only show non-deforming vertex groups', 'attr': 'use_filter_deform_reverse'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Other', 'default': False, 'options': set(), 'description': 'Only show non-deforming vertex groups', 'attr': 'use_filter_deform_reverse'}>
use_filter_empty: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Filter Empty', 'default': False, 'options': set(), 'description': 'Whether to filter empty vertex groups', 'attr': 'use_filter_empty'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Filter Empty', 'default': False, 'options': set(), 'description': 'Whether to filter empty vertex groups', 'attr': 'use_filter_empty'}>
use_filter_empty_reverse: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Reverse Empty', 'default': False, 'options': set(), 'description': 'Reverse empty filtering', 'attr': 'use_filter_empty_reverse'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Reverse Empty', 'default': False, 'options': set(), 'description': 'Reverse empty filtering', 'attr': 'use_filter_empty_reverse'}>
use_order_name: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Name', 'default': False, 'options': set(), 'description': 'Sort groups by their name (case-insensitive)', 'update': <function CNV_UL_vgroups_selector._gen_order_update.<locals>._u at 0x7f6a0e9e4e50>, 'attr': 'use_order_name'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Name', 'default': False, 'options': set(), 'description': 'Sort groups by their name (case-insensitive)', 'update': <function CNV_UL_vgroups_selector._gen_order_update.<locals>._u>, 'attr': 'use_order_name'}>
use_order_importance: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Importance', 'default': False, 'options': set(), 'description': 'Sort groups by their average weight in the mesh', 'update': <function CNV_UL_vgroups_selector._gen_order_update.<locals>._u at 0x7f6a0e9e4ee0>, 'attr': 'use_order_importance'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Importance', 'default': False, 'options': set(), 'description': 'Sort groups by their average weight in the mesh', 'update': <function CNV_UL_vgroups_selector._gen_order_update.<locals>._u>, 'attr': 'use_order_importance'}>
use_filter_orderby_invert: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Order by Invert', 'default': False, 'options': set(), 'description': 'Invert the sort by order', 'attr': 'use_filter_orderby_invert'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Order by Invert', 'default': False, 'options': set(), 'description': 'Invert the sort by order', 'attr': 'use_filter_orderby_invert'}>
def
draw_item( self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag):
927 def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag): 928 # Just in case, we do not use it here! 929 self.use_filter_invert = False 930 931 # assert(isinstance(item, bpy.types.VertexGroup) 932 #vgroup = getattr(data, 'matched_vgroups')[item.index] 933 if self.layout_type in {'DEFAULT', 'COMPACT'}: 934 # Here we use one feature of new filtering feature: it can pass data to draw_item, through flt_flag 935 # parameter, which contains exactly what filter_items set in its filter list for this item! 936 # In this case, we show empty groups grayed out. 937 cached_value = self.cached_values.get(item.name, None) 938 if (cached_value != None) and (cached_value != item.value): 939 item.preferred = item.value 940 941 if self.use_filter_deform: 942 item.value = bool(flt_flag & self.VGROUP_DEFORM) and item.preferred 943 else: 944 item.value = item.preferred 945 946 self.cached_values[item.name] = item.value 947 948 if flt_flag & (self.VGROUP_EMPTY | self.VGROUP_DEFORM): 949 col = layout.column() 950 col.enabled = False 951 col.alignment = 'LEFT' 952 col.prop(item, "value", text=item.name, emboss=False, icon_value=icon) 953 else: 954 layout.prop(item, "value", text=item.name, icon_value=icon) 955 956 icon = 'RADIOBUT_ON' if item.preferred else 'RADIOBUT_OFF' 957 layout.prop(item, "preferred", text="", icon=compat.icon(icon), emboss=False) 958 elif self.layout_type in {'GRID'}: 959 layout.alignment = 'CENTER' 960 if flt_flag & self.VGROUP_EMPTY: 961 layout.enabled = False 962 layout.label(text="", icon_value=icon)
def
draw_filter(self, context, layout):
964 def draw_filter(self, context, layout): 965 # Nothing much to say here, it's usual UI code... 966 row = layout.row() 967 if not self.expanded_layout: 968 layout.active = True 969 layout.enabled = True 970 row.active = True 971 row.enabled = True 972 self.expanded_layout = True 973 974 subrow = row.row(align=True) 975 subrow.prop(self, "filter_name", text="") 976 icon = 'ZOOM_OUT' if self.use_filter_name_reverse else 'ZOOM_IN' 977 subrow.prop(self, "use_filter_name_reverse", text="", icon=icon) 978 979 subrow = row.row(align=True) 980 subrow.prop(self, "use_filter_deform", toggle=True) 981 icon = 'ZOOM_OUT' if self.use_filter_deform_reverse else 'ZOOM_IN' 982 subrow.prop(self, "use_filter_deform_reverse", text="", icon=icon) 983 984 #subrow = row.row(align=True) 985 #subrow.prop(self, "use_filter_empty", toggle=True) 986 #icon = 'ZOOM_OUT' if self.use_filter_empty_reverse else 'ZOOM_IN' 987 #subrow.prop(self, "use_filter_empty_reverse", text="", icon=icon) 988 989 row = layout.row(align=True) 990 row.label(text="Order by:") 991 row.prop(self, "use_order_name", toggle=True) 992 #row.prop(self, "use_order_importance", toggle=True) 993 icon = 'TRIA_UP' if self.use_filter_orderby_invert else 'TRIA_DOWN' 994 row.prop(self, "use_filter_orderby_invert", text="", icon=icon)
def
filter_items_empty_vgroups(self, context, vgroups):
996 def filter_items_empty_vgroups(self, context, vgroups): 997 # This helper function checks vgroups to find out whether they are empty, and what's their average weights. 998 # TODO: This should be RNA helper actually (a vgroup prop like "raw_data: ((vidx, vweight), etc.)"). 999 # Too slow for python! 1000 obj_data = context.active_object.data 1001 ret = {vg.index: [True, 0.0] for vg in vgroups} 1002 if hasattr(obj_data, "vertices"): # Mesh data 1003 if obj_data.is_editmode: 1004 import bmesh 1005 bm = bmesh.from_edit_mesh(obj_data) 1006 # only ever one deform weight layer 1007 dvert_lay = bm.verts.layers.deform.active 1008 fact = 1 / len(bm.verts) 1009 if dvert_lay: 1010 for v in bm.verts: 1011 for vg_idx, vg_weight in v[dvert_lay].items(): 1012 ret[vg_idx][0] = False 1013 ret[vg_idx][1] += vg_weight * fact 1014 else: 1015 fact = 1 / len(obj_data.vertices) 1016 for v in obj_data.vertices: 1017 for vg in v.groups: 1018 ret[vg.group][0] = False 1019 ret[vg.group][1] += vg.weight * fact 1020 elif hasattr(obj_data, "points"): # Lattice data 1021 # XXX no access to lattice editdata? 1022 fact = 1 / len(obj_data.points) 1023 for v in obj_data.points: 1024 for vg in v.groups: 1025 ret[vg.group][0] = False 1026 ret[vg.group][1] += vg.weight * fact 1027 return ret
def
filter_items(self, context, data, propname):
1029 def filter_items(self, context, data, propname): 1030 # This function gets the collection property (as the usual tuple (data, propname)), and must return two lists: 1031 # * The first one is for filtering, it must contain 32bit integers were self.bitflag_filter_item marks the 1032 # matching item as filtered (i.e. to be shown), and 31 other bits are free for custom needs. Here we use the 1033 # first one to mark VGROUP_EMPTY. 1034 # * The second one is for reordering, it must return a list containing the new indices of the items (which 1035 # gives us a mapping org_idx -> new_idx). 1036 # Please note that the default UI_UL_list defines helper functions for common tasks (see its doc for more info). 1037 # If you do not make filtering and/or ordering, return empty list(s) (this will be more efficient than 1038 # returning full lists doing nothing!). 1039 items = getattr(data, propname) 1040 1041 if self.armature == None: 1042 target_ob, source_ob = common.get_target_and_source_ob(context) 1043 armature_ob = target_ob.find_armature() or source_ob.find_armature() 1044 self.armature = armature_ob and armature_ob.data or False 1045 1046 if not self.local_bone_names: 1047 target_ob, source_ob = common.get_target_and_source_ob(context) 1048 bone_data_ob = (target_ob.get("LocalBoneData:0") and target_ob) or (source_ob.get("LocalBoneData:0") and source_ob) or None 1049 if bone_data_ob: 1050 local_bone_data = model_export.CNV_OT_export_cm3d2_model.local_bone_data_parser(model_export.CNV_OT_export_cm3d2_model.indexed_data_generator(bone_data_ob, prefix="LocalBoneData:")) 1051 self.local_bone_names = [ bone['name'] for bone in local_bone_data ] 1052 1053 if not self.cached_values: 1054 self.cached_values = { item.name: item.value for item in items } 1055 #vgroups = [ getattr(data, 'matched_vgroups')[item.index][0] for item in items ] 1056 helper_funcs = bpy.types.UI_UL_list 1057 1058 # Default return values. 1059 flt_flags = [] 1060 flt_neworder = [] 1061 1062 # Pre-compute of vgroups data, CPU-intensive. :/ 1063 #vgroups_empty = self.filter_items_empty_vgroups(context, vgroups) 1064 1065 # Filtering by name 1066 if self.filter_name: 1067 flt_flags = helper_funcs.filter_items_by_name(self.filter_name, self.bitflag_filter_item, items, "name", 1068 reverse=self.use_filter_name_reverse) 1069 if not flt_flags: 1070 flt_flags = [self.bitflag_filter_item] * len(items) 1071 1072 for idx, vg in enumerate(items): 1073 # Filter by deform. 1074 if self.use_filter_deform: 1075 flt_flags[idx] |= self.VGROUP_DEFORM 1076 if self.use_filter_deform: 1077 if self.armature and self.armature.get(vg.name): 1078 if not self.use_filter_deform_reverse: 1079 flt_flags[idx] &= ~self.VGROUP_DEFORM 1080 elif bone_data_ob and (vg.name in self.local_bone_names): 1081 if not self.use_filter_deform_reverse: 1082 flt_flags[idx] &= ~self.VGROUP_DEFORM 1083 elif self.use_filter_deform_reverse or (not self.armature and not self.local_bone_names): 1084 flt_flags[idx] &= ~self.VGROUP_DEFORM 1085 else: 1086 flt_flags[idx] &= ~self.VGROUP_DEFORM 1087 1088 # Filter by emptiness. 1089 #if vgroups_empty[vg.index][0]: 1090 # flt_flags[idx] |= self.VGROUP_EMPTY 1091 # if self.use_filter_empty and self.use_filter_empty_reverse: 1092 # flt_flags[idx] &= ~self.bitflag_filter_item 1093 #elif self.use_filter_empty and not self.use_filter_empty_reverse: 1094 # flt_flags[idx] &= ~self.bitflag_filter_item 1095 1096 # Reorder by name or average weight. 1097 if self.use_order_name: 1098 flt_neworder = helper_funcs.sort_items_by_name(vgroups, "name") 1099 #elif self.use_order_importance: 1100 # _sort = [(idx, vgroups_empty[vg.index][1]) for idx, vg in enumerate(vgroups)] 1101 # flt_neworder = helper_funcs.sort_items_helper(_sort, lambda e: e[1], True) 1102 1103 return flt_flags, flt_neworder
Inherited Members
- bpy_types._GenericUI
- is_extended
- append
- prepend
- remove
- 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_weighted_shape_key_transfer1118@compat.BlRegister() 1119class CNV_OT_weighted_shape_key_transfer(shape_key_transfer_op, bpy.types.Operator): 1120 bl_idname = 'object.weighted_shape_key_transfer' 1121 bl_label = "Weighted shape key transfer" 1122 bl_description = "Transfers the shape keys of other selected mesh to the active mesh, using matching vertex groups as masks" 1123 bl_options = {'REGISTER', 'UNDO'} 1124 1125 step_size = bpy.props.IntProperty(name="Step Size (low = quality, high = speed)", default=1, min=1, max=100, soft_min=1, soft_max=10, step=1) 1126 extend_range = bpy.props.FloatProperty(name="Range magnification", default=1.1, min=1.0001, max=5.0, soft_min=1.0001, soft_max=5.0, step=10, precision=2) 1127 1128 near_vert_data = [] 1129 near_vert_multi_total = [] 1130 my_iter = None 1131 1132 matched_vgroups = [] 1133 using_vgroups = bpy.props.CollectionProperty(type=common.CNV_SelectorItem) 1134 active_vgroup = bpy.props.IntProperty(name="Active Vertex Group") 1135 1136 #armature = bpy.props.PointerProperty(type=bpy.types.ID) 1137 #bone_data_ob = bpy.props.PointerProperty(type=bpy.types.ID) 1138 armature = None 1139 bone_data_ob = None 1140 1141 @classmethod 1142 def poll(cls, context): 1143 obs = context.selected_objects 1144 if len(obs) == 2: 1145 active_ob = context.active_object 1146 for ob in obs: 1147 if ob.type != 'MESH': 1148 return False 1149 if ob.data.shape_keys and ob.name != active_ob.name: 1150 return True 1151 return False 1152 1153 def draw(self, context): 1154 CNV_OT_precision_shape_key_transfer.draw(self, context) 1155 target_ob, source_ob = common.get_target_and_source_ob(context) 1156 1157 self.matched_vgroups = common.values_of_matched_keys(target_ob.vertex_groups, source_ob.vertex_groups) 1158 print(f_("len(matched) = {length}", length=len(self.matched_vgroups))) 1159 armature_ob = target_ob.find_armature() or source_ob.find_armature() 1160 self.armature = armature_ob and armature_ob.data 1161 self.bone_data_ob = (target_ob.get("LocalBoneData:0") and target_ob) or (source_ob.get("LocalBoneData:0") and source_ob) or None 1162 1163 for index, vgs in enumerate(self.matched_vgroups): 1164 target_vg, source_vg = vgs 1165 vg_name = target_vg.name 1166 if self.using_vgroups.get(vg_name): 1167 continue 1168 1169 print(index, vg_name) 1170 new_prop = self.using_vgroups.add() 1171 new_prop.name = vg_name 1172 new_prop.index = index 1173 new_prop.value = True 1174 1175 self.layout.template_list("CNV_UL_vgroups_selector", "", self, "using_vgroups", self, "active_vgroup") 1176 self.layout.label(text="Show filters", icon='FILE_PARENT') 1177 1178 def prepare(self, context): 1179 shape_key_transfer_op.prepare(self, context) 1180 1181 target_me = self.target_ob.data 1182 source_me = self.source_ob.data 1183 1184 self.matched_vgroups = [ ( self.target_ob.vertex_groups.get(vg.name), self.source_ob.vertex_groups.get(vg.name) ) for vg in self.using_vgroups] 1185 1186 context.window_manager.progress_begin(0, len(target_me.vertices)) 1187 progress_reduce = len(target_me.vertices) // 200 + 1 1188 self.near_vert_data = [] 1189 self.near_vert_multi_total = [] 1190 near_vert_multi_total_append = self.near_vert_multi_total.append 1191 1192 for vert in target_me.vertices: 1193 new_vert_data = [] 1194 self.near_vert_data.append(new_vert_data) 1195 self.near_vert_data_append = new_vert_data.append 1196 1197 target_co = compat.mul(self.target_ob.matrix_world, vert.co) #vert.co 1198 mini_co, mini_index, mini_dist = self.kd.find(target_co) 1199 radius = mini_dist * self.extend_range 1200 diff_radius = radius - mini_dist 1201 1202 multi_total = 0.0 1203 for co, index, dist in self.kd.find_range(target_co, radius): 1204 if 0 < diff_radius: 1205 multi = (diff_radius - (dist - mini_dist)) / diff_radius 1206 else: 1207 multi = 1.0 1208 1209 avg_weight_match = 0 1210 for target_vg, source_vg in self.matched_vgroups: 1211 target_weight = 0 1212 try: 1213 target_weight = target_vg.weight(vert.index) 1214 except: 1215 pass 1216 source_weight = 0 1217 try: 1218 source_weight = source_vg.weight(index) 1219 except: 1220 pass 1221 avg_weight_match += -abs(source_weight - target_weight) + target_weight 1222 if avg_weight_match > 1: 1223 avg_weight_match = 1 1224 elif avg_weight_match < 0: 1225 avg_weight_match = 0 1226 1227 multi *= avg_weight_match 1228 self.near_vert_data_append((index, multi)) 1229 multi_total += multi 1230 near_vert_multi_total_append(multi_total) 1231 1232 if vert.index % progress_reduce == 0: 1233 context.window_manager.progress_update(vert.index) 1234 context.window_manager.progress_end() 1235 1236 self.my_iter = iter(transfer_shape_key_iter(self.target_ob, self.source_ob, binded_shape_key=self.binded_shape_key)) 1237 1238 #self.source_raw_data = numpy.ndarray(shape=(len(source_me.vertices), 3), dtype=float, order='C') 1239 #self.target_raw_data = numpy.ndarray(shape=(len(target_me.vertices), 3), dtype=float, order='C') 1240 1241 #self.binded_raw_data = numpy.ndarray(shape=len(self.source_bind_data)*3, dtype=float, order='C') 1242 #self.source_bind_data.foreach_get('co', self.binded_raw_data) 1243 #self.binded_raw_data.resize(self.binded_raw_data.size//3, 3) 1244 1245 context.window_manager.progress_begin(0, len(source_me.shape_keys.key_blocks) * len(target_me.vertices)) 1246 context.window_manager.progress_update(0) 1247 1248 invoke = CNV_OT_precision_shape_key_transfer.invoke 1249 loop = CNV_OT_precision_shape_key_transfer.loop 1250 cleanup = CNV_OT_precision_shape_key_transfer.cleanup
bl_description =
'Transfers the shape keys of other selected mesh to the active mesh, using matching vertex groups as masks'
step_size: <_PropertyDeferred, <built-in function IntProperty>, {'name': 'Step Size (low = quality, high = speed)', 'default': 1, 'min': 1, 'max': 100, 'soft_min': 1, 'soft_max': 10, 'step': 1, 'attr': 'step_size'}> =
<_PropertyDeferred, <built-in function IntProperty>, {'name': 'Step Size (low = quality, high = speed)', 'default': 1, 'min': 1, 'max': 100, 'soft_min': 1, 'soft_max': 10, 'step': 1, 'attr': 'step_size'}>
extend_range: <_PropertyDeferred, <built-in function FloatProperty>, {'name': 'Range magnification', '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': 'Range magnification', 'default': 1.1, 'min': 1.0001, 'max': 5.0, 'soft_min': 1.0001, 'soft_max': 5.0, 'step': 10, 'precision': 2, 'attr': 'extend_range'}>
using_vgroups: <_PropertyDeferred, <built-in function CollectionProperty>, {'type': <class 'CM3D2 Converter.common.CNV_SelectorItem'>, 'attr': 'using_vgroups'}> =
<_PropertyDeferred, <built-in function CollectionProperty>, {'type': <class 'CM3D2 Converter.common.CNV_SelectorItem'>, 'attr': 'using_vgroups'}>
active_vgroup: <_PropertyDeferred, <built-in function IntProperty>, {'name': 'Active Vertex Group', 'attr': 'active_vgroup'}> =
<_PropertyDeferred, <built-in function IntProperty>, {'name': 'Active Vertex Group', 'attr': 'active_vgroup'}>
@classmethod
def
poll(cls, context):
1141 @classmethod 1142 def poll(cls, context): 1143 obs = context.selected_objects 1144 if len(obs) == 2: 1145 active_ob = context.active_object 1146 for ob in obs: 1147 if ob.type != 'MESH': 1148 return False 1149 if ob.data.shape_keys and ob.name != active_ob.name: 1150 return True 1151 return False
def
draw(self, context):
1153 def draw(self, context): 1154 CNV_OT_precision_shape_key_transfer.draw(self, context) 1155 target_ob, source_ob = common.get_target_and_source_ob(context) 1156 1157 self.matched_vgroups = common.values_of_matched_keys(target_ob.vertex_groups, source_ob.vertex_groups) 1158 print(f_("len(matched) = {length}", length=len(self.matched_vgroups))) 1159 armature_ob = target_ob.find_armature() or source_ob.find_armature() 1160 self.armature = armature_ob and armature_ob.data 1161 self.bone_data_ob = (target_ob.get("LocalBoneData:0") and target_ob) or (source_ob.get("LocalBoneData:0") and source_ob) or None 1162 1163 for index, vgs in enumerate(self.matched_vgroups): 1164 target_vg, source_vg = vgs 1165 vg_name = target_vg.name 1166 if self.using_vgroups.get(vg_name): 1167 continue 1168 1169 print(index, vg_name) 1170 new_prop = self.using_vgroups.add() 1171 new_prop.name = vg_name 1172 new_prop.index = index 1173 new_prop.value = True 1174 1175 self.layout.template_list("CNV_UL_vgroups_selector", "", self, "using_vgroups", self, "active_vgroup") 1176 self.layout.label(text="Show filters", icon='FILE_PARENT')
def
prepare(self, context):
1178 def prepare(self, context): 1179 shape_key_transfer_op.prepare(self, context) 1180 1181 target_me = self.target_ob.data 1182 source_me = self.source_ob.data 1183 1184 self.matched_vgroups = [ ( self.target_ob.vertex_groups.get(vg.name), self.source_ob.vertex_groups.get(vg.name) ) for vg in self.using_vgroups] 1185 1186 context.window_manager.progress_begin(0, len(target_me.vertices)) 1187 progress_reduce = len(target_me.vertices) // 200 + 1 1188 self.near_vert_data = [] 1189 self.near_vert_multi_total = [] 1190 near_vert_multi_total_append = self.near_vert_multi_total.append 1191 1192 for vert in target_me.vertices: 1193 new_vert_data = [] 1194 self.near_vert_data.append(new_vert_data) 1195 self.near_vert_data_append = new_vert_data.append 1196 1197 target_co = compat.mul(self.target_ob.matrix_world, vert.co) #vert.co 1198 mini_co, mini_index, mini_dist = self.kd.find(target_co) 1199 radius = mini_dist * self.extend_range 1200 diff_radius = radius - mini_dist 1201 1202 multi_total = 0.0 1203 for co, index, dist in self.kd.find_range(target_co, radius): 1204 if 0 < diff_radius: 1205 multi = (diff_radius - (dist - mini_dist)) / diff_radius 1206 else: 1207 multi = 1.0 1208 1209 avg_weight_match = 0 1210 for target_vg, source_vg in self.matched_vgroups: 1211 target_weight = 0 1212 try: 1213 target_weight = target_vg.weight(vert.index) 1214 except: 1215 pass 1216 source_weight = 0 1217 try: 1218 source_weight = source_vg.weight(index) 1219 except: 1220 pass 1221 avg_weight_match += -abs(source_weight - target_weight) + target_weight 1222 if avg_weight_match > 1: 1223 avg_weight_match = 1 1224 elif avg_weight_match < 0: 1225 avg_weight_match = 0 1226 1227 multi *= avg_weight_match 1228 self.near_vert_data_append((index, multi)) 1229 multi_total += multi 1230 near_vert_multi_total_append(multi_total) 1231 1232 if vert.index % progress_reduce == 0: 1233 context.window_manager.progress_update(vert.index) 1234 context.window_manager.progress_end() 1235 1236 self.my_iter = iter(transfer_shape_key_iter(self.target_ob, self.source_ob, binded_shape_key=self.binded_shape_key)) 1237 1238 #self.source_raw_data = numpy.ndarray(shape=(len(source_me.vertices), 3), dtype=float, order='C') 1239 #self.target_raw_data = numpy.ndarray(shape=(len(target_me.vertices), 3), dtype=float, order='C') 1240 1241 #self.binded_raw_data = numpy.ndarray(shape=len(self.source_bind_data)*3, dtype=float, order='C') 1242 #self.source_bind_data.foreach_get('co', self.binded_raw_data) 1243 #self.binded_raw_data.resize(self.binded_raw_data.size//3, 3) 1244 1245 context.window_manager.progress_begin(0, len(source_me.shape_keys.key_blocks) * len(target_me.vertices)) 1246 context.window_manager.progress_update(0)
def
loop(self, context):
692 def loop(self, context): 693 #bpy.ops.object.mode_set(mode='OBJECT') 694 source_shape_key_index, target_shape_key, binded_shape_key_data, source_shape_key_data, target_shape_key_data = next(self.my_iter, (-1, None, None, None, None)) 695 if not target_shape_key: 696 context.window_manager.progress_end() 697 return True 698 699 #print("Loop for " + target_shape_key.name) 700 701 #context.window_manager.progress_begin( 0, len(self.source_ob.shape_keys.key_blocks) * len(target_ob.data.vertices) ) 702 progress = source_shape_key_index * len(self.target_ob.data.vertices) 703 #context.window_manager.progress_update( progress ) 704 705 diff_data = [None] * len(source_shape_key_data) 706 near_diff_co = mathutils.Vector.Fill(3, 0) # Creates a vector of length 3 filled with 0's 707 def check(index, near_diff_co=near_diff_co): 708 near_diff_co.zero() # This should be faster than creating a new vector every time 709 710 if self.near_vert_multi_total[index] > 0: 711 for near_index, near_multi in self.near_vert_data[index]: 712 diff_data[near_index] = diff_data[near_index] or source_shape_key_data[near_index].co - binded_shape_key_data[near_index].co 713 near_diff_co += diff_data[near_index] * near_multi 714 715 near_diff_co /= self.near_vert_multi_total[index] 716 717 context.window_manager.progress_update( progress + index ) 718 719 if near_diff_co.length > 2e-126: # 2e-126 is the smallest float != 0 720 target_shape_key_data[index].co += near_diff_co 721 return True 722 723 is_changed = False 724 just_changed = False 725 if self.step_size > 1: 726 found_more = False 727 for i in range(0, len(target_shape_key_data), self.step_size): 728 729 if check(i) or found_more: 730 is_changed = True 731 found_more = False 732 if not just_changed: 733 for j in range(i-self.step_size+1, i): 734 if j < len(target_shape_key_data) and j > 0: 735 found_more = check(j) or found_more 736 for k in range(i+1, i+self.step_size): 737 if k < len(target_shape_key_data) and k > 0: 738 found_more = check(k) or found_more 739 just_changed = True 740 else: 741 just_changed = False 742 743 else: # if self.step_size == 1: 744 for index, binded_vert, source_vert in zip(range(len(diff_data)), binded_shape_key_data, source_shape_key_data): 745 diff_data[index] = source_vert.co - binded_vert.co 746 if diff_data[index].length > 2e-126: 747 just_changed = True 748 749 if just_changed: 750 for target_vert, near_indices, near_total in zip(target_shape_key_data, self.near_vert_data, self.near_vert_multi_total): 751 near_diff_co.zero() # This should be faster than creating a new vector every time 752 753 if near_total > 0: 754 for near_index, near_multi in near_indices: 755 near_diff_co += diff_data[near_index] * near_multi 756 757 near_diff_co /= near_total 758 759 if near_diff_co.length > 2e-126: # 2e-126 is the smallest float != 0 760 target_vert.co += near_diff_co 761 is_changed = True 762 763 progress += 1 764 context.window_manager.progress_update( progress ) 765 else: 766 context.window_manager.progress_update( progress + len(target_shape_key_data) ) 767 768 self.is_shapeds[target_shape_key.name] = self.is_shapeds.get(target_shape_key.name) or is_changed 769 self.my_iter.update() # only call this when done with current iteration. 770 #bpy.ops.object.mode_set(mode='SCULPT') # Preview shape keys while transfering
Inherited Members
- shape_key_transfer_op
- is_first_remove_all
- is_remove_empty
- is_bind_current_mix
- subdivide_number
- target_ob
- source_ob
- og_source_ob
- is_finished
- is_canceled
- pre_mode
- pre_selected
- binded_shape_key
- kd
- is_shapeds
- execute
- modal
- finish
- cancel
- 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_multiply_shape_key1254@compat.BlRegister() 1255class CNV_OT_multiply_shape_key(bpy.types.Operator): 1256 bl_idname = 'object.multiply_shape_key' 1257 bl_label = "シェイプキーの変形に乗算" 1258 bl_description = "シェイプキーの変形に数値を乗算し、変形の強度を増減させます" 1259 bl_options = {'REGISTER', 'UNDO'} 1260 1261 multi = bpy.props.FloatProperty(name="倍率", description="シェイプキーの拡大率です", default=1.1, min=-10, max=10, soft_min=-10, soft_max=10, step=10, precision=2) 1262 items = [ 1263 ('ACTIVE', "アクティブのみ", "", 'HAND', 1), 1264 ('UP', "アクティブより上", "", 'TRIA_UP_BAR', 2), 1265 ('DOWN', "アクティブより下", "", 'TRIA_DOWN_BAR', 3), 1266 ('ALL', "全て", "", 'ARROW_LEFTRIGHT', 4), 1267 ] 1268 mode = bpy.props.EnumProperty(items=items, name="対象", default='ACTIVE') 1269 1270 @classmethod 1271 def poll(cls, context): 1272 if context.active_object: 1273 ob = context.active_object 1274 if ob.type == 'MESH': 1275 return ob.active_shape_key 1276 return False 1277 1278 def invoke(self, context, event): 1279 return context.window_manager.invoke_props_dialog(self) 1280 1281 def draw(self, context): 1282 self.layout.prop(self, 'multi', icon='ARROW_LEFTRIGHT') 1283 self.layout.prop(self, 'mode', icon='VIEWZOOM') 1284 1285 def execute(self, context): 1286 ob = context.active_object 1287 me = ob.data 1288 shape_keys = me.shape_keys 1289 pre_mode = ob.mode 1290 bpy.ops.object.mode_set(mode='OBJECT') 1291 1292 target_shapes = [] 1293 if self.mode == 'ACTIVE': 1294 target_shapes.append(ob.active_shape_key) 1295 elif self.mode == 'UP': 1296 for index, key_block in enumerate(shape_keys.key_blocks): 1297 if index <= ob.active_shape_key_index: 1298 target_shapes.append(key_block) 1299 elif self.mode == 'UP': 1300 for index, key_block in enumerate(shape_keys.key_blocks): 1301 if ob.active_shape_key_index <= index: 1302 target_shapes.append(key_block) 1303 elif self.mode == 'ALL': 1304 for key_block in shape_keys.key_blocks: 1305 target_shapes.append(key_block) 1306 1307 for shape in target_shapes: 1308 data = shape.data 1309 for i, vert in enumerate(me.vertices): 1310 diff = data[i].co - vert.co 1311 diff *= self.multi 1312 data[i].co = vert.co + diff 1313 bpy.ops.object.mode_set(mode=pre_mode) 1314 return {'FINISHED'}
multi: <_PropertyDeferred, <built-in function FloatProperty>, {'name': '倍率', 'description': 'シェイプキーの拡大率です', 'default': 1.1, 'min': -10, 'max': 10, 'soft_min': -10, 'soft_max': 10, 'step': 10, 'precision': 2, 'attr': 'multi'}> =
<_PropertyDeferred, <built-in function FloatProperty>, {'name': '倍率', 'description': 'シェイプキーの拡大率です', 'default': 1.1, 'min': -10, 'max': 10, 'soft_min': -10, 'soft_max': 10, 'step': 10, 'precision': 2, 'attr': 'multi'}>
items =
[('ACTIVE', 'アクティブのみ', '', 'HAND', 1), ('UP', 'アクティブより上', '', 'TRIA_UP_BAR', 2), ('DOWN', 'アクティブより下', '', 'TRIA_DOWN_BAR', 3), ('ALL', '全て', '', 'ARROW_LEFTRIGHT', 4)]
mode: <_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': 'mode'}> =
<_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': 'mode'}>
def
execute(self, context):
1285 def execute(self, context): 1286 ob = context.active_object 1287 me = ob.data 1288 shape_keys = me.shape_keys 1289 pre_mode = ob.mode 1290 bpy.ops.object.mode_set(mode='OBJECT') 1291 1292 target_shapes = [] 1293 if self.mode == 'ACTIVE': 1294 target_shapes.append(ob.active_shape_key) 1295 elif self.mode == 'UP': 1296 for index, key_block in enumerate(shape_keys.key_blocks): 1297 if index <= ob.active_shape_key_index: 1298 target_shapes.append(key_block) 1299 elif self.mode == 'UP': 1300 for index, key_block in enumerate(shape_keys.key_blocks): 1301 if ob.active_shape_key_index <= index: 1302 target_shapes.append(key_block) 1303 elif self.mode == 'ALL': 1304 for key_block in shape_keys.key_blocks: 1305 target_shapes.append(key_block) 1306 1307 for shape in target_shapes: 1308 data = shape.data 1309 for i, vert in enumerate(me.vertices): 1310 diff = data[i].co - vert.co 1311 diff *= self.multi 1312 data[i].co = vert.co + diff 1313 bpy.ops.object.mode_set(mode=pre_mode) 1314 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_shape_key1318@compat.BlRegister() 1319class CNV_OT_blur_shape_key(bpy.types.Operator): 1320 bl_idname = 'object.blur_shape_key' 1321 bl_label = "シェイプキーぼかし" 1322 bl_description = "アクティブ、もしくは全てのシェイプキーをぼかします" 1323 bl_options = {'REGISTER', 'UNDO'} 1324 1325 items = [ 1326 ('ACTIVE', "アクティブのみ", "", 'HAND', 1), 1327 ('UP', "アクティブより上", "", 'TRIA_UP_BAR', 2), 1328 ('DOWN', "アクティブより下", "", 'TRIA_DOWN_BAR', 3), 1329 ('ALL', "全て", "", 'ARROW_LEFTRIGHT', 4), 1330 ] 1331 target = bpy.props.EnumProperty(items=items, name="対象", default='ACTIVE') 1332 radius = bpy.props.FloatProperty(name="範囲倍率", default=3, min=0.1, max=50, soft_min=0.1, soft_max=50, step=50, precision=2) 1333 strength = bpy.props.IntProperty(name="強さ", default=1, min=1, max=10, soft_min=1, soft_max=10) 1334 items = [ 1335 ('BOTH', "増減両方", "", 'AUTOMERGE_ON', 1), 1336 ('ADD', "増加のみ", "", 'TRIA_UP', 2), 1337 ('SUB', "減少のみ", "", 'TRIA_DOWN', 3), 1338 ] 1339 effect = bpy.props.EnumProperty(items=items, name="ぼかし効果", default='BOTH') 1340 items = [ 1341 ('LINER', "ライナー", "", 'LINCURVE', 1), 1342 ('SMOOTH1', "スムーズ1", "", 'SMOOTHCURVE', 2), 1343 ('SMOOTH2', "スムーズ2", "", 'SMOOTHCURVE', 3), 1344 ] 1345 blend = bpy.props.EnumProperty(items=items, name="減衰タイプ", default='LINER') 1346 1347 @classmethod 1348 def poll(cls, context): 1349 ob = context.active_object 1350 if ob and ob.type == 'MESH': 1351 me = ob.data 1352 return me.shape_keys 1353 return False 1354 1355 def invoke(self, context, event): 1356 return context.window_manager.invoke_props_dialog(self) 1357 1358 def draw(self, context): 1359 self.layout.prop(self, 'target', icon='VIEWZOOM') 1360 self.layout.prop(self, 'radius', icon='RADIOBUT_OFF') 1361 self.layout.prop(self, 'strength', icon='ARROW_LEFTRIGHT') 1362 self.layout.prop(self, 'effect', icon='BRUSH_BLUR') 1363 self.layout.prop(self, 'blend', icon='IPO_SINE') 1364 1365 def execute(self, context): 1366 ob = context.active_object 1367 me = ob.data 1368 1369 pre_mode = ob.mode 1370 bpy.ops.object.mode_set(mode='OBJECT') 1371 1372 bm = bmesh.new() 1373 bm.from_mesh(me) 1374 edge_lengths = [e.calc_length() for e in bm.edges] 1375 bm.free() 1376 1377 edge_lengths.sort() 1378 average_edge_length = sum(edge_lengths) / len(edge_lengths) 1379 center_index = int((len(edge_lengths) - 1) / 2.0) 1380 average_edge_length = (average_edge_length + edge_lengths[center_index]) / 2 1381 radius = average_edge_length * self.radius 1382 1383 context.window_manager.progress_begin(0, len(me.vertices)) 1384 progress_reduce = len(me.vertices) // 200 + 1 1385 near_vert_data = [] 1386 kd = mathutils.kdtree.KDTree(len(me.vertices)) 1387 for vert in me.vertices: 1388 kd.insert(vert.co.copy(), vert.index) 1389 kd.balance() 1390 for vert in me.vertices: 1391 near_vert_data.append([]) 1392 near_vert_data_append = near_vert_data[-1].append 1393 for co, index, dist in kd.find_range(vert.co, radius): 1394 multi = (radius - dist) / radius 1395 if self.blend == 'SMOOTH1': 1396 multi = common.in_out_quad_blend(multi) 1397 elif self.blend == 'SMOOTH2': 1398 multi = common.bezier_blend(multi) 1399 near_vert_data_append((index, multi)) 1400 if vert.index % progress_reduce == 0: 1401 context.window_manager.progress_update(vert.index) 1402 context.window_manager.progress_end() 1403 1404 target_shape_keys = [] 1405 if self.target == 'ACTIVE': 1406 target_shape_keys.append(ob.active_shape_key) 1407 elif self.target == 'UP': 1408 for index, shape_key in enumerate(me.shape_keys.key_blocks): 1409 if index <= ob.active_shape_key_index: 1410 target_shape_keys.append(shape_key) 1411 elif self.target == 'DOWN': 1412 for index, shape_key in enumerate(me.shape_keys.key_blocks): 1413 if ob.active_shape_key_index <= index: 1414 target_shape_keys.append(shape_key) 1415 elif self.target == 'ALL': 1416 for index, shape_key in enumerate(me.shape_keys.key_blocks): 1417 target_shape_keys.append(shape_key) 1418 1419 progress_total = len(target_shape_keys) * self.strength * len(me.vertices) 1420 context.window_manager.progress_begin(0, progress_total) 1421 progress_reduce = progress_total // 200 + 1 1422 progress_count = 0 1423 for strength_count in range(self.strength): 1424 for shape_key in target_shape_keys: 1425 1426 shapes = [] 1427 shapes_append = shapes.append 1428 for index, vert in enumerate(me.vertices): 1429 co = shape_key.data[index].co - vert.co 1430 shapes_append(co) 1431 1432 for vert in me.vertices: 1433 1434 target_shape = shapes[vert.index] 1435 1436 total_shape = mathutils.Vector() 1437 total_multi = 0.0 1438 for index, multi in near_vert_data[vert.index]: 1439 co = shapes[index] 1440 if self.effect == 'ADD': 1441 if target_shape.length <= co.length: 1442 total_shape += co * multi 1443 total_multi += multi 1444 elif self.effect == 'SUB': 1445 if co.length <= target_shape.length: 1446 total_shape += co * multi 1447 total_multi += multi 1448 else: 1449 total_shape += co * multi 1450 total_multi += multi 1451 1452 if 0 < total_multi: 1453 average_shape = total_shape / total_multi 1454 else: 1455 average_shape = mathutils.Vector() 1456 1457 shape_key.data[vert.index].co = vert.co + average_shape 1458 1459 progress_count += 1 1460 if progress_count % progress_reduce == 0: 1461 context.window_manager.progress_update(progress_count) 1462 1463 context.window_manager.progress_end() 1464 bpy.ops.object.mode_set(mode=pre_mode) 1465 return {'FINISHED'}
items =
[('LINER', 'ライナー', '', 'LINCURVE', 1), ('SMOOTH1', 'スムーズ1', '', 'SMOOTHCURVE', 2), ('SMOOTH2', 'スムーズ2', '', 'SMOOTHCURVE', 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'}>
blend: <_PropertyDeferred, <built-in function EnumProperty>, {'items': [('LINER', 'ライナー', '', 'LINCURVE', 1), ('SMOOTH1', 'スムーズ1', '', 'SMOOTHCURVE', 2), ('SMOOTH2', 'スムーズ2', '', 'SMOOTHCURVE', 3)], 'name': '減衰タイプ', 'default': 'LINER', 'attr': 'blend'}> =
<_PropertyDeferred, <built-in function EnumProperty>, {'items': [('LINER', 'ライナー', '', 'LINCURVE', 1), ('SMOOTH1', 'スムーズ1', '', 'SMOOTHCURVE', 2), ('SMOOTH2', 'スムーズ2', '', 'SMOOTHCURVE', 3)], 'name': '減衰タイプ', 'default': 'LINER', 'attr': 'blend'}>
def
draw(self, context):
1358 def draw(self, context): 1359 self.layout.prop(self, 'target', icon='VIEWZOOM') 1360 self.layout.prop(self, 'radius', icon='RADIOBUT_OFF') 1361 self.layout.prop(self, 'strength', icon='ARROW_LEFTRIGHT') 1362 self.layout.prop(self, 'effect', icon='BRUSH_BLUR') 1363 self.layout.prop(self, 'blend', icon='IPO_SINE')
def
execute(self, context):
1365 def execute(self, context): 1366 ob = context.active_object 1367 me = ob.data 1368 1369 pre_mode = ob.mode 1370 bpy.ops.object.mode_set(mode='OBJECT') 1371 1372 bm = bmesh.new() 1373 bm.from_mesh(me) 1374 edge_lengths = [e.calc_length() for e in bm.edges] 1375 bm.free() 1376 1377 edge_lengths.sort() 1378 average_edge_length = sum(edge_lengths) / len(edge_lengths) 1379 center_index = int((len(edge_lengths) - 1) / 2.0) 1380 average_edge_length = (average_edge_length + edge_lengths[center_index]) / 2 1381 radius = average_edge_length * self.radius 1382 1383 context.window_manager.progress_begin(0, len(me.vertices)) 1384 progress_reduce = len(me.vertices) // 200 + 1 1385 near_vert_data = [] 1386 kd = mathutils.kdtree.KDTree(len(me.vertices)) 1387 for vert in me.vertices: 1388 kd.insert(vert.co.copy(), vert.index) 1389 kd.balance() 1390 for vert in me.vertices: 1391 near_vert_data.append([]) 1392 near_vert_data_append = near_vert_data[-1].append 1393 for co, index, dist in kd.find_range(vert.co, radius): 1394 multi = (radius - dist) / radius 1395 if self.blend == 'SMOOTH1': 1396 multi = common.in_out_quad_blend(multi) 1397 elif self.blend == 'SMOOTH2': 1398 multi = common.bezier_blend(multi) 1399 near_vert_data_append((index, multi)) 1400 if vert.index % progress_reduce == 0: 1401 context.window_manager.progress_update(vert.index) 1402 context.window_manager.progress_end() 1403 1404 target_shape_keys = [] 1405 if self.target == 'ACTIVE': 1406 target_shape_keys.append(ob.active_shape_key) 1407 elif self.target == 'UP': 1408 for index, shape_key in enumerate(me.shape_keys.key_blocks): 1409 if index <= ob.active_shape_key_index: 1410 target_shape_keys.append(shape_key) 1411 elif self.target == 'DOWN': 1412 for index, shape_key in enumerate(me.shape_keys.key_blocks): 1413 if ob.active_shape_key_index <= index: 1414 target_shape_keys.append(shape_key) 1415 elif self.target == 'ALL': 1416 for index, shape_key in enumerate(me.shape_keys.key_blocks): 1417 target_shape_keys.append(shape_key) 1418 1419 progress_total = len(target_shape_keys) * self.strength * len(me.vertices) 1420 context.window_manager.progress_begin(0, progress_total) 1421 progress_reduce = progress_total // 200 + 1 1422 progress_count = 0 1423 for strength_count in range(self.strength): 1424 for shape_key in target_shape_keys: 1425 1426 shapes = [] 1427 shapes_append = shapes.append 1428 for index, vert in enumerate(me.vertices): 1429 co = shape_key.data[index].co - vert.co 1430 shapes_append(co) 1431 1432 for vert in me.vertices: 1433 1434 target_shape = shapes[vert.index] 1435 1436 total_shape = mathutils.Vector() 1437 total_multi = 0.0 1438 for index, multi in near_vert_data[vert.index]: 1439 co = shapes[index] 1440 if self.effect == 'ADD': 1441 if target_shape.length <= co.length: 1442 total_shape += co * multi 1443 total_multi += multi 1444 elif self.effect == 'SUB': 1445 if co.length <= target_shape.length: 1446 total_shape += co * multi 1447 total_multi += multi 1448 else: 1449 total_shape += co * multi 1450 total_multi += multi 1451 1452 if 0 < total_multi: 1453 average_shape = total_shape / total_multi 1454 else: 1455 average_shape = mathutils.Vector() 1456 1457 shape_key.data[vert.index].co = vert.co + average_shape 1458 1459 progress_count += 1 1460 if progress_count % progress_reduce == 0: 1461 context.window_manager.progress_update(progress_count) 1462 1463 context.window_manager.progress_end() 1464 bpy.ops.object.mode_set(mode=pre_mode) 1465 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_change_base_shape_key1469@compat.BlRegister() 1470class CNV_OT_change_base_shape_key(bpy.types.Operator): 1471 bl_idname = 'object.change_base_shape_key' 1472 bl_label = "このシェイプキーをベースに" 1473 bl_description = "アクティブなシェイプキーを他のシェイプキーのベースにします" 1474 bl_options = {'REGISTER', 'UNDO'} 1475 1476 is_deform_mesh = bpy.props.BoolProperty(name="素メッシュを調整", default=True) 1477 is_deform_other_shape = bpy.props.BoolProperty(name="他シェイプを調整", default=True) 1478 1479 @classmethod 1480 def poll(cls, context): 1481 ob = context.active_object 1482 return ob and ob.type == 'MESH' and 1 <= ob.active_shape_key_index 1483 1484 def invoke(self, context, event): 1485 return context.window_manager.invoke_props_dialog(self) 1486 1487 def draw(self, context): 1488 self.layout.prop(self, 'is_deform_mesh', icon='MESH_DATA') 1489 self.layout.prop(self, 'is_deform_other_shape', icon='SHAPEKEY_DATA') 1490 1491 def execute(self, context): 1492 ob = context.active_object 1493 me = ob.data 1494 1495 pre_mode = ob.mode 1496 bpy.ops.object.mode_set(mode='OBJECT') 1497 1498 target_shape_key = ob.active_shape_key 1499 old_shape_key = me.shape_keys.key_blocks[0] 1500 1501 # TOP指定でindex=1になるケースは、さらにもう一度UP 1502 bpy.ops.object.shape_key_move(type='TOP') 1503 if ob.active_shape_key_index == 1: 1504 bpy.ops.object.shape_key_move(type='UP') 1505 1506 target_shape_key.relative_key = target_shape_key 1507 old_shape_key.relative_key = target_shape_key 1508 1509 if self.is_deform_mesh: 1510 for vert in me.vertices: 1511 vert.co = target_shape_key.data[vert.index].co.copy() 1512 1513 if self.is_deform_other_shape: 1514 for shape_key in me.shape_keys.key_blocks: 1515 if shape_key.name == target_shape_key.name or shape_key.name == old_shape_key.name: 1516 continue 1517 if shape_key.relative_key.name == old_shape_key.name: 1518 shape_key.relative_key = target_shape_key 1519 for vert in me.vertices: 1520 diff_co = target_shape_key.data[vert.index].co - old_shape_key.data[vert.index].co 1521 shape_key.data[vert.index].co = shape_key.data[vert.index].co + diff_co 1522 1523 bpy.ops.object.mode_set(mode=pre_mode) 1524 return {'FINISHED'}
is_deform_mesh: <_PropertyDeferred, <built-in function BoolProperty>, {'name': '素メッシュを調整', 'default': True, 'attr': 'is_deform_mesh'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': '素メッシュを調整', 'default': True, 'attr': 'is_deform_mesh'}>
is_deform_other_shape: <_PropertyDeferred, <built-in function BoolProperty>, {'name': '他シェイプを調整', 'default': True, 'attr': 'is_deform_other_shape'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': '他シェイプを調整', 'default': True, 'attr': 'is_deform_other_shape'}>
def
execute(self, context):
1491 def execute(self, context): 1492 ob = context.active_object 1493 me = ob.data 1494 1495 pre_mode = ob.mode 1496 bpy.ops.object.mode_set(mode='OBJECT') 1497 1498 target_shape_key = ob.active_shape_key 1499 old_shape_key = me.shape_keys.key_blocks[0] 1500 1501 # TOP指定でindex=1になるケースは、さらにもう一度UP 1502 bpy.ops.object.shape_key_move(type='TOP') 1503 if ob.active_shape_key_index == 1: 1504 bpy.ops.object.shape_key_move(type='UP') 1505 1506 target_shape_key.relative_key = target_shape_key 1507 old_shape_key.relative_key = target_shape_key 1508 1509 if self.is_deform_mesh: 1510 for vert in me.vertices: 1511 vert.co = target_shape_key.data[vert.index].co.copy() 1512 1513 if self.is_deform_other_shape: 1514 for shape_key in me.shape_keys.key_blocks: 1515 if shape_key.name == target_shape_key.name or shape_key.name == old_shape_key.name: 1516 continue 1517 if shape_key.relative_key.name == old_shape_key.name: 1518 shape_key.relative_key = target_shape_key 1519 for vert in me.vertices: 1520 diff_co = target_shape_key.data[vert.index].co - old_shape_key.data[vert.index].co 1521 shape_key.data[vert.index].co = shape_key.data[vert.index].co + diff_co 1522 1523 bpy.ops.object.mode_set(mode=pre_mode) 1524 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_copy_shape_key_values1528@compat.BlRegister() 1529class CNV_OT_copy_shape_key_values(bpy.types.Operator): 1530 bl_idname = 'object.copy_shape_key_values' 1531 bl_label = "Copy shape key values" 1532 bl_description = "Copy the shape key values from the other selected mesh" 1533 bl_options = {'REGISTER', 'UNDO'} 1534 1535 use_drivers = bpy.props.BoolProperty(name="Apply as drivers", default=False) 1536 1537 @classmethod 1538 def poll(cls, context): 1539 obs = context.selected_objects 1540 if len(obs) == 2: 1541 active_ob = context.active_object 1542 for ob in obs: 1543 if ob.type != 'MESH': 1544 return False 1545 if ob.data.shape_keys and ob.name != active_ob.name: 1546 return True 1547 return False 1548 1549 def invoke(self, context, event): 1550 return context.window_manager.invoke_props_dialog(self) 1551 1552 def draw(self, context): 1553 self.layout.prop(self, 'use_drivers', icon='DRIVER') 1554 1555 def execute(self, context): 1556 target_ob, source_ob = common.get_target_and_source_ob(context) 1557 1558 source_sks = source_ob.data.shape_keys.key_blocks 1559 target_sks = target_ob.data.shape_keys.key_blocks 1560 for source_sk, target_sk in common.values_of_matched_keys(source_sks, target_sks): 1561 if not self.use_drivers: 1562 target_sk.value = source_sk.value 1563 else: 1564 driver = target_sk.driver_add('value').driver 1565 driver.type = 'AVERAGE' 1566 1567 driver_var = driver.variables.new() if len(driver.variables) < 1 else driver.variables[0] 1568 driver_var.type = 'SINGLE_PROP' 1569 1570 driver_target = driver_var.targets[0] 1571 driver_target.id_type = 'KEY' 1572 driver_target.id = source_sk.id_data 1573 driver_target.data_path = source_sk.path_from_id('value') 1574 1575 1576 return {'FINISHED'}
use_drivers: <_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Apply as drivers', 'default': False, 'attr': 'use_drivers'}> =
<_PropertyDeferred, <built-in function BoolProperty>, {'name': 'Apply as drivers', 'default': False, 'attr': 'use_drivers'}>
@classmethod
def
poll(cls, context):
1537 @classmethod 1538 def poll(cls, context): 1539 obs = context.selected_objects 1540 if len(obs) == 2: 1541 active_ob = context.active_object 1542 for ob in obs: 1543 if ob.type != 'MESH': 1544 return False 1545 if ob.data.shape_keys and ob.name != active_ob.name: 1546 return True 1547 return False
def
execute(self, context):
1555 def execute(self, context): 1556 target_ob, source_ob = common.get_target_and_source_ob(context) 1557 1558 source_sks = source_ob.data.shape_keys.key_blocks 1559 target_sks = target_ob.data.shape_keys.key_blocks 1560 for source_sk, target_sk in common.values_of_matched_keys(source_sks, target_sks): 1561 if not self.use_drivers: 1562 target_sk.value = source_sk.value 1563 else: 1564 driver = target_sk.driver_add('value').driver 1565 driver.type = 'AVERAGE' 1566 1567 driver_var = driver.variables.new() if len(driver.variables) < 1 else driver.variables[0] 1568 driver_var.type = 'SINGLE_PROP' 1569 1570 driver_target = driver_var.targets[0] 1571 driver_target.id_type = 'KEY' 1572 driver_target.id = source_sk.id_data 1573 driver_target.data_path = source_sk.path_from_id('value') 1574 1575 1576 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