MaxPlus and Setting Slice_Modifier Position/Rotation/Scale & "Animatable"

Hi, I am trying to animate a slice plane modifier using MaxPlus only, no Maxscript in between.

After some painful trial and error I got far enough to get the “SubAnim” from then modifier, but now I CANNOT assign new position coordinates or anything else.
I just don’t know how I am suppose to set new positions etc to this SubAnim.

Basically, how do I assign new values to an “Animatable” class object. I can’t figure out how to use a setattrib either for those…

#Get Slice_Plane from Slice Modifier
slicePlane = sliceMod.GetSubAnim(1)
#Assign new values to plane
slicePlane[0] = newPos #Problem Here
slicePlane[1] = newRot #Here
slicePlane[2] = newScl #And Here

>>><type ‘exceptions.TypeError’> ‘Animatable’ object does not support item assignment

#This can show the attributes I wanna change
for x in slicePlane:
print x
>>>Animatable(Position XYZ)
>>>Animatable(Euler XYZ)
>>>Animatable(Bezier Scale)

In Maxscript it was much easier "obj.modifiers[x].Slice_Plane.position = newPos

Has anyone ever done something like this with MaxPlus, or does anyone Know how to???

Thank you!

import MaxPlus

SliceModifierID = MaxPlus.Class_ID(775373310, 309340855)

for node in MaxPlus.SelectionManager.Nodes:
	for mod in node.Modifiers:
		if mod.ClassID == SliceModifierID:
			slicePlane  = mod.GetSubAnim(1)
			transform_ctrl = MaxPlus.Control__CastFrom(slicePlane)

			pos = transform_ctrl.GetPositionController().GetPoint3Value()
			rot = transform_ctrl.GetRotationController().GetQuatValue()
			scale = transform_ctrl.GetScaleController().GetScaleValue()

			print pos, rot, scale

			transform_ctrl.GetPositionController().SetPoint3Value(MaxPlus.Point3(0, 0, 10))

Thank you very very much for showing me how to do this, you saved me loads of time, assuming I would even guess that you can do it this way.

May I ask that you explain a little bit the “MaxPlus.Control__CastFrom()”, what exactly are we doing when we call this method?

Thanks a bunch! :D::D::D:

Great stuff Swordslayer!

I’ve made little changes to meet the recommended guidelines shown in the Python API documentation.

Basically, MaxPlus already has the class ids defined, so we just use that, MaxPlus.ClassIds.SliceModifier, also changed the cast like they show it in the docs, MaxPlus.Control._CastFrom()

There is an example called demoTypeCasting.py, you might want to have a look at it and any search on type casting should give you valuable results.

When you get the subAnim, we are getting an object type Animatable that doesn’t have the required methods we need, so we get that by type casting it into a Control object (which inherits Animatable) by using CastFrom(). Hope this makes some sense :slight_smile:


import MaxPlus

for node in MaxPlus.SelectionManager.Nodes:
    for mod in node.Modifiers:
        if mod.ClassID == MaxPlus.ClassIds.SliceModifier:
            slicePlane = mod.GetSubAnim(1)
            transform_ctrl = MaxPlus.Control._CastFrom(slicePlane)

            pos = transform_ctrl.GetPositionController().GetPoint3Value()
            rot = transform_ctrl.GetRotationController().GetQuatValue()
            scale = transform_ctrl.GetScaleController().GetScaleValue()

            print pos, rot, scale

            transform_ctrl.GetPositionController().SetPoint3Value(
                MaxPlus.Point3(0, 0, 10))

Thanks for the explanation, makes sense now. Also makes sense why what I had been trying to do before wasn’t working.

Not sure if I should start a new thread but anyone know the MaxPlus equivalent of Maxscripts “nodeGetBoundingBox <node> <matrix3>” ??:):

EDIT:
I think i found it, print the max values of the bounding box
firstObj = MaxPlus.INode_GetINodeByName(“Box001”)
firstObjBaseObject = firstObj.GetBaseObject()
firstObjBox = firstObjBaseObject.GetLocalBoundBox(anotherObj, MaxPlus.ViewportManager_GetActiveViewport())
print firstObjBox.GetMax()

I have this in my YCDI MaxPlus Packages


def GetWorldBoundBox(node):
    """ Gets world boundingbox of node
        :param MaxPlus.INode node: an object node
        :rtype:  MaxPlus.Box3
    """

    # Viewport Manager
    vm = MaxPlus.ViewportManager
    # Get active viewport
    av = vm.GetActiveViewport()

    return node.GetBaseObject().GetWorldBoundBox(node, av)

def GetLocalBoundBox(node):
    """ Gets local boundingbox of node
        :param MaxPlus.INode node: an object node
        :rtype:  MaxPlus.Box3
    """

    # Viewport Manager
    vm = MaxPlus.ViewportManager
    # Get active viewport
    av = vm.GetActiveViewport()

    return node.GetBaseObject().GetLocalBoundBox(node, av)

Thanks Kameleon, I was just wondering why we need to get the viewport for these operations?

EDIT:
Also, how to go about getting a ModContext using MaxPlus? What do I need to cast from??
ModContext__CastFrom(xxx)

There you go:


derived_obj = node.GetDerivedObject()
mc = MaxPlus.ModContext._CastFrom(derived_obj.GetModContext(0)

Where “0” is the index of the modifier.

Thanks again for the help. I’ve just picked up MaxPlus last couple of days, and it makes me kinda miss Maxscript quite a bit, it just seemed so much better documented…

I got more questions though…

How to Rotate an object controllers Y by 90 (Euler), when the controller value type is a Quat??

Also anyway of getting the global ticks/frame value??

Thanks again for the help :slight_smile: