RTVal のPythonアプリケーションからの使用

この節では RTVal を Pythonで書かれた Fabric Engine クライアントアプリからどのように扱うかについて説明します。

Fabric Engine クライアントハンドルを得る

ここでは Fabric Engine クライアントハンドルを得たものします。クライアントハンドルの取得については The fabricClient Object を参照してください。

以後この節ではクライアントハンドルを Python変数名『 client 』で参照することとします。

$ python
>>> import FabricEngine.Core
>>> client = FabricEngine.Core.createClient()
[FABRIC:MT] Fabric Engine version 2.4.0 (development)
[FABRIC:MT] Crash handler registered.
[FABRIC:MT] Loading DFG presets from C:\Users\helge\Fabric\Presets
[FABRIC:MT] Registered extensions {Alembic:1.1.0,AlembicImporter:1.0.0,AlembicWrapper:1.7.0,Animation:1.1.1,AttributeHelpers:1.0.1,BinPacking:1.0.0,Bullet:1.0.3,BulletHelpers:1.0.0,Characters:1.2.0,Containers:1.2.0,DFGWrapper:1.2.0,FabricInterfaces:1.1.0,FabricSynchronization:1.2.0,Fbx:1.2.1,FbxHelpers:1.1.0,FbxImporter:1.0.0,FileIO:1.3.1,FreeTypeGL:2.3.0,GenericImporter:1.0.0,Geometry:1.6.0,Images:2.1.0,InlineDrawing:1.6.0,JSON:1.1.0,LA:1.0.0,Manipulation:1.2.1,Math:1.5.0,FabricOBJ:1.1.0,ObjImporter:1.0.0,OGLWrappers:1.2.0,FabricOGL:1.1.0,OpenImageIO:1.1.0,OSOGL:1.1.0,Parameters:1.1.0,Singletons:1.1.2,FabricStatistics:1.1.1,Text:2.4.0,Util:1.4.1,ImageProcessing,RTR,RTRAdaptors,Multipede,Particles} in directory: ${FABRIC_DIR}\Exts
>>>

派生型の使用

KLの基本型 ―例えば StringUInt32 であればRTValで常につかえます。これらはビルトインであり常に使用可能です。

RTVal で  派生型 を使用するのであれば、まずその型を登録(register)します。

多くのカスタム型は built-in extensions により提供されています。これらの型はエクステンションをロードすることによりRTValで使用可能になりあmす。エクステンションは KLコードから読み込まれる(例えば DG operators として)か、明示的にエクステンションを読み込むこともできます。明示的な読み込みには client.loadExtension(<extension name>) メソッドを使用します。

カスタム型を登録することもできます。 Registered Types を参照してください。

以下の用例では、カスタム構造体 <MyType> を登録しカスタムコンストラクタといくつかのメソッドと共に使用します。

>>> myTypeSource = """
... function MyType(String string, UInt32 uint32) {
...   this.string = string;
...   this.uint32 = uint32;
... }
...
... function MyType.tweet() {
...   report("Tweet tweet!  string='" + this.string + "' uint32=" + this.uint32);
... }
...
... function MyType.append!(MyType that) {
...   this.string += that.string;
...   this.uint32 += that.uint32;
... }
... """
>>>
>>> myTypeDesc = {
...   'members': [{'string': 'String'}, {'uint32': 'UInt32'}],
...   'klBindings': {
...     'filename': "(inline)",
...     'sourceCode': myTypeSource
...    }
... }
>>>
>>> client.RT.registerType('MyType', myTypeDesc)
[FABRIC:MT] Compiled extension MyType in 0.961ms
[FABRIC:ID] Optimized extension MyType in 29.553ms
>>> print client.RT.getRegisteredTypes()['MyType']
{'uuid': '8E8A7F4C', 'name': 'MyType', 'members': [{'type': 'String', 'name': 'string'}, {'type': 'UInt32', 'name': 'uint32'}], 'size': 32}
>>>

