At what point…

Apr 14, 2011 by     7 Comments    Posted under: Maxscript, News

Do you realise that you’ve been writing maxscript for too long….?


Adding mirror functionality to a custom rig

Jan 18, 2011 by     No Comments    Posted under: 3dsMax, Characters, DotNet, Maxscript, Rigging, User Controls

On our last production, I had to find a quick way of implementing a system that would allow me to set up basic mirror functions on any custom rig. Specifically, I needed a toolset that would standardise the main functions of the rig like selection tools for the current side of the body, mirror select, copy, paste and IK/FK switching.

I accomplished this with a simple custom attribute that allowed me to store the nodes of each half of the body as weak references. If you don’t already know, weak references are the golden goose of character rigging. They allow you to store arbitrary nodes within the character so that you can retrieve them at any stage without having to select the node itself.


In the video below, you can see how I set up a full mirror/rig selection system on a puppetshop rig. Puppetshop is great because it already has a load of good stuff already built in, so the GUI I add in the video (which is a custom dotnet interface) can simply expose functions from the puppetshop API.

The selector always knows what the opposite side of the rig is because you store each node’s opposite in the parameter block of the attribute.

So what it does is allows me to set up this feature on any rig within a minute or so. Because you can store the node assignments to XML, it is easy to mirror the nodes onto the other side of the rig for even faster setup.

I hope this video gives some insight into how automated setups can save a wealth of time in production.

If you want to try a max script that uses weak references on a rig, please read my previous post –

I talk about them in a bit more detail and show you how you can control rig visibility using them.

Using HitchHiker – Building a Matte/Shadow material utility

Dec 21, 2010 by     2 Comments    Posted under: 3dsMax, DotNet, Imaging, Maxscript

This post is to show how you can use Hitchhiker to develop maxscript tools in super quick time. Any auto thumbnailer is going to have about a million uses in production in my humble opinion, as most of the development time on a utility is spend building the UI functionality. Whilst on the subject of dotnet controls, I would like to draw attention to another dotnet thumbnail control out there, written by TD Jason Labbe. It’s got a few things that Hitchhiker doesn’t have like multi select, so might be useful if Hitchhiker hasn’t quite got the functionality you need. Nice job Jason! You can read about it here

The Problem

On our last production, we had to consolidate a lot of complex sets into background renders as we were on tight deadlines. Often, the shot would need some form of contact shadow as well as masking so the obvious choice was to use the mental ray matte material. Depending on the type of shot, HD render times could be cut from 10 minutes per frame to around 45 seconds, with no perceptible loss of detail or quality.

The Solution

I wrote a utility that allowed the user to browse for the rendered backgrounds and quickly set the material up as a matte material, ready to be assigned to the background objects and rendered.


This utility is a good example of what you can use HitchHiker for. The browsing window allows you to pick the map that you want, and with the new cache mode property on the latest hitchhiker, is very fast. The greatest thing was that I wrote this entire script on my train ride home one day. This was exactly why I wrote HitchHiker, a plug and play solution to speed up my deployment of production scripts.

The material setup is simple,  matte/shadow.reflection (mi) shader is made with an Environment/Background Map (mi) shader. The texturemap is then set to screen and the blur taken down. You have to option to include the alpha so that you can use the render for composition. While the script itself is simple, it’s an example of how you can speed up a common bottleneck with very small programming outlay. Suffice to say, it checks that Mental Ray is the production renderer, as the materials are only compatible with that, but if you wanted to adjust it for use with an alternative material setup, it would be very easy. There is a method called :

CreateMatteShader <texmap> <slotnumber>

This could be changed to whatever you liked.

There is another assembly that is included with this script too, called ImageBot. It is an extended picturebox that allows you to zoom in on an area by dragging a marquee with the the right mouse button. This could also be used in other scripts, for example a rendered frame buffer.

How to use the script

Make a copy of your scene and render all static elements and save the rendered background file. Start MonsterMatte and use HitchHiker to navigate to the rendered file and select it. Press ‘Create Shader’. The matte material will be created and added to the designated material slot.

Apply the material to anything in the scene that isn’t animated and render – watch how fast it goes!


To install, just download the script below and copy the folders to the root of your max installation. You’ll see the macroscript listed under the ‘LoneRobot’ category and is called MonsterMatte. It’s not really a monster tool, but unfortunately when I was writing this article that monster mash song kept going round my head, damn pesky irritating brain.

Merry Xmas!


You can tell by the way I walk I’m a woman’s man

Nov 2, 2010 by     1 Comment     Posted under: 3dsMax, Maxscript, Rigging, Technical Research

zfOn the last few projects I have used a system that I developed as part of Lonerobot R&D. It’s a walk system called Zimmerframe that is targeted at optimising animation production. The reason I decided to develop something like this was simple – when working as an animator on production, there were many times, especially when I was up against the clock on my weekly allocation that I wanted to eliminate the time it took setting up characters into position in a shot. This was so I could get on with actually animating the performance!

