Advertise with Googlier.com Taguma CG Lab https://cg-lab.mirage-factory.net CG tech. blog Mon, 22 May 2023 03:12:22 +0000 ja hourly 1 https://wordpress.org/?v=7.1.3 https://cg-lab.mirage-factory.net/wp-content/uploads/sites/7/2019/09/cropped-site_icon-32x32.png Taguma CG Lab https://cg-lab.mirage-factory.net 32 32 Taguma CG Lab false podcast Taguma CG Lab https://cg-lab.mirage-factory.net/wp-content/plugins/powerpress/rss_default.jpg https://cg-lab.mirage-factory.net 動作を実装する(2) | 3dsmax Python入門 #03 https://cg-lab.mirage-factory.net/2023/05/892/?utm_source=rss&utm_medium=rss&utm_campaign=post-892 https://cg-lab.mirage-factory.net/2023/05/892/#respond Mon, 22 May 2023 03:12:21 +0000 https://cg-lab.mirage-factory.net/?p=892  前回、作ろうとしているツールの動作を考え、それを分解してシンプルな操作を行う関数に分ける、という考え方を解説しました。  前回は関数を実装するところまでしか説明していなかったのですが、シンプルな機能の関数を実装したら、 […]

The post 動作を実装する(2) | 3dsmax Python入門 #03 first appeared on Taguma CG Lab.]]>
 前回、作ろうとしているツールの動作を考え、それを分解してシンプルな操作を行う関数に分ける、という考え方を解説しました。

 前回は関数を実装するところまでしか説明していなかったのですが、シンプルな機能の関数を実装したら、その都度、その動作を確認しておきましょう。今回はその確認の作業から始めたいと思います。

関数の動作を確認する

 改めて、重複になりますが前回実装したlineCopy という関数を確認します。

def lineCopy(src_nodes, count, offset, copy_type):
    '''
        元オブジェクトを指定した個数、間隔で線形にコピーして並べる
        args:
            list: 元オブジェクトのリスト
            int: コピー個数
            Point3: コピーの移動距離(ベクトル)
            name: コピーの種類(コピー、参照、インスタンス)
        return:
            list: 元オブジェクトを含む、生成されたもののリスト
    '''
    return_nodes = copy(src_nodes)
    for n in src_nodes:
        cur_offset = offset
        for i in range(count - 1):
            _, new_nodes = runtime.maxOps.CloneNodes(n, offset=cur_offset, cloneType=copy_type, newNodes=byref(None))
            return_nodes.extend(new_nodes)
            cur_offset += offset
    return return_nodes

 この関数をGridCopy.py の中に書き、3dsmax でテストしてみます。

定義した関数を評価して使える状態にする

 3dsmax 2023を起動し、スクリプトエディタを開いてGridCopy.py を開いた状態にします。開いた状態で3dsmax を終了すれば、次回起動時にまた開いた状態で起動してくれます。開いたら、GridCopy.py を開いたスクリプトエディタの窓の中をマウスでクリックしてアクティブにし、そこでCtrl+E を実行します。スクリプトエディタ上でのCtrl+E はEvaluate、つまりスクリプト内容の「評価」という動作になります。MAXScript で書いたものを評価するとスクリプトリスナーに何らかの出力があるのですが、Python のものだとコード内に出力が明示されていない限り何も表示されません。表示は何も出ませんが、Ctrl+E をした後、リスナーのPython モードで関数名を入力してEnter すると、画像のように関数が定義されていることを確認できます。なお、コード内で定義されていない名前を入力した場合は図のようにエラーになります。

評価したスクリプト内で定義したものはリスナーから使えるようになる

 このように、GridCopy.py 内で定義したgridCopy とlineCopy はリスナーから使うことができます。試しに定義されていない「abcde」という文字列を実行してみると、「そんな名前は定義されていない」というエラーが返ってきます。

関数をテストする

 この関数は以下の4つの引数を要します。

  • コピー元オブジェクトのリスト
  • コピーする個数
  • コピーを配置する間隔
  • コピーの種類

 テストを行うためにはこれらの引数が必要になるので、これを事前に用意します。まずはコピー元オブジェクトのリストですが、これはオブジェクトとしてシーン内に存在する必要があるので、まずシーン内に適当にオブジェクトを作りましょう。

 引数で求められるのはオブジェクトの「リスト」ですが、ひとまずボックスを1つだけ作りました。このボックスを引数に渡すため、リストに入れておく必要があります。テスト用として、ここでは「選択されているオブジェクトをPythonのリストにして取得する」という方法をやってみましょう。

 テストのポイントとして、先ほどGridCopy.py を評価したので、ファイル内でインポートしておいたものはリスナーからすでに使える状態になっている、ということを覚えておきましょう。

 シーン内で、先ほど作ったボックスを選択した状態にします。その状態でPythonモードのリスナーに以下を入力して実行します。リスナーのPython モードでは、普通にエンターを押すと「改行」という扱いになるので、実行するときはShift+Enter を押します。これは複数行にわたるコードをリスナーで実行するための配慮なので、最初煩わしく感じるかもしれませんがぜひ慣れておきましょう。

sel = list(runtime.selection)

 このコードはMAXScript のグローバル変数selection を引数としてPython のlist 関数を実行する、という意味です。MAXSript のグローバル名前空間はpymxs.runtime に定義されているので、MAXScript におけるselection は、Pythonからだとpymxs.runtime.selection でアクセスすることができます。

 selection はMAXScript のコレクションオブジェクトというものなので、これをPython のリストに変換するため、上記のようにlist 関数を使用しています。実行したらsel の中身を出力してみましょう。

sel に「選択されているオブジェクトのリスト」が取得できた

 次に、コピーする個数を用意します。個数は何個でも良いですが、最初は2 が良いでしょう。個数分ループするというコードの性質上、最低でもループが2回回るようにしたいところです。これも一応変数に格納しておきます。

count = 2

 テストするだけなら別に変数に入れておかなくても良いのですが、テストコードをコピペして何回も実行するために変数に入れておくと便利です。これは後程どういう意味か実際にやってみます。

 三つ目はオフセット、コピーしたものを配置する場所を指定するベクトル情報です。これはMAXScript のPoint3 というデータで渡しますが、Point3データはX、Y、Z それぞれの移動距離を格納したリストのようなデータです。以下のようにして生成することができます。

offset = runtime.point3(30, 0, 0)

 前述のようにPoint3 はruntime 内で定義されているので、runtime.point3 として呼び出します。引数にfloat(浮動小数点数)を3つ渡すことでベクトルを生成することができます。ここでは結果がわかりやすいように、X軸方向にのみ移動するようなベクトルにしておきましょう、

 最後に、コピー種別を指定します。コピー種別はコピー、参照、インスタンスの三種で、それぞれMAXScript の名前オブジェクトで指定します。名前オブジェクトはnameという関数にPython の文字列(str)を渡すことで生成できます。

copy_type = runtime.name("copy")

 これでlineCopy に必要な引数がすべて、リスナーの名前空間内に用意できました。さっそくlineCopy を実行して、シーンがどのようになるか観察してみましょう。

lineCopy(sel, count, offset, copy_type)
テストの実行結果

 シーン内に実行した結果が反映され、さらに、リスナーには戻り値が出力されています。戻り値には元のオブジェクトも含まれていることに注目してください。また、lineCopy 関数はコピーを実行した結果いくつになるのか、という値を「個数」として扱っているため、2を渡した結果としてオブジェクトが2個になった、ということです。

 では次に、改めて今シーンにある2つのボックスを元に、今度はY軸方向に3個コピーしてみましょう。先ほどの実行結果を変数で受け取っていなかったので、改めてシーンのボックス2つを選択し、選択をリストで取得します。

