Houdiniプラグイン開発備忘録2 ~ CMakeでのビルド編

最近、どうしても自作したいSOPがあり、Windows環境でのHoudiniのプラグイン開発を少しずつ学び始めたので、忘れないようにメモしていこうと思います。

HDKリファレンスには、「Visual Studio IDEからビルドする場合は、代わりにCMakeを使用することをお勧めします。」との記載があります。

また、「hcustomによるビルドでは単一のソースファイルしか扱えない」旨の記述もあり、hcustomは手軽ではあるけど不便だろうなぁと想像しています。

というわけで、今後使い続けることになりそうなCMakeでのビルドを知るため、前回のhcustomによるビルドに引き続き、CMakeを使うビルドを試してみようと思います。

今回もサンプルの「SOP_Star」を使います。


■CMakeとは?

CMakeは、プログラムのビルド処理を自動化してくれる便利ツールです。
今回CMakeで行う処理は、大きく分けて以下の2つです。

・Visual Studioのソリューションとプロジェクトの自動生成
・自動生成されたそれらのファイルを使ってビルドを行う


■環境の準備

まず、手元の環境ですが、以下のようなツールがインストールされています。

Houdini 18.0.532 Indie
Visual Studio 2019 / 2017 / 2015 Community
CMake 3.18.1


■ビルド手順

・ソースコードをコピーする

以下のフォルダを、任意の場所にコピーします。
今回は、以下のようにコピーしました。

コピー元:$HFS\toolkit\samples\SOP\SOP_Star
コピー先:D:\temp\HDK\build_cmake\SOP_Star

※$HFSは、Houdiniのインストールフォルダで、デフォルトでは以下のようなパス

C:\Program Files\Side Effects Software\Houdini 18.0.532

・Command Line Toolsを起動する

スタートメニューで「Command Line Tools」と検索し、起動します。
「Command Line Tools 18.0.532」というタイトルの、Houdini関連の環境変数が一通り自動的にセットされたコマンドプロンプトが立ち上がります。

※どんな環境変数がセットされているかは「set」と打ち込んでEnterを押すことで一覧表示、確認できます。


・ソリューションとプロジェクトを生成する

CMakeでソリューションの生成を実行すると、カレントのフォルダに、Visual Studioのソリューションファイル以外にも、いろいろなファイルが大量に生成されます。

これらの生成されたファイルがソースコードと混ざってしまうと、生成前の状態に戻すことが難しくなります。

そのため、予めビルド生成物をまとめるbuildフォルダをつくり、もとのソースコードとCMakeの生成ファイルを切り分けて管理できるようにします。

まずソースコードと同じフォルダにbuildという名前のフォルダを作ります。

D:\temp\HDK\build_cmake\SOP_Star\build

Command Line Toolsで以下を実行し、カレントのフォルダをbuildフォルダに移動します

cd /d D:\temp\HDK\build_cmake\SOP_Star\build

次に、以下を実行し、ソリューションとプロジェクトを生成します。

cmake ..

カレントのbuildフォルダに、各種ファイルが生成されます。

ここで生成されるソリューションとプロジェクトは、SOP_Starフォルダの直下にあるCMakeLists.txtファイルに記述された設定に基づいて作成されます。

カスタムプラグインを作成するためのCMakeLists.txtを書く際は、このCMakeLists.txtを参考にすれば良さそうです。


・プラグインをビルドする

続いて、以下を実行します。

cmake --build . --clean-first

これで、カレントのフォルダにあるソリューションとプロジェクトファイルを使い、プラグインが以下のフォルダにビルドされます。

%USERPROFILE%\Documents\houdini18.0\dso
SOP_Star.dll
SOP_Star.exp
SOP_Star.ilk
SOP_Star.lib
SOP_Star.pdb

・Houdiniでプラグインの動作を確認する

・Houdiniを起動
・Geometryを内でTABメニューから[star]と検索し[Star]ノードを作成
・無事ビューポート内に星型のジオメトリが現れればOKです。


ここまでで、hcustomとCMakeのそれぞれを使って、手軽にプラグインがビルドできることを確認してみました。

同じ要領で、サンプルにある各種プラグインをビルドすることができそうです。

