On this page
この機能はまだ開発中です。現在の機能は未完成で変更される予定であり、 ドキュメントが手薄だったり、なかったりします。 これを使用するときは、そのことに我慢してください。

関数とは、特定の演算を実行するコードブロックです。 APEX Scriptでは、ユーザは独自の関数を作成したり、ビルトインのAPEX関数を使用することができます。

ユーザ定義関数

APEX Scriptのユーザ定義関数は、APEXネットワークビュー内でサブネットノードとして表現されます。 サブネットノードの内容に関数ロジックが含まれます。

APEX Scriptコード:

# 関数定義
def test(a: Int, b: Int):
    c: Int = a + b
    return c

# 関数コール
x = test(1, 2)
関数のサブネットノード(左)、サブネットの内容(右)

Pythonと同様、ユーザはAPEX Scriptで独自の関数を定義することができます:

  • 関数は、defキーワードの後に関数名を使用することで定義します。

  • 引数と呼ばれる情報は、括弧()内のカンマで区切った値のリストを介して関数に渡すことができます。これらの引数は、関数名の後で指定します。

  • 関数がコールされた時に実行されるコードブロックは、インデントする必要があります。

  • 情報は関数によって返すことができ、それらの戻り値はreturnステートメントを使用して指定します。関数は、カンマで区切った複数の値を返すことができます。

  • 関数は、コールされる前に定義しておく必要があります。

引数のない関数:

# 関数定義
def test():
    a = 1.5
    return a

# 関数コール; 結果はx = 1.5
x = test()

関数コールの引数の数は、関数定義の引数の数と一致していなければなりません。関数の引数に注釈を付けることをお勧めします:

# 関数定義
def test(a: Int, b: Int):
    c: Int = a + b
    return c

# 関数コール
x = test(1, 2)

キーワード引数 を使用して関数をコールすることができます。 キーワード引数は、<key>=<value>構文の引数で、<key>は引数の名前です。 キーワード引数を使用して関数をコールする場合には、引数の順序は問いません。

Note

キーワード引数と位置指定引数(非キーワード引数)の両方を使用して関数をコールする場合、キーワード引数は位置指定引数の後に配置する必要があります。

# 関数定義
def test(a: Int, b: Int):
    c: Int = a + b
    return c

# キーワード引数を使用した関数コール
x = test(b=1, a=2)

__dot__を含んだキーワード引数は.に置換されます。 これにより、APEX ScriptはBase.skelBase.shpなどのジオメトリ名を持つリグを処理することができます:

# 関数はBase.skelを受け取り、Base.shpを返します
def test(Base__dot__skel: Geometry):
    Base__dot__shp = Geometry()
    return Base__dot__shp

# グラフ入力はBase.skelです
geo = BindInput(Base__dot__skel=Geometry())
x: Geometry = test(geo)
サブネットの入力および出力でのリグのジオメトリ名

関数は複数の値を返すことができ、引数リストの後の->に続いて戻り値の型を指定することができます:

def test(a: Vector3, b: Float) -> tuple(Vector3, Float):
    a = a * b
    b = b + 1.0
    return a, b

x, y = test(a=(3.0, 3.0, 3.0), b=5.5)

特定の戻り値を無視したい場合は、それらの値をアンダースコア(_)に割り当てます:

def test(a: Int):
    a1: Int = a + 1
    a2: Int = a + 2
    a3: Int = a + 3
    a4: Int = a + 4
    return a1, a2, a3, a4

_, _, x, _ = test(1)

関数の引数にデフォルト値を指定することができます。関数が引数なしでコールされた場合、デフォルト値が使用されます:

def foo(a: String = 'test', b: Int = Int(Float(1.5))):
    return a, b

# 結果: x = 'test', y = 1
x, y = foo()

関数コール内のエクスプレッション:

def foo(a: String, b: Int):
    return a, b

m = 3
n = 4
p = 'test'

# 結果: x = 'test3', y = 7
x, y = foo(p+String(m), m+n)

関数は、他の関数内にネスト化することができます:

def foo(a: Int):
    return a+2

# この関数内でfooをコールします
def bar(a: Int) -> Int:
    return foo(a)+4

