メタ データ

C++ API のメタ データ クラスは、その他すべての Maya API クラスと同様に .NET API に移植されています。さらに、.NET のパワーを活用するため、いくつかの機能強化が行われています。

メタデータは、Maya API の非常に重要な部分であり、クライアントがカスタム データ(ストラクチャ、タイプ、要素ごとのデータにアクセスする方法など)を記述できるようになります。.NET 機能では、C++ の場合よりも処理を大幅に単純化し、強力にすることができます。

.NET の例

このページに記載した .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++ メソッドのリストとともに次に示します。

Associations

これは、MFnDependencyNode.metadata プロパティを取得することなどによって Maya オブジェクトからメタ データにアクセスするためのエントリ ポイントです。

基本的に、チャネルの単純なコンテナです。

C++ から移植された興味深いメソッドをいくつか示します。

Channel

これは、同じトポロジ(同じストリーム インデックス)を共有しているか、ユーザーが論理的に同じグループになるように決定したため(シミュレーション エフェクトのすべてのストリームが同じチャネルになり、エフェクトの名前であるチャネル名が付けられる場合など)にグループ化されたストリームのコレクションです。

チャネルに名前を付けることができますが、いくつかの名前は予約されています(たとえば、vertex はストリームが頂点ごとに 1 つの要素を持つことを示すために予約されています)。

C++ から移植された興味深いメソッドをいくつか示します。

Stream

これは、オブジェクト上のデータのコンテナです(たとえば、メッシュ上のチャネル vertex では、ストリームにある頂点と同じ数のデータ要素があります)。

C++ から移植された興味深いメソッドをいくつか示します。

Structure

このクラスは、ストリームの各要素で検出されたメタ データについて記述します。

このメソッド AddMember は、メタ データの各フィールドを定義するために使用されます。

たとえば、1 つの float と 1 つのブール値をすべての要素に格納する場合は、最初にストラクチャを作成し、AddMember(kFloat,1,”MyFloatField”)AddMember(kBoolean,1,”MyBooleanField”) を呼び出します。

C++ から移植された興味深いメソッドをいくつか示します。

ハンドル

このクラスを使用すると、ストリームの指定された要素のデータにアクセスすることができます。

ハンドルを使用すると、ストリーム要素のデータを取得または設定することができます。

C++ から移植された興味深いメソッドをいくつか示します。

.NET を利用する

.NET では、メタデータ API にいくつかの拡張機能が提供されます。ここでは、.NET の拡張機能がある API の主なクラスを示します。

Associations

このクラスは Channel オブジェクトのディクショナリとして機能するため、.NET インタフェース IDictionary<Channel> をサポートします。

このインタフェースを使用すると、.NET のディクショナリから予測される次の標準のメソッド/プロパティを使用することができます。

このメソッドを使用すると名前が指定されたチャネルを検索することができ、同時にその有無がテストされます。

このインデクサも名前が指定されたチャネルを検索しますが、この場合、チャネルは見つかる必要があります(見つからない場合は、KeyNotFoundException をスローします)。

これは単に、指定された名前のチャネルが存在しているかどうかをテストします。チャネルは返されません。

これはチャネルを追加します。または同じ名前のチャネルが既に存在している場合は ArgumentException をスローします。

これを使用すると、複雑な C++/STL 構文の代わりに非常に単純な構文を使用してすべてのチャネルを列挙することができます。

for( AssociationsIterator iter = myAssociations.begin(); iter.notEqual( myAssociations.end() ); iter.next() ) { Channel chnl = iter.__deref__(); ... }

Channel

このクラスは Stream オブジェクトのディクショナリとして機能するため、.NET インタフェース IDictionary<Stream> をサポートします。

このインタフェースを使用すると、.NET のディクショナリから予測される次の標準のメソッド/プロパティを使用することができます。

このメソッドを使用すると名前が指定されたストリームを検索することができ、同時にその有無がテストされます。

Stream stream = myChannel[“myStream”];

このインデクサも名前が指定されたストリームを検索しますが、この場合、ストリームは見つかる必要があります(見つからない場合は、KeyNotFoundException をスローします)。

if( myChannel.ContainsKey( “myStream” ) { ... }

これは単に、指定された名前のストリームが存在しているかどうかをテストします。ストリームは返されません。

myChannel.Add( “myStream”, myStream );

これはストリームを追加します。または同じ名前のストリームが既に存在している場合は ArgumentException をスローします。

foreach( Stream stream in myChannel ) { ... }

これを使用すると、複雑な C++/STL 構文の代わりに非常に単純な構文を使用してすべてのストリームを列挙することができます。

for( ChannelIterator iter = myChannel.begin(); iter.notEqual( myChannel.end() ); iter.next() ) { Stream stream = iter.__deref__(); ... }

Stream

このクラスは Handle オブジェクトのディクショナリとして機能するため、.NET インタフェース IDictionary<Handle> をサポートします。

これを使用すると、.NET のディクショナリから予測される次の標準のメソッド/プロパティを使用することができます。

このメソッドを使用するとインデックスが指定されたハンドルを検索することができ、同時にその有無がテストされます。

Handle handle = myStream[18];

このインデクサもインデックスが指定されたハンドルを検索しますが、この場合、ハンドルは見つかる必要があります(見つからない場合は、KeyNotFoundException をスローします)。

if( myStream.ContainsKey(18) { ... }

これは単に、指定されたインデックスのハンドルが存在しているかどうかをテストします。ハンドルは返されません。

myStream.Add( 18, myHandle );

これはハンドルを追加します。または同じインデックスのハンドルが既に存在している場合は ArgumentException をスローします。

foreach( Handle handle in myStream ) { ... }

これを使用すると、複雑な C++/STL 構文の代わりに非常に単純な構文を使用してすべてのハンドルを列挙することができます。

for( StreamIterator iter = myStream.begin(); iter.notEqual( myStream.end() ); iter.next() ) { Handle handle = iter.__deref__(); ... }

Structure

この構文を有効にするために 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())。Structure オブジェクトに対応するメンバーを作成するときに 1 より大きいサイズを指定した場合は、整数値の配列に対するポインタとして使用できます(例: asInt32()[6])。

.NET の場合、API でポインタを使用することはできなかったため、タイプごとに 2 つのプロパティが提供されています。

クライアントの .NET タイプと一致: データの記述とアクセスを簡略化するための最終テクニック

Structure クラスを使用して要素のデータを記述すると、データにアクセスする Handle クラスは次のようにかなり低レベルになり、C++ のようになります。

使用しているタイプの各メンバーが予想される Structure オブジェクトのメンバーとなっているデータを、クラスまたはストラクチャ内で記述している場合は、.NET によってプロセス全体が単純化されます。

新しい StructureClass アセンブリ アトリビュート

プラグインで新しい 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 など)。各ストラクチャ メンバーの名前は、タイプの対応するメンバーの名前です。

Handle から .NET オブジェクト(またはその逆)に変換する

.NET オブジェクト タイプの新しい StructureClass アセンブリ アトリビュート(前のセクションを参照)を使用する場合は、次に示す Handle クラスの新しいメソッドを使用することができます。

これにより、メンバーは .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

これにより、メンバーは 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> を使用する場合、これらのメソッドは不要になります。

StreamForType: .NET タイプに合わせた Stream クラスの特殊なバージョン

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 ) { … }

強力な LINQ を使用して SQL のような照会を実行する

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 を実装しているためです。