Houdini – Python(HOM)によるパラメータ操作

 前回は、HOMによりノードを選択する操作に関する記事を書きました。
 ノードを探して選択するだけではあまりにも寂しいので、今回はそれに引き続き、ノードのパラメータを操作する部分に関するメモを書き残したいと思います。
 Houdiniでは、ノードパラメータへの操作パスが幾通りも存在しており、とても柔軟な作りになっていますが、今回は、自分がよくやる方法を取り上げます。
「もっと便利な方法があるよー」などの情報があればぜひ教えてください。
 ここで紹介する各種メソッドはあくまでも一例で、実際には更に多くのメソッドが用意されているので、ぜひマニュアルも参照してください。
http://www.sidefx.com/docs/houdini/hom/hou/Node.html
http://www.sidefx.com/docs/houdini/hom/hou/Parm.html
http://www.sidefx.com/docs/houdini/hom/hou/ParmTuple.html


パラメータ名の確認

 まず最初に、パラメータへアクセスする際はパラメータ名が重要になるので、名前の確認方法から。

Parameter Spread Sheetで確認

 Parameter Spread Sheetで、ノードとパラメータをツリー表示で確認できる。
 各項目名の右端にある、カッコで囲まれた名前が実際にアクセスする際に使用するパラメータ名。パラメータにはUniform Scaleのような単一の値を持つパラメータと、位置や回転など、複数の値をひとまとめにして持つ配列パラメータがある。
 下図を例にすると、Axis Divisionsという「ラベル」が付けられたパラメータは、実際の名前が「divrate」であり、3つの子要素(divratex/y/z)を持っている配列パラメータである事がわかる。

ドラッグ&ドロップで確認


 Python Source Editorに、パラメータラベルをドラッグ&ドロップすることで正しい配列パラメータのパスと名前が確認できる。

ポップアップウインドウで確認

 Parametersパネルなどでパラメータ名ラベルの上にマウスカーソルを置き、少し待つと、パラメータ情報がポップアップで表示される。この中の、Parametersの部分にある文字列が、このノードにおけるパラメータの正式な名前になる。

↑↑↑
 この場合、tx ty tz がパラメータ名であると確認できるが、あくまでも配列要素の単一パラメータ名のみがわかる。
 正確な親の配列パラメータ名が知りたければ、Parameter Spread Sheetを確認するなど一手間必要。(大体の場合、配列要素パラメータ名の末尾を削ったものが配列パラメータ名になっているっぽい)


おおまかなパラメータアクセスの方法

パラメータへのアクセス方法

  1. パスを指定して直接パラメータオブジェクト(hou.Parm)へアクセス
  2. 任意のノードのパラメータオブジェクト(hou.Parm)へ、名前を指定してアクセス

パラメータの操作方法

  1. ノードオブジェクト(hou.Node)を直接操作
  2. パラメータオブジェクト(hou.Parm)を通して操作

単一パラメータオブジェクトの取得

import hou

# パラメータオブジェクトを直接取得
parm_direct = hou.parm('parm_path')

# ノードオブジェクトからパラメータ名を使用してパラメータオブジェクトを取得 
node = hou.node("node_path")
parm_from_node = node.parm('parm_name')

配列パラメータオブジェクトの取得

 translateやrotateのような複数要素で構成されるパラメータは、配列パラメータとしてまとめて各要素へアクセスすることができる。
 基本的に、これは単一のパラメータへのアクセスと同じように行うが、その際は、parmTupleやevalParmTupleといった配列パラメータを扱う専用のメソッドを使う。

import hou

# 配列パラメータオブジェクトの子要素配列を取得
tupleParm_direct = hou.parmTuple('tupleParm_path')

# 配列パラメータの値を取得
# ノードオブジェクトをつかまえておく
node = hou.node("node_path")
tupleParm_from_node = node.parmTuple('tupleParm_name')

# 子要素へは、tupleParmの配列要素へアクセスして行う
tupleParm_x = tupleParm_from_node[0]
tupleParm_y = tupleParm_from_node[1]
tupleParm_z = tupleParm_from_node[2]

単一パラメータの値を取得する

 eval~と名づけられたメソッドを使う。
 型を指定して型変換しながら値を取得するメソッドや、時間指定で値を取得するメソッド、それらを組み合わせて取得するメソッドなどもある。

import hou

# 単一パラメータオブジェクトから現在の値を取得
parm = hou.parm("parm_path")
parmValue = parm.eval()

