C++ API のメタ データ クラスは、その他すべての Maya API クラスと同様に .NET API に移植されています。さらに、.NET のパワーを活用するため、いくつかの機能強化が行われています。
メタデータは、Maya API の非常に重要な部分であり、クライアントがカスタム データ(ストラクチャ、タイプ、要素ごとのデータにアクセスする方法など)を記述できるようになります。.NET 機能では、C++ の場合よりも処理を大幅に単純化し、強力にすることができます。
このページに記載した .NET 拡張機能を紹介する興味深い複数の例は、Maya に付属している例にも含まれています(「コンパイルの例」を参照)。Microsoft Visual Studio を使用して examples.sln ソリューション(Developer Kit をインストールした devkit¥dotnet¥examples フォルダ内、「ビルド環境を設定する: Windows 環境(64 ビット)」を参照)を開くと、これらのサンプルを MetaData フォルダ内で見つけることができます。いくつかの興味深い例を次に示します。
この例では、新しい StructureClass アセンブリ アトリビュートと .NET タイプ用の特殊なクラス StreamForType<T> (つまり、例の MyStructureClass)の使用方法を示します。
これにより、すべてが汎用クラス StreamForType<T> によって自動的に実行されるため、Structure クラスと Handle クラスが不要になります。
この例はまた、LINQ を使用して任意のコレクションで照会を実行する方法も示します(例では、ストリームの要素を照会します)。
この例では、Associations、Channel、Stream、および Structure クラスで .NET インタフェース IEnumerable がサポートされるおかげで、foreach 構文を使用した場合に列挙チャネル、ストリーム、ストラクチャ メンバーおよびストリームハンドルをどれだけシンプルにできるかを示します。
メタデータ API の主なクラスのリストとそれぞれの簡単な概要を、.NET に移植された C++ メソッドのリストとともに次に示します。
これは、MFnDependencyNode.metadata プロパティを取得することなどによって Maya オブジェクトからメタ データにアクセスするためのエントリ ポイントです。
これは、同じトポロジ(同じストリーム インデックス)を共有しているか、ユーザーが論理的に同じグループになるように決定したため(シミュレーション エフェクトのすべてのストリームが同じチャネルになり、エフェクトの名前であるチャネル名が付けられる場合など)にグループ化されたストリームのコレクションです。
チャネルに名前を付けることができますが、いくつかの名前は予約されています(たとえば、vertex はストリームが頂点ごとに 1 つの要素を持つことを示すために予約されています)。
このクラスは、ストリームの各要素で検出されたメタ データについて記述します。
このメソッド AddMember は、メタ データの各フィールドを定義するために使用されます。
たとえば、1 つの float と 1 つのブール値をすべての要素に格納する場合は、最初にストラクチャを作成し、AddMember(kFloat,1,”MyFloatField”) と AddMember(kBoolean,1,”MyBooleanField”) を呼び出します。
このクラスを使用すると、ストリームの指定された要素のデータにアクセスすることができます。
ハンドルを使用すると、ストリーム要素のデータを取得または設定することができます。
.NET では、メタデータ API にいくつかの拡張機能が提供されます。ここでは、.NET の拡張機能がある API の主なクラスを示します。
Channel オブジェクトのディクショナリとして機能するため、このクラスは .NET インタフェース IDictionary<Channel> をサポートします。
このインタフェースを使用すると、.NET のディクショナリから予測される次の標準のメソッド/プロパティを使用することができます。
Channel channel; if( myAssociations.TryGetValue( “vertex”, out channel ) { ... }
Channel channel = myAssociations[“vertex”];
このインデクサも名前が指定されたチャネルを検索しますが、この場合、チャネルは見つかる必要があります(見つからない場合、KeyNotFoundException が発生します)。
if(myAssociations.ContainsKey( “vertex” ) { ... }
myAssociations.Add( “vertex”, myChannel );
foreach( Channel chnl in myAssociations ) { ... }
これを使用すると、複雑な C++/STL 構文の代わりに非常に単純な構文を使用してすべてのチャネルを列挙することができます。
for( AssociationsIterator iter = myAssociations.begin(); iter.notEqual( myAssociations.end() ); iter.next() ) { Channel chnl = iter.__deref__(); ... }
Stream オブジェクトのディクショナリとして機能するため、このクラスは .NET インタフェース IDictionary<Stream> をサポートします。
このインタフェースを使用すると、.NET のディクショナリから予測される次の標準のメソッド/プロパティを使用することができます。
Stream stream; if( myChannel.TryGetValue( “myStream”, out stream ) { ... }
Stream stream = myChannel[“myStream”];
if( myChannel.ContainsKey( “myStream” ) { ... }
myChannel.Add( “myStream”, myStream );
foreach( Stream stream in myChannel ) { ... }
for( ChannelIterator iter = myChannel.begin(); iter.notEqual( myChannel.end() ); iter.next() ) { Stream stream = iter.__deref__(); ... }
Handle オブジェクトのディクショナリとして機能するため、このクラスは .NET インタフェース IDictionary<Handle> をサポートします。
これを使用すると、.NET のディクショナリから予測される次の標準のメソッド/プロパティを使用することができます。
Handle handle; if( myStream.TryGetValue( 18, out handle ) { ... }
Handle handle = myStream[18];
if( myStream.ContainsKey(18) { ... }
myStream.Add( 18, myHandle );
foreach( Handle handle in myStream ) { ... }
for( StreamIterator iter = myStream.begin(); iter.notEqual( myStream.end() ); iter.next() ) { Handle handle = iter.__deref__(); ... }
この構文を有効にするため、IEnumerable<Member> が追加されました。
foreach( Member member in myStructure ) { ... }
これを使用すると、複雑な C++/STL 構文の代わりに非常に単純な構文を使用してすべてのメンバーを列挙することができます。
for( StructureIterator iter = myStream.begin(); iter.notEqual( myStream.end() ); iter.next() ) { Member member = iter.__deref__(); ... }
C++ API はポインタを使用して単一のメソッドを使用する(例: int *asInt32())ため、特定のタイプのデータにアクセスすることができます。単一の整数に対するポインタとして使用する(たとえば、myInteger = * asInt32())ことも、整数値の配列(たとえば、asInt32()[6])に対するポインタとして使用することもできます(Structure オブジェクトに対応するメンバーを作成したときに 1 より大きいサイズを指定した場合)。
.NET の場合、タイプごとに 2 つのプロパティが提供されるため、API でポインタを使用することはできませんでした。
Structure クラスを使用して要素のデータを記述すると、データにアクセスする Handle クラスは次のようにかなり低レベルになり、C++ のようになります。
.NET では、タイプの各メンバーが予想される Structure オブジェクトのメンバーであるデータを記述するクラスまたはストラクチャがある限り、プロセス全体が単純化されます。
プラグインで新しい StructureClass アセンブリ アトリビュートを使用し、メタデータのストラクチャを記述するタイプを提供する場合、.NET では自動的に Structure オブジェクトを作成することができます。
[assembly: StructureClass(typeof(MyNameSpace.MyStructureClass))] namespace MyNameSpace { public class MyStructureClass { public int a; public int b { get; set; } public float[] c = new float[3]; } }
イントロスペクションのおかげで、.NET はクラス(またはストラクチャ)ですべてのパブリック フィールドおよびプロパティを検索し、Structure.registerStructure を使用して登録されている Structure オブジェクトを自動的に作成することができます。
ストラクチャの名前は、タイプのフル ネームです(たとえば、MyNameSpace.MyStructureClass)。各ストラクチャ メンバーの名前は、タイプの対応するメンバーの名前です。
.NET オブジェクトのタイプとともに新しい StructureClass アセンブリ アトリビュート(前のセクションを参照)を使用する場合、Handle クラスの次の新しいメソッドを使用することができます。
public void SetFromDotNetObject( Object dotNetObj, Structure structure );
これにより、メンバーは .NET オブジェクトから Handle オブジェクトに高レベルでコピーされます。
for each public member in the type of dotNetObj find corresponding member in Structure object call this.setPositionByMemberIndex( index of member found ) if type is int32 this.asInt = get value of member in dotNetObj else if type is float this.asFloat = get value of member in dotNetObj … next
public void CopyToDotNetObject(Object dotNetObj, Structure structure);
これにより、メンバーは Handle オブジェクトから .NET オブジェクトに高レベルでコピーされます。
for each public member in the type of dotNetObj find corresponding member in Structure object call this.setPositionByMemberIndex( index of member found ) if type is int32 dotNetObj.SetValue( this.asInt ) else if type is float dotNetObj.SetValue( this.asFloat ) … next
ただし、次のセクションで説明する新しい汎用クラス StreamForType<T> を使用する場合、これらのメソッドは不要になります。
Stream は Structure を記述する .NET タイプを提供できない場合に使用するクラスですが、新しい StructureClassアセンブリ アトリビュートを使用してタイプを提供した場合には クラス StreamForType<T> を使用する必要があります。
この汎用クラスの T パラメータに同じタイプを使用すると、タイプに完全にカスタマイズされた Stream のようなクラスが提供されます。
この汎用クラスは Stream オブジェクトのラッパーであり、2 つの方法で作成することができます。2 つのコンストラクタを次に示します。
このコンストラクタは、タイプ T のフル ネームで呼び出された既存の Structure を検索します(StructureClass アセンブリ アトリビュートを使用した場合に検索する必要があります)。コンストラクタは、指定された名前と検出されたストラクチャを使用して新しい Stream オブジェクトを作成し、この新しい Stream オブジェクトをラップします。
このコンストラクタは、指定されたストリームをラップするだけでなく、このベース ストリームのストラクチャがタイプと互換性のあることが保証されます(StructureClass アセンブリ アトリビュートを使用した場合にその必要があります)。
Stream クラスは Handle オブジェクトのディクショナリとみなすことができますが、StreamForType<T> 汎用クラスは T が .NET クラス(またはストラクチャ)である T オブジェクトのディクショナリとみなすことができます。これは、汎用クラスによってサポートされた IDictionary<T> を使用して実行されます。
指定されたインデックスを持つ Handle オブジェクトを検索し、T の新しいインスタンスで handle.CopyToDotNetObject() を呼び出します。新しいインスタンスを返すか、ハンドルが見つからない場合は false を返します。
指定したインデックスで新しいハンドルを作成し、指定された T の値を使用して handle.SetFromDotNetObject() を呼び出します。
これにより、次の構文が有効になります(MyStructureClass は T パラメータに使用される .NET タイプです)。
MyStructureClass myDotNetObj ; myDotNetObj.myInt = 3; myStream[18] = myDotNetObj;
上記の例では、指定されたインデックスに .NET オブジェクトを追加するか、既にそのインデックスにオブジェクトが存在する場合は現在のオブジェクトを置き換えます。
MyStructureClass myDotNetObj = myStream[18];
上記の例では、指定されたインデックスで検出されたオブジェクトが返されるか、オブジェクトがそのインデックスに存在しない場合は KeyNotFoundException が発生します。
次の構文はコンパイルされますが、ベース ストリーム内の要素は変更されないため、予期されるようには動作しませんので注意してください。
myStream[18].myInt = 1.6;
上記の例では代わりにインデクサで返されたコピーの myInt フィールドが変更されるため、まったく意味がありません。
MyStructureClass temp = myStream[18]; temp.myInt = 1.6; myStream[18] = temp;
これにより、この単純な構文を使用してすべての要素が列挙されます。
foreach( MyStructureClass myDotNetObj in myStream ) { … }
IEnumerable はほとんどのクラスでサポートされているため、データで非常に強力な照会を実行することができます。
たとえば、クラスに振幅というフィールドがあり、振幅が 0.5 よりも大きいメッシュの頂点のチャネルでメタデータを検索する場合、次のようにしてこれを簡単に行うことができます。
Channel chn = myAssociations["vertex"]; Stream chnStream = chn[“myStream”]; var strm = new StreamForType<MyStructureClass>(chnStream); var list = strm.Where( ( MyStructureClass obj ) =>obj.amplitude >0.5 ); foreach( MyStructureClass obj in list ) { … }
var list = strm.Where( ( KeyValuePair<Index,MyStructureClass> keyvalue ) => keyvalue.Value.amplitude > 0.5 ); foreach (var keyvalue in list ) { Index index = keyvalue.Key; MyStructureClass obj = keyvalue.Value; … }
このタイプの照会は、Associations、Channel、Stream、および Structure のいずれかのクラスで実行できます。これらはすべて、LINQ が必要とする唯一のインタフェースである IEnumerable を実装しているためです。