PDA

View Full Version : Newbie Questions - Where do I look?


Context
10-02-2007, 12:35 AM
Hello,

I'm a total newbie at both programming as a concept and MaxScript in particular. As many have suggested, I took a look at MaxScripting to improve my modeling pipeline, and I'm fascinated by the possibilities, but I'm stymied by my own limitations.

I looked around and found that the principal source for learning MaxScript is the MAXScript Reference manual that comes with Max. I've been reading through both the introductory sections as well as the little "HowTo" tutorials.

I've done a good half dozen of them, but I have started trying to make a tool that I actually might use. The locking mechanism from the first tutorial is interesting, but not particularly useful. I want to create a tool that lets me lock each axis individually for each transform type.

Not terribly complex, but I can't figure out how to do it.

I've decided to use checkboxes for the interface, and I've even figured out that getTransformLockFlags requires a node designation to actually work, and that 'selection' is not a valid node designation (this took me quite some time, as I mentioned, I'm a hopeless newbie).

I'm stuck on two parts. I need some way of running a function when the selection changes. I want the checkboxes to update depending on what is your current selection is. I'm thinking that when you change your selection the function will grab the transformlockflags for the new node, and then run through a loop that will update the "checked" category of the checkboxes.

I'm also stuck on how to get the node designation of the current selection. The only way of getting a node designation I can find is "GetNodeByName" which is useless in this situation as I don't want to have the user type in the name of the selection as that is both time consuming and ridiculous.

Could someone point me in the right direction?

Also: I'm interested in the "isEnabled" and "isChecked" body event handlers. They're cool, but they also seem to work only on a script as a whole. Is there some way of having something similar effect multiple buttons in a single rollout?

I'm thinking that I would have to script the function of each button separately, save them as separate max scripts, and then... I don't know. I don't know how to add separate Max Scripts into a rollout.

Again, any help would be greatly appreciated.

taniamc
10-02-2007, 02:12 AM
Hi there,

I'm just on my way out, but I wanted to respond to what I knew I could help with quickly from your question.

As far as getting the selection, you can assign your selection to a variable as such...

selection = $

In MaxScript the symbol $ represents whatever is currently selected.

I hope that helps atleast part of your problem.

TC:

PS I'm not sure what you mean by not being able to use getTransformLockFlags on a selection node. The following example code should demonstrate how getTransformLockFlags will work on a selection.

testNode = $ -- gets current selection
setTransformLockFlags testNode #all -- sets all transform locks on
getTransformLockFlags(testNode) --returns an array of all locked axis (in this case all)

RustyKnight
10-02-2007, 02:27 AM
Hi Context! I'm going to start out by suppling a rather abstract answer to your questions, bascially because you've already started out doing all the right things...reading the docs and trial and error.

I'm stuck on two parts. I need some way of running a function when the selection changes. I want the checkboxes to update depending on what is your current selection is. I'm thinking that when you change your selection the function will grab the transformlockflags for the new node, and then run through a loop that will update the "checked" category of the checkboxes.

The first place you want to look for this is under the callbacks section of the documents. It basically allows you to attach a listener to max and be update when certain actions take place, like the selection changes...I'll give you a hint, you will want "selectionSetChanged"


I'm also stuck on how to get the node designation of the current selection. The only way of getting a node designation I can find is "GetNodeByName" which is useless in this situation as I don't want to have the user type in the name of the selection as that is both time consuming and ridiculous.

"selection" is an objectset, which you can access as if it were an array. Something like the following will loop through the selection and get each object in turn that is selected.

for obj in selection do (
-- the variable "obj" is the next node in selection, you would act upon this...
format "%\n" obj.name
-- Put your code here
)

You could also use "$" to get the currently selected object, but this will only return a single object, not the list...


Could someone point me in the right direction?

That's what these fantastic people do best :)


Also: I'm interested in the "isEnabled" and "isChecked" body event handlers. They're cool, but they also seem to work only on a script as a whole. Is there some way of having something similar effect multiple buttons in a single rollout?

Short answer, no...long answer, kind-a. In cases like this, I tend to write a function that does the work I need it to and call that function from within each of the individual event handlers, passing in the required information via parameters. It's still kinda messy, but it does the job.


I'm thinking that I would have to script the function of each button separately, save them as separate max scripts, and then... I don't know. I don't know how to add separate Max Scripts into a rollout.

Hay, we are thinking big! I love it! You use either "include" or "filein" to include the contents of other scripts into you own.

Each one works differently, so you will want to read the docs and decide which one is most useful to for your needs

Have fun
Shane

Context
10-02-2007, 03:07 AM
Thank you. A lot.

Both for the speed and the depth of your responses. They were amazingly helpful.

When I finally finish my silly little tool I'm sure I will have more questions. I have... the usual unrealistic newbie-dreams.

Also: I am entirely sure that I will have managed to construct the most bloated piece of code imaginable. Yay!

Thanks again.

RustyKnight
10-02-2007, 03:30 AM
I've been coding maxscript for just over 18 months and I'm still learning new ways to do old tricks!!

It's the fun part :)

Shane

taniamc
10-02-2007, 03:44 AM
Bloated code or not, you're trying and that's all that matters!


Best of luck to you.

Bobo
10-02-2007, 04:47 AM
I've decided to use checkboxes for the interface, and I've even figured out that getTransformLockFlags requires a node designation to actually work, and that 'selection' is not a valid node designation (this took me quite some time, as I mentioned, I'm a hopeless newbie).


As everybody said, you are generally doing the right thing.
Of course, from your perspective, it is difficult to tell which parts appear to be hard and which parts are REALLY too advanced.


I'm stuck on two parts. I need some way of running a function when the selection changes. I want the checkboxes to update depending on what is your current selection is. I'm thinking that when you change your selection the function will grab the transformlockflags for the new node, and then run through a loop that will update the "checked" category of the checkboxes.


'Selection' is an internal collection of objects that contains the currently selected objects. It is dynamically updated as selections change. You can snapshot the current selection so it does not update dynamically as the actual selection changes in the viewports by converting it to array and storing in a variable.

For example,
theSel = selection as array --returns an array of all selected scene objects

You can now use a for loop to "visit" each one and perform operations on them, EVEN if the operation would alter the scene selection in some way - the array will keep all the object references, unless you delete objects from the scene in which case there might be complications.

It is NOT a good idea to use $ because it morphs between a single node and a collection of nodes depending on how many are selected and can cause problems in your code if you haven't ensured beforehand what $ really means at the current moment.



I'm also stuck on how to get the node designation of the current selection. The only way of getting a node designation I can find is "GetNodeByName" which is useless in this situation as I don't want to have the user type in the name of the selection as that is both time consuming and ridiculous.

Could someone point me in the right direction?


Once you have an array of nodes, you can just say

for o in theSel do ... -- o will be assigned one object at a time each time the loop repeats.



Also: I'm interested in the "isEnabled" and "isChecked" body event handlers. They're cool, but they also seem to work only on a script as a whole. Is there some way of having something similar effect multiple buttons in a single rollout?

I'm thinking that I would have to script the function of each button separately, save them as separate max scripts, and then... I don't know. I don't know how to add separate Max Scripts into a rollout.

Again, any help would be greatly appreciated.

As mentioned already, to get your script to update when the scene selection changed, you would need callbacks. These ARE advanced matter and I would suggest writing a simpler version of the script first where you have a button for getting the current states of salected objects - when you press it, the code will be executed. Later, when you get that part working, you could add a callback and make it run that same code automagically.

Striding in smaller steps is usually helpful, otherwise you might get overwhelmed by the advanced stuff and miss the simpler things...

Keep on asking questions, the best way to learn MAXScript is by trying things out. To echo the other posters, I have been writing MAXScript for 10+ years and actually wrote all the HowTo tutorials in the Help, but I STILL discover new things every day. (I hope this does not sound scary ;))

Context
10-04-2007, 01:15 AM
Hello again,

So I've pretty much finished my rediculous little tool. I have two more questions.

The first is fairly simple. I want to be able to call a function and have it change a variety of variables. This does not appear to be something functions can do, which confuses the heck out of me. I basically want to be able to trigger a section of code by calling a... something. Is this a function? If so, I'll have more questions.

Second: I've got most of the code working so far, but I wanted to create a sort of stopgap for changing selection. I wanted to have the rollout close when you change selection. For some reason this returns an error of ">> MAXScript Callback script Exception: -- Type error: closeRolloutFloater requires RolloutFloater, got: undefined <<"

Despite the fact that I -am- calling it on a floater. I am confused.

The code for my tool is here:

macroScript BigLock category: "Testing"
(
on isEnabled return
(
selection.count == 1
)
on execute do
(
global boomrollout = "closeRolloutFloater(big_lock_float) \n"
boomrollout += "callbacks.removescripts id:#biglocks"
callbacks.addScript #selectionSetChanged boomrollout id:#biglocks
global keyhole = $
global lockarray = getTransformLockFlags (keyhole)
rollout lock_rol "Locks"
(
button lock "Lock All" width:77
button unlock "Unlock All" width:77

checkbutton posx "X" checked:lockarray[1] across:3 offset:[0,13]
on posx changed state do
(
lockarray[1] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton posY "Y" checked:lockarray[2]offset:[0,13]
on posY changed state do
(
lockarray[2] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton posZ "Z" checked:lockarray[3]offset:[0,13]
on posZ changed state do
(
lockarray[3] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton rotX "X" checked:lockarray[4] across:3 offset:[0,20]
on rotX changed state do
(
lockarray[4] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton rotY "Y" checked:lockarray[5] offset:[0,20]
on rotY changed state do
(
lockarray[5] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton rotZ "Z" checked:lockarray[6] offset:[0,20]
on rotZ changed state do
(
lockarray[6] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton scaleX "X" checked:lockarray[7] across: 3 offset:[0,20]
on scaleX changed state do
(
lockarray[7] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton scaleY "Y" checked:lockarray[8] offset:[0,20]
on scaleY changed state do
(
lockarray[8] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton scaleZ "Z" checked:lockarray[9] offset:[0,20]
on scaleZ changed state do
(
lockarray[9] = state
setTransFormLockFlags keyhole lockarray
)

groupBox posbox "Position" pos:[10,57] width: 87 height: 42
groupBox rotbox "Rotation" pos:[10,103] width: 87 height: 42
groupBox scalebox "Scale" pos:[10,149] width: 87 height: 42


on lock pressed do
(
posx.checked=true
posy.checked=true
posz.checked=true
rotx.checked=true
roty.checked=true
rotz.checked=true
scalex.checked=true
scaley.checked=true
scalez.checked=true
setTransformLockFlags keyhole #all
)
on unlock pressed do
(
posx.checked=false
posy.checked=false
posz.checked=false
rotx.checked=false
roty.checked=false
rotz.checked=false
scalex.checked=false
scaley.checked=false
scalez.checked=false
setTransformLockFlags keyhole #none
)

)
big_lock_float=newrolloutfloater "Big Lock" 120 230
addrollout lock_rol big_lock_float
)
)

Any help would be greatly appeciated. Is this as hidiously bloated as I imagine it to be?

RustyKnight
10-04-2007, 01:36 AM
Hello again,

So I've pretty much finished my rediculous little tool. I have two more questions.

* Cheers *


The first is fairly simple. I want to be able to call a function and have it change a variety of variables. This does not appear to be something functions can do, which confuses the heck out of me. I basically want to be able to trigger a section of code by calling a... something. Is this a function? If so, I'll have more questions.

Okay, I'm confused, I think you might need to explain yourself a little further...

I think where you are getting stuck is that variables are been passed by reference only, this basically means that you can't change the underlying "value" of the variable from within a function. You want to be able to pass by "value"...You need pefix your function parameter with "&" and the variable with "&" when passing the value...


fn test &aValue = (
-- This will change the "value" of the variable
aValue = 10
)

(
local aValue = 1
-- aValue is equal to 1
test &aValue
-- Will now print 10...
format "aValue = %\n" aValue
)


Passing by value and passing by reference is a relative simple concept, but it can take some time to get your head around...


Second: I've got most of the code working so far, but I wanted to create a sort of stopgap for changing selection. I wanted to have the rollout close when you change selection. For some reason this returns an error of ">> MAXScript Callback script Exception: -- Type error: closeRolloutFloater requires RolloutFloater, got: undefined <<"

Despite the fact that I -am- calling it on a floater. I am confused.

Ha, have the answer to this...

global boomrollout = "closeRolloutFloater(big_lock_float) \n"
boomrollout += "callbacks.removescripts id:#biglocks"
.
.
.
big_lock_float=newrolloutfloater "Big Lock" 120 230

Okay, the first line is good. No real problems...
The last line is the actually issue! big_lock_float is only defined as local to the on execute event handler, therefore when max tries to run you call back script, it is not able to find the variable, because the callback is occuring outside of the execute event handler...it's a scope issue...

At the top of the script add "global big_lock_float"...This will allow the callback mechanism to find it and it should close your rollout...

edt: Actually, put it at the top of the macro defination....

macroscript ... (
global big_lock_float
.
.
.
)


Keep it coming!!

Shane

Context
10-04-2007, 02:35 AM
Aha! It was a scope problem. I thought it might be something like that.

I tend to fail at communication. I want to do precisely what you are suggesting, but I don't want to have to send the variables to the function.

basically I want to to something like this:


function newobject
(
keyhole = $
lockarray = getTransformLockFlags (keyhole)
posx.checked = lockarray[1]
...
...
scalez.checked = lockarray[9]
)


The problem is, of course, that the function can't pass all those variables back to the rest of the script. Will it work if I pass all of the variables to the function, and use the "&" sign to send them all back?

Actually, yeah, that sounds like it should work. I'm going to go try that. If it doesn't work, I'll have more questions.

Thanks very much for the help.

RustyKnight
10-04-2007, 02:47 AM
You can access the controls in the rollout in 1 of 2 ways...

If you define a function within the rollout, the function will have scope to access the controls and modify them as required or for that fact, any variables you define within the context of the rollout...


rollout myRollOut ... (
local myVar -- I'm accessiable from within the context of the script...

button myButton ... -- I'm accessiable from within the context of the script....

fn myFun = (
myVar = "Something..."
myButton.text = myVar
)
)

Now, if you need to access them "outside" of the rollout, ie from within the macro script, you should be able to gain access via the rollout's variable name...I've found this to be problem matic as changes to a rollout when it is not visible tend to get ignored...in my experience...

So using the example above...

myRollout.myFun() -- This is calling the function inside my rollout
myRollout.myVar = "Something new" -- This is accessing the variable inside the rollout

I don't know if that helps any...

Cheers
Shane

Context
10-04-2007, 05:01 AM
Very interesting. Also, useful.

Does that mean that if you want to access a variable control within a rollout from outside the maxscript you have to "stack" additional locators?

By which I mean something like

myscript.myfloaterrollout.myrollout.myvariable.control

Or are they simply inaccessible?

RustyKnight
10-04-2007, 05:06 AM
Very interesting. Also, useful.

Does that mean that if you want to access a variable control within a rollout from outside the maxscript you have to "stack" additional locators?

By which I mean something like

myscript.myfloaterrollout.myrollout.myvariable.control

Or are they simply inaccessible?
Ahhh, no, but yes...

You don't need to specifiy the script, but you do need to make sure it's within it's current context...ie you've included the parent script so that the current script knows about it somehow...this is the tricky part

Bobo
10-04-2007, 02:44 PM
Very interesting. Also, useful.

Does that mean that if you want to access a variable control within a rollout from outside the maxscript you have to "stack" additional locators?

By which I mean something like

myscript.myfloaterrollout.myrollout.myvariable.control

Or are they simply inaccessible?

The only thing you cannot access is a MacroScript's body. Thus, you have to define the rollout as a global variable, then you can access anything inside incl. functions, controls and their event handlers from other scripts or the Listener.

MacroScript someMacro category:"Some Category"
(
global Somerollout
rollout Somerollout "Somerollout"
(
button someButton "Press me!"
on someButton pressed do print "Pressed!"
)
createDialog Somerollout
)

--press the icon to open the rollout, then type

Somerollout.somebutton.pressed()


Within a MacroScript, you can also declare all local variables used within the code in the BEGINNING of the Macro definition - this will make them visible to any code inside the Macro's body.

MacroScript someMacro category:"Some Category"
(
local SomeVariable
local anotherVariable
rollout Somerollout "Somerollout"
(
fn someFunction =
(
SomeVariable = 10
anotherVariable = "Some String"
)

button someButton "Press Me"
on someButton pressed do
(
someFunction()
print SomeVariable
print anotherVariable
)
)
createDialog Somerollout
)

RustyKnight
10-05-2007, 12:01 AM
Thanks Bobo! As always, lots of useful information!!

Shane

RustyKnight
10-05-2007, 12:18 AM
Thanks Bobo! As always, lots of useful information!!

Shane

Context
10-05-2007, 04:27 AM
Thanks again. Very useful.

I was pretty busy today, but I'll post my completed little tool tomorrow.

Huzzah!

I have another question however. Today I was messing around with rigging and how I could optimize my rigging through using maxscript (even simple macro's are amazingly efficient).

I was wondering if it were possible to wire a value from an object (like... Xrotation) to a spinner or control in a scripted rollout. I know that you could simply script the -effect- of the spinner, but I imagine it would be cleaner if you could use pre-existing systems.

Are there any functions that can do anything like this or is this a pipe-dream?

Context
10-07-2007, 05:17 AM
Hello again.

I've finished my locking mechanism. You can gaze upon it's code here:


macroScript BigLock category: "Testing"
(
on isEnabled return
(
selection.count == 1
)
on execute do
(
function newkeyhole shifter =
global keyhole = $
global lockarray = getTransformLockFlags (keyhole)
rollout lock_rol "Locks"
(
button lock "Lock All" width:77
button unlock "Unlock All" width:77

checkbutton posx "X" checked:lockarray[1] across:3 offset:[0,13]
on posx changed state do
(
lockarray[1] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton posY "Y" checked:lockarray[2]offset:[0,13]
on posY changed state do
(
lockarray[2] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton posZ "Z" checked:lockarray[3]offset:[0,13]
on posZ changed state do
(
lockarray[3] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton rotX "X" checked:lockarray[4] across:3 offset:[0,20]
on rotX changed state do
(
lockarray[4] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton rotY "Y" checked:lockarray[5] offset:[0,20]
on rotY changed state do
(
lockarray[5] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton rotZ "Z" checked:lockarray[6] offset:[0,20]
on rotZ changed state do
(
lockarray[6] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton scaleX "X" checked:lockarray[7] across: 3 offset:[0,20]
on scaleX changed state do
(
lockarray[7] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton scaleY "Y" checked:lockarray[8] offset:[0,20]
on scaleY changed state do
(
lockarray[8] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton scaleZ "Z" checked:lockarray[9] offset:[0,20]
on scaleZ changed state do
(
lockarray[9] = state
setTransFormLockFlags keyhole lockarray
)

groupBox posbox "Position" pos:[10,57] width: 87 height: 42
groupBox rotbox "Rotation" pos:[10,103] width: 87 height: 42
groupBox scalebox "Scale" pos:[10,149] width: 87 height: 42

on lock pressed do
(
posx.checked=true
posy.checked=true
posz.checked=true
rotx.checked=true
roty.checked=true
rotz.checked=true
scalex.checked=true
scaley.checked=true
scalez.checked=true
setTransformLockFlags keyhole #all
)
on unlock pressed do
(
posx.checked=false
posy.checked=false
posz.checked=false
rotx.checked=false
roty.checked=false
rotz.checked=false
scalex.checked=false
scaley.checked=false
scalez.checked=false
setTransformLockFlags keyhole #none
)
)
big_lock_float=newrolloutfloater "Big Lock" 120 230
addrollout lock_rol big_lock_float
)
)


It's working now, which is good, but I don't think it's terribly elegant in execution. Is there a way to optimize this? Is it unnecessarily large?

I am also working on a wiring tool to increase the speed at which I can do simple wiring.

It's... almost working. I decided that the best way for me to automate the input of a complex command would be to build a string that contained the code I wanted, and then to execute that code.

Unfortunately, the execute <script> function seems to be... broken? It just doesn't work. When I type in the code that my tool gives me, it works, but when I try to execute the script that contains EXACTLY the same code, it gives me this error:

>> MAXScript Wire Controller Exception: -- Unable to convert: undefined to type: Float <<
>> MAXScript Wire Controller Exception: -- Unable to convert: undefined to type: Float <<


I have no idea what it's doing or why. Could someone tell me why executre (string) doesn't work? Am I missing something? Any help would be deeply appreciated as this is kinda driving me nuts.

Here is what I have so far.

global mainrollout

rollout mainrollout "Wire Tool"
(
--The "WireThem" function builds a string based on the state of the UI. It then
--executes that string to wire the objects together.

function wirethem =
(
global connectstring

global string_selection
global string_select_plane

global string_target
global string_target_plane

global string_selfact = " \"" + mainrollout.selectfactor.text + "\""
global string_tarfact = " \"" + mainrollout.targetfactor.text + "\""

case mainrollout.selectplane.selection of
(
1: (string_select_plane = "[#X")
2: (string_select_plane = "[#Y")
3: (string_select_plane = "[#Z")
)

case mainrollout.selectparam.selection of
(
1: string_selection = " $.pos.controller" + string_select_plane + "_Position]"
2: string_selection = " $.rotation.controller" + string_select_plane + "_Rotation]"
3: string_selection = " $.transform.controller[#Scale]"
)

case mainrollout.targetplane.selection of
(
1: (string_target_plane = "[#X")
2: (string_target_plane = "[#Y")
3: (string_target_plane = "[#Z")
)

case mainrollout.targetparam.selection of
(
1: string_target = " $" + mainrollout.grabit.object.name + ".pos.controller" + string_target_plane + "_Position]"
2: string_target = " $" + mainrollout.grabit.object.name + ".rotation.controller" + string_target_plane + "_Rotation]"
3: string_target = " $" + mainrollout.grabit.object.name + ".transform.controller[#Scale]"
)

connectstring = "macros.run \"Parameter Wire\" \"paramWire\"\n"

case mainrollout.connectwire.state of
(
1: connectstring += "paramWire.connect" + string_target + string_selection + string_tarfact
2: connectstring += "paramWire.connect" + string_selection + string_target + string_selfact
3: connectstring += "paramWire.connect2way" + string_selection + string_target + string_selfact + string_tarfact
)

print connectstring

execute connectstring
)

function selectiontextswitch =
(
global plane

case mainrollout.selectplane.selection of
(
1: plane = "X"
2: plane = "Y"
3: plane = "Z"
)

case mainrollout.selectparam.selection of
(
1: mainrollout.selectfactor.text = plane + "Position"
2: mainrollout.selectfactor.text = plane + "Rotation"
3: mainrollout.selectfactor.text = "Scale"
)
)

function targettextswitch =
(
global plane

case mainrollout.targetplane.selection of
(
1: plane = "X"
2: plane = "Y"
3: plane = "Z"
)

case mainrollout.targetparam.selection of
(
1: mainrollout.targetfactor.text = plane + "Position"
2: mainrollout.targetfactor.text = plane + "Rotation"
3: mainrollout.targetfactor.text = "Scale"
)
)

-- This area defines the UI
listbox selectparam items: #("Position", "Rotation", "Scale") across: 2 height: 3 width: 80 offset: [3,15]
listbox selectplane items: #("X", "Y", "Z") height: 3 width: 80 offset: [4,15]
edittext selectfactor "Factor:" text: "XPosition"

pickbutton grabit "Select A Target" tooltip: "Pick an object" offset: [0,8] width: 189
radiobuttons connectwire labels:#("Up", "Down", "Mix") columns: 3

listbox targetparam items: #("Position", "Rotation", "Scale") across: 2 height: 3 width: 80 offset: [3,15]
listbox targetplane items: #("X", "Y", "Z") height: 3 width: 80 offset: [4,15]
edittext targetfactor "Factor:" text: "XPosition"

groupbox groupselect "Selection" pos:[5,5] width: 189 height: 90
groupbox grouptarget "Target" pos:[5,146] width: 189 height: 90

button linkit "Link" width: 92 offset: [-5,0] across: 2
button breakit "Break" width: 92 offset: [5,0]

-- Calling functions and pressing buttons

on selectparam selected useless do selectiontextswitch()
on selectplane selected useless do selectiontextswitch()
on targetparam selected useless do targettextswitch()
on targetplane selected useless do targettextswitch()

on grabit picked obj do
if isValidNode obj do grabit.tooltip = "Current Selection: " + obj.name

on linkit pressed do if grabit.object != undefined do wirethem()

)
createdialog mainrollout 200 267

Thank you in advance. :)

RustyKnight
10-07-2007, 06:44 AM
Hi Context!

Some nice work there my friend!

Personally, I wouldn't place ALL the functions and rollouts within the execute event handler, but that's just me. It might be better to put them within the body of the macroscript

macroscript ... (
-- Add functions that need to be accessed by both the macroscript and the roll outs...
fn myBigFatFunction ... (
)

rollout ... (
-- Add functions relevent to the rollout only
)

on execute do (
big_lock_float=newrolloutfloater "Big Lock" 120 230
addrollout lock_rol big_lock_float
)
)



I would also make "big_lock_float" global so you can dispose of any existing rollouts that might still be hanging around when the macroscript is re-run...ie

macroscript ... (
global big_lock_float -- note that we don't apply a value to it! This is important...

on execute do (
if big_lock_float != undefined do closerolloutfloter big_lock_float

-- Reinitalise the rollout here and continue on...
)
)

But that's just me...I like to try and stay clean!

I am also working on a wiring tool to increase the speed at which I can do simple wiring.
Please let me say, that this tool is really cool! You've really done a great job on it and you going to slap you're self when you discover why it won't work...

Firstly, lose
connectstring = "macros.run \"Parameter Wire\" \"paramWire\"\n"
It is simply not doing what you want and you don't need it for what you are trying to do...

I would also change the global variables listed in the "wirethem" function to local. I had some interesting issues while testing the code, with the connectstring gradually getting longer and longer because it was maintaining a context out side of the script. And as far as I can tell, they don't need to be. They only have relevance within the context of the function...

global connectstring

global string_selection
global string_select_plane

global string_target
global string_target_plane

global string_selfact = " \"" + mainrollout.selectfactor.text + "\""
global string_tarfact = " \"" + mainrollout.targetfactor.text + "\""


But now, onto the crust of the problem.

The real issue you are having is within the "expression" part of the paramwire interface...

Currently, your connect string is reading something like this:
paramWire.connect $Box01.pos.controller[#X_Position] $.pos.controller[#X_Position] "XPosition"
When you want it to read something like this:
paramWire.connect $Box01.pos.controller[#X_Position] $.pos.controller[#X_Position] "X_Position"
Can you spot the difference?

Look at the very last statement. Your code reads:
"XPosition"
When it should read:
"X_Position"

I know, it seems so small and insignificant, but there it is. Max does not know what "XPosition" is, because it is undefined, where as "X_Position" is the value of the controller, within the context of the expression editor.

So in the function "selectiontextswitch" you need to change the case statement to read

case mainrollout.selectparam.selection of
(
1: mainrollout.selectfactor.text = plane + "_Position"
2: mainrollout.selectfactor.text = plane + "_Rotation"
3: mainrollout.selectfactor.text = "Scale"
)

And, you need to change the controls "selectfactor" and "targetfactor" to

edittext selectfactor "Factor:" text: "X_Position"
edittext targetfactor "Factor:" text: "X_Position"


That should work. I've really knocked your code around during my testing, so I might have missed something, so if any one else sees something i've missed, please pipe in and let us know :thumbsup:

And, I have to say context, you've done some really nice code! I like what you've done and I like the way you've done it! Very nice!

I would normally dis-encourage you from using the execute function where you can, as it is very slow in its opertation, but for you now, it should suffice.

Take a look at the paramWire interface in the docs for more info, but essentially, you've already been using it ;)

Shane

Context
10-07-2007, 04:45 PM
Ha! Syntax is not my friend! Thank you very much for figuring that out. I would have smashed my brains out on that before realizing I was missing a single underscore.

The code I posted for my locking mechanism last time was an (inexplicalby) old version. I'm saving my scripts in two different locations (which is a bad move) so I accidentially posted an old and nonfunctional version. This one works! And it also does almost everything you suggested.

macroScript BigLock category: "Testing"
(
global keyhole = $
global lockarray = getTransformLockFlags (keyhole)
global lock_rol
global big_lock_float
global rebuildlock

function rebuildlock =
(
if $ != undefined do
(
keyhole = $
lockarray = getTransformLockFlags (keyhole)
lock_rol.posx.checked = lockarray[1]
lock_rol.posy.checked = lockarray[2]
lock_rol.posz.checked = lockarray[3]
lock_rol.rotx.checked = lockarray[4]
lock_rol.roty.checked = lockarray[5]
lock_rol.rotz.checked = lockarray[6]
lock_rol.scalex.checked = lockarray[7]
lock_rol.scaley.checked = lockarray[8]
lock_rol.scalez.checked = lockarray[9]
)

if big_lock_float.open != true do
(
callbacks.removescripts id:#biglocks
)
)

callbacks.addScript #selectionSetChanged "rebuildlock()" id: #biglocks persistent: false

on isEnabled return
(
selection.count == 1
)
on execute do
(
rollout lock_rol "Locks"
(
button lock "Lock All" width:77
button unlock "Unlock All" width:77

checkbutton posx "X" checked:lockarray[1] across:3 offset:[0,13]
on posx changed state do
(
lockarray[1] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton posY "Y" checked:lockarray[2]offset:[0,13]
on posY changed state do
(
lockarray[2] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton posZ "Z" checked:lockarray[3]offset:[0,13]
on posZ changed state do
(
lockarray[3] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton rotX "X" checked:lockarray[4] across:3 offset:[0,20]
on rotX changed state do
(
lockarray[4] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton rotY "Y" checked:lockarray[5] offset:[0,20]
on rotY changed state do
(
lockarray[5] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton rotZ "Z" checked:lockarray[6] offset:[0,20]
on rotZ changed state do
(
lockarray[6] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton scaleX "X" checked:lockarray[7] across: 3 offset:[0,20]
on scaleX changed state do
(
lockarray[7] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton scaleY "Y" checked:lockarray[8] offset:[0,20]
on scaleY changed state do
(
lockarray[8] = state
setTransFormLockFlags keyhole lockarray
)
checkbutton scaleZ "Z" checked:lockarray[9] offset:[0,20]
on scaleZ changed state do
(
lockarray[9] = state
setTransFormLockFlags keyhole lockarray
)

groupBox posbox "Position" pos:[10,57] width: 87 height: 42
groupBox rotbox "Rotation" pos:[10,103] width: 87 height: 42
groupBox scalebox "Scale" pos:[10,149] width: 87 height: 42

on lock pressed do
(
posx.checked=true
posy.checked=true
posz.checked=true
rotx.checked=true
roty.checked=true
rotz.checked=true
scalex.checked=true
scaley.checked=true
scalez.checked=true
setTransformLockFlags keyhole #all
)
on unlock pressed do
(
posx.checked=false
posy.checked=false
posz.checked=false
rotx.checked=false
roty.checked=false
rotz.checked=false
scalex.checked=false
scaley.checked=false
scalez.checked=false
setTransformLockFlags keyhole #none
)

)
big_lock_float=newrolloutfloater "" 120 227
addrollout lock_rol big_lock_float
)
)

As for my wiring box. It works too! Hurrah!

I started having problems with collectstring getting bigger and bigger as well... so I just changed it's initial definition to:

global connectstring = ""

Which works. Changing it to local caused a whole host of problems.

Here is the final code for my wiring doohicky. I think I might make icons for the "Up/Down/Mixed" section, but this is pretty much it.



global mainrollout

rollout mainrollout "Wire Tool"
(
--The "WireThem" function builds a string based on the state of the UI. It then
--executes that string to wire the objects together.

function wirethem =
(
global connectstring = ""

global string_selection
global string_select_plane

global string_target
global string_target_plane

global string_selfact = " \"" + mainrollout.selectfactor.text + "\""
global string_tarfact = " \"" + mainrollout.targetfactor.text + "\""

case mainrollout.selectplane.selection of
(
1: (string_select_plane = "[#X")
2: (string_select_plane = "[#Y")
3: (string_select_plane = "[#Z")
)

case mainrollout.selectparam.selection of
(
1: string_selection = " $.pos.controller" + string_select_plane + "_Position]"
2: string_selection = " $.rotation.controller" + string_select_plane + "_Rotation]"
3: string_selection = " $.transform.controller[#Scale]"
)

case mainrollout.targetplane.selection of
(
1: (string_target_plane = "[#X")
2: (string_target_plane = "[#Y")
3: (string_target_plane = "[#Z")
)

case mainrollout.targetparam.selection of
(
1: string_target = " $" + mainrollout.grabit.object.name + ".pos.controller" + string_target_plane + "_Position]"
2: string_target = " $" + mainrollout.grabit.object.name + ".rotation.controller" + string_target_plane + "_Rotation]"
3: string_target = " $" + mainrollout.grabit.object.name + ".transform.controller[#Scale]"
)

case mainrollout.connectwire.state of
(
1: connectstring += "paramWire.connect" + string_target + string_selection + string_tarfact
2: connectstring += "paramWire.connect" + string_selection + string_target + string_selfact
3: connectstring += "paramWire.connect2way" + string_selection + string_target + string_selfact + string_tarfact
)

-- print connectstring

execute connectstring
)


--This function adds presets to the selectfactor box

function selectiontextswitch =
(
global plane

case mainrollout.selectplane.selection of
(
1: plane = "X"
2: plane = "Y"
3: plane = "Z"
)

case mainrollout.selectparam.selection of
(
1: mainrollout.selectfactor.text = plane + "_Position"
2: mainrollout.selectfactor.text = plane + "_Rotation"
3: mainrollout.selectfactor.text = "Scale"
)
)

--This function adds presets to the targetfactor box

function targettextswitch =
(
global plane

case mainrollout.targetplane.selection of
(
1: plane = "X"
2: plane = "Y"
3: plane = "Z"
)

case mainrollout.targetparam.selection of
(
1: mainrollout.targetfactor.text = plane + "_Position"
2: mainrollout.targetfactor.text = plane + "_Rotation"
3: mainrollout.targetfactor.text = "Scale"
)
)

function shatter =
(
$.pos.controller.X_Position.controller = bezier_float ()
$.pos.controller.Y_Position.controller = bezier_float ()
$.pos.controller.Z_Position.controller = bezier_float ()

$.rotation.controller.X_Rotation.controller = bezier_float ()
$.rotation.controller.Y_Rotation.controller = bezier_float ()
$.rotation.controller.Z_Rotation.controller = bezier_float ()

$.scale.controller = bezier_scale ()

)

-- This area defines the UI
listbox selectparam items: #("Position", "Rotation", "Scale") across: 2 height: 3 width: 80 offset: [3,15]
listbox selectplane items: #("X", "Y", "Z") height: 3 width: 80 offset: [4,15]
edittext selectfactor "Factor:" text: "X_Position"

pickbutton grabit "Select A Target" tooltip: "Pick an object" offset: [0,8] width: 189
radiobuttons connectwire labels:#("Up", "Down", "Mix") columns: 3

listbox targetparam items: #("Position", "Rotation", "Scale") across: 2 height: 3 width: 80 offset: [3,15]
listbox targetplane items: #("X", "Y", "Z") height: 3 width: 80 offset: [4,15]
edittext targetfactor "Factor:" text: "X_Position"

groupbox groupselect "Selection" pos:[5,5] width: 189 height: 90
groupbox grouptarget "Target" pos:[5,146] width: 189 height: 90

button linkit "Link" width: 92 offset: [-5,0] across: 2
button breakit "Break" width: 92 offset: [5,0]

-- Calling functions and pressing buttons

on selectparam selected useless do selectiontextswitch()
on selectplane selected useless do selectiontextswitch()
on targetparam selected useless do targettextswitch()
on targetplane selected useless do targettextswitch()

on grabit picked obj do
if isValidNode obj do grabit.tooltip = "Current Selection: " + obj.name

on linkit pressed do if grabit.object != undefined do wirethem()
on breakit pressed do if $ != undefined do shatter()

)
createdialog mainrollout 200 267

It's pretty much the same as last time, except I added the "Shatter" function, which strips wiring parameters from an object by resetting it's Assigned Controllers.

This has been fun so far, except for the feeling of being stabbed in the brain. The utility of being able to make my own tools does exceed my frustration. Thanks again for the help.

I am still wondering if it was possible to wire an object parameter to a spinner created in a Maxscript. I am making an... odd rig, and I was hoping to create it's own floating rollout. Is this possible?

Oh yes... what alternatives to useing the Execute (string) function would you suggest? The only one I can think of is long lists of nested case statements. I gather the execute function is memory inefficient? How would I avoid using that statement, without having to add hundreds of more lines of code?

Thanks in advance!

RustyKnight
10-07-2007, 09:52 PM
macroScript BigLock category: "Testing"
(
global keyhole = $
global lockarray = getTransformLockFlags (keyhole)
global lock_rol
global big_lock_float
global rebuildlock


With global variables, you need to ask yourself, do I want to access this variable outside of this scope?? ie The macroscript...

A problem you might run into later on, is if you reuse variable names, which is something we all do from time to time. So if you have two global variables with the same name, you are actually using the singal variable, which will cause no end of head aches and unexpected problems that are next to near impossible to solve...especially if another script by someone else is using that variable ;)


As for my wiring box. It works too! Hurrah!

Alright!


I started having problems with collectstring getting bigger and bigger as well... so I just changed it's initial definition to:

global connectstring = ""

Which works. Changing it to local caused a whole host of problems.

Really? Worked okay for me. That would suggest to me that the connectstring value is been accessed out side the function or you are not initialising it properly...


Perhaps you need to move into the local scope of the rollout instead of the within the function? That way it is always avaliable within in the rollout, but the WHOLE max environment...

But if you look at this section here:

case mainrollout.connectwire.state of
(
1: connectstring += "paramWire.connect" + string_target + string_selection + string_tarfact
2: connectstring += "paramWire.connect" + string_selection + string_target + string_selfact
3: connectstring += "paramWire.connect2way" + string_selection + string_target + string_selfact + string_tarfact
)

This is the only section I recall you assigning a value to the connect string. The problem I see here is " += " assignment. Which is basically appending the right side value to the connctstring, which may be undefined, unless (and you have) initialised it.

You could write it:

case mainrollout.connectwire.state of
(
1: connectstring = "paramWire.connect" + string_target + string_selection + string_tarfact
2: connectstring = "paramWire.connect" + string_selection + string_target + string_selfact
3: connectstring = "paramWire.connect2way" + string_selection + string_target + string_selfact + string_tarfact
)
And since this is the main section you construct the string anyway, that should work.

I'm sorry, I'm very picky about variable scope, both from a memory point of view, but also from making sure that my variables don't accidently get changed some where else in my code...oh the horror stories I can tell...


I am still wondering if it was possible to wire an object parameter to a spinner created in a Maxscript. I am making an... odd rig, and I was hoping to create it's own floating rollout. Is this possible?
Off the top of my head, yes, I'm sure it is, but I can't recall how to do it right now...I'm suppose to be getting ready for work :P...I'll pull up some old code from my rigging script and see what I did.


Oh yes... what alternatives to useing the Execute (string) function would you suggest? The only one I can think of is long lists of nested case statements. I gather the execute function is memory inefficient? How would I avoid using that statement, without having to add hundreds of more lines of code?

Sometimes, there isn't one and to be honset for what you've done it works. It just tends to be very expensive from an execution point of view...

Instead of assigning all the values to strings, you keep references of the each of the object elements...

I have to get to work, so I will cut up the code there and post how I might do it then...

Shane

RustyKnight
10-08-2007, 12:43 AM
Okay, here is the "non execute" method;

fn getSubAnimValue iType iSubAnim = (
/*
local lstSubAnims = #(#(#X_Position, #Y_Position, #Z_Position),
#(#X_Rotation, #Y_Rotation, #Z_Rotation),
#(#X_Scale, #Y_Scale, #Z_Scale))
*/

-- Select the sub anim parameter based on the type
-- and "plane"
case iType of (
1:
case iSubAnim of (
1: return #X_Position
2: return #Y_Position
3: return #Z_Position
)
2:
case iSubAnim of (
1: return #X_Rotation
2: return #Y_Rotation
3: return #Z_Rotation
)
3:
case iSubAnim of (
1: return #X_Scale
2: return #Y_Scale
3: return #Z_Scale
)
)

return undefined
)

fn getController oNode iType = (
-- Get the object controller
case iType of (
1: return oNode.pos.controller
2: return oNode.rotation.controller
3: return oNode.transform.controller
)
)

-- Get the sub anim value that can be used by the
-- param wire interface...ie node.pos.controller[#X_Position]
fn getWireParameter oNode iType iSubAnim = (
return (getController oNode iType)[(getSubAnimValue iType iSubAnim)]
)

function wirethem =
(
-- Get the selection sub anim parameter
local selectAnim = getWireParameter $ mainrollout.selectparam.selection mainrollout.selectplane.selection
-- Get the target sub anim parameter
local targetAnim = getWireParameter mainrollout.grabit.object mainrollout.targetparam.selection mainrollout.targetplane.selection

-- Wire them together
case mainrollout.connectwire.state of
(
1: paramWire.connect targetAnim selectAnim string_tarfact
2: paramWire.connect selectAnim targetAnim string_selfact
3: paramWire.connect2way selectAnim targetAnim string_selfact
)
)


In fact, this is the long hand version, the short hand version would change the getSubAnimValue function to:

fn getSubAnimValue iType iSubAnim = (
local lstSubAnims = #(#(#X_Position, #Y_Position, #Z_Position),
#(#X_Rotation, #Y_Rotation, #Z_Rotation),
#(#X_Scale, #Y_Scale, #Z_Scale))
return lstSubAnims[iType][iSubAnim]
)

But I thought the case statement was slightly more readable...

Have fun
Shane

Context
10-08-2007, 01:50 AM
Variable Scope -> I get that. I had to go back and debug the lockscript because I was having scope problems all over the place. Local variables are happy.

I will be more careful with this in the future.

Non Execute Method -> I... have no idea what you're doing here, but I will find out. I do not know what subAnim's are, but I imagine that the documentation will tell me. Thanks again, for all your help.

Rig -> I was just thinking about this on the way home from a thanksgiving party. The easiest way I can think is just have the spinner in the code automatically update a spinner on an invisible object, and then wire the parameter you want to the spinner on the object. It's indirect, but it would work.

If you can think up a better way, I am all ears.

Doubleplus thanks.

RustyKnight
10-08-2007, 02:07 AM
Non Execute Method -> I... have no idea what you're doing here
That's alright, neither do i ;)...lol

ed: Seriously though, I am simply accessing the information directly from the object.

"subanims" are any animatable property that is visible as track in the Track ViewYou can basically access them via "named property access" methods...ie

$.pos.controller[#X_Position] -- Get the subanim for the x position of the position controller.

This is the information you pass to the paramwire interface that does all the work for you.

RustyKnight
10-08-2007, 03:02 AM
Rig -> I was just thinking about this on the way home from a thanksgiving party. The easiest way I can think is just have the spinner in the code automatically update a spinner on an invisible object, and then wire the parameter you want to the spinner on the object. It's indirect, but it would work.
I've had an really quick look at this. Simply, I created an object, dropped an attribute holder modifier onto and created a custom attribute named "Param1" which is a float wrapped around a spinner...Then I typed this...

paramWire.connect $.modifiers[#Attribute_Holder].Custom_Attributes[#Param1] $.pos.controller[#X_Position] "Param1"

And this gives control of the objects x-position over to the custom attribute. So yes, it can be done and it is all done on itself...

I'll hunt through my old code, because I know I've scripted custom attributes before...

Shane

RustyKnight
10-08-2007, 05:13 AM
Okay, I knew I had it tucked away some where...

This will create a box object, add a empty modifier, add a "custom attribute" and rollout to the empty modifier, wire it all together and finally select the object so you can see the handy work...

(
-- Create a box object
local oBox = box()

-- Create a new empty modifier for the custom attributes to reside in...
local modMyAttributes = EmptyModifier name:"My Attributes"

-- Add the new modifier to the box
addModifier oBox modMyAttributes

-- Construct the custom attributes
-- Look up scripted custom attributes, or get Pen's awesome rigging DVD set for
-- more details
local aMine = attributes MyCustomAttributes (
parameters main rollout:MyRollout (
-- This is the actual value we work with...it is what will appear in the
-- track view and parameter wiring dialog
fValue type:#float ui:spnValue default:0
-- Note that the ui parameter is pointing to the spinner below
)

-- This is what the user will see...
rollout MyRollout "My Controls" (
-- Changes to the spinner will be reflected in the fValue above
spinner spnValue "X Position: " type:#float range:[-10000, 10000, 0]
)
)

-- Add the custom attribute to the box
-- Note the way we reference the modifier!
custAttributes.add oBox.My_Attributes aMine

-- Wire it all together
paramWire.connect oBox.modifiers[#My_Attributes].MyCustomAttributes[#fValue] oBox.pos.controller[#X_Position] "fValue"

-- Switch the command panel over to the modify tab...
setCommandPanelTaskMode mode:#modify

-- Select the box so you can marvel...
select oBox
)

I've tried to highlight portions of the code that are important (hay, it's all important), but because we tend not to reference the a variable in the scene you might be use to, it can quickly become confusing.

Now have LOTs of fun!! :)

Shane

CGTalk Moderation
10-08-2007, 05:13 AM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.