sel = list(runtime.selection)

 これを実行すると、先ほど定義したsel という変数の中身が上書きされ、現在の選択状態がsel に格納されます。さらに、count、offset の値も変更しましょう。

count = 3
offset = runtime.point3(0, 40, 0)

 ここまで実行したうえで、先ほどlineCopy のテストをしたときのコマンドをコピぺしましょう。

lineCopy(sel, count, offset, copy_type)

 このように、テストに使用する引数を簡単なものでも変数に入れるようにしておくと、同じコードを引数を変えてテストするときにとても便利です。

 続いて、今度はここまでの操作でできた6個のボックスを元に、Z軸方向にも実行してみます。今回はcopy_type も変更してみましょう。まず、シーン内でここまでに作った6個のボックスを選択しておき、以下を実行します。

sel = list(runtime.selection)
count = 4
offset = runtime.point3(0, 0, 50)
copy_type = runtime.name("reference")

 このように必要な変数を用意したうえで、さっきと同じlineCopy の呼び出しを実行します。

lineCopy(sel, count, offset, copy_type)
参照コピーしたので、元のものを変更するとコピー先にも反映される

 このように、今回はコピー種別を「参照」にしたので、一番下のオブジェクト(元にしたもの)を編集するとそのコピーたちにも同じ編集結果が反映されているのがわかります。さらに、「コピー」でコピーした部分は変化していないことにも注目してください。

 これで、lineCopy 関数が意図通りに実装されていることが確認できました。さらに、今テストしたXYZそれぞれの軸方向への実行をまとめて行うようにgridCopy関数を実装すればよさそうだ、ということも確認できたと思います。

まとめ

  • シンプルな関数を実装し、それを個別にテストしておく
  • テストに必要な引数を変数に入れておいてテストすると良い
  • 引数をいろいろ変化させて正しく動くか確認する

The post 動作を実装する(2) | 3dsmax Python入門 #03 first appeared on Taguma CG Lab.]]>
https://cg-lab.mirage-factory.net/2023/05/892/feed/ 0
動作を実装する(1) | 3dsmax python入門 #02 https://cg-lab.mirage-factory.net/2023/05/856/?utm_source=rss&utm_medium=rss&utm_campaign=post-856 https://cg-lab.mirage-factory.net/2023/05/856/#respond Fri, 19 May 2023 09:44:59 +0000 https://cg-lab.mirage-factory.net/?p=856  早速ツールを作ってみようと思うわけですが、まず最初にすべきことは、「どんな機能を持ったツールにするか」という部分です。このチュートリアルでは、2020版のチュートリアルで作ったのと同じ機能をもつツールを作りましょう。同 […]

The post 動作を実装する(1) | 3dsmax python入門 #02 first appeared on Taguma CG Lab.]]>
 早速ツールを作ってみようと思うわけですが、まず最初にすべきことは、「どんな機能を持ったツールにするか」という部分です。このチュートリアルでは、2020版のチュートリアルで作ったのと同じ機能をもつツールを作りましょう。同じ機能を実装していきますが、全体をPythonで作り、MaxPlus を使わずにpymxs を使用することになるため、コードは全く違うものになります。

仕様の検討

必要な動作

 この部分は以前の記事と重複しますが、次のような動作をするツールを考えます。

 このように、あるオブジェクトを、X軸、Y軸、Z軸方向にそれぞれ指定した個数、間隔でコピーするものを考えます。以前のチュートリアルでは単純にコピーしただけでしたが、今回のツールではコピー、インスタンス、参照を選べるようにしてみましょう。

入力の検討

 まず、ユーザに入力させる情報をどうするか考えます。必要な情報をリストアップしてみます。

  • X方向のコピー数とコピー間隔
  • Y方向のコピー数とコピー間隔
  • Z方向のコピー数とコピー間隔  シーン内のオブジェクトを選択し、6つの情報を入力して実行したら結果が得られる、というのが良さそうです。
  • コピー、インスタンス、参照の選択

 これらの情報をユーザから受け取り、実行すると結果が得られる、という動作が想定されます。情報を受け取る部分はUI(ユーザインターフェース)が受け持ちますが、一旦UIは後回しにし、まずは機能の部分だけで実装を進めていきましょう。あとでUIから操作できるようにする必要があるので、それを見越した設計をしていきます。

設計

クラスか関数群(モジュール)か

 動作として想定されるのは、必要な情報を入力した上で実行ボタンを押すと結果が得られる、というものです。大掛かりなツールであれば実行部分もクラスとして設計したほうが良いのですが、今回は実行する内容が非常にシンプルなので、関数群(モジュール)として実装していこうと思います。

 今回はクラスの詳しい説明は省略しますが、クラスというのは簡単に言うと「何らかのデータとそのデータを操作する関数をセットにしたもの」なので、クラスにするか関数群にするかは、その関数によって変更されるデータを保持しておく必要があるかどうか、というあたりで判断すると良いと思います。今回のツールはシーンの状態を操作するだけで、結果の状態を保持しておく必要はないのでクラスにする必要はないという判断です。(チュートリアルの後半で説明しますが、機能とUIを合体させるところで最終的にはクラス化します)

フォルダ構造

 それほど大規模なツールを作るわけではないので複雑なフォルダ構造は必要ありませんが、このツールに関連するファイルだけは一つにまとまっていたほうが開発しやすいので、ツール名のフォルダを作ってその中にファイルを置くことにしましょう。サブフォルダが必要になったら適宜追加するとして、まずはツール名のフォルダを作ります。ここではツールの動作から「GridCopyTool」という名前にし、その中にGridCopy.py というファイルを置くことにします。

 Pythonがインストールされていると、拡張子.py はPython のアイコンになります。このGridCopy.py に関数を書いていきましょう。

関数設計のポイント

 ここから実際の関数を考えますが、まず最初の方針として、必要なデータはすべて引数として受け取るようにしましょう。クラスのメンバ関数の場合は、メンバ変数をメンバ関数内で使用するといった実装も行いますが、単体の関数として設計するのであれば必要なものはすべて引数として受け取り、関数の外にある変数などを使わないような設計にしておくのが良いです。そうすることでこの関数の再利用性が高まり、コードの可読性(読みやすさ)も高まります。

関数を定義する

 ではさっそく、今回の実行の本体になる関数を実装していきましょう。まずは関数の枠を書きます。その前に、ファイルの先頭にライブラリのインポートを書きましょう。必要なものが出てきたら都度ファイルの先頭に書き足していきます。ここではまず、3dsmax のツールを作るときに欠かせないものを書いておきましょう。

from pymxs import runtime

 実は最近の3dsmax では起動時にすでにpymxs はインポートされた状態になっているのですが、コードとしては書いておかないとコード単体で読むときにわからなくなるので、明示的にインポートしておきます。pymxs全体をインポートしても良いのですが、必要なものを限定して書いたほうが何をインポートしているのか意識するようになるので、個別にインポートする習慣をつけておくのが良いと思います。

関数の枠を書く

 まず関数が受け取るべき引数を考えます。必要なのは

  • 元のオブジェクト
  • X軸方向のコピー個数と間隔
  • Y軸方向のコピー個数と間隔
  • Z軸方向のコピー個数と間隔
  • コピー種別(コピー、インスタンス、参照のいずれか)

 このような感じです。けっこうたくさんの引数が必要ですね。全部で8個になります。この関数は結果を返すのではなく、シーン自体を操作するものなので、一旦戻り値はなしで書いていきます。(成否を返す関数にすることもできますが、それは追々考えることにして、一旦は戻り値なしで書いていきましょう)

 ポイントとして、関数を考えるときはまず受け取る引数と関数名を考え、なにも中身のない関数として枠だけ作る、というのがお勧めです。