KLでの基本型、構造体 の RTVal の作成

型を作成するには client.RT.types.<TypeName>(<constructor parameters>) メソッドを呼びます:

>>> myRTVal = client.RT.types.MyType("Hello", 42)
>>> print myRTVal
<RTVal:{string:"Hello",uint32:42}>
>>>

RTVal のコンストラクタへパラメータが渡されているのがわかりますね。RTVal が自動で値を表す文字列へと変換しているところもわかります。デバッグの際にとても有用です。

存在しないコンストラクタを呼び出してしまうと例外が発生してしまいます。

>>> client.RT.types.MyType("foo")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: KL compile failed: constructArgs__ST.kl:2:106: error: no resolution for constructor MyType(io _CN<ST>)
candidates are:
  function MyType(MyType)
  function MyType()
  function MyType(String, UInt32)
>>>

KLでのオブジェクト の RTVal の作成

警告

KL中のオブジェクト同様、新規に作成したあるオブジェクト型な RTVal は、常にnullになります。非nullなオブジェクトRTValの作成には、 client.RT.types.<MyObjType>.create(<create args>) を呼びます。ただし、このKLオブジェクトが『複数のパラメタ付』のコンストラクタを実装している場合については、 client.RT.types.<MyObjType>(<create args>) を呼ぶことにより非nullの オブジェクトRTValを直接コンストラクトすることができます。

Pythonでの構造体(もしくは基本型)とオブジェクトRTValのコンストラクト例を示します。KL オブジェクトと構造体宣言定義は MyExtエクステンションにあり:

object MyObj {
  Float32 value;
};

MyObj() {
  this.value = 0.0f;
}

MyObj(Float32 value) {
  this.value = value;
}

struct MyStruct {
  Float32 value;
};

MyStruct() {
  this.value = 0.0f;
}

MyStruct(Float32 value) {
  this.value = value;
}

Pythonでコンストラクト:

client.loadExtension('MyExt')

# 1. Objects
# 1.1 Direct call from RTVal
myObj = self.client.RT.types.MyObj()
str(myObj.type('String').getSimpleType()) >>> None
str(myObj) >>> Obj <RTVal:null>

myObj = self.client.RT.types.MyObj(1)
str(myObj.type('String').getSimpleType()) >>> MyObj
str(myObj) >>> <RTVal:{value:+1.0}>

myObj = self.client.RT.types.MyObj.create()
str(myObj.type('String').getSimpleType()) >>> MyObj
str(myObj) >>> <RTVal:{value:+0.0}>

myObj = self.client.RT.types.MyObj.create(1)
str(myObj.type('String').getSimpleType()) >>> MyObj
str(myObj) >>> <RTVal:{value:+1.0}>

# 1.2 Call from RTVal type
myObjType = getattr(self.client.RT.types, "MyObj")

myObj = myObjType()
str(myObj.type('String').getSimpleType()) >>> None
str(myObj) >>> <RTVal:null>

myObj = myObjType(1)
str(myObj.type('String').getSimpleType()) >>> MyObj
str(myObj) >>> <RTVal:{value:+1.0}>

myObj = myObjType.create()
str(myObj.type('String').getSimpleType()) >>> MyObj
str(myObj) >>> <RTVal:{value:+0.0}>

myObj = myObjType.create(1)
str(myObj.type('String').getSimpleType()) >>> MyObj
str(myObj) >>> <RTVal:{value:+1.0}>


# 2. Structures (same for basic types)
# 2.1 Direct call from RTVal
myStruct = self.client.RT.types.MyStruct()
str(myStruct.type('String').getSimpleType()) >>> MyStruct
str(myStruct) >>> <RTVal:{value:+0.0}>

myStruct = self.client.RT.types.MyStruct(1)
str(myStruct.type('String').getSimpleType()) >>> MyStruct
str(myStruct) >>> <RTVal:{value:+1.0}>