# 結果: x1 = 3, x2 = 7
x1 = foo(1)
x2 = bar(1)

関数は、数珠繋ぎにすることができます:

geo.computeTransform().transform(xform=Matrix4())

関数をサブグラフとして保存する

APEX Script SOPを使用することで、関数をサブグラフとしてディスクに保存することができます。 こうして保存されたサブグラフは、APEX Scriptコード内でグローバル関数として使用することができます。 他のユーザが作成したものも含め、所有しているサブグラフはすべてAPEX Script内で関数として使用することができます。

関数をサブグラフとして保存するには:

  1. APEX Script SOPで、 Subgraphs パラメータをオンにします。

  2. Subgraphs セクションの Subgraph スニペットパラメータ内で関数を定義し、その関数定義の上に@subgraphデコレータを記述します。

    例:

    @subgraph
    def test(a: Int, b: Int):
        c: Int = a + b
        return c
    
  3. サブグラフは@/apexgraph内に.bgeoファイルとして保存する必要があります。 @HOUDINI_PATHのディレクトリを展開します。 @ディレクトリを指定しなかった場合、サブグラフは$HOME/apexgraphに保存されます。 サブグラフの場所とファイル名は、 Geometry File パラメータまたはサブグラフデコレータの引数の@subgraph(<file_path>)で設定することができます。

  4. Save Subgraphs ボタンをクリックします。

同じAPEX Script SOP内または新規のAPEX Script SOP内でAPEX Scriptコードを記述する際に、その保存したサブグラフを関数として使用できるようになりました。

デコレータ

デコレータとは、他の関数に対して追加処理を実施するものであり、@記号を付けて指定します。例:

@subgraph
@namespace('my_namespace')
@safeguard_inputs(True)
def test(a: Int, b: Int):
    c: Int = a + b
    return c

デコレータ

説明

subgraph

@subgraphデコレータを使用すると、それに続く関数をサブグラフとして保存することができます。<file_path>引数はオプションです:

@subgraph([<file_path>])

例:

@subgraph('$HIP/subgraph_test.bgeo')

namespace

@namespaceデコレータを使用すると、関数にネームスペースを指定することができます。関数をサブグラフとして保存した後、<namespace>.<function>を使用して関数をコールすることができます。@namespaceデコレータの構文は以下の通りです:

@namespace('<namespace>')

例えば、APEX Script SOPの Subgraphs セクションで次のサブグラフを定義することができます:

@subgraph
@namespace('my_namespace')
def test(a: Int, b: Int):
    c: Int = a + b
    return c

関数をサブグラフとして保存した後、以下のステートメントを使用してサブグラフ関数をコールすることができます:

x = my_namespace.test(1,2)

safeguard_inputs

@safeguard_inputsデコレータは、必要に応じて関数の入力引数のコピーを作成することで、グラフのソートの問題を防ぐことができます。 @safeguard_inputsデコレータの構文は以下の通りです:

@safeguard_inputs(True|False)

@safeguard_inputsをTrueに設定すると、以下の条件の時に、APEX Scriptは関数の入力引数のコピーを作成します(グラフにValueノードを追加します):

  • 入力引数がインプレースポートに渡されている場合。

    且つ

  • その入力の名前が関数のどの出力名にも一致していない場合。

@safeguard_inputsをFalseに設定すると、APEX Scriptは関数の引数のコピーを作成しません。 これによってグラフのソートの問題が生じる可能性があるため、@safeguard_inputsをFalseに設定する時は注意して使用してください。

@safeguard_inputsが関数の前にインクルードされていない場合、デフォルトでTrueに設定されます。

ビルトイン関数

APEX Script内では、ビルトイン関数のライブラリを利用することができます。 これらの関数は、APEXネームスペース、または、オブジェクト指向の構文(この構文では、関数は入力の1つに作用します)のどちらかを使用してコールすることができます。 使用可能なAPEX関数のリストは、APEXノードの索引、APEXネットワークビューでのTabメニュー、またはAPEX Script SOPAPEX Autorig Component SOPAPEX Rigscript Component SOPSnippet パラメータ内で文字入力した時の自動補完機能で確認することができます。