def gridCopy(src_node, x_count, x_interval, y_count, y_interval, z_count, z_interval, copy_type):
    pass

 関数を作るときは、まずこのような枠だけ作り、何を渡して何が返るのかを考えると良いです。

複雑な動作をシンプルな動作に分解する

 このgridCopy という関数は枠を見るとわかるように、かなり多くの引数を受け取ってけっこう複雑な処理をすることになります。ちょっと複雑なので、ここで最初にやった手動の操作を振り返ってみましょう。重複になりますがもう一度さっきのgif を見てみます。

 この動作を言葉で説明してみましょう。

  1. コピー元のオブジェクトを選択する
  2. 選択したオブジェクトをy軸方向に複数コピー
  3. コピー後の一列分をx軸方向に複数コピー
  4. xy平面に並んだ一面分をZ軸方向に複数コピー

 という動作です。最初に選択されたものをコピーし、コピーされた結果のものを選択してまたコピー、という動作をx、y、zそれぞれの軸に対して行っています。このgifでは最初にy軸方向へコピーしていますが、x軸方向を先に行っても同じなので、ツールではx→y→zの順に行いましょう。「元になるものを等間隔に複数コピーする」という動作が共通なので、ある軸方向にこれを行う動作をサブ関数として実装します。

 このような複雑な一連の操作を行いたい場合、その操作を解きほぐして、シンプルな動作の組み合わせにできないかを考えましょう。そのうえで、一つ一つは単機能になる関数を作り、それを組み合わせて複雑な処理を実現するという発想で考えるのが良いでしょう。

 ここでは、「元のオブジェクト」「コピーする個数」「コピーする間隔」の三つの情報を受け取って1方向にコピーするlineCopy という関数を考えましょう。これをそれぞれの軸方向に3回実行することでgridCopy を実現します。lineCopy も枠だけ書いてみましょう。

def lineCopy(src_nodes, count, offset, copy_type):
    pass

 枠はこんな感じです。元オブジェクトのリスト、コピー個数、コピー間隔、コピー種別の4つを受け取ってコピー操作を実行します。元オブジェクトをリストにするのは、gridCopy における2回目以降の処理に対応するためです。つまり1回目は元オブジェクト1つだけが入ったリストを受け取る、という想定です。

 これだとコピーする軸方向が指定できないような気がしますが、offset(移動距離)をPoint3というベクトルのデータで受け取ることでコピー方向を指定する想定です。どの引数がどんな型(タイプ)なのかわかるように、コメントに書いておきましょう。

def lineCopy(sec_nodes, count, offset, copy_type):
    '''
        元オブジェクトを指定した個数、間隔で線形にコピーして並べる
        args:
            list: 元オブジェクトのリスト
            int: コピー個数
            Point3: コピーの移動距離(ベクトル)
            name: コピーの種類(コピー、参照、インスタンス)
    '''
    pass

※ 最近のPython は引数や戻り値の型を指定して書く方法もあるのですが、そこを説明すると少し難しくなるので、このチュートリアルでは旧来のPythonの書き方で、型は自動識別を使用するように書いていきます。

※ ここで書いたPoint3 やname というのはPythonに用意されている型ではなく、pymxs のruntime モジュール内で定義されている型です。そのため、pymxs.runtime がインポートされている状態でないと使えません。

 ここで、このlineCopy という関数は最終的にはgridCopy という関数の中で呼ばれる、ということを想定しておきます。使われ方として、まず元のオブジェクトを選択し、x軸方向にlineCopy を行います。次に、x軸方向にコピーされたものたちを元オブジェクトとしてy軸方向にlineCopy を行います。つまり、lineCopy した結果できたもののリストが次のlineCopy の引数になるということなので、コピーしてできたオブジェクトのリストをlineCopy 関数から返す必要があるわけです。

 では関数を書いていきましょう。

def lineCopy(src_nodes, count, offset, copy_type):
    # 戻り値として返すための配列を用意し、元オブジェクトとして渡されてきたものをコピーしておく
    return_nodes = copy(src_nodes)

 まず、戻り値を返すためにreturn_nodes というリストを用意します。これは次のlineCopy の元オブジェクトになるリストなので、今回元オブジェクトとして渡されてきたものも含まれている必要があります。

 ここで、copy という関数を使っていますが、この関数はリストオブジェクトのコピーを生成して返すという関数です。Pythonでは、通常の代入でlist を扱うとコピーはされず、同じものを参照するような動作になります。このため、物理的に別物として扱いたい場合は「同じ要素を持つリストをもう一個生成する」という動作を行う必要があります。copy 関数はそれを行うためのもので、copy というモジュールに入っています。そのためインポートセクションに以下を追加してください。

from copy import copy

 次に、渡された元オブジェクトのリストに含まれているものを、指定された移動間隔で配置しながらコピーする処理を書きます。

# 元オブジェクトリストに含まれているオブジェクトそれぞれに対して実行
for n in src_nodes:
    # 今対象になっているオブジェクトの配置先(移動距離)をcur_offset とする
    cur_offset = offset
  # 指定個数分のコピーを行う
    for i in range(count - 1):
        # オブジェクトをコピーする処理

 複数のオブジェクトを複数個コピーするので、二重のfor ループになっています。もっと効率の良い方法があるだろうという話はありますが、ここではコードのわかりやすさを重視してこのまま二重ループで進めていきます。

 最初なので、少々冗長ではありますが、ここでの「オブジェクトをコピーする処理」の部分をどうやって実装するのか、リファレンスの読み方も含めて説明していきます。

必要な機能を探す方法

 3dsmax 2023 のPython 開発で主流なのはpymxs というモジュールを使う方法です。このモジュールはpython からMaxScript の機能を使用するものなので、MaxScript の知識が必要です。目的の機能を実装するのに調べるべきは、MaxScript のヘルプなのです。

 3dsmax のメインメニューからスクリプト→MAXScript ヘルプ を開きます。最近のヘルプはオンラインヘルプなのでWebブラウザが起動して情報が表示されます。このMAXScript ヘルプを使って目的の機能を探します。

 今回使うのは、maxOps というインタフェースの中にあるcloneNodes という関数です。該当部分のリファレンスは以下のようになっています。

 MaxScript のリファレンスは非常にわかりやすく丁寧な説明がされているので便利です。渡すべき引数とその内容が細かく説明されています。これを見ると、cloneNodes に渡さなければならない引数は最初のnode (元オブジェクト)だけで、それ以外は全部オプション(渡さなかった場合はデフォルト値が使われる)だということがわかります。元オブジェクトだけ渡して実行すると、元のオブジェクトと同じ位置にコピーが生成される、という動作になります。

 基本的にはこうやって調べた関数をpymxs のruntime モジュールから使えば良い、という話なのですが、この関数は参照渡しの引数があるので少々ややこしいです。今回はその説明を詳しくしていきます。

3dsmax のPython でMaxScript の参照渡し引数を要する関数を使う方法

 長い小見出しになってしまいましたが、おそらく最初ここで躓く人が多いのではないかと思うのでちょっと冗長な説明をしてみたいと思います。

 参照渡しというのはMaxScript で”&”をつけて渡す引数のことです。ここでは、cloneNodes 関数によって生成された新しいオブジェクトを受け取るために、キーワード引数のnewNodes に配列を渡す必要があります。はじめに、MaxScript で書くとどうなるのかやってみましょう。

-- MAXScript
src = Box()
new = #()
maxOps.cloneNodes src newNodes: &new

