Home Technical Talk

MaxScript Knowledge and Question Thread

1
polycounter lvl 15
Offline / Send Message
McGreed polycounter lvl 15
From purely selfish needs and from what I learned from talking with others, I decided to start a MaxScript knowledge and question thread, where we can post all the resources for MaxScript we got and post any scripting questions we have, without cluttering up the Tech Artist - What are you working on: FOREVER Edition! thread.

The Tech Artist thread is great for posting overall stuff related to MaxScript, such as releases of new scripts and so on, but it would be nice with a official thread (would be nice if it was sticky), where we can discuss and question/answer stuff about MaxScript without clutting up the thread with specific and general scripting questions.

It will also be easier for those who dabbling in MaxScript to find any info they want, without going though the whole (so far) 39 pages in the Tech thread, and post any simple or complex questions they might have.

If you have any additions to the links below, or suggestions, please send me a IM and I'll add them as soon as possible.


Sites, tutorials and examples:

Using DotNet In MAXScript by Bobo
Some examples using DotNet with maxscript and DotNet referances

Maxscript one-liners for artists
Scripts examples of how to script something into one line, which you might normally use several.

CGTalk - 3dsMax SDK and MaxScript subforum
MaxScript forum on CGTalk where there is lot of information to get.

CG++ Chinese MaxScript forum
A forum for MaxScript, in Chinese

Paul Neale and PEN Productions Inc. tutorials (not just MaxScript)
Some good tutorials and discussions about MaxScript. Also have tutorials to different areas of 3D.

ScriptAttack.com - Maxscript Snippets
A bunch of snippets of example codes

Homme 3D MaxScripts
Script examples and snippets

Emrah gunduz's maxscripts
A bunch of 'random' script examples

Enrico Gullotti - MaxScript
Some articles and scripts snippets for MaxScript

Ruben Garza - MaxScript
Some tutorials and scripts example for MaxScript

Raphael Steves - MaxScript
Lot of scripts examples and snippets

MaxScript made easy, a blog by Dave Wortley
Blog with script examples and explanations

Max Editor Tips: Abbreviations
How to make use of abbreviations to insert code blocks

Tools and resources:

Autodesk's official maxscript documentation
Official documentation for 3DS Max MaxScript

3ds Max Python API Documentation
Official documentation for 3ds Max Python API

3ds Max 2014 Python thread, with some help for first time user
Forum thread with good information about using Python and installing

Microsoft .NET Framework for referencing
Official documentation for DotNet

Using Notepad++ or VS as a maxscript IDE (External MaxScript IDE)
Develop Scripts and Manipulate 3dsMax from your favorite IDE or application

Sublime Text editor
Another alternative editor to edit maxscripts in. Use the link below to get it work with MaxScript.

Sublime Text Editor MXS Support
Thread on scriptspot with help and files for using Sublime Text editor to edit and run maxscript files

Autocomplete in the MaxScript Editor
How to use and enable autocomplete in 3DS Max Maxscript Editor

DarkScintilla color scheme for MaxScript Editor
A dark color scheme for the editor in MAX, that is more relaxing for your eyes, then the bright background there is normally.


Scripts (outside Polycount):

Scriptspot.com
Proberly the biggest collection of scripts, free/paid/open/closed source.

Jim Jagger's JJTools
Scripts by Jim Jagger, mainly aiming towards animation


Polycount MaxScripts threads:

Outliner 2.0
The Outliner 2.0 is a fast and easy to use scene management tool.

Handplane
Handplane is a new tool being developed by Luke Hodorwicz and Alec Moody with the goal of solving tangent basis mismatch issues across multiple game engines.

miauu's Script Pack vol. 2
miauu's Script Pack vol.2 is a set of scripts that will help you to make faster and easier some of the tedious task when you use 3ds Max.