# 型を指定ながら現在の値を取得
parmIntValue = parm.evalAsInt()
parmFloatValue = parm.evalAsFloat()
parmNodeValue = parm.evalAsNode()

# 指定時間での値を取得
parmAtFrameValue = parm.evalAtFrame( FRAME_NUMBER )

# 型指定と時間指定の組み合わせ
parmIntAtFrameValue = parm.evalAsIntAtFrame( FRAME_NUMBER )
parmFloatAtFrameValue = parm.evalAsFloatAtFrame( FRAME_NUMBER )

配列パラメータの値を取得する

 配列パラメータの値取得も、単一パラメータとほぼ同様。

import hou

# 配列パラメータオブジェクトから現在の値を取得
tupleParm = hou.parmTuple("parm_path")
tupleParmValue = tupleParm.eval()

# 型を指定ながら現在の値を取得
parmIntValues = tupleParm.evalAsInts()
parmFloatValues = tupleParm.evalAsFloats()
parmNodeValues = tupleParm.evalAsNodes()

# 指定時間での値を取得
parmAtTimeValue = parm.evalAtTime( TIME )
parmAtFrameValue = parm.evalAtFrame( FRAME_NUMBER )

# 型指定と時間指定の組み合わせ
parmIntAtFrameValues = parm.evalAsIntsAtFrame( FRAME_NUMBER )
parmFloatAtFrameValues = parm.evalAsFloatsAtFrame( FRAME_NUMBER )

パラメータをセットする

 値のセットは、set系メソッドを使う。
 事前に組み立てたパラメータ辞書をsetParmsメソッドに与え、一括で値をセットするのがとても楽なのでおすすめ。
 その際、setParmsメソッドでは配列パラメータ名は認識できないので、各要素を個別の単一パラメータとして辞書に値を用意します。

import hou

# 単一パラメータに値をセットする
parm = hou.parm('parm_path')
parm.set( value )

# 配列パラメータにシンプルな値の配列をセットする
tupleParm = hou.parmTuple('parm_path')
tupleParm.set( (value1, value2, value3, ...) )

# ノードオブジェクトを使い、複数のパラメータをまとめてセットする
# セットしたいパラメータを辞書に溜め込み、setParms()に与える
parmDict = { 'parm_name1':value1, 'parm_name2':value2, ... }
node = hou.node('node_path')
node.setParms(parmDict)

 以上、何か不明点などあれば気軽にご質問ください。
 間違いがあればツッコミも大歓迎です。

CGWORLD v.230 に寄稿させていただきました

久しぶりの更新が宣伝というのもアレですが・・・

2017/09/08発売 の CGWORLD v.230 に掲載の特集記事「すぐに役立つTIPSとアプローチ ワンランク上のキャラクターリギング」に、株式会社コロッサス名義で「ダイナミックコントロールリグのためのツール活用術」というタイトルの記事を寄稿させていただきました。

地味に手間のかかる馬の手綱セットアップを例として、ダイナミックリグの作成方法と、ツールによって作業を簡単化する方法を紹介しています。

また、スクリプトは書いたことがあるけれど、APIまでは手を出したことがないという方向けに、Maya Python API 2.0によるOpenMayaの基本的な使い方の解説と、メッシュ上の最近接点の取得や、カーブ上の一点が持つ各ベクトルからのマトリクス組み立てといった、覚えておくと色々な場面で役に立つ内容も盛り込んでみました。

また、現在在籍している「株式会社コロッサス」のブログにて、ツールの配布等もしておりますので、ぜひ本誌を手に取っていただけると幸いです。

<株式会社 コロッサス:ブログ CGWORLD v.230
<CGWORLD SHOP:CGWORLD v.230

MYAM_QuickSelector 途中経過

今年の1月末くらいに作り始めたのはいいものの、公私ともに忙しく、なかなか手がつけられなかったツール「MYAM_QuickSelector」が、このGWでだいぶ形になってきたので、途中経過を駆け足で記事にしてみます。

Qtは目的に到達するまでの手続きが多く面倒な部分も多いですが、慣れるて来るととても楽しいです。もうMaya標準のGUIライブラリは触れません。


■画面サンプル

bandicam 2016-05-08 22-32-34-391

好きな画像をツール上からキャプチャし、背景に置いた状態で好きなノードに対応するボタンを配置し、いつでも簡単に選択できるようになります。
選択できるアイテムはいわゆるDAGオブジェクトだけでなく、マテリアルなどのDGノードにも対応します。


