Home Technical Talk

custom maxscript panel for modeling, management and exp/import models

sublime tool
Offline / Send Message
renderhjs sublime tool
after replying here people asked me if they could have that script with the spinner that lets me smooth surfaces without the usual hassle in 3dsmax.
The picture I posted back then was:
http://www.renderhjs.net/bbs/polycount/tips/quarantine_map_smoothing_groups.gif

its a common task for me fixing the smoothing groups of assets I model. But since discovered maxscript I tried to automate and simplify common tasks in 3dsmax and thus a personal collection of scripts grew mainly also because I have to often switch computers each with their own unique installation of 3dsmax.
Having a swiss-knife for my personal needs thus was a welcome step into working more effecient- even more considering that simple maxscripts dont need any installation at all and can be executed in any max version.

some of my intentions:
- no depending code or systems (no installation, no additional files like classes or ini files, no complicated sub-folder hierarchy)
- no macro scripts,- maybe its just me but I dont like them at all because it takes often to much time configuring them and bind them to toolbars, shortcuts ect. scattering scripts imo.
- not to much buttons, less is more and several steps (but what counts as 1 action) into 1 button ect.

whats inside?

here is a screenshot of the panel:
renderhjs_maxscript_panel_01.gif

typical scenarios I have
A.)
- creating a cube
- hitting (03) to convert it to a poly without resetting the pivot
- centering the cube on my closest axis (e.g front view) with (04)
- adding a symetry modifier (05)
- adding the turbo smooth modifier and instantly going to the first element of my poly with preview of the final result under (11)
- from now on I can model in sub-div style with a symetry modifier

B.)
- I have a model to be uv- unwrapped, select it
- goto (12) hit "get" select a url with the extention .obj and hit "export selected"
... meanwhile I create the uv layout in my favourite tool and replace the very same file once done...
- back in max I now simply need to delete the yet selected object and hit "import + smooth" to place the fixed UV- model at the very same spot I exported the earlier one
this method works equally good for zBrush/ Mudbox work
note: in the future I want to detect if a extention has been added and if not automaticly set it for example to ".obj" - so it needs less typing.

C.)
I want to create a brick wall with 4 different brick types
- build the 4 bricks
- instanciate with one of the bricks a wall with a shift in the interval for each row
- make instances apart from the scene from the other 3 bricks
- select all objects and hit (06) to shuffle all bricks that are selected. This will switch all positions of the bricks under each other randomly
- to make it look more natural add a random rotation spin to all bricks by selecting all bricks of the wall and hit (08) to randomly rotate it at a range of 5°. hit it a few more times for a more extreme result.

there are many more scenarios esspecially with the smoothing slider which hels alot defining better shapes of a model with a few clicks and the best possible result.
A favourite of me is also the very big fix key, its main intention is to change typical max primitive objects that often have annoying default values like height intersections of a cylinder. This button sets it to just 1 intersection,- same for the box and plane object. When a poly object is selected it switches the turboSmooth modifiers in it on and off.


if you want to use or try it out simply either
1.) download the maxscript put it in your
{3dsmaxFolder}\3ds Max 9\Scripts\Startup
e.g
c:\programs\3ds Max 9\Scripts\Startup
and restart 3dsmax- now with each start the panel should pop up- delete it from the folder and your will never ever see it again.
2.) open the maxscript listener (F11 key) and create a new script (ctrl + n) paste the script and hit "evaluate All" (ctrl + e)

the maxscript file:
download:
http://www.renderhjs.net/bbs/polycount/submissions/maxscripts/renderhjs_tools.ms
alternativly copy paste this and run it in the maxscript listener
[php]function custom_smoothing angle=(
try(
angle = ceil( angle as integer) as integer;

local faces_selected = $.EditablePoly.GetSelection #Face
local select_all_state = faces_selected.isEmpty;
--print("sel: "+ as string);
if (select_all_state)then(
local faces_num = polyop.getNumFaces $;--number of faces
$.EditablePoly.SetSelection #Face #{1..faces_num}
)
$.autoSmoothThreshold = angle
$.EditablePoly.autosmooth ()
if (select_all_state)then(
$.EditablePoly.SetSelection #Face #{}
)
tools_1.label_angle_smoothnes.text = (angle as string)+" °";
)catch()
)

