Clear out the dead wood from the layer manager

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

Occasionally, you end up with a load of layers in your scene that are no use to anybody, serve no function to society, and end up taking valuable space like an unwanted uncle at a wedding.


Here is a macro that will check the layer manager interface and remove any layer that is empty.

Macroscript ClearEmptyLayers 
tooltip:"Clear Empty Layers" 
buttontext:"Clear Layers" 

DeletedLayerCount = 0 
local deflayer = layermanager.getlayer 0 
deflayer.current = true
for i = Layermanager.count-1 to 1 by-1 do
		layer = layermanager.getLayer i  
		local thislayername = 
		layer.nodes &theNodes  
		if thenodes.count== 0 then (LayerManager.deleteLayerbyname thislayername;DeletedLayerCount +=1)
	if not DeletedLayerCount == 0 then Messagebox ("Number of layers removed - " + DeletedLayerCount as string) title:"Layer Manager" 

And after –


If only it was that easy to remove Piers Morgan from ‘the scene’

One thing to note is how the layermanager interface retrieves the contents of the layer –

if thenodes.count== 0 then …

The ampersand before the variable declares an array to be used and populates it with the layer contents. You might also notice that it deletes the layers in reverse order, since each time you delete one, the layer count becomes less. So if you deleted it going forward you would probably reach a point where the loop integer would be higher than the number of remaining layers, boo.

You also have to minus one from the total count as the default layer is located at 0.

Supressing 3dsMax hotkeys in composite control assemblies

Sep 15, 2009 by     No Comments    Posted under: 3dsMax, DotNet, Maxscript, Tips and Tricks

The way to use a dotnet textbox on a max rollout is to use the gotfocus and lostfocus events. This allows you to know when the textbox has been highlighted for text entry, and use the enableaccelerators command to temporailly suspend the max hotkeys.

rollout tbtest “MXS Dotnet texbox” width:227 height:23
dotnetcontrol textboxctrl “TextBox” pos:[1,1] width:225 height:16

on textboxctrl gotfocus sender arg do enableacellerators = false
on textboxctrl lostfocus sender arg do enableacellerators = true
createdialog tbtest

In my previous article, I detailed a method that can automatically set the backcolor of a custom control using the managedservices.dll . A similar principle could be applied to the textbox. If you were building a custom control, you could override the gotfocus and lostfocus methods and use the managedservices dll to call the maxscript function. Then you have a control that has this functionality built in. It’s really simple, you just write a control that inherits the textbox and add the functionality you need. The VB code is below –

Public Class MaxTextBox
Inherits System.Windows.Forms.TextBox

Protected Overrides Sub OnGotFocus(ByVal e As System.EventArgs)
End Sub

Protected Overrides Sub OnLostFocus(ByVal e As System.EventArgs)
End Sub
End Class

Again, the solution is simple to implement in maxscript, so this would be perfectly okay. However, if you wanted to build a composite control that contained a textbox, you would be stuck with the same problem. The code above would be all you would need to add. You would use the maxtextbox inside a composite control, If you added this class to your solution in visual studio, you could drag the new inherited control and add 3dsMax functionality natively to the assembly.

I should mention that this method is only available in 3dsMax 2010

Generating PDF Documents via MXS and .NET

Apr 14, 2009 by     No Comments    Posted under: 3dsMax, DotNet, Imaging, Maxscript, Program Automation, Technical Research

Recently, I discovered an open source library written in C# called PDFSharp. This came about because I was working on a script that generated a text file with information in at the end. Unfortunately, when it came to refining the presentation, my ASCI skills are seriously limited. For example, my attempt at the LoneRobot logo looked like this –

......... ___
........ /../
......8=/- /=8  - BeepBeepDoWopDoDoopDeep!
....... ---
........| |

In the magazine “ASCI Art Lovers Monthly” , this attempt gained a four asterisk rating, although others have subsequently informed me that the **** peppered over the artistic comments page are not related to this.

Because this generated document was to be printed and used as reference, It got me thinking about whether I could generate better printable documents directly from 3dsMax without having to use OpenOffice, Acrobat or any of my presentation skills. Fortunately, PDFSharp is an assembly that allows you to create PDF documents. The way it does this is to mirror a whole set of GDI+/WPF drawing classes and allow you to construct the page programmatically via DotNet. (If you want to look at a few DotNet GDI+ drawing methods for 3dsmax, check out this page)

Regardless of it’s limited appeal within Max, it is an amazing piece of open source code that should most certainly be looked at.