# 2.2 Call from RTVal type
myStructType = getattr(self.client.RT.types, "MyStruct")
myStruct = myStructType()
str(myStruct.type('String').getSimpleType()) >>> MyStruct
str(myStruct) >>> <RTVal:{value:+0.0}>

myStruct = myStructType(1)
str(myStruct.type('String').getSimpleType()) >>> MyStruct
str(myStruct) >>> <RTVal:{value:+1.0}>

RTVal配列の作成

型の配列を作成するには client.RT.types.<TypeName>.createArray(<OptionalListOfRTVals>) メソッドを呼びます:

>>> // Create an empty Vec3 Array
>>> myRTValArray = client.RT.types.Vec3.createArray()
>>> print myRTValArray
<RTVal:[]>
>>>
>>> // Create a Vec3 Array with two Vec3 items
>>> vec1 = client.RT.types.Vec3(0, 1, 0)
>>> vec2 = client.RT.types.Vec3(0, 2, 0)
>>> myRTValArray = client.RT.types.Vec3.createArray([vec1, vec2])
>>>
>>> print myRTValArray
<RTVal:[{x:+0.0,y:+1.0,z:+0.0},{x:+0.0,y:+2.0,z:+0.0}]>
>>>
>>> // Create a Scalar Array with two items from Python built in float types
>>> myRTValArray = client.RT.types.Scalar.createArray([1.5, 2.5])
>>>
>>> print myRTValArray
<RTVal:[+1.5,+2.5]>

2次RTVal配列

ある型の2次元配列を作成するには、 <Type>[] の名前を持つ属性を、登録された型(registered types)オブジェクトの中から検索し、戻ってきた型に対し .createArray() メソッドを実行する必要があります。

>>> matrixA = client.RT.types.Mat44()
>>> matrixB = client.RT.types.Mat44()
>>>
>>> matrixC = client.RT.types.Mat44()
>>> matrixD = client.RT.types.Mat44()
>>> matrixE = client.RT.types.Mat44()
>>>
>>> subArray1 = client.RT.types.Mat44.createArray([matrixA, matrixB])
>>> subArray2 = client.RT.types.Mat44.createArray([matrixC, matrixD, matrixE])
>>>
>>> registeredTypes = client.RT.types
>>> mat44ArrayType = getattr(registeredTypes, 'Mat44[]')
>>> matrix2DArray = mat44ArrayType.createArray([subArray1, subArray2])
>>>
>>> print "Matrix 2D Array Size: " + str(len(matrix2DArray))
>>> print "SubArray 1 Array Size: " + str(len(matrix2DArray[0]))
>>> print "SubArray 2 Array Size: " + str(len(matrix2DArray[1]))
Matrix 2D Array Size: 2
SubArray 1 Array Size: 2
SubArray 2 Array Size: 3

RTVal からPythonの値を取得

RTVal メソッド,メンバはディフォルトでPYthonでは ‘RTVal’ として表現されます。しかし単純な型 ―整数や文字列などは Pythonの組み込みの方として扱いたい事が多いでしょう。そこで getSimpleType() メソッドを使用します。RTVal が単純なPythonの型を返すか、ディフォルトの型に当てはまらない場合は ‘None’ を返します:

>>> print myRTVal
<RTVal:{string:"Hello",uint32:42}>
>>> print myRTVal.getSimpleType()
None
>>> print myRTVal.uint32
<RTVal:42>
>>> print myRTVal.uint32.getSimpleType()
42

RTVal のメソッド呼び出し

メソッドの呼び出しには、単純にメソッドを呼び出します。引数もわたします。

注釈