■マルチプラットフォーム(DCCツール間)

bandicam 2016-05-08 23-10-05-047

Maya上でもHoudini上でも全く同様に動作します。(現在はMayaのみ対応ですが)
その他にも、最小限の拡張でPySideが使えるすべてのDCCツールに対応可能です。
DCCツールを変えた時にも、操作法が統一されていると覚え直す必要もなく楽です。


■キャプチャ機能

bandicam 2016-05-08 23-13-10-644

Bandicamのようにウインドウを使ってキャプチャ範囲を指定して、背景画像として取り込むことができます。


■セレクターボタンは任意の画像が使えます

bandicam 2016-05-08 23-12-43-450

ジョイントやコントロールリグなど、タイプごとに任意の画像をアイコンとして設定できます。
また、各アイコンのサイズも自由に変更できるため、よく使うコントローラを目立たせておくなど、視認性を高められます。
アイコン置き場フォルダに任意の画像を格納しておけば、自動的にツール内で使用できるようになります。


■その他

その他にも、配置したボタンの整列なども手軽に行なえます。


もう少し洗練して、早くリリースしたいなあ。

MYAM_genericSelector(仮名)を作り始めました

昔とある仕事場で作成した MYAM_quickSelector2 の後継ツールをQtで作り始めました。

前述のツールの機能に加え、足したい機能は大体以下の様な感じです

・オブジェクトアイコンに任意の画像が使用できる
・オブジェクトアイコンのサイズや形状を自由に変更できる
・Maya/MotionBuilder/Houdiniあたりで共通して使えるようにする
・選択オブジェクトのグループ登録/簡単選択

まだほとんど仮組みの状態ですが、完成したらCreative Crashあたりで公開すると思います。

MYAM_genericSelector_SS001

Ricoh THETA S でIBL画像を作成するためのスクリプトを書きました

簡易的なIBL用のHDRIを作成するため、素材になる天球マップ画像をRicoh THETA SをiPhoneで遠隔操作して手軽にオートブラケット撮影できるようにしてみました。

撮影方法は、ISOを固定しEVを変化させて行う露出ブラケット撮影になります。
今の所、使用する撮影モードではシャッタースピードを同時に操作できなそうなので、自動設定にしてあります。

THETA SはRAWデータでの撮影ができず、また撮影時に厳密なホワイトバランス設定などもできないので精度は落ちますが、簡易的に使用するにはそこそこ使えるHDRIが得られると思います。

iOSデバイスに限らず、THETA Sとネットワーク接続できるデバイスで、なおかつ標準的なPythonを実行できるなら同じように使用できるはずです。

というわけで、需要がありそうなのでソースコードを公開します。

時間の関係で良い作例の用意ができていませんが、そのうち貼りたいと思います。

・使い方

1:デバイスとTHETA Sをネットワーク接続接続します

2:下記スクリプトをデバイス内のPython実行環境で実行します
当方は、iPhone6S+上で iOS用のPython 2.7 というアプリを使用しています。

3:撮影されたJPEG画像をHDR ShopLuminance HDRなどを使ってHDRIにします

#******************************************************************************
'''
Tool Name     : MYAM_ThetaAutoBracket.py
Description   : 
Copyright     : (c) Yamabe Michiyoshi
Author Name   : Yamabe Michiyoshi
'''
#******************************************************************************

import httplib
import json
import time

# 
class ThetaSettings(object):
    # Full
    # evs = [-2.0, -1.7, -1.3, -1.0, -0.7, -0.3, 0.0, 0.3, 0.7, 1.0, 1.3, 1.7, 2.0]

    # Simple
    evs = [-2.0, -1.0, 0.0, 1.0, 2.0]

    exposureProgram = 9
    iso = 100
    whiteBalance = "auto"
    _shutterVolume = 50

    shutterInterval = 1

