Maxscript help needed: Edit Poly modifer Selection

Maxscript help needed: Edit Poly modifer Selection

darawork
Advisor Advisor
1,186 Views
5 Replies
Message 1 of 6

Maxscript help needed: Edit Poly modifer Selection

darawork
Advisor
Advisor

Hi all,

I found this post interesting on the main forums.
Seems there was once code on Scriptspot script years ago, which has since gone offline.

Would anyone here be able to upgrade the code so as to work with 2023 Max?
Please inform the original writer before attempting to release it as a new script.

Tool by: Mathieson Facer
E-mail: info@mathiesonfacer.com

Best regards 🙂

 

 

 

/*
Tool Name: 'Modifier Auto-Select'

Tool by: Mathieson Facer
E-mail: info@mathiesonfacer.com
Website: www.mathiesonfacer.com
Start Date: August 24, 2009
	
History:
	February 28, 2010 - v2.02
		- Fixed typo in selectModifier function. Was passing argument to navigateModStack function as
		findMod when in the navigateModStack definition it was actually findModifier. Was causing tool
		not to work with multiple nodes selected.
	February 26, 2010 - v2.01
		- Made it so with multiple selected objects, if it doesn't find the modifier instance
		after walking down the stack, it walks back up to the top so the user doesn't have
		the bottom modifier auto-selected.
	February 25, 2010 - v2.0
		- Cleaned up code for re-release.
		- Added functionality when multiple nodes selected.
		- Made selectModifier and selectBaseobject two different functions.
To Do:
	- To do list goes here.		
Bugs:
	- List of bugs goes here.
Testing:

Test code can be placed in this area.

*/