関数をコールするための様々な構文:

geo = BindInput(Geometry())

# APEXおよびSOPネームスペースを使用して関数をコールします 
a = apex.sop.copytopoints(geo)

# 関数をコールするためのオブジェクト指向構文
b = geo.copytopoints()

以下の例では、APEXネームスペースを使用してappend()関数をコールしています。 append()の1番目の入力はインプレースポートなので、入力配列xは更新されます:

x = ['aaa']

x = apex.array.append_String(x, 'bbb')

# 結果: x = ['aaa', 'bbb']

append()の結果を別の変数に割り当てた場合、元の配列は更新されません:

x = ['aaa']

y = apex.array.append_String(x, 'bbb')

# 結果: x = ['aaa'], y = ['aaa', 'bbb']

オブジェクト指向の構文を使用する場合、関数の1番目の入力がその関数が作用する“オブジェクト”になっているので、その関数の1番目の入力を関数コールの前に記述します。 このオブジェクトは、1番目の入力として関数に渡されます。 この1番目の入力がインプレースポートだった場合、その入力はインプレース(直接データを書き換え)で更新されます:

x = ['aaa']

x.append_String('bbb')

# 結果: x = ['aaa', 'bbb']

オブジェクト指向のappend()を変数に割り当てた場合、その変数にはappend()の2番目の戻り値が設定されます:

x = ['aaa']

idx = x.append_String('bbb')

# 結果: x = ['aaa', 'bbb'], idx = 1

関数の1番目の入力がインプレースポートでなかった場合、その入力は更新されません:

x = ['aaa','bbb','ccc']

y = x.get_String(1)

# 結果: x = ['aaa', 'bbb', 'ccc'], y = 'bbb'

引数ありで関数をコールする:

box = apex.sop.box(t=Vector3(1,1,1), scale=Float(2))

Sine()などのネームスペースなしの関数では、APEXネームスペースを前に付ける必要はありません:

# APEXネームスペースあり
a = apex.sine(1.0)

# APEXネームスペースなし
b = sine(1.0)

デフォルトでは、APEX Scriptは最新バージョンのAPEX関数ではなく、コールされた通りのバージョンのAPEX関数を使用します。 例えば、IntBitMaskToBool()は最初のバージョンの関数をコールします。 2番目のバージョンのIntBitMaskToBoolを使用したい場合は、IntBitMaskToBool.v2_0()をコールします。 APEX関数の特定のバージョンを使用する方法については、特殊関数のHoudiniVersion()を参照してください。

テンプレート関数

テンプレート関数は、様々な型のデータを処理する関数です。 例えば、geo::SetPrimAttribValue<T>はテンプレート関数で、<T>は様々な型のいずれかになります。 テンプレート関数は、次の2つの方法で指定することができます:

型を含む特定の関数名を使用する:

geo.setPrimAttribValue_String(prim, 'name', 'test')

valuetype引数を使用して型(valuetype=<type>)を指定する:

geo.setPrimAttribValue(prim, 'name', 'test', valuetype=String)

特殊関数

APEX Scriptには、グラフノードに情報を追加したり、特定のバージョンのAPEXノードを使用したり、グラフ内からグラフを呼び出す機能など、様々なグラフオペレーションを実行できる特殊関数が用意されています。

関数

説明

BeginMetadata, EndMetadata

ループおよびifブロックのブロック開始ノードと終了ノード(例えばForBeginIfEnd)にメタデータを設定します。利用可能なメタデータは、特殊関数の引数に載っています。

y = 0

for x in range(3):
    # ForBeginノードにメタデータを設定します
    BeginMetadata(__name='test_begin', __color=(1,0,1), __tags=['tag1', 'tag2'])

    y = y + 1

    # ForEndノードにメタデータを設定します
    EndMetadata(__name='test_end', __pos=(1,1,1), __tags=['out_tag'])

BindInput, BindOutput

グラフの入力と出力を定義します。 詳細は、BindInput()およびBindOutput()の使用方法についてはこの例を、BindInput()の使用例についてはグラフ入力を指定する方法を参照してください。

HoudiniVersion