#
class Theta(object):
    def __init__(self):
        self.headers = {"Content-Type":"application/json", "Accept":"application/json"}

    # Auto Bracket
    def takePicturesByAutoBracket(self):

        print "--------------------"
        print "START AUTO BRACKET"
        print "--------------------"

        # Connect to THETA 
        connection = self.connect()

        # Start Session
        self.startSession(connection)

        # Get Session ID
        responseData = self.getResponseData(connection)
        sessionId = responseData["results"]["sessionId"]

        # Take Pictures
        for i in range(len(ThetaSettings.evs)):
            
            # Set options
            ev = ThetaSettings.evs[i]
            
            print "----------"
            print "%i / %i" % ((i + 1), len(ThetaSettings.evs))
            print "iso : %i" % ThetaSettings.iso
            print "EV  : %d" % ev

            picOpt = {"exposureProgram":ThetaSettings.exposureProgram,
                      "exposureCompensation":ev,
                      "iso":ThetaSettings.iso,
                      "whiteBalance":ThetaSettings.whiteBalance,
                      "_shutterVolume":ThetaSettings._shutterVolume}

            self.setOptions(connection, sessionId, optionParams=picOpt)
            responseData = self.getResponseData(connection)

            # Take Pictute
            self.takePicture(connection, sessionId)
            
            # Wait to finish idle time
            self.waitFinishCurrentCommand(connection, interval=ThetaSettings.shutterInterval)

        # Close Session
        self.closeSession(connection, sessionId)

        # Disconnect from THETA
        self.disconnect(connection)

        print "--------------------"
        print "FINISH AUTO BRACKET"
        print "--------------------"

    # HTTPConnection Control
    def connect(self):
        connection = httplib.HTTPConnection("192.168.1.1",80)
        return connection

    def disconnect(self,connection):
        connection.close()

    # Common Commands
    def postExecCommand(self,connection,params):
        connection.request("POST", "/osc/commands/execute", params, self.headers)

    def postStatusCommand(self, connection, commandId):
        params = json.dumps({ "id":commandId })
        connection.request("POST", "/osc/commands/status", params, self.headers)

    def waitFinishCurrentCommand(self, connection, interval=1):
        responseData = self.getResponseData(connection)
        commandId = responseData["id"]

        while True:
            self.postStatusCommand(connection, commandId)
            responseData = self.getResponseData(connection)
            commandStatus = responseData["state"]

            if "inProgress" == commandStatus:
                print "commandId [%s] : Processing..." % ( commandId )
                time.sleep(interval)
            else:
                print "commandId [%s] : Finish!" % ( commandId )
                break

    # Command Control
    def startSession(self, connection):
        params = json.dumps({ "name":"camera.startSession", "parameters":{} })
        self.postExecCommand(connection, params)

    def updateSession(self,connection,sessionId):
        params = json.dumps({ "name":"camera.updateSession", "parameters":{"sessionId":sessionId} })
        self.postExecCommand(connection, params)

    def closeSession(self, connection, sessionId):
        params = json.dumps({ "name":"camera.closeSession", "parameters":{"sessionId":sessionId} })
        self.postExecCommand(connection, params)

    def takePicture(self,connection , sessionId):
        params = json.dumps({ "name":"camera.takePicture", "parameters":{"sessionId":sessionId} })
        self.postExecCommand(connection, params)

    def setOptions(self, connection, sessionId, optionParams):
        params = { "name":"camera.setOptions" , "parameters":{"sessionId":sessionId, "options":{} } }

        for key , value in optionParams.items():
            params["parameters"]["options"][key] = value

        jsonParams = json.dumps(params)
        self.postExecCommand(connection, jsonParams )

    def getOptions(self, connection, sessionId, optionNames):
        params = { "name":"camera.setOptions" , "parameters":{"sessionId":sessionId, "optionNames":{} } }

        for key , value in optionParams.items():
            params["parameters"]["options"][key] = value

        jsonParams = json.dumps(params)
        self.postExecCommand(connection, jsonParams )

        self.postExecCommand(connection, params)

    # Support Functions
    def getResponseString(self,connection):
        response = connection.getresponse()
        responseString = response.read()
        return responseString

    def getResponseData(self,connection):
        responseString = self.getResponseString(connection)
        responseData = self.convertJSONToPythonData(responseString)
        return responseData

    def convertJSONToPythonData(self,jsonDataString):
        jsonDecoder = json.JSONDecoder()
        pythonData = jsonDecoder.decode(jsonDataString)
        return pythonData

    def convertPythonDataToJSON(self,pythonData):
        jsonEncoder = json.JSONEncoder()
        jsonDataString = jsonEncoder.encode(pythonData)
        return jsonDataString

def main():
    theta = Theta()
    theta.takePicturesByAutoBracket()

if __name__ == "__main__":
    main()

・設定