struct MJF_modifierAutoSelect
(
	uiInst,
	
	selectionTimer = dotNetObject "System.Windows.Forms.Timer" ,
	storedModClass = "Baseobject",
	
	fn createUi =
	(
		uiInst = rollout modifierAutoSelectR "Modifier Auto-Select - facerFX Tools" width:200
		(
			local rW = 200
			local w1 = rW * .875
			local w2 = w1 / 2
			local w3 = w1 / 3
			local spnW1 = 50
			
			dropdownlist modClassDdl items:#() width:(w1 * .7) height:20 align:#left across:2
			button refreshModsBtn "Refresh" width:(w1 * .28) align:#right
			checkbox searchNameCbx "Has string:" align:#left across:2
			edittext searchNameEtb align:#right width:(w2 * 1.17)
			checkbutton enableCbt "Auto-Select Enabled" width:w1 align:#center
			
			fn getModClassesAsStrings =
			(
				local sceneMods = #()
				for x in objects do sceneMods += x.modifiers
				
				local modClassStrings = for x in sceneMods collect (classOf x) as string
				modClassStrings = makeUniqueArray modClassStrings
				append modClassStrings "Baseobject"
				sort modClassStrings
			)
			
			fn toggleAutoSelect state =
			(
				MJF_modifierAutoSelect.enableCallbacks state
				
				if state == false then
				(
					MJF_modifierAutoSelect.selectionTimer.stop()
				)
			)
			
			fn updateUi =
			(
				local modClasses = getModClassesAsStrings()
				modClassDdl.items = modClasses
				
				local modIndex = findItem modClasses MJF_modifierAutoSelect.storedModClass
				modClassDdl.selection = case modIndex of
				(
					0: 1
					default: modIndex
				)
			)
			
			on modClassDdl selected val do
			(
				MJF_modifierAutoSelect.storedModClass = modClassDdl.selected
			)
			
			on refreshModsBtn pressed do
			(
				updateUi()
			)
			
			on enableCbt changed val do
			(
				toggleAutoSelect val
			)
			
			on modifierAutoSelectR open do
			(
				updateUi()
			)
			
			on modifierAutoSelectR close do
			(
				toggleAutoSelect false
			)
		)
	),
	
	fn launchUi =
	(
		if classOf uiInst == RolloutClass then destroyDialog uiInst
		
		createDialog uiInst style:#(#style_titlebar, #style_border, #style_sysmenu)
	),
	
	fn enableCallbacks state =
	(
		callbacks.removeScripts id:#MJF_modifierAutoSelect
		
		if state then
		(
			callbacks.addScript #selectionSetChanged \
				"MJF_modifierAutoSelect.startSelectionChangeTimer()" \
				id:#MJF_modifierAutoSelect
		)
	),
	
	fn selectBaseobject objs =
	(
		if (objs.count == 1) then
		(
			local baseobject = objs[1].baseobject
			
			if (modPanel.getCurrentObject()) != baseobject then
			(
				modPanel.setCurrentObject baseobject node:objs[1]
			)
		)
	),
	
	fn getModsFromObjs objs modClass =
	(
		local allMods = #()
		for x in objs do allMods += x.modifiers
		
		local theMods = for x in allMods where (classOf x == modClass) collect x
		
		-- This filters for only the modifiers with the matching string pattern in the name.
		if uiInst.searchNameCbx.state then
		(
			theMods = for x in theMods where \
				(matchPattern x.name pattern:("*" + uiInst.searchNameEtb.text + "*")) collect x
		)
		
		-- This filters out any modifiers that do not have an instance on all supplied nodes.
		for i = theMods.count to 1 by -1 do
		(
			local deleteMod = false
			
			for x in objs do
			(
				if ((findItem x.modifiers theMods[i]) == 0) then
				(
					deleteMod = true
				)
			)
			
			if deleteMod then
			(
				deleteItem theMods i
			)
		)
		
		theMods
	),
	
	fn navigateModStack direction:#down findModifier: =
	(
		local lastMod = undefined		-- Needed to check if we're at the end of the modifier stack.
		while (modPanel.getCurrentObject() != findModifier) and \
			(modPanel.getCurrentObject() != lastMod) do
		(
			lastMod = modPanel.getCurrentObject()
			case direction of
			(
				#down: max prev mod
				#up: max next mod
			)
		)
	),
	
	fn selectModifier objs modClassStr =
	(
		/*
		<DOC> Selects the modifier of the specified class from the currently selected node.  Doesn't work with multiple nodes selected.
		Arguments:
			<array> objs:	An array of nodes.  Needs to be an array since it will work with "selection".
			<modifier> modClassStr:	Class of a modifier to auto-select.
		Return:
			Does not return anything.
		*/
		
		local modClass = execute modClassStr
		
		if (modClass != undefined) then
		(
			local theMod = (getModsFromObjs objs modClass)[1]
			
			if (isValidObj theMod) then
			(
				if (objs.count == 1) then
				(
					if (modPanel.getCurrentObject() != theMod) then
					(
						modPanel.setCurrentObject theMod node:objs[1]
					)
				)
				else if (objs.count > 1) then
				(
					navigateModStack findModifier:theMod
					
					if (modPanel.getCurrentObject() != theMod) then
					(
						navigateModStack direction:#up
					)
				)
			)
		)
	),
	
	fn initTimer =
	(
		-- This function is out of scope because it is being called through the timer. Need to call anything
		-- in the struct through the struct instance.
		fn callSelectMod =
		(
			if (MJF_modifierAutoSelect.storedModClass == "Baseobject") then
			(
				MJF_modifierAutoSelect.selectBaseobject (selection as array)
			)
			else
			(
				MJF_modifierAutoSelect.selectModifier (selection as array) MJF_modifierAutoSelect.storedModClass
			)
			MJF_modifierAutoSelect.selectionTimer.stop()
		)
		
		dotnet.addEventHandler MJF_modifierAutoSelect.selectionTimer "tick" callSelectMod
		MJF_modifierAutoSelect.selectionTimer.interval = 1
	),
	
	fn startSelectionChangeTimer =
	(
		/*
		<DOC> Rollout and timer required to call the selectMod function above because of an issue with the callback mechanism.
		Arguments:
			<array> objs:	An array of nodes.  Needs to be an array since it will work with "selection".
			<modifier> modifier:	Class of a modifier to auto-select.
		Return:
			Does not return anything.
		*/
		
		selectionTimer.start()
	)
)
MJF_modifierAutoSelect = MJF_modifierAutoSelect()
MJF_modifierAutoSelect.createUi()
MJF_modifierAutoSelect.initTimer()

-- MJF_modifierAutoSelect.launchUi()

 

 

 

Darawork
AutoDesk User
Windows 11, 3DS Max 2026, Revit 2026, AutoCad 2026, Dell Precision 7875 nVidia - Quadro RTX4000 ATA - AMD Ryzen Threadripper PRO 7965WX 24-Cores - 128GB RAM

0 Likes
1,187 Views
5 Replies
Replies (5)
Message 2 of 6

denisT.MaxDoctor
Advisor
Advisor

 

 

macroscript DTS_STACK_JUMP 
	category:"DTS" 
	buttontext:"STACK JUMP" 
	toolTip:"Auto Stack Jump (+SHIFT Use Current Class) (+ESC Use Current Object)" 
	autoUndoEnabled:off
	silentErrors:off 