使用するAPEX関数のバージョンを設定します。 例えば、HoudiniVersion('20.5.250')は、APEX ScriptにHoudiniバージョン20.5.250のAPEX関数セットを使用するよう指示します。

ヘッダテンプレート内にHoudiniバージョンを指定することもできます:

  1. Header パラメータをオンにします。

  2. Header セクションで、 Add Version をオンにします。

  3. Edit をオンにしてHoudiniバージョンを変更します。

HoudiniVersion()関数では、newestignoreの2つの引数も利用可能です。

  • HoudiniVersion('newest') - 最新バージョンのAPEX関数を使用します。

  • HoudiniVersion('ignore') - コールされた通りのバージョンのAPEX関数を使用します。例えば、IntBitMaskToBool()は最初のバージョンの関数をコールします。2番目のバージョンのIntBitMaskToBoolを使用したい場合は、IntBitMaskToBool.v2_0()をコールします。

HoudiniVersion()に引数がない場合、これはHoudiniVersion('ignore')をコールするのと同じです。

InputMetadata, OutputMetadata

サブネット内のグラフ入力(パラメータ)および出力ノードにメタデータを設定します。 利用可能なメタデータは、特殊関数の引数に載っています。

def test(a,b):
    # サブネット入力ノードにメタデータを設定します
    InputMetadata(__name='abc', __pos=(1,0,0), __properties={'test': 1.5})

    c: Float = a + b

    # サブネット出力ノードにメタデータを設定します
    OutputMetadata(__color=(1,0,1), __tags=['out_tag1','out_tag2'])

    return c

x = test(1,2)

InvokeOutputs

別のグラフを呼び出すを参照してください。

SetGraph

APEXノードIDをグラフに関連付けるを参照してください。

StickyNote

グラフ内にステッキーノートを作成します。 特殊関数の引数に加え、以下に載せている引数を使用して、ステッキーノートに特定のプロパティを設定することができます:

引数

contents

String

dimensions

Vector2

is_background_hidden

Bool

is_collapsed

Bool

text_color

Vector3

text_size

Float

例:

StickyNote(contents='hello', dimensions=(3.0, 3.0), is_background_hidden=False, is_collapsed=False, text_color=(1.0, 0.0, 0.0), text_size=0.0, __color=(0,0,1))

_portalias

コピーを作成することなく、変数の別の識別子を作成します。これによって、グラフのソートの問題が発生する可能性があるため、注意して使用してください。

変数のエイリアスを作成します:

x = 1.5
y = _portalias(x)

# y = 1.5
BindOutput(y)

関数に割り当てられた変数のエイリアスを作成します:

def test():
    x = 1
    return x

m = test()
m_alias = _portalias(m)

# m_alias = 1, n = 3
n = m_alias + 2

関数でエイリアスを使用します:

def test(a: Int, b: Int):
    c: Int = a + b
    d = _portalias(c)
    d += 2
    return d

# m = 5
m = test(1, 2)

特殊関数の引数

以下に示す特殊関数の引数を使用すると、ノードに関する特定の情報を指定することができます:

引数

説明

__name

String

ノードの名前。

__color

Vector3

ノードのカラー。

__pos

Vector3

グラフ内のノードの位置。

__properties

Dict

ノードに格納されているプロパティ

__tags

StringArray

ノードに格納されているタグ

例:

def abc(a: Vector3, b: Float):
    a = a * b
    b = b + 1.0
    return a, b

# サブネットノード名は関数の名前に設定されます
subnet_a = abc(a=(3.0, 3.0, 3.0), b=5.5)

# サブネットノードの名前とカラーを設定するために使用される特殊関数の引数
subnet_b = abc(a=(3.0, 3.0, 3.0), b=5.5, __name='test_b', __color=(1,0,0))
特殊関数の引数を使用してサブネットノードを作成

関数のマッピング

APEX Scriptでは、ビルトインのAPEX関数の一部に便利な名前のマッピングが用意されています。

グラフ関数

このセクションの例では、次の定義を使用します:

graph = ApexGraphHandle()

# グラフに2つのTransformObjectノードを追加します
a = graph.addNode('test_a', 'TransformObject')
b = graph.addNode('test_b', 'TransformObject')

