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 ^^
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 ;)
The file: rot_cube.c4d
# 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)
# Get out when the tag is not attached to a BaseObject
node = op.GetObject()
if node is None:
# Get out when the position cache, PREVIOUS_POSITION, has not been yet
if PREVIOUS_POSITION is None:
PREVIOUS_POSITION = node.GetMg().off
# 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:
# 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,
# 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
# 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