bpy.ops系は遅いのですよ・・・。
あとどれがアクティブでセレクトなのかとか後で改造するときに調べるの大変なのでなるべくbpy.data系を使っていきたい今日この頃・・・
拡大縮小
import bpy #bpy.ops系(ローカル座標基準の場合のみ) bpy.ops.transform.resize(value=(a, b, c), orient_type='LOCAL') #data系 obj = bpy.context.object obj.scale=[obj.scale[0] * a , obj.scale[1] * b , obj.scale[2] * c] #あるいは s = [1,2,3] obj.scale = [x * y for (x, y) in zip(obj.scale, s)] #bpy.ops系(ローカル座標と位置指定つき) bpy.ops.transform.resize( value=val, orient_type='LOCAL', center_override = zero_p) #data系 from mathutils import Vector obj = bpy.context.object sca = obj.scale bpy.context.object.scale = [sc*va for sc,va in zip(sca,val)] dist = (obj.location - zero_p) obj.location=Vector([di*tr for di,tr in zip(dist,val)]) + zero_p #obj.scaleはmatrix_worldでも取得できる obj.matrix_world.decompose()[2]
移動
import bpy a = 1 b = 2 c = 3 #bpy.ops系 #グローバル bpy.ops.transform.translate(value=(a, b, c)) #ローカル座標の方向に移動かつ移動量はグローバル値 bpy.ops.transform.translate(value=va, orient_type='LOCAL', orient_matrix_type='GLOBAL') #ノーマルの方向に移動 bpy.ops.transform.translate(value=(0, 0, length_adj), orient_type='NORMAL') #data系 #グローバル obj = bpy.context.object obj.location = [obj.location[0] + a , obj.location[1] + b , obj.location[2] + c] #別件でimport mathutils してれば va = mathutils.Vectol(a,b,c) obj.location = obj.location + va #あるいは loc = [a,b,c] obj.location = [x + l for (x, l) in zip(obj.location, loc)] #ローカル座標の方向に移動かつ移動量はグローバル値 msh = obj.data for v in msh.vertices: if v.select: v.co = [v.co.x+a/obj.scale[0], v.co.y+b/obj.scale[1], v.co.z+c/obj.scale[2]] #ローカル座標の方向に移動し移動量はグローバル値、原点も一緒に移動 a = 0 b = 0 c = 1 val = [a,b,c] msh = obj.data va = [d/s for d,s in zip(val,obj.scale)] mat = obj.matrix_world obj.location = mat @ Vector(va) #ノーマルの方向に移動(編集モード・面の場合のみ) #オブジェクトモードで使用 import bpy a = 0 b = 0 c = 5 import mathutils obj = bpy.context.object msh = obj.data va = mathutils.Vector((a,b,c)) mat = obj.matrix_world m = mat.copy() for p in msh.polygons: if p.select: norm = p.normal mx_inv = mat.inverted() mx_norm = mx_inv.transposed().to_3x3() world_norm = mx_norm @ norm world_norm.normalize() m[0][2] = world_norm[0] m[1][2] = world_norm[1] m[2][2] = world_norm[2] gro = m @ va lo = mx_inv @ gro for id in p.vertices: msh.vertices[id].co = msh.vertices[id].co + lo #obj.locationはmatrix_worldでも取得できる obj.matrix_world.decompose()[0]
トランスフォームの適用
そこまでやらんでもいい気がするけど
#bpy.ops系(全適用) import bpy bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) #data系 obj = bpy.context.object mw = obj.matrix_world msh = bpy.data.meshes[obj.data.name] for v in msh.vertices: v.co = mw @ v.co obj.location = [0,0,0] obj.rotation = [0,0,0] obj.scale = [1,1,1] #↓拡大縮小だけ適用する場合 #meshの頂点座標はローカル座標 #もっとシンプルな方法ありそうな気がする for v in msh.vertices: verts=[] for s,vv in zip(obj.scale,v.co): verts.append(s*vv) v.co=verts obj.scale = [1,1,1]
複製
思ったより面倒だった。
2.8系でコレクション機能が追加されたので注意。
import bpy #bpy.ops系 bpy.ops.object.duplicate() #data系 #指定した名前と同じ名前のオブジェクトが既にあると #aaa.001という感じの名前になる obj = bpy.context.object co=bpy.context.collection msh = bpy.data.meshes[obj.data.name].copy() o = bpy.data.objects.new(name='aaa', object_data=msh) co.objects.link(o) #以下のようにするとコピー元のコレクションの外に #コピペされてしまうので要注意 scene = bpy.context.scene scene.collection.objects.link(o)
複製&移動
注意点としてbpy.ops系は選択(アクティブなモノではない)されたものに作用し新しく増やしたものが選択&アクティブになる。
data系は増えたものはアクティブ&選択にならない
ループで増やすときはどのオブジェクトがアクティブなのか気を付けなくてはならない
import bpy a=1 b=2 c=3 bpy.ops.object.duplicate_move(TRANSFORM_OT_translate={"value":(a, b, c)}) #data系 src_obj = bpy.context.object co = bpy.context.collection new_obj = src_obj.copy() new_obj.data = src_obj.data.copy() new_obj.location=[src_obj.location[0] + a , src_obj.location[1] + b , src_obj.location[2] + c] co.objects.link(new_obj) src_obj.select_set(False) new_obj.select_set(True) bpy.context.view_layer.objects.active=new_obj
全選択、全選択解除
複製ほどじゃないが2倍ほどdata系のほうが早い
import bpy #bpy.ops系 bpy.ops.object.select_all(action='SELECT') bpy.ops.object.select_all(action='DESELECT') #data系 for obj in bpy.context.collection.objects: obj.select_set(True) for obj in bpy.context.collection.objects: obj.select_set(False)
ペアレンツ
早い早くない以前に最後に思いついてペアレンツすることが多いので、選択状態にするのが面倒なときに使える
import bpy #bpy.ops系 最後に選択したもの(アクティブなオブジェクト)が親になる bpy.ops.object.parent_set(type='OBJECT') #data系 #o.parent = bpy.context.active_objectだけだと子のオブジェクトが移動しちゃうから selected_objects = bpy.context.selected_objects.copy() for o in selected_objects: if o != bpy.context.active_object: o.parent = bpy.context.active_object o.matrix_parent_inverse = bpy.context.active_object.matrix_world.inverted()
エンプティー生成
そうたくさん画面に増やすものじゃないがopsで1000個だすと12秒、data系だと0.059秒で200倍・・・
倍数マジックかもしないがとにかくopsは遅いんじゃ。
import bpy #bpy.ops系 bpy.ops.object.empty_add(type='PLAIN_AXES', location=(0, 0, 0)) #data系 co=bpy.context.collection mt = bpy.data.objects.new('empty', None) co.objects.link(mt) mt.empty_display_size = 2 #エンプティを矢印に変えたければ以下 mt.empty_display_type = 'ARROWS'
せん断
せん断はそんなに遅くない
bpy.opsで1000回やって0.15秒、dataで0.01秒だった
複製(bpy.ops.object.duplicate)は100回やって0.4秒だったことを考えると複製だけがやけに遅いだけなのかも。
#bpy.ops系 shear_va=0.5 axis='X' bpy.ops.transform.shear(value=shear_va, orient_axis=axis, orient_axis_ortho='Y', orient_type='LOCAL', orient_matrix_type='GLOBAL') #data系 import mathutils obj = bpy.context.object mat_s = mathutils.Matrix.Shear('XY',4,(0,0.5/obj.scale[1])) msh = bpy.context.object.data for v in msh.vertices: if v.select == True: v.co = mat_s@v.co
↓bpy.opsがどのぐらい遅いかはこっち(実際に時間を測ってみた)
katsumi3.hatenablog.com