# 辞書
d = {'xord': 3}

関数

マップ先

addNode(<name>, <callback>, [<color>])

graph::AddNode

graph.addNode('abc', 'Add<Float>', (1,0,1))

addWire(<srcport>, <dstport>)

graph::ConnectInput

graph.addWire(a.xform_out, b.parent)

addWire(<srcnode>, <srcname>, <dstnode>, <dstname>)

graph::FindAndConnectInput

graph.addWire(a, 'xform', b, 'parent')

connectedPorts(<portid>)

graph::GetConnectedPorts

x = graph.connectedPorts(b.parent)

connectNodes(<srcport>, <dstport>)

graph::ConnectInput

graph.connectNodes(a.xform_out, b.parent)

connectNodes(<srcnode>, <srcname>, <dstnode>, <dstname>)

graph::FindAndConnectInput

graph.connectNodes(a, 'xform', b, 'parent')

debug(<doinvoke>)

graph::Compile

<doinvoke>が指定されていない場合、デフォルトはTrueです。

graph.debug()

findOrAddPort(<nodeid>, <portname>)

graph::FindOrAddPort

x = graph.findOrAddPort(a, 'xform[in]')

freeze

Value<ApexGraphHandle>

Valueノードにグラフを“保存”します。

g1 = graph.freeze()

getInputPorts(<node>)

graph::NodeInputs

x = graph.getInputPorts(a)

getOutputPorts(<node>)

graph::NodeOutputs

x = graph.getOutputPorts(a)

getPort(<nodeid>, <portname>)

graph::FindPort

x = graph.getPort(a, 'xform[in]')

matchNodes(<pattern>)

graph::FindNodes

x = graph.matchNodes('test*')

matchPorts(<pattern>)

graph::FindPorts

x = graph.matchPorts('test*:xform[in]')

ports(<pattern>)

graph::FindPorts

x = graph.ports('*xform*')

promoteInput(<portid>, [<subportname>], [<parmnodeid>], <parmname>)

graph::PromoteInput

graph.promoteInput(a.parent, 'p')

promoteOutput(<portid>, [<outputnodeid>], <outputname>)

graph::PromoteOutput

graph.promoteOutput(b.xform_out, 'x')

setNodeParms(<nodeid>, <parms>)

graph::UpdateNodeParms

graph.setNodeParms(a, d)

ノード関数

このセクションの例では、次の定義を使用します:

graph = ApexGraphHandle()

# TransformObjectノード'test_a'
a = graph.addNode('test_a', 'TransformObject')

# 辞書
d = {'abc': 3} 

関数

マップ先

ancestors()

graphutils::NodeAncestors

# ノードをサブネットにパックします
node_arr: ApexNodeIDArray = [a]
graph.packSubnet('my_test', node_arr)

# 'my_test'を返します
anc = a.ancestors()

callbackName()

graphutils::NodeCallbackName

x = a.callbackName()

data()

graph::NodeData

(name, callback, pos, color, parms, tags, properties, output) = a.data()

inPorts()

graph::NodeInputs

x = a.inPorts()

name()

graph::NodeName

x = a.name()

outPorts()

graph::NodeOutputs

x = a.outPorts()

parent()

graphutils::NodeParent

x = a.parent()

parms()

graphutils::NodeParms

x = a.parms()

path()

graphutils::NodePath

x = a.path()

port(<portname>)

graph::FindPort

x = a.port('xform[in]')

properties()

graphutils::NodeProperties

x = a.properties()

setColor(<color>)

graph::UpdateNode

a.setColor((1,0,0))

setName(<name>)

graph::UpdateNode

a.setName('aaa')

setParms(<parms>)

graph::UpdateNodeParms

a.setParms(d)

setPos(<pos>)

graph::UpdateNode

a.setPos((1,2,3))

setProperties(<properties>)

graph::UpdateNodeProperties

a.setProperties(d)

setTags(<tags>)

graph::UpdateNodeTags

a.setTags(['tag1','tag2'])

tags()

graphutils::NodeTags

x = a.tags()

ポート関数

このセクションの例では、次の定義を使用します:

graph = ApexGraphHandle()

