Browsing"Characters"

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.

Stuff the revolution, I’m thinking about a Character Selection Framework

Jun 1, 2010 by     10 Comments    Posted under: 3dsMax, Characters, DotNet, Rigging, Technical Research, User Controls

 

One of the most time consuming things always seems to be building pesky UI’s in Maxscript. The ever reliable Visual Maxscript editor, whilst having the reputation of Marmite, has been compromised by a spectacular bug in the last few releases that prints the last few characters of the previous line onto the next one, thus scrambling your code.

rollout MXSMashup "Untitled" width:169 height:171
     (
         button btn1 "Button" pos:[7,8] width:70 height:39
     39
         bitmap bmp1 "Bitmap" pos:[97,25] width:52 height:84
    84
        checkbutton ckb1 "CheckButton" pos:[28,138] width:132 height:20
    20
        combobox cbx1 "ComboBox" pos:[16,91] width:55 height:1
    1
        edittext edt1 "" pos:[14,60] width:55 height:20
    20
    )

All this means you end up manually  tweaking positions and sizes till the end of time, for a script  that is essentially doing something very simple. It’s always faster to have a small dialog with a picture of the rig with fast access to individual bones to remove the necessity of picking them in the viewport. However, generating these things up can usually take more time than we have to give in production. I thought it would be an interesting focus to my research to see how easy it would be to implement a system that allowed even a non-scripter to build specialised dialogs for selection, ultimately a task that you end up doing a billion times with a rig.

The IDE driven usercontrol approach

Now Visual Studio has a pretty nifty IDE, and you can layout windows forms pretty darned fast with it. It would be rather handy to have something similar but centred around 3dsMax, albeit with a basic layout toolset. The obvious approach (in terms of linking the dotnet framework to 3dsMax objects) was binding controls to the names of scene nodes. Okay, maybe scene node names might not be the most robust of solutions but it is fine for this sort of thing – but if someone starts renaming rigs just for fun, you’re going to be screwed on a bigger level than just the selection tools.

Look at this lot –

all dialogs

With Rig Studio, you can do this without any coding in no time at all. Here’s how –

A Runtime Design Surface

Have a look at the video below to see an example of how RigStudio sets up a basic control dialog and then stores it to be read by a user end interface.

You build up an interface by drag and drop, and there are preset sizes and shapes defined for speed. You can size these up to whatever you like. There are options to set the forecolor and background color, and to position text blocks to illustrate the different areas of the dialog.

You might recognise the control on the right of the UI – It’s a propertygrid and is a great little control for exactly this type of thing, as it takes a dotnetobject and displays an area so that you can adjust the properties easily. You can use this out of the box, but I have customised it to only display the properties that you need to adjust. I have also written a custom UITypeEditor class to handle the control list drop down method for choosing hierarchies.

TypeEditor

This is not a default behaviour of the propertygrid control. the Child property on a rigcontrol object is a string denoting the name of the next node in the hierarchy. In order to give better design-time support, I am asking the UITypeEditor  to take an array of controls from the design surface and build a sorted listbox from the results. It needs to be sorted as the controls could have been created in any order.

Custom Toolstrip Renderers

Alignment-Options_Dotnet Alignment-Options

Keen-eyed UI aficionados will notice the similarity of the drop down menu on the left to 3ds Max’s menu system, and not the default dotnet toolstrip renderer (which is on the right). Its a subtle difference, but this is due to the use of a custom toolstrip renderer. Autodesk has provided this as part of the MaxCustomControls namespace.

MaxToolStripSystemRenderer is a custom renderer and is assigned by simply giving an instance of it to the renderer property. So you can implement this into Maxscript pretty easilly too, if you want everything to look like it is part of the same application.

-- VB
Me.CtrlMenu.Renderer = New MaxCustomControls.MaxToolStripSystemRenderer()
-- MXS
CtrlMenu.Renderer = dotnetobject "MaxCustomControls.MaxToolStripSystemRenderer"

What might annoy you about the toolstrip, especially if you run the UI from the dark side of the force, is the border that windows paints around the toolstrip menu. This is just plain annoying if you are trying to keep a nice minimal looking UI.

toolstrip

It’s probably not so noticeable on a light UI scheme, but it would be great to get rid of it. With some poking around on some VB forums, I found this solution. There is a protected method called OnRenderToolstripBorder. All that you need to do is to override this and tell it to do nothing, as you don’t want the border. The great thing about class inheritance is that we can inherit the custom renderer class made by Autodesk, implement the functionality we need, and have a new class to use. By only overriding the methods that we want to change, we keep all the other existing render bits. It is actually a pretty short entry to have a version of the Autodesk Toolstrip renderer without the white border line –

