Tagged with " homepost"

Autodesk Webinar

Apr 10, 2012 by     No Comments    Posted under: DotNet

Despite a nagging suspicion that “Webinar” is a made up word, I will be speaking with my colleague Ben Robins about some of our work at Nexus and how 3dsmax features in our animation pipeline.

Details can be found here :

Jamies Jewels

Autodesk site

Two Clicks From Amsterdam

Oct 2, 2011 by     2 Comments    Posted under: 3dsMax, Characters, DotNet, Maxscript, Tips and Tricks

End User Event Roundup



Yes it’s a little on the late side. Due to me starting a new job on the following Monday at Nexus in London my EUE write up has been a little delayed.

So what is EUE? If you don’t already know, it’s a 3d event that has been going for a few years now that allows like-minded 3d enthusiasts and proffesionals to all get together and attend talks. This is all great, but the cherry on the top is when you realise that it’s hosted in a PUB in Utrech.


It was an absolute honour to be asked to speak there this year. There were too many great people that I met to thank them all, but a special mention goes to Bobo, Rune Spaans and Ted Boardman (Both just about the nicest people you’d ever meet in the 3d industry) and to the people I hung out with over the weekend – Johan Boekoven, Yoni Cohen and Gonzalo Rueda. Also, massive thanks extend to Joep Van Den Steen and Michiel Quist who organised the whole thing, and of course to Jamie and Shane from Autodesk who were all great guys.

My talk at EUE was to not just showcase a few of the animation systems that I have written over the last few years, but to get across  the many potential stages of development for tools. Whether you are writing a line of code to make your life easier or trying to establish a pipeline, good tools will persist and ill-thought out ones will not last even one production. There is no correlation between the complexity or the number of lines of code in the success of a tool. Your animators will decide!

MaxScript Lesson Ahoy!

One of the tools that I have tended to re-use over many productions is the LayerControl script. It allows animators to bypass the layer manager and hide layers according to object type. While there are a few ways of achieving this on a node level using AppData or UserProps, you can quickly setup a system like this with a methodical and consistent naming convention. When working, hiding and showing rig controls and meshes on masse become a simpler and quicker affair. This might not sound much, but over a long animation process tools like this save time.

Lets look at how to set this system up.

Firstly, at the rigging stage, you will want to make sure that all of your characters have consistent layer names.

It is the suffixes of these layers that the script will be using to control whether a layer is hidden or not.

Script Stage 1

-- Basic Layer Visibility Control

( local str = “MESH” local ishidden = true for i= 1 to (LayerManager.count-1) where ( (dotnetobject “system.string” (LayerManager.getLayer i).name).endswith str ) do (LayerManager.getLayer i).ishidden = ishidden )

This is a basic piece of code that will hide any layer with the suffix “MESH”. As it stands, its not really useful for anything except to illustrate the code of the script. Note that this has been formatted this way in order to make it clearer, you could add it all into one line. Using where in a loop is a useful trick. In this case, it allows us to perform the hiding of the layer without having to collect the Mesh layers into an array and iterating that. You’ll notice I use a dotnet string method in this. You could just as easily use :

matchpattern (LayerManager.getLayer i).name pattern:(“*”+str)

The dotnet method might be fractionally slower to execute, but since we’re talking a few milliseconds, it’s not going to make a whole lot of difference. I included it to illustrate the dotnet string obejct. While maxscript string methods are great, there are even more methods available to you via the dotnet methods should you need it. Use whichever one you like, it’s not going to make the final script any better or worse. If you’re just getting into scripting and programming, you’ll find a lot of the time you’ll want to make sure the core of the script is working before fiddling around with the UI.

Script Stage 2

The first script was just to establish how the method for hiding and unhiding will work.  The next version builds a basic UI and starts to add the functionality we want. It now works by calling a function that takes two arguments. For novice scripters, a function is something used widely in programming to represent and operation that you will want to re-use multiple times. As a form of shorthand, you just call the function name and pass the values it requires rather than typing the same code out repeatedly. In function calls the extra values are known as arguments. In this case, the suffix string that we want to hide/unhide is the first argument. The second argument is to decide whether the function will hide or unhide the layer. Since this is an either/or type, we use a boolean argument of true or false. So in this code we use a button click to hide the layer (passing the string and the value true) and the button’s right click handler to pass the same string and false to unhide it.

