Browsing"Maxscript"

Visibility Tracks and Mapped Functions

Jan 14, 2009 by     8 Comments    Posted under: 3dsMax, Maxscript

Some methods are useful and can get overlooked when perusing the MXS help. For me, one of them is the use of a mapped function in maxscript.

Mapped functions act in the same way as a normal function but instead of manually specifying a loop for each element of an array or collection, the loop is performed automatically when a collection or array of values is passed as the first argument of a function.

There is still nothing wrong with performing a function on each element in turn, (and I frequently do), but I wanted to highlight this as an option that can added to the scripting toolkit.

The example is used in this visibility script. I needed something that i could set the object visibility quickly without having to open the curve editor each time. Therefore, if an object did not have a visibility track, it should be added.

 

 

 

mapped fn setviskey objs visval intan outtan =
(
if (getVisController objs) == undefined do VisibilityAssistant.adddviscontroller objs
local tvis = objs.visibility.controller
addnewkey tvis currenttime
keyind = getKeyIndex tvis currenttime
tvis.keys[keyind].value = visval
tvis.keys[keyind].inTangentType = intan
tvis.keys[keyind].outTangentType = outtan
)

The mapped function is passed the objs array (via selection as array) and then loops through each element. There is no need to specify a for/loop.

In theory this should be more efficient and faster, but for something like this it really doesnt make any difference.

The other thing of mild interest in the script is a custom trackbar filter. It allows you to only see visibility keys on the trackbar. Feel free to look through the code. You’ll also see the script is contained within a Struct. This allows you to specify one global variable and call many methods from it, including the rollout creation.

download mapped functions script

Using Base64 encoding in 3dsMax

Dec 17, 2008 by     8 Comments    Posted under: 3dsMax, DotNet, Imaging, Maxscript

If you have ever received an email and instead of your normal information for pharmaceutical-related special offers and personal member enhancement, you get a jumble of nonsense, you’re probably already aware of what a Base64 encoded string looks like. Email clients use MIME to transfer messages and attachments, and one way to break up things like images so that it can be sent is Base64 encoding.

aWNlLCBpY2UgYmFieSwgZHVtIGR1bSBkdW0gZHVtIGR1bSBkdW0g
ZHVtIGR1bSBkdW0sIHZhbmlsbGEgaWNlLCBpY2UsIGJhYnk=

Despite looking like the sound you make when trapping your plums in the fridge door, Base64 encoded strings can be used to represent images and sounds and deployed with scripts to avoid the need for external linked dependencies.

DotNet Provides some easy methods to do this within the framework, so here are some functions for converting images to Base64 within 3dsmax below.

fn ConvertImageToBase64String filename =
(
if (doesfileexist filename) do
(
memstream = dotnetobject "System.IO.MemoryStream"
ImgLoaded = ImageClass.fromfile filename
ImgLoaded.save memstream ImgLoaded.rawformat
Base64string = ConvertClass.ToBase64String (memstream.ToArray())
memstream.close()
return Base64String
)
)
fn ConvertBase64StringToImage string =
(
bytearr = convertclass.FromBase64String string
memstream = dotnetobject "System.IO.MemoryStream" bytearr
DecodedImg = ImageClass.fromstream memstream
memstream.close()
return DecodedImg
)

I’ve added these into a utility with a few extras (namely functions to convert and play Wav files using Base64 encoding) You can download this script at the bottom of the page.

Grimlock says “SSdtIGdvbm5hIG9wZW4gYSBjYW4gb2YgV0hPT1BBU1Mgb24geW91IQ==”

If if you have ever read the “Bitmap Values” topic in the MXSHelp, you will be aware of this script –

b=selectbitmap() -- open image file browser
bname="bitmap_"+(getfilenamefile b.filename) -- build name from filename
w=b.width -- get properties of bitmap
h=b.height
format "----------nfn load_% = (n" bname -- start defining function
format "local %=bitmap % %n" bname w h -- create bitmap in function
-- write out a function that unpacks an integer into a pixel color
format "fn unpack val = for p in val collect (r=p/256^2; g=p/256-r*256; b=mod p 256; color r g b)n"
for r=0 to h-1 do -- for each row in the bitmap
-- have function write the column of pixels to the bitmap
( format "setpixels % [0,%] (unpack #(" bname r
pixels=getpixels b [0,r] w -- read in the column of pixels
for c=1 to w do -- loop through each pixel
( p=pixels[c] -- get the pixel
-- pack the pixel into an integer and write it out
format "%" (((p.r as integer)*256+(p.g as integer))*256+(p.b as integer))
if c != w then -- if not at end of data
format ", " -- write a comma
else
format "))n" -- else close out the line
)
)
format "return %n" bname -- function returns the bitmap
format ")n----------n" -- finish off function definition
)