Public Class BorderlessMaxToolStripRenderer
Inherits MaxCustomControls.MaxToolStripSystemRenderer
	Protected Overrides Sub OnRenderToolStripBorder _
				(ByVal e As System.Windows.Forms.ToolStripRenderEventArgs)
 	'Do nothing
 	End Sub
End Class

So now you can have the great functionality of the toolstrip without having an incongruous looking UI. It would be pretty easy to dynamically compile this class as part of a maxscript, so that you dont have to distribute an assembly too.

Toolstrip-CustomRenderer

Weirdly, you don’t get this border with a menustrip, which is fine for most things, but it’s not got the same choice of controls. Its a shame i didnt realise this until I had gone down this route!

Design-TIme Alignment Functions

Controls have a left, right, top and bottom property which returns their position relating to their parent container. This is a local position and unrelated to screen position. This makes the majority of options in the alignment menu relatively easy. Where it becomes more complicated is control spacing. Controls can be added in any order, so you need a method of deciding where a control is in dialog position terms. If you are performing a spacing arrangement, you need a method that can take an array of controls and return them sorted into order by their location property. As you can space vertically or horizionally, you’ll need a way of tells the method the direction that you want to sort them. It is precisiely this sort of logic that the Icomparer can handle, sorting abstract classes according to  defined properties.

Fortunately, the dotNet framework has some powerful sorting method called Icomparer for doing this job.

Public Enum AlignmentCompareOptions
    Vertical
    Horizontal
End Enum

Friend Class PointComparer
    Implements IComparer(Of Drawing.Point)
    Dim _CompareDirection As AlignmentCompareOptions

    Public Sub New(ByVal Direction As AlignmentCompareOptions)
        _CompareDirection = Direction
    End Sub
    Public Function Compare(ByVal x As Drawing.Point, ByVal y As Drawing.Point) As Integer Implements IComparer(Of System.Drawing.Point).Compare
        Dim pointX As Point = DirectCast(x, Point)
        Dim pointY As Point = DirectCast(y, Point)

        Select Case _CompareDirection
            Case AlignmentCompareOptions.Vertical
                Select Case True
                    Case pointX.Y > pointY.Y
                        Return 1
                    Case pointX.Y < pointY.Y
                        Return -1
                    Case pointX.Y = pointY.Y
                        Return 0
                End Select
            Case AlignmentCompareOptions.Horizontal
                Select Case True
                    Case pointX.X > pointY.X
                        Return 1
                    Case pointX.X < pointY.X
                        Return -1
                    Case pointX.X = pointY.X
                        Return 0
                End Select
        End Select

    End Function

End Class

In order to use this class, you add the controls to a SortedDictionary, one of the classes that you can supply an Icomparer to the constructor. You’ll see this SortedDictionary takes a point value, and a control value. As these are added the Sorted dictionary automatically sorts them into the order they are according to the direction (vertical or horizontal)

Dim CompareControlList As New SortedDictionary(Of Point, Control)(New PointComparer(Direction))

The Icomparer sorts it by returning an integer that relates to the evaluated expression within the IComparer, 1 if pointA is greater than pointB, –1 if B is greater than A, and 0 if they are the same.

The conclusion to all this is that you can use the results to calculate the difference between the first and last control instances, deduct the combined height or width of the controls and divide by the number of gaps to get the distance needed to space the controls evenly.

XML Control Serialization

This was a new thing for me to get my head around, and in doing so I genuinely believe I have only scratched the surface on the sort of things that you can do. In my case, XML serialization provided an elegant solution to this problem –

XMLfile.ChildNodes.ItemOf(i).ChildNodes.ItemOf(i).ChildNodes.ItemOf(i).InnerText

Sometimes, negotiating an XML tree manually becomes difficult to track, but in this case, you explicitly know what type of information you are dealing with. XML serialization allows you to deconstruct dotnetobjects, with the idea that you can use the serializer’s logic to re-assemble them at another time, perhaps even to send data to a compliant application on a different computer.

To use the example of, say a bog-standard button, we could serialize this but we have to address a couple of issues first. XML needs a specific data type to store the information. You couldn’t just pass it an enum style like “borderstyle.fixedsingle” as a string and expect it to know what to do with it. Also, there are a sh*t load of properties that we have on a button, many of which don’t particularly represent anything to do with the visual state. You would be serializing a lot of useless information into the XML file, making the file larger than it needs to be.