My logic was that there are a multitude of what I call ‘bread-and-butter’ entry and exit shots where characters need to walk in or out at the start and end of shot. On a production, you want to have the walk standardized as much as possible so it struck me that if you stored the distance information of a walk cycle to XML, you could automate the translation part of the animation depending on how many steps you are taking. On a walk it’s pretty much set in stone if you want to avoid foot slip and I also wanted a more precise way of calculating these walk around curved motion paths too.

This was certainly not designed to remove any of the inherent character from the walk cycle – These can be animated however the animator originally wished, except that the actual cycle must be on linear keys to provide a constant speed to the step cycle.

My solution involved adding a helper object – the zimmerframe dummy which stored the XML preset walk data in a parameter block.

Zimmerframe has a small collection of extras to augment it’s operation on this production – A walk cycle maker, A walk browser featuring a custom XML dotnet interface, and footlocker, a simple but useful way of fixing any foot slip around tight corners.

The key to this system is it’s simplicity. However, by combining different walk and run cycles you can build a complex movement animation in a very short amount of time – leaving you to spend the time making the acting good!

Featured here are some screen grabs I made to help the animators on our last production use the system. They are in quicktime mp4 format. I hope it goes some way to explaining how the system works and the ease by which it fits into the max UI and program flow.

1 – Setting up Macros

2 – Setting up a linear run

3 – Removing the helper

4 – Setting up a curved walk

5 – Creating a new walk cycle

6 – Correcting foot slip around corners

Floating Dialog Amnesty

Mar 16, 2010 by     9 Comments    Posted under: 3dsMax, DotNet, Maxscript, Tips and Tricks


Ack, not another one of those posts where the only picture is a p*ss poor dialog with some stolen twinkly twink icons. My old art teacher would be despairing right now, and perhaps comforting himself by grinding a yellow ochre paste. Oh, how he loved that yellow ochre.

A situation existed recently where between working on the train and at work, I’d lost some dialogs on the screen space outside the resolution of my laptop monitor. A few of 3ds max’s window locations are stored in the ini file.

inipath = getMAXIniFile()
setINISetting inipath “MtlEditorPosition”  “MainWindow” “0 0 375 734”
setINISetting inipath “RenderVFBPosition”  “Position” “0 0”
setINISetting inipath “MaterialBrowserDialogPosition” “Dimension” “0 0 340 600”
setINISetting inipath “EnvironmentDialogPosition” “Dimension” “0 0 350 580”
setINISetting inipath “RenderDialogPosition” “Dimension” “0 0 360 750”
setINISetting inipath “mentalrayMessagesPosition” “Dimension” “0 0 600 400”
setINISetting inipath “RenderPresetsCategoryDialogPosition” “RenderPresetsCategoryDialogDimension” “0 0 210 280”

But most of the useful ones are not – like the layer manager and curve editor.

Unfortunately, you cant set their position like you can a rollout floater – it meant it was time for the programmatic equivalent of James Herriot putting on a large rubber glove.

Windows has a wealth of useful functions in the API that aren’t yet exposed to the dotnet framework – well not out the box, there is a managed API but I haven’t looked into this yet . However this doesn’t mean you cant use native API calls in a managed code environment. You can declare an API call in a dotnet assembly, and with a little coercion, get it working as you want.

pinvokelogoThe place to look for Win32 and API related stuff is This has loads of info about how to call these windows functions via the managed dotnet framework

From the site itself-

“The term PInvoke is derived from the phrase “Platform Invoke”. PInvoke signatures are native method signatures, also known as Declare statements in VB”

After looking on this site, I found the methods i was looking for.

  • GetWindowRect – Gets the size of the window from the window handle
  • GetWindowPlacement – Gets the current window location from the window handle
  • MoveWindow – Moves the window to a screen location

All that was need now was to convert these signatures into a format string ready to compile directly from 3dsmax, so that I could call these win32 functions from a dotnetobject within 3dsmax, and hopefully retrieve my missing windows.

When you compile a dll directly in 3dsmax, you are taking the code that you would write within the Visual Studio window and sending it to the compiler manually. So the flavour of managed language you are using dictates the compiler type.

The key to this class is to set up some custom properties so that the data coming out when used in 3dsmax is not too weird –  it is logical to return a drawing.point for a location, and a drawing.size for a window size for instance.

Getwindowrect uses a RECT structure (a structure is similar to a class) but creating one in max can be tricky. Mike Biddlecombe on CG Talk had posted a method to instantiate a structure in 3dsmax by adding it into an array and retrieving the first member. That was brilliant stuff and typical of the sort of stuff that Mike is figuring out the whole time. I decided in this case, that since I was compiling an assembly anyway, i’d just handle all of the structure business there and avoid any potential banana skins. So I added a width and height property to the RECT structure definition, meaning after it was instantiated, you can call the method from PInvoke and return a structure that has standard properties. Planning return values in your classes is something that helps in the long run – It makes deployment much easier in Max. I have found that since the VS interface is easier to debug, it’s a good place to do as much as you can there.