Base64 is a DotNet method of performing the same thing, and instead of returning a max bitmap, it returns a Dotnet image.

This could probably benefit from being moved into a dedicated dotnet assembly, as I found with the color control a few weeks back, similar functions are much slower within max. Therefore if you are converting large images you might find the UI snagged up for a while.

Finally, to convert text to and from Base64, here’s a great site I found that will do it for you! –

http://textop.us/Encryption/Base64

download script

Building a DotNet Colour Chart (Not for Elephants)

Nov 24, 2008 by     No Comments    Posted under: DotNet, Imaging, Maxscript

If you’re anything like me then you have a mind like a sieve. So much so, I can barely remember the names of the dotnet colours, let alone get their pesky American spelling right. It’s colour alright???!!! 🙂

Anyhoo, despite this, and not knowing what “GoldenRod” is, I decided to make a script that built an interface by retrieving the colors via the enumeration class. I’ve talked about enums last article but this introduces another way of using them. This certainly isn’t the only way to do something like this, but it was the way I worked out so it’ll do for me. The DotNetControl is a FlowLayoutPanel, which is really useful for building dynamic interfaces, (I use one in my HitchHiker control) as you can pass it an array of controls to build it.

It’s quite easy to get the brightness of the colour when creating the labels. Having a cutoff where if the color has a brightness value of less than 0.3 will change the forecolor to a light color. I’ve changed the code to reflect this. Also, i’ve split the array into two, to make the differentiation between systemcolors and named colors. (System colors are the colors used on the current windows color scheme)

Update –

  • Added copying to clipboard of dotnet string
  • Added DotNet color dialog to pick color
  • Added RGB swatches

To Do –

I am going to see if Reflection can be used to make this script faster. At the moment it takes a few seconds I cant work out if it is setting up the button array or the color property retrieval.

I will also try sorting the colors by Hue (Thanks to Dave Stewart for the suggestion) It will be an interesting test to see if an IComparer class can work in 3dsMax.

download script

Using Inheritance to build a custom control

Nov 19, 2008 by     No Comments    Posted under: DotNet, Maxscript, User Controls

One of the really good things about writing your own controls in Visual Studio is the ability to not actually write one. This is great because you don’t have to do any of the hard work, you just pick the control that is closest to the functionality you want, then inherit the class. This is much the same if as using extends in a scripted plugin. What this does is gives you a fully featured version of the control and allows you to build extra properties and methods for the bits you want to add, or override the existing properties to do something flash.

In this case, i’m not doing this at all, but I am just tailoring the button control to be integrated into 3dsMax in a more direct way. If you use a dotnetcontrol constructor to implement a dotnet button onto a MXS form, you will get a default instance of a windows.forms.button, which you then have to alter in the rollout’s load event. This isn’t problem really, but i wanted to see if it was possible to build a class that returned a button in the correct visual state from the word go, leaving very few, if any, things to do in the load event. The first property I wanted to implement was to return a flat button with a background color the same as 3dsMax’s default UI color. The other things I experimented with was the idea of using embedded resources to hold common UI icons, rather like the examples on the GDI+ article i wrote a while ago. I also wanted a one line method to align the text and image around in a logical way.

It doesn’t avoid the issue of having to perform some UI stuff in the rollout open handler, but it does most of what you need for you. You also avoid the flicker of the controls as they redraw themselves with the correct background color. If you still want to build MXS rollout rather than dedicated Dotnet rollouts this could be a reasonable solution. (Don’t forget, if you build a dotnet form with the MaxForm class in 3dsMax, the controls will take on the correct UI color automatically.)

Using DotNet Enums in MaxScript

