3ds Max Keyboard Shortcuts

13

Replies

  • perna
    Offline / Send Message
    perna ngon master
    Bryan: I love this kind of thing.

    Did you test the code on a clean restart of max though? Because I can't get this to run.

    Those "this" references seem unusual/illegal to me and won't evaluate on 2009. 2010 throws "Unable to convert: [457,102] to type: Point3" (makes no sense that I can see) and won't run.

    I also spot the first "this.updateDisplay" just referencing the function, not calling it.
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 14
    Sorry Perna: I should of waited to post the code I just didnt think I would have time to get back to it in the next few days. Im actually using a previous version of this code on that moveToFurthest script from the image but what I posted was a newer version that made changing the menu entries more accessible to others, but there are problems I didnt experience lastnight... I will update the OP later tonight and clean up the code a bit.

    On the topic of the "this" reference from the maxscript reference:

    http://docs.autodesk.com/3DSMAX/14/ENU/MAXScript%20Help%202012//index.html?url=files/GUID-D5426B3F-7060-4C02-BA75-CB24A66B2AB-451.htm,topicNumber=d28e151103
    The 'this' local variable always contains the MAXScript value corresponding to the current instance of the scripted plug-in. This variable provides a guaranteed way of accessing parameters on the new object within plug-in code (in case there are locals or globals with the same name), and it is a way of referencing the plug-in in case you want to store it to a variable.

    That references scripted plugins but you can see here in the maxscript reference that it can be used with structs as well. See the example reference's on create do event:

    http://docs.autodesk.com/3DSMAX/14/ENU/MAXScript%20Help%202012//index.html?url=files/GUID-0D3592DD-2F20-4A41-B19F-544426B8186-454.htm,topicNumber=d28e151491
    
    EXAMPLE- DEFINITION AND CREATION:
    struct foo2
    (
    A = 1,
    B = 3,
    fn error = throw "ZZZ",
    on create do format "Struct Created: %\n" this,
    on clone do format "Struct Cloned: %\n" this
    )
     
    f = foo2 ()
    RESULT:
    #Struct:foo2(
    a:<data>; Public,
    b:<data>; Public,
    error:<fn>; Public)
    Struct Created: (foo2 a:1 b:3)
    (foo2 a:1 b:3)
    


    I honestly haven't used it much and may have over used it here.

    I should have a cleaner way of writing the code and Ill see what I can do tonight and edit the OP. There are some redraw and error handling issues that need to be addressed that aren't in the other version of the script that I have. Been using the moveToFurthest version of the script for about 2 years now without much of an issue.

    Disclaimer:I'm not the best at writing clean readable code or have any programming experience I just work in my on little self-taught bubble. I'm just now trying to post more scripting questions and getting my code on the boards for others to inspect so that I'm no longer working in that bubble and can get proper feedback. I hope I don't lead anyone down the wrong path with my examples :)
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 14
    Ok I have cleaned up the code and created a mzp for installing.

    The mzp file will create a BCT_PieMenu.ms file in you startup scripts folder
    ("Program Files\Autodesk\3ds Max 2012\Scripts\Startup\BCTools\BCT_PieMenu.ms")

    It will also install a macroscript in your usermacros folder called BCTools-BCT_PieMenu_Example.mcr
    ("Users\user_folder\AppData\Local\Autodesk\3dsMax\2012 - 64bit\enu\UI\usermacros\BCTools-BCT_PieMenu_Example.mcr")

    Add this script to a hotkey from within the BCTools category. You can't really add this a toolbar or quad menu, it needs to be fired from within a viewport. The uv window does not count as a viewport which kinda sucks.

    Grab the mzp here: http://www.bryancavett.com/maxscripts/BCT_PieMenu.mzp

    All the labels, radius, count, colors, and sensitivity (dist_threshold) are handled in the macroscripts that the user creates. The tool returns an index of the item under the mouse with which you can put in a case of block to fire a specific function or execute more code. I suggest firing other self contained macros using the macros.run function or other max functions you can nab from the macrorecorder.

    You can use the tool 2 ways:
    - Swipe Method: Fire the tool and then swipe the mouse in the direction of the the function you want to use. Once you move the mouse a certain distance (dist_threshold) away from the menu start position it will fire the script.

    - Click Method: Fire the script and click on the highlighted label.

    I've only tested this on max2012 but I don't think there's anything im doing that would not be compatible with 2009.

    One cool thing with this script is you can have a pie menu launch another pie menu and you end up with a simple gesture based hotkey. If I have time I'll make a video to show it off.


    Hope you guys can get some use out of it.
  • MrOneTwo
    Offline / Send Message
    MrOneTwo polycounter lvl 7
    I get this error in max 2009. I wanted pie menus in max for so long! Even tried a few but they were always buggy. This I can run on 2009 though :( (works in 2013)

    pMbrhM.png
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 14
    hmm I just tried it on 2009 and it seems that I'm using some methods that it doesnt like.

    I needed to remove the public and private scopes as well as the 'this' reference and it worked. Try downloading it again.
  • MrOneTwo
    Offline / Send Message
    MrOneTwo polycounter lvl 7
    Works great. The best implementation of pie menu i tried. For now I just changed it to view pie menu. Probably will mess with it a little bit more. It is so refreshing using max 2009 with cool scripts instead of 2013 :D
  • perna
    Offline / Send Message
    perna ngon master
    Bryan: Yep, the Private/Public/This stuff can just plain be removed there.

    There's a bug: You compare values relative to the current angle without taking into account that 360 doesn't wrap around to 0. This leads to problems getting the up-stroke to register.

    So we can round off the angle, which makes the check cleaner too:
    function update_BCT_PM =
    (
      curr_pos = start_pos - mouse.pos
      local s = slice_amount as integer
      local curr_degree = ((atan2 curr_pos.x -curr_pos.y) + 180) as integer
      curr_degree = mod ((curr_degree+(s/2))/s*s) 360	-- round off the angle
      for i in 1 to labels.count do if curr_degree == slice_amount*(i-1) and ((distance start_pos mouse.pos) > threshold or clicked) then
      (
    	  selected = i
    	  end_BCT_PM()
      )
      drawLabels()
      gw.enlargeUpdateRect #whole
      gw.updateScreen()
    ),
    

    Also, the "completeRedraw" was making the text mad flicker here. Commenting it out gives a smooth result, what happens on your end?

    Underneath I pick at stuff, hope this is feedback you want, otherwise I'm sorry.

    There's also some stuff happening like
    highlighted = i
    selected = highlighted
    

    instead of just
    selected = i
    
    :)

    There are some similar things happening to this. like getAngle, a tiny struct function used only once, might as well inline. struct members that are only used once, might as well be a local var.


    There's these two functions where logic is repeated and you have to create an array to move data between them:
        function getLabelPositions =
        (
            local label_positions = #()
            local angle_amount = 0.0
            local textHalfLength
            
            for i in 1 to labels.count do
            (
                -- get the point on a circle around the mouse position
                label_positions[i] = [0,0,0]
                label_positions[i].x = (Sin angle_amount) * radius + start_pos.x
                label_positions[i].y = -(Cos angle_amount) * radius + start_pos.y
                
                -- center text on x
                textHalfLength = (gw.getTextExtent labels[i])*0.5
                label_positions[i].x -= textHalfLength.x
                
                angle_amount += slice_amount
            )
            
            return label_positions
        ),
        
        function drawLabels = 
        (
            positions = getLabelPositions()
            for i in 1 to positions.count do
            (
                if highlighted == i then
                    gw.wText positions[i] labels[i] color:color_sel
                else
                    gw.wText positions[i] labels[i] color:color_unsel
            )
        ),
    

    Replacement code:
    function drawLabels = for i = 1 to labels.count do    -- get the point on a circle around the mouse position
    (
      local x = ((Sin (slice_amount * i)) * radius + start_pos.x) - (gw.getTextExtent labels[i]).x *0.5 -- center text
      local y = -(Cos (slice_amount * i)) * radius + start_pos.y
      gw.wText [x,y,00] labels[i] color:(if highlighted == i then color_sel else color_unsel)
    ),
    

    I've noticed that you use strange syntax:

    if ... do
    for i in x to y do

    should be

    if ... then
    for i = x to y do


    Currently internal logic is handled in the macroscript. Put this in a function in the struct. You'll then have the macro clean without all that extra stuff:
    macroscript BCT_PieMenu_Example
    category:"BCTools"
    (
        BCT_PM_Settings.labels = #("top", "top_right", "right", "bottom_right", "bottom", "bottom_left", "left", "top_left") -- menu labels (Supports 2 - 8 strings. You can have more if you edit the hardcoded limit but then it becomes hard to select because the slice is so small)
        BCT_PM_Settings.radius = 100 -- radius of the menu (This is useful if you require the strings to be very long)
        BCT_PM_Settings.threshold = 100 --  distance in pixels from the starting mouse pos required to fire a script
        BCT_PM_Settings.color_sel = [44,206,253] -- color of selected text
        BCT_PM_Settings.color_unsel = white -- color of unselected text
    
        -- check for errors within the user supplied settings from above
        case BCT_PM_Settings.Run() of
    	(
    		1:(print "Ran top")
    		2:(print "Ran top_right")
    		3:(print "Ran right")
    		4:(print "Ran bottom_right")
    		5:(print "Ran bottom")
    		6:(print "Ran bottom_left")
    		7:(print "Ran left")
    		8:(print "Ran top_left")
    		#aborted:(print "aborted") -- user right clicked or hit esc
    	)
    
    )
    

    and a new function in the struct:
    fn Run =
    (
      if (not BCT_PM_Settings.error()) then
        startTool BCT_PM_Tool numPoints:1  --start tool
      completeRedraw() -- clear any artifacts left over from drawing the text
      BCT_PM_Settings.selected
    )
    

    Note that I've change the "error()" return value. When you get an error, it should return true, not false :)

    and the error function was:
    function error =
    (
      local errorFree = true
      local errorMessageText = ""
      if BCT_PM_Settings.labels.count < label_count_min or BCT_PM_Settings.labels.count > label_count_max then
      (
        errorFree = false
        errorMessageText = "BCT_PM_Settings() only supports " + label_count_min as string + " - " + label_count_max as string + " labels!"
      )
      else
      (
        for i in BCT_PM_Settings.labels do
        (
          if classOf i != String then errorFree = false
        )
      
        if not errorFree then
        (
          errorMessageText = "BCT_PM_Settings() One of the BCT_PM_Settings.labels is not of class 'string'"
        )
        else
        (
          errorFree = true
        )
      )
      
      if not errorFree then messageBox errorMessageText
      return errorFree
    ),
    

    the errorFree/errorMessageText makes sense to me for a large complex function with many cases, but here we might as well:
    fn error =
    (
      if (BCT_PM_Settings.labels.count < label_count_min) or (BCT_PM_Settings.labels.count > label_count_max) then
      (
        messageBox ("BCT_PM_Settings() only supports " + label_count_min as string + " - " + label_count_max as string + " labels!")
        return true
      )
      else
        for i in BCT_PM_Settings.labels do
          if classOf i != String then
          (
            messageBox ("BCT_PM_Settings() One of the BCT_PM_Settings.labels is not of class 'string'")
            return true
          )
      return false
    ),
    

    We have a boolean called "aborted", and all it does is:

    if aborted then selected = #aborted

    so we might as well just get rid of that variable and modify "selected" directly

    and get rid of "end_BCT_PM", which calls "stopTool BCT_PM_Tool", and just call the latter directly, and put any cleanup code in "on stop".

    and get rid of "BCT_PM_ScreenUpdate", because all it does is call "BCT_PM_Settings.update_BCT_PM()" (plus it's glocal). Just call the latter directly

    Similarly, we can get rid of "clicked", since all it does it call a function when true, so we might as well just call the function directly instead and remove the click check from "update_BCT_PM".

    I'd personally hardcode stuff like "label_count_min", since you don't intend for those variables to be altered and they're only accessed once. Variable typically means it's going to change.

    get rid of the curr_pos struct variable and make it local to the function it's used in.

    _BCT_PM suffix - any reason for this? BCT prefix would be to distinguish globals, but other than that ought to be nicer to just write "update" than "update_BCT_PM", etc

    Removed the label color highlight as updateScreen looks ugly here, at least without special handling

    Added boxes behind the text so it won't drown in the wrong background scene.
    Per128_BC_PieMenu.jpg

    final code:
    --======================================================================================
    -- BCT_PieMenu
    -- Author Bryan Cavett
    -- Version 0.9.Per128Edits01
    -- PieMenu mouse tool and struct to hold settings
    -- See BCTools-BCT_PieMenu_Example.mcr for examples on how to set up the tool in a custom macroscript
    --======================================================================================
    global BCT_PM_Tool
    global BCT_PM_Settings
    struct BCT_PM_Settings
    (
        start_pos		= [0,0],
        slice_amount	= 0.0,
        counter			= 0,
        labels			= #("index_1", "index_2", "index_3", "index_4", "index_5", "index_6", "index_7", "index_8"),
        selected		= 0,
        radius			= 100,
        threshold		= 200,
        color_unsel 	= (color 197 197 197),
    	color_box		= (color 17 17 17),
        fn drawLabels	= for i = 1 to labels.count do		-- get the point on a circle around the mouse position
        (
    		gw.setTransform(Matrix3 1)
    		local x = ((Sin (slice_amount * i)) * radius + start_pos.x) -- center text
    		local y = -(Cos (slice_amount * i)) * radius + start_pos.y
    		local cbox = (box2 (x - 64) (y-18) 96 97)
    		cbox.w = 130
    		cbox.h=20
    		gw.wrect cbox color_box
    			print i
    		gw.wText [x-(gw.getTextExtent labels[i]).x *0.5,y,00] labels[i] color:color_unsel
        ),
        fn update =
        (
            local curr_pos = start_pos - mouse.pos
    		local s = slice_amount as integer
            local curr_degree = ((atan2 curr_pos.x -curr_pos.y) + 180) as integer
    		curr_degree = mod ((curr_degree+(s/2))/s*s) 360	-- round off the angle
            for i = 1 to labels.count do
    		(
    			if curr_degree == slice_amount*(i-1)  then
    				selected = i
    			if (distance start_pos mouse.pos) > threshold then
    				stopTool BCT_PM_Tool
    		)
            drawLabels()
            gw.enlargeUpdateRect #whole
            gw.updateScreen()
        ),
        fn start =
        (
            unRegisterRedrawViewsCallback update
            registerRedrawViewsCallback update
            selected = 0
            start_pos = mouse.pos
            slice_amount = 360.0/labels.count
            update()
        ),
        fn error =
        (
            if (BCT_PM_Settings.labels.count < 2) or (BCT_PM_Settings.labels.count > 8) then
    		(
                messageBox ("BCT_PM_Settings() only supports 2 - 8 labels!")
    			return true
    		)
            else for i in BCT_PM_Settings.labels do
    			if classOf i != String then
    			(
    				messageBox ("BCT_PM_Settings() One of the BCT_PM_Settings.labels is not of class 'string'")
    				return true
    			)
            return false
        ),
        fn Run =
    	(
    		if (not BCT_PM_Settings.error()) then
    		(
    			startTool BCT_PM_Tool numPoints:1  --start tool
    			completeRedraw() -- clear any artifacts left over from drawing the text
    		)
    		BCT_PM_Settings.selected
    	)
    )
    tool BCT_PM_Tool
    (
        on start do
            BCT_PM_Settings.start()
    	on stop do
    		unRegisterRedrawViewsCallback BCT_PM_Settings.update
        on freeMove do
            BCT_PM_Settings.update()
        on mousePoint clickno do
    		stopTool BCT_PM_Tool
        on mouseAbort clickno do
        (
    		selected = #aborted
    		stopTool BCT_PM_Tool
    	)
    )
    BCT_PM_Settings = BCT_PM_Settings()
    


    Ooops, a lot of stuff. Hope it's helpful and not annoying :poly002:
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 14
    Wow Perna that is really awesome feedback and all totally valid. I've never had anyone with proper experience take the time to review and comment on my code, really grateful for that :) It must have taken some time to write that up but it makes the tool better for everyone to use.
    _BCT_PM suffix - any reason for this? BCT prefix would be to distinguish globals, but other than that ought to be nicer to just write "update" than "update_BCT_PM", etc

    A lot of my variable names are pretty sloppy and are the result of me not going back in and cleaning them up based on what I intended to use them for at the beginning and what they are actually used for at the end of the scripting process. Im terrible at that.
    Removed the label color highlight as updateScreen looks ugly here, at least without special handling

    I'd like to keep this in there if I can get it flicker less. I find it useful to see what Im selecting as well as for others who want to use the click method of firing the script
    There are some similar things happening to this. like getAngle, a tiny struct function used only once, might as well inline. struct members that are only used once, might as well be a local var.


    There's these two functions where logic is repeated and you have to create an array to move data between them

    The logic and program flow changes you made make perfect sense and I'm beating myself up for not thinking of some of the stuff you thought of when combining code into fewer lines

    It will take a bit for all that to sink in but Ill incorporate it into the script and package it all up. I'll release it later tonight or this weekend.

    MrOneTwo: did you get a chance to try having one menu fire another to test the gesture hotkey abilities of it?

    Perna: There is also a bug I need to fix that makes the script fire a lot of commands in a row if you hold the hotkey down and drag the mouse around. Not sure how to handle that one yet.

    Also the 'selected = #aborted' your calling in tool should be 'BCT_PM_Settings.selected = #aborted' since the tool is not in the struct. Im sure you know this and its a typo but Ill add that to my version when I package it up.

    Its not a perfect script but I hope it improves some peoples workflow and hotkey use.
  • MrOneTwo
    Offline / Send Message
    MrOneTwo polycounter lvl 7
    Nope I didn't check gesture commands yet. I hope I will be able to check them tomorrow. Also noticed this 'up stroke' problem (not wrapping angle around 360) but Perna already fix it. Thanks Bryan and Perna for creating this. I tested it in actual work. Simple 'views' pie menu and it's great. Almost as smooth as Modos. After you two brainstorming it will become flawless!
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 14
    New version up: http://www.bryancavett.com/maxscripts/BCT_PieMenu.mzp

    Check the example mcr for changes to the names of the color variables if your code is breaking on existing macroscripts you've set up.

    FIXES (9/07/2012):
    - merged in Per128Edits01 (http://www.polycount.com/forum/showpost.php?p=1663172&postcount=108)
    - (BCT_PM_Settings.node_underMouse) now returns the node under the mouse at the time of execution (could be useful in your scripts)
    - I now stop the script from firing if the user is holding down the hotkey to prevent multiple executions

    TODO: Return subobject under mouse at time of execution
    TODO: Add more checks for user supplied settings
    TODO: Adjust menu and mouse position if near the edge of the screen
    TODO: Check to make sure the user is over a valid viewport
  • miauu
    Offline / Send Message
    miauu polycounter lvl 8
    Off Topic.
    perna wrote: »
    I've noticed that you use strange syntax:

    if ... do


    should be

    if ... then
    Perna, why If ... do to be strange syntax?
    MXS reference says:
    The do form is provided particularly for use during interactive sessions in the MAXScript Listener.
    but also says,
    Generally, you can use both If...Then and If...Do within script code, but you cannot use If...Do when an 'else' expression is required for the cases the condition evaluates to false.
    So, if an IF condition doesn’t need ELSE whay no to use IF-DO?
  • perna
    Offline / Send Message
    perna ngon master
    miauu wrote: »
    So, if an IF condition doesn’t need ELSE whay no to use IF-DO?

    Well, the real question is: "why would you?" There's no actual advantage to it, and the obvious disadvantage is that you can't decide at some point in the future to add an ELSE. Code evolves, so why restrict yourself?

    "IF...DO" is irregular and unnecessary, a quirk of the maxscript language not present in any other major programming languages I've used, such as pascal, visual basic and ASP. Other languages, like the C-family and javascript skip that part entirely and just executes the next statement if the IF-statement resolves to true.

    So since DO evaluates, you are free to use it as a personal subjective quirk, as a colorful preference that to my knowledge has no objective advantages whatsoever. It may hold some emotional value - maybe it makes one feel like a rebel or hard-core unless you write code so fast and accurately that you never make errors and your productivity depends entirely on how fast you type (which is not the case for ordinary humans, but I'm stretching here to come up with a compelling reason to use DO).

    If there are any valid objective reasons to use DO in IF statements, I'm not aware of them. It doesn't even execute faster so holds no advantages in an inner loop.
  • miauu
    Offline / Send Message
    miauu polycounter lvl 8
    Hi, perna.
    With all my respect, I am not agree with you.
    perna wrote: »
    Well, the real question is: "why would you?"
    Because:
    - it helps me to keep the code clear.
    - I do not feel feel like a rebel or hard-core - using IF..Do it is a habit now
    - at any point in the future you can add an ELSE, just have to replace DO with THEN.
    - I suppose that you use Abreviations, so how fast the typing is have no matter
    - and the most important reason - it is a habit and works.
    perna wrote: »
    If there are any valid objective reasons to use DO in IF statements
    If there are any valid objective reasons to NOT use DO in IF statements?

    perna wrote: »
    "IF...DO" is irregular and unnecessary, a quirk of the maxscript language not present in any other major programming languages
    Other programing languages are... other programing languages. If..DO is a part of maxscript.

    perna wrote: »
    It doesn't even execute faster so holds no advantages in an inner loop.
    I have a friend which is far more experienced in 3dsMAx and maxscript than me. He believes that IF..DO is faster then IF..THEN, when 3ds max is not reinstalled a long time, when there are a lot of scripts, that are loaded on every 3dsmax session(startup folders, stdplugs folder). I can't confirm this, because I keep my max clean and use it only for scripting.
  • perna
    Offline / Send Message
    perna ngon master
    miauu: The objective facts have been covered. There's no need to defend your subjective preferences, as no one is attacking them.

    Subjectively I prefer languages which don't even have THEN or DO, so I certainly don't rely on them to keep my code clear, meaning that if I have to use them, I choose the one that's less work. Having to replace DO with THEN every time I add an ELSE clause would be annoying to me, hence my preference.

    Bryan used inconsistent syntax, hence my recommendation to him. Hope it's clear to you now.

    Regarding THEN being slower on a cluttered install: I don't see anything logical about that, nor does it happen on my most (extremely) cluttered install. Even if it was true, we're talking about something that would not affect most people.
  • miauu
    Offline / Send Message
    miauu polycounter lvl 8
    It all depends on point of view :)

    With respect.
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 14
    I really have no preference to using DO or THEN. Maxscript is the only language I know and use and I've honestly never thought of only using THEN for the sake consistency but it makes sense.

    I really need to take a step back and critique my own scripts and find a way to make it more consistent with variable, struct, and function names and so on but this thread isnt about coding standards (though I am grateful for the critique perna)

    Lets see more cool ideas posted in this thread or are we getting away from your original purpose here Perna?
  • perna
    Offline / Send Message
    perna ngon master
    Bryan: The pie thing is definitely on the subject of "shortcuts". I guess OCDs will just have to tolerate the chatter surrounding it :-D Of course, more discussion on shortcuts is great.

    More feedback on your script:
    - (BCT_PM_Settings.node_underMouse) now returns the node under the mouse at the time of execution (could be useful in your scripts)
    [...]
    TODO: Return subobject under mouse at time of execution

    My advice: Focus functionality or you end up with bloat and too many dependencies. Functionality such as checking the object under the mouse and the current SO should be in a separate library. You wouldn't want someone to build a menu struct that they don't need just to check which object is underneath the cursor. It may seem like a small point, but speaking from experience this is one of the things that end up biting you in the ass bigtime.
    TODO: Adjust menu and mouse position if near the edge of the screen

    Unfortunately won't work for absolute positioning devices (tablet, touch). An alternative might be to adjust the menu radius when close to the edge.



    I would remove the click-functionality. Currently it interferes with the swipe-functionality and objectively isn't necessary. To elaborate:

    Swipe intuitively should terminate above the label, which isn't possible as long as you have the click functionality. "Enter the label are" is the same as click. Other than this, the two concepts are literally identical.

    Configurable swipe-length and radius is well-intended and common thinking when in the programmer mode. But in reality it just means that you end up with lots of scripts that feel inconsistent, mess with muscle memory and all-in-all make for a poor user experience across the board. Those values are prime examples of something you'd want to keep consistent and logical (instead of just arbitrary values that make no practical sense)

    I know the temptation to throw in everything and the kitchen sink, but personally I'm a big fan of the google approach, which seems more in tune with user-mindsets than programmer-mindsets: Make one focused tool work extremely well and give it sensible defaults instead of lots of options, AND make the various tools and programs consistent with each other so you don't have to keep re-learning stuff because of small differences (the programmer intended for them to provide more options, but in practice they just got very annoying). Programmers typically don't like to write like that, but I would say that's clearly what users want. Myself, I love to customize tools and programs, but I love it much more when the defaults work great without any need for customization. So, I'd say, hard-code the mentioned parameters and remove the mouse-click functionality so that the swipe can function in a more intuitive way.



    The last thing I want to suggest is to always use 8 labels. This ties in with the above: Provide a consistent user-experience. Training muscle-memory for 45 degree swipes is straight-forward, but a 7-option menu currently means leaning 51 degree angles. That's really awkward. The user has to count the number of options, then try to recall the movement for that number. In code you can just hard-code 8 labels and just do this:
    BCT_PM_Settings.labels = #("top", "top_right", "right", "", "bottom", "", "left", "top_left")
    

    ... and just don't make labels for the empty strings. The thing is to think from the user-perspective (interface, hand movement) and not the programmer-perspective (mathematics... 360/numOptions).


    Oh and to deal with the flickering problem, just make sure you only update the view when the label color has actually changed. I agree the label highlight is something you'd want.

    Finally, just in terms of clean code, what you might want to do is move functionality inside the mousetool and only use the struct for passing parameters.
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 14
    Thanks Perna.

    I will see what I can do about simplifying the script and incorporating this feedback.

    One thing I would like to note about the nodeUnderMouse thing. I wanted to include that to help non scripts have access to this since its something that would compliment the script nicely but not something that is easy for them to code. The variables are there but you don't have to use them. I don't think it would become annoying to the end user unless I just didn't put enough error checking in and it breaks... which it shouldn't if I add a try()catch() surrounding it.

    A couple of workflow ideas surrounding the nodeUnderMouse could include:

    - Having an object selected and wanting to attatch, group, parent, or boolean another object without leaving the selected one. Just hover over the other geometry, fire the pie menu and choose group/ungroup or parent/unparent

    - Get the material of another object and assign it to the selected one without leaving it

    - Assigning an object to or removing from the selected objects layer by hovering over it

    - Copying pivots from object under mouse to selected object or aligning them.

    Stuff someone might want implement but they do not have the tools or scripting skills to do so. Its also nice do these types of things with the swipe of the hand. It feels more intuitive than the standard, selected then apply by clicking routine.

    Edit: Another reason to include it in the script instead of another library is because this would be difficult to achieve with the way the menu works. It does not allow access to firing your scripts unless you move the mouse to swipe. If you're moving the mouse then its possible the cursor is no longer over the object you want to query. Any thoughts on that? Should I expose an event to the user that happens when the tool is started or are we getting into bloating the tool again?


    I doubt I can get the face under the mouse working since I have to snapshot a edit poly and work off the mesh. Which them returns the triangulated face and it could differ in index :(
  • perna
    Offline / Send Message
    perna ngon master
    Thanks Perna.
    One thing I would like to note about the nodeUnderMouse thing. I wanted to include that to help non scripts have access to this since its something that would compliment the script nicely but not something that is easy for them to code.

    You are writing maxscript libraries for people who don't maxscript? Wouldn't it make more sense to make a visual editor? Well, I won't get much into that, as it's what you aim to do, just hope you don't find out that it's a waste of time.

    I probably didn't make myself clear on "another library", though. It won't affect the user at all, it just makes for cleaner code. They would still have access to exactly the same functionality, you would just remove the unnecessary dependency.
    Another reason to include it in the script instead of another library is because this would be difficult to achieve with the way the menu works. It does not allow access to firing your scripts unless you move the mouse to swipe. If you're moving the mouse then its possible the cursor is no longer over the object you want to query. Any thoughts on that?
    So they bring up the menu, swipe-select "alighObjects", then click on the object to align the selected object to. Your initial mouse position doesn't matter, nor do you have to include anything else in the menu script. Or am I missing something?
    I doubt I can get the face under the mouse working since I have to snapshot a edit poly and work off the mesh. Which them returns the triangulated face and it could differ in index :(
    vert indices are the same between poly and mesh, so get the verts of the mesh face and find the face with those verts in epoly.
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 14
    You are writing maxscript libraries for people who don't maxscript? Wouldn't it make more sense to make a visual editor? Well, I won't get much into that, as it's what you aim to do, just hope you don't find out that it's a waste of time.

    Hah. Yes I am well aware that this is more than likely, a waste of time. I've shown so many artists workflow improving and automation scripts that I use and all they say is "That's cool! I could use that." and go back to using stock methods.

    I mainly wanted to target the artists who are more technical and are going to take the time to set up the script without a visual editor if they see it will improve their workflow.

    I also felt this thread was moving towards catering to those types of artists with all the code snippets we were posting in the last few pages. Also hoping to get more people discussing this type of stuff in this thread. Like you said before, most people approve of this thread but don't contribute. The last few pages have been mainly me and you contributing :)
    So they bring up the menu, swipe-select "alighObjects", then click on the object to align the selected object to. Your initial mouse position doesn't matter, nor do you have to include anything else in the menu script. Or am I missing something?

    I guess it's more of a preference and not a workflow improvement thing here. Have you tested a script like this yet? I find it feels much better to do certain commands this way as opposed to hitting a key and then clicking an object. Try it out with parenting/un-parenting or grouping/un-grouping an object. It's much faster for me to just swipe over an object to fire the script for this sort of thing. And anything I can do to reduce the amount of times I have to click the better it is for me.
    vert indices are the same between poly and mesh, so get the verts of the mesh face and find the face with those verts in epoly.

    Thanks, I had not thought of that.

    Edit:
    Just thought I would throw this in here because its a workflow that relates to getting nodes under the mouse. I have one script that utilizes this. It's a script that gets the uv bounding box under the mouse and moves the selected uvs to within that space. It awesome for stacking and aligning tiling textures. Say I have a plane in the scene that has a texture that contains 4 tiling trims. I just slice up the plane so that each poly has 1 part of the texture and break the uvs. This leaves me with 4 uv clusters on the plane. I then have the object I'm working on and applying uvs to, I select a uv cluster on the object and then hold my mouse over the part of the texture I want to align it to, fire the script and it does all the alignment for me. I'm just pissed that max 2012 made this unusable because opening and closing the edit uv window is so damn slow. Kinda off topic I guess
  • perna
    Offline / Send Message
    perna ngon master
    I guess it's more of a preference and not a workflow improvement thing here. Have you tested a script like this yet? I find it feels much better to do certain commands this way as opposed to hitting a key and then clicking an object. Try it out with parenting/un-parenting or grouping/un-grouping an object. It's much faster for me to just swipe over an object to fire the script for this sort of thing. And anything I can do to reduce the amount of times I have to click the better it is for me.

    I think we may be talking about two different things here or mixing up terms. Are you talking about the user indicating another object? And if so, how do you do that by swiping?

    If you don't want clicks, just use whatever object was under the cursor when bringing up the menu.

    Ah, I think I know what you mean now. You want the menu script to handle such things because if an entirely stand-alone function were to use the object underneath the cursor that would conflict with having to move the cursor to swipe in the menu?
    In that case, just pass the pre-swipe mouse position as a parameter. The core menu stuff isn't touched at all and there are no dependencies:
    fn myFunction p_pos:mouse.pos =
    [...]
    OriginalMousePos = mouse.pos
    case BCT_PM_Settings.Run() of
    (
    	1:(myFunction p_pos:OriginalMousePos)
    [...]
    


    It's definitely true about showing stuff to people and then they go back to using stock stuff. I mean, people even use the default keyboard shortcuts, so what are you going to do.

    I mean look at 3Point Shader Pro, it has all these mad features we expected to see people create super complex shader effects with, and I've not seen one person actually make use of them, despite the continued sales.

    People tend to want something that just works, without any configuration and so on, and if they don't get that they just continue using defaults, no matter how awful those are.

    Most of the time people can't even be bothered to download and install a script, even if they think it looks amazing. I'm reminded of this time I recommended a script to someone that would save him vast amounts of time, then a long while afterwards asked how it was working out, upon which he replied that he hadn't had the time to install it. Didn't have time to install the script that would save him lots of time, well that's how it tends to go :)

    That's just the reality of it, and it tends to back up my design ideal that you try NOT to give people options, configuration, all that stuff... just make the defaults objectively good. Do your research, think it through. Of course, it takes skill, but you can work it out.

    See, that's the thing; some people are always going to make a big fuss about something they subjectively prefer without being able to back it up with logical objective arguments. The thing is though, that if forced to use the objectively superior method, they WILL adapt to it.. and they will prefer it, with time... people can just be extremely stubborn about it. Most of the time that they hang on to something like that it's for fear of change, even though they'll give you lots of other explanations for it. I'm sure you know lots of people who were die-hard Firefox users a while ago when you tried to convince them to change to Chrome, and they gave you a million reasons why they preferred FF. But now pretty much all of them have switched to Chrome, and not because the app has changed since they gave all their arguments. They just managed to let go of their irrational/subjective attachments. [oh dear god please don't let someone use this as an excuse to start some browser war discussion in this thread - it was just the first example that popped into my head]
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 14
    I think we may be talking about two different things here or mixing up terms. Are you talking about the user indicating another object? And if so, how do you do that by swiping?

    If you don't want clicks, just use whatever object was under the cursor when bringing up the menu.

    Ah, I think I know what you mean now. You want the menu script to handle such things because if an entirely stand-alone function were to use the object underneath the cursor that would conflict with having to move the cursor to swipe in the menu?
    In that case, just pass the pre-swipe mouse position as a parameter. The core menu stuff isn't touched at all and there are no dependencies

    Yes, that's what I was talking about and you can actually do this right now using "BCT_PM_Settings.start_pos", since I'm already saving that value.

    I could release a function outside the struct that gets the node under that position so that its separate and would make the struct cleaner. I just want to make sure I expose some sort of function that gets the node under the mouse to people who may not of known about it and who would want to incorporate it into this script or another. This is getting into more of a universal function library and away from just a pie menu tool, which is what you were referring to before.
    Most of the time people can't even be bothered to download and install a script, even if they think it looks amazing. I'm reminded of this time I recommended a script to someone that would save him vast amounts of time, then a long while afterwards asked how it was working out, upon which he replied that he hadn't had the time to install it. Didn't have time to install the script that would save him lots of time, well that's how it tends to go

    I see this all the time :)
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 14
    Ok this is probably the last time Ill update this script until people start using it and reporting any bugs. http://www.bryancavett.com/maxscripts/BCT_PieMenu.mzp

    FIXES (9/09/2012):
    - simplified the script as suggested by perna
    - removed the ability to click a label in order force the user to swipe as intended
    - selection highlighting (I am not seeing any flickering in max2012 but have not tested it on 2009 since I dont have it here)
    - removed node_underMouse functionality. If there is interest in this functionality I will release it in another function library
    - to ensure that each label is at a predictable 45 degree angle the script now requires 8 labels. If you want to only show 4 then create an empty string in the slot you want to be hidden.
    example:
    BCT_PM_Settings.labels = #("index_1", "", "index_3", "", "index_5", "", "index_7", "")
    

    Here's an example of the bare minimum you should have in your macroscripts:

    - a label array that has 8 entries
    - case of expression where you add your code based on the selected index (so you would replace the 'print "Ran top"' dummy codes I have here with your own)

    You can find this macroscript under the category:"BCTools" "BCT_PieMenu_Example" once you install the script
    macroscript BCT_PieMenu_Example
    category:"BCTools"
    (
        BCT_PM_Settings.labels = #("top", "top_right", "right", "bottom_right", "bottom", "bottom_left", "left", "top_left")
        case BCT_PM_Settings.run() of
    	(
    		1:(print "Ran top")
    		2:(print "Ran top_right")
    		3:(print "Ran right")
    		4:(print "Ran bottom_right")
    		5:(print "Ran bottom")
    		6:(print "Ran bottom_left")
    		7:(print "Ran left")
    		8:(print "Ran top_left")
    		#aborted:(print "aborted")
    	)
        
    )
    

    piemenu_example2.gif
  • MrOneTwo
    Offline / Send Message
    MrOneTwo polycounter lvl 7
    I was using previous verisions and didn't really stumbled on any bugs ;p Will test this new version now.

    I wanted to test those gesture commands with multiple menus. Since I can't nest marcroscripts should I do it via macros.run ? I tried that and got errors (that this new macroscript is not an integer; just created new macroscript file and tried to run it with macros.run). My Maxscript knowledge is still lacking so any tip how should it look ?

    edit : First bug I noticed in 2009 is flickering. It occurs when you move the cursor.
  • editableclips
    I agree that motion graphics is a different beast than 3D animation or compositing, but I feel that motion graphics works would fit best in the animation wip/finished galleries, as it really is animation.
  • perna
    Offline / Send Message
    perna ngon master
    MrOneTwo wrote: »
    edit : First bug I noticed in 2009 is flickering. It occurs when you move the cursor.

    BC: Try to update the screen only when changes happen (if hasChanged then...), the flickering is really unbearable.

    The menus are super sweet now.

    Something I've seen is common with your code is you take the long way around to get at something. During design that's normal, but afterwards you can cut things down a bit; makes the code more manageable and readable. I go through some stuff step by step below so you can follow the thinking.

    Applying principles of algebra:

    from:
    local box_width = text_width + [20,4]
    if min_boxWidth < text_width.x+20 then min_boxWidth = text_width.x+20
    

    to:
    local box_width = text_width + [20,4]
    if min_boxWidth < box_width.x then min_boxWidth = box_width.x
    

    which gives us:
    if min_boxWidth < box_width.x then min_boxWidth = box_width.x
    if box_width.x < min_boxWidth then box_width.x = min_boxWidth
    

    which is another way of writing:
    if min_boxWidth < box_width.x then min_boxWidth = box_width.x
    if min_boxWidth > box_width.x then box_width.x = min_boxWidth
    

    which means we can just use ELSE instead:
    if min_boxWidth < box_width.x then min_boxWidth = box_width.x else box_width.x = min_boxWidth
    


    and now we have:
    local box_width = text_width + [20,4]
    if min_boxWidth < box_width.x then min_boxWidth = box_width.x else box_width.x = min_boxWidth
    cbox.x = x-(box_width.x/2.0)
    cbox.y = y-box_width.y
    cbox.w = box_width.x
    cbox.h = box_width.y
    

    but we might as well set cbox directly and drop box_width:
    cbox.w = text_width.x + 20
    cbox.h = text_width.y + 4
    if cbox.w < min_boxWidth then cbox.w =  min_boxWidth
    cbox.x = x-(cbox.w/2.0)
    cbox.y = y-xbox.h
    

    And on and on like that to go from this:
    for i = 1 to labels.count do
    (
        local text_width = gw.getTextExtent labels[i]
        local box_width = text_width + [20,4]
        local x = ((Sin (slice_amount * (i-1))) * radius + start_pos.x)
        local y = -(Cos (slice_amount * (i-1))) * radius + start_pos.y
        local cbox = box2 x y 0 0
    
        if min_boxWidth < text_width.x+20 then min_boxWidth = text_width.x+20
        if box_width.x < min_boxWidth then box_width.x = min_boxWidth
        cbox.x = x-(box_width.x/2.0)
        cbox.y = y-box_width.y
        cbox.w = box_width.x
        cbox.h = box_width.y
    
        if selected == i then
        (
            if labels[i] != "" then gw.wrect cbox color_box_sel
            gw.wText [x-text_width.x *0.5,y,00] labels[i] color:color_text_sel
        )
        else
        (
            if labels[i] != "" then gw.wrect cbox color_box
            gw.wText [x-text_width.x *0.5,y,00] labels[i] color:color_text
        )
    )
    
    

    To this:
    for i = 1 to labels.count do
    (
        local x = ((Sin (slice_amount * (i-1))) * radius + start_pos.x)
        local y = -(Cos (slice_amount * (i-1))) * radius + start_pos.y
        local textDim = gw.getTextExtent labels[i]
        local boxBox = box2 x (y-textDim.y-4) (textDim.x) (textDim.y + 8)
    
        if boxBox.w < min_boxWidth then boxBox.w = min_boxWidth
        translate boxBox [-boxBox.w/2,0]	-- changing boxBox.x directly only moves the corner
    
        if labels[i] != "" then gw.wrect boxBox (if selected == i then color_box_sel else color_box)
        gw.wText [x- textDim.x/2,y,00] labels[i] color:(if selected == i then color_text_sel else color_text)
    )
    

    Note that there's a difference between 2.0 (float) and 2 (integer)
    The variables named *_width actually contained height as well, so name them something logical
  • Bryan Cavett
    Offline / Send Message
    Bryan Cavett polycounter lvl 14
    Fixed the flickering and few other issues that I had found.

    http://www.bryancavett.com/maxscripts/BCT_PieMenu.mzp

    Perna: thanks for that bit of feedback! I would like to get it into the script but its not incorporated into this version (no time right now and I wanted to get the flickering fixed for 2009 asap:))

    MrOneTwo:

    To get the submenus working I would suggest making a separate macroscript that has the sub pie menu configured with the labels and functions you want and then just call it with macros.run in the top level menu. Of course there are ways to do this from one macroscript but this is the easiest way to explain it to you. I would suggest playing with the threshold of the submenus if the default threshold is too sensitive. It takes a bit to train your hand at first.


    Also you can put multiple macroscripts in one .ms file. When you execute the ms file it will install all the macros
    --MAIN MENU
    macroscript Menu1
    category:"MyMenus"
    (
    
        BCT_PM_Settings.labels = #("Menu1_submenu1", "Menu1_submenu2", "Menu1_submenu3", "Menu1_submenu4", "Menu1_submenu5", "Menu1_submenu6", "Menu1_submenu7", "Menu1_submenu8")
        case BCT_PM_Settings.run() of
        (
            1:(macros.run "MyMenus" "Menu1_submenu1")
            2:(macros.run "MyMenus" "Menu1_submenu2")
            3:(macros.run "MyMenus" "Menu1_submenu3")
            4:(macros.run "MyMenus" "Menu1_submenu4")
            5:(macros.run "MyMenus" "Menu1_submenu5")
            6:(macros.run "MyMenus" "Menu1_submenu6")
            7:(macros.run "MyMenus" "Menu1_submenu7")
            8:(macros.run "MyMenus" "Menu1_submenu8")
            #aborted:(print "aborted")
        )
        
    )
    
    
    -- SUB MENUS
    macroscript Menu1_submenu1
    category:"MyMenus"
    (
    
        BCT_PM_Settings.labels = #("top", "top_right", "right", "bottom_right", "bottom", "bottom_left", "left", "top_left")
    	BCT_PM_Settings.threshold = 120
        case BCT_PM_Settings.run() of
        (
            1:(print "Ran top")
            2:(print "Ran top_right")
            3:(print "Ran right")
            4:(print "Ran bottom_right")
            5:(print "Ran bottom")
            6:(print "Ran bottom_left")
            7:(print "Ran left")
            8:(print "Ran top_left")
            #aborted:(print "aborted")
        )
        
    )
    
    macroscript Menu1_submenu2
    category:"MyMenus"
    (
    
        BCT_PM_Settings.labels = #("top", "top_right", "right", "bottom_right", "bottom", "bottom_left", "left", "top_left")
    	BCT_PM_Settings.threshold = 120
        case BCT_PM_Settings.run() of
        (
            1:(print "Ran top")
            2:(print "Ran top_right")
            3:(print "Ran right")
            4:(print "Ran bottom_right")
            5:(print "Ran bottom")
            6:(print "Ran bottom_left")
            7:(print "Ran left")
            8:(print "Ran top_left")
            #aborted:(print "aborted")
        )
        
    )
    
    macroscript Menu1_submenu3
    category:"MyMenus"
    (
    
        BCT_PM_Settings.labels = #("top", "top_right", "right", "bottom_right", "bottom", "bottom_left", "left", "top_left")
    	BCT_PM_Settings.threshold = 120
        case BCT_PM_Settings.run() of
        (
            1:(print "Ran top")
            2:(print "Ran top_right")
            3:(print "Ran right")
            4:(print "Ran bottom_right")
            5:(print "Ran bottom")
            6:(print "Ran bottom_left")
            7:(print "Ran left")
            8:(print "Ran top_left")
            #aborted:(print "aborted")
        )
        
    )
    
    macroscript Menu1_submenu4
    category:"MyMenus"
    (
    
        BCT_PM_Settings.labels = #("top", "top_right", "right", "bottom_right", "bottom", "bottom_left", "left", "top_left")
    	BCT_PM_Settings.threshold = 120
        case BCT_PM_Settings.run() of
        (
            1:(print "Ran top")
            2:(print "Ran top_right")
            3:(print "Ran right")
            4:(print "Ran bottom_right")
            5:(print "Ran bottom")
            6:(print "Ran bottom_left")
            7:(print "Ran left")
            8:(print "Ran top_left")
            #aborted:(print "aborted")
        )
        
    )
    
    macroscript Menu1_submenu5
    category:"MyMenus"
    (
    
        BCT_PM_Settings.labels = #("top", "top_right", "right", "bottom_right", "bottom", "bottom_left", "left", "top_left")
    	BCT_PM_Settings.threshold = 120
        case BCT_PM_Settings.run() of
        (
            1:(print "Ran top")
            2:(print "Ran top_right")
            3:(print "Ran right")
            4:(print "Ran bottom_right")
            5:(print "Ran bottom")
            6:(print "Ran bottom_left")
            7:(print "Ran left")
            8:(print "Ran top_left")
            #aborted:(print "aborted")
        )
        
    )
    
    macroscript Menu1_submenu6
    category:"MyMenus"
    (
    
        BCT_PM_Settings.labels = #("top", "top_right", "right", "bottom_right", "bottom", "bottom_left", "left", "top_left")
    	BCT_PM_Settings.threshold = 120
        case BCT_PM_Settings.run() of
        (
            1:(print "Ran top")
            2:(print "Ran top_right")
            3:(print "Ran right")
            4:(print "Ran bottom_right")
            5:(print "Ran bottom")
            6:(print "Ran bottom_left")
            7:(print "Ran left")
            8:(print "Ran top_left")
            #aborted:(print "aborted")
        )
        
    )
    
    macroscript Menu1_submenu7
    category:"MyMenus"
    (
    
        BCT_PM_Settings.labels = #("top", "top_right", "right", "bottom_right", "bottom", "bottom_left", "left", "top_left")
    	BCT_PM_Settings.threshold = 120
        case BCT_PM_Settings.run() of
        (
            1:(print "Ran top")
            2:(print "Ran top_right")
            3:(print "Ran right")
            4:(print "Ran bottom_right")
            5:(print "Ran bottom")
            6:(print "Ran bottom_left")
            7:(print "Ran left")
            8:(print "Ran top_left")
            #aborted:(print "aborted")
        )
        
    )
    
    macroscript Menu1_submenu8
    category:"MyMenus"
    (
    
        BCT_PM_Settings.labels = #("top", "top_right", "right", "bottom_right", "bottom", "bottom_left", "left", "top_left")
    	BCT_PM_Settings.threshold = 120
        case BCT_PM_Settings.run() of
        (
            1:(print "Ran top")
            2:(print "Ran top_right")
            3:(print "Ran right")
            4:(print "Ran bottom_right")
            5:(print "Ran bottom")
            6:(print "Ran bottom_left")
            7:(print "Ran left")
            8:(print "Ran top_left")
            #aborted:(print "aborted")
        )
        
    )
    
  • MrOneTwo
    Offline / Send Message
    MrOneTwo polycounter lvl 7
    I tried something like this but now I see where did I make the mistake! I will test it soon!
  • MrOneTwo
    Offline / Send Message
    MrOneTwo polycounter lvl 7
    First of all bumping the thread. So many people wanted good pie menus for 3ds max... now you have it! Where are the cat pictures and praises ?

    I messed with the nested menus. They work great! Below I pasted the code for context senstivie menus. They show nested menu based on the selection. If object is Editable Poly or has Edit Poly modifier it runs different menu than if nothing is selected or object isn't one of those two. It makes those menus super powerful. With a bit of customization they can hold hundreds of commands.

    I didn't include this 'MyMenus Menu1_submenu9_context_EPoly' since it's just another submenu in the same file just like Bryan Cavett showed in previous post.
    
    --MAIN MENU
    macroscript Menu1
    category:"MyMenus"
    (
    
        BCT_PM_Settings.labels = #("Modifiers", "Primitives", "Menu1_submenu3", "Menu1_submenu4", "Menu1_submenu5", "Menu1_submenu6", "Menu1_submenu7", "Menu1_submenu8")
        case BCT_PM_Settings.run() of
        (
            1:(macros.run "MyMenus" "Menu1_submenu1")
            2:(macros.run "MyMenus" "Menu1_submenu2")
            3:(macros.run "MyMenus" "Menu1_submenu3")
            4:(macros.run "MyMenus" "Menu1_submenu4")
            
            5:(
            
                currObj = modPanel.getCurrentObject()
                currBaseObj = selection[1]
                case (classof(currObj) as string) of
                (
                    "Edit_Poly":
                    (
                    macros.run "MyMenus" "Menu1_submenu9_context_EPoly"    
                    )
                    "Editable_Poly":
                    (
                    macros.run "MyMenus" "Menu1_submenu9_context_EPoly"
                    )
                    default: macros.run "MyMenus" "Menu1_submenu5"
                )
            
                )
            
            6:(macros.run "MyMenus" "Menu1_submenu6")
            7:(macros.run "MyMenus" "Menu1_submenu7")
            8:(macros.run "MyMenus" "Menu1_submenu8")
            #aborted:(print "aborted")
        )
        
    )
    
    
  • Revel
    Offline / Send Message
    Revel greentooth
    Major necrobump, sorry guys! But recently I just started to do a clean up of my Max shotcut key cus I realize how slow it is compare to how modo works (use the same shortcut for the same operation no matter what mode I'm in, most noticeably when modeling and UVing) and trying to make some "smart" scripts to combine many operation to a single key.

    I said about clean up, mean really wipe clean all of the default assigned shortcut, right from the MaxStartUI.kbd file. But whenever I try to load the file, it loads alright (empty shortcut list) but as soon as I close and reopen max, all of those default shortcut are written back into the MaxStartUI.kbd automatically. Anyone notice such behavior? And if so, how should I deal with it?

    Really wanted to try how Perna set his keyboard shortcut, but this issue just holding me back since yesterday.

    FYI: 3ds Max 2009/ 3ds Clean installed.
  • perna
    Offline / Send Message
    perna ngon master
    Revel: I did the same thing way back when except I manually cleared everything in the shortcut editor instead of editing the .kbd, so that ought to work. I remember that there was a trick to making it work though. Might have been instead of clearing, I assigned everything to the same shortcut. Can't remember entirely how I did it, but there is a way to go about it
  • Revel
    Offline / Send Message
    Revel greentooth
    Yeah! nice one Perna. I edited all the shortcut key become the same in .kbd file using regex so anything match the pattern "=\w+\s\w+" replace with "=0 0" and it will overwrite (remove) all my shortcut including the one I carefully crafted based on your suggestion, that's why we need to always backup the files that we actually wanted.

    And after that copy back those values in (from back up kbd file into the one on maxroot/ui). In my case those values are always the top 24 lines of number (that don't make any sense to human eyes) plus the rest at bottom of the list that actually got a word in it, something like "1=11 81 SubObject_1`Modifier Stack 647394". If I did the regex edit since the beginning, there'll be no need for an extra step to copy those values in.

    IMPORTANT STEP; after all that go to the maxroot folder and find splash.cfg it contain all the shortcut key information in a more human language way (shortcut key written as ctrl+x instead of some hax number), remove all values in it but I keep the <...></...> tag though. And make it as read only file. I have no idea what is this file for but it did the trick preventing max to keep on write the default shortcut key in!
  • perna
    Offline / Send Message
    perna ngon master
    haha, nice writeup.
    I don't have a splash.ini anymore, have you tried just deleting it?
  • Revel
    Offline / Send Message
    Revel greentooth
    I try to delete the splash.cfg (it's .cfg file though, I don't have .ini file as well in the folder) and as expected max will rebuild it. But it didn't give me the weird behavior like previous, haha! 3ds max works in a mysterious way! :D

    Edit; the newly created splash.cfg file does not required to set as read only file anymore. So I wonder if since the first we delete the splash.cfg whether or not it will give us the weird behavior ? hmmm...

    In case you wondering what was inside the file, here is the snippet of what I got;
    <actionGroup name="Snaps Action Table">
        <action name="Snap To Vertex Toggle" shortcut="Ctrl+F3"/>
        <action name="Snap To Edge/Segment Toggle" shortcut="Shift+F3"/>
        <action name="Snap To Face Toggle" shortcut="Alt+F3"/>
    </actionGroup>
    
    <actionGroup name="FixAmbientActions">
    </actionGroup>
    
    <actionGroup name="InstanceDuplMapActions">
    </actionGroup>
    
    <actionGroup name="MMCleanActions">
    </actionGroup>
    
    <actionGroup name="Action">
    </actionGroup>
    
    The list goes on long after this one. As you can see it's humanly possible to read. Why wont Autodesk make the actual kbd file to looks like this...oh well...
  • Revel
    Offline / Send Message
    Revel greentooth
    Hey Per, quick question; how did you make your custom UI Action/ Shortcut window box to be as bis as this? I can't seem to simply drag resize the window bigger.
  • Revel
    Offline / Send Message
    Revel greentooth
    Hello people! Does anyone of you guys still using max 2009?
    If yes, can you check whether max break the 'ctrl+alt+r' combination for shortcut? I check on max 2014 before and it seems like all fine on that version, but not sure on other version. It's kind of a weird bug?
  • miauu
    Offline / Send Message
    miauu polycounter lvl 8
    What do you mean by break?
    I've just assigned "ctrl+alt+del" to run one of my scripts and it works well.
    Restarted Max and the hotkey works.
  • Revel
    Offline / Send Message
    Revel greentooth
    Well, it's just wont work haha I know it's weird right? I try to assign it from the hotkey editor window, press ctrl+alt then whenever I press +r, it just keep on display ctrl+alt, max seems like not recognize ctrl+alt+r as a valid input. Fine, I hack it in from the kbd file, still not working.

    Miauu, what version are you using? yeah other then ctrl+alt+r are working, it's just that 1 combination that max cant register as a valid input for the hotkey..hmmm...
  • miauu
    Offline / Send Message
    miauu polycounter lvl 8
  • Revel
    Offline / Send Message
    Revel greentooth
    Oh, can you try assign the ctrl+alt+r to something?
    ctrl+alt+q, ctrl+alt+w, ctrl+alt+e all is working, just ctrl+alt+r that give me problem..
  • miauu
    Offline / Send Message
    miauu polycounter lvl 8
    My mistake. I have assigned "ctrl+alt+r" to one of my scripts and the hotkey works without problems.
  • fdfxd
    Thanks, these tips were very useful. :D
  • Revel
    Offline / Send Message
    Revel greentooth
    Hmm, weird..but I did messed around with the kbd files and deleting a file related to keyboard shortcut (splash.cfg) though, you can see few post above in this thread, that might have caused it somehow...But for now I'll just change it to other shortcut key, since it seems just affecting my max alone.
  • perna
    Offline / Send Message
    perna ngon master
    Revel:
    ctrl+alt+r works on my max 2009 too. I have all updates installed except the very last one. You might have some other software running a keyboard hook, meaning the issue has nothing to do with 3ds Max. Try assigning that shortcut in another program and see what happens.
  • Revel
    Offline / Send Message
    Revel greentooth
    Per, you're right. It's not max related problem, but it's my windows doing something with it. I have no idea what could that be..Best I just avoid using that combination for now..
    FYI, I'm on Win7 64bit.
  • thebinhle
    Offline / Send Message
    thebinhle polycounter lvl 5
    Perna. Can you share me your shortcuts keyboard file ? Thank you very much.
  • perna
    Offline / Send Message
    perna ngon master
    thebinhle wrote: »
    Perna. Can you share me your shortcuts keyboard file ? Thank you very much.

    Thebinhle, I use smartFunctions for everything, so the shortcut file won't work for anyone else
  • martinszeme
    Offline / Send Message
    martinszeme polycounter lvl 8
    perna, I was wondering if you could share your colour scheme? Love how it looks and whenever I try adjusting them myself individually I colour stuff incorectly as they've been named very poorly. Also it would be cool if one could change UI colours interactively in max, instead of going in some menus etc. Autodesk need to step up their game...
  • poopipe
    Offline / Send Message
    poopipe sublime tool
    Revel wrote: »
    Per, you're right. It's not max related problem, but it's my windows doing something with it. I have no idea what could that be..Best I just avoid using that combination for now..
    FYI, I'm on Win7 64bit.

    Bit late now but I had a similar issue a while back - alt+w simply wouldnt work, reinstalling had no effect etc.etc.. It came back after I rebuilt my machine so I put it down to shitty luck ...

    And on topic

    Perna (or anyone) could you point me at an example script or just the function for setting keyboard shortcuts up - last time I looked I couldnt find anything and I cant be arsed manually setting 2015 up again..
    Ta :)
  • haiddasalami
    Offline / Send Message
    haiddasalami polycounter lvl 9
    poopipe wrote: »
    Perna (or anyone) could you point me at an example script or just the function for setting keyboard shortcuts up - last time I looked I couldnt find anything and I cant be arsed manually setting 2015 up again..
    Ta :)

    Just export your shortcuts and import them in. Unless Im misreading this. You need to do *.* cause they changed the format for keyboard shortcut files in 2015
13
Sign In or Register to comment.