-- gizmoControl v0.4 for 3ds Max 9 - 29.07.07 - 23.02.08 -- (c) Martin Breidt (martin@breidt.net) -- -- This script will allow you to link a modifier gizmo to some other scene object. Afterwards, the gizmo -- will always be fully aligned to that other scene object, which can be freely animated by any kind of -- controller and linked to any other scene node. In some way this is very similar to World Space Modifiers -- but it works for any kind of modifier gizmo. -- -- Can be very useful for creating complex modifier animations, for example animating a Slice -- modifier's plane along a path, attaching a UVW Map modfier to some animated helper object, -- transforming a Displace modifier with some other object, or having an extra node control the -- symmety plane of a Symmetry modifier. -- -- Currently only works for modifier gizmos that can move, rotate and scale -- -- Known limitations: -- * Instancing modifiers that have controlled gizmos will produce incorrect results -- -- New in v0.4: -- * some minor UI tweaks -- * fixed major bug in transform calcluation when objects had offset pivots -- New in v0.3: -- * Added option (SHIFT key) to keep initial offset: gizmo will not move but still be linked to control node -- New in v0.2: -- * Copy/pasting a modifier that has a linked gizmo now works correctly -- * Added a button to remove the scripted controler that links the gizmo to the control node. Press SHIFT to leave gizmo where it is. -- * Should work correctly with Merge and Xref -- -- To Do: -- * Use Custom Attributes to pick control object and to remove control macroscript gizmoControl category:"MB Tools" ( local modList = #() -- list of modifier names local mArr = #() -- list of modifiers local modSelect = 0 -- which modifier was selected in sub dialog local sourceObj -- object that holds the modifier local targetObj -- object that will control the modifier gizmo local gcDialog -- the main dialog local ok_to_link = 0 -- go button will be enabled if this is > 1 fn getMod_M3_SubAnims m = ( -- return all subAnims of a modifier m that are controlled by a Matrix3Controller local res = #() for i = 1 to m.numSubs do ( if (superclassof m[i].controller)==Matrix3Controller then append res m[i] ) res ) rollout modListRO "Modifier Stack" width:160 height:184 -- dialog to list all modifiers of one object and have the user pick one ( listbox lb_modList "" pos:[8,8] width:144 height:10 button b_ok "OK" pos:[8,152] width:65 height:20 button b_cancel "Cancel" pos:[88,152] width:65 height:20 fn updateModPick itm = ( if itm!=undefined and itm>0 then ( -- store selection modSelect = itm -- update main dialog gcDialog.p_pickMod.text = lb_modList.selected gcDialog.b_reset.enabled = true ok_to_link += 1 if ok_to_link>1 then gcDialog.b_doIt.enabled = true ) destroyDialog modListRO ) on modListRO open do ( -- build list of modifiers present in the object mArr = sourceObj.modifiers modList = for m in mArr collect m.name -- build list from variable lb_modList.items = modList gcDialog.p_pickMod.text = "Pick modifier object" gcDialog.b_reset.enabled = false ok_to_link = 0 ) on lb_modList doubleClicked itm do ( -- user has selected the modifier mArr[itm] updateModPick itm ) on b_ok pressed do ( updateModPick lb_modList.selection ) on b_cancel pressed do ( modSelect=0 destroyDialog modListRO ) ) rollout aboutRO "About gizmoControl" width:256 height:288 ( label l1 "gizmoControl links a modifier's gizmo to any other scene node. This can be useful to animate a Slice modifier or a UVW Map modifier, for example. Usage:" pos:[8,8] width:240 height:72 label l2 "1. Pick an object with modifiers" pos:[16,80] width:146 height:13 label l3 "2. Select which modifier to use" pos:[16,95] width:232 height:13 label l4 "3. Pick the control object that the modifier's gizmo will be aligned to" pos:[16,110] width:232 height:28 label l5 "4. Hit 'Link' to create the link between the gizmo and the control object. If you press SHIFT at the same time, the gizmo will keep it's initial offset" pos:[16,140] width:232 height:40 label l6 "5. From now on, the gizmo will follow exactly any transformations of the control object" pos:[16,182] width:232 height:28 button b_ok "OK" pos:[80,264] width:80 height:16 label lbl7 "6. To remove the link, use the 'Reset' button. Press SHIFT at the same time to keep the gizmo at the current location" pos:[16,212] width:232 height:41 on b_ok pressed do ( destroyDialog aboutRO ) ) rollout gcDialog "gizmoControl" width:216 height:160 ( local stage = 0 label l1 "gizmoControl v0.4 - © 2008 M. Breidt" pos:[8,8] width:176 height:13 button b_about "?" pos:[192,7] width:16 height:16 toolTip:"Information about gizmoControl" pickbutton p_pickMod "Pick modifier object" pos:[8,32] width:96 height:21 toolTip:"Pick scene object with a modifier that has gizmo which should be controlled by the other object's transformation" pickbutton p_pickNode "Pick control object" pos:[112,32] width:96 height:21 message:"" toolTip:"Pick scene object that will control the modifier gizmo" button b_reset "Reset gizmo" enabled:false pos:[8,64] width:200 height:21 toolTip:"Reset selected modifier gizmo and remove link to control node.\nHold SHIFT to keep current transformation" button b_doIt "Link gizmo to node transform" enabled:false pos:[8,96] width:200 height:21 toolTip:"Create scripted controller for the selected modifier gizmo to always align the gizmo to the control node.\nHold SHIFT to keep initial offset." button b_close "Close" pos:[64,128] width:80 height:21 toolTip:"Close gizmoControl dialog" on b_about pressed do createDialog aboutRO on p_pickMod picked obj do ( sourceObj = obj createDialog modListRO -- modal:true -- show list of existing modifiers ) on p_pickNode picked obj do ( -- control object is picked targetObj = obj p_pickNode.text = obj.name ok_to_link += 1 if ok_to_link>1 then b_doIt.enabled = true ) on b_reset pressed do ( -- reset (=unlink) the gizmo local doReset = not keyboard.shiftPressed if not (isValidNode sourceObj) then ( messageBox "Please pick a valid modifier object!" ) else ( if modSelect==0 then ( messageBox "Please select a modifier!" ) else ( -- remove scripted controller from gizmo transform local myMod_SA_Arr = getMod_M3_SubAnims mArr[modSelect] if myMod_SA_Arr.count > 0 then ( -- we have at least one suitable subAnim in the controller myMod_SA = myMod_SA_Arr[1] -- format "gizmoControl: Using modifier = % and subAnim = % to reset controller\n" mArr[modSelect] myMod_SA.name myMod_SA.controller = prs() -- keeps current transform if doReset then ( -- user wants the gizmo to be reset and not just stay where it is and unlink it myMod_SA.position = [0,0,0] myMod_SA.rotation = quat 0 0 0 1 myMod_SA.scale = [1,1,1] ) ) else ( messageBox ("No suitable gizmo found in modifier " + mArr[modSelect].name ) ) ) ) ) on b_doIt pressed do ( -- link modifier gizmo to control object local keepOffset = keyboard.shiftPressed -- if Shift was pressed, the gizmo will not move during unlinking if not (isValidNode sourceObj) then ( messageBox "Please pick a valid modifier object!" ) else ( if modSelect==0 then ( messageBox "Please select a modifier!" ) else ( if not (isValidNode targetObj) then ( messageBox "Please pick a valid control object!" ) else ( -- create scripted controller for gizmo transform -- get a list of potential gizmos from the selected modifier local myMod_SA_Arr = getMod_M3_SubAnims mArr[modSelect] if myMod_SA_Arr.count > 0 then ( -- we have at least one suitable subAnim in the controller myMod_SA = myMod_SA_Arr[1] -- format "gizmoControl: Using modifier = % and subAnim = % to create script controller\n" mArr[modSelect] myMod_SA.name myC = myMod_SA.controller = transform_script() myC.AddNode "ctrlObj" targetObj myC.AddNode "modObj" sourceObj local myEx = "modObj = (refs.dependentNodes this)[1]\n" append myEx "if (classof modObj[#transform])==SubAnim then dependsOn modObj.transform.controller\n" -- get modifier context TM and store in script controller as hardcoded value mCtxTM = getModContextTM sourceObj mArr[modSelect] append myEx ("mCtxTM = " + (mCtxTM as string) + "\n") local mOff, offset if keepOffset then ( -- gizmo stays where it is but will be linked to control object mOff = myMod_SA.controller.value -- current gizmo transform local cTrans = targetObj.transform * mCtxTM * inverse sourceObj.objectTransform -- current target transform in gizmo coords offset = mOff * (inverse cTrans) ) else ( -- gizmo will jump to control object's position mOff = offset = matrix3 1 -- append myEx "if (isValidNode ctrlObj) and (isValidNode modObj) then (ctrlObj.transform * mCtxTM * inverse modObj.objectTransform) else (matrix3 1)" -- $.modifiers[#slice].slice_plane.transform = $pt.transform * inverse $.transform * (getModContextTM $ $.modifiers[#slice]) * inverse ( (scaleMatrix $.objectoffsetscale) * ($.objectoffsetrot as matrix3) * (transMatrix $.objectoffsetpos) ) -- world coord * getModContextTM * (inverse modnode.objectTransform) ) append myEx "if (isValidNode ctrlObj) and (isValidNode modObj) then (\n" append myEx (" ctrlTrans = " + (offset as string) + " * (ctrlObj.transform * mCtxTM * inverse modObj.objectTransform)\n") append myEx (") else " + (mOff as string) ) myC.setExpression myEx messageBox ("Successfully linked\n\n$" + sourceObj.name + "." + mArr[modSelect].name + "." + myMod_SA.name + "\n\nto node $" + targetObj.name) title:"gizmoControl" beep:false ) else ( messageBox ("No suitable gizmo found in modifier " + mArr[modSelect].name) ) ) ) ) ) -- end: on on b_close pressed do ( destroyDialog gcDialog ) ) -- open dialog on execute createDialog gcDialog ) -- end: macroscript