new

 このコードは

  1. Box を生成してsrc という変数に格納
  2. new という空の配列を定義
  3. src をコピーし、新たに生成されたものをnew に格納
  4. new の内容を出力

 という動作をします。実行してみましょう。

 このように、Boxオブジェクトを生成し、それをコピーしたうえで新たに生成されたものを受け取る、という動作ができます。新たに生成されたものは戻り値としてではなく、参照引数で渡した変数の内容が更新されるという形で受け取ったことに注目してください。

 ちなみに、このcloneNodes はさらに移動距離を渡すoffset という引数にも対応しているので、offset と newNodes を渡してこれを実行すれば今回欲しいlineCopy の機能は実現できそうです。

 ここで問題になるのが、参照引数の渡し方です。ちょっと直感的にはわかりにくい書き方になるのでじっくりついてきてください。

pymxs.byref をインポートする

 pymxs モジュールには、MaxScript の参照引数を使うためのbyref という型が用意されています。これをインポートして使うのですが、ちょっととっつきにくい書き方になります。上記のMaxScript をそのままPython で書き直してみましょう。

from pymxs import runtime, byref

src = runtime.box()

# 参照渡しの関数を使用すると、関数の実行結果と、参照引数として受け取る内容のものが返ってきます。
# 戻り値が複数になるため、複数の変数で受け取ります。
# ここれは関数の戻り値をr で受け、newNodes に渡して得られるはずの参照引数の中身をnew で受け取っています。
r, new = runtime.maxOps.cloneNodes(src, newNodes=byref(None))

print(r)
print(new)

 結果はこのようになります。

 byref を使う関数を実行すると、MAXScript で実行したときの戻り値のほか、参照引数として受け取れるはずのものが戻り値として返ってきます。Python での書き方は、受け取る変数を用意しておき、その参照引数を渡す箇所にbyref(None) を渡す、という感じになります。

 これを踏まえてlineCopy のコピー部分を考えましょう

コピー操作の実装

# 元オブジェクトリストに含まれているオブジェクトそれぞれに対して実行
for n in src_nodes:
    # 今対象になっているオブジェクトの配置先(移動距離)をcur_offset とする
    cur_offset = offset
  # 指定個数分のコピーを行う
    for i in range(count - 1):
        # オブジェクトをコピーする処理
        # 受け取るけれど使用する予定のない戻り値は_ で受けるのが慣例
        # コピー種別も渡すようにしておく
        _, new_nodes = runtime.maxOps.CloneNodes(n, offset=cur_offset, cloneType=copy_type, newNodes=byref(None))
     # new_nodes で受け取ったものを最終的に返すreturn_nodes に追加する
        return_nodes.extend(new_nodes)
        # cur_offset に offset 1つ分を足す
        cur_offset += offset

 このような形になります。これを入れてlineCopy を完成させましょう。

def lineCopy(src_nodes, count, offset, copy_type):
    return_nodes = copy(src_nodes)
    for n in src_nodes:
        cur_offset = offset
        for i in range(count - 1):
            _, new_nodes = runtime.maxOps.CloneNodes(n, offset=cur_offset, cloneType=copy_type, newNodes=byref(None))
            return_nodes.extend(new_nodes)
            cur_offset += offset
    return return_nodes

 関数の最後で出来上がったreturn_nodes を返して終わります。

まとめ

 かなり長くなったのでこの辺でいったん中断しましょう。

  • 機能は関数として考える
  • 関数は引数を受け取り、戻り値を返すように設計する
  • 複雑な操作は単純な操作に分解する
  • MaxScript の参照変数をPythonから使うときはpymxs.byref を使う
The post 動作を実装する(1) | 3dsmax python入門 #02 first appeared on Taguma CG Lab.]]>
https://cg-lab.mirage-factory.net/2023/05/856/feed/ 0
開発環境の用意 | 3dsmax python入門 #01 https://cg-lab.mirage-factory.net/2023/05/839/?utm_source=rss&utm_medium=rss&utm_campaign=post-839 https://cg-lab.mirage-factory.net/2023/05/839/#respond Wed, 17 May 2023 08:56:51 +0000 https://cg-lab.mirage-factory.net/?p=839 3dsmax でPytohn ツールを開発するための準備について解説します。

The post 開発環境の用意 | 3dsmax python入門 #01 first appeared on Taguma CG Lab.]]>
 このチュートリアルでは「3dsmax2023以降で」「UIのあるツールを」「Pythonを使って作る」という内容を進めていきます。まずは必要な環境を用意していきますが、本稿ではシンプルな環境を作って進めていき、チュートリアルの最後におまけとして、より効率的な、ツール開発をメインでやる人向けの環境を紹介する予定です。

Python をインストールする

 3dsmax 2023にはPython 3.9.7 が同梱されています。この、3dsmax に標準でついているPython を使っても開発はできるのですが、UIを作る際に便利なツールが含まれていないため、別途独立したPython の環境を用意したほうが良いでしょう。

Python公式サイトにアクセスする

 まずpython の公式サイトであるpython.org にアクセスします。なにかとお世話になるサイトで、URLもシンプルなので覚えてしまいましょう。

 メインメニューからDownloads→Windows と進みます。

 この画像の時点では最新の安定板は3.11.3のようですね。

ダウンロードするバージョンを探す

 インストールするPythonは3.9.7以降であればなんでも構いませんが、あまり詳しくない人は3.9系にしておくと無難です。この項の執筆時点で3.9系の最新は3.9.16ですが、Windows用のインストールバイナリは3.9.13までしか出ていないようなのでここでは3.9.13を入れてみます。もちろん3dsmax 2023と同じ3.9.7を入れても問題ありません。

 表示されるリストの中から3.9系で「Windows installer(64-bit)」が用意されているものを探します。基本的にページ上部にあるものほど新しいですが、Windows用のインストーラが用意されていないバージョンもあるため、インストーラのリンクが存在しているバージョンを探しましょう。この時点では3.9系でインストーラができている最も新しいバージョンは3.9.13でした。

インストールする

 ダウンロードしたインストーラを実行してインストールします。

 インストーラの最初の画面で、まず下の方にある「Add Python3.9 to PATH」という項目にチェックを入れます。これは環境変数にPythonのパスを追加するという意味ですが、その意味が分からない人はチェックを入れておきましょう。わかる人は任意でチェックを入れるか判断してください。Pythonの複数バージョンを共存させている場合等、PATHに追加しないほうが良いケースもあります。

 準備ができたら上の「Install Now」というところを押すとインストールが始まります。インストール先がPython2系のときとは変更されています。Windows10のUAC(ユーザ・アカウント・コントロール)に対応するため、インストール先がユーザディレクトリになっています。古くからPythonを使っている人はこれを嫌って以前のようなパス(C:¥Python 等)に変更している人もいますが、Win10以降ではこの運用が標準なのでこちらに慣れたほうが良いと思います。

 以降はインストーラに従ってインストールすればOKです。特に気を付けるべきところはありません。

インストールを確認

 インストールが終わったら確認してみましょう。スタートメニューでcmd と打って検索し、コマンドプロンプトを開きます。この操作もしょっちゅう使うので覚えておきましょう。Winキー+Rでcmd と打って起動する方法でも構いません。ツール開発者たるもの、コマンドプロンプトは息をするように起動して使えるようにしておきましょう。

 起動したらコマンドプロンプトに以下のコマンドを入力して実行してみてください。

python -V

 このように、今インストールしたPython のバージョンが表示されれば成功です。

