Meta Data

The meta data classes of the C++ API are ported to the .NET API just like all other Maya API classes. In addition, several enhancements have been made to take advantage of the power of .NET.

Metadata is a very important part of the Maya API, and enables clients to describe their custom data (its structure, the types, how to access the data per element, and so forth). .NET features can make the process much simpler and more powerful than it is in C++.

.NET Examples

Several interesting examples that demonstrate the .NET enhancements described on this page are included with the examples that are shipped with Maya (see Compiling examples). Open the examples.sln solution (in the devkit\dotnet\examples folder of your Developer Kit installation; see Setting up your build environment: Windows environment (64-bit)) using Microsoft Visual Studio, and you can find these examples in the MetaData folder. The following are some interesting examples:

Summary

The following is a list of the main classes of the metadata API, and a quick summary of each along with a list of C++ methods that have been ported to .NET:

Associations

This is an entry point to access the meta data from a Maya object (for example, by getting the MFnDependencyNode.metadata property).

It’s basically a simple container of channels.

A few interesting methods ported from C++ are:

Channel

This is a collection of streams that are grouped together, either because they share the same topology (the same stream indices) or because the user decides they should logically be in the same group (for example, all the streams for a simulation effect would be in the same channel and given a channel name that is the name of the effect).

A channel can be named, but some names are reserved (for example, vertex is reserved to indicate that its streams have one element per vertex).

A few interesting methods ported from C++ are:

Stream

This is a container of data on the object (for example, on a mesh, in the channel vertex, you have as many data elements as you have vertices in the stream).

A few interesting methods ported from C++ are:

Structure

This class describes the meta data found at each element of the stream.

Its method AddMember is used to define each field of the meta data.

For instance, if you want one float and one boolean to be stored at every element, first create a structure and call AddMember(kFloat,1,”MyFloatField”) and AddMember(kBoolean,1,”MyBooleanField”).

A few interesting methods ported from C++ are:

Handle

This class allows you to access the data for a specified element of the stream.

With a handle, you can get or set the data of the stream element.

A few interesting methods ported from C++ are:

Taking advantage of .NET

.NET provides several enhancements to the metadata API. Here are the main classes of the API with the .NET enhancements:

Associations

Because it acts as a dictionary of Channel objects, the class supports the .NET interface IDictionary<Channel>.

This interface enables you to use the following standard methods/properties that are expected from a dictionary in .NET:

Channel

Because it acts as a dictionary of Stream objects, the class supports the .NET interface IDictionary<Stream>.

This interface enables you to use the following standard methods/properties that are expected from a dictionary in .NET:

Stream

Because it acts as a dictionary of Handle objects, the class supports the .NET interface IDictionary<Handle>.

This enables you to use the following standard methods/properties that are expected from a dictionary in .NET:

Structure

IEnumerable<Member> was added in order to enable this syntax:

foreach( Member member in myStructure )  { ... }

This lets you enumerate all the members using this very simple syntax instead of the complicated C++/STL syntax:

for( StructureIterator iter = myStream.begin(); iter.notEqual( myStream.end() ); iter.next() ) { 
    Member member = iter.__deref__();
    ... 
}

Handle

The C++ API uses a single method using pointers (for example, int *asInt32()) to let you access the data for a given type. You can use it as a pointer to a single integer (for example, myInteger = *asInt32()) or an array of integer values (for example, asInt32()[6]) if you had specified a size of greater than 1 when you created the corresponding member in the Structure object.

For .NET, using pointers in the API was not feasible so two properties are provided per type:

Matching a client .NET type: the ultimate technique to simplify data description and access

Using the Structure class to describe the data of an element and then the Handle class to access the data is quite low-level and C++ like, for example:

.NET simplifies the whole process, as long as you have a class or structure that describes the data where each member of your type is a member in the expected Structure object.

The new StructureClass assembly attribute

In your plug-in, if you use the new StructureClass assembly attribute and provide your type that describes the structure of your metadata, .NET can automatically create the Structure object for you:

[assembly: StructureClass(typeof(MyNameSpace.MyStructureClass))]

namespace MyNameSpace
{
   public class MyStructureClass
   {
      public int a;
      public int b { get; set; }
      public float[] c = new float[3];
   }
}

Thanks to introspection, .NET can find all the public fields and properties in your class (or struct) and automatically create a Structure object that is registered using Structure.registerStructure.

The name of the Structure is the full name of your type (for example, MyNameSpace.MyStructureClass). The name of each Structure member is the name of the corresponding member in your type.

Converting from a Handle to a .NET object or vice-versa

If you use the new StructureClass assembly attribute (see previous section) with the type of a .NET object, you can use the new following methods of the Handle class:

These methods become unnecessary, however, when you use the new generic class StreamForType<T> explained in the next section.

StreamForType: special version of the Stream class tailored to your .NET type

While Stream is the class to use when you can’t provide a .NET type that describes your Structure, the class StreamForType<T> should be used when you have provided your type with the new StructureClass assembly attribute.

Use the same type for the T parameter of this generic class and you are given a Stream-like class that is completely tailored for your type.

This generic class is a wrapper to a Stream object so you can create it in two ways. The following are the two constructors:

While the Stream class can be seen as a dictionary of Handle objects, the StreamForType<T> generic class can be seen as a dictionary of T objects where T is your .NET class (or struct). This is done with the IDictionary<T> supported by the generic class.

The following are a few interesting methods:

Using the power LINQ to perform SQL-like queries

Since IEnumerable is supported on most classes, you can perform very powerful queries with your data.

For instance, if your class has a field called amplitude, and you want to find the metadata in the vertex channel of a mesh where the amplitude is greater than 0.5, you can do this easily as follows:

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

To query the vertex index, you can do as follows:

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;
   …   
}

This type of query can be done on any of the following classes: Associations, Channel, Stream and Structure because they all implement IEnumerable, which is the only interface LINQ requires.