Home Technical Talk

[MAXScript] Save/ Load SO Selection

interpolator
Offline / Send Message
Revel interpolator
EDIT; Seems like I found the solution already :)
Just replace the "EditPolyMod.SetSelection ePoly_obj #Face face_savedSelection" with "Edit_Poly.select ePoly_obj #Face face_savedSelection"..need to test some more, hopefully it wont break anything else.

Yeah, this weekend I really feel like wanted to learn some maxscript to improve my workflow, some of you guys might notice my other maxscript threads here recently, heh!

So, straight to the point; Save/ Load here is not a save selected object to .max file or something, but is save to memory to be recalled later on. Forgive me if it's a brute force code...

The code for save selection into memory;
( -- save SO selection into memory
    global vert_savedSelection = #{}
    global edge_savedSelection = #{}
    global face_savedSelection = #{}
    
    if (selection.count == 1) then
    (
        case (classOf $.modifiers[1]) of
        (
            undefinedClass:
            (
                global base_obj = $.baseobject
                case subobjectlevel of
                (
                    1:
                    (
                        num_vert = polyOp.getVertSelection base_obj
                        for sel in num_vert do
                        (
                            vert_savedSelection[sel] = true
                        )
                    )
                    
                    2:
                    (
                        num_edge = polyOp.getEdgeSelection base_obj
                        for sel in num_edge do
                        (
                            edge_savedSelection[sel] = true
                        )
                    )
                    
                    4:
                    (
                        num_faces = polyOp.getFaceSelection base_obj
                        for sel in num_faces do
                        (
                            face_savedSelection[sel] = true
                        )
                    )
                )
            )
            
            Edit_Poly:
            (
                global ePoly_obj = $.Edit_Poly
                case subobjectlevel of
                (
                    1:
                    (
                        num_vert = EditPolyMod.GetSelection ePoly_obj #Vertex
                        for sel in num_vert do
                        (
                            vert_savedSelection[sel] = true
                        )
                    )
                    
                    2:
                    (
                        num_edge = EditPolyMod.GetSelection ePoly_obj #Edge
                        for sel in num_edge do
                        (
                            edge_savedSelection[sel] = true
                        )
                    )
                    
                    4:
                    (
                        num_faces = EditPolyMod.GetSelection ePoly_obj #Face
                        for sel in num_faces do
                        (
                            face_savedSelection[sel] = true
                        )
                    )
                )
            )
        )
    )
)
And to recall it back;
( -- load SO selection from memory
    if (selection.count == 1) then
    (
        case (classOf $.modifiers[1]) of
        (
            undefinedClass:
            (
                case subobjectlevel of
                (
                    1:
                    (
                        if (vert_savedSelection != undefined) do
                        (
                            polyop.setVertSelection base_obj vert_savedSelection
                            max modify mode
                            modPanel.setCurrentObject base_obj
                            subobjectlevel = 1
                        )
                    )
                    
                    2:
                    (
                        if (edge_savedSelection != undefined) do
                        (
                            polyop.setEdgeSelection base_obj edge_savedSelection
                            max modify mode
                            modPanel.setCurrentObject base_obj
                            subobjectlevel = 2
                        )
                    )
                    
                    4:
                    (
                        if (face_savedSelection != undefined) do
                        (
                            polyop.setFaceSelection base_obj face_savedSelection
                            max modify mode
                            modPanel.setCurrentObject base_obj
                            subobjectlevel = 4
                        )
                    )
                )
            )
            
            Edit_Poly:
            (
                case subobjectlevel of
                (
                    1:
                    (
                        if (vert_savedSelection != undefined) do
                        (
                            EditPolyMod.SetSelection ePoly_obj #Vertex vert_savedSelection
                            max modify mode
                            modPanel.setCurrentObject ePoly_obj
                            subobjectlevel = 1
                        )
                    )
                    
                    2:
                    (
                        if (edge_savedSelection != undefined) do
                        (
                            EditPolyMod.SetSelection ePoly_obj #Edge edge_savedSelection
                            max modify mode
                            modPanel.setCurrentObject ePoly_obj
                            subobjectlevel = 2
                        )
                    )
                    
                    4:
                    (
                        if (face_savedSelection != undefined) do
                        (
                            EditPolyMod.SetSelection ePoly_obj #Face face_savedSelection
                            max modify mode
                            modPanel.setCurrentObject ePoly_obj
                            subobjectlevel = 4
                        )
                    )
                )
            )
        )
    )
)
Editable Poly object works as expected, but Edit Poly just can't recall the selection somehow, I know that it did put the face selection into the array because when I type face_savedSelection on the listener, it come up with the array of the selected polys. I'm guessing the line of "EditPolyMod.SetSelection ePoly_obj #Face face_savedSelection" didn't do the task that I intended it to do.

Does anyone have any idea for this?