fn DialogWindowOpsClass =
source = “”
source += “Imports System.Runtime.InteropServicesn”
source += “Imports System.Drawingn”
source += “Public Class DialogWindowOpsn”
source += “Public Structure RECTn”
source += “Public left As Integern”
source += “Public top As Integern”
source += “Public right As Integern”
source += “Public bottom As Integern”
source += “Public ReadOnly Property Width() As Integern”
source += “Getn”
source += “Return right – leftn”
source += “End Getn”
source += “End Propertyn”
source += “Public ReadOnly Property Height() As Integern”
source += “Getn”
source += “Return bottom – topn”
source += “End Getn”
source += “End Propertyn”
source += “End Structuren”
source += “Public Structure POINTAPIn”
source += “Public x As Integern”
source += “Public y As Integern”
source += “End Structuren”
source += “Public Structure WINDOWPLACEMENTn”
source += “Public Length As Integern”
source += “Public flags As Integern”
source += “Public showCmd As Integern”
source += “Public ptMinPosition As POINTAPIn”
source += “Public ptMaxPosition As POINTAPIn”
source += “Public rcNormalPosition As RECTn”
source += “End Structuren”
source += “<DllImport(“user32.dll”)> _n”
source += “Public Shared Function MoveWindow(ByVal hWnd As System.IntPtr, ByVal x As Integer, ByVal y As Integer, ByVal nWidth As Integer, ByVal nHeight As Integer, ByVal bRepaint As Boolean) As Booleann”
source += “End Functionn”
source += “<DllImport(“user32.dll”)> _n”
source += “Public Shared Function GetWindowRect(ByVal hWnd As System.IntPtr, ByRef lpRect As RECT) As Booleann”
source += “End Functionn”
source += “<DllImport(“user32.dll”)> _n”
source += “Public Shared Function GetWindowPlacement(ByVal hWnd As System.IntPtr, ByRef lpwndpl As WINDOWPLACEMENT) As Booleann”
source += “End Functionn”
source += “Public Function WindowSize(ByVal Hwnd As System.IntPtr) As System.Drawing.Sizen”
source += “Dim LPRECT As RECTn”
source += “GetWindowRect(Hwnd, LPRECT)n”
source += “Dim WinSize As System.drawing.size = New System.drawing.size(LPRECT.Width, LPRECT.Height)n”
source += “Return WinSizen”
source += “End Functionn”
source += “Public Function WindowPosition(ByVal Hwnd As System.IntPtr) As System.Drawing.Pointn”
source += “Dim intRet As Integern”
source += “wpTemp.Length = System.Runtime.InteropServices.Marshal.SizeOf(wpTemp)n”
source += “intRet = GetWindowPlacement(Hwnd, wpTemp)n”
source += “Dim WinPoint As System.drawing.point = New System.drawing.point(wpTemp.rcNormalPosition.left,”
source += “Return WinPointn”
source += “End Functionn”
source += “End Class”

VBProvider = dotnetobject “Microsoft.VisualBasic.VBCodeProvider”
compilerParams = dotnetobject “System.CodeDom.Compiler.CompilerParameters”
compilerParams.ReferencedAssemblies.add “C:WindowsMicrosoft.NETFrameworkv2.0.50727System.drawing.dll”
compilerParams.GenerateInMemory = on
compilerResults = VBProvider.CompileAssemblyFromSource compilerParams #(source)

— this is very useful to debug your source code and check for referencing errors
if (compilerResults.Errors.Count > 0 ) then
errs = stringstream “”
for i = 0 to (compilerResults.Errors.Count-1) do
err = compilerResults.Errors.Item[i]
format “Error:% Line:% Column:% %n” err.ErrorNumber err.Line
err.Column err.ErrorText to:errs
MessageBox (errs as string) title: “Errors encountered while compiling VB code”
return undefined
return compilerResults.CompiledAssembly.CreateInstance “DialogWindowOps”

What this code does is make a class that can be instantiated in max and used to control the positions of the dialogs in the 3dsmax user interface, whether we can see them or not.

In order to call them though, we need an array of handles for the active max dialogs. Fortunately, Maxscript gives us a way of doing this –


The rest, was building an interface. I went for the datagridview – Its a complex user control and I am probably only scratching the surface of the sort of things you can do. Its designed for handling masses of complex data from data sources like sql databases but you can just use it like a listview. Its just got a few more options.

Maxscript also has methods for interacting with win32 functions, so it’s entirely possible that this could be achieved using that, but then that’d just be cheating, wouldn’t it?