Browsing"Characters"

SliderMan – Does everything that a slider can

Mar 8, 2015 by     3 Comments    Posted under: 3dsMax, Characters, Maxscript, pipeline, Rigging, User Controls

After many years of rigging in 3dsMax I have to admit I’ve not been a fan of viewport sliders and joysticks. Coming from animation,  I always felt that too much clutter from rigs gets in the way of the character silhouette that you’re supposed to be honing. So I’ve always been a custom attribute squirrel, hiding stuff away in the command panel.

However I was recently rigging some organic tree-like structures, and the rig and the the mesh were such that I felt it was needed to have some form of feedback about the state of the controls twinned with the animation mesh. My rigging sense was tingling – It made me realise that I didn’t have a toolset for making these myself. That’s fine, its not hard to make them, but I thought I’d give myself a couple of train rides to and from work to block something out quickly that I could give back to the community.

So SliderMan is the outcome.

UI

There’s a few options, horizontal, vertical with flipped and centre positioned slider layouts, as well as the traditional joystick setup.

They are resizable (If the option is selected) and there’s some rudimentary alignment tools and a group option to draw a border around them. To resize, just change the width and length properties on the slider border. A joystick has to be square on creation, as the script makes a decision on whether it is a horizontal, vertical or joystick slider on the rectangle dimensions. But the joystick can be changed after creation to a rectangle if needed.

bevel

There’s also a parameter block so you can wire anything you like to them quickly and easily. The values are normalized between 0 and 1 for the default slider, or -1 and 1 for a centered slider.

SliderWire

 

A joystick has two parameters for each direction

JoyWire

That’s pretty much it!

To Use – Just make a rectangle shape that you want to use, or let the script create one for you. There’s a couple of point helpers to handle the resizing and limits, so you can  pop them on a hidden layer in your rig.

Enjoy!

download script

 

 

Two Clicks From Amsterdam

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

End User Event Roundup

 