Python でのメソッド呼び出しは、<戻り値の型の名前>を文字列で最初の引数に渡さなくてはいけません。戻り値がないメソッドでは、「空の文字列」をわたします。このクセのある挙動は将来の Fabric Engine で修正する予定です。
>>> quat = client.RT.types.Quat()
>>> upVec = client.RT.types.Vec3(0, 1, 0)
>>> dirVec = client.RT.types.Vec3(1, 0, 0)
>>>
>>> // You have to pass an emtpy string when calling methods that don't return a type.
>>> quat.setFromDirectionAndUpvector('', dirVec, upVec)
>>>
>>> print quat
<RTVal:{v:{x:+0.0,y:+0.707106,z:+0.0},w:+0.707106}>
>>>
>>> myRTVal.tweet('')
[FABRIC:MT] Tweet tweet!  string='Hello' uint32=42
>>>

素のPython型 ―整数や文字列に加え、他の RTValを引数として渡すこともできます。

>>> myRTVal2 = client.RT.types.MyType(", there", 71)
>>> print(myRTVal2)
<RTVal:{string:", there",uint32:71}>
>>> myRTVal.append('', myRTVal2)
>>> myRTVal.tweet('')
[FABRIC:MT] Tweet tweet!  string='Hello, there' uint32=113
>>>

Fabric Core への Python インタフェースには、いくつかの特別なメソッド名が定義されています。たとえば、getJSON and getDesc です。もし呼び出したいメソッド名が、これらの特別なメソッド名と一致している場合、以上のように直接呼ぶ出すことが可能ですし、特別なメソッド callMethod に名前を与え、望みのメソッドを呼ぶ出すことができます。 callMethod は(もしあるのならば)戻り値の型名を一番目のパラメータにとり、二番目のパラメータにはメソッド名をとりメソッド呼び出しの引数とします。

>>> myRTVal.callMethod('', 'tweet')
[FABRIC:MT] Tweet tweet!  string='Hello, there' uint32=113
>>>

RTVal 参照のコピー

RTVal を指すPython変数は参照です。つまり、RTValをある変数に代入するということ参照を他にもう一個設けることと同義です。通底する値のコピーは『行いません』。

RTVal の破棄

Pythonでは、スコープ外へと外れたRTValを自動で破棄します。

Dependency Graph とのインタフェース

dependency graph とのインタフェースに RTValを使用することが可能です。 node.getValue(<member>, <slice>) メソッドにより RTValのメンバの値を取得できます:

>>> node = client.DG.createNode("node")
>>> node.addMember("myType", "MyType")
>>> print node.getValue("myType", 0)
<RTVal:{string:"",uint32:0}>
>>>

おなじように、 setValue(<member>, <slice>, <value>) メソッドによりRTValの値を設定できます:

>>> node.setValue("myType", 0, myRTVal)
>>> print node.getValue("myType", 0)
<RTVal:{string:"Hello, there",uint32:113}>
>>>

この2つのメソッドは イベント,イベントハンドラと共に使用することができます。

警告

getValue を dependency graph ノードに対して呼ぶ場合、戻りRTVal は『コピー』です。参照では『ありません』。したがって戻りRTVal を変更しても元のノードの値は変更されずにおわります。

KL RTVal の活用

KL RTVal 型の Python RTValを、アプリケーション内外の一般的なデータ受け渡しのために利用することができます。
Python RTVal は KL での RTVal と1対1対応するような同等なものではありません。
Pythonからアクセするする際は、他の KLの型と同じく、 KL :kl-ref:`RTVal`s は Python RTVal に内包されます。
KL RTVal をラップした値を Python RTVal へ置き換えるにはどうするかを示す例です:
# Access the KL :kl-ref:`RTVal` containing the KL data we want.
klRTVal = getRTVal(....);

# Now, get the type of the KL data wrapped in the KL :kl-ref:`RTVal`.
valType = str(klRTVal.type('String').getSimpleType())

# Then, construct a new Python RTVal containing the KL data.
rtValType = getattr(self.client.RT.types, valType)
pythonRTVal = rtValType(klRTVal)