Writing a Custom Serialization class

I ended up by writing a class specifically to handle the serialization of the design surface to XML. It actually consists of three classes, the RigControlSerializer is where the action happens. It stores all the dialog information necessary to recreate the size and shape of the stored data. RigControl and RigControlText are subclasses that allow the serializer to loop through the controls on the design surface and store them into an array list. So the serializer property controlist actually has an array of the RigControl class, not the actual control itself, but is perfect as it is all the information that the deserializer needs to recreate the control faithfully on another surface.

Custom Serializer Classes

The great thing about writing a serializer class is that each property becomes a branch in the xml file automatically,  and subsequent paths are made as each property is serialized in order. So an entire dialog can be written out to XML as follows –

Private Function SerializeDesignSurfacetoXML(ByVal XMLFilename As String) As Boolean

        Try
            Dim SavedDialog As RigControlSerializer = New RigControlSerializer()

            With SavedDialog
                .DialogSize = DesignSurface.Size
                .DialogBackcolor = (CType(DesignSurface.BackColor, Color)).ToArgb
                .ControlList = CreateRigControlCopyArray()
                .TextLabelList = CreateTextLabelCopyArray()
                .BackgroundImage = CType(Me.DesignSurface.BackgroundImage, System.Drawing.Bitmap)
                .CharacterName = Me.CharacterName
            End With

            Dim writer As New XmlSerializer(GetType(RigControlSerializer))
            Dim file As New StreamWriter(XMLFilename)
            writer.Serialize(file, SavedDialog)
            file.Close()
            writer = Nothing
            Return True

        Catch ex As Exception
            Return False
            MessageBox.Show(ex.InnerException.ToString)
        End Try
    End Function

That, for me is a pretty straightforward way of converting a whole bunch of dotnet objects into an XML file!

As you control the headings of the XML branches via the class, its pretty easy to see what is going on in the XML file itself. As much as it seems like more work, I think it is a quite elegant method.

xml

One other useful thing to crop up with this is Binary Serialization. You use the same serializer class to dump all the control positions to a temporary .dat file. Why is this useful? You’ve just used the serializer to introduce an undo buffer, in case the control alignment didn’t happen as you expected, or as I do pick the wrong direction to align.

The RigSelector User Control

RigSelectorVS

The ultimate payoff to the IDE approach is providing a simple way to rebuild the selection dialog from the XML data. The RigSelector usercontrol is just a composite control that only really features a panel. The key is having a method to deserialize the XML to populate the panel with the stored node hierarchy.

We have reused the BorderlessAutodeskToolstripRenderer that we used earlier for the button.

It can resize the parent form to the dimensions of the stored dialog control.

RigSelectorClass

The rest is done by adding the handlers when deserializing the control to fire the selection within max. This perhaps the simplest part. To fire Maxscript code from within a dotnetclass you use –

>ManagedServices.MaxscriptSDK.ExecuteMaxscriptCommand(<<MXS_String>>)

Lastly, my best piece of advice about implementing ANY form of custom dotnet control class in 3dsMax.

Don’t be shy about calling enable accelerators

to pass focus back to your max shortcuts!

As much as the maxform control is supposed to handle all this stuff, using a custom class can put a spanner in the works, and there’s nothing worse than realising that you’ve just knocked out your hotkeys.

ManagedServices.AppSDK.EnableAccelerators()

I call this after every button click, which might be overkill but it’s better than losing focus to your dotnetcontrol and being powerless to get them back without a restart or frenzied maxscript call. Any one wanting to do this sort of thing should really look into ManagedServices.dll, there is some great stuff in there.

Polar-DesignSurface

I hope this article has been of interest! Comments are always welcome!

Rig Studio Update!

I recently added automatic rig generation to Rig Studio – This speeds things up considerably.

I have also added a new control – A layer control. This allows you to store a name string of a corresponding layer. This has hide and unhide functionality build in, so you can control character rig and mesh visibility without having to open the layer manager.

DotnetObject “LoneRobot.Child”

Mar 22, 2010 by     11 Comments    Posted under: Characters

I’d like to introduce the latest LoneRobot build – and it’s the one that I am most proud of.

VSBuild

He/She is due mid September and the beautiful Mrs.Robot is doing very well. I can only imagine that menu option 3 above will shortly become my life for the foreseeable future.

Controlling Node Visibility with Custom Attributes

Jan 20, 2009 by     No Comments    Posted under: 3dsMax, Characters, Maxscript, Rigging

