Blender:コマンド再実行
そういえばBlenderにコマンドの再実行があったなぁとは思っていたけど実行したことがありませんでした。なぜならメニューから呼び出すのが面倒だから。
Softimage 3DからはじまりXSIを経てMayaにも移植されたメニューを中クリックすることでそのメニュー内のコマンドの中で一番最近実行されたものが再実行されるって機能は、大変素敵なのでBlenderにも欲しいと思ったりはしますが、多くの操作をショートカットから行ったり、そもそも全てのコマンドがメニューに出ているわけではないBlenderにはそぐわないでしょうか。
で、コマンドの再実行自体はメニューの Edit→Repeat Last な訳で、一般的なBlenderショートカットでは Shift + R だそうですけど、3DCGソフト全般で一般的なショートカットを選択している人向けには G が割り振れておりました。これは知らなかった。
ということで、今後、ちょっと楽になりそうです。(あと New face from edgesも知らんかった(^^;)
Blender:Shortcut VUr
ほぉ、こんなものがあるんですね。
shortcut_VUr: Blender mouse and keyboard input viewer[GitHub]
ショートカットやマウス操作をBlenderのウインドウ内に表示してくれるアドオンだそうです。ダウンロードしたZipファイルからそのままインストールすると以下のようなパネルが追加されて、
Start Shortcut VUrボタンを押すと以下のように画面に表示してくれる、と。
これを使って颯爽と操作説明とかできるような腕になりたいものっすね。
<追記>Hi-DPIな環境に対応しておらず、表示が小さくなっちゃうのはちょっと残念</追記>
Photoshop の編集→変形の基準点の動作が変わってた
今更な話なのですけど、ググるとCC 2019かららしいのですが…
Command + Tで呼び出される自由変形において、ハンドルを掴んで拡大縮小したり回転させたりする時、Optionを押しながら行うと対象範囲の中心を基準にした変形が行われます。その中心地点を基準点と呼んでいるようですが、以前はその位置をOption + クリックした場所に指定することができました。
2019以降、基準点の表示がデフォルトでオフになっていたそうです。だから任意の位置を中心に変形させるときに難儀していたのですけどね。
オンにするには
ここをチェックするだけでした。以降、オンにした状態でツールが起動します。
しかして、Option + クリックしても基準点位置は移動しません。これはOption + ドラッグで動作しました。ここってポイントをクリックするんじゃなくて、ちょっとだけドラッグしてポイントを指定するイメージっすね。
ということで、今度これが必要な作業が発生しても以前のパフォーマンスに戻れます。
ちなみに変形ツールにおけるOption + クリックはワープツールでの機能強化のショートカットとして動作して、
こっちは知らなかったのでかなり効率あがりそうです。
とりあえず親の座標系にする
こんな感じで三つのオブジェクトObjA, ObjB, ObjCが同じ階層にいます。そいで、これらをParentコマンドでBの子供にCをAの子供にBを設定します。設定した結果が下。
この時注目したいのは位置情報が階層構造を作る前と変化ないこと。だからワールドの座標系で表示されているかと思いきや、一番親のObjAを動かしてもObjB、ObjCの情報に変化は生じません。ここの数値は何もしないと親子関係を作った時のワールド座標って事みたいです。なので(個人的には)これは全く役に立たない情報に思えます。だからObjB、ObjCのRelationsの中のParent TypeをObjectにしてやると親の座標系の数値に変わるのでそう設定したいのですけど、それに変更した瞬間、ObjBとObjCの位置は、親子関係を作った時のワールド座標の数値をローカル座標系の数値として取り扱いやがります。つまり、位置が吹っ飛ぶ。
これを回避するためのスクリプトを考えるわけですが、スケールが絡まなければ大変にシンプルで、子供を選択して以下のスクリプトを実行するだけで良いようです。
import bpy childObj = bpy.context.selected_objects[0] matrixWorld = childObj.matrix_world childObj.parent_type = "OBJECT" childObj.matrix_world = matrixWorld
ワールド座標系内での変換を保持しておいて、Parent TypeをObjectに変更した後にその値に戻してやる、って考え方。関連するオブジェクトのスケールが1ならこれで問題なく動作するようです。こいつを簡単に呼び出せるようにしておけば、当面の用は足りるかね。
ちなみに matrix_world でワールド座標系のマトリクスを扱ってますけど、ローカル座標系のそれは matrix_local でよろしいそうです。
なを、座標変換では変換行列を乗算する事が多いわけですけど、Python 3.5以降ではそれは@演算子なのだそうで、3.7.7が動いているらしいBlender 2.91.0では当然それが使えて、 matrixWorld@matrixWorld.inversed() とかやるとワールドの原点に行ってくれたりします。変換部分を勉強すればこの辺の知識が役に立ってくるはずなのだけど、とりあえず知識がないのものでスケール問題に対処できません。困ったものです。
Blender 2 Viz|artist
FBX出力のための設定メモ。検証環境、Blender 2.91 beta。Viz|artist 3.12。
シーンの制作を始める前に設定しておくべき所。
シーンのフレームレートを59.94fpsに。ユニットスケールを0.01に変更。前者はVizの環境設定で設定されているフレームレートに合わせる。後者は1ユニットをセンチメートルにするための設定。
import bpy scene = bpy.context.scene sceneUnitSettings = scene.unit_settings sceneUnitSettings.system = 'METRIC' sceneUnitSettings.scale_length = 0.01 sceneRenderSettings = scene.render sceneRenderSettings.fps = 60 sceneRenderSettings.fps_base = 1.0010000467300415 print( 'SET Unit_Scale:', round( sceneUnitSettings.scale_length , 3) , \ ' FPS:' , '{:.2f}'.format(sceneRenderSettings.fps / sceneRenderSettings.fps_base) )
スクリプトで設定するとこんな感じらしい。新規シーンを作る時、これを介して作成されるものを作ればいいのかな?
FBXの出力パラメータ
Apply ScalingsはFBX All(FBX Local以外)。Bake AnimationのNLA StripsとAll Actionsのチェックを外す。
以上で、Z-UpとY-Upの違いのために親階層に90度の回転が入ってしまう以外は想定どおりにVizにデータを渡せると思われる。なお、ボーンによるアニメーションの検証はしていない。今後の課題。
Blenderの親子関係時の座標
BlenderのプロパティエディタのTransformに表示される座標。これが、何を指しているのか分からなくなりませんか?
例えばこんな感じにEmptyオブジェクトが配置されています。ちょっと傾いた大きいEmptyオブジェクトと、原点から離れた位置にある小さいEmptyオブジェクトがあります。で、小さい方を大きい方の子供にします。Object→Parent→Object を実行させて親子関係を構築します。
この状態での小さい方のTransformの値は Location ( -5, 0, 0 ) Rotation( 0, 0, 0 ) でして、親の座標系ではなくワールド空間での値が表示されてるなと思うわけです。じゃあ親になってる大きいEmptyの回転値を全部0にしたらどうなるかと言うと、
んーと、Location ( -5, 0, 0 ) Rotation( 0, 0, 0 ) と値変わらず。
ちなみに 3D Cursor を小さいEmpty位置に持っていくと、3D Cursor は Location ( -4.98, 0, 0.4484 ) って表示されるわけですよ。つまり小さいEmptyのワールド空間での位置はそれと同じで、回転にも値が入るはず。また、大きいEmptyは位置、回転、スケールが全部リセット値になってるから、小さいEmptyの親の座標系での値もワールド空間での値と同じになるはずなわけで。なので、Transfromに表示されている数字は何?ってわけです。
んー、ほんとにいったい何なのだろう。
ちなみにこの状態で、RelationsのParent TypeでObjectを選び直すと、小さいEmptyのLocation ( -5, 0, 0 ) の位置は、親の座標系基準になってくれます。Clear Parent Inverseコマンドを実行した状態になる感じかな?
この挙動、どうも過去から変わっていないようなのだけど、Blender使いの人たちは困ってないのだろうか? ていうか、親の座標系の中で作業をしたい場合、どうしているのだろう?
ていうか、選択オブジェクトのワールド座標とローカル座標のそれぞれの数値を見て調整したのいだが、どうすりゃいいんだ?
Operator Search
Blender 2.90.1 でアドオンのお勉強でもしようかなとチュートリアルをやってていきなりつまずいていたのですけどね。
チュートリアルのしょっぱなのスクリプトは、特にメニューに現れないコマンドの作成で、Blenderにそのスクリプトファイルをアドオンとしてインストールしたら、Operator Searchで検索するとそのコマンドが現れて実行できる、って物でした。
一般的なBlenderだとF3で現れる?Operator SearchはEditメニューの中にあります。しかし2.90ではそれがMenu Searchになっていた関係で、なにせメニューサーチだからメニューにないコマンドはリストアップされてこず、アドオンを登録できたのに何でコマンドが検索されないんだと悩み続けたのでした。
で、解決法。PreferencesのInterface→Display の中にある Developer Extras をオンにする。するとOperator Searchを使えるようになり、チュートリアルを先に進めるようになった模様。
チュートリアルで実行されるBlenderとバージョン違いでの学習で早速トラップにかかった、そんなお話でございます。
地球的球
とりいそぎ、球の一部を緯度・経度の範囲で切り取った形状が欲しかったのでスクリプトを書いてみた。SoftimageのSphereは機能としてこれを動的に作るのがついてるのになぁ…
import bpy import math #参考 https://qiita.com/kenyoshi17/items/b93bbba6451e3c6017e5 #緯度 latitude lat_0 = -45.0 lat_1 = 45.0 #経度 longitude lon_0 = 0.0 lon_1 = 360.0 #分割数(緯度方向、経度方向) latSplitCnt = 20 lonSplitCnt = 40 #級の半径 r = 10.0 verts = [] faces = [] uvs = [] latBaseAngle = ( lat_1 - lat_0 )/latSplitCnt lonBaseAngle = ( lon_1 - lon_0 )/lonSplitCnt #頂点のリスト for i in range( latSplitCnt + 1 ) : yAngle = lat_0 + latBaseAngle * i print( i, ":" , yAngle ) z = r * math.sin( math.radians( yAngle ) ) ri = r * ( math.cos( math.radians( yAngle ) ) ) for j in range( lonSplitCnt + 1 ): x = ri * math.cos( math.radians( lonBaseAngle ) * j ) y = ri * math.sin( math.radians( lonBaseAngle ) * j ) verts.append( ( x, y, z ) ) uvx = j * 1.0/lonSplitCnt uvy = i * 1.0/latSplitCnt uvs.append( ( uvx, uvy ) ) #ポリゴンの並びのリスト for i in range( latSplitCnt ) : for j in range( lonSplitCnt ): v0 = i * ( lonSplitCnt + 1 ) + j v1 = v0 + 1 v2 = ( i + 1 ) * ( lonSplitCnt + 1 ) + j + 1 v3 = v2 - 1 face = (v0, v1, v2, v3) faces.append( face ) msh = bpy.data.meshes.new( name='GlobeSphere' ) msh.from_pydata( verts, [], faces ) msh.update() #UVの設定 #参考 https://blender.stackexchange.com/questions/160157/how-to-set-uv-coordinates-for-a-mesh-in-blender-2-8x-with-script uvlayer = msh.uv_layers.new() msh.uv_layers.active = uvlayer for face in msh.polygons: for vert_idx, loop_idx in zip( face.vertices, face.loop_indices ): uvlayer.data[ loop_idx ].uv = ( uvs[ vert_idx ] ) obj = bpy.data.objects.new( name='GlobeSphere', object_data=msh ) scene = bpy.context.scene scene.collection.objects.link( obj ) bpy.context.view_layer.objects.active = obj obj.select = True if (lon_0 == 0.0 and lon_1 == 360.0) or lat_0 == -90.0 or lat_1 == 90.0: bpy.ops.object.editmode_toggle() bpy.ops.mesh.remove_doubles(threshold=1e-05) bpy.ops.object.editmode_toggle()
こんな具合↓で出来るのだけど、これをAdd→Meshから呼び出して云々するにはお勉強が必要す。
Blenderでさ、名前検索して検索結果をまとめて選択するにはどうしたらいいの?
タイトルの通りなのですけど、
こんな感じでEmptyの子供にオブジェクトが配置されているシーンで、そのオブジェクトが一定のルールで名前がつけられているとします。
つまりはこんな具合に、Emptyの子供のオブジェクトは、例えば Cube〜って名前になっているとして、こいつらをまとめて選択したいなって思った時、どうしたらいいんでしょうか?
って考えた時、Outlinerの検索フィールドに文字列入れたらいけそうだと思うじゃないですか。でも確かにフィルタリングされるけど、それらを選択することがどうも出来ないようなんですよね。
正解かはわかりませんけど、名前検索して選択したければ、Selectメニューにそれっぽいコマンドがあります。
ってことで、これでとりあえず選択は出来ましたが、Outlinerがどうも直感的じゃないですねぇって思いました。
ちなみにこの例の場合、選択したい連中はソースとなるオブジェクトとObject Dataを共有するので Select → Select Linked → Object Data でも選択できます。
余談ながら、同じ構造の複数オブジェクトを選択して、例えば複数のカーブを選択して、Object DataのGeometryからBevelのDepthの値を一括変更したい時、Optionキーを押しながらフィールドをアクティブにすると、選択されたオブジェクト全部に値が適用されるんすね。おじちゃんスクリプト 書いてたよ…
以上、今日のメモでした。
オブジェクトを複製してある名前を持ったオブジェクトの子供にする
プロットされたEmptyの位置にあるオブジェクトを配置したいと、そう思うわけです。
下図のCubeのインスタンスをEmptyの子供にしたい、と。
import bpy srcObj = bpy.data.objects['Cube'] parentObjs = [] for scnObj in bpy.data.objects: if 'Empty' in scnObj.name: parentObjs.append(scnObj) parentsNo = len( parentObjs ) for i in range(parentsNo): newObj = bpy.data.objects.new('Cube' + str(i) , srcObj.data) bpy.data.collections['Collection'].objects.link( newObj ) newObj.parent = parentObjs[ i ]
この結果、
という感じで出来たような気がする。というメモ。