# グラフに2つのTransformObjectノードを追加します
a = graph.addNode('test_a', 'TransformObject')
b = graph.addNode('test_b', 'TransformObject')

# ノード'test_a'のポート
a_in = a.t_in
a_out = a.xform_out

# ノード'test_b'のポート
b_in = b.parent_in
b_out = b.t_out

関数

マップ先

connect(<dstport>)

graph::ConnectInput

グラフの同じネストレベルのポートを接続します。

a_out.connect(b_in)

connected()

graph::GetConnectedPorts

x = a_out.connected()

connectPath(<dstport>, [<name>])

graph::AddWirePath

サブネットにあるノードに接続するなど、グラフの異なるネストレベルのポートを接続します。サブネットノードに作成される接続ポートの名前は、<name>に設定されます。

# グラフにノードを追加します
c = graph.addNode('test_x', 'TransformObject')

# ノードをサブネットにパックします
node_arr: ApexNodeIDArray = [c]
graph.packSubnet('test_subnet', node_arr)

# サブネットのノードに接続します
a_out.connectPath(c.xform_in, 'abc')

data()

graph::PortData

(name, alias, datatype, porttype, isconnected, ispromoted) = a_out.data()

disconnect()

graph::ConnectInput

b_in.disconnect()

node()

graph::PortNode

x = a_out.node()

promote(<name>)

graph::PromotePort

a_out.promote('aa')

promoted()

graph::GetPromotedPort

x = a_out.promoted()

promoteInput(<name>)

graph::PromoteInput

a_in.promoteInput('tt')

promoteOutput(<name>)

graph::PromoteOutput

b_out.promoteOutput('tt')

subport(<portname>)

graph::GetSubPort

subport()を使用してノードにサブポートを作成する場合、新規のサブポートに何かを接続するまで、グラフに表示されません。

# グラフにノードを追加します
c = graph.addNode('test_c', 'Add<Float>')
d = graph.addNode('test_d', 'Add<Float>')

# サブポート'dd'を作成します
d_in = d.b_in.subport('dd')

# サブポート'dd'に接続します
c.result.connect(d_in)

データ関数

このセクションの例では、次の定義を使用します:

# 辞書
d = {'abc': 3}

# ベクトル
v1 = (1,2,3)
v2 = (2,4,5)

# マトリックス
m = Matrix4(2)

関数

マップ先

computeTransform(<mode>)

sop::kinefx::computetransform

geo: Geometry = BindInput()
geo.addJoint('joint_1', Matrix4(2))
geo.addJoint('joint_2', Matrix4(3))

x = geo.computetransform(mode=2)

cross(<b>)

CrossProduct

v = v1.cross(v2)

distance(<b>)

Distance

v = v1.distance(v2)

distanceTo(<b>)

Distance

v = v1.distanceTo(v2)

dot(<b>)

DotProduct

v = v1.dot(v2)

freeze()

Value<Dict>, Value<Geometry>

Valueノードに辞書またはジオメトリを“保存”します。

d2 = d.freeze()

invert()

Invert<Matrix4>

この関数はインプレースで実行されます - 入力マトリックスが更新されるため、出力変数に割り当てる必要はありません。

m.invert()

matrixToRotateTo(<b>)

transform::Dihedral<Matrix4>

v = v1.matrixToRotateTo(v2)

normalize()

Normalize

v = v1.normalize()

normalized()

Normalize

この関数はインプレースで実行されます - 入力ベクトルが更新されます。

v1.normalized()

normalized()が変数に割り当てられる場合、変数はベクトルの長さに設定されます:

v = v1.normalized()

smoothRotation(<b>, <rord>)

transform::SmoothRotation

v = v1.smoothRotation(v2, 1)

コンテンツライブラリのサンプル

コンテンツライブラリのこのサンプルでは、象の鼻にサイン波のコントロールをセットアップする方法が説明されています:

KineFX

概要

キャラクタ要素の準備

APEXグラフを使用したリギング

APEXスクリプトを使用したリググラフの構築

リグコンポーネントを使用したリギング

ビューポート内でアニメーションを付ける

SOPベースのアニメーション

変形

アニメーションのリターゲット

H20以前

ペイン

別表