Replies

  • miauu
    Offline / Send Message
    miauu polycounter lvl 13
    ( -- save SO selection into memory
        global vert_savedSelectionBA
        global edge_savedSelectionBA
        global face_savedSelectionBA
        
        if (selection.count == 1) then
        (
            case (classOf $.modifiers[1]) of
            (
                undefinedClass:
                (
                    global base_obj = $.baseobject
                    case subobjectlevel of
                    (
                        1:
                        (
                            vert_savedSelectionBA = polyOp.getVertSelection base_obj
                        )
                        
                        2:
                        (
                            edge_savedSelectionBA = polyOp.getEdgeSelection base_obj
                        )
                        
                        4:
                        (
                            face_savedSelectionBA = polyOp.getFaceSelection base_obj
                        )
                    )
                )
                
                Edit_Poly:
                (
                    case subobjectlevel of
                    (
                        1:
                        (
                            vert_savedSelectionBA = EditPolyMod.GetSelection $.Edit_Poly #Vertex
                        )
                        
                        2:
                        (
                            edge_savedSelectionBA = EditPolyMod.GetSelection $.Edit_Poly #Edge
                        )
                        
                        4:
                        (
                            face_savedSelectionBA = EditPolyMod.GetSelection $.Edit_Poly #Face
                        )
                    )
                )
            )
        )
    )
    
    ( -- load SO selection from memory
        if (selection.count == 1) then
        (
            case (classOf $.modifiers[1]) of
            (
                undefinedClass:
                (
                    case subobjectlevel of
                    (
                        1:
                        (
                            if (vert_savedSelectionBA != undefined) do
                            (
                                polyop.setVertSelection $ vert_savedSelectionBA
                            )
                        )
                        
                        2:
                        (
                            if (edge_savedSelectionBA != undefined) do
                            (
                                polyop.setEdgeSelection $ edge_savedSelectionBA
                            )
                        )
                        
                        4:
                        (
                            if (face_savedSelectionBA != undefined) do
                            (
                                polyop.setFaceSelection $ face_savedSelectionBA
                            )
                        )
                    )
                )
                
                Edit_Poly:
                (
                    case subobjectlevel of
                    (
                        1:
                        (
                            if (vert_savedSelectionBA != undefined) do
                            (
                                EditPolyMod.SetSelection $.Edit_Poly #Vertex #{}
                                EditPolyMod.Select $.Edit_Poly #Vertex vert_savedSelectionBA
                            )
                        )
                        
                        2:
                        (
                            if (edge_savedSelectionBA != undefined) do
                            (        
                                EditPolyMod.SetSelection $.Edit_Poly #Edge #{}
                                EditPolyMod.Select $.Edit_Poly #Edge edge_savedSelectionBA
                            )
                        )
                        
                        4:
                        (
                            if (face_savedSelectionBA != undefined) do
                            (
                                EditPolyMod.SetSelection $.Edit_Poly #Face #{}
                                EditPolyMod.Select $.Edit_Poly #Face face_savedSelectionBA
                            )
                        )
                    )
                )
            )
        )
    )
    

    Do not use:
    global vert_SavedSelection = #{}
    global vert_savedSelection = #{}
    global face_savedSelection = #{}

    because when you use the script to save the currently selected verts they will ba saved. When you use the script again to save the currently selected faces, the vert_SavedSelection will become an empty bitarray ana you will loose the saved vertices and edges.
  • monster
    Offline / Send Message
    monster polycounter
    This functionality already exists in 3DS Max.

    1. With sub objects selected just type a name into the Selection Sets drop down. Then to retrieve a selection just use the drop down list. You can modify stored sub object selections by pressing the button next to the drop down.
    2. In the ribbon the Stored Selections Panel. Is similar to the scripts you wrote.
  • Revel
    Offline / Send Message
    Revel interpolator
    @miauu - awesome! thanks so much for helping me on writing this script, you know that my previous iteration of code somehow didn't work anymore, I no idea why -_-" but your edited version works very smooth! :)

    Hmmmm...about the array #{}, so you mean that max can only save 1 array? why does the vert_savedSelection array can overwrite the content of edge_savedSelection array? since both refer to different array variable, no?

    And does the "BA" at the end of the vert_savedSelectionBA has any meaning?
    Thanks so much again miauu!

    @monster - hey man, thanks for the tips about selection sets drop down, to be honest I never used it before O_O but I guess with a shortcut key it'll be much more faster rather then navigating to the top toolbar to manage my selection sets, but I'm sure it'll be come in handy for a larger scene with many complex object :)
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 19
    Revel: Miauu is saying that you are resetting the global values to empty bitarrays every time you run the script, thus losing the previously save selection set. If you just define the global at the top of the script instead of assigning values to it then it wont be overridden every time you run it.

    The "BA" at the end probably just means this will be a "bitArray". Just a coding style.

    I've written some selection set scripts in the past that assigned random names automatically for me so I never had to type anything in. This was nice because you just select some mesh parts and then hit the hotkey to assign them to a new selection set. I dont care what they are called and retrieving them isnt a big deal either. Just select one polygon on the part I want to recall and then use another script to search the named selection sets that contain the poly and then set the selection to that selection set. Much faster than typing in names and browsing the drop down list. Although I hardly ever use the script because the selection sets tend to break during modeling when the vert/edge/poly ids get renumbered.
  • Revel
    Offline / Send Message
    Revel interpolator
    Ah that make perfect sense, thanks for clarification, Bryan! :)
  • miauu
    Offline / Send Message
    miauu polycounter lvl 13
    Bryan is right for everything. :)
  • monster
    Offline / Send Message
    monster polycounter
    I actually commented on the built in methods so that you might research the maxscript behind them. My first step in creating any tool is to see if it already exists.

    For example. the two scripts below are shorter and avoid extra global variables.
    (
    	if selection.count == 1 do
    	(
    		subLevel = case subobjectlevel of
    		(
    			1: 1
    			2: 2
    			3: 2
    			4: 3
    			5: 3
    			default: undefined
    		)
    		
    		case subLevel of
    		(
    			1: selStore = PolyBoost.SelStorage1 = PolyBGetSel 1
    			2: selStore = PolyBoost.SelStorage2 = PolyBGetSel 2
    			3: selStore = PolyBoost.SelStorage3 = PolyBGetSel 3
    			default: undefined
    		)
    	)
    )
    
    (
    	if selection.count == 1 do
    	(
    		subLevel = case subobjectlevel of
    		(
    			1: 1
    			2: 2
    			3: 2
    			4: 3
    			5: 3
    			default: undefined
    		)
    		
    		case subLevel of
    		(
    			1: if PolyBoost.SelStorage1 != undefined do PolyBSetSel 1 PolyBoost.SelStorage1
    			2: if PolyBoost.SelStorage2 != undefined do PolyBSetSel 2 PolyBoost.SelStorage2
    			3: if PolyBoost.SelStorage3 != undefined do PolyBSetSel 3 PolyBoost.SelStorage3
    			default: undefined
    		)
    	)
    	
    	max views redraw
    )
    
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 19
    Thanks for posting that example Monster! I didn't know you could store the results of a 'case of' expression into a variable. I've just used them to execute code in my scripts.


    And of course now that I go back and check the documentation for the case expression it clearly shows it being used that way in their example...
  • miauu
    Offline / Send Message
    miauu polycounter lvl 13
    I think that the guys like us(me, Bryan, monster) have to show to the beginners in maxscript several solutions(if there are several solutions) of one task, so they can learn something from each of the samples. Any of us also can learn something new. :)
  • Revel
    Offline / Send Message
    Revel interpolator
    Oh wow! that's an interesting approach with the 'case of' stuff. Yeah I notice the script that I wrote most of the time are duplicating within the script itself just because Editable Poly and Edit Poly are slightly different in maxscript. Looking at your example I will be able to do it like bellow I suppose;
    macroScript SelectSmart
    category:"_Smart"
    Tooltip:"Select Smart"
    ButtonText:"Select Smart"
    (
        local vert_savedSelectionBA
        local edge_savedSelectionBA
        local face_savedSelectionBA
        local base_obj = $.baseobject
        subLevel = case subobjectlevel of
            (
                1: 1
                2: 2
                3: 2
                4: 3
                5: 3
            )
        curMod = case (classOf $.modifiers[1]) of
            (
                baseMod: undefinedClass
                editMod: Edit_Poly
            )
        keyPressed = case of
            (
                ctrl: (keyboard.controlPressed and not keyboard.shiftPressed and not keyboard.altPressed)
                shift: (keyboard.shiftPressed and not keyboard.controlPressed and not keyboard.altPressed)
                alt: (keyboard.altPressed and not keyboard.controlPressed and not keyboard.shiftPressed)
                ctrl_shift: (keyboard.shiftPressed and keyboard.controlPressed and not keyboard.altPressed)
                ctrl_alt: (keyboard.altPressed and keyboard.controlPressed and not keyboard.shiftPressed)
            )
    
        case keyPressed of
        (
            default: macros.run "Outliner" "toggleOutliner"
            ctrl: max select all
            shift: max select invert
            alt: max select none
            ctrl_shift:
            ( -- save SO selection into memory
                if selection.count == 1 do
                (
                    case subLevel of
                    (
                        1: case curMod of
                        (
                            baseMod: vert_savedSelectionBA = polyOp.getVertSelection base_obj
                            editMod: vert_savedSelectionBA = EditPolyMod.GetSelection $.Edit_Poly #Vertex
                        )
                        2: case curMod of
                        (
                            baseMod: edge_savedSelectionBA = polyOp.getEdgeSelection base_obj
                            editMod: edge_savedSelectionBA = EditPolyMod.GetSelection $.Edit_Poly #Edge
                        )
                        3: case curMod of
                        (
                            baseMod: face_savedSelectionBA = polyOp.getFaceSelection base_obj
                            editMod: face_savedSelectionBA = EditPolyMod.GetSelection $.Edit_Poly #Face
                        )
                    )
                )
            )
            ctrl_alt:
            (
                if selection.count == 1 do
                (
                    case subLevel of
                    (
                        1: case curMod of
                        (
                            baseMod: if (vert_savedSelectionBA != undefined) do polyop.setVertSelection $ vert_savedSelectionBA
                            editMod: if (vert_savedSelectionBA != undefined) do
                                (
                                    EditPolyMod.SetSelection $.Edit_Poly #Vertex #{}
                                    EditPolyMod.Select $.Edit_Poly #Vertex vert_savedSelectionBA
                                )
                        )
                        2: case curMod of
                        (
                            baseMod: if (edge_savedSelectionBA != undefined) do polyop.setEdgeSelection $ edge_savedSelectionBA
                            editMod: if (edge_savedSelectionBA != undefined) do
                                (        
                                    EditPolyMod.SetSelection $.Edit_Poly #Edge #{}
                                    EditPolyMod.Select $.Edit_Poly #Edge edge_savedSelectionBA
                                )
                        )
                        3: case curMod of
                        (
                            baseMod: if (face_savedSelectionBA != undefined) do polyop.setFaceSelection $ face_savedSelectionBA
                            editMod: if (face_savedSelectionBA != undefined) do
                                (
                                    EditPolyMod.SetSelection $.Edit_Poly #Face #{}
                                    EditPolyMod.Select $.Edit_Poly #Face face_savedSelectionBA
                                )
                        )
                    )
                )
            )
        )
    )
    
    Is it good? bad? or any other way to trim it some more? (currently I trim 56 lines of code using this approach compare to my earlier brute force version, and it sure does looks more pretty haha!)

    Thanks guys, couldn't do it without your help :)
  • Revel
    Offline / Send Message
    Revel interpolator
    Sorry, quick question; from the above example which one do you guys prefer to write from bellow option and why?
    case subLevel of
    (
        1: case curMod of
        (
            baseMod: -- content
            editMod: -- content
        )
        2: case curMod of
        (
            baseMod: -- content
            editMod: -- content
        )
        3: case curMod of
        (
            baseMod: -- content
            editMod: -- content
        )
    )
    
    OR
    case curMod of
    (
        baseMod: case subLevel of
        (
            1: -- content
            2: -- content
            3: -- content
        )
        editMod: case subLevel of
        (
            1: -- content
            2: -- content
            3: -- content
        )
     )
    
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 19
    There's nothing wrong with either of those methods from what I can see. They do the same thing so its what ever you prefer.

    Now as far as the actual code you're writing and what you're trying to accomplish? That can be improved upon but it of course takes time and experience. I'm still learning everyday. I'm sure this code i'm about to post can be improved upon even more.

    Try to think of better ways for compacting or re-suing your code, look for tricks in other scripts that you may not already know about to help you improve.

    Here's a way of storing selections to a variable and recalling them all from the same function without specifying edit poly mod or editable poly base case expressions. Im able to do this because of the 'PolyBGetSel' and 'PolyBSetSel' functions work in either case. The help for these functions seem to be missing from the help files though :(
    fn isValidEPolySelection =
    (
    	-- Validation function to test if the correct paramters are preset for the script to run
    	curr_mod = modPanel.GetCurrentObject()
    	if selection.count == 1 and (classOf curr_mod == Editable_Poly or classOf curr_mod == Edit_Poly) then return True else return False
    )
    
    fn selectionManager PolyBFunction selection_BA: =
    (
    	savedSelectionBA = #{}
    	if isValidEPolySelection() then
    	(
    		if (subobjectLevel == 1) then savedSelectionBA = PolyBFunction 1 selection_BA
    		else if (subobjectLevel == 2) or (subobjectLevel == 3) then savedSelectionBA = PolyBFunction 2 selection_BA
    		else if (subobjectLevel == 4) or (subobjectLevel == 5) then savedSelectionBA = PolyBFunction 3 selection_BA
    	)
    	max views redraw
    	return savedSelectionBA
    	
    )
    
    -- USAGE:
    -- The PolyBGetSel and PolyBSetSel functions work on both editable poly and edit poly modifiers.
    -- For some reason I cant find the reference to them in the MAXSCRIPT help files
    
    new_selection = selectionManager( PolyBGetSel ) --save the selection to a variable
    selectionManager PolyBSetSel selection_BA:new_selection --retrieve the selection
    
  • Revel
    Offline / Send Message
    Revel interpolator
    Hi Bryan, that's too complex for me to understand for now O_O
    Haven't really read anything about defining a function though, so might come back here in the future after I more familiar with function :)

    Anyway, is it a good idea to define these codes below as a global on 1 file for other scripts use? So I don't have to type case of keyboard.controlPressed or other common one each time I want to use it inside my script?
    -- global variable collection
    global xSubLevel = case subobjectlevel of
        (
            1: 1
            2: 2
            3: 2
            4: 3
            5: 3
        )
    global xCurMod = case (classOf $.modifiers[1]) of
        (
            baseMod: undefinedClass
            editMod: Edit_Poly
        )
    global xKeyPressed = case of
        (
            ctrl: (keyboard.controlPressed and not keyboard.shiftPressed and not keyboard.altPressed)
            shift: (keyboard.shiftPressed and not keyboard.controlPressed and not keyboard.altPressed)
            alt: (keyboard.altPressed and not keyboard.controlPressed and not keyboard.shiftPressed)
            ctrl_shift: (keyboard.shiftPressed and keyboard.controlPressed and not keyboard.altPressed)
            ctrl_alt: (keyboard.altPressed and keyboard.controlPressed and not keyboard.shiftPressed)
        )
    
    EDIT_01: woops all the above need to wrap inside "if selection.count == 1 do" otherwise it will throw modifiers is undefined on max start.
    EDIT_02: seems like it's not working, assign it to the global variable like that just wont work as expected..oh well..
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 19
    Revel: I would suggest going through the help file tutorials, they are good at explaining the basics of scope and local/global variables. If you want to use globals then try to give them a more personal variable name to prevent other scripts from overriding them. What if you installed another script that used xCurMod? Yours or theirs would break the others scripts. Give them a name like 'revelSubLevel', 'revelCurMod' and so on. The chance of someone using that name is pretty low.

    Wrapping your global code inside a selection.count block will not work because max will only define those globals if your selection.count == 1 so if you want to use xKeyPressed on multiple objects but have not yet used it on a single object then it would not be defined and would throw a undefined error.

    Also your xCurMod code doesnt work correctly. Its currently on looking at the top level modifier not the currently selected modifer.
    classOf (modPanel.getCurrentObject())
    

    ^is what you want to use to get the currently selected modifier

    Check out the maxscript help tutorials and also check out these videos http://vimeo.com/album/1514565


    By the way here's a way to get a single global variable for all those functions by using structs. I know this may be too complex for you right now but its the way a lot of scripters have been defining larger libraries in the past few years.
    --global scope here... define our single global variable
    global gRevel
    
    (
    	-- In local scope here because of our brackets. Define our struct to contain all our functions and variables
    	struct Revel_Struct
    	(
    		fn xSubLevel =
    		(
    			case subobjectlevel of
    			(
    				1: 1
    				2: 2
    				3: 2
    				4: 3
    				5: 3
    			)
    		),
    		
    		fn xCurMod =
    		(
    			case ( classOf (modPanel.getCurrentObject()) ) of
    			(
    				baseMod: undefinedClass
    				editMod: Edit_Poly
    			)
    		),
    		
    		fn xKeyPressed =
    		(
    			case of
    			(
    				ctrl: (keyboard.controlPressed and not keyboard.shiftPressed and not keyboard.altPressed)
    				shift: (keyboard.shiftPressed and not keyboard.controlPressed and not keyboard.altPressed)
    				alt: (keyboard.altPressed and not keyboard.controlPressed and not keyboard.shiftPressed)
    				ctrl_shift: (keyboard.shiftPressed and keyboard.controlPressed and not keyboard.altPressed)
    				ctrl_alt: (keyboard.altPressed and keyboard.controlPressed and not keyboard.shiftPressed)
    			)
    		)
    	)
    )
    
    
    -- Assign the struct to our global variable
    gRevel = Revel_Struct()
    
    print ( gRevel.xSubLevel() )
    
  • Revel
    Offline / Send Message
    Revel interpolator
    Yeah, you're right. I shouldn't straight away jump into scripting without knowing the basic stuff. Thanks man!
  • Revel
    Offline / Send Message
    Revel interpolator
    Hmm, I've been reading the maxscript manual, haven't really go through section-by-section though. But already try to understand how fn and struct works.
    Anyway Bryan, does the script above suppose to work if I put on startup folder? so it auto run everytime, but currently it gives me;
    -- Type error: Call needs function or class, got: undefined
    
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 19
    Whoops error on my part. I assigned the gRevel variable to Revel_Struct() but it's not visible outside those brackets. So its returning undefined. Fixed here:
    --global scope here... define our single global variable
    global gRevel
    
    (
    	-- In local scope here because of our brackets. Define our struct to contain all our functions and variables
    	struct Revel_Struct
    	(
    		fn xSubLevel =
    		(
    			case subobjectlevel of
    			(
    				1: 1
    				2: 2
    				3: 2
    				4: 3
    				5: 3
    			)
    		),
    		
    		fn xCurMod =
    		(
    			case ( classOf (modPanel.getCurrentObject()) ) of
    			(
    				baseMod: undefinedClass
    				editMod: Edit_Poly
    			)
    		),
    		
    		fn xKeyPressed =
    		(
    			case of
    			(
    				ctrl: (keyboard.controlPressed and not keyboard.shiftPressed and not keyboard.altPressed)
    				shift: (keyboard.shiftPressed and not keyboard.controlPressed and not keyboard.altPressed)
    				alt: (keyboard.altPressed and not keyboard.controlPressed and not keyboard.shiftPressed)
    				ctrl_shift: (keyboard.shiftPressed and keyboard.controlPressed and not keyboard.altPressed)
    				ctrl_alt: (keyboard.altPressed and keyboard.controlPressed and not keyboard.shiftPressed)
    			)
    		)
    	)
    	
    	-- Assign the struct to our global variable
    	gRevel = Revel_Struct()
    )
    
  • Revel
    Offline / Send Message
    Revel interpolator
    Alright, thanks Bryan, you are a genius! :)
    Found something that doesn't work with the previous code and manage to make it to work properly, but thought I'd like ask the other experienced maxscripter of what's actually going on here;
    The problem is on the keyPressed, if I use the code;
    ctrl: (keyboard.controlPressed and not keyboard.shiftPressed and not keyboard.altPressed)
    shift: (keyboard.shiftPressed and not keyboard.controlPressed and not keyboard.altPressed)
    alt: (keyboard.altPressed and not keyboard.controlPressed and not keyboard.shiftPressed)
    none: default
    
    And on my script:
    case of
    (
        ctrl: -- do something
        shift: -- do something
        alt: -- do something
        none: -- do something
    )
    
    It just won't work, but if I use like below;
    (keyboard.controlPressed and not keyboard.shiftPressed and not keyboard.altPressed): return #ctrl
    (keyboard.shiftPressed and not keyboard.controlPressed and not keyboard.altPressed): return #shift
    (keyboard.altPressed and not keyboard.controlPressed and not keyboard.shiftPressed): return #alt
    default: return #none
    
    And on my script;
    case of
    (
        #ctrl: -- do something
        #shift: -- do something
        #alt: -- do something
        #none: -- do something
    )
    
    It is doing what I ask it to do.
    Is this the correct way of doing it? Actually I found the tips from bigley's script.

    PS: note that if I didn't use # before the ctrl/shift/alt/none, the script become not working.
    But it doesn't work like that on subobjectlevel, without # still able to detect;
    case of
    (
        1: print "vertex"
        2: print "edge"
        3: print "face"
    )
    
    subLevel 1 -- "vertex"
    subLevel 2 and 3 --"edge"
    subLevel 4 and 5 -- "face"
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 19
    Thats because ctrl, shift, alt, and none in the first script are not what you think. They are being used as variables there that are not defined. ctrl, shift, alt, and none are not keywords known to maxscript so its thinking they are just variables you've assigned somewhere else. When you add # in front of a name they become name values http://docs.autodesk.com/3DSMAX/16/ENU/MAXScript-Help//index.html?query=Name%20Values

    If you want to use those variables and not name values then assign the keyboard pressed code to those variables and use them in your case of expression.
    (
    	
    	ctrl = (keyboard.controlPressed and not keyboard.shiftPressed and not keyboard.altPressed)  -- returns true or false
    	shift = (keyboard.shiftPressed and not keyboard.controlPressed and not keyboard.altPressed) -- returns true or false
    	alt = (keyboard.altPressed and not keyboard.controlPressed and not keyboard.shiftPressed) -- returns true or false
    	
    	case of
    	(
    		ctrl: --do something
    		shift: --do something
    		alt: --do something
    	)
    	
    )
    

    But why do you feel the need to do it this way? I would suggest using it the way that works already as I described here http://www.polycount.com/forum/showpost.php?p=1656293&postcount=92
  • Revel
    Offline / Send Message
    Revel interpolator
    Dang! that's the thread that started me doing all this maxscript stuff few weeks ago. Read too many pages of words makes me forgot the very first thing that I read which started my journey onto maxscript >_< part of it because back then I have no idea of what fn is and how to use those 2 scripts together, that's why it didn't stuck to my head..hehe

    Nah, I didn't feel like want to use it without # though, the question is just out of curiosity.
    Thanks so much, Bryan!
  • Revel
    Offline / Send Message
    Revel interpolator
    EDIT: Hmmm..found out that actually the "default: genericMode" that cause a problem..somehow....using the "default:" on the individual script works.

    Oh man, maxscript just keep on refuse to be friend with me T_T
    Here's the situation; I got the following code on my startup script based on Bryan suggestion and organize stuff using struct and function;
    global glRevel
    (
        struct stRevel
        (
            -- some other functions...
    
            fn fnRevel_curClass =
            (
                case (classOf (modPanel.getCurrentObject())) of
                (
                    Editable_Poly: editableMode
                    Edit_Poly: editMode
                    Unwrap_UVW: uvMode
                    default: genericMode
                )
            ),
    
            -- some other functions...
        )
        glRevel = stRevel()
    )
    
    And I have this on the individual script;
    (
        case selection.count of
        (
            0:
            case glRevel.fnRevel_modKey() of
            (
                #none: max snap toggle
                #alt: max angle snap toggle
            )
            
            1:
            case glRevel.fnRevel_curClass() of
            (
                genericMode:
                (
                    print "genericMode"
                    print (classOf (modPanel.getCurrentObject()))
                    case glRevel.fnRevel_modKey() of
                    (
                        #none: max snap toggle
                        #alt: max angle snap toggle
                    )
                )
                
                uvMode:
                (
                    print "uvMode"
                    print (classOf (modPanel.getCurrentObject()))
                    case glRevel.fnRevel_modKey() of
                    (
                        #none: actionMan.executeAction 2077580866 "40132" -- grid snap
                        #alt: $.modifiers[#unwrap_uvw].unwrap.snap () -- pixel snap
                    )
                )
            )
        )
    )
    
    Any reason why my "case of" always goes into the "genericMode"? even though the "print (classOf (modPanel.getCurrentObject()))" inside of it saying it is indeed "Unwrap_UVW"? why won't it enter the "uvMode" instead?

    Already tried with or without the "return #" also tried putting "uvMode: Unwrap_UVW" instead of "Unwrap_UVW: uvMode" and seems like nothing can help it to run properly...:(

    PS: Bryan, so sorry if my questions takes too much of your time :\
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 19
    So what's happening is you're, again, assigning to variables that do not exist. I'm thinking you do not understand the basic principles here and relying on the case of expression and code from the other threads.

    Here's what's happening:
    - Your case of in the function fnRevel_curClass is returning 'undefined'
    - The case of in the other code is testing if case of undefined equals genericMode , which does not exist here either so its returning undefined and making the expression true
    - Since it is true the case of expression executes the code for that and as it says in the help for case expressions
    Only the first expression whose label matches the test expression is evaluated.
    
    So that's the only one that executed.

    If you moved the uvMode above the genericMode it would of executed that first but this is all wrong. You need to learn about the most basic concept in maxscript 'varaibles', then 'if 'else', then 'loops' then functions and so on. You're trying to run before you can crawl.

    Another thing your doing is creating code that does the same thing in other code. You're using a case of expression to get the current modifer and then testing it again using another case of expression but its essentially doing the same code again with a little extra to execute what you wanted it to do in the first place.

    You also don't need to append the Revel prefix to every bit of code. That is something for objects in global scope so its only needed on the glRevel variable.

    Instead of
    glRevel.fnRevel_curClass()
    
    It would be
    glRevel.curClass()
    
  • Revel
    Offline / Send Message
    Revel interpolator
    Hello Bryan, thanks so much for your advice and time reading and answering my question :)
    Yeah, I know I'm probably won't creating script that will be used by many people, mostly my script will be just to support my personal modeling need and workflow, so it will be revolve around editable poly/edit poly/ unwrap uv/ turbosmooth/ and stardard modeling tools like cut/ weld/ bridge/etc.

    And about code that checking similar thing on another code, I assume you're saying about the keyPressed declared on global script and re-check on the other script, correct? well it's just for cleanness purpose actually, probably it doesn't make sense to do that?hehe..

    I'm trying to create the setup like Perna's suggested here so it's many set of scripts that using the value of the variable that we declared as global. Instead of typing "keyboard.controlPressed and not ..." every time, now I can just type #ctrl.
  • miauu
    Offline / Send Message
    miauu polycounter lvl 13
    Inspired from this thread and the problems that default Named Selection Sets and Storage Selections have(preserving the proper selection after some operations) I've created a script that can save and load sub-objects selections with no problems. Video of the script.
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 19
    Awesome miauu. How many selection sets can you save per subobject level?
  • miauu
    Offline / Send Message
    miauu polycounter lvl 13
    Awesome miauu. How many selection sets can you save per subobject level?
    Only one selection set per sub-object level. I can increase it to 5, but first I will try to find a way to increase the number to 10 or more.

    The main drawback is that the selection sets are not saved with the scene.
  • Revel
    Offline / Send Message
    Revel interpolator
    Nice one!..I guess you can temporary use the max default selection sets when user want to quit max and restore it back when load it up and continue back using your code when doing the modeling, since the default selection sets get saved into the max file and there is no chance for max messed up the selection sets while it's not running..hehe
  • miauu
    Offline / Send Message
    miauu polycounter lvl 13
    Revel wrote: »
    Nice one!..I guess you can temporary use the max default selection sets when user want to quit max and restore it back when load it up and continue back using your code when doing the modeling, since the default selection sets get saved into the max file and there is no chance for max messed up the selection sets while it's not running..hehe

    I have plans to do almost the same, but first I need to find the best approach.
    There is no way using maxscript to delete subobjects Named Selection Sets.This can lead to long, long list with sub objects selection sets, and this can be very frustrating.
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 19
    miauu: You might be able to delete selection sets by investigating this stock macroscript
    C:\Program Files\Autodesk\3ds Max 2014\MacroScripts\Macro_NamedSelSets.mcr
    
  • miauu
    Offline / Send Message
    miauu polycounter lvl 13
    miauu: You might be able to delete selection sets by investigating this stock macroscript
    C:\Program Files\Autodesk\3ds Max 2014\MacroScripts\Macro_NamedSelSets.mcr
    
    This is only for creating selection sets of objects. It not works with sub-objects.
  • Revel
    Offline / Send Message
    Revel interpolator
    Hey guys, recently I tried to use the scripts that I created on my office computer, but it keeps on throw me an error about "modKey is undefined". I did put my global .ms file on my startup folder so my struct and all it's function should work, no?

    Funny thing is the same exact thing work on my home computer. Even more funny thing is that it work once before on my office but after restart, Max start throw me an error again...Is there any huge different on how Max 2014 and Max 2009 treat a maxscript file?

    FYI: my office use Max 2014, my home computer Max 2009, and its not just about modKey, but subLevel and curClass as well...

    Did you guys experienced this before?
  • monster
    Offline / Send Message
    monster polycounter
    Unintuitively, the "Startup" folder is evaluated after all other script folders, just after the default scene is loaded.

    You need to place your global.ms file in a user-defined plugin folder.

    Please read this article on the script evaluation order.

    http://docs.autodesk.com/3DSMAX/16/ENU/MAXScript-Help/files/GUID-615D14FB-0F2D-4801-B381-1128C4128C70.htm
  • Revel
    Offline / Send Message
    Revel interpolator
    Yeah I did put it on my user startup script folder on AppData..but the thing is if I load my custom shortcut key (that use to the function inside the global) after Max startup, it work. But if I make that shortcut key to load as MaxStart, it failed to recognize the global...
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 19
    That's because the start up scripts are evaluated after macroscripts as described in the link Monster provided. You should put your start up scripts in a local folder on your computer and then add that path to the Customize > Configure System Paths > 3rd party tab dialog and restart max. Your start up script will then load before the macroscripts are loaded.

    I have all my personal scripts in my dropbox folder that I set the system path to at work and home so I always have them up to date.
    The folder has a global struct that has includes from a (lib) folder and on creating that struct it installs all my macroscripts from the (macro) folder.

    scripts_folder.PNG
    global BCTP
    (
        struct BCToolsPublic
        (
            include "Dropbox\\Scripts\\max_scripts\\BCToolsPublic\\src\\(lib)\\inputOps.ms",
            include "Dropbox\\Scripts\\max_scripts\\BCToolsPublic\\src\\(lib)\modifierOps.ms",
            include "Dropbox\\Scripts\\max_scripts\\BCToolsPublic\\src\\(lib)\\transformOps.ms",
            include "Dropbox\\Scripts\\max_scripts\\BCToolsPublic\\src\\(lib)\\stackOps.ms",
            include "Dropbox\\Scripts\\max_scripts\\BCToolsPublic\\src\\(lib)\\selectionOps.ms",
            
            mapped fn install dir =
            (
                fileIn dir
            ),
            
            on create do
            (
                install (getFiles "Dropbox\\Scripts\\max_scripts\\BCToolsPublic\\src\\(macros)\\*.mcr")
            )
        )
        BCTP = BCToolsPublic()
        
    )
    
  • Revel
    Offline / Send Message
    Revel interpolator
    Ah! thats awesome!..I do have my GDrive local folder on both so I can map it to it instead!
    I should look into it further as currently I still abit blank of what is happening on your folder structure. What if you put all the macros in d folder specified on the system path? Is it because related to this sentence from the help file "The 3ds Max scene has not been created and the viewports have not been created at this point. You cannot create scene objects or do anything UI related in these scripts"?
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 19
    You can add macros into that directory if you would like to but be careful if your doing this for a team of artists and the folder is on a shared network. If another artist right clicks the macroscript in the customize dialog and chooses "Edit Macrosript File" they could potentially change the one on the network and not the one in their local AppData folder. Even this method of installing macros has the same problem. I don't know of a good way of installing macros properly other than the .mzp method which i don't particularity like.

    Honestly I have the macroscript install here just so I can uninstall them easily if I decided to get rid of one later on. I hate having my "BCTools" category cluttered with macroscripts that I no longer use so I have an uninstall command that uninstalls them and re-installs them at start up. I forgot to add that to the code in the above post.
    global BCTP
    (
        struct BCToolsPublic
        (
            include "Dropbox\\Scripts\\max_scripts\\BCToolsPublic\\src\\(lib)\\inputOps.ms",
            include "Dropbox\\Scripts\\max_scripts\\BCToolsPublic\\src\\(lib)\modifierOps.ms",
            include "Dropbox\\Scripts\\max_scripts\\BCToolsPublic\\src\\(lib)\\transformOps.ms",
            include "Dropbox\\Scripts\\max_scripts\\BCToolsPublic\\src\\(lib)\\stackOps.ms",
            include "Dropbox\\Scripts\\max_scripts\\BCToolsPublic\\src\\(lib)\\selectionOps.ms",
            
    	mapped fn uninstall file =
    	(
    	    format "Uninstalling - %\n" file
    	    deleteFile file
    	),
    		
            mapped fn install file =
            (
    	    format "Installing - %\n" file
    	    fileIn file
            ),
            
            on create do
            (
    	    uninstall ( getFiles ((getDir #userMacros) + "\\BCTools_Public*.mcr") )
                install (getFiles "Dropbox\\Scripts\\max_scripts\\BCToolsPublic\\src\\(macros)\\*.mcr")
            )
        )
        BCTP = BCToolsPublic()
        
    )
    
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 19
    Also I should note that if you do put your macroscripts in this folder you will have the same global undefined problem that you had before depending on which order max reads in the files. It may read in the macros before the global.ms file you have. But if you set it up my way you wont have that problem since putting "()" around the macro folder's name prevents max from reading that directory until we tell it to in our struct's create event.

    I would like to hear from others on how they handle these things. I also just found the macros.load() function which fixes the problem of someone trying to edit macros outside of their app directory when right clicking on their name in the customize dialog. I just add it to my create event after my install and pass it the usermacros directory. The only downside is it loads the macros again when max loads them in its next start up step. Doesnt seem to be a hit on loading time for me. Might become an issue if you have a ton of macroscripts though.
  • haiddasalami
    Offline / Send Message
    haiddasalami polycounter lvl 14
    I don't know of a good way of installing macros properly other than the .mzp method which i don't particularity like.

    Not sure if there is one. I use perforce and do a query to see if it has been updated and if not it deletes the mcr and re-evaluates from the depot (an ms file). This is done as the menu of max is being built (as i create a menu and attach the scripts to it)
Sign In or Register to comment.