PySide2 をインストールする

 3dsmax のツール開発でUIを開発するのに使用するPySide2 というライブラリパッケージがあります。これをインストールしましょう。PySide2 は3dsmax にも入っているのですが、普通にインストールすると一緒に入るはずのツール群が入っていません。そこでこのように別途インストールしてそのツール群を取得しようというわけです。

pip をアップグレードする

 Python には追加のライブラリパッケージを管理するパッケージマネージャとして、pip というツールが用意されています。現在のPythonでは標準でインストールされているため、Python本体を入れるだけで使える状態になっています。Pythonをインストールしたらまずpipをアップグレードする、というのはセットにして覚えておきましょう。

 コマンドプロンプトに以下のコマンドを入力します。

python -m pip install --upgrade pip

 これはpip というパッケージマネージャを使用して、pip 自身を更新する、というコマンドになります。pip が最新になっていない場合、pip を使って何かをインストールするたびに「pipが最新じゃないよ」というメッセージが出るので、Python をインストールしたらまずpip をアップグレードしてしまうと良いでしょう。

PySide2 をインストールする

 pip がアップグレードできたらPySide2を入れましょう。

pytohn -m pip install PySide2

 余談ですがここで使用している-m というオプションは、python を起動し、-m の後ろに書いてあるモジュールをロードする、という意味です。モジュールをロードしてそのモジュールのコマンドを使用する場合にこういう書き方をします。

 上のコマンドを実行すると次のようになります。

 pip でのインストールは「Successfully installed 〇〇」と表示されれば成功しています。ここではPySide2をインストールしましたが、インストールされたのはPySide2の5.15.2.1 というバージョンで、一緒に依存パッケージであるshiboken2 もインストールされたことがわかります。

Qt Designer の場所を確認する

 ここまででPySide2がインストールできたわけですが、これをインストールすることによって一緒にインストールされるツールの中に、「Qt Designer」というPySide2を使ってUIを作るためのGUIツールがあります。3dsmax2023 にはPySide2は入っていますがこのQt Designer が含まれていないので、このように独立した環境でインストールしたわけです。

 ではこのQt Designer がインストールされている場所を確認し、スタートメニューやタスクバーにPinしたり、ショートカットを作成したり、各自起動しやすい方法で登録しておきましょう。

 Python3 系はデフォルトでは%LOCALAPPDATA%\Programs\Python 内にインストールされます。pip でインストールしたPySide2 はその中の以下のパスにあります。

%LOCALAPPDATA%\Programs\Python\Python39\Lib\site-packages\PySide2

 このdesigner.exe というのがQt Designer というツールです。これを右クリックし、スタートメニューかタスクバーにPinするか、ショートカットとしてデスクトップに置いておくと良いでしょう。

まとめ

  • ターゲットの3dsmax と同じかより新しいバージョンのPython をインストールする
  • Python は3dsmax に入っているのとマイナーバージョン(バージョンナンバーの小数第一位)まで同じものを使うのが無難
  • pip をアップグレードする
  • pip を使用してPySide2 をインストールする
  • Qt Designer を起動しやすいようにPinするかショートカットを用意しておく
The post 開発環境の用意 | 3dsmax python入門 #01 first appeared on Taguma CG Lab.]]>
https://cg-lab.mirage-factory.net/2023/05/839/feed/ 0
[2023版]3dsmax Python開発事情|3dsmax python入門 #00 https://cg-lab.mirage-factory.net/2023/05/824/?utm_source=rss&utm_medium=rss&utm_campaign=post-824 https://cg-lab.mirage-factory.net/2023/05/824/#respond Wed, 17 May 2023 08:53:28 +0000 https://cg-lab.mirage-factory.net/?p=824 2023年時点での3dsmax におけるPython 開発入門チュートリアルを始めるにあたって書いた2023年5月時点での実情あれこれ。

The post [2023版]3dsmax Python開発事情|3dsmax python入門 #00 first appeared on Taguma CG Lab.]]>
 

前置き挨拶

 まいどご無沙汰しております。おかげさまで3dsmax のツール開発に関するチュートリアルが細く長く読んでいただけているのですが、内容的にもはや古くて使えない(3dsmax2022以降では使えない)ため、最新の状況にアップデートして、3dsmax におけるPython を用いたツール開発のチュートリアルとして新たに書こうと思います。

 これを書いているのは2023年5月、私の所属しているプロダクションでは今のところ3dsmax 2022が主力、2023の検証が進んでいて、今後2023を使うプロジェクトも増えてくるかな、といった状況です。

 今回新たに初歩的なチュートリアルを書いてみるにあたり、この記事はその第0回として、2023年5月現在の3dsmax の事情について書いてみます。

MaxPlus → pymxs へ

 個人的に大きな問題だったのは、以前のチュートリアルでも使用していたMaxPlus というクラスが丸ごと廃止になったことです。厳密には廃止というか、Python2.7用として用意されていたMaxPlusについてPython3 では提供しない、という決定がされたという形でした。これのおかげで既存社内ツールの大幅な書き直しが発生して「超」大変でした。

MaxPlus とはなんだったのか

 この話はあまり初心者向けではないので読み飛ばしていただいて構いませんが、MaxPlus の廃止は個人的にかなりショッキングな出来事だったので少し書いておきます。

 MaxPlus というのは3dsmax に用意されているC++API(SDKとして提供されているもの)をPython でラップしたラッパーのようなパッケージで、これを使うことで、C++API を使わないとできなかったようなことがかなりの部分でPythonのみで書けるようになりました。私はこれに大喜びし、積極的にMaxPlus を使って3dsmax 2020までのツール開発を行っていました。

 ところが、3dsmax が2020から2021になるとき、Python 環境は3のほうがメインになり、MaxPlus はno longer available という状態になりました。Python3への移行は必至だし、そうなれば過去のツールが何一つ使えないという状態が見えてきました。

 MaxPlus はものすごく強力なパッケージだったのですが、内容がC++APIのラッパーに過ぎなかったこともあり、MaxScriptからPython に手を出していった多くの開発者にとってとっつきにくいものだったのだと思います。そのためユーザが増えず、Autodesk としては、「これいらないんじゃね?」という話になったのではないかと邪推しています。たしかに作ってる側からすればコスパ悪い状態だったのでしょうね。

全書き直し発生

 うちの社内では、3dsmax2021はスキップし、主力は2020→2022へと移行していきました。支援ツールについても、3dsmax 2022をターゲットにし、Python2から3へのリライト、MaxPlus からpymxs へのリライトなどを進めました。一部MaxPlus でないと使えない機能については、C++ を使ってMaxScript を拡張し、それをpymxsから呼ぶ、といった回りくどいやり方で実現したものもあります。このコストをどうしてくれるんだAutodeskさん…

Pythonマイナーバージョン差異問題

 さらに、3dsmax 2022用に開発したツールが、2023では動かない問題もありました。これはPythonのマイナーバージョンが違うことにより、.py ファイルをコンパイルして生成されるバイトコード.pyc ファイルの中身が異なる、という現象に起因したものでした。

 配布用のインストーラにはpyc ファイルを同梱するようにしていたため、2022用のpycバイナリが2023では動かないという問題はけっこう大きく、2022用と2023用を別ツールとして用意しなければならない、という状況になりました。3dsmax 2021、2022は3.7系、2023は3.9系であることがこの問題の原因です。今後2024以降でもPythonのマイナーバージョンが更新されることは十分想定できるので、今後3dsmax のバージョンごとにインストールバイナリを用意しなければならない、という事態になりそうです。

