Browsing"Tips and Tricks"

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

Single Pass Ambient Occlusion

Feb 15, 2009 by     No Comments    Posted under: 3dsMax, Technical Research, Tips and Tricks

Most people will be aware of the use of the Mental Ray Ambient Occlusion (AO) shader by now and will have read the tutorials scattered around the interwebulator. The question is – Now that the A&D material features ambient occlusion, do you still need to use the old AO shader on it’s own any more?

As great as the A&D shader is, you might not need a material with this many features, or you might want to just add AO to a model without changing the material you already have. Without the need to add a reflection pass into the equation, the process of rendering an occlusion pass is a way of adding an extra layer of soft shadow areas, and can be combined with either a diffuse pass or an ambient color pass depending on the effect required –

This is combined by overlaying the separate occlusion pass with the multiply screen mode in Photoshop or After Effects/Combustion.

You can obviously add a bitmap texture to the light channel of the AO material, but you would lose any specular shading that the standard material provides. Before the A&D material was launched, you could get specular shading and AO by putting a ‘Material to shader’ (MTS) in the bright channel, and then placing your standard material in this. However, it wasn’t a perfect solution, for the following reasons –

  • navigating through the material was problematic as the MTS shader sort of stopped the material editor as clicking on the assigned material presented you with the assign new material box, not the parameters to edit it.
  • There was no way of previewing the texture in the viewport, leading to an instanced texture in the contour channel which had to be turned off to render but was visible in the viewport.
  • Most importantly, there was no actual control of the intensity of the AO effect. You could of course use the parameters within the material (spread, Falloff) to curtail the degree of occlusion but there were times when we wanted to have per-object control of the effect, like what the A&D material offers as well as not necessarily wanting 100% of the occluded solution. (one thing about using the AO shader as a light shader springs up is that there settings are the same for every object)

The solution cropped up to use the new Composite material that ported with 3ds Max 2008.

This material allows you to set up complex overlay materials like you would in Photoshop, with options for the layer modes too. This means that you can keep a standard material and just add this pass, technically like you would when you composite them together in Photoshop.

The best part is that not only to you have other overlay methods to experiment with, you can also change the intensity of the Occlusion effect without losing the spread and falloff values that you like.

This image below was rendered using this technique, and has no shadows, Just Ambient Occlusion.

3ds Max File Drop via DotNet

Aug 26, 2008 by     3 Comments    Posted under: 3dsMax, DotNet, Tips and Tricks

If you need to mimic the way that max presents you with merge/open/xref options when you drag into a viewport, you can perform this yourself with DotNet. Max expects a dataobject containing a string array. Depending on the filetype that this string member points to will decide whether max will prompt you with a file open menu or a texture map as viewport background. The other option is you can also pass a map path to drag onto slots on the material editor.

Below is a snippet of the code for this operation. It is entirely handled within the mousedown handler of the control. It makes sense in this case as you wouldn’t put it in the mousemove handler for example as this would mean the code would be needlessly called multiple times.

on btndragdrop mouseDown sender args do 
theIniFile = getdir #maxData + "3dsmax.ini" 
theKeys = getIniSetting theIniFile "FileList" 
maxfilearray = for o in theKeys where o != "MaxFiles" collect (getIniSetting theIniFIle "FileList" o)
intnum = dotnetobject "System.Int32" 0 
filenamestring= dotnetobject "System.string" maxfilearray[1] 
dropfile = dotnetobject "System.String[]" 1 
dropfile.setvalue filenamestring intnum 
DataObj = dotnetobject "DataObject" ((dotnetclass "DataFormats").filedrop) dropfile 
sender.dodragdrop Dataobj ((dotnetclass "DragDropEffects").Copy) 

The only thing to note is that the Dataobject takes an object as it’s first parameter. This can basically be anything you like, except this in this case. The SDK notes that it needs an array in this object. The square brackets after the dropfile variable denotes that it is an array of strings with 1 member, not a string. Passing a string object therefore has no effect and does not present you with the menu seen below, which is the result of the correct format of the DataObject.

Not the end of the story…

I noticed an odd behavior using drag and drop in max. When employed on a dotnetcontrol in a maxscript window, it classes it as being ‘inside’ max even though it is a dotnet object. Therefore you could in theory initialize the drag and drop onto the label control to perform a legitimate drop. This behavior is possibly not wanted, if you needed to perform other handlers – the drag drop might get triggered accidentally. The way around this is to use the mouseeventargs to check if the mouse has moved outside the client area of the control.

on DragDropOps open do 
btndragdrop.allowdrop = false
btndragdrop.text = "Hooray! A Drag/Drop Enabled Label!!!\n\nTo drop a Texturemap, just pass the map path string in the dataobject instead of a max file path. This will also work if draging a map to the material editor" btndragdrop.borderstyle = (dotNetClass "System.Windows.Forms.BorderStyle").fixedsingle 
btndragdrop.backcolor = (dotnetclass "System.Drawing.Color").orangered 
btndragdrop.forecolor = (dotnetclass "System.Drawing.Color").yellow 

on btndragdrop mousemove sender args do 
if (sender.clientrectangle.contains args.x args.y) then 
setsyscur #arrow 
 setSysCur #move

on btndragdrop mouseup sender args do 
if (sender.clientrectangle.contains args.x args.y) then 
( ) 
theIniFile = getdir #maxData + "3dsmax.ini" 
theKeys = getIniSetting theIniFile "FileList" m
axfilearray = for o in theKeys where o != "MaxFiles" collect getIniSetting theIniFIle "FileList" o 
filenameString = maxfilearray[1] 
dropfile = dotnetobject "System.String[]" 1 
dropfile.setvalue filenameString 0 
DataObj = dotnetobject "DataObject" ((dotnetclass "DataFormats").filedrop) dropfile 
sender.dodragdrop Dataobj ((dotnetclass "DragDropEffects").Copy) ) )