アストラルプリズム

PC、スマホ、ゲームなどの備忘録と日記

Blender python bmeshのデータ更新のタイミング

bmeshのデータ更新について調べてみた。
オブジェクトモードと編集モードでは更新タイミングが違うので要注意。

長くなるので結論だけ先に書く
##################################
オブジェクトモードで使う場合
命令文:

bm = bmesh.new()
bm.from_mesh(plane.data)

3dビューから手動及びbpy.opy系を使って頂点を変更したときは以下が必要
編集モードやオブジェクトモードに行っても更新されないでデータも壊れないし何も起きない

plane.data.update()
bm = bmesh.new()

pythonコンソールから頂点データを編集した場合は
bm.to_mesh(plane.data)
を使わないと実際のオブジェクトには反映されない
(データを色々いじった後に最後に実行するといい)

################################

#####################
編集モードの場合
命令文:
bm = bmesh.from_edit_mesh(plane.data)
3Dビューから手動で変更後は即データが更新される
bpy.ops系使っても即時更新される
pythonコンソールからの変更も即時更新
(画面表示はオブジェクトモードになってから更新される)
loopsも読み取り専用とあるが
bm.faces[0].loops[8].vert.co.x=5
のように即時更新
#######################


以下実験結果

オブジェクトモードで使った場合

plane = bpy.context.object
#bmshをオブジェクトモードのまま使う
bm = bmesh.new()
bm.from_mesh(plane.data)
bm.verts.ensure_lookup_table()
ff=bm.copy()
ff.verts.ensure_lookup_table()
#頂点の座標を表示
print("ff : ", ff.verts[0].co)
print("bm : ", bm.verts[0].co)

結果
ff :
bm :

copy()したffもensure_lookup_table()が必要。面倒。

その後手動で頂点を動かす
(infoにはbpy.ops.bpy.ops.transform.translateが表示されてるのでbpy.opsを使ったのと同等?)

f:id:katsumi3:20200202122059p:plain

移動した頂点位置は(0,0,0)になってるはずだけど取れた値は最初と変わらない。
ff :
bm :

bmeshで頂点を変更した際に使うbm.to_mesh(plane.data)を使ってみても変化なし
ffもbmも頂点データをコピーしてるだけで内容が更新されないのかもしれない。

bm.from_mesh(plane.data)
bm.verts.ensure_lookup_table()
#頂点の座標を表示
print("ff : ", ff.verts[0].co)
print("bm : ", bm.verts[0].co)

更新されてない
ff :
bm :

bm.free()
print("ff : ", ff.verts[0].co)
print("bm : ", bm.verts[0].co)

結果
ff :
bmの方は当然bmeshがないよというエラーになる
Traceback (most recent call last):
File "", line 1, in
ReferenceError: BMesh data of type BMesh has been removed

bm.from_mesh(plane.data)
bm.verts.ensure_lookup_table()
print("ff : ", ff.verts[0].co)
print("bm : ", bm.verts[0].co)

結果
ff :
ffのprint以外はすべてエラー

plane = bpy.context.object
#bmshをオブジェクトモードのまま使う
bm = bmesh.new()
bm.from_mesh(plane.data)
bm.verts.ensure_lookup_table()
#頂点の座標を表示
print("ff : ", ff.verts[0].co)
print("bm : ", bm.verts[0].co)

結果
ff :
bm :
どういう事だってばよ。全く更新されてない

てかここでオブジェクトをオブジェクトモード→エディットモード→オブジェクトモードに戻すと元の形に戻った。
f:id:katsumi3:20200202130302p:plain
どういう事だってばよ...

plane.data.update()
bm.from_mesh(plane.data)
bm.verts.ensure_lookup_table()
#頂点の座標を表示
print("ff : ", ff.verts[0].co)
print("bm : ", bm.verts[0].co)

結果
ff :
bm :
変わらない

plane.data.update()
bm = bmesh.new()
bm.from_mesh(plane.data)
bm.verts.ensure_lookup_table()
#頂点の座標を表示
print("ff : ", ff.verts[0].co)
print("bm : ", bm.verts[0].co)

結果
ff :
bm :
更新された

結論
オブジェクトモードでbmesh使ってる時に3dビューで手動(bps.ops系も?)使ったら

plane.data.update()
bm = bmesh.new()

が必要・・・
疲れた...bm.from_mesh(plane.data)にある通りplane.dataをplane.data.update()で更新してあげないとダメでかつbmもbm = bmesh.new()でおニューにしてあげないといけないのね、わかった。

pythonコンソールから変更した場合

print("saisyo",bm.verts[0].co.y)

bm.verts[0].co.y=0
print("mae : ",bm.verts[0].co.y)

bm.to_mesh(plane.data)
print("ato : ",bm.verts[0].co.y)

結果
saisyo 5.0
mae : 0.0
ato : 0.0

データーが変わってるのはmaeだけど実際オブジェクトに反映されてるのは
bm.to_mesh(plane.data)
した後なので要注意



編集モードの場合

こっちはたぶん超楽だと思われ。

bpy.ops.object.mode_set(mode='EDIT')
plane=bpy.context.object
bm = bmesh.from_edit_mesh(plane.data)
bm.verts.ensure_lookup_table()
ff=bm.copy()
ff.verts.ensure_lookup_table()
#頂点の座標を表示
print("ff : ", ff.verts[0].co)
print("bm : ", bm.verts[0].co)

結果
ff :
bm :

その後手動で頂点を移動

print("ff : ", ff.verts[0].co)
print("bm : ", bm.verts[0].co)

結果
ff :
bm :

即変更が反映されている

pythonコンソール側での更新

bm.verts[0].co.y=0
print(bm.verts[0].co.y)

結果
0.0
画面には反映されないがオブジェクトには反映されている様子。
(カーソルを持って行ったりすると画面が更新される)

bpy.ops.transform.translate(value=(0, 5, 0), ....を使っても
print(bm.verts[0].co.y)
結果
5
即時更新される