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.

GDI+ Methods – The DotNet Paint event

Oct 28, 2008 by     2 Comments    Posted under: 3dsMax, DotNet, Imaging

In order to use GDI+ to draw vector based shapes, outlines and objects in DotNet, one option available is to use the paint event.

I have been working on some GDI+ functions that can dynamically generate icons without the need for bitmaps. The idea of this came from a post on CGTalk some time ago by Dave Stewart who previewed a similar idea based around MXS and setpixel methods if i remember rightly. I wanted to write a dotnet equivalent. I am trying to include the sort of icons you use on dialogs for generic actions like arrows, numbers, pickbuttons etc.

One nice thing is using translatetransform() within the graphics surface, that way you can draw the icon in one position and then pass an angle in the function and it draws it pointing the correct direction. this simplifies things greatly and means you have one function for all arrow icons.

With the same thing in mind, the plus icon also becomes the cross icon with translatetransform, as does the square when it becomes the diamond. The numbers adjusts the font size to the largest possible according to the number of digits.

Each paint event function passes an outline colour and a fill colour along with the graphics surface so you can adapt the icons to whatever scheme you like, including the current 3dsmax one.

There are a few options with a GDI+ drawing surface when it comes to how you perform the drawing itself. I decided to opt for the graphicspath object method as you are able to treat it as a closed entity. This means you can fill it with colors, stroke the outline and apply linestyles and edge joins. Constructing the shape from individual lines always treats them as individual lines and not the overall shape. In order to make the corners sharp on a graphicspath object, one option in this heavilly overloaded function is to pass a byte array. I’m still not sure why this is neccesary, except for the fact that it doesn’t work properly without it. I think it must be something to do with how the corners are rendered.

I set up a loop to build the dotnet arrays in max, remember that dotnet arrays start at 0, not at 1 like in Maxscript.

-- specify an array of points

PlusGDIArray = dotNetObject "System.drawing.point[]" 12

-- and a byte array of the same size

ByteArray = dotNetObject "System.byte[]" 12

-- store the points you want to place in the array

local pointloop = #((pt1 = dotNetObject PointClass 8 2),
(pt2= dotNetObject PointClass 8 8),
(pt3= dotNetObject PointClass 2 8),
(pt4 = dotNetObject PointClass 2 16),
(pt5 = dotNetObject PointClass 8 16),
(pt6 = dotNetObject PointClass 8 22),
(pt7= dotNetObject PointClass 16 22),
(pt8 = dotNetObject PointClass 16 16),
(pt9 = dotNetObject PointClass 22 16),
(pt10 = dotNetObject PointClass 22 8),
(pt11 = dotNetObject PointClass 16 8),
(pt12 = dotNetObject PointClass 16 2))

-- and apply them into the dotnetarray
for i = 1 to pointloop.count do PlusGDIArray.setvalue pointloop[i] (i-1)

--repeat for the byte array
local byteloop = #((Byte1 = dotNetObject ByteClass 0),
(Byte2= dotNetObject ByteClass 1),
(Byte3= dotNetObject ByteClass 1),
(Byte4 = dotNetObject ByteClass 1),
(Byte5 = dotNetObject ByteClass 1),
(Byte6 = dotNetObject ByteClass 1),
(Byte7= dotNetObject ByteClass 1),
(Byte8 = dotNetObject ByteClass 1),
(Byte9 = dotNetObject ByteClass 1),
(Byte10 = dotNetObject ByteClass 1),
(Byte11 = dotNetObject ByteClass 1),
(Byte12 = dotNetObject ByteClass 129))
for i = 1 to byteloop.count do ByteArray.setvalue Byteloop[i] (i-1)
-- finally add both arrays to a graphicspath object
ClosedPath = dotnetobject "System.Drawing.Drawing2D.GraphicsPath" PlusGDIArray ByteArray

download script

Delving into the DotNet SDK – A timechange callback via Dotnet

Oct 28, 2008 by     No Comments    Posted under: 3dsMax, DotNet, Technical Research

A quick look into the dotnet SDK shows a few useful classes that one can use. Over time as I discover what some are for I will hopefully be able to document some of them. One class that stuck out to me was the ManagedServices.AnimationFrameChangeListener class. After a quick inspection of the methods and properties I was able to work out how to enable this and get it talking to max – namely calling a function when the timeslider was scrubbed back and forth.

