[SOLVED] Accessing Root Object from Custom Attribute Modifier


tl;dr I want to be able to access the root object through a custom attribute modifier applied to that object.

I’m working on a script that imports animations from a game (Rocket League) and rebuilds them inside 3ds Max. I recently added a feature that would let you import multiple camera angles from the same section of time of a replay, but the sync isn’t perfect. I want to be able to shift all the keyframes of a camera by a specified amount of time.

My ideal method is applying an empty modifier to the camera with a “Shift Amount” spinner and an “Apply Shift” button. What that would then do is use the MoveKeys function on the position, rotation, and fov controllers to shift all the keys by that specified amount. However, I can’t seem to find a way to access the camera itself from within that custom attributes modifier.

Is it possible to access the root object of a custom attribute modifier from the attribute’s rollout?

Sample code:

local TheCamera = FreeCamera name:CameraName

-- Add custom attributes
AddModifier TheCamera (EmptyModifier())
local TheModifier = TheCamera.modifiers[1]
TheModifier.name = "Sync Shift"
SyncShiftModifier = attributes SyncShiftAttributes
	-- Sync Shift rollout
	parameters syncshiftparams rollout:ro_syncshift
		ShiftAmount type:#float ui:SpinnerShiftAmount
	rollout ro_syncshift "Sync Shift"
		spinner  SpinnerShiftAmount "Shift Amount " width:162 pos:[-5,5] range:[-1000,1000,0] type:#float
		button   ButtonApplyShift "Apply Shift" width:152 pos:[5,25]
		on ButtonApplyShift pressed do
			-- I understand why the error exists, I would just like to know *how* to access the camera --
			MoveKeys TheCamera.Position.X_Position.Controller SpinnerShiftAmount.value
			MoveKeys TheCamera.Position.Y_Position.Controller SpinnerShiftAmount.value
			MoveKeys TheCamera.Position.Z_Position.Controller SpinnerShiftAmount.value
			MoveKeys TheCamera.Rotation.X_Rotation.Controller SpinnerShiftAmount.value
			MoveKeys TheCamera.Rotation.Y_Rotation.Controller SpinnerShiftAmount.value
			MoveKeys TheCamera.Rotation.Z_Rotation.Controller SpinnerShiftAmount.value
			MoveKeys TheCamera.FOV.Controller 1
			SpinnerShiftAmount.value = 0
CustAttributes.Add TheModifier SyncShiftModifier


Nevermind, solved!

I just realized something: this modifier will only be visible to the user if they have only the camera selected. Since that is the case, the script could access the camera from selection:

local ThisCamera = selection[1]

MoveKeys ThisCamera.Position.X_Position.Controller SpinnerShiftAmount.value
MoveKeys ThisCamera.Position.Y_Position.Controller SpinnerShiftAmount.value

How would I cast selection to a camera so that I could access its position / rotation / fov controllers? edit: I forgot to cross this part out when I figured it out. Originally I was trying to access the controllers on selection directly which didn’t work, but accessing them through selection[1] worked just fine.


Just in case you were wondering how to do this anyways - If you wanted to access a specific node on a modifier and thus it’s baseobject you can save a weak reference to that node in the parameter block as a #MaxObject type and store a NodeMonitor node:.
This will get funky if you instance the modifier.
NodeMonitor is not documented, but is super useful


Interesting, I didn’t know #MaxObject and NodeMonitor existed. Thanks!


This is not always true… Stack might be in the “pinned” state, when selected node doesn’t match an owned node in the stack.


Huh, I’ve been using max on and off for 10 years and never knew you could pin the stack! I think for this script I won’t need to handle that case since for the most part the user would import the camera, select it, do the shift if necessary, and then never need to touch the camera again. I think it would be very unlikely that any of them would decide to pin the camera since they won’t need to modify it 90% of the time.

Thanks for enlightening me about pinning though! If only I had noticed that button years ago…