2023年版チュートリアルについて

 以上のような状況を踏まえて、2023年時点での3dsmax のPython開発入門という位置づけで、UIを持ったかんたんな支援ツールの開発をどういう風に始めれば良いのか、という部分について、詳しく解説したチュートリアルを書いていこうと思っています。このチュートリアルについては過去のチュートリアルと同様、全く初めての人がこれから始めるという状態を想定して、かなり冗長に説明していくつもりです。

 一方で、ある程度書いている人向けの話というのも発信していくと良いかなと思うので、チュートリアルとは別にそういう記事も書いていこうかなと思っています。ひそかに期待してもらえると嬉しいです。

 けっこうこのブログ全般、ほとんど反応はないけれど実は見られているらしい、という状態で、書いている本人としてはなかなかモチベーションが上がらないので、もし役に立ってましたらSNSでのシェアとかしていただけますとアガります。それによって更新頻度が上がるかは不明ですが、要望などもあれば寄せていただけると記事を書く参考になります。どうぞお気軽にご活用くださいませ。

The post [2023版]3dsmax Python開発事情|3dsmax python入門 #00 first appeared on Taguma CG Lab.]]>
https://cg-lab.mirage-factory.net/2023/05/824/feed/ 0
3dsmax2023 でマクロにブロックコメントを使うと起動時にエラーになる https://cg-lab.mirage-factory.net/2022/11/812/?utm_source=rss&utm_medium=rss&utm_campaign=post-812 https://cg-lab.mirage-factory.net/2022/11/812/#comments Fri, 18 Nov 2022 02:35:05 +0000 https://cg-lab.mirage-factory.net/?p=812  超お久しぶりです。皆さんお元気でしたでしょうか。僕は特に体調を崩したりとかいうこともなく非常に元気にしております。最近会社では新設された技術開発室でマネージャを任されるという事態になり、あちこち顔を出すことが多くなって […]

The post 3dsmax2023 でマクロにブロックコメントを使うと起動時にエラーになる first appeared on Taguma CG Lab.]]>
 超お久しぶりです。皆さんお元気でしたでしょうか。僕は特に体調を崩したりとかいうこともなく非常に元気にしております。最近会社では新設された技術開発室でマネージャを任されるという事態になり、あちこち顔を出すことが多くなってきてます。どこかで見かけたらぜひお声がけください。

3dsmax2023 で発見したマクロの問題

 さて、そんな具合に管理方面の仕事が増えてきて自分がコードを書くことはだいぶ減っているのですが、過去に作ったもののメンテやら何やらを中心に一応実作業もしてます。そんな中で、3dsmax2023で、2022まで普通に動いていたマクロスクリプトをそのまま持ってくるとエラーになるケースがあり、どうもブロックコメントがNGであるということがわかったので紹介します。

※ 追記あり。コメントで指摘を頂き、ブロックコメントが全部NGなのではなく、マクロ宣言と本体の間に書くのがNGだということがわかりました。(2022以前ではOKだったのでやはり2023からの変更ではありますが)

MaxScript におけるコメントに関して

 まず前提知識として、MaxScriptにおけるコメントの記述に関しておさらいしておきます。そんなこと知ってるという人は読み飛ばしてください。 

 多くのプログラム言語には実行コードとは関係ないコメントを記入する書式が用意されていて、これまた多くの言語で、ブロックコメントとインラインコメントの二つが用意されています。MaxScriptの場合は以下の二種類です。

-- 1行コメント
-- 「--」 と記入するとそこから後ろがコメントになる

a = 0 -- aを0で初期化 といった具合に行の途中からコメントにすることもできる

/*
ブロックコメント
/と* を並べて書くととそこからブロックコメントが始まり、
改行を含んで複数行のコメントを書くことができる。*と/を並べて書いたところで終わる。
*/

3dsmax2023 で問題が起こるケース

 ようやく本題。今回、3dsmax2022 以前のバージョンに向けて作成、配布していたツールを2023 でも使えるように用意していたところ、3dsmax を起動した際にマクロがロードされない、という事態が発生しました。スクリプトリスナーを見ると下記のようになっていました。

「/*」が不明なキーワードと言われる

 ここで検証したコードを載せておきます。

 わずかこれだけ。これを、Usermacroフォルダに格納し、3dsmax2023 を起動すると上記のようにリスナーにエラー(というか黒文字なのでエラー扱いではない?)が出て、このマクロは評価されていない状態になります。

 ここでやっかいなのは、おかしいな、と思ってこのマクロスクリプトをスクリプトエディタで開き、そこで評価(Ctrl+E)すると、なんの問題も起きずに評価され、このマクロが使えるようになるんですね。起動時の読み込みでのみ、エラーになります。なんでだよ。

 解決策は簡単で、ブロックコメントを使わずに全部一行ずつコメントにすればOKです。

 ブロックコメントでマクロ宣言よりも前に書く、のが良いようです。(詳細後述)

 こうすると起動時のエラーは出なくなり、正常にロードされます。

結論~追記 2022.11.28

 ブロックコメント使うのやめましょう、という話です。他の言語でもドキュメント自動生成ツールみたいなものを使う際に、ブロックコメントよりも通常の一行コメントの方が推奨されていたりするケースがありますし、なるべくコメントは一行コメントで書いた方が良さそうだと思っています。

kotenpa さんからコメントで、ブロックコメントが全部NGなのではなく、マクロ宣言と本体の間に書くのがNGだというご指摘をいただきました。検証してみたところ以下のようなことがわかりました。

/*
    マクロ宣言より前に書くのはOK。
    3dsmax 本体に最初から搭載されているマクロはここにブロックコメントで説明が書いてある。
*/

MacroScript TestMacro
ButtonText:"TEST"
Category:"TG_Tools"

/*
    マクロ宣言の後、本体のブロックが始まる前の部分に書かれたブロックコメントはNG
    僕は2022までここにマクロの説明を書いていたのでことごとくエラーに…
*/

(
    /*
        マクロ本体の中ならブロックコメントを書いてもOK
    */
    print "test done."
)

 マクロ宣言を書いた後、マクロの本体が始まる前(マクロ本体の()の前)にブロックコメントを書くとNG(一行コメントならOK)で、マクロ本体の中であればブロックコメントもOK、という挙動のようです。この挙動は3dsmax2022 には無かったのでやはり2023から何か変化したのだと思います。

 どうするのがベストプラクティス?と思って、3dsmax に標準搭載されているマクロを見てみたところ、マクロ宣言よりも前にブロックコメントで説明が書かれていました。ちなみにサードパーティのV-Rayのマクロは先頭がマクロ宣言で、本体内に一行コメントでコメントが記載されているのみでした。

 僕は個人的に、マクロ宣言の後にそのマクロの説明をブロックコメントで書いておいてから本体を書く、という感じにしていたのでこれが3dsmax2023でエラーになった、という事態でした。いったん一行コメントに直すという方法で対処したのですが、公式のマクロに習い、マクロ宣言よりも前に出す、という形式にしようと思います。

The post 3dsmax2023 でマクロにブロックコメントを使うと起動時にエラーになる first appeared on Taguma CG Lab.]]>
https://cg-lab.mirage-factory.net/2022/11/812/feed/ 2
PySide2 で重い処理をしている間プログレスバーを表示する https://cg-lab.mirage-factory.net/2021/09/798/?utm_source=rss&utm_medium=rss&utm_campaign=post-798 https://cg-lab.mirage-factory.net/2021/09/798/#respond Thu, 23 Sep 2021 06:18:16 +0000 https://cg-lab.mirage-factory.net/?p=798  表題の通りの内容。最終コードだけ欲しい人はこちら。 いきさつ  PySide2 を使ってアプリケーションを作るとき、重めの処理を行うとその間UIが「応答なし」になってしまう。自分しか使わないツールならば別にこれでも良い […]