The example is just to show it working, it updates a label control on the dialog with the current frame. However, knowing that this is possible from dotnet is interesting, as in the future it might be possible to use it to bind max controllers to dotnet objects and controls. Whether it is any better or worse that the current timechange callback that MXS implements is something I’m yet to figure out. The way i see it is that it’s been put there for a reason, I’m sure a use can be found!

global DotNetTimeChangeCallback

rollout DotNetTimeCallback "DotNet Time Callback" width:194 height:70

fn testcallback =
DotNetTimeCallback.framelbl.text = "Current Frame - " + currenttime as string

button btnaddCB"Add Callback" pos:[3,3] width:92 height:40
button btnremoveCB "Remove Callback" pos:[99,3] width:92 height:40
dotnetcontrol framelbl "label" pos:[4,46] width:186 height:21

on DotNetTimeCallback open do
framelbl.borderstyle = (dotNetClass "System.Windows.Forms.BorderStyle").fixedsingle
framelbl.backcolor = (dotnetclass "System.Drawing.Color").slategray
framelbl.forecolor = (dotnetclass "System.Drawing.Color").black
framelbl.textalign = (dotnetclass "System.Drawing.ContentAlignment").middlecenter
framelbl.text = "Current Frame - " + currenttime as string

on btnaddCB pressed do
if DotNetTimeChangeCallback == undefined do
DotNetTimeChangeCallback= dotnetobject "ManagedServices.AnimationFrameChangeListener"
dotNet.addEventHandler DotNetTimeChangeCallback "AnimationFrameChanged" testcallback
framelbl.backcolor = (dotnetclass "System.Drawing.Color").orangered
framelbl.forecolor = (dotnetclass "System.Drawing.Color").yellow

on btnremoveCB pressed do
dotNet.removeEventHandler DotNetTimeChangeCallback "AnimationFrameChanged" testcallback
DotNetTimeChangeCallback = nothing
framelbl.backcolor = (dotnetclass "System.Drawing.Color").slategray
framelbl.forecolor = (dotnetclass "System.Drawing.Color").black


createdialog DotNetTimeCallback

TrackBot – A Max Custom UI Control for Characters

Oct 27, 2008 by     No Comments    Posted under: 3dsMax, Characters, DotNet, Technical Research, User Controls

This control was made to try and build some UI shortcuts when using morph controls on a character. I wanted to be able to add a control that gave me the ability to reset, nudge and scrub the morph values. In the past I would have to build each button control in MXS and make sure they all worked.

With this control, I am hoping it will work well enough to use in a facial rig setup.

There are a few extras with this control of use. Firstly, the slider, once focused can be moved by rolling the mouse wheel. This makes it good for fine tuning expressions. And secondly, the label color can be changed so that you can visually group expressions together by type. The nudge buttons can be used with shift and control to nudge values of 10 and 5 respectively. I will add a property to set these soon, as you may wish to specify max and min values smaller than this.

The buttons are drawn with GDI+, with the exception of the key icon, which is embedded as a resource in the control. I would like the button images to fill with the control color eventually too.

Apart from the Valuechanged event, there is a setkey event that is fired every time – yup, you guessed it – the setkey button is pressed.

Here is the class diagram so that you can see all the properties and events-

  • BarColor – The color of the right hand side of the slider
  • ElapsedColor – The color of the left hand side of the slider
  • ResetValue – Specify a value for reset, in case your slider goes negative for example.
  • Title – The text on the control
  • LabelColor – the text background

the slider is a C# control from CodeProject. I have used it because it implemented the great mouse wheel functionality. I have mirrored some of the properties in the control to allow the user to change the slider ui colors. I am currently trying to convert the source to VB so that i can embed it within the control. You can see the article here

One thing i have noticed with running dotnet controls in the max command panel is panel seems to refresh after the controls, and subsequently makes them dissapear. The only way to get them back is to drag the command panel out and in again. While i am not exactly sure why this is happening, it can be fixed in a slightly hacky way by placing a timer with an interval of 1 in the rollout which ticks once, invalidating the controls via the refresh() method, like so –

rollout Trackbot "" width:170 height:249
dotNetControl TBslider1 "LoneRobot.Trackbot" pos:[1,3] width:156 height:54
dotNetControl TBslider2 "LoneRobot.Trackbot" pos:[1,61] width:156 height:54
dotNetControl TBslider3 "LoneRobot.Trackbot" pos:[1,119] width:156 height:54
dotNetControl TBslider4 "LoneRobot.Trackbot" pos:[1,174] width:156 height:54
timer refresh "" interval:1