http://www.enduserevent.com/

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
category:"LoneRobot"
toolTip:"Show Hide Layers"
buttontext:"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
			)
			else
				(
				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
			)
			else
			(
				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.

Conclusion

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

 

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 –

Channels_AlphatoLayerMask()

Channels_DeleteAlpha()

Convert_ImagetoCMYK()

Convert_ImagetoGrayscale()

Convert_ImagetoIndexedColor()

Convert_ImagetoLabColor()

Convert_ImagetoRGB()

Paths_DeleteAll()

Selection_ActivateBottomLayer()

Selection_ActivateTopLayer()

Selection_CreateNewDocumentFrom()

Selection_FromAlpha()

Selection_SelectLayerContents()

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

Automation_CutOut()

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 –

download

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.

RigGUI

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 –

http://lonerobot.net/?p=158

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

Flexible Animation System for Fingers and Hands

Oct 8, 2010 by     No Comments    Posted under: 3dsMax, Characters, DotNet, User Controls

digitui

For many years as a character animator I have wanted a system to aid with the animation of fingers. It’s one of the things that tend to be done as an afterthought when pushed for time on a production, but there is no doubting that some of the nicest animation will always have really great hand gestures. The balance is trying to find the balance between time and ease of animation. If you are really pushed for time, it would be far better that you could store hand poses that you use most of the time, with a view to having a robust toolset to allow you to refine them afterwards.

I recently wrote a system called Digit that goes some way towards addressing this issue in production. The key to this system working well was to provide a UI that was intuitive to the way people currently work in max, and making it minimal and simple enough that it didn’t need too much understanding. This article will hopefully explain the process that I undertook in planning and executing this system.

Planning the control layout

Digit-Roughs

I usually start with some sketches – i have a technical journal that I write ideas and loops into. This over the years (and much to my chagrin) has replaced my sketchbook but it does give me a kind of technical reference for everything. I will scribble UI ideas down before I code anything, as it helps me to plan in advance potential layout conflicts. It is by no means final but it gets me thinking about how everything is going to interact. The thing to remember is that it’s not something you’ll proudly display, but it helps to organise your mind and avoid any UI layout problems along the line. UI design is a hard thing to do, generally people will tell you when it is wrong in a heartbeat and not even notice if it is right. It’s one of those thankless tasks that only you can appreciate.

With Digit I felt I could deviate from the look of the max UI – firstly, square controls were simply not going to cut it, and I wanted to allow control to adjust the layout when running in either a portrait or landscape mode.  The FlowLayoutPanel is built for this task and I’ve used it extensively for my controls up to now. The layout logic was pretty simple, I didn’t want to separate the controls too much, what it gave me was two main panels – the selection interface, and the slider controls.

The critical thing for me in terms of the functionality of the system was that it was as intuitive as possible, and mimicked a lot of the same methods as the native max UI. This was mainly the use of CTRL and ALT to add and remove from selection, and the double-click hierarchy selection. My other consideration was to try to keep the dialog as minimal as possible. I had setup finger controls in the past and found, due to the nature of wanting them to be able to control individual fingers, that they can become large slider interfaces. I thought that this time i would be able to use less and still have the same level of control. Here is what I came up with in the end, they are the same UserControl, they have a hand property, Left and Right which decides what UI colour they get.

Colour profiles

Selection based curl slider

I did this by having a two stage approach, working on a specific selection or on the entire hand. If no finger element was selected, the curl slider affected the entire hand. If a particular finger had been selected, then the curl would only operate on that finger. The same went for resetting the finger position. This worked well, you could quickly set up different poses based on others.

Splay and Thumb position and rotation were different. You don’t want to splay individual fingers, it looks odd so this acts on the entire finger rig. And individual thumb position and rotation are also necessary. However the thumb curl operates on the global curl slider.

The +Thumb button was something that came from the evolution of the control and testing it on some real characters. Sometimes, you didn’t want to move the thumb but still move the fingers. This checkbutton allows you to affect the thumb curl and splay with the other fingers – as before, it is based on the selection, so you have to have no part selected for this to work.

help

Its all put in place with a bit of simple matrix rotation. You can prerotate a matrix according to an angle, so each time the slider moves, it is calling a function that supplies the current transform and returns a new transform matrix that is prerotated by the angle from the slider.

Medial,Distal and Proximal Selectors

The three selectors to the left of the fingers are to select across the first, second and third bones. I thought this gives an extra control if the automatic blended rotation of the fingers isn’t quite enough to get the hand pose you are after.

Pose Storage and Mirroring Transforms

I have been using XML for a while now to store any sort of pose when I use a reference transform monitor to store a node. Most of my facial animation systems and rig setups use these. Once the directory is specified, you can store the local transform of each node and that’s all you need. I use an XMLLayoutPanel to handle the files. Its a dotnet user control I released a while ago that handles all the layout and even lets you specify a custom thumbnail. Check my previous blog post if you want to have a go at using it.

When you apply it back on the hand later, you multiply the current finger’s parent transform with the XML preset’s local transform and you have applied your stored preset. Because you extract the local transform, the pose is the same, even when applied to a different hand position as you are making a new transform matrix from the current parent’s position. When you want to mirror the transform to the other hand, that is a little more involved.

You start by reading the local transform of the finger nodes. Then, you Mirror the transform matrices of all bones, by creating a mirrored transform around a common parent (like the character’s root). However the other arm is not necessarily in the same position. So, you need to create a mirrored transform of the hand too. Once you have this, you can get the mirrored bones local transform matrix. This becomes the one you use, as you can then multiply this mirrored local transform onto the current location of the parent (i.e. the opposite hand) With this you can paste a hand pose from one arm to the other, regardless of the arm position. I was grateful for all of the brilliant examples on the CGTalk board to help with this, specifically from Paul Neale.

Setup

It is possible to set up a character with this complete hand animation system without any Maxscript. This works on Bipeds, CATrigs, Puppetshop rigs and custom bone rigs, so hopefully it will be useful for many projects to come.