メタ データ

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 フォルダ内で見つけることができます。いくつかの興味深い例を次に示します。

概要

メタデータ API の主なクラスのリストとそれぞれの簡単な概要を、.NET に移植された C++ メソッドのリストとともに次に示します。

Associations

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

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

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

  • Channel findChannel(string) は、名前が指定されたチャネルを検索します。
  • void setChannel(Channel) は、チャネルを追加するか、同じ名前を持つ既存のチャネルを置き換えます。
  • bool removeChannel(string) は、名前が指定されたチャネルを除去します。
  • AssociationsIterator begin() は、C++/STL 形式のイテレータを返し、チャネルを反復します。

チャネル(Channel)

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

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

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

  • Stream findDataStream(string) は、名前が指定されたストリームを検索します。
  • Stream setDataStream(Stream) は、ストリームを追加します。
  • void removeDataStream(string) は、名前が指定されたストリームを除去します。
  • ChannelIterator begin() は、C++/STL 形式のイテレータを返し、ストリームを反復します。
  • bool removeElement(Index elementIndex) は、チャネルで検出されたすべてのストリームから指定された要素を除去します。
  • bool addElement(Index elementIndex) は、チャネルで検出されたすべてのストリームに指定された要素を追加します。

Stream

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

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

  • Stream(Structure, string) は、指定されたストラクチャを使用して指定された名前の Stream オブジェクトを作成します。
  • bool setElement(Index, Handle) は、指定されたインデックスのデータを別のデータに置き換えます。
  • Handle element(Index) は、指定されたインデックスでデータのハンドルを取得します。
  • StreamIterator begin() は、C++/STL 形式のイテレータを返し、ハンドルを反復します。

Structure

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

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

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

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

  • bool addMember( Member.eDataType, uint arraySize, string memberName ) は、指定されたタイプ、サイズ(一般的には 1)、および名前のメンバーをストラクチャに追加します。
  • StructureIterator begin() は、C++/STL 形式のイテレータを返し、メンバーを反復します。

ハンドル

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

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

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

  • bool setPositionByMemberIndex(uint) は、このハンドルが関連付けられているストラクチャのメンバーを示します。
  • int[] asInt32Array は、(整数が 1 つのみの場合でも)整数データを整数の配列として取得または設定します。
  • float[] asFloatArray は、(float が 1 つのみの場合でも) float データを float の配列として取得または設定します。
  • など...

.NET を利用する

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

Associations

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 );

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

  • foreach( Channel chnl in myAssociations )  { ... }

    これを使用すると、複雑な 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; 
    if( myChannel.TryGetValue( “myStream”, out stream ) { ... }
    このメソッドを使用すると名前が指定されたストリームを検索することができ、同時にその有無がテストされます。
  • 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; 
    if( myStream.TryGetValue( 18, out handle ) { ... }
    このメソッドを使用するとインデックスが指定されたハンドルを検索することができ、同時にその有無がテストされます。
  • 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())ことも、整数値の配列(たとえば、asInt32()[6])に対するポインタとして使用することもできます(Structure オブジェクトに対応するメンバーを作成したときに 1 より大きいサイズを指定した場合)。

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

  • int[] asInt32Array は、整数の配列として整数データを取得または設定します。
  • int asInt32 は、単一の整数を取得または設定します(対応するストラクチャ メンバーで検出されたサイズが 1 より大きい場合は ApplicationException が発生します)。
  • float[] asFloatArray は、float の配列として float データを取得または設定します。
  • float asFloat は、単一の float を取得または設定します(対応するストラクチャ メンバーで検出されたサイズが 1 より大きい場合は ApplicationException が発生します)。
  • など...

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

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

.NET では、タイプの各メンバーが予想される Structure オブジェクトのメンバーであるデータを記述するクラスまたはストラクチャがある限り、プロセス全体が単純化されます。

新しい 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 クラスの次の新しいメソッドを使用することができます。

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

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

Stream は Structure を記述する .NET タイプを提供できない場合に使用するクラスですが、新しい StructureClassアセンブリ アトリビュートを使用してタイプを提供した場合には クラス StreamForType<T> を使用する必要があります。

この汎用クラスの T パラメータに同じタイプを使用すると、タイプに完全にカスタマイズされた Stream のようなクラスが提供されます。

この汎用クラスは Stream オブジェクトのラッパーであり、2 つの方法で作成することができます。2 つのコンストラクタを次に示します。

  • StreamForType<T>(string streamName)

    このコンストラクタは、タイプ T のフル ネームで呼び出された既存の Structure を検索します(StructureClass アセンブリ アトリビュートを使用した場合に検索する必要があります)。コンストラクタは、指定された名前と検出されたストラクチャを使用して新しい Stream オブジェクトを作成し、この新しい Stream オブジェクトをラップします。

  • StreamForType<T>(Stream baseStream)

    このコンストラクタは、指定されたストリームをラップするだけでなく、このベース ストリームのストラクチャがタイプと互換性のあることが保証されます(StructureClass アセンブリ アトリビュートを使用した場合にその必要があります)。

Stream クラスは Handle オブジェクトのディクショナリとみなすことができますが、StreamForType<T> 汎用クラスは T が .NET クラス(またはストラクチャ)である T オブジェクトのディクショナリとみなすことができます。これは、汎用クラスによってサポートされた IDictionary<T> を使用して実行されます。

いくつかの興味深いメソッドを次に示します。

  • bool TryGetValue(Index, out T value)

    指定されたインデックスを持つ Handle オブジェクトを検索し、T の新しいインスタンスで handle.CopyToDotNetObject() を呼び出します。新しいインスタンスを返すか、ハンドルが見つからない場合は false を返します。

  • void Add(Index, T value)

    指定したインデックスで新しいハンドルを作成し、指定された T の値を使用して handle.SetFromDotNetObject() を呼び出します。

  • T this[Index key]

    これにより、次の構文が有効になります(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;
    

  • IEnumerator<T> GetEnumerator()

    これにより、この単純な構文を使用してすべての要素が列挙されます。

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