In order to integrate this, I wrote a small MXS Struct to wrap a few of the functions of PDFSharp’s GDI+ drawing methods, since this was all I was interested in using for my purposes. There are many, many more drawing and formatting options available through this assembly, but in the struct the following methods are available –

  • DrawImage (With optional scale multiplier)
  • DrawRectangle (Specify a corner radius to draw a rounded rectangle)
  • DrawEllipse
  • DrawLine (With different line styles)
  • DrawString (With various paragraph alignment options – Left,Right,Justify,Center)

The PDF is drawn on a GDI+ drawing surface, in this case a GDI bitmap the size of an A4 document. There is a basic example within the test code that creates the following PDF file –

Don’t Forget, you’ll need to replace the line where it loads in and draws the image, or the TestDocument function will fail.

You can get PDFSharp from here, and of course download the example below as normal. It’s well commented so you should be able to get an idea of how to use it. The source code of PDFSharp has a pretty comprehensive array of test projects to pick at.

I’m still waiting for our subscription copy of max 2010, so I’ll probably add a version later using the private/public declarations available in this release.

If anyone is interested, the text on the PDF is from the rather beautiful short story ‘The Man Who Planted Trees‘ by Jean Giono.

download script

Basic Color Adjustment in 3ds Max without Photoshop

Mar 17, 2009 by     No Comments    Posted under: DotNet, Imaging, Maxscript

A fair bit of my MXS/DotNet tinkering leads me on tangents that previously haven’t been within the remit of what I started. Normally this means I start writing a script which is half finished before I have an idea that seems far more interesting than the thing I actually started, by which time it’s too late and I’ve disappeared into a swirling abyss of ignorance and subterfuge.

Recently I was adding to another script for a 3D/Sculpture crossover project, when I realized that it would be helpful if I could perform some kind of image tweak in the script in order to adjust the results of what I was generating, without having to load it into Photoshop. Also, I was wondering why people use those white earbuds that came with their swooshy new ipod/phone when they are clearly crap. The other day I clearly identified that someone was listening to Glen Campbell, and that wasn’t good for me on two levels. One, that I could hear it, and two, that I knew it was Glen Campbell. Do you see what I mean about tangents?

Much of this article is the result of a post by Ofer Zelichover on CGTalk – When thinking about this I remembered a colormatrix method he posted a little while back. So thanks Ofer, you did much of the hard work already, I’ve just added a few different matrices to the mix.

The ColorMatrix Class

If you are familiar with how 3dsMax performs translation,rotation and scaling in the application, you will be familiar with the Transform Matrix. The Color Matrix is a similar principle, except with a 5×5 matrix with each row containing information about the RGB channels of an image, (with an extra column for the alpha channel). Without having to get into exactly what goes on, (there is plenty of information about this explained by far better qualified people) you can pass different color matrices via DotNet to an image in order to manipulate the pixel colour like the way a Transform matrix manipulates vertices or nodes.

Some of the Matrices are absolute values. I found two examples of a grayscale matrix. The one below seemed to be the most popular, taken from the NTSC guidelines on conversion of a color TV image to a black and white one. However, I also found an alternate grayscale matrix that accounts for linear color space. This seems to keep the highlights of the the original image a little better. I’ve included both methods in the struct code for comparison.

Some of the adjustment matrices need the current pixel values in order to base their adjustments, so these are implemented via some functions that pass back the corrected color matrix object. There is one function that handles all of the work, and the methods are commented within the download.

It is performed drawing the adjusted image onto a GDI bitmap. This is just about fast enough to perform the color adjustments within the utility. I had experimented with the lockbits method which is using unmanaged code but 3dsMax seems to have problems with this. It is important to specify the bitmap in the utility outside this function, since you don’t want to be continually creating bitmap objects with each slider event, as you would create a big memory problem. (There is a setimage function that creates it when the image is specified)

Filters Featured (Starting From Top Left)

  • Adjust Red Balance
  • Adjust Green Balance
  • Adjust Blue Balance
  • Saturation
  • Contrast
  • Brightness
  • Invert
  • Grayscale
  • Sepia
  • Red Channel Only
  • Green Channel Only
  • Blue Channel Only

At the moment, the utility passes the image back to a max display bitmap for save, this was to allow for a save in a non-windows image format. This is possible with an external image assembly, but not necessary for this.

The only other thing to note is the UI has a couple of custom controls – most notably the slider component that you can download with the code. This needs to be put in your scripts directory. These are some things i had developed for use in character setups, but were a bit more compact and had some functionality that could be set when the utility opens, rather than hardcoding it all into the script. I like this control because, when focused, it will allow you to scroll the middle mouse button to move the slider position.

If possible, I’d like to add a multiplication function to pass multiple matrices as dotnet doesn’t allow for this in the bitmapdata class. One for the future, and another half finished script. Apologies to any Glen Campbell fans, he’s not that bad, my Dad used to listen to him when I was young. Before his breakdown.


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