try(destroydialog HideRigs)catch()

rollout HideRigs "" width:73 height:84
	fn LayerVisibiltyBySuffix str ishidden =
	for i= 1 to (LayerManager.count-1) where ((dotnetobject "system.string" (LayerManager.getLayer i).name).endswith str) do ((LayerManager.getLayer i).ishidden = ishidden)

	button btnRIG "Rig" pos:[2,56] width:67 height:23	border:false
	button btnMESH "Mesh" pos:[3,30] width:67 height:23 border:false
	button btnCTRLS "Controls" pos:[3,3] width:67 height:23 border:false

	on btnRIG pressed do LayerVisibiltyBySuffix "RIG" true
	on btnCTRLS pressed do LayerVisibiltyBySuffix "CTRLS" true
	on btnMESH pressed do LayerVisibiltyBySuffix "MESH" true

	on btnRIG rightclick do LayerVisibiltyBySuffix "RIG" false
	on btnCTRLS rightclick do LayerVisibiltyBySuffix "CTRLS" false
	on btnMESH rightclick do LayerVisibiltyBySuffix "MESH" false	

createdialog HideRigs

Script Stage 3

Stage 3 has some improvements in the form of replacing the max controls with some dotnet controls. Have read of the code and I’ll discuss what s going on afterwards.

macroScript ShowHideLayers
toolTip:"Show Hide Layers"
	try(destroydialog HideRigs)catch()

	rollout HideRigs "" width:84 height:324
		local DotNetColorMan = (dotnetclass "managedservices.cuiupdater").getinstance()
		local MlbSelection = #("Angus", "Big_Pig", "Bo", "Cow", "Cowhand_One", "Cowhand_Two", "Crow", "Farmer", "FarmGirl", "Hebaa", "Leonard", "Mini", "Piggy", "Shebaa", "Trinny", "Trotski", "Unicorn", "Winnie")

		fn LayerVisibiltyBySuffix str lbx ishidden =
		if 	lbx.selection.isEmpty then
				for i= 1 to (LayerManager.count-1) where ((dotnetobject "system.string" (LayerManager.getLayer i).name).endswith str) do ((LayerManager.getLayer i).ishidden = ishidden)
				enableaccelerators = true
				for each in lbx.selection  do
					local lay = LayerManager.getLayerfromname (lbx.items[each] +"_"+ str)
					if lay !=undefined then lay.ishidden = ishidden
					enableaccelerators = true

		fn MouseButton args = dotNet.compareEnums args.button (dotnetclass "System.Windows.Forms.MouseButtons").left

		dotNetControl btnRIG "button" pos:[1,56] width:82 height:23
		dotNetControl btnMESH "button" pos:[1,30] width:82 height:23
		dotNetControl btnCTRLS "button" pos:[1,3] width:82 height:23
		Multilistbox lbxCh "" pos:[1,82] width:82 height:18 items:MlbSelection

		on HideRigs open do
		btnRIG.flatstyle = btnMESH.flatstyle= btnCTRLS.flatstyle =(dotNetclass "System.Windows.Forms.FlatStyle").Flat
		btnRIG.backcolor = btnMESH.backcolor= btnCTRLS.backcolor = DotNetColorMan.GetControlColor()
		btnRIG.forecolor = btnMESH.forecolor= btnCTRLS.forecolor = DotNetColorMan.GetTextColor()
		btnRIG.text = "Rig"
		btnMESH.text = "Mesh"
		btnCTRLS.text = "Controls"

		on btnRIG mouseDown sender args do LayerVisibiltyBySuffix "RIG" lbxCh (MouseButton args)
		on btnMESH mouseDown sender args do LayerVisibiltyBySuffix "MESH" lbxCh (MouseButton args)
		on btnCTRLS mouseDown sender args do LayerVisibiltyBySuffix "CTRLS" lbxCh (MouseButton args)

		on lbxCh rightclick do lbxch.selection = #{}

createdialog HideRigs

