Hi,
I have seen and as expected this was discussed already in the past, but I could not find any working example in this forum.
I'd simply like to have a (gimbal lock safe) python replacement for the Target (Expression) Tag.
I'm attaching what I have come up with until now...
two versions #1 without , #2 with quaternion --> sadly #1 has gimbal issues and #2 fails completely
both with a pitch lock like in the Target Tag --> sure this could be done more elegant .-)
It was stated in one of the (below) mentioned threads, that support can't solve such problems for the users,
but this would be such a valuable tool in the quiver that it might be an exception (?)
best, index
def lookat(op, target, pitchlock=0):
''' gimbal lock UNSAFE target expression
this basically works, but results in axis flips sometimes '''
r1 = op.GetRelRot() # backup
p1 = op.GetMg().off # from
p2 = target.GetMg().off # to
tmp = c4d.Vector(0, 1, 0) # temp-up
fo = (p2 - p1).GetNormalized() # forward
ri = tmp.Cross(fo).GetNormalized() # right
up = fo.Cross(ri).GetNormalized() # up
# https://developers.maxon.net/forum/topic/11152/object-target-expression-in-python/2
# try to mitigate gimbal-lock --> UNSAFE, RESULTS IN AXIS FLIPS !
if (fo + tmp).z < 0: up.x = -up.x
if abs(ri.x - 1.0) < 0.001: ri = -ri
m = c4d.Matrix()
m.v1 = ri
m.v2 = up
m.v3 = fo
m.off = p1
op.SetMg(m)
if pitchlock:
r = op.GetRelRot()
op.SetRelRot(c4d.Vector(r.x, r1.y, r.z))
def lookatQ(op, target, pitchlock=0):
''' quaternion based (gimbal lock safe ???) target expression
sadly doesn't work and results in a wrong rotation '''
r1 = op.GetRelRot() # backup
p1 = op.GetMg().off # from
p2 = target.GetMg().off # to
tmp = c4d.Vector(0, 1, 0) # temp-up
fo = (p2 - p1).GetNormalized() # forward
ri = tmp.Cross(fo).GetNormalized() # right
up = fo.Cross(ri).GetNormalized() # up
# copied from
# https://www.gamedev.net/forums/topic/613595-quaternion-lookrotationlookat-up/
w = math.sqrt(1.0 + ri.x + up.y + fo.z) * 0.5
w4_recip = 1.0 / (4.0 * w)
x = (up.z - fo.y) * w4_recip
y = (fo.x - ri.z) * w4_recip
z = (ri.y - up.x) * w4_recip
# given the above is correct, is this correctly applied here ???
q = c4d.Quaternion()
v = c4d.Vector(x, y, z)
q.SetAxis(v, w)
m = q.GetMatrix()
m.off = p1
op.SetMg(m)
if pitchlock:
r = op.GetRelRot()
op.SetRelRot(c4d.Vector(r.x, r1.y, r.z))