設定の変更を行うにはコードを直接書き換える必要があります。
撮影枚数は、ThetaSettingsの evs で指定されているEV値の数で指定されます。
撮影ごとにリスト内のEV値がそれぞれ使用されます。

・EV値と撮影枚数の設定

class ThetaSettings(object):
    # Full
    # evs = [-2.0, -1.7, -1.3, -1.0, -0.7, -0.3, 0.0, 0.3, 0.7, 1.0, 1.3, 1.7, 2.0]

    # Simple
    evs = [-2.0, -1.0, 0.0, 1.0, 2.0]

指定できるEV値は、上記の Full で指定されている13個の値になります。
THETA Sは一枚撮影するごとに内部処理に約8秒ほどかかるようなので、撮影枚数が多くなると時間がかかります。
必要に応じ、不要な値を削除するなどして調整してください。

・各種オプションの設定

    exposureProgram = 9
    iso = 100
    whiteBalance = "auto"
    _shutterVolume = 50

現在は、上記を指定できます。
必要に応じ、RICOH THETA API v2 Referenceを参照して値を設定してください。
といいつつおそらく上記の中では iso と _shutterVolume くらいしかいじらないと思います。

・_shutterVolume
0−100の間で指定します。0が無音です。

・iso
以下の値が使えます
100, 125, 160, 200, 250, 320, 400, 500, 640, 800, 1000, 1250, 1600

Fabric Engine 2 プラグインをMayaへ導入する

ふと気が付くとFabric Engine 2.0 がリリースされていたので、導入方法をメモしておきます。
やり方はほとんどバージョン1.15.3と変わらず。ライセンス入力あたりだけが大きく違います。

説明が急ぎ足なので、後で加筆修正するかも。


■Fabric Engine サイト

http://fabricengine.com/


■ダウンロードリンク

以前のようにEvaluation Lisenceのダウンロードリンクはありません。
ライセンスは後から取得し入力します。

FabricEngine-2.0.0-Windows-x86_64.zip
約500MB程度。ダウンロードを待ち、完了させます。


■解凍とコピー

ダウンロードが完了したら、zipファイルを解凍します。
解凍してできたフォルダ「FabricEngine-2.0.0-Windows-x86_64」は、C:\Program Filesの直下に置きました。


■ライセンスの取得

再びダウンロードページに戻り、ページ下部にある評価ライセンス請求フォームへ記入し、送信します。


送信が完了すると、ライセンスコードが表示されます。

このライセンスコードを保存しておき、あとでFabric Engineのライセンス登録フォームに記入します。


■Maya.envの編集

・以下を追加
MAYA_MODULE_PATH=C:\Program Files\FabricEngine-2.0.0-Windows-x86_64\DCCIntegrations\FabricMaya2014;


■Maya起動とプラグインのロード

ロードが完了すると、メニューに「Fabric」が追加されます。


■ライセンスの入力

ちょっと曖昧ですが、初回のプラグインロード時か初回のグラフ作成時にライセンス入力を求められるので、以前の手順で入手していたライセンスコードをコピーペーストして認証を完了させます。


 

■グラフを作ってみる

Fabricメニュ- → Create Graph

今バージョンから、メインで使用するノードタイプがcanvasNodeに変更されたようです。

Open Canvas ボタンを押します。

その他のノードベースツールで定番の操作、TABキーを押し、ノード名の一部を入力することでインテリセンスを使用して任意のノードを簡単に作成できます。

また、左右端のスライドバーをドラッグすると、ノード一覧が表示されます。
サブカテゴリを開き、キャンバス内にドラッグしてノードを追加できます。

ポートの追加は、左右の入出力ポードリストを右クリックし、Create Portを選択して行えます。

過去に作成したツールのご紹介

リンク

テクニカルアーティストを自称しているのに、あまりにも普段からツールネタが少ないのはどうかと思っていたところ、許可がいただけたので、自分がこれまで作ってきたツールの一部を載せてみたいと思います。

ご興味のある方はどうぞご覧ください。

 

Fabric Engine 1.15.3 プラグインをMayaへ導入する

Fabric Engine 1.15.3 を Maya 2014 64bit(EN) に導入するまでの手順を解説します。


インストール


 ・Fabric Engine公式サイトでファイルをダウンロード

 まずは、Fabric Engineのダウンロードです。
Fabric Engine公式サイトへ行きダウンロードします。