Replies

  • McGreed
    Options
    Offline / Send Message
    McGreed polycounter lvl 15
    And just reserving the next post if needed. ;)
  • aleksdigital
  • marks
  • McGreed
    Options
    Offline / Send Message
    McGreed polycounter lvl 15
    Added it to first post, cheers.
  • tassel
  • McGreed
    Options
    Offline / Send Message
    McGreed polycounter lvl 15
    Updated.

    Got a question as well, I got a dialog window with some spinners, and I got a custom helper object which contains some parameters which can animated. Got two example scripts below, which can be used to show the function I want to achive.
    At the moment, if I change the spinner, it does change the parameter in the helper, but it's not automatic and if its animated, the spinner are not linked to the parameter and will not change value. I would like to link the dialog boxs UI to the parameters in the helper object, so if you change the spinners, the parameters automatic change, and the other way around as well (in case of animation).

    Using those two scripts below, just run both, put the helper in the scene from the Helper tab, keep the helper selected, change the spinner, then press "Update" on either the dialog or the Helper, and you see the text update. Now, turn AutoKey on and do an animation range from 1 to 50. If you move the time to 25 and the press the "Update" button, you can see the value is properly updated in the text, meaning that the parameter is animated.
    But the problem is that the spinner doesn't show this, and doesn't change since it's not linked. Currently the script update the parameters whenever I change the spinners.

    Helper script
    plugin Helper TestHelper
    name:"Test"
    classID:#(1084217646, -674216016)
    category:"Standard"
    extends:dummy
    version:1
    invisible:false
    (
        local rolloutHelper
        
        parameters parRollHelper --rollout:rolloutHelper
        (
            -- This parameter contains the coordinates
            pointPos type:#Point3 animatable:true default:[11,0,0]
        )
    
        fn printValues =
        (
            -- print the coordinates into the label in the UI for testing purpose
            rolloutHelper.lblValues.text = "pointPos: "+ pointPos as string + "\n"
        )
        
        rollout rolloutHelper "DST Helper" width:162 height:67
        (
            -- label with coordinates displayed
            button btnPrint "update" pos:[20,5] width:90 height:20
            label lblValues "" pos:[5,60] width:153 height:270
            
    
            on btnPrint pressed do
                printValues()
    
            on rolloutHelper open do
                printValues()
        )
    )
    
    Dialog script
        global newFloat
    
        rollout rollSpinner "Spinner" width:346 height:148
        (
            spinner pointPosX "X:" pos:[38,8] width:75 height:16 type:#worldunits
            spinner pointPosY "Y:" pos:[38,27] width:75 height:16 type:#worldunits
            spinner pointPosZ "Z:" pos:[38,46] width:75 height:16 type:#worldunits
            button btnUpdate "Update" pos:[120,33] width:90 height:20
            
            on btnTest1 pressed do
                $.printValues()
            
            on pointPosX changed val do
                $.pointPos.x = val
            on pointPosY changed val do
                $.pointPos.y = val
            on pointPosZ changed val do
                $.pointPos.z = val
        )
            
        newFloat = newRolloutFloater "rolloutMainDialog" 250 300 700 100
        addRollout rollSpinner newFloat
    
  • Pac_187
    Options
    Offline / Send Message
    Pac_187 polycounter lvl 11
    The spinners have a "controller" paramter, that should do the trick for you ;)

    See the help for an example:
    MAXScript Spinner UI Control
  • McGreed
    Options
    Offline / Send Message
    McGreed polycounter lvl 15
    Yeah, I looked at the controllers, however I keep getting an error. This is the string I changed to change the first entry in the point3 variable (x):

    spinner pointPosX "X:" pos:[38,8] width:75 height:16 type:#worldunits controller: ($.pointPos.x)

    but I get this error:

    -- Unable to convert: 0.0 to type: Controller
  • haiddasalami
    Options
    Offline / Send Message
    haiddasalami polycounter lvl 14
    i think the controller is looking for an actual type of controller and not an int/float as seen in the docs:

    [controller:(<controller>)]

    Dont have a working max at home but take a look at this example:
    utility foo "foo"
    (
    spinner ball_radius "Ball radius" range:[0,1000,1] \
    controller:($ball.radius.controller)
    )
    
  • McGreed
    Options
    Offline / Send Message
    McGreed polycounter lvl 15
    But it also says that you can link it to a parameter, and that's what I'm trying to do. :( Since it's a custom helper, there isn't any controller per say, unless that's something I need to create in the helper?
  • animax
    Options
    Offline / Send Message
    animax polycounter lvl 15
    Great topic McGreed!

    Here's one more:
    Max Editor Tips: Abbreviations
    by JHN's techart blog
  • monster
    Options
    Offline / Send Message
    monster polycounter
  • haiddasalami
    Options
    Offline / Send Message
    haiddasalami polycounter lvl 14
    McGreed wrote: »
    But it also says that you can link it to a parameter, and that's what I'm trying to do. :( Since it's a custom helper, there isn't any controller per say, unless that's something I need to create in the helper?

    From the docs:
    You can either specify a controller already assigned to the parameter you want to link to, or you can create the controller in your script and then assign that controller to the parameter of interest.

    Will try and see if i can get something to work. Havent used controllers much though thats probably my lack of indepth max knowledge.

    Also +1 to dark interface. Had to change some stuff around but love it.
  • McGreed
    Options
    Offline / Send Message
    McGreed polycounter lvl 15
    @monster
    Ahh that does look more comfortable to work with, added to list and to MAX now.

    @haiddasalami
    Yeah, that's my problem as well, I never really done anything with animation or controllers so far with maxscript, so are stumbling a bit. Would appericate if you could get any workable example to show, so I know what might be wrong, or if its not possible at all.
  • haiddasalami
    Options
    Offline / Send Message
    haiddasalami polycounter lvl 14
    Finally got it to work. Had to edit you're script a bit. Separated the point3 to separate floats (probably better way to do this) and then created controllers in script and attached them
    plugin Helper TestHelper
    name:"Test"
    classID:#(1084217646, -674216016)
    category:"Standard"
    extends:dummy
    version:1
    invisible:false
    (
        local rolloutHelper
        
        parameters parRollHelper --rollout:rolloutHelper
        (
            -- This parameter contains the coordinates
            pointPosX type:#Float animatable:true default:1.0	
    		pointPosY type:#Float animatable:true default:1.0
    		pointPosZ type:#Float animatable:true default:1.0
        )
    
        fn printValues =
        (
            -- print the coordinates into the label in the UI for testing purpose
    		b = [pointPosX,pointPosY,pointPosZ]
            rolloutHelper.lblValues.text = "pointPos: "+ b as string + "\n"
        )
        
        rollout rolloutHelper "DST Helper" width:162 height:67
        (
            -- label with coordinates displayed
            button btnPrint "update" pos:[20,5] width:90 height:20
            label lblValues "" pos:[5,60] width:153 height:270
            
    
            on btnPrint pressed do
                printValues()
    
            on rolloutHelper open do (
    			pointPosX.controller = bezier_float()
    			pointPosY.controller = bezier_float()
    			pointPosZ.controller = bezier_float()
                printValues()
    		)
        )
    )
    
        global newFloat
    
        rollout rollSpinner "Spinner" width:346 height:148
        (
            spinner pointPosX "X:" pos:[38,8] width:75 height:16 type:#worldunits controller:($.pointPosX.controller)
            spinner pointPosY "Y:" pos:[38,27] width:75 height:16 type:#worldunits controller:($.pointPosY.controller)
            spinner pointPosZ "Z:" pos:[38,46] width:75 height:16 type:#worldunits controller:($.pointPosZ.controller)
            button btnUpdate "Update" pos:[120,33] width:90 height:20
            
            on btnUpdate pressed do
                $.printValues()
            
            on pointPosX changed val do (
                $.pointPosX = val
    			$.printValues()
    		)
            on pointPosY changed val do (
                $.pointPosY = val
    			$.printValues()
    		)
            on pointPosZ changed val do (
                $.pointPosZ = val
    			$.printValues()
    		)
        )
            
        newFloat = newRolloutFloater "rolloutMainDialog" 250 300 700 100
        addRollout rollSpinner newFloat
    
  • McGreed
    Options
    Offline / Send Message
    McGreed polycounter lvl 15
    Thanks, I was afraid of that. I prefer that they are put into a point3, because its easier to work with, and because my other script has a LOT of spinners, so it would cut down on repeating coding.
    I still believe it might be possible to get it to work by using bezier_point3(), but maybe not. Until then, I will split it up as you suggested and take it from there. If I figure out how to keep it in a point3, I'll post it. Cheers.

    Oh a little thing, I noticed the controller is created by on rollout open, however what if the rollout never opens. With this example it does, because I used $, however I will be using named variables to get the object, which means the rollout is never open except when you manipulate the helper. So the controller won't initialize, would it? Wouldn't I need to create some kind of callback function?

    EDIT: Ah never mind the last part, on my walk to work, my brain started to work again and I realised that I could just put it into a function on the helper, and when my dialog box opens, it just calls it.
  • McGreed
    Options
    Offline / Send Message
    McGreed polycounter lvl 15
    I'm using a different machine then before and wanted to install Notepad++ 6.5.1 to use with my maxscripts, however the usual MaxScript definition files is crashing the program.

    Anyone got a working version of it?
  • McGreed
    Options
    Offline / Send Message
    McGreed polycounter lvl 15
    Regarding Notepad, I ended up taking the files I already had and try to recreate them again in Notepad++, which turned out to be easy enough (though there is some quirks).

    Just take the file below and save the file on your harddisk. Then in Notepad++, go to the menu Languages and pick "Define your language...". In there, choose "Import" and pick the file you just saved and you should now have a MaxScript option in the Language menu.

    I changed the background color from white to a grey-bluish one, but you can just do a search and replace on the XML file to replace with one you prefer. Easier then to manually change it in Notepad menu. :)

    Download this file and follow the instructions above:
    https://dl.dropboxusercontent.com/u/997277/3d/MaxScript.xml
  • Mrfred
    Options
    Offline / Send Message
    Mrfred polycounter lvl 4
    I'm stuck on something that seem really easy.

    While importing data from an xml I have to link the data to pre-referenced node
    So....
    as I import the xml string value I get "Ctrl_l_foot"
    but i need to convert that string to a variable and be Ctrl_l_foot_node.node ( remove the string)
    so basically transform "Ctrl_l_foot" into Ctrl_l_foot_node.node or
    something like $modifiers[1].myattributes.Ctrl_l_foot_node.node so I can access it
  • McGreed
    Options
    Offline / Send Message
    McGreed polycounter lvl 15
    If "Ctrl_l_foot" is an object/bone/node, you should be able to just use
    myNode = GetNodeByName("Ctrl_l_foot")
    I'm not quite sure what you mean with the last part.
  • Mrfred
    Options
    Offline / Send Message
    Mrfred polycounter lvl 4
    Thanks but I cant use the getNodeByName command as all the system is built using NodeTransformMonitor.

    All my controllers are stored and referenced inside a custom attribute... All i need to do is to access the stored node using the string from my xml

    That way I can copy the rig, merge it, rename controller and evrything still works (except for the xml part)
  • McGreed
    Options
    Offline / Send Message
    McGreed polycounter lvl 15
    So what you are getting from the xml is just a string with the name of the variable, correct?
    If that is the case, the only way I can come up right now is to use the "execute" command.
    So you have to put together the string before execute it, this little example might work:
    myArray = #("Ctrl_l_foot","Ctrl_r_foot","Ctrl_l_hand","Ctrl_r_hand")
    for i in myArray do
    (
        exCode = "myNode = $.modifiers[1].myattributes." + i as string + "_node.node"
        execute exCode
        print myNode
    )
    
  • Mrfred
    Options
    Offline / Send Message
    Mrfred polycounter lvl 4
    doesn't work for what I need here's my FUNC
    while enum.MoveNext() do
    (
    	animbuttonState = on
    	local objName 		= enum.current.Attributes.Item[0].Value
    	local targetCtrl = (objName + "_node.node" )
    	format "objName =%\n" targetCtrl
    )
    
    normally in this script I can select or get any object using:
    select whateverIWant_node.node I need to stay away from the $ and the usual selection stuff as I can not refer directly to the name of the object but rather to a variable ( which is not a string)

    shhh.... converting that string to a variable seemed so easy at first

    I'm about to redo/rethink my whole xml format in order to not store the controller name as a string

    EDIT: possible solution: use matchpattern and try to retrieve the #maxObject parameters stored in the CA

    EDIT #2:
    a= "a = 1"
    execute a
    returns 1

    but this logic wont work with the weak reference (sigh....)
  • McGreed
    Options
    Offline / Send Message
    McGreed polycounter lvl 15
    The problem with your function as it is, is that your variable is just a string and not really a variable.Your output will just be a string like this:

    objName = "mybone_node.node"

    instead of

    objName = mybone_node.node

    Which makes a huge difference.It should work if you execute the 5th line like this:
    execute ("local targetCtrl = " + objName + "_node.node")
    
    Then your targetCtrl would contain a node and not a string.
    Just do what you would normally do if you have a static name (ignoring the xml and hardcode the file), and then 'convert' the line into one you use execute.

    Through I still feel like that there is something I'm missing with your issue/example.

    If anyone else have some input, we can always use that. :)
  • Mrfred
    Options
    Offline / Send Message
    Mrfred polycounter lvl 4
    thanks for the help

    however, that command doesn't work either, everything that usually works throws error when I put it in my function (arghh..)
    Whatver, I found a workaround using matchpattern
  • monster
    Options
    Offline / Send Message
    monster polycounter
    Execute operates on a global scope, and returns the value of the expression. You need to capture the output, not place the variable in the expression. Also, you can't declare a local variable in global scope. Alternatively, you can use ReadExpr. Execute seems a tad faster because you don't have to convert the string to a stringStream.

    Mrfred, I know what you are trying to do. Use Weak References to avoid hard coding object names.

    If you format your data in something other than XML you can probably just ReadExp through the whole file very fast. Although, if your rig isn't complex this may not be an issue.

    In any case, heres an example of using both. Just change/build the myNode string.
    (
    	myNode = "$.modifiers[1].UpperArm.node"
    
    	--THIS
    	myTarget = readExpr (myNode as stringStream)
    
    	--OR THIS
    	myTarget = execute myNode 
    )
    
  • McGreed
    Options
    Offline / Send Message
    McGreed polycounter lvl 15
    Updated list with Sublime.

    @monster
    I never thought about using it like that, in my head it was just related to reading from files, so I didn't really need it, but neat enough. Any idea though, which one is most efficient in execution/resources?

    Also, I ran into an annoying problem, that would explain lot of the headaches I had earlier when testing. When I have certain variable like this:

    myVar1 = myVar2

    It points to the variable instead of the content of the variable. Any idea what I need to look up to know when and where this happens? I know it will happen with arrays, from what I found so far, and I seem to have the same issue with I'm dealing with matrixs. Also, any idea how to avoid it, because I had only partial success by using "myVar1 = copy myVar2" ?

    The issues gives me problems when I'm trying to multiply two matrixs (newMatrix = nMatrix1 + nMatrix2), sometimes it works and only my position and rotation is affected (as I would expect with the values in them), however half the time the scale gets doubled, with the same values. Its driving me nuts. I can show an example if needed.
  • haiddasalami
    Options
    Offline / Send Message
    haiddasalami polycounter lvl 14
    So something I found out interesting recently was I had something like this
    struct
    struct
    rollout
    rollout
    

    and inside the rollout control declaration I was able to initialize a struct but as soon as I put it into a macroscript, there were scope issues. Though I just moved everything to a
    struct
        struct
        struct
        rollout
    
    and initialize the struct, pointing a variable self to the new variable. Kinda reminds me of oop way of thinking. Wonder what approached you guys use for tools and what not. A lot of what I've seen has been the way I've been working which is functions rollouts etc within a macroscript.

    @McGreed: Post up an example probably easier to see what's happening and people can chime in. I know I probably sound like a hypocrite after what I just talked about. Hoping to release the tool soon.
  • McGreed
    Options
    Offline / Send Message
    McGreed polycounter lvl 15
    I wanted to look into using struct, since I still have a global and two I wanted to remove, but haven't got around it yet.

    Here is an example script of what I'm trying to do. The problem I have right now is that the two first objects will be placed correctly in relation to each another, but the ones afterwards don't (the take the same position as the second one) Also, they don't do it properly, if the dummy is rotated:

    EDIT: Added a picture of what I mean.
    (
        -- Create 3 boxes objects called TestBox1-3, and another 3 called ChildTestBox1-3
        -- And create a dummy object called $Dummy
        -- The ChildTestBox1-3 objects should mimic the position and rotation (and scale if  
        -- I can get it to work) related to the 'parents' objects, TestBox1-3, but relative to the
        -- dummy.
        local sourceNodes    = #($TestBox1, $TestBox2, $TestBox2)
        local childNodes    = #($ChildTestBox1, $ChildTestBox2, $ChildTestBox3)
        local sourceTransform = #() -- The relative transform between the first and x source object
        local nodeDummy = $Dummy -- the offset transform where the children will be moved to
        local firstDifference
        
        firstTransform = copy sourceNodes[1].transform -- The first source objects transform, used as start point
        sourceTransform[1] = matrix3 1 -- Create a neutral matrix
        sourceTransform[1].rotation = firstTransform.rotation -- Rotate the matrix to match first source obj
        for o = 1 to 3 do
        (
            -- Now loop through all source objects can find the transform difference between
            -- the first object and the o object
            if o != 1 do -- since 1 is already calculated, ignore
            (
                secondTransform = copy sourceNodes[o].transform
                sourceTransform[o] = secondTransform - firstTransform
            )
        )
        
        offsetTransform = nodeDummy.transform -- get the dummys transforms
        for x = 1 to 3 do
        (
            strTrans1 = copy offsetTransform
            -- Now move the objects in relation to the first object and the dummy
            in coordsys strTrans1 preTranslate strTrans1 sourceTransform[1].position
            in coordsys strTrans1 preRotate strTrans1 sourceTransform[1].rotation
            if x == 1 then
                childNodes[1].transform = copy strTrans1
            else
                childNodes[x].transform = ((copy strTrans1) + sourceTransform[x])
        )
    )
    
    matrixmadness.png
  • haiddasalami
    Options
    Offline / Send Message
    haiddasalami polycounter lvl 14
    So found an awesome bug with dotneteventhandlers and structs. If you try and print a property within a struct, the event handler doesnt know about the struct.
    Found a solution on

    http://forums.cgsociety.org/showthread.php?t=1023731.

    Basically put the struct into the dotnet control tag. So what I have is a self variable (cant use this as im inside a rollout. Need to test this but I assign the initialized variable to self so the struct knows about it. Hopefully I made sense :S

    myNewTool = newTool()
    myNewTool.self = myNewTool
    myNewTool.run()

    Dumping this in case someone encounters this.
  • monster
    Options
    Offline / Send Message
    monster polycounter
    McGreed, sourceNodes array is defined incorrectly. But other than that it works for me.

    However, to me it seems the script is too complex for what you are trying to do, and if sourceNodes[1] is scaled you get some odd behavior.

    The code below gives me the same result as your script when sourceNode[1] isn't scaled, but I think it does what you want when it is. The idea is to "remove" the sourceNode[1] transform then multiply it by the dummies. Of course I could be misunderstanding your goal. I just need more information.
    (	
        local sourceNodes = #($TestBox1, $TestBox2, $TestBox3)
        local childNodes = #($ChildTestBox1, $ChildTestBox2, $ChildTestBox3)
        local nodeDummy = $Dummy
    	
    	local transArray = #()
    	local nodeCount = sourceNodes.count
    	
    	for i = 1 to nodeCount do
    	(
    		childNodes[i].transform = sourceNodes[i].transform * inverse sourceNodes[1].transform * nodeDummy.transform
    	)
    )
    
  • McGreed
    Options
    Offline / Send Message
    McGreed polycounter lvl 15
    @monster
    You are right, the script kinda of evolved into that, because I couldn't get it to work, so I tried to work around it. My original script was actually exactly like yours, the only difference is that you used * and inverse, and I tried getting + and - to work with it.

    I guess that math with matrixs is different then doing it with ordinary math, and I must admit it doesn't make sense to me, that you multiply instead of add. But cheers, that does work and I can now trim the fat of my script and get it back to something like 1/10 the size.... and working. ;)
  • ZeroMoon
    Options
    Offline / Send Message
    Heya. I got a question about maxscript, which I couldn't get a final reply on, on CGTalk thread, so I was hoping someone here might help me.

    I'm trying to manipulate and animate a lot of objects, and I want to store the (animated) positions of them into a dummy.

    However, since the amount of objects varies and is dynamic, I cannot create a predefined amount of parameters, for example like myParaObjPos1, myParaObjPos2, myParaObjPos3 ect.

    So I want to store the positions into one array (or floattab as I was told would work on CGTalk), and that works fine, as long as I have the dummy parameters and UI the same place, however since I want to manipulate the values using a dialogbox, I cannot figure out how to link a single value in an array to a specific UI spinner in a dialog box.

    I got an example from denis on CGTalk where he used attributes, however it was for when parameters and UI was in the same place, and not in a dummy. When I try to use attribute on the dummy, I get an error.

    The script example would take one position in the floatTab and link it to the UI spinner, so that when either changed, they would both reflect that. It especially important because I want to store the animated values.

    This is my test example scripts, one for the dummy:
    plugin Helper TestDummy
      name:"TestDummy"
      classID:#(15555555, -12323112)
      category:"Standard"
      extends:dummy
      (
          global MultiVarAttribute = attributes MultiVarAttribute
          (
              parameters paraDummy
              (
                  testPos    type:#floatTab animatable:on tabsize:3 tabsizevariable:off
              )
          )
          local helperObj
          on getDisplayMesh do
          (
              if (helperObj == undefined) do helperObj = createInstance pyramid width:10 depth:10 height:10 widthsegs:1 setRenderable:0
              helperObj.mesh
          )
          
          tool create ( 
              on mousePoint click do
              (
                  viewTM = getCPTM()
                  nodeTM = (transMatrix worldPoint) * (inverse viewTM)
                  #stop
              )
          )
      )
    
    and one for the dialog (which isn't complete since I can't get past getting attribute to work):
     try(destroydialog testDummy) catch()
      
      curObj = $DummyObj
      
      rollout testDummy "Dialog" height:300 width:400 
      (
          spinner spnValue "Val: " pos:[55,56] width:45 height:16 enabled:true range:[1,1e+008,1] type:#float scale:1 \
              controller:(curObj.testPos[1])
      
          on spnValue changed val do
          (
              format "Spinner/Amount: % / %\n" val curObj.testPos
          )
          on testDummy open do
          (
              format "Amount: %\n" curObj.testPos
              --spnValue.controller = curObj.testPos[1]
          )
      )
      createDialog testDummy pos:[300, 200]
    
    Any help would be appreciated, I feel like I'm missing something here. :poly127:
  • McGreed
    Options
    Offline / Send Message
    McGreed polycounter lvl 15
    I actually tried to get my x,y,z into one parameter when I was working on updating my array script, but I never got it to work, so I would actually be interested if you ever get that to work.
    I haven't tried the attributes, might check it, however my question would be, how was it suppose to work? Link the parameter to a global attribute variable or something?
  • Volkrai
    Options
    Offline / Send Message
    Ok.. so I am currently working on a rollout that allows me to create geometric primitives, move and rotate selected objects, delete a selected object, create a blank or identity matrix, be able to modify the matrix, apply a matrix to a selected object, find the class of an object and the parent class, apply a class specific action, add animation keys to an object and then finally change the animation key curves on that object.

    I am stuck on that last part; the changing the animation key curves, I get an error when i run this saying "type error: array index must be positive number, got 0.. I can't for the life of me figure out where the error lies. All help is hugely appreciated.

    Below is my code so far:

    resetmaxfile #noPrompt

    resetmaxfile #noPrompt

    try (destroydialog myrollout) catch()

    rollout class_actions "class_actions" width:310 height:158
    (

    button move_class "Apply" pos:[120,91] width:60 height:40
    edittext x_class "X:" pos:[20,26] width:70 height:30
    edittext y_class "Y:" pos:[120,26] width:70 height:30
    edittext z_class "Z:" pos:[220,26] width:70 height:30

    on move_class pressed do
    (
    for obj in $* do (if classof selection[1] == classof obj
    then obj.pos +=[x_class.text as float,y_class.text as float, z_class.text as float]


    )
    )
    )

    fn findcurvetype in_out_curve =
    (
    case (in_out_curve) of
    (
    1: in_out_curve = #smooth
    2: in_out_curve = #fast
    3: in_out_curve = #slow
    4: in_out_curve = #linear
    5: in_out_curve = #step
    6: in_out_curve = #flat
    default: messagebox "Error"
    )
    return in_out_curve )

    rollout myrollout "myrollout" width:422 height:724

    (
    dropdownList Objectcreate "Select Object" pos:[36,17] width:76 height:40 items:#("box", "cylinder", "sphere", "pyramid", "torus")

    label lbl1 "Move Object" pos:[36,77] width:65 height:14

    label lbl3 "X Position:" pos:[23,101] width:53 height:16
    label lbl4 "Y Position:" pos:[23,136] width:57 height:16
    label lbl5 "Z Position:" pos:[23,169] width:58 height:19
    label lbl13 "X Rotation:" pos:[231,101] width:58 height:19
    label lbl14 "Y Rotation:" pos:[231,136] width:58 height:19
    label lbl15 "Z Rotation:" pos:[231,169] width:58 height:19
    label lbl93 "Parent Class :" pos:[122,411] width:73 height:15
    label classname "undefined" pos:[190,394] width:137 height:15
    label superclassname "undefined" pos:[191,411] width:137 height:15
    label translbl "Translation:" pos:[30,259] width:70 height:25
    label scalelbl "Scale:" pos:[30,301] width:70 height:25
    label rotlbl "Rotation:" pos:[30,343] width:70 height:25
    label lbl92 "Class :" pos:[123,394] width:43 height:15
    label lbl43 "Create a Matrix" pos:[36,223] width:82 height:18
    label lbl44 "Create Animation" pos:[36,465] width:96 height:27
    label animatexyz "Rotate:" pos:[31,487] width:45 height:22
    label lbl165 "Translate:" pos:[24,527] width:51 height:20
    label lbl238 "Edit Curve" pos:[28,586] width:68 height:17

    edittext move_x "" pos:[75,103] width:60 height:20
    edittext move_y "" pos:[75,136] width:60 height:20
    edittext move_z "" pos:[75,169] width:60 height:20
    edittext rotate_x "" pos:[283,103] width:60 height:20
    edittext rotate_y "" pos:[283,136] width:60 height:20
    edittext rotate_z "" pos:[283,169] width:60 height:20
    edittext xtrans "X:" pos:[100,259] width:50 height:25
    edittext ytrans "Y:" pos:[170,259] width:50 height:25
    edittext ztrans "Z:" pos:[240,259] width:50 height:25
    edittext xscale "1" pos:[100,301] width:50 height:25
    edittext yscale "1" pos:[170,301] width:50 height:25
    edittext zscale "1" pos:[240,301] width:50 height:25
    edittext xrot "X:" pos:[99,343] width:50 height:25
    edittext yrot "Y:" pos:[170,343] width:50 height:25
    edittext zrot "Z:" pos:[240,343] width:50 height:25
    edittext animate_x "X:" pos:[81,488] width:55 height:25
    edittext animate_y "Y:" pos:[148,487] width:55 height:25
    edittext animate_z "Z:" pos:[216,487] width:55 height:25
    edittext translate_x "X:" pos:[81,527] width:55 height:25
    edittext translate_y "Y:" pos:[148,527] width:55 height:25
    edittext translate_z "Z:" pos:[216,527] width:55 height:25
    edittext frame_number "Frame No:" pos:[282,486] width:98 height:21
    edittext edt53 "" pos:[336,466] width:0 height:0

    button apply_move "Apply" pos:[139,128] width:60 height:39
    button apply_rotation "Apply" pos:[347,128] width:60 height:39
    button delete_selection "Delete" pos:[159,676] width:99 height:32
    button creatematrix "Create Matrix" pos:[302,259] width:87 height:108
    button findclass "Find Class" pos:[58,392] width:61 height:36
    button classactions "Class Actions" pos:[254,392] width:128 height:37
    button apply_animation "Apply" pos:[286,518] width:97 height:35

    radiobuttons matrix_opt "" pos:[253,223] width:129 height:16 labels:#("identity", "blank") default:1 columns:2
    radiobuttons in_curve "In Curve" pos:[91,596] width:118 height:62 labels:#("Smooth", "Fast", "Slow", "Linear", "Step", "Flat") columns:2
    radiobuttons out_curve "Out" pos:[229,596] width:118 height:62 labels:#("Smooth", "Fast", "Slow", "Linear", "Step", "Flat") columns:2


    on Objectcreate selected sel do
    (
    print Objectcreate.selected
    case (Objectcreate.selected) of
    (
    "box": box isselected:on

    "cylinder": cylinder isselected:on

    "sphere": sphere isselected:on

    "pyramid": pyramid isselected:on

    "torus": torus isselected:on

    )
    )
    on apply_move pressed do
    (
    for obj in selection do obj.pos += [move_x.text as float,move_y.text as float, move_z.text as float]
    )
    on apply_rotation pressed do
    (

    obrot = eulerangles (rotate_x.text as float) (rotate_y.text as float) (rotate_z.text as float)

    for obj in selection do rotate obj obrot

    )
    on delete_selection pressed do
    (
    for obj in selection do delete $selection

    )
    on creatematrix pressed do
    (
    if matrix_opt.state == 1 then mymatrix = matrix3 1
    else mymatrix = matrix3 0

    mymatrix.row1.x = (xscale.text as float)
    mymatrix.row2.y = (yscale.text as float)
    mymatrix.row3.z = (zscale.text as float)

    mymatrix.row4.x = (xtrans.text as float)
    mymatrix.row4.y = (ytrans.text as float)
    mymatrix.row4.z = (ztrans.text as float)

    xrota = rotatexmatrix (xrot.text as float)
    yrota = rotateymatrix (yrot.text as float)
    zrota = rotatezmatrix (zrot.text as float)

    mymatrix = mymatrix*xrota*yrota*zrota



    for obj in selection do
    (
    obj.transform = mymatrix
    )
    )
    on findclass pressed do
    (
    classname.text = (classof selection[1]) as stringd
    superclassname.text = (superclassof selection[1]) as string

    )
    on classactions pressed do
    (
    createdialog class_actions
    )
    on apply_animation pressed do
    (
    with animate on
    (
    at time (frame_number.text as integer)

    (

    findcurvetype
    curve_in = findcurvetype in_curve.state
    curve_out = findcurvetype out_curve.state

    )
    for obj in selection do
    (
    rotval = eulerangles (animate_x.text as float)(animate_y.text as float)(animate_z.text as float)
    rotate obj rotval
    mykey = getkeyindex obj.pos.controller.x_position.controller (frame_number.text as time)
    obj.pos = [translate_x.text as float,translate_y.text as float,translate_z.text as float]
    obj.pos.controller.x_position.controller.keys[mykey].intangenttype =curve_in
    obj.pos.controller.y_position.controller.keys[mykey].intangenttype =curve_in
    obj.pos.controller.z_position.controller.keys[mykey].intangenttype =curve_in
    obj.pos.controller.x_position.controller.keys[mykey].intangenttype =curve_out
    obj.pos.controller.y_position.controller.keys[mykey].intangenttype =curve_out
    obj.pos.controller.z_position.controller.keys[mykey].intangenttype =curve_out
    )
    )
    )
    )



    createdialog myrollout
  • McGreed
    Options
    Offline / Send Message
    McGreed polycounter lvl 15
    From what I tested, apparently the keys array starts at 1 and not at 0, so you cannot have the frame number being 0.0 as that what mykey returns.
    Don't know if there is any reason for this, because I was sure that you could have keyframes in the -minus area.
  • Noors
    Options
    Offline / Send Message
    Noors greentooth
    If key index is 0 it means you have no key.
    Never dealt with animations but i don't think your "with animate on" is working.
    Also you try to access the position key before moving your object ? It certainly didn't create it.
    Why not use addNewKey ?

    edit: "at time" works only for the rest of the line, so you have to write something like
    theTime = frame_number.text as time
    rotval = eulerangles (animate_x.text as float)(animate_y.text as float)(animate_z.text as float)
    posval = [translate_x.text as float,translate_y.text as float,translate_z.text as float] 
    
    at time theTime obj.pos = posval
    
    at time theTime rotate obj rotval
    
    
  • Roca
    Options
    Offline / Send Message
    Hello Everybody.

    I got a strange problem with Max 2014.
    Render to Texture Wont open. Not over the menue bar as well the shortkey 0 doesen't work. I tried it in new files, in old ones. and I haven't the singles clue why this specific window won't open. All other render windows open probebly. I hope anyone could help.

    Thanks

    Roca
  • Primergy
    Options
    Offline / Send Message
    Quick question about maxscript

    I want to export some model information to a xml file.

    Currently I'm using a simple code like this to add new elements:

    format "<element>" to:example_file
    format "<\element>" to:example_file

    This works for most of the time, but now I want to add an attribute-entry (you know, those red dots in xml notepad) but I don't know if i can resemble this with my current approach?
  • monster
    Options
    Offline / Send Message
    monster polycounter
    Primergy,

    I recommend using dotNET in MaxScript for reading / writing XML. It's not very hard, and it's way faster than printing out an xml with format.

    Reading:
    http://paulneale.com/tutorials/dotNet/xml/xmlReading.htm

    Writing:
    http://paulneale.com/tutorials/dotNet/xml/xml.htm
  • Primergy
    Options
    Offline / Send Message
    Thank you for the tip, I will take a look into it :)

    But in theory - would it even be possible to replicate the xml tree with format?
  • monster
    Options
    Offline / Send Message
    monster polycounter
    Yeah, it's totally possible. If you really want to use format it's best to build each line instead of trying to edit lines you already printed to a file.
  • Joost
    Options
    Offline / Send Message
    Joost polycount sponsor
    This thread is a goldmine, thanks!

    I'm struggling with this simple script right now. I'd like to create a script that assigns a smoothing group to any face that has no smoothing groups assigned, in all selected objects, while ignoring any modifiers.

    I tried it with a for loop and a function but I can't figure out how to make it work with multiple objects.

    I'm probably doing this all wrong but I'm still quite inexperienced with maxscript.
    fn FixSmoothingGroups =
    	
    (
    	modPanel.setCurrentObject $.baseObject
    	Subobjectlevel = 4 
    	$.EditablePoly.selectBySmoothGroup -1835337729
    			(
    				max select invert
    			)
    	$.autoSmoothThreshold = 30
    	$.EditablePoly.autosmooth ()
    )
    	
    FixSmoothingGroups()
    
  • haiddasalami
    Options
    Offline / Send Message
    haiddasalami polycounter lvl 14
    Joost wrote: »
    This thread is a goldmine, thanks!

    I'm struggling with this simple script right now. I'd like to create a script that assigns a smoothing group to any face that has no smoothing groups assigned, in all selected objects, while ignoring any modifiers.

    I tried it with a for loop and a function but I can't figure out how to make it work with multiple objects.

    I'm probably doing this all wrong but I'm still quite inexperienced with maxscript.
    fn FixSmoothingGroups =
    	
    (
    	modPanel.setCurrentObject $.baseObject
    	Subobjectlevel = 4 
    	$.EditablePoly.selectBySmoothGroup -1835337729
    			(
    				max select invert
    			)
    	$.autoSmoothThreshold = 30
    	$.EditablePoly.autosmooth ()
    )
    	
    FixSmoothingGroups()
    
    fn FixSmoothingGroups objectArray =
    	
    (
    	for obj in objectArray do 
    	(
    		modPanel.setCurrentObject obj.baseObject
    		Subobjectlevel = 4 
    		obj.EditablePoly.selectBySmoothGroup -1835337729
    		(
    			max select invert
    		)
    		obj.autoSmoothThreshold = 30
    		obj.EditablePoly.autosmooth ()
    	)
    )
    
    selArray = selection as Array 
    FixSmoothingGroups selArray
    

    SelArray is basically your selection of objects passed into the function and you just cycle through each using the temp variable obj.
  • Joost
    Options
    Offline / Send Message
    Joost polycount sponsor
    Thanks for the quick fix and the explanation, much appreciated! :)
  • monster
    Options
    Offline / Send Message
    monster polycounter
    Joost, haiddasalami answer is 100% correct. I just wanted to show this alternate method. You can speed up the script significantly by avoiding updating the UI. For example on 4 default spheres your script runs at 740 ms, and the rewritten one below runs in 3 ms.
    (
    	t = timeStamp()
    	
    	mapped fn setUnassignedSmoothingGroupFaces obj =
    	(
    		fCount = polyOp.getNumFaces obj
    		result = #{}
    		result.count = fCount
    		
    		getFSmoothGroup = polyop.getFaceSmoothGroup	
    		for f = 1 to fCount where getFSmoothGroup obj f == 0 do
    		(
    			result[f] = true
    		)
    		
    		polyop.setFaceSelection obj result
    		obj.autoSmoothThreshold = 30
    		polyop.autoSmooth obj
    	)
    	
    	setUnassignedSmoothingGroupFaces selection
    	
    	format "% ms\n" (timeStamp()-t)
    )
    
  • Joost
    Options
    Offline / Send Message
    Joost polycount sponsor
    Thanks Monster! I considered trying the principle behind your script but I had no idea how to actually write the script. Very interesting to see the way you solved it.
  • Joost
    Options
    Offline / Send Message
    Joost polycount sponsor
    Does anyone know how to speed up this script? It's probably a pretty backwards way of doing it but it's the only way I could get it to work.
    The script is supposed to collapse the bottom modifier. It also seems to struggle with edit poly modifiers.
    (
    ModIndex = for m in $.modifiers where classof m != meshsmooth collect m 
    ModCount = ModIndex.count 
    maxOps.CollapseNodeTo $ ModCount true
    )
    
  • monster
    Options
    Offline / Send Message
    monster polycounter
    Joost, I'm not sure what the script is trying to do. You say "Collapse the bottom modifier." Do you mean collapse all modifiers beneath a Meshsmooth modifier? Right now it's just counting all the modifiers that aren't a Meshsmooth and collapsing that amount. But if you have more than one modifier above a Meshsmooth it's gone too.

    This will collapse everything below the top Meshsmooth. There's not really a way to make it faster. If you've got a lot of objects you can switch to the Creation tab to avoid UI updates.
    (
    	mods = $.modifiers
    	mCount = mods.count 
    	for i = 1 to mCount where classof mods[i] == meshsmooth do
    	(
    		maxOps.CollapseNodeTo $ (i+1) true
    		exit
    	)
    )
    
  • Joost
    Options
    Offline / Send Message
    Joost polycount sponsor
    Sorry I should have clarified. The script is only for single objects. I picked Meshsmooth because that's a modifier that I never use and I didn't really know a better way to phrase that part of the script.

    All it's supposed to do is collapse the modifier with the highest index to the base node. so for example in this image

    AMQg2bJ.png

    bend = index 1
    symmetry = index 2
    and unwrap = index 3.

    I only want to collapse the unwrap(lowest)modifier so to find the index of the bottom modifier I count the number of modifiers and then collapse the modifier with that index. As I said it's probably a very backwards way of doing it but it's the only way I could get it to work. The script does work, though there's a bit of a delay. And I was just wondering if there's a smarter way of doing it. (which there almost definitely is)

    I feel like this constantly when it comes to maxscript :poly142:

    HFfjWONm.jpg
1
Sign In or Register to comment.