(
	struct dts_stack_jump_struct
	(
	public
		active = off,

		this_mode,

		last_node = ok,
		this_node,
		
		last_edit = ok,
		this_edit,
		need_edit,
				
		obj_class = undefined, -- or any Modifier or SpacewarpModifier Class,

		fn is_modifier obj = superclassof obj == Modifier or superclassof obj == SpacewarpModifier, 
		fn is_modifier_class obj = superclassof obj == MAXWrapper and (iskindof obj Modifier or iskindof obj SpacewarpModifier), 
		
		fn stack_jump = 
		(
			this_mode = getCommandPanelTaskMode()
			this_node = selection[1]
			
			if (this_mode == #modify and isvalidnode this_node) do
			(			
				this_edit = modpanel.getcurrentobject()
				
				need_edit = if is_modifier obj_class then obj_class 
					else if is_modifier_class obj_class then this_node.modifiers[obj_class] 
					else this_node.baseobject
						
				if (this_edit != undefined) then
				(
					if isvalidobj need_edit and this_node != last_node do 
					(
						if (need_edit != this_edit) do
						(
							callbacks.removescripts id:#dts_stack_jump
							if (last_edit == undefined) then
							(
								modpanel.setcurrentobject this_edit
								this_node = undefined
							)
							else
							(
								modpanel.setcurrentobject need_edit	
							)
							callbacks.addscript #modPanelSelChanged "(globalvars.get #DTS_STACK_JUMP).stack_jump()" id:#dts_stack_jump
						)
					)
				)
				else
				(
					if (last_edit != undefined) do this_node = undefined
				)
			)
			
			last_node = this_node
			last_edit = this_edit
		),
		on create do
		(
			callbacks.removescripts id:#dts_stack_jump
			last_node = undefined 
			active = off
		)
	)
	
	local d
	local s = dts_stack_jump_struct
	global DTS_STACK_JUMP = dts_stack_jump_struct()
	
	
	fn enabled = isstruct (d = try (globalvars.get #DTS_STACK_JUMP) catch())
	on isEnabled return (enabled())
	on isChecked return (enabled() and d.active)
		
	on execute do if (enabled()) do
	(
		callbacks.removescripts id:#dts_stack_jump
		if keyboard.shiftpressed then
		(
			d.this_edit = modpanel.getcurrentobject()
			d.obj_class = if s.is_modifier d.this_edit do classof d.this_edit
			d.active = off
		)
		else if keyboard.escpressed do
		(
			d.this_edit = modpanel.getcurrentobject()
			d.obj_class = if s.is_modifier d.this_edit do d.this_edit
			d.active = off
		)
		if (d.active = not d.active) do
		(
			callbacks.addscript #modPanelSelChanged "(globalvars.get #DTS_STACK_JUMP).stack_jump()" id:#dts_stack_jump
			
			d.last_node = undefined
			d.last_edit = ok
			d.stack_jump()
		)
		updateToolbarButtons()
	)
)

 

 

 

Save it as a maxscript MCR file and place it in any macroscripts startup directory. After loading MAX, you will be able to find the macro. Looking at your experience, you should easily understand how to use it... or ask me for details.

 

 

And let the author correct the script above.

 

 

code has been changed due to bug fixes and improvements added...

Message 3 of 6

darawork
Advisor
Advisor

Hi, the original Maxscript doesn't work in 2023, that's why I'm reposting it here. I'll test your Maxscripting tomorrow. Cheers dude. Looks good. It's always good to mention names. The original coder looks very OG. 🙂 

Regards, 

Darawork
AutoDesk User
Windows 11, 3DS Max 2026, Revit 2026, AutoCad 2026, Dell Precision 7875 nVidia - Quadro RTX4000 ATA - AMD Ryzen Threadripper PRO 7965WX 24-Cores - 128GB RAM

0 Likes
Message 4 of 6

denisT.MaxDoctor
Advisor
Advisor

@darawork wrote:
  1. Hi, the original Maxscript doesn't work in 2023, that's why I'm reposting it here.  

it's the author's problem 😎

Message 5 of 6

darawork
Advisor
Advisor

Not if loads of users are looking for the code. It got 53 likes on Scriptspot then just died. 53 likes is a decent amount of likes on Scriptspot. That's intriguing.

 

Regards, 

Darawork
AutoDesk User
Windows 11, 3DS Max 2026, Revit 2026, AutoCad 2026, Dell Precision 7875 nVidia - Quadro RTX4000 ATA - AMD Ryzen Threadripper PRO 7965WX 24-Cores - 128GB RAM

0 Likes
Message 6 of 6

denisT.MaxDoctor
Advisor
Advisor

@darawork wrote:

Not if loads of users are looking for the code. It got 53 likes on Scriptspot then just died. 53 likes is a decent amount of likes on Scriptspot. That's intriguing.

 


Since I took the time to write this script, I'm still interested to know what practical value it has... Could you explain it to me?
I remember being asked several times by different clients about a similar script, and I wrote several variants at different times... But this one seems to me the most elegant, despite the fact that I did not understand its practical purpose.

0 Likes