Home Coding, Scripting, Shaders

[3DS Max] Save Poses

polycounter lvl 6
Offline / Send Message
dg3duy polycounter lvl 6
Any kind of example to be able to save a couple of poses of a single object and have the possibility to load the poses, any maxscript code you can share on this topic?

Replies

  • poopipe
    Offline / Send Message
    poopipe high dynamic range
    Which bit are you struggling with ? 

    copying a transform is simply a case of assigning a node's transform to a variable (t = node.transform)
    applying a stored transform is simply a matter of assigning a variable to a node's transform (node.transform = t)

    if you work in a rollout, anything you store will persist as long as the rollout is open so you can happily store multiple transforms without fannying around with persistent stuff at scene level.

    the UI is the where it might get a bit tedious
  • dg3duy
    Offline / Send Message
    dg3duy polycounter lvl 6
    poopipe said:
    Which bit are you struggling with ? 

    copying a transform is simply a case of assigning a node's transform to a variable (t = node.transform)
    applying a stored transform is simply a matter of assigning a variable to a node's transform (node.transform = t)

    if you work in a rollout, anything you store will persist as long as the rollout is open so you can happily store multiple transforms without fannying around with persistent stuff at scene level.

    the UI is the where it might get a bit tedious

    I mean something like this type of script, the problem is that you have to add a parameter block and attribute holder to keep the information in the scene, which I don't know how to do it.

    https://forums.cgsociety.org/t/saving-character-poses-on-custom-rigs/880779/5


    ------copy pose

    
     MyLatestPose = #() --empty container 
     Obj = selection as array --current selection
     for i = 1 to Obj.count do
     (
     o = Obj[i]
     t = Obj[i].transform.controller.value --get the local value of the controller
     append MyLatestPose #(o,t)
     )
     
    

    ---- to paste

    
     if MyLatestPose != undefined then --<span>simple test to make sure a pose has been copyed
     (<span>
     for i = 1 to MyLatestPose.count do
     (
     objTranAr = MyLatestPose[i]
     o = objTranAr[1] -- get transform data
     t = objTranAr[2] -- get the object it belongs too
     o.transform.controller.value = t 
     </span>)
     )</span>
  • poopipe
    Offline / Send Message
    poopipe high dynamic range
    that's the fannying around I was talking about. It's definitely in the docs but it's been 10 years since I last did anything that needed it. 

    I imagine Monster will be along with an answer at some point.  might be worth changing the thread title to indicate you need help storing persistent data  
  • dg3duy
    Offline / Send Message
    dg3duy polycounter lvl 6
    poopipe said:
    that's the fannying around I was talking about. It's definitely in the docs but it's been 10 years since I last did anything that needed it. 

    I imagine Monster will be along with an answer at some point.  might be worth changing the thread title to indicate you need help storing persistent data  
    I know that monster is one of the few who are still active in answering users' questions. He also has an incredible knowledge of the whole program, I hope he can answer this question and help some "new" users.
  • monster
    Offline / Send Message
    monster Polycount Sponsor
    We're dealing with some major weather issues down in Texas! But I managed to whip up this example with some pseudo code in the rollout section.

    Things to note:
    • In General, as poopipe mentioned, you operate with the poses stored in the rollout. You only access a custom attribute when opening and closing (loading and saving) the rollout.
    • I don't use Attribute Holder because they can only be accessed when an object is selected.
    • Instead I save directly to the RootNode. Think of that as the object all unparented object are parented to. :D
    • For the CA value types, I use #stringTab, instead of #maxObjectTab because if an object is deleted it really freaks out that there is a reference to a deleted object in the Custom Attribute.
    • Also, #stringTab has maximum compatibility. #paramBlock2 was added in 2018 and I still have people using Max 2009 and such.
    • I guess you can use #matrix3Tab for the Transform Array, but i already typed it all out.
    global PoseTools
    
    struct PoseToolStruct
    (
        poseCA = attributes poseLibrary
        (
            parameters pose_params
            (
                'poseNameArray' type:#stringtab  tabSize:0 tabSizeVariable:true
                'objectArray' type:#stringtab  tabSize:0 tabSizeVariable:true
                'transformArray' type:#stringtab  tabSize:0 tabSizeVariable:true
            )
        ),
    
        PoseToolRollout = rollout PoseToolRollout "Pose Tool" width:#cmdPanel
        (
            --UI CODE
    
            --STRUCT TO STORE A POSE IN THE ROLLOUT
            struct Pose
            (
                poseName = "",
                objectArray = #(),
                transformArray = #()
            )
    
            --ARRAY OF CURRENT POSES FOR THE ROLLOUT
            local PoseArray = #()
    
            --FUNCTION TO ADD A CA TO THE MAX FILE
            fn addPoseCAToRootNode =
            (
                if not isProperty rootnode #poseLibrary do
                (
                    format "Adding poseLibrary to the rootnode.\n"
                    custAttributes.add rootnode PoseToolStruct.poseCA
                )
            )
    
            --LOAD THE POSES STORED IN THE MAX FILE
            fn LoadPosesFromCustomAttribute =
            (
                --ENSURE A CA HAS BEEN ADDED TO THE FILE
                addPoseCAToRootNode()
    
                --CLEAR THE POSE ARRAY AND GET THE NUMBER OF STORED POSES
                PoseArray = #()
                pCount = rootnode.poseNameArray.count
    
                --FOR EACH STORED POSE 
                for i = 1 to pCount do
                (
                    NewPose = Pose poseName:(rootnode.poseNameArray[i]) objectArray:(execute(rootnode.objectArray[i])) transformArray:(execute(rootnode.transformArray[i]))
                    append PoseArray NewPose
                )
            )
    
            --FILL THE CA WITH THE CURRENT POSE ARRAY
            fn SavePosesToCustomAttribute =
            (
                --ENSURE A CA HAS BEEN ADDED TO THE FILE
                addPoseCAToRootNode()
    
                --SET THE VALUES FOR THE CA
                with printAllElements on
                (
                    rootnode.poseNameArray = for p in PoseArray Collect p.poseName
                    rootnode.objArray = for p in PoseArray Collect (p.objectArray as string)
                    rootnode.transArray = for p in PoseArray Collect (p.transformArray as string)
                )
            )
    
            --HANDLE ROLLOUT OPENING
            on PoseToolRollout open do
            (
                LoadPosesFromCustomAttribute()
            )
    
            --HANDLE ROLLOUT CLOSING
            on PoseToolRollout close do
            (
                SavePosesToCustomAttribute()
            )
    
            --COPY FUNCTION ADDS A NEW POSE (STRUCT) TO THE POSE ARRAY
            --PASTE FUNCTION APPLIES A POSE FROM THE POSE ARRAY TO OBJECTS
        )
    
    )
    
    PoseTools = PoseToolStruct()

  • dg3duy
    Offline / Send Message
    dg3duy polycounter lvl 6
    Wowww! This is a great example of what I was looking for! thanks so much for sharing!
Sign In or Register to comment.