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()
transfer_shape_key_iter(target_ob, source_ob, binded_shape_key=None)
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]
index = -1
target_ob = None
source_ob = None
binded_shape_key = None
binded_shape_key_data = None
source_iter = None
source_shape_key_data = None
target_shape_key_data = None
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
def free(self, destructive=False):
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()
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'}>
target_ob = None
source_ob = None
og_source_ob = None
is_finished = False
is_canceled = False
pre_mode = None
pre_selected = None
binded_shape_key = None
kd = None
is_shapeds = {}
def draw(self, context):
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')
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 cancel(self, context):
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)
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_transfer(shape_key_transfer_op, bpy_types.Operator):
423@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)
bl_idname = 'object.quick_shape_key_transfer'
bl_label = 'クイック・シェイプキー転送'
bl_description = 'アクティブなメッシュに他の選択メッシュのシェイプキーを高速で転送します'
bl_options = {'REGISTER', 'UNDO'}
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'}>
near_vert_indexs = []
my_iter = None
@classmethod
def poll(cls, context):
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
def invoke(self, context, event):
447    def invoke(self, context, event):
448        return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
450    def draw(self, context):
451        shape_key_transfer_op.draw(self, context)
452        self.layout.prop(self, '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.
def cleanup(self, context):
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)
bl_rna = <bpy_struct, Struct("OBJECT_OT_quick_shape_key_transfer")>
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_hidden
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_transfer(shape_key_transfer_op, bpy_types.Operator):
520@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)
bl_idname = 'object.precision_shape_key_transfer'
bl_label = '空間ぼかし・シェイプキー転送'
bl_description = 'アクティブなメッシュに他の選択メッシュのシェイプキーを遠いほどぼかして転送します'
bl_options = {'REGISTER', 'UNDO'}
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'}>
near_vert_data = []
near_vert_multi_total = []
my_iter = None
@classmethod
def poll(cls, context):
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
def invoke(self, context, event):
551    def invoke(self, context, event):
552        return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
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')
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
def cleanup(self, context):
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)
bl_rna = <bpy_struct, Struct("OBJECT_OT_precision_shape_key_transfer")>
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_hidden
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_selector(bpy_types.UIList):
 851@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
bl_label = 'Vertex Groups Selector'
bl_options = {'DEFAULT_CLOSED'}
bl_region_type = 'WINDOW'
bl_space_type = 'PROPERTIES'
VGROUP_EMPTY = 2
VGROUP_DEFORM = 1
armature = None
local_bone_names = None
cached_values = {}
expanded_layout = False
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
bl_rna = <bpy_struct, Struct("CNV_UL_vgroups_selector")>
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_hidden
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_transfer(shape_key_transfer_op, bpy_types.Operator):
1118@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_idname = 'object.weighted_shape_key_transfer'
bl_label = 'Weighted shape key transfer'
bl_description = 'Transfers the shape keys of other selected mesh to the active mesh, using matching vertex groups as masks'
bl_options = {'REGISTER', 'UNDO'}
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'}>
near_vert_data = []
near_vert_multi_total = []
my_iter = None
matched_vgroups = []
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'}>
armature = None
bone_data_ob = None
@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 invoke(self, context, event):
551    def invoke(self, context, event):
552        return context.window_manager.invoke_props_dialog(self)
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
def cleanup(self, context):
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)
bl_rna = <bpy_struct, Struct("OBJECT_OT_weighted_shape_key_transfer")>
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_hidden
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_key(bpy_types.Operator):
1254@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'}
bl_idname = 'object.multiply_shape_key'
bl_label = 'シェイプキーの変形に乗算'
bl_description = 'シェイプキーの変形に数値を乗算し、変形の強度を増減させます'
bl_options = {'REGISTER', 'UNDO'}
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'}>
@classmethod
def poll(cls, context):
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
def invoke(self, context, event):
1278    def invoke(self, context, event):
1279        return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
1281    def draw(self, context):
1282        self.layout.prop(self, 'multi', icon='ARROW_LEFTRIGHT')
1283        self.layout.prop(self, 'mode', icon='VIEWZOOM')
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'}
bl_rna = <bpy_struct, Struct("OBJECT_OT_multiply_shape_key")>
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_hidden
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_key(bpy_types.Operator):
1318@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'}
bl_idname = 'object.blur_shape_key'
bl_label = 'シェイプキーぼかし'
bl_description = 'アクティブ、もしくは全てのシェイプキーをぼかします'
bl_options = {'REGISTER', 'UNDO'}
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'}>
@classmethod
def poll(cls, context):
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
def invoke(self, context, event):
1355    def invoke(self, context, event):
1356        return context.window_manager.invoke_props_dialog(self)
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'}
bl_rna = <bpy_struct, Struct("OBJECT_OT_blur_shape_key")>
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_hidden
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_key(bpy_types.Operator):
1469@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'}
bl_idname = 'object.change_base_shape_key'
bl_label = 'このシェイプキーをベースに'
bl_description = 'アクティブなシェイプキーを他のシェイプキーのベースにします'
bl_options = {'REGISTER', 'UNDO'}
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'}>
@classmethod
def poll(cls, context):
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
def invoke(self, context, event):
1484    def invoke(self, context, event):
1485        return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
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')
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'}
bl_rna = <bpy_struct, Struct("OBJECT_OT_change_base_shape_key")>
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_hidden
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_values(bpy_types.Operator):
1528@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'}
bl_idname = 'object.copy_shape_key_values'
bl_label = 'Copy shape key values'
bl_description = 'Copy the shape key values from the other selected mesh'
bl_options = {'REGISTER', 'UNDO'}
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 invoke(self, context, event):
1549    def invoke(self, context, event):
1550        return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
1552    def draw(self, context):
1553        self.layout.prop(self, 'use_drivers', icon='DRIVER')
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'}
bl_rna = <bpy_struct, Struct("OBJECT_OT_copy_shape_key_values")>
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_hidden
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