【Fabric Engine】http://fabricengine.com/

 今回は個人的に評価/研究のために使用するので、Evaluation ライセンスでダウンロードしました。


REQUEST LICENSEを押します。
すると、ユーザー情報の登録フォームページが開きます。


・ユーザー情報登録フォームに必要事項を入力

各項目を入力します。

First Name : 姓
Last Name : 名
E-Mail : メールアドレス
JOB Title : 役職
Company : 会社名
WebSite : 会社のWebサイトURL(個人であれば個人サイト)

Please tell us a little about what you
do and how you plan to use Fabric Engine.

Fabric Engineを使用する目的を簡単に記入します。

入力が終わったらSUBMITを押します。
すると、ダウンロードページが開きます。


・ダウンロードページ

使用するOSのブロックにあるDOWNLOADボタンを押し、ダウンロードを開始します。

※当方はWindows版を使用するので、Windows環境での解説をします。


・ファイルの解凍と配置

 ダウンロードした「FabricEngine-1.15.3-Windows-x86_64.zip」を解凍します。
解凍して出来たフォルダ「FabricEngine-1.15.3-Windows-x86_64」を任意の場所へ移動します。
今回は、分かりやすい C:\Program Files 直下に移動する事にしました。


・Maya.envを編集しモジュールパスを通す

Maya.envファイルはMaya2014の場合、以下のパスにあります。

C:\Users\USER_NAME\Documents\maya\2014-x64\Maya.env

上記.envファイルをテキストエディタで開き、以下の行を追加します

MAYA_MODULE_PATH=C:\Program Files\FabricEngine-1.15.3-Windows-x86_64\SpliceIntegrations\FabricSpliceMaya2014SP3;

※各パスは各々の環境に合わせて適宜書き換えてください。
※当方の環境はMaya2014ですので、FabricSpliceMaya2014SP3にします
※良いエディタがない場合、ワードパッドは使わずメモ帳を使用すると安心です。


・MayaにFabric Engine(FabricSpliceMaya.mll)をロードする

 Mayaを起動し、Plugin Managerを開きます。
前の手順でMaya.envに追記された「MAYA_MODULE_PATH」で示されるパスの中にFabricSpliceMaya.mll があることを確認し、Loadedにチェックを入れます。問題なくチェックできればロード完了です。
必要に応じてAuto Loadをチェックしておくと、次回以降のMaya起動時に自動ロードされるようになり便利です。

 緑色の i ボタンを押し、プラグインにより追加されたノードやコマンドを確認してみます。

 以上でMayaへのFabric Engineの導入は完了です。


・ノードを作ってみる

 試しに、Mayaで以下のPythonコードを実行してみます。

import maya.cmds as cmds
cmds.createNode("spliceMayaNode")

 Maya起動後の初回ノード作成時は、Fabric Engineのシステムをロードするためか、実際にノードが作成されるまで結構時間がかかります。

 先ほど作成したspliceMayaNodeノードをアトリビュートエディターで表示してみました。

 実際にコードを編集する際は、Open Splice Editor ボタンで開くSplice Editorを使用することになります。

 具体的な使い方やTIPSは追々勉強しながら書いていこうと思います。

書籍「マイクロインタラクション ―UI/UXデザインの神が宿る細部」

良いツールの要素とはなんだろうと考えた時、第一に安定性や機能の豊富さ等が挙げられると思いますが、それと同程度以上に必要な要素とは、ユーザにとっての使い易さやわかり易さなのではないかと思います。

ツールに含まれる動作一つをマイクロインタラクションと呼びます。
マイクロインタラクションは、トリガールールフィードバックループとモードの4ステップに分割して考えることができます。

トリガー : 動作が起こるきっかけ
ルール : 動作内容
フィードバック : ルールにより引き起こされる反応
ループとモード : ルールの長期的な動作

本書では、ツール設計の際、いかに良いマイクロインタラクションを設計し、ユーザーフレンドリーなツールをつくり上げるかに焦点を当てながら、既存のWebサイトやソフトウェアからGUIを抜粋し具体例として挙げ、それらの良い点と悪い点を考察します。

ツールを作るとき、理解しやすく使いやすいツールにしたいと考えるけれど、その方法に明確な指標を持てておらず、結果的にユーザの操作に対して反応がぎこちなく、分かりにくいツールになってしまう悩みを持っているような方には特にお勧めしたい一冊です。