function switch_turbosmooth obj=(
for o in obj do if superclassof o == geometryClass do (
for mod in o.modifiers where classof mod == turbosmooth do(
if o.turbosmooth.enabled then(
o.turboSmooth.enabled = false;
)else(
o.turboSmooth.enabled = true;
)
)
)
)


rollout tools_1 "common" width:190 height:276
(
button btn_poly_center "poly xRes. center" pos:[5,3] width:88 height:17
button btn2 "sym. box" pos:[8,151] width:55 height:17
button btn3 "smth. cube" pos:[66,151] width:69 height:17
button btn5 "plane" pos:[137,151] width:46 height:17
button btn_unwrap "uv unwrap elem." pos:[97,3] width:88 height:17
button btn_merge "merge to one poly" pos:[5,20] width:88 height:17
button btn_null "null x,y,z" pos:[97,20] width:48 height:17
button btn_sym "sym." pos:[149,20] width:36 height:17

GroupBox grp1 "multiple objects:" pos:[4,37] width:181 height:37
GroupBox grp2 "surface smoothing angle" pos:[4,74] width:183 height:40
label label_angle_smoothnes "360 °" pos:[154,75] width:24 height:12
slider slider_smooth_angle "" pos:[12,86] width:177 height:25 range:[0,360,180] ticks:0

button btn_shuffle_pos "shuffle pos" pos:[7,50] width:56 height:21
button btn_random_wire_color "random color" pos:[64,50] width:69 height:21
button btn_random_rotation "rand. rot." pos:[134,50] width:48 height:21

button btn_fix_any "fix primitives" pos:[5,117] width:182 height:31

-- ############ code ##############



on btn_poly_center pressed do
(
try(
disableSceneRedraw()

CenterPivot($selection);
ResetXForm($selection);
macros.run "Modifier Stack" "Convert_to_Poly"

enableSceneRedraw()
completeRedraw()
)catch()
)
on btn2 pressed do
(
--symbox
Box widthsegs:2 length:50 width:50 height:50 pos:[0,0,0] isSelected:true
ConvertTo $ Editable_Poly
polyOp.deleteFaces $ #{1, 3, 5, 9, 10}
macros.run "Modifiers" "Symmetry"
modPanel.setCurrentObject $.baseObject
showEndResult = true;
)
on btn3 pressed do
(
--smooth cube
Box widthsegs:1 length:50 width:50 height:50 pos:[0,0,-25] isSelected:true;
modPanel.addModToSelection (TurboSmooth ()) ui:on;
$.modifiers[#TurboSmooth].iterations = 1;
CenterPivot($selection);
ResetXForm($selection);
ConvertTo $ Editable_Poly;
)
on btn5 pressed do
(
--plane
Plane length:25 width:35 transform:(matrix3 [1,0,0] [0,0,1] [0,-1,0] [0,0,0]) isSelected:on;
$.lengthsegs = 2;
$.widthsegs = 2;
ConvertTo $ Editable_Poly;
subobjectLevel = 2;
)
on btn_unwrap pressed do
(
try(
disableSceneRedraw()

modPanel.addModToSelection (Unwrap_UVW ()) ui:on

$.modifiers[#unwrap_uvw].unwrap5.setShowMapSeams off
$.modifiers[#unwrap_uvw].unwrap5.setPeltAlwaysShowSeams off

subobjectLevel = 3
modPanel.setCurrentObject $.modifiers[#Unwrap_UVW]

$.modifiers[#unwrap_uvw].unwrap2.setFreeFormMode on
$.modifiers[#unwrap_uvw].unwrap.edit ()

subobjectLevel = 3
$.modifiers[#unwrap_uvw].unwrap.DisplayMap off

$.modifiers[#unwrap_uvw].unwrap.edit ()-- to maximize
$.modifiers[#unwrap_uvw].unwrap.edit ()-- to maximize

$.unwrap_uvw.unwrap2.setGeomSelectElementMode(true); -- select viewport
$.unwrap_uvw.unwrap2.setTVElementMode(true);--select element UV view
$.unwrap_uvw.unwrap2.setTVSubObjectMode(3);--face selection more
$.unwrap_uvw.unwrap2.setShowMap(false);--disable tex view

-- new stuff
$.modifiers[#unwrap_uvw].unwrap2.setFillMode 2;
$.modifiers[#unwrap_uvw].unwrap2.setBackgroundColor [255-44,255-52,255-43];
$.modifiers[#unwrap_uvw].unwrap2.setGridVisible false

enableSceneRedraw()
completeRedraw()
)catch
(
enableSceneRedraw()
completeRedraw()
)
)
on btn_merge pressed do
(
try(
disableSceneRedraw()
for i in 1 to selection.count do
Try(ConvertTo Selection Editable_Poly)Catch()

baseObj = selection[1]
items = #()
for s in selection do append items s

for i in items do (
if (i != baseObj) then (
if (classof i == Editable_mesh) or (classof i == Editable_Poly) then
baseObj.EditablePoly.attach i baseObj
)
)
enableSceneRedraw()
completeRedraw()
)catch()
)
on btn_null pressed do
(
try(
disableSceneRedraw()

x = abs $.pos[1];
y = abs $.pos[2];
z = abs $.pos[3];
placed = false;

if (x!=0 and placed == false)then(
if ((x<y and y!=0) or y==0) then(
$.pos = [0, $.pos[2], $.pos[3]];
placed = true;
)
)

if (y!=0 and placed == false)then(
if ((y<z and z!=0) or z==0) then(
$.pos = [$.pos[1],0, $.pos[3]];
placed = true;
)
)
if (z!=0 and placed == false)then(
$.pos = [$.pos[1],$.pos[2],0];
placed = true;
)

enableSceneRedraw()
completeRedraw()
)catch(
messageBox "error null"
)
)
on btn_sym pressed do
(
try(
modPanel.addModToSelection (symmetry ()) ui:on
$.modifiers[#Symmetry].axis = 0;
$.modifiers[#Symmetry].flip = true;
modPanel.setCurrentObject $.baseObject
showEndResult = true;
--
)catch()
)
on slider_smooth_angle changed val do
(
try(
custom_smoothing(val);
)catch()
)
on btn_shuffle_pos pressed do
(
--shuffle positions
try(
plist = for i in 1 to selection.count collect selection.pos
--rlist = for i in 1 to selection.count collect selection.rotation
for i in selection do(
local theidx = (random 1 plist.count)
i.pos = plist[theidx]
--i.rotation = rlist[theidx]
deleteitem plist theidx
--deleteitem rlist theidx
)
)catch()

)
on btn_random_wire_color pressed do
(
--random wire colors
try(
for i = 1 to selection.count do
(
obj = selection;
w = obj.max.x - obj.min.x;
h = obj.max.y - obj.min.y;
d = obj.max.z - obj.min.z;
v = w*h*d;
obj.wirecolor = color (random 0 255) (random 0 255) (random 0 255)
)
)catch()
)
on btn_random_rotation pressed do
(
--random rotation ~5°
try(
local range=5;
for i in 1 to selection.count do(
local x = ceil(random 0.0 2) as integer-1;
local y = ceil(random 0.0 2) as integer-1;
local z = ceil(random 0.0 2) as integer-1;
local new_angle = ceil(random 0.0 (2*range))-range;
rotate selection (angleaxis new_angle [x,y,z]);
)
)catch()
)
on btn_fix_any pressed do
(--fix any key
try(

for i in 1 to selection.count do(
local obj = selection;
case (classof obj) of
(
Editable_Poly:(
--check for turbo smooth on/off
switch_turbosmooth obj;
)
PolyMeshObject:(
switch_turbosmooth obj;
)
Editable_mesh:(
switch_turbosmooth obj;
)
Box:(
selection.widthsegs = 1;
selection.heightsegs = 1;
selection.lengthsegs = 1;
)
Cylinder:(
selection.capsegs = 1;
selection.heightsegs = 1;
selection.sides = 12;
)
Plane:(
selection.lengthsegs = 1;
selection.widthsegs = 1;
)
Sphere:(
selection.segs = 8;
)

default:(
local string_obj = (classof obj) as string+".*";
print("not supported");
showclass string_obj;
)
)


--print("type: "+(classof selection) as string);
)
)catch()
)
)
rollout tools_2 "modifier sets" width:190 height:287
(
button btn1 "TurboSmooth" pos:[4,3] width:72 height:18
button btn2 "Symetry" pos:[4,22] width:46 height:18
button btn3 "Shell" pos:[81,22] width:56 height:18
button btn4 "edit poly" pos:[81,3] width:56 height:18
button btn5 "extrude" pos:[140,3] width:48 height:18
button btn6 "noise" pos:[150,47] width:37 height:18
button btn7 "abs" pos:[51,22] width:25 height:18

button btn52 "flip normals" pos:[4,47] width:70 height:18
button btn53 "xForm" pos:[4,66] width:70 height:18
button btn54 "Cloth" pos:[77,47] width:72 height:18
button btn55 "cap hole" pos:[140,22] width:48 height:18
button btn56 "Garmet Mkr." pos:[77,66] width:72 height:18
--####################################################




on btn1 pressed do
(
try
(
modPanel.addModToSelection (TurboSmooth ()) ui:on
$.modifiers[#TurboSmooth].useRenderIterations = on
$.modifiers[#TurboSmooth].renderIterations = 2
$.modifiers[#TurboSmooth].iterations = 1

modPanel.setCurrentObject $.baseObject;
showEndResult = true;
)
catch()
)
on btn2 pressed do
(
try
(
modPanel.addModToSelection (symmetry ()) ui:on
$.modifiers[#Symmetry].axis = 0;
--$.modifiers[#Symmetry].flip = true;
modPanel.setCurrentObject $.baseObject;
showEndResult = true;

)
catch()
)
on btn3 pressed do
(
try
(
modPanel.addModToSelection (Shell ()) ui:on
)
catch()
)
on btn4 pressed do
(
try
(
modPanel.addModToSelection (Edit_Poly ()) ui:on
)
catch()
)
on btn5 pressed do
(
try
modPanel.addModToSelection (Extrude ()) ui:on
catch()
)
on btn6 pressed do
(
try
(
modPanel.addModToSelection (Noisemodifier ()) ui:on
$.modifiers[#Noise].fractal = on
$.modifiers[#Noise].roughness = 1
$.modifiers[#Noise].strength = [5,5,5]
)
catch()
)
on btn7 pressed do
(
try
(
modPanel.addModToSelection (symmetry ()) ui:on
$.modifiers[#Symmetry].axis = 0;
--$.modifiers[#Symmetry].flip = true;
$.modifiers[#Symmetry][#mirror].position = [-$.pos[1],-$.pos[2],-$.pos[3]];
modPanel.setCurrentObject $.baseObject;
showEndResult = true;
)
catch()
)
on btn52 pressed do
(
try
(
--new
)
catch()
)
on btn53 pressed do
(
try
(
--new
)
catch()
)
on btn54 pressed do
(
try
(
--new
)
catch()
)
on btn55 pressed do
(
try
(
--new
)
catch()
)
on btn56 pressed do
(
try
(
--new
)
catch()
)
)-- ///////////////// OBJ im/ export ////////////////////////


function obj_tools_showName =(
try(
array = filterString (obj_tools.textfield.text as string) "\\"
obj_tools.label_filename.text = array[array.count] as string;
)catch()
)

-- ///////////////// OBJ im/ export ////////////////////////
rollout obj_tools "obj-file import/export" width:190 height:287
(
button btn_import "import + smooth" pos:[5,47] width:90 height:18
button btn_export "export selected" pos:[97,47] width:89 height:18
button btn_get "get" pos:[4,8] width:27 height:33


edittext textfield "url" pos:[35,9] width:151 height:17

edittext label_filename "filename" pos:[34,27] width:152 height:17


on btn_import pressed do
(
try
(

file_url = (obj_tools.textfield.text );

disableSceneRedraw()
--change this directory to modify where the obj in script imports from (should be the same as the directory above)
importFile ( file_url ) #noPrompt
AddMod smooth
$.modifiers[#Smooth].smoothingBits = 1
ConvertTo $ Editable_Poly

enableSceneRedraw()
completeRedraw()

)
catch(
enableSceneRedraw()
completeRedraw()
)
)
on btn_export pressed do
(
try(
file_url = (obj_tools.textfield.text );
print ("file: "+file_url)

exportFile (file_url) #noPrompt selectedOnly:true
)catch(
messagebox("error exporting")
)
)
on btn_get pressed do
(
try(

path = getSaveFileName "select obj" \
filename:"some filename..." \
types:"OBJ(*.obj)|*.*|"

obj_tools.textfield.text = path;
obj_tools_showName();
)catch()

)
on textfield changed sa do
(
obj_tools_showName();
)
on edt3 changed sa do
(
obj_tools_showName();
)
)
-- ///////////////// OBJ im/ export ////////////////////////
try (-- close existing rolloutFloater
closeRolloutFloater totFloater
) catch ()

totFloater = newRolloutFloater "render|hjs 2008 tools" 208 416
addrollout tools_1 totFloater
addrollout tools_2 totFloater
addrollout obj_tools totFloater--obj

obj_tools.textfield.text = "C:\\temp.obj";
obj_tools_showName();[/php]

you are free to modify and improve it,- or change it to your own needs

Replies

  • Rob Galanakis
    Options
    Offline / Send Message
    Three code-related things:
    First, avoid try/catch so much... you should handle unhandled cases better (such as no object selected, etc.), using an if/then, and print or messageBox the error.
    Second, and this is pretty minor- if you're not using the functions outside of the rollout, put them inside of the rollout. This will keep them in the rollout's scope, and not in a global scope.
    And finally, something usually missed when learning MXS:
    "for o in obj do if superclassof o == geometryClass do"
    is much better written:
    "for o in obj where superclassof o == geometryClass do"
    The difference is, using the 'do/if/do' is actually nesting a statement using bad-formatted code. It makes it harder to read and troubleshoot. Using 'where' keeps it as part of the 'for' statement, not its own statement.

    Stuff like this is really useful... I am not sure how much use people will get from specific tools like this because, as you know, it is so tied to your workflow. But I really hope it encourages people to 'take the plunge' and do things like this for themselves. Even as a TA I sometimes don't write myself these sort of repetitive tools, but they take all the mind-numbing button pressing out of repetitive tasks and are always worth the time cost in satisfaction. They are really simple to write as well, since they are just a number of macros.
  • renderhjs
    Options
    Offline / Send Message
    renderhjs sublime tool
    thanks for the in depth code review- I know it is sluggish at some spots as in most cases it will do for me as long as it works. I will try to improve it with what you have written,- thx a lot for that

    I once had this idea of creating a website with maxscirp snippets where people could compose their own panels without maxscript expierence while having still the selection of many usefull original code blocks for their daily workflow. A bit like this recent baby from adobe:
    adobe-configurator.gif
    where you can customize your own panel using Java script commands through a flash swf movie
    http://labs.adobe.com/technologies/configurator/
    something like that for 3dsmax would be awesome and better step of encouraging 3d artists to customize a little bit more their own interface
  • Mark Dygert
    Options
    Offline / Send Message
    Three code-related things:
    First, avoid try/catch so much... you should handle unhandled cases better (such as no object selected, etc.), using an if/then, and print or messageBox the error.
    Second, and this is pretty minor- if you're not using the functions outside of the rollout, put them inside of the rollout. This will keep them in the rollout's scope, and not in a global scope.
    And finally, something usually missed when learning MXS:
    "for o in obj do if superclassof o == geometryClass do"
    is much better written:
    "for o in obj where superclassof o == geometryClass do"
    The difference is, using the 'do/if/do' is actually nesting a statement using bad-formatted code. It makes it harder to read and troubleshoot. Using 'where' keeps it as part of the 'for' statement, not its own statement.

    Stuff like this is really useful... I am not sure how much use people will get from specific tools like this because, as you know, it is so tied to your workflow. But I really hope it encourages people to 'take the plunge' and do things like this for themselves. Even as a TA I sometimes don't write myself these sort of repetitive tools, but they take all the mind-numbing button pressing out of repetitive tasks and are always worth the time cost in satisfaction. They are really simple to write as well, since they are just a number of macros.
    Rob, these are great tips, thanks a ton. I'll be going over my scripts and doing some rewrites of the "if-do-if's". I took the plunge a while back. With a big thanks to a few people around here and I was writing those helpful little scripts for myself and co-workers. Nothing better then saving yourself from the headache of repetition, well maybe seeing it help someone else also, hahaha.

    Renderhjs, cool stuff man, you've encouraged me to dive deeper into the guts of PH, cool stuff thanks!
Sign In or Register to comment.