Change the llRotBetween algorithm.

The former ones don't match LSL's behaviour, in particular llRotBetween(<1,0,0>,<-1,0,0>) should return <0,0,1,0> but it didn't. Fix by using an algorithm that is closer to the one used by the sim.
This commit is contained in:
Sei Lisa 2017-01-27 02:31:00 +01:00
parent 48e1035ac3
commit 37a4c86516

View file

@ -1650,50 +1650,37 @@ def llRotBetween(v1, v2):
assert isvector(v1) assert isvector(v1)
assert isvector(v2) assert isvector(v2)
aabb = math.sqrt(mul(v1, v1, f32=False) * mul(v2, v2, f32=False)) # product of the lengths of the arguments # Loosely based on the "Bad" reference implementation and
if aabb == 0.: # on SL source code (pre Moon Metty's changes).
return ZERO_ROTATION # the arguments are too small, return zero rotation # See <https://bitbucket.org/lindenlab/viewer-release/src/015080d8/indra/llmath/llquaternion.cpp#llquaternion.cpp-425>
ab = mul(v1, v2, f32=False) / aabb # normalized dotproduct of the arguments (cosine) v1 = llVecNorm(v1)
c = Vector(((v1[1] * v2[2] - v1[2] * v2[1]) / aabb, # normalized crossproduct of the arguments v2 = llVecNorm(v2)
(v1[2] * v2[0] - v1[0] * v2[2]) / aabb, dot = mul(v1, v2)
(v1[0] * v2[1] - v1[1] * v2[0]) / aabb)) axis = mod(v1, v2)
cc = mul(c, c, f32=False) # squared length of the normalized crossproduct (sine) threshold = float.fromhex('0x1.fffffcp-1')
if cc != 0.: # test if the arguments are (anti)parallel if -threshold <= dot <= threshold:
if ab > -0.7071067811865476: # test if the angle is smaller than 3/4 PI # non-aligned - their cross product is a good axis
s = 1. + ab # use the cosine to adjust the s-element m = math.sqrt(mul(axis, axis) + (dot + 1.) * (dot + 1.))
else: return Quaternion(F32((axis[0] / m, axis[1] / m, axis[2] / m,
s = cc / (1. + math.sqrt(1. - cc)); # use the sine to adjust the s-element (dot + 1.) / m)))
m = math.sqrt(cc + s * s) # the magnitude of the quaternion # about aligned - two cases to deal with
return Quaternion(F32((c[0] / m, c[1] / m, c[2] / m, s / m))) # return the normalized quaternion if dot > 0.:
if ab > 0.: # test if the angle is smaller than PI/2 # same signs
return ZERO_ROTATION # the arguments are parallel return Quaternion((0., 0., 0., 1.))
m = math.sqrt(v1[0] * v1[0] + v1[1] * v1[1]) # the length of one argument projected on the XY-plane # opposite signs - find one vector in the plane perpendicular to
if m != 0.: # either vector, to use as axis. We do this by choosing an arbitrary
return Quaternion(F32((v1[1] / m, -v1[0] / m, 0., 0.))) # return rotation with the axis in the XY-plane # vector (<1,0,0> in our case), and calculating the cross product with it,
return Quaternion((0., 0., 1., 0.)) # rotate around the Z-axis # which will be perpendicular to both. But matching the SL results requires
# another cross product of the input with the result, so we do that.
# Algorithm by Moon Metty (for reference) ortho = mod(mod(v1, Vector((1., 0., 0.))), v1)
# dot = mul(v1, v2, f32=False) ortho = Vector((0. if f == 0. else f for f in ortho)) # remove minus zero
# cross = mod(v1, v2, f32=False) m = mul(ortho, ortho)
# csq = mul(cross, cross, f32=False) if m < float.fromhex('0x1.b7cdfep-34'):
# # The input vectors were aligned with <1,0,0>, so this was not a
# ddc2 = dot*dot + csq # good choice. Return 180 deg. rotation over Z instead.
# return Quaternion((0., 0., 1., 0.))
# DenormalStart = float.fromhex('0x1p-149') m = math.sqrt(m)
# if ddc2 >= DenormalStart: return Quaternion(F32((ortho[0] / m, ortho[1] / m, ortho[2] / m, 0.)))
# if csq >= DenormalStart:
# s = math.sqrt(ddc2) + dot;
# m = math.sqrt(csq + s*s);
# return Quaternion(F32((cross[0]/m, cross[1]/m, cross[2]/m, s/m)))
#
# # Deal with degenerate cases here
# if dot > 0:
# return ZERO_ROTATION
# m = math.sqrt(v1[0]*v1[0] + v1[1]*v1[1])
# if m >= DenormalStart:
# return Quaternion(F32((v1[1]/m, -v1[0]/m, 0., 0.)))
# return Quaternion((1., 0., 0., 0.))
# return ZERO_ROTATION
def llRound(f): def llRound(f):
assert isfloat(f) assert isfloat(f)