Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Register
    • Login

    Rotate object around world axis regardless of orientation

    General Talk
    python
    2
    4
    889
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • codysorgenfreyC
      codysorgenfrey
      last edited by

      Hello!

      I'm having a hard time wrapping my brain around this script I'm trying to write.

      I want to rotate a cube along the floor in the direction that it's being animated. The problem I'm running into is that I need to be able to specify the rotation around the world axis (just like how the rotate tool does) so that when the cube is oriented in any direction, I can still rotate properly with the animation.

      Here's some pseudo code and a screen grab. Any help would be greatly appreciated!

      # Rolling
      distTraveled = pos - PREV_POS
      numRevs.x = (distTraveled.x / circumfrence.x)
      numRevs.z = (distTraveled.z / circumfrence.z)
      outRot = numRevs * c4d.utils.DegToRad(360.0)
      objMx = rotObj.GetMl()
      rotMx = c4d.utils.MatrixRotY(-outRot.z) * c4d.utils.MatrixRotZ(outRot.x)
      rotObj.SetMl(objMx * rotMx)
      

      https://microsoft-my.sharepoint-df.com/✌/p/cosorgen/ESTEQCvARE1Nn4F3qnKfCC8B1nahC5Y3iHhSvgOtkhO0DQ?e=xhrNCK

      This code almost works, but some of the time the cube rotates the wrong direction (although on the right axis).

      ferdinandF 1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand @codysorgenfrey
        last edited by ferdinand

        Hello @codysorgenfrey,

        thank you for reaching out to us. Your question is a bit hard to answer, given the abstract nature of the provided code and the absence of a specific setup/file. I would however point out that:

        1. This number of revisions expressed as a vector, i.e., separated onto axis, strikes me as a bit odd, since this is not how rotation works ^^
        2. Using BaseObject.GetMl() can make things harder to compute, depending on your setup. I would stay in world space.
        3. Most importantly I would point that you do not seem to take order of rotation into account, i.e., that matrix multiplication is not commutative, i.e., M * N != N * M which might get in the way in your second to last line, as you do not make this dependent on the travel direction.

        I personally would also take a more "transform" oriented approach. As it much easier to just rotate a vector than figuring out into which direction you are now traveling and what that means for constructing a transform in "the proper way". I have provided below a quick solution as Python scripting tag, which is more meant to line out the approach rather than being a full solution (which we cannot provide). My solution requires an "aligned-with-world-frame" orientation for the start of travel. It would not be too hard to fix this, you must account for that additional delta before applying the final transform. I am just running a bit of time here, especially since this question is a bit out of scope of support (as it is just a math question).

        If you need further help, also with the "alignment" condition, just ask 😉

        Cheers,
        Ferdinand

        The result:
        abc.gif

        The file: rot_cube.c4d

        The code:

        import c4d
        import math
        
        # Global non-static variables are a really bad idea. Please replace this
        # in a production environment with a more safe approach by for example
        # storing this as a user-data parameter or just as an element in a
        # BaseContainer of a node.
        PREVIOUS_POSITION = None
        
        NULL_VECTOR = c4d.Vector()
        UP_VECTOR = c4d.Vector(0, 1, 0)
        
        def main():
            """
            """
            # Get out when the tag is not attached to a BaseObject
            node = op.GetObject()
            if node is None:
                return
        
            # Get out when the position cache, PREVIOUS_POSITION, has not been yet
            # initialized.
            global PREVIOUS_POSITION
            if PREVIOUS_POSITION is None:
                PREVIOUS_POSITION = node.GetMg().off
                return
        
            # Compute the position delta and get out when it is the null/zero vector,
            # i.e., nothing happened.
            nodeMg = node.GetMg()
            currentPosition = nodeMg.off
            positionDelta = currentPosition - PREVIOUS_POSITION
            if positionDelta == NULL_VECTOR:
                return
        
            # Now we are going to compute the angle of rotation similarly to what you
            # did as a ratio of arc length (of travel) to circumference.
        
            # Compute the circumference of the traveling object as the maximum radius
            # of its bounding box. This also could be done for a non spherical case,
            # but would get quite a bit more complicated.
            boundingBoxRadii = node.GetRad()
            travelObjectCircumference = 2 * math.pi * max(boundingBoxRadii.x,
                                                          boundingBoxRadii.y,
                                                          boundingBoxRadii.z)
        
            # The traveled distance since the last update, i.e., the arc-length of the
            # angle of rotation.
            travelDistance = positionDelta.GetLength()
        
            # The angle of rotation, based on the arc-length to angle of rotation
            # relation:
            #
            #   arc_length        theta
            # --------------- = ---------
            #  circumference       2π
            #
            theta = travelDistance / travelObjectCircumference * 2 * math.pi
        
            # Now we are going to construct a transform for that angle. First we are
            # going to construct our axis rotation which is orthogonal to the travel
            # vector and an arbitrary up-vector, i.e., we simply assume one degree or
            # otherwise would have to also track the banking of the traveling object.
        
            axisRot = ~positionDelta % UP_VECTOR
        
            # Now we are just computing our final desired transform, we could do this,
            # i.e., constructing a transform out of an axis and an angle, manually but
            # since the Python SDK does offer RotAxisToMatrix, we are going to be lazy
            # and just use that :)
            transform = c4d.utils.RotAxisToMatrix(axisRot, theta)
        
            # Now we are just going to write this into the object's transform in world
            # space, i.e., what Cinema does call its global matrix.
            mg = node.SetMg(nodeMg * transform)
        
            # And we have to update our position cache at the end.
            PREVIOUS_POSITION = currentPosition
        

        MAXON SDK Specialist
        developers.maxon.net

        codysorgenfreyC 1 Reply Last reply Reply Quote 0
        • codysorgenfreyC
          codysorgenfrey @ferdinand
          last edited by

          Thanks so much @ferdinand! You all at Maxon are the best. Sorry my code is a shit show, just a quick hack at a rolling cube rig.

          For anyone in the future looking for this solution here's what I needed:

              # Rolling
              distTraveled = pos - PREV_POS
              numRevs.x = (distTraveled.x / circumfrence.x)
              numRevs.z = (distTraveled.z / circumfrence.z)
              outRot = numRevs * c4d.utils.DegToRad(360.0)
              objMx = rotObj.GetMl()
              worldXInLocal = ~objMx * c4d.Vector(1, 0, 0)
              worldZInLocal = ~objMx * c4d.Vector(0, 0, 1)
              xTrans = c4d.utils.RotAxisToMatrix(worldXInLocal, -outRot.z)
              zTrans = c4d.utils.RotAxisToMatrix(worldZInLocal, outRot.x)
              rotObj.SetMl(objMx * xTrans * zTrans)
          

          I needed the cube to be able to roll the correct way regardless of orientation. Admittedly this was a simple matrix transform issue that anyone who actually understood graphics programming would've known. (LOL not me)

          Here's the full scene for anyone interested. test_cube_roll.c4d

          1 Reply Last reply Reply Quote 0
          • ferdinandF
            ferdinand
            last edited by ferdinand

            Hello @codysorgenfrey,

            without any further questions, we will consider this topic as solved by Monday and flag it accordingly.

            Thank you for your understanding,
            Ferdinand

            MAXON SDK Specialist
            developers.maxon.net

            1 Reply Last reply Reply Quote 0
            • First post
              Last post