With any custom class objects, it’s highly likely Enums will be used, as they are a way of organizing custom properties into easily recognizable names, rather like structs in MXS have members to describe elements so that you don’t have to resort to array elements with confusing index numbers.

To use an enum in 3DSMax, you use a plus symbol in between the class string and the enum type. therefore, in the maxbutton class, to get the enums used you would type the following –

(dotnetclass "LoneRobot.Controls.MaxButton+CustomIcons").NewDocument
(dotnetclass "LoneRobot.Controls.MaxButton+LayoutStyles").Top
(dotnetclass "LoneRobot.Controls.MaxButton+ColorPresets").MaxUIDark

you would then use this to set the custom properties in the class.

Here is the full class diagram of the Maxbutton Control –

I hope this article has been of interest to you. Please download the class library and try it for yourself. there is a basic MXS script that builds the rollout at the top of the page. You can explore trying the different enums for yourself!

download script

BotNet Framework – Packing dotnet objects into functions for ease of use

Oct 26, 2008 by     No Comments    Posted under: 3dsMax, DotNet, Maxscript, User Controls


I wanted to script a library of functions to help me set up certain DotNet UI objects without having to type everything out each time i wanted to use them. I obviously could not resist the pun either. One of the methods of setting up a DotNetControl in 3DS Max in order to have a cohesive UI look you need to set multiple properties for each. Please note this is very much a work in progress. If you feel the need to add a function to this please do so, but be sure to let me know so that I can update it!

For example, to setup a button with a see-through background image, you can call the following botnet function –


fn imagebutton btn image style bgcolor mobg mdbg tcolor:undefined =
-- tcolor is transparent color to be passed to the imagelist
(
btn.backcolor = colorpreset bgcolor
btn.flatstyle = style
btn.FlatAppearance.MouseOverBackColor = colorpreset mobg
btn.FlatAppearance.MouseDownBackColor = colorpreset mdbg
if tcolor != undefined then (btn.imagelist = (imagelist image invisible:tcolor)) else (btn.imagelist =(imagelist image))
btn.imageindex = 0
image = nothing
)

The variable tcolor is passed to an imagelist control object and used as the mask color. In dotnet you can pass an image as the button background color, but in order to get a transparent image, you need to pass an imagelist.

Note that the easy way to do this is to use a 32 bit PNG for the button image. Windows will recognise the transparency information and render the button correctly. However, if you only have a 24 bit image, this method is the way, so that you can supply the background color as a matte.

When placed within the on open event of the rollout, the code looks like this. Note i am using an image with a white background in my scripts folder called “addremoveicons.bmp”



rollout DNimagebuttons "BotNet Button Methods()" width:190 height:220
(
dotNetControl btnclose "button" pos:[9,9] width:80 height:45
dotNetControl msgbox "button" pos:[9,70] width:175 height:45
dotNetControl msgbox2 "button" pos:[9,125] width:95 height:85

on DNimagebuttons open do
(
BotNet.imagebutton btnclose ((getdir #scripts) + "addremoveicons.bmp") flat #maxback #maxactive #crimson tcolor:#white BotNet.imagetextbutton msgbox "A Message Here" 8 ((getdir #scripts) + "addremoveicons.bmp") flat #slategray #maxactive #limegreen tcolor:#white fontcolor:#red textalignment:botnet.bnfalign.BottomRight imagealignment:botnet.bnfalign.TopLeft BotNet.imagetextbutton msgbox2 "or Here" 8 ((getdir #scripts) + "addremoveicons.bmp") flat #maxback #yellow #limegreen tcolor:#white fontcolor:#purple textalignment:botnet.bnfalign.BottomCenter imagealignment:botnet.bnfalign.TopCenter
)

on msgbox click do
(
local msgstring = "Waheeeey, a funky dotnet button"
Botnet.messagebox " LoneRobot.BotNetFramework" msgstring
)

)
createdialog DNimagebuttons style:#(#style_toolwindow, #style_sysmenu)

Depending on which alignment option you provide can yield a variety of different button styles. I have set the button style to flat, i just prefer this but you could change it to system buttons should you prefer. At the moment it is limited to these UI functions, i hope in the future to add many more.

download script

you will need this logo in your scripts directory if you want to use the BotNet MessageBox function. You can obviously replace this with another image but if you want, right click this one and download…