This was a small attribute inspired after reading Paul Neale’s article on Weak Referencing in 3dsMax. It allows you to store references to nodes within the attribute and then globally control their visibility in a scene. The only thing to note is that obviously, if you are hiding a node that the attribute is on, you will no longer have the dialog showing but you can just place this on a root node or dummy object too. On the visibility attribute, use the set button to specify the nodes you want to control the visibility of. There are three separate groups for mesh, rig and control objects.

Weak References are great for character animators because you can store a load of information about objects in a rig without having the issue of referring to it explicitly by name, nor do you have dependency issues as it stores the node with some jiggery pokery between the RefTargMonitorRefMaker and NodeTransformMonitor classes. Without going into it anymore than that, you can specify a reference in a custom attribute and refer to it via Maxscript like CustomAttribute.Weasel_RootBone.node. This will return the node.

You specify a Weak Reference by using a parameter block in the custom attribute. This must be set to the #maxobject type. If you want to store an array of nodes you should use the #maxobjecttab type. In the reset character utility above i have used a #maxobject to store the rootnode, and a #maxobjecttab to store the other bones.

Look at the code below and you will see the parameter declared in the first section of the attribute. You can obviously set all these things via script but these utilities are a more manageable front end in case you need to add anything to the character afterwards.

parameters main
(
RigVisNodes type:#MaxObjectTab tabSize:0 tabSizeVariable:true
MeshVisNodes type:#MaxObjectTab tabSize:0 tabSizeVariable:true
CtrlObjsNodes type:#MaxObjectTab tabSize:0 tabSizeVariable:true
HideCVisUI type:#boolean default:false
RigVisHidden type:#boolean default:false
MeshVisHidden type:#boolean default:false
CtrlObjsVisHidden type:#boolean default:false
)

Once you have the UI working as you like, you can alter these to any purpose you want. I have added a turbosmooth control that allows you to globally set the viewport/render state of the added nodes, as well as a skin pose utility that takes a custom rig and stores allows you to reset the whole rig back to a default position. In the case of storing the visibility nodes, it goes like this –

if VisObjs != undefined do
(
MeshVisNodes = #() -- resets the #maxobjecttab array
for i in Visobjs do append MeshVisNodes(nodeTransformMonitor node:i forwardTransformChangeMsgs:false)
)

It is worth researching the types available in the parameter block, as others can be very useful also. I have used the #boolean option to store the UI state of the attribute and this means once minimized, it stays like that when run subsequent times. This keeps everything local to the attribute and eliminates the need for ini files to store settings.

Using the boolean switch could just as easily be applied to the state of a checkbutton – swapping of materials or changing the spline base recomb option on a hair and fur modifier for example. This state is stored within the attribute.

Improvements you can make

Currently, the attribute doesn’t check if the node has been hidden manually. This could be done with a callback but I haven’t implemented it yet. The problem is that you might have to press the button again to toggle the objectset to the state you want.

To add to an object

Select it and run the script, it will apply it to the currently selected stack item.

Where to look in the Maxscript Help for further information

Scripted Plug-in Clauses

Weak References To Nodes in the Expression/Script Controller

download

Stretchable Limb System for Biped

Nov 18, 2008 by     No Comments    Posted under: 3dsMax, Characters, Rigging, Technical Research

In a recent project, we were in a position that we needed a rig that incorporated stretchable limbs. The proportions of the characters made it neccesary in order for them to be able to do things like pick up objects etc. This seems to be a common problem when you have characers with large bodies and short arms. Unfortunately, this had to be set up in a hurry, so building a custom IK rig was out of the question. The solution we came up with was to bind a set of expression controllers into a custom attibute definition.

Each hand, foot and spine link is wired together, with the X scale controlling the main linear scale and the YZ scaling performing a mild squash/stretch effect. The intermediate limbs also have an attribute to control the percentage, should you want to perfom slightly less scaling at the top of the spine chain, for instance.

Each control node can launche a floater control (This is WIP, as i want to refine the interface into a dotnet control) This has sliders and node selection tools for the control of the scaling.

This is the expression control dialog – Here you can see the variables that are bound to the custom attribute parameter block. This is possibly going to be adapted into a script controller in the future, using weak references to tie in the whole rig to other animation systems.

Finally, and most importantly the setup of this extra layer is automated in a rollout, that allows you to store presets and tweak the intermediate controllers before you apply. It is a system that can provide this very quickly – It only provides an interface to biped’s subanim functionality.