on Trackbot open do = true

on refresh tick do
TBslider4.refresh() = false



Hello again, In my recent article about ColorMatrix, I included an update to my TrackBot slider assembly. Previously, it had included a C# slider component that I handled via a custom event. Using this method can be really useful, as you can make a composite control of many other existing elements. The UI is still the same although I changed the appearance of the slider slightly. (In the picture below, I’ve hidden the lower controls by making the UI height of the dotnetcontrol smaller)

Click the image if you want to read the ColorMatrix article, and get the code for Image adjustments in Max.

I’ve updated the assembly with a VB conversion of the slider component, which means that it is now integrated into one assembly. I’ve changed the way the event is triggered also, so let me know if it doesn’t function as you expect. As usual, it is a download below.

download script

BotNet Framework – Packing dotnet objects into functions for ease of use

Oct 26, 2008 by     No Comments    Posted under: 3dsMax, DotNet, Maxscript, User Controls

I wanted to script a library of functions to help me set up certain DotNet UI objects without having to type everything out each time i wanted to use them. I obviously could not resist the pun either. One of the methods of setting up a DotNetControl in 3DS Max in order to have a cohesive UI look you need to set multiple properties for each. Please note this is very much a work in progress. If you feel the need to add a function to this please do so, but be sure to let me know so that I can update it!

For example, to setup a button with a see-through background image, you can call the following botnet function –

fn imagebutton btn image style bgcolor mobg mdbg tcolor:undefined =
-- tcolor is transparent color to be passed to the imagelist
btn.backcolor = colorpreset bgcolor
btn.flatstyle = style
btn.FlatAppearance.MouseOverBackColor = colorpreset mobg
btn.FlatAppearance.MouseDownBackColor = colorpreset mdbg
if tcolor != undefined then (btn.imagelist = (imagelist image invisible:tcolor)) else (btn.imagelist =(imagelist image))
btn.imageindex = 0
image = nothing

The variable tcolor is passed to an imagelist control object and used as the mask color. In dotnet you can pass an image as the button background color, but in order to get a transparent image, you need to pass an imagelist.

Note that the easy way to do this is to use a 32 bit PNG for the button image. Windows will recognise the transparency information and render the button correctly. However, if you only have a 24 bit image, this method is the way, so that you can supply the background color as a matte.

When placed within the on open event of the rollout, the code looks like this. Note i am using an image with a white background in my scripts folder called “addremoveicons.bmp”

rollout DNimagebuttons "BotNet Button Methods()" width:190 height:220
dotNetControl btnclose "button" pos:[9,9] width:80 height:45
dotNetControl msgbox "button" pos:[9,70] width:175 height:45
dotNetControl msgbox2 "button" pos:[9,125] width:95 height:85

on DNimagebuttons open do
BotNet.imagebutton btnclose ((getdir #scripts) + "addremoveicons.bmp") flat #maxback #maxactive #crimson tcolor:#white BotNet.imagetextbutton msgbox "A Message Here" 8 ((getdir #scripts) + "addremoveicons.bmp") flat #slategray #maxactive #limegreen tcolor:#white fontcolor:#red textalignment:botnet.bnfalign.BottomRight imagealignment:botnet.bnfalign.TopLeft BotNet.imagetextbutton msgbox2 "or Here" 8 ((getdir #scripts) + "addremoveicons.bmp") flat #maxback #yellow #limegreen tcolor:#white fontcolor:#purple textalignment:botnet.bnfalign.BottomCenter imagealignment:botnet.bnfalign.TopCenter

on msgbox click do
local msgstring = "Waheeeey, a funky dotnet button"
Botnet.messagebox " LoneRobot.BotNetFramework" msgstring

createdialog DNimagebuttons style:#(#style_toolwindow, #style_sysmenu)

Depending on which alignment option you provide can yield a variety of different button styles. I have set the button style to flat, i just prefer this but you could change it to system buttons should you prefer. At the moment it is limited to these UI functions, i hope in the future to add many more.

download script

you will need this logo in your scripts directory if you want to use the BotNet MessageBox function. You can obviously replace this with another image but if you want, right click this one and download…