You’ll see that there is only one handler for each button. So you may be wondering how it passes the required true/false argument with just one call. This is one reason why we use  dotentcontrols instead of max UI controls. When you click a dotnetcontrol you can get some additional provided about which mouse button you have used. This is contained within the args property. The mousedownevent calls a function before passing it’s final argument. This function returns the boolean argument we need. So before handling the mousedown event, it asks this function a question:

fn MouseButton args = dotNet.compareEnums args.button (dotnetclass "System.Windows.Forms.MouseButtons").left

this translates in plain speech to “is the mouse button clicked the left button?”. We use dotnet.compareenums to return either true or false in answering a comparision of the mouse button used to click each button and the enumeration of the left button. Dotnet uses enumerations to simplify data types rather than expecting the user to identify an arbitrary integer code.

The other addition to this script is to add a listbox of character names. These names are hardcoded into an array at the start of the script. You could easily scan a set of character folders and retrieve these names dynamically. You just need to make sure you are consistent with naming or the system will break down.

Script Stage 4

From now on, I wont be posting the entire code for each example, but highlighting the important parts of the new code. Don’t worry though, full code samples will be provided at the end of the post so you’ll be able to see what I mean.  Stage 4 brings in base64 encoded strings to store button bitmaps. There’s no point me going into this as i’ve posted about this before here. In normal circumstances, the base64 struct would be added to the startupscripts as a separate entity so that it executes only once on maxstart.

We have also ramped up the modifier key functionality. I’m personally a fan of having multiple functions on a single button – purely for the fact that you keep the UI as small as possible. There’s nothing worse than cramming a UI with extra buttons that could be easily passed to a shift click variation of the same button. In the case of the layer control, its critical to keep the footprint as small as possible and just add a button with If somebody doesn’t want to use a shft click or ctrl click then they won’t. If they do, you’ve already got the functionality in there. You can’t lose really. I’ve always considered that you’ve memorised a whole load of keyboard shortcuts up to this point, it wont hurt to memorise a couple more. The key is to make them match design patterns that already exist in the software. So if you have shift click to select all the objects in a particular layer, then shift + ctrl click should add these nodes to the current selection, exactly how max appends a ctrl select. It’s just a way of keeping things consistent.

So this list of modifer key functions are as follows –

Left Click – Hide Layer

Right Click – Unhide Layer

Shift Click – Select Nodes on layer

Shift Ctrl Click – Add nodes to selection

Shift Double Click – Isolate layer node selection

Alt-Left Click – Freeze layer

Alt-Right Click – Unfreeze layer

Ctrl – Alt Click – Perform an inverse action – i.e. If one character name is selected, do the hide/unhide action on all other layers EXCEPT the selected one.

Too many? my logic is if they are overkill, people won’t use them. Bear in mind all of the permutations above were as a result of animators asking for them over the course of many productions!

Script Stage 5

Okay, final polish time here. The dotnet multilistbox has been changed to ownerdraw mode. This is more advanced dotnet stuff but it means that you can take control of the appearance of a dotnetcontrol in ways where the original appearance properties do not do what you require.

on dnlbx DrawItem sender args do
			if (dotNet.compareEnums args.state (dotnetclass "DrawItemState").Selected) then
				-- draw the selected state of the listbox
				args.Graphics.FillRectangle selBrush args.bounds -- background colour
				args.Graphics.DrawString dnlbx.Items.Item[args.index] args.font brushes.purple  args.bounds.location.x args.bounds.location.y -- draw the text
				args.DrawBackground() -- this is an inbuild call to draw the default background
				args.Graphics.DrawString dnlbx.Items.Item[args.index] args.font uTextBrush args.bounds.location.x args.bounds.location.y -- draw the text string
			--	args.DrawFocusRectangle() -- draw the focus rectangle last

The comments should explain what is going on.


So that’s it for my EUE talk roundup. I hope you’ve been able to get something out of it. If any part makes someone want to start coding useful tools to help their company productions then it’s all been worthwhile. For anyone reading this that attended my talk, thanks for turning up and not throwing anything. If you have any questions, feel free to contact me.

All versions of the script are available to download below.

If you want a PDF of the visual slide material that I displayed in the background, you can find it here


SpeechBot – A handy script to load and save morpher keys

Jun 20, 2011 by     3 Comments    Posted under: 3dsMax


Yakkety Yak, It is always said that careless talk can cost lives, but careless talk in production costs you time. Perhaps that is why they call them deadlines.