The post PySide2 で重い処理をしている間プログレスバーを表示する first appeared on Taguma CG Lab.]]>
 表題の通りの内容。最終コードだけ欲しい人はこちら。

いきさつ

 PySide2 を使ってアプリケーションを作るとき、重めの処理を行うとその間UIが「応答なし」になってしまう。自分しか使わないツールならば別にこれでも良いのだけれど、やはり他の人が使う場合に、ソフトウェアが停止したのか処理中なのかわからない、というのはよろしくない。

 そこで、重い処理をしている間プログレスバーを表示したい。どのぐらい時間がかかるかわからない処理ではあるものの、処理中であることを明確にして、少なくともソフトウェアが停止したわけではないと見てわかるようにしたい。

処理を別スレッドにしたのにUIは「応答なし」になる

 処理中にプログレスバーを持ったダイアログを別窓として開き、終了時に自動的に閉じるようにしたい。そのためモードレスのダイアログを用意してそれを開く。最初に書いたコード(抜粋)がこちら。

# 仮の何か時間のかかる処理
def heavyProcess(self):
    time.sleep(10)

# 実行ボタンのハンドラ
def on_btn_clicked(self):
    # 時間のかかる処理を別スレッドで実行
    th = threading.Thread(target=self.heavyProcess, daemon=True)
    th.start()
    # 処理を開始したあとにメインスレッドでプログレスバーのダイアログを表示
    pg = ProgressDialog(self)
    pg.open()
    
    # Thread.join() で別スレッドの終了を待って
    th.join()
    # プログレスバーの窓を閉じる
    pg.accept()

 threading モジュールを使って別スレッドを立て、時間のかかる処理を走らせる。その間にメインスレッドでダイアログを表示し、別スレッドの終了を待ってダイアログを閉じる。

 なんとなく、これで行けるような気がしたのだがうまくいかない。

別スレッドの処理中、UIは「応答なし」になる

 こんな感じで、ダイアログ自体は開くものの、中身が表示されないまま「応答なし」になる。別スレッドの処理中はこの状態が続き、スレッドの終了とともにダイアログが閉じられて以降は正常動作に戻る。

モーダルダイアログにするとプログレスバーは正常に動く

 ためしにこのダイアログをモーダルなダイアログとして表示してみる。

 # 処理を開始したあとにメインスレッドでプログレスバーのダイアログを表示
    pg = ProgressDialog(self)
    # exec_() で開くとモーダルダイアログになる
    pg.exec_()
    
    # Thread.join() で別スレッドの終了を待つ
    th.join()

 これは一見うまくいく。

見た目としては成功な感じ

 ただ、これはモーダルなダイアログ、つまりこのダイアログを閉じるまでメインスレッドが止まったまま、という状態なので別スレッドの処理が終わってもダイアログは表示され続けてしまう。これだと別スレッドがどうなっているか全然わからないためプログレスバーの意味がない。

[解決] processEvents() を使う

 あれこれ模索して結局このモードレスなUIが応答なしになる現象から逃れられなかった私はTwitter に救いを求めた。

 するとCG関係のエンジニアリング界隈で有名なchiyamaさん(@chiyama) が教えてくださった。

 これがビンゴ。

        # ダイアログはモードレスで開く
        pg.open()
        # スレッドが生きている間、定期的にQApplicatiln.processEvents() を呼ぶ
        while th.is_alive():
            QtWidgets.QApplication.processEvents()
        # スレッドが終了するとis_alive() がFalse になってwhile を抜ける
        pg.accept()

 なお、QApplication はPySide ではQtGui に含まれていたけれど、PySide2 になってQtWidgets というモジュールに移されたのでこのような呼び方になる。場合によってwhile 節の中でtime.sleep() などで呼び出しサイクルを設定しても良いかもしれない。

 なお、教えてくれたchiyama さんはCGWorld の連載「痴山紘史の日本CG見聞録」などで有名な方です。助かりました。

最終的なコード(全体)

The post PySide2 で重い処理をしている間プログレスバーを表示する first appeared on Taguma CG Lab.]]>
https://cg-lab.mirage-factory.net/2021/09/798/feed/ 0
チュートリアルのまとめページを作りました https://cg-lab.mirage-factory.net/2021/09/794/?utm_source=rss&utm_medium=rss&utm_campaign=post-794 https://cg-lab.mirage-factory.net/2021/09/794/#respond Fri, 03 Sep 2021 00:50:52 +0000 https://cg-lab.mirage-factory.net/?p=794 お久しぶりです。あまりにも間が空きすぎてオワコン化した感がありますが生きてました。 さて、一部で好評をいただいている3dsmax のスクリプティング初心者向けチュートリアルですが、記事が埋もれていて探しにくい状態が続いて […]

The post チュートリアルのまとめページを作りました first appeared on Taguma CG Lab.]]>
お久しぶりです。あまりにも間が空きすぎてオワコン化した感がありますが生きてました。

さて、一部で好評をいただいている3dsmax のスクリプティング初心者向けチュートリアルですが、記事が埋もれていて探しにくい状態が続いていたので、固定ページにまとめました。

チュートリアルまとめページ

3dsmax のMaxScript の入門チュートリアルと、Pythonを使用するツール作成のチュートリアルです。Python の方のものはMax2020対応版で、3dsmax 2021のPython2 モードでは動きますが、Python3モード、および3dsmax 2022以降では動きません。そのため、これの3dsmax2022 対応版も今後追加しようと考えております。

このチュートリアルはなかなか他では説明されていない部分に触れた内容になっているため、これから3dsmax でのスクリプティングを始めようとする人はもちろん、既にある程度やっている方でも発見があるかもしれません。ぜひご活用いただければと思います。

The post チュートリアルのまとめページを作りました first appeared on Taguma CG Lab.]]>
https://cg-lab.mirage-factory.net/2021/09/794/feed/ 0
表示の不具合について https://cg-lab.mirage-factory.net/2020/10/717/?utm_source=rss&utm_medium=rss&utm_campaign=post-717 https://cg-lab.mirage-factory.net/2020/10/717/#respond Sat, 31 Oct 2020 01:27:45 +0000 http://cg-lab.mirage-factory.net/?p=717  ここ数日、このサイトを含めたマルチサイトを管理しているWordPress と各種プラグインなどを更新したところ、一部記事でタイトル以外の中身が何も表示されないという現象が発生していました。 原因  原因を調査したところ […]

The post 表示の不具合について first appeared on Taguma CG Lab.]]>
 ここ数日、このサイトを含めたマルチサイトを管理しているWordPress と各種プラグインなどを更新したところ、一部記事でタイトル以外の中身が何も表示されないという現象が発生していました。

原因

 原因を調査したところ、いくつかのプラグインに含まれているJavaScript でエラーが発生していました。WordPress の最近のバージョンアップでプラグイン周りの大きな変更があったようで、更新頻度の低いプラグインはこれに対応できておらず、正常に動作しないことが判明しました。

 使用していたテーマも一部機能が正しく動かない状態になっていました。

対策

  • テーマのバージョンアップ
  • プラグイン類のバージョンアップ
  • エラーの出るプラグインを無効化

という対策を行いました。表示されていなかった記事について、正しく表示される状態になったことを確認しました。

 ご迷惑をおかけしました。正しく表示されていない記事などありましたら、Twitter @tagdev1 までお知らせいただけるとありがたいです。

