Hi
This is going to be a relatively short post, based on the previous (and humongous!) post here
What’s it going to be about? Creating useful functions in MaxScript.
What’s a function in MaxScript?
Well, think of them as handy snippets of reusable code, which save on typing (always a good thing!) and the best thing about them is, you can feed them fresh information as and when required. So you type or create it once, and then use it further down the line whenever you need it. If you set it up right, you can also feed it with unique values on the fly.
Easier to demonstrate than explain in words, I think.
Going back to this post which was about creating timber cladding. We added a UVWmap modifier to each member or ‘slat’ of our cladding after they were created, and this UVW map was a simple box map with a size of 300cm for length, width and height.
We got MaxScript to do this for us using the following code –
claddingUVWmap = UVWMap()
claddingUVWmap.maptype = 4
claddingUVWmap.length = 300.0
claddingUVWmap.width = 300.0
claddingUVWmap.height = 300.0
If this is unfamiliar, then essentially UVWMap() creates an empty UVW map modifier, and ‘defining’ it as claddingUVWmap allows us to grab and tweak it.
The .maptype line coming next swaps the modifier from its default setting (of plane mapping) to box mapping, and then you can probably guess the rest, where we set its length, width and height to be 300, which refers to the system units being used, in my case centimetres.
MaxScript always uses the system units.
OK. Moving on from that quick refresher.
What if we wanted a box map of 450cm instead, say for something like brickwork?
We could copy and paste the above, and change a few things for sure –
brickworkUVWmap = UVWMap()
brickworkUVWmap.maptype = 4
brickworkUVWmap.length = 450.0
brickworkUVWmap.width = 450.0
brickworkUVWmap.height = 450.0
But doesn’t that feel a bit tedious and dumb? OK, it’s only a few lines, but still.
So we have a thing, some Geometry, and we want to add a simple UVW box map to it, and sometimes its UVW map dimensions will be 300 cm, sometimes 450 or something completely different.
We could write our own bespoke snippet of MaxScript code, package it all up (this is what a function is) and give it a name, but also give it the option of supplying fresh settings or values whenever we use it.
When we use it, we would probably write something like
addAsimpleUVWBoxMapTo theThing 450
Or
addAsimpleUVWBoxMapTo theThing 300
So ‘addAsimpleUVWBoxMapTo’ is our reference to the ‘function’ that we’ve created, and then we’re giving it two bits of info to work with.
When I say ‘function that we’ve created’, we’ll get to this later when I show you how to create one. The two bits of info?
theThing – we need to add the UVWmap modifier to something!
So that could be $box001 or $selection[1] or ‘theCladdingSlatWeJustCreated’
450 – hopefully that’s becoming obvious as you become more familiar with MaxScript?
That will be the numerical value that we set the length, width and height of our UVW map to.
So how and when do we create our bespoke functions?
Let’s tackle the ‘when’ – at the start of our MaxScripts. We need to ‘make’ them before we can use them after all.
The how?
function Name Info moreInfo someMoreInfo =
(
Do stuff… with or to the info..
)
That does look computer-programme-y, right?
That said, if you did get all the way through the lengthy creating cladding post here, you should be used to it by now?
Let me explain it by using our example, where we’re adding a UVW box map to an object
function addAsimpleUVWBoxMapTo theThing theSize =
(
Do stuff… to theThing with the info from theSize..
)
Starting the line with ‘function’ tells MaxScript that we’re going to define and create, well, a function.
Remember box position:[0,0,0] length:100 from the creating timber cladding post? Well it’s unsurprisingly a bit like that in terms of syntax.
addAsimpleUVWBoxMapTo is its name (stupidly lengthy, I know), so we can use it later. It needs a name, or we’ll never find or run it!
Maybe ‘quickBoxMap’ would be a better name.
function quickBoxMap theThing theSize =
(
Do stuff… to theThing with the info from theSize..
)
The ‘Do stuff’ bit then, this is the good stuff that the function will do or ‘execute’ and is enclosed between two brackets. Here we’re going to create an empty UVW map, set its type to box, and dial in the same dimensions (theSize) for length, width and height. Once we’ve done that, then we’ll add this UVW map modifier to our geometry or rather, theThing..
We’re already familiar with creating UVW map modifiers, so it’s just a case of adapting our patch of MaxScript to work more generically, with the two variables we’re supplying our function with, theThing and theSize.
Actually, one thing, we’ll set our UVWmap to a variable, so that we can work with the UVW map settings easily. This is again a concept talked about in this earlier post. We won’t call the UVWmap modifier ‘claddingUVWmap’ or ‘brickworkUVWmap’ though, let’s call it ‘ourUVWmap’.
function quickBoxMap theThing theSize =
(
ourUVWmap = UVWMap()
ourUVWmap.maptype = 4
ourUVWmap.length = theSize
ourUVWmap.width = theSize
ourUVWmap.height = theSize
addModifier theThing ourUVWmap
)
Interesting that the addModifier line looks like a function in itself. Maybe it is, buried deep down in the bowels of MaxScript..
So, this is still just the definition bit for our function. We’ve set it up, got it ready to go, but haven’t used or run it yet.
If you had a box, box001 in your scene, then you could add a 300cm UVWmap modifier to it, like so (assuming you’ve already set the function up earlier in your script).
quickBoxMap $box001 300
Voila! Remember to include the $ dollar sign when playing around with Geometry in a 3ds Max scene.
Fancy a 450cm box map instead? No problem –
quickBoxMap $box001 450
Actually, a useful thing would be the inclusion of comments or hints when you’re creating your own MaxScripts, something I’ve not touched on as yet.
But obviously, if you type something into a script, there needs to be a way of telling MaxScript to ignore it, you wouldn’t want it to try and execute ‘this is the bit where I add the UVW map’. It would give a big fat error.
If you want to have a comment or handy reminder in MaxScript, then you delineate it with two dashes —
This will ‘tell’ MaxScript to ignore the characters coming after up until the end of the current line of text. In the Script editor window it will actually change this text to green and a different font.
I’ve included an HTML copy of our MaxScript below, so you can see how it appears in the Script editor window, with the comments added. Feel free to swipe and copy this and paste it into an empty Script editor window. If you then run it via Tools>Evaluate All, then of course, you’ll need a box001 in your scene..
--define a function which adds a box UVW mapping modifier to an object, with the same value for l, w and h
function quickBoxMap theThing theSize =
(
ourUVWmap = UVWMap() --create an empty uvw map modifier and label this 'ourUVWMap'
ourUVWmap.maptype = 4 -- .mapType = 4 changes the UVW mapping from 'plane' to 'box'
ourUVWmap.length = theSize --these next three lines set the length, width and height to whatever value (theSize) we supply the function with
ourUVWmap.width = theSize
ourUVWmap.height = theSize
addModifier theThing ourUVWmap --this last line goes right ahead and adds our UVW map to our object (theThing)
)
quickBoxMap $box001 450 --this line runs our function and adds a 450cm box map to box001
So that my friends, is it!
Why use functions? You don’t have to of course, but they do neaten things up, and by setting them up to receive values, or ‘parameters’ then they do become a ‘one size fits all’ kinda tool.
Going back to the timber cladding script (again found here), there are a few opportunities for the inclusion of functions, one to rotate something in the local X axis, another to add an UVWxForm modifier and randomise its offsets.
That can be some homework for you.
But let me give a fresh edit of the timber cladding script below, where I include our quickBoxMap function, and you can see it in context.
I’ve added our function after the initial few lines (where we’re setting up a few variables for the cladding slat size and so forth), and I’ve also included some comments where hopefully, you can see where I define the function, and then later on, when I run it, adding a 300cm UVW box map to our freshly creating cladding slat.
claddingModule = 15.0
claddingThickness = 1.2
claddingMaterial = sceneMaterials["timberCladding"]
waterproofingMaterial = sceneMaterials["felt"]
claddingJoint = 0.5
--define a function which adds a box UVW mapping modifier to an object, with the same value for l, w and h
function quickBoxMap theThing theSize =
(
ourUVWmap = UVWMap() --create an empty uvw map modifier and label this 'ourUVWMap'
ourUVWmap.maptype = 4 -- .mapType = 4 changes the UVW mapping from 'plane' to 'box'
ourUVWmap.length = theSize --these next three lines set the length, width and height to whatever value (theSize) we supply the function with
ourUVWmap.width = theSize
ourUVWmap.height = theSize
addModifier theThing ourUVWmap --this last line goes right ahead and adds our UVW map to our object (theThing)
)
thePlane = $selection[1]
slatHeight = thePlane.length
numberOfSlatsRequired = (thePlane.width / claddingModule)
numberOfSlatsRequired = ceil numberOfSlatsRequired as integer
slatWidth = (thePlane.width / numberOfSlatsRequired)
NewSlat = box length:claddingThickness width:slatWidth height:slatHeight transform:thePlane.transform
NewSlat.name = uniquename "timberSlat"
NewSlat.material = claddingMaterial
append TheCladdingSlats NewSlat
slatRotation = eulerangles -90 0 0
In coordsys local rotate newSlat slatRotation
In coordsys local move NewSlat [( (thePlane.width/2) * -1 ), 0,0]
In coordsys local move NewSlat [(newSlat.width/2), 0,0]
In coordsys local move NewSlat [0,0,( (thePlane.length/2) * -1 )]
In coordsys local move NewSlat [0,((claddingThickness/2) * -1),0]
-- this is where I run our quickBoxMap function, adding a 300cm box mapping to our freshly created slat..
quickBoxMap newSlat 300
For i=1 to (numberOfSlatsRequired - 1) do
(
copiedSlat = copy NewSlat
In coordsys local move copiedSlat [(NewSlat.width * i), 0, 0]
append TheCladdingSlats copiedSlat
)
For i in TheCladdingSlats do
(
UVshift = UVW_Xform()
UVshift.U_Offset = (random -1.0 1.0)
UVshift.V_Offset = (random -1.0 1.0)
addModifier i UVshift
)
For i in TheCladdingSlats do
(
i.HeightSegs = 10
slatTwist = Twist()
slatTwist.angle = (random -3.0 3.0)
addModifier i slatTwist
)
For i in TheCladdingSlats do i.width -= claddingJoint
thePlane.material = waterproofingMaterial