For many years, I have used the same system to deal with lipsync within production, and it is with great pleasure that I release it to the community now. This script is called Speechbot, and will take a character that uses a morph modifier for lipsync and save the data to XML. This can then be re-applied at a later stage.

My approach for years and many productions has been to lipsync everything before you begin animation (which is hardly ground breaking), but it does make most sense to do this first as it doesn’t interrupt the animation workflow. Over the years, this has worked well with all sizes of productions as you can assign the lipsync to junior animators who want to get an idea of the production before jumping in at the deep end. It also allows you to standardise the output as the people doing it become more familiar with the characters and you don’t get individual animators doing their scenes differently. Generally the lipsync at this stage is to provide the sense of the speech phonemes/visemes and not necessarily the facial performance so the artist is still free to adjust the keys and push the expressions further when they animate to the scene audio.

Being able to save the files to XML has helped on the larger productions too, as we have been able to outsource the lipsync to another animation company who only need to email their work to us at the end of each episode in the form of small XML data files. This is the only thing we have to worry about synchronising. We also worked with two other external companies who were doing animation. Again, they could be emailed the lipsync for their scenes and everything was cohesive and organised.

Why don’t you just use max’s Load/Save animation?

That’s a simple one – excluding the fact that load/save animaition is barely scriptable, it just doesn’t work reliably enough. In the dark, Pre-Speechbot days we tried using it and found that max would ask us to infrequently remap controller tracks when loading and saving to the same object in the scene! This behaviour was odd and rather inconvenient, but did force me into writing something that is targeted specifically to character lipsync. It was only recently when this exact issue was brought up by Martin Briedt on the Beta forum that I thought that others were finding this issue frustrating too.

Who can use this script?

People wanting to save and restore character lipsync in the form of morph data.

In fact, it can save any morph data from any object. Speechbot stores each channel incrementally, along with value data and custom key tangents. So if you base all of your characters along the same structure (ie the morh channels are the same shape target and in the same order) you can load the same lipsync or morph data onto different characters.

Who can’t use this script?

With the exception of Piers Morgan and N-Dubs, Nobody. Well, if you have a bone based facial system, you can’t. But since you can still set up a perfectly reasonable morph driven character mouth, it’s designed specifically for those types. I will probably have to develop a new system in the future that takes in the ability to store TM data from facial nodes as well as lipsync but that’s for another time.

Using the Interface

Speechbot is a macroscript, so you’ll have to drop it into your UI/Macroscripts directory. If you don’t know how to do this then you’ll have to look outside this article (there are many tutorials on how to install macroscripts but here is one)













The workflow for using Speechbot is simple –

  1. Select an object with a morpher modifer
  2. click the save button




That’s it!

When you are ready to load the data back onto the character, select the object with the morpher modifier, choose the XML file and press load





Object filter

SB_f The interface has a dropdown list of all objects in the scene with morpher modifiers applied to them, so you can pick from this dropdown rather than physically selecting it. There is a textbox where you can type common wildcard path searches, like *mouth* so that you only pick up objects with that text in their object name.

This helps if you have numerous morphers in a scene.

Working cache directory

In order to speed up the loading of lipsync data, Speechbot has a working cache directory. When you run the script, you’ll see that it will search a folder where it always looks for XML files to load. This can be set by clicking the folder icon. This stores the location and it always looks for XML files in this location. I have done this because normally an animator has a scene or scenes of characers to animate, and the lipsync XML files can be organised on the server into scenes so that the artist can set the working cache to the folder with their allocated scene at the start of the week and always get the correct files whenever they run.


If you hold the mouse over the button, you’ll be shown where the current cache directory is. If you hold SHIFT and click the button, windows will open an explorer dialog in the working cache location.





Loading/Saving to a location outside of the Working Cache Directory

With either, hold CONTROL down when pressing either load or save and you’ll be asked for a file if you want to load from another location.

Controlling key loading

Speechbot automatically saves all keys, from the entire animation range but has a few options so that you can control how the keys are loaded back onto the object.

Keep Existing Keys

