Rotate object around world axis regardless of orientation
-
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)
This code almost works, but some of the time the cube rotates the wrong direction (although on the right axis).
-
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:
- 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 ^^
- Using
BaseObject.GetMl()
can make things harder to compute, depending on your setup. I would stay in world space. - 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,
FerdinandThe result:
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
-
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
-
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