アストラルプリズム

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

Blender python 平面に垂直かつ外向きにオブジェクトを置く

毎度毎度座標系のスクリプトを作ろうとして、ド忘れしてひどい目に合うので、分からないなりに考え方と手順を書く。

 

それぞれの辺の中心に面に垂直かつ面の外側に向かってモンキーを置きたい

図にするとこんな感じ。

f:id:katsumi3:20191229234156p:plain

 

手順

1、面の垂直方向を調べる

2、辺の外側の向きを調べる

3、辺の中心座標を調べる

4、モンキーの向いている方向を調べる

5、モンキーの置く場所と向きを決める

6、モンキーの位置の微調整

 

めんどくさいところ

・ローカル座標とグローバル座標の違い(トランスフォーム)で値がおかしくなる。

マトリックスワールド等計算が面倒な場合は必要な数値をとる為だけに平面をコピペしてトランスフォームを適用し使用後は削除でもいいと思う(だが誤差が一番少ない方法が分からない)。

1、面の垂直方向を調べる

ノーマルの方向でわかる。

 

plane = bpy.context.object

p_mat = plane.matrix_world

msh = bpy.data.meshes[plane.data.name]

norm = msh.polygons[0].normal

#normalをグローバル座標にする

inv = p_mat.invert()

mat_norm = inv.transposed().to_3x3()

norm = mat_norm @ norm

norm.normalized()

 

 

トランスフォームを適用せずマトリックスワールドをかけた場合と適用しただけの場合誤差が少ないのは?

xを60度傾けた面で確認してみたがトランスフォームを適用せずのほうが誤差が少ない様だ。

f:id:katsumi3:20191230215526p:plain


f:id:katsumi3:20191230001958p:plain

 

2、辺の外側の向きを調べる

よくわからんけど辺の向きとノーマルにcrossってのを使うと辺の外側の向きが分かるらしい

辺の向き

・ローカル座標

muki = msh.vertices[1].co- msh.vertices[0].co

・グローバル座標

muki = mw @ msh.vertices[1].co - mw @ msh.vertices[0].co

xaxis = muki.normalized()

辺の外側の向き

cro = muki.cross(norm).normalized()

ノーマライズしないとモンキーに縮小拡大がついてしまう。

muki = muki.normalized()

辺の向きも同様にノーマライズ

正直理屈が良く分からないorz

計算した値をまとめてマトリックスワールドと掛けた方が誤差が少ない気がするんだけど、まとめてやる方法がよくわからない。

 外側の向きはこの図ではマイナスX方向になる

f:id:katsumi3:20191230003730p:plain

 

3、辺の中心座標を調べる

単純に数学的に調べるだけ

ローカル座標

loc = (msh.vertices[1].co + msh.vertices[0].co) / 2

グローバル座標

loc = (mw @ msh.vertices[1].co + mw @ msh.vertices[0].co) / 2

これはmwを

loc = mw @ ((msh.vertices[1].co + mw @ msh.vertices[0].co) / 2)

とまとめれるが他のはまとめると値が変わったりするので不用意にまとめると酷い目に会う。

 

4、モンキーの向きを確認する

モンキーを呼び出したときの向きを目視で確認しておく。

bpy.ops.mesh.primitive_monkey_add(enter_editmode=False, location=(0, 0, 0))

obj = bpy.context.object

モンキーはマイナスY方向が正面になっている。

ついでにモンキーの顔、標準で2mらしい。でかいな。

f:id:katsumi3:20191230004959p:plain


5、モンキーの置く場所と向きを決める

上の図から

・モンキーの横方向(X)は辺の向きに合わせたい

・モンキーの正面の方向(-Y)は辺のクロスの向きに合わせたい

・モンキーの頭の方向(Z)は平面のノーマルの方向に合わせたい

・辺の中心+モンキーのzの大きさ/2にモンキーを置きたい

 

以上をまとめてモンキー用のマトリックスワールドを作って入れておく

import mathutils

monkey_mw = mathutils.Matrix([
[muki.x, -cro.x, norm.x, loc.x],
[muki.y, -cro.y, norm.y, loc.y],
[muki.z, -cro.z, norm.z, loc.z],
[0,0,0,1]
])

モンキーの現在の位置のマトリックスワールドと移動後のマトリックスワールドを掛ける。

obj.matrix_world = bpy.context.selected_objects[0].matrix_world @ monkey_mw

 

6、モンキーの位置の微調整

f:id:katsumi3:20191230210637p:plain

ここまで出来た。あとちょっと。

モンキーをモンキーの頭の方向に1mほど持ち上げたい。

面の法線方向がZで辺の中心方向がXとして移動したい方向のベクトルを作る
vec = mathutils.Vector((0,0,1))
inv = obj.matrix_world.copy()
inv.invert()
vec_rot = vec @ inv
obj.location = obj.location + vec_rot

 

ローカル座標=反転させたマトリックスワールド@グローバル座標

なので元の位置(obj.location)にローカル座標にした移動量(vec_rot)を足している

 

完成

f:id:katsumi3:20191230213533p:plain

モンキーを立体にして平面の内側におきたい時は

vec = mathutils.Vector((0,1,1))とするといい。

f:id:katsumi3:20191230213801p:plain

 

参考 ローカル軸上でオブジェクトを移動する

blender.stackovernet.com