By default, it clears all existing morph keys as normally you will want to replace any lipsync currently on the character, but if you want to avoid this behaviour, then hold down shift before you press load. This will keep the existing keydata. This is useful if you wanted to combine two files together, or load append some previous XML cache to the end of some other morph data.

Specify Load Range


Just as when it saves, Speechbot will restore all the keys over the entire range. However, if you just want to restore a specific time range, you can do so by clicking the Specify Load Time Range checkbox and typing a range in the spinners.



Use Offset


Occasionally, we would bump the audio forward in the curve editor to give us some more time at the start of a scene, so you can offset the key by a specific amount by checking use offset and providing a number.

Appendix – Interesting Scripting Factoids

I often wonder why these sections are called appendices. Is it because usually nobody ever bothers to read it, meaning that like the human appendix, doesn’t actually serve any purpose? (unless you count the abilty to digest grass as a good thing)

Anyway, so that I can give some scripting nuggets to anybody interested to read them, this is the place to find them.

Autonomous resource generation

It’s titles like that which put people off programming, but it’s sort of true. When you first run Speechbot, it contains a struct called LR_SpeechbotStruct. This part of the code checks if the the speechbot directory exists. If it doesnt, it assumes that this is the first time you have run the script and creates the directories and build the png image files that the dialog uses. I does this by way of constructing them from Base64 encoded strings. I wrote about this in a previous article but this is a practical example of how to use this method in a script so that you don’t have to provide any assets along with the code. If it detects that the directory is present, it assumes that the icons have been already created and doesn nothing, so it doesn’t interfere once the script has been run after the first time.

If you want to learn how to use XML to save pertinent data, take a look at the code. Since it is an XML based script, it seemed a bit silly to store dialog data to an ini file, so Speechbot uses an XML ini file. There is a method in the script for doing this so take a look and use it in your scripts too.

Download Speechbot V5

download speechbot

New Script! Scene Bracket

May 21, 2011 by     3 Comments    Posted under: DotNet

I’ve been going through a folder on my hard drive and seeing if there are any useful scripted nibbles that I can release to the community. This is the first, and is a script that has been used in production  for many, many, years.

It simply allows you to set up a pair of frame tags that store the current time frame you are working in. If you need to move the timeslider range to see some other animation keys, you simply press the set button to return to the stored range. This is an invaluable time saver in animation production as ranges change for each shot in a length of scene audio.

The dialog is very small too, meaning it can sit in the trackbar and not clutter up the screen space (see picture above)

Usage –

0 Key jumps the timeslider start point to frame 0

> xxxf key moves the time slider to the start time

< xxxf key moves the time slider to the end time > Set < Returns the animation range to the stored range. By clicking this button, you can replace the stored range to the current frame range.

m click and drag the dialog to a different location.

x closes the dialog

I hope you find it useful, It may not look like much, but many of the animators swear by it!

Photoshop Automation Project Update

Mar 21, 2011 by     No Comments    Posted under: Characters, DotNet, Imaging, Program Automation, Technical Research

Veteran LR.net readers will know that a few years back I published a research project into using managed code to control Photoshop using COM interop. If you are not sure of the article in question, then you can read it here.

It’s been a popular article, so it is with great pleasure that I am now publishing an update. Now that my baby girl is sleeping through the night I’m actually in a position to think clearly about programming again. The new assembly contains a few bug fixes (namely the save option didn’t work properly, oops) and some new methods. The full list can be seen in the class layout at the end of the article.

New methods –














Most of these should be self explanatory. I have chained a few of these new functions into a new automation –


On my latest batch of characters, I was making 2D cut-out style characters based on shots of real people. These PS files were constructed from a green screen shoot of the band Blue Soup.

Blue Soup

In order to get these assets into 3dsMax in order to rig the characters, each layer would have to be selected, pasted into a new document and saved. Then a work path would have to be created and exported. Not a difficult operation, but with four characters (and an average of twenty layers each) is a reasonably time consuming one. Here’s a screen grab of the action working – If you are thinking that the process takes a while on some layers, it’s because the Photoshop file is composed from 6K RAW images shot on a Canon 5D Mk2, and my old dell laptop.

So there you are, a nice time-saving action that is controlled completely via managed code. I invoked this via my test project in VS2008, but it could easily be fired off via 3dsMax itself.

Full class listing –

And as always, download the assembly below –