The post 表示の不具合について first appeared on Taguma CG Lab.]]>
https://cg-lab.mirage-factory.net/2020/10/717/feed/ 0
Mayaで選択オブジェクトの透過表示を切り替えるMel https://cg-lab.mirage-factory.net/2020/09/697/?utm_source=rss&utm_medium=rss&utm_campaign=post-697 https://cg-lab.mirage-factory.net/2020/09/697/#respond Wed, 16 Sep 2020 11:56:33 +0000 http://cg-lab.mirage-factory.net/?p=697 ビューポートでオブジェクトを透過表示したい  3dsmax ではオブジェクトを選択してビューポート上で個別にシースルー表示する、というのが簡単にできますが、Maya ではどうやるのかな、と疑問に。  どうやらMel でで […]

The post Mayaで選択オブジェクトの透過表示を切り替えるMel first appeared on Taguma CG Lab.]]>
ビューポートでオブジェクトを透過表示したい

 3dsmax ではオブジェクトを選択してビューポート上で個別にシースルー表示する、というのが簡単にできますが、Maya ではどうやるのかな、と疑問に。

 どうやらMel でできるぞってことで書いてみました。

{
    int $state[];
    int $val;
    $sel = `ls -sl`;
    for($n in $sel){
        $state = `displaySurface -query -x $n`;
        print $state;
        $val = $state[0] ? 0 : 1;
        displaySurface -x $val $n;
    }
}

シェルフに置いておくと超便利!

The post Mayaで選択オブジェクトの透過表示を切り替えるMel first appeared on Taguma CG Lab.]]>
https://cg-lab.mirage-factory.net/2020/09/697/feed/ 0
3dsmax でテキストデータをオブジェクトに保存する方法 https://cg-lab.mirage-factory.net/2020/09/678/?utm_source=rss&utm_medium=rss&utm_campaign=post-678 https://cg-lab.mirage-factory.net/2020/09/678/#respond Tue, 08 Sep 2020 05:40:24 +0000 http://cg-lab.mirage-factory.net/?p=678  コメントで「3dsmax でメモだけを残すモディファイヤみたいなものを作れないか」という質問を頂いたのでその説明を。  このエントリではスクリプトを使用しない方法を紹介します。追ってスクリプトから操作する方法も紹介した […]

The post 3dsmax でテキストデータをオブジェクトに保存する方法 first appeared on Taguma CG Lab.]]>
 コメントで「3dsmax でメモだけを残すモディファイヤみたいなものを作れないか」という質問を頂いたのでその説明を。

 このエントリではスクリプトを使用しない方法を紹介します。追ってスクリプトから操作する方法も紹介したいと思います。

カスタムアトリビュートとは

 3dsmax ではシーン内のオブジェクト(厳密にはノード)に対してカスタムアトリビュートを持たせることができます。

 カスタムアトリビュートというのは文字通り、カスタムなアトリビュート、つまり、ユーザが自由に追加できるアトリビュート、パラメータという意味です。カスタムアトリビュートはさまざまなところに保存できますが、今回質問は「モディファイヤみたいなもの」ということだったのでモディファイヤを使用する方法を紹介してみようと思います。

アトリビュートホルダーを適用する

 シーン内の任意のオブジェクトを選択し、「アトリビュートホルダー」というモディファイヤを適用します。

 アトリビュートホルダはその名の通り、アトリビュートを保持するという目的だけのためのモディファイヤです。これ自身にはパラメータは一つもありません。

パラメータを追加する

 メインメニューバーから、アニメーション→パラメータ編集とたどります。

 若干UIが不親切ですが、「パラメータ編集」機能を使って、カスタムアトリビュートの操作ができます。3dsmax において、アトリビュートとパラメータという用語はほぼ同じものとして使われています。

パラメータの設定を行う

 パラメータ編集画面では、追加するカスタムアトリビュートの詳細や、ユーザインタフェースの設定などができます。

 今回はオブジェクトに対して「メモ」を追加したいので、文字列(String)タイプのパラメータを追加しましょう。

 まずは追加する場所を設定します。

「タイプを追加」のプルダウンから、追加先を選択できます。

  • 選択したオブジェクトの基本レベル
    • 選択されているオブジェクトそのものにアトリビュートを追加します
  • 選択したオブジェクトの現在のモディファイヤ
    • 選択されているモディファイヤにアトリビュートを追加します
  • 選択したオブジェクトのマテリアル
    • 選択されているオブジェクトのマテリアルにアトリビュートを追加します
  • トラックを選択
    • トラックを指定してアトリビュートを追加します。(これを使うとシーンにオブジェクトが何もなくてもシーンルートトラックにアトリビュートを追加することができます)

 今回はモディファイヤを選択しましょう。オブジェクトを選択して修正パネルを開き、先ほど追加した「アトリビュートホルダ―」を選択しておいてください。

 続いてパラメータタイプを設定します。これは追加するアトリビュートの型(タイプ)を設定する部分です。プルダウンを開いてみると、実に様々なタイプのアトリビュートを追加できることがわかります。今回は「String」を選びます。

 パラメータタイプを設定するとそれと連動してUIタイプが変化します。UIタイプはEditTextで良いのでそのままにしておき、名前を設定しましょう。今回はメモを入力する想定なのでmemoとしました。

 これで準備ができたので、少し上へ戻って「追加」ボタンを押します。「追加」を押すと、選択していたアトリビュートホルダにカスタムアトリビュートが追加されます。

 追加されたアトリビュートを見ると、テキスト編集部分が狭いですね…。入力したいメモが一言だけなのであればこれでも良いですが、これだと狭い場合はこのUIを修正しましょう。

 すでに作成されているアトリビュートを編集する際は、「パラメータ編集」画面の「編集/削除」ボタンを押します。

 「編集/削除」ボタンを押すと「アトリビュート/パラメータを編集」という窓が開き、カスタムアトリビュートが列挙されるので、編集したい項目を選択します。この辺もUIがわかりにくいのですが、選択した時点で「パラメータ編集」窓の中身は選択したアトリビュートの内容に更新されます。

 この状態で内容を変更します。今回は「ストリングUIオプション」のところで、「サイズ」の「高さ」を拡大しましょう。

 ここでは適当に50にしてみました。初期値で確認できるように、一行分でこの高さは17でした。それを参考に、必要な大きさに拡大してください。

 数値を変更したら「アトリビュート/パラメータを編集」の画面に戻り、左下の「変更を適用」を押します。

 手順をおさらいするとこのようになります。

  1. パラメータ編集窓で「編集/削除」ボタンを押す
  2. 「アトリビュート/パラメータを編集」窓が開くので、修正したいパラメータを選択する
  3. 編集したい項目を修正する
  4. 「変更を適用」ボタンを押す
  5. 変更が反映される

カスタムアトリビュートを使う

 これで、ティーポットオブジェクトにメモを入力するためのアトリビュートが追加できました。さっそく使ってみましょう。

 メモが書けるようになりました。もちろんこのメモはシーンデータに保存されます。モデルの更新情報や、リグの注意点などを書いておくと便利だったりします。

まとめ

 カスタムアトリビュートは非常に便利で、アイデア次第でかなり発展的な使い方もできるのですが、UIのデザインが良くないせいでとっつきにくいものになっていると感じます。分かりにくさの原因のひとつは用語が統一されていないことです。

 カスタムアトリビュートはわかりにくいですが、難しくはないのでぜひ使ってみてください。

 なお、カスタムアトリビュートはスクリプトからもかなり柔軟に操作でき、GUIから操作すると面倒なこともより簡単にできたりします。が、こちらもGUIと同じかそれ以上にわかりにくいので、そのうち解説エントリを書きたいと思います。応用するとなんでもかんでもシーンデータに保存できるようになって恐ろしく便利です。

The post 3dsmax でテキストデータをオブジェクトに保存する方法 first appeared on Taguma CG Lab.]]>
https://cg-lab.mirage-factory.net/2020/09/678/feed/ 0