Loving LINQ is easy because it’s beautiful

Oct 7, 2010 by     6 Comments    Posted under: 3dsMax, Characters, DotNet, Technical Research, User Controls

One thing is certain, XML is prolific. I wanted to research the most efficient way of using this versatile language in future systems I develop for 3dsmax animation pipelines.

One of my latest research projects is looking at various options for storing information about characters in a project. There are many different types of data that is useful to be able to pull up, from node information to walk cycle data. What i haven’t had before is a unified method for storage and retrieval. I have been using XML in my character tools for some years quite successfully, from Lipsync storage to Walk Cycles.

I’m sure most studios out there are using some form of database, whether it be for asset tracking etc, and I’m sure that this is a perfect solution. However for this problem, after looking at SQL server configurations and methods I found a different approach that could span my need for database-like data handling and the transparency of storing to XML.

XML Verbosity

I’ll hold my hat up, part of this might be because I went to art school and didn’t pay enough attention in Maths, but there was something distinctly hit and miss about using XML within a dotnet assembly. The Document Object Model (DOM) felt quite cumbersome and wasn’t like the elegant OOP approach I was looking for. What was a transparent user experience with intellisense in visual studio, using XML on an object level became a clunky affair. With my last post about RigStudio, my dynamic character selection framework, I integrated a custom XML serlializer to take the guesswork out of the XML parsing and creation.

The Microsoft Language Integrated Query framework (or LINQ) is one of the more recent introductions to the dotnet framework and allows XML data to finally be treated like data. You can now perform queries and operations on the XML tree in a proper object orientated approach. If you go the whole hog, you can also build an XML schema from an existing XML template and have intellisense support for the XML document. Most importantly, the Linq XML classes are enumerable classes, meaning you can iterate then easily, meaning you can extract, merge and join portions of the whole document tree into other branches and documents.

Also available to the VB programmer are XML Literals. Basically, this means you can begin typing a variable directly into an xml tree – so your code actually resembles an XML document and allows you to integrate variables into the tree dynamically, so you can loop object collections to build complex XML documents completely via code.

Xelement

As you can see, you can format it exactly like an XML document directly from the variable declaration. To add a variable into the document, you can use :

<%= your variable here %>

This allows one to pass another XElement at this point to nest more complex trees. This is certainly far simpler to set up than a custom serliazation class.

Where LINQ fits in with 3dsMax

In terms of max, you can’t really perform the query commands that are the really useful part of LINQ (Well not to my initial research). The main class you will want to use with LINQ to XML is the XElement. In raw dotnetclass form within 3dsmax, you are using it in a similar way to how XML was previously treated – i.e. the DOM model. However, providing a class library that utilises the LINQ query methods could be worthwhile. Visual Studio is a mature development environment, and you are adding something to max. In terms of the deployment, I have no problem adding a dll to the max startup. I met with the 3dsMax design team a while back through work and they assured me dotnet is going to be with max for a long time to come.

Where the XElement class is useful, is that it can encompass many different XML files, or a single branch within a particular file. If you had a selection of XML documents that stored the scene nodes of a different scenes,You could use a LINQ query to get the filenames where a specific object resided. But my purposes, whilst similar was to get this working within a character pipeline.

My idea was to create a type of XML Database that I called Silo. This would be setup in Max using managedservices.MaxsciptSDK functions to pass object names into the assembly that could then be written to XML. It could also integrate RigStudio into the same file. What this means is there is a front end that can quickly store node data about a character rig which can then be queried, giving access potentially to any data applicable to the characters within a particular project, whether this would entail node data, mirroring information, Lipsync, Walk Cycle footstep length – the possibilities are endless.

Most of the time, animators want to be able to control visibility of characters at various points, some times you want to see the controls for speed, sometimes the mesh for previews. Having different layers for each of these types is fine, but when you have a lot of characters, it can mean navigating the layer manager is more involved. Wouldn’t it be great to just store these relationships in a datasource and keep the entire character on one layer? In fact, you could have ALL characters on a single layer if you wanted, the database could then handle all node interaction.

The front end on my prototype looks like this, I’m still trying to work out the best layout at the moment, as it feels a little thrown on to me. However it’s enough to quickly setup a multiple character database that can be automatically bound to my Rigselector control. Each tab allows me to store selected elements of the rig into the various sections.

SiloConsole

Here’s a useful class that you can use within a dotnet assembly – I needed a way of getting the selected object names into the dotnet assembly in order to save them to XML

Public Class MaxOps
    Public Function GetCurrentSelection(ByVal SingleNode As Boolean) As List(Of String)
        Dim NodeList As New List(Of String)
        Dim Selectioncount As Integer = ManagedServices.MaxscriptSDK.ExecuteIntMaxscriptQuery("Selection.count")
        If SingleNode Then
            If Selectioncount = 1 Then
                NodeList.Add(ManagedServices.MaxscriptSDK.ExecuteStringMaxscriptQuery("Selection[1].name"))
                Return NodeList
            Else
                Return Nothing
            End If
        ElseIf Selectioncount > 0 Then
            For i As Integer = 1 To Selectioncount
                NodeList.Add(ManagedServices.MaxscriptSDK.ExecuteStringMaxscriptQuery("Selection[" & i.ToString & "].name"))
            Next
            Return NodeList
        Else
            Return Nothing
        End If
    End Function
End Class

The final tab updates RigStudio for Silo compatibility. You can now build a rig selector directly from silo, or import a previous version.

One option with Silo is you can specify a species for the character, so that you can control visibility of different types of characters. For example you can use LINQ to combine types of queries. A literal translation would be to ask –

“Unhide all animation control nodes that reside in the animal species”

With LINQ, this command would look a bit like this –

query

You can see the use of XML style parentheses in the query. These are known as axis constraints that return the xml nodes of ANY character with the same branch name. This means you are using the XML nodes like objects.

Another thing to remember, is that within max a system.array with be cast into a max array type. So if you are using dotnet lists and specialized.collections within the assembly, that is fine, but in order to avoid extra code in maxscript it’s best to make sure the function has the appropriate return type.

Hooking up the XML Database

dbclass

This is a breakdown of the SiloDatabase class – This uses LINQ to consolidate an XML file into a queryable dotnetobject in 3dsMax. As you can see, there are many methods, all of which can be hardwired into other assemblies, as you would only usually be running a single instance of this class. This means I can bind other assemblies to use the database without any maxscript interaction. This is always my goal, maximum flexibility with minimal deployment. The deployment for this whole database pipeline within a project? a couple of lines in max startup. It grows with the project and all the selection logic is built into the controls, not the deployment code, so can evolve and improve as the project goes on. The core of the database object isn’t really a database of course, its a collection of XML files. But when max instantiates the Silo Database dotnetobject, it appears and acts like one because of the LINQ query methods.

404
бюро пирогова отзывы сотрудников привычные