SPLINE: interpolate angle

Blitz3D Forums/Blitz3D Programming/SPLINE: interpolate angle

Mathieu A(Posted 2003) [#1]
Hi!

I made my own editor for my game, which contain a cinematic editor. All the camera movement are made with spline. It works like 3DSMax, I mean you create your keyframe.

But I've got a problem, with the interpolation of the angle.

An exemple:

The camera angle is beetween 180 and (-180). Or when I create a keyframe 1 with an angle of 170 and a keyframe 2 with an angle of -150. The program will interpolate the angle like that : 170-160-150-140-...-0-(-10)-(-20)-...-(-150).

But in fact I'd like that it interpolates like that 170-180-(-170)-(-160)-(-150)

Do you have a trick to solve my problem?
Quaternion can resolve that?

Thank you


sswift(Posted 2003) [#2]
So, you want the camera to take the shortest route to get to the destination orientation?

That is exactly what quaternions were designed to do.

In fact, they take a more direct route to the destination orientation than Euler rotations will. So the rotations look more natural.

If you ignore roll, and imagine the center of the camera as the center of the sphere, and the point which the camera is aiming at as a point on the sphere, a Quaternion rotation will take the shortest arc across the surface of the sphere to the destination point. A Euler rotation on the other hand may not follow that shortest route.


Anyhow here's the functions I use to do quaternion interpolations. Should be really easy to figure out.

; -------------------------------------------------------------------------------------------------------------------
; QuatLib - Quaternion rotation library
; -------------------------------------------------------------------------------------------------------------------

Const QuatToEuler_Epsilon# = 0.001
Const QuatSlerp_Epsilon#   = 0.0001

; These variables are where the conversion and interpolation functions return information.
Global QuatLib_X#
Global QuatLib_Y#
Global QuatLib_Z#
Global QuatLib_W#

Global QuatLib_Pitch#
Global QuatLib_Yaw#
Global QuatLib_Roll#


; -------------------------------------------------------------------------------------------------------------------
; This function takes a Euler rotation and returns a quaternion which represents it.
; 
; Parameters:
;
;	Pitch#, Yaw#, Roll#:
;		Euler rotation representing the orientation of an object.
; -------------------------------------------------------------------------------------------------------------------
Function EulerToQuat(Pitch#, Yaw#, Roll#)
	
	Pitch# = Pitch# / 2.0
	Yaw#   = Yaw#   / 2.0
	Roll#  = Roll#  / 2.0
	
	Cos_Pitch# = Cos(Pitch#)
	Cos_Yaw#   = Cos(Yaw#)
	Cos_Roll#  = Cos(-Roll#)
	
	Sin_Pitch# = Sin(Pitch#)
	Sin_Yaw#   = Sin(Yaw#)
	Sin_Roll#  = Sin(-Roll#)
	
	CpCy# = Cos_Pitch# * Cos_Yaw#
	SpSy# = Sin_Pitch# * Sin_Yaw#
	SpCy# = Sin_Pitch# * Cos_Yaw#
	CpSy# = Cos_Pitch# * Sin_Yaw#

	QuatLib_X# = (Sin_Roll# * CpCy#) - (Cos_Roll# * SpSy#)
	QuatLib_Y# = (Cos_Roll# * SpCy#) + (Sin_Roll# * CpSy#)
	QuatLib_Z# = (Cos_Roll# * CpSy#) - (Sin_Roll# * SpCy#)
	QuatLib_W# = (Cos_Roll# * CpCy#) + (Sin_Roll# * SpSy#)

End Function


; -------------------------------------------------------------------------------------------------------------------
; This function takes a starting and ending orientation, in the form of two quaternions, and returns a quaternion
; which represents an interpolated orientation which lies somewhere between the two.
;
; In english, this function allows you to rotate an object from one orientation to another.  The quaternion math
; ensures that the rotation takes the shortest route from A to B. 
;
; Imagine that the object is inside a sphere.  Points A and B points on the sphere which the object points at when
; in it's starting and ending orientations.
;
;
; Parameters:
;
;	Q1x#, Q1y#, Q1z#, Q1w#:
;		Quaternion representing the orientation to interpolate from.
;
;	Q2x#, Q2y#, Q2z#, Q2w#:
;		Quaternion representing the orientation to interpolate to.
;
;	I#:
;		A value between 0 and 1 which defines the point between the two interpolations you want to calculate.
; -------------------------------------------------------------------------------------------------------------------
Function QuatSlerp(Q1x#, Q1y#, Q1z#, Q1w#, Q2x#, Q2y#, Q2z#, Q2w#, Interpolator#)

	; This is the dot product of two 4d vectors.  I think.
	;
	; Assuming these vectors are normalized, this value should be in the range -1..0..1, and should represent the
	; angle between the vectors... Ie, how much they point away from one another.
	;
	; In 2d and 3d, two vectors can only point away from one another by up to 180 degrees.  this occurs when they
	; are pointing in opposite directions.  Therefore I must surmise that for vectors in 4d, this is true as well.
	; Isn't that interesting?
	Cos_O# = (Q1x#*Q2x#) + (Q1y#*Q2y#) + (Q1z#*Q2z#) + (Q1w#*Q2w#)

	If Cos_O# <= 0
		Cos_O# = -Cos_O#
		Q2x#   = -Q2x#
		Q2y#   = -Q2y#
		Q2z#   = -Q2z#
		Q2w#   = -Q2w#
	EndIf

	; Is the angle between the two rotations small enough that we can use linear interpolation?
	If (1.0-Cos_O#) > QuatSlerp_Epsilon#

		; No.
		O# = ACos(Cos_O#)
		Sin_O# = Sin(O#)
		I1# = Sin((1.0-Interpolator#) * O#) / Sin_O#
		I2# = Sin(Interpolator#       * O#) / Sin_O#

	Else

		; Yes.  Use linear interpolation.
		I1# = 1.0 - Interpolator#
		I2# = Interpolator#
		
	EndIf

	QuatLib_X# = (I1# * Q1x#) + (I2# * Q2x#)
	QuatLib_Y# = (I1# * Q1y#) + (I2# * Q2y#)
	QuatLib_Z# = (I1# * Q1z#) + (I2# * Q2z#)
	QuatLib_W# = (I1# * Q1w#) + (I2# * Q2w#)

End Function


; -------------------------------------------------------------------------------------------------------------------
; This function converts a quaternion to a set of Euler angles.
;
;
; Parameters:
;
;	Qx#, Qy#, Qz#, Qw#:
;		Quaternion to convert to Euler angles.
;
; -------------------------------------------------------------------------------------------------------------------
Function QuatToEuler(Qx#, Qy#, Qz#, Qw#)

	Qx2# = Qx# * 2.0
	Qy2# = Qy# * 2.0
	Qz2# = Qz# * 2.0
	
	Sin_T# = (Qy2# * Qw#) - (Qx2# * Qz#)
	Cos_T# = 1.0 - (Sin_T# * Sin_T#)


	If Abs(Cos_T#) > QuatToEuler_Epsilon#
		Cos_T# = Sqr(Cos_T#)	
	Else
		Cos_T# = 0
	EndIf
		
		
	If Abs(Cos_T#) > QuatToEuler_Epsilon#

		Sin_V# = (      (Qy2# * Qz#) + (Qx2# * Qw#)) / Cos_T#
		Cos_V# = (1.0 - (Qx2# * Qx#) - (Qy2# * Qy#)) / Cos_T#
		Sin_F# = (      (Qx2# * Qy#) + (Qz2# * Qw#)) / Cos_T#
		Cos_F# = (1.0 - (Qy2# * Qy#) - (Qz2# * Qz#)) / Cos_T#

	Else

		Sin_V# =       (Qx2# * Qw#) - (Qy2# * Qz#)
		Cos_V# = 1.0 - (Qx2# * Qx#) - (Qz2# * Qz#)
		Sin_F# = 0
		Cos_F# = 1.0

	EndIf

	QuatLib_Pitch# =  ATan2(Sin_T#, Cos_T#)
	QuatLib_Yaw#   =  ATan2(Sin_F#, Cos_F#)
	QuatLib_Roll#  = -ATan2(Sin_V#, Cos_V#)

End Function


; -------------------------------------------------------------------------------------------------------------------
; This function returns a quaternion which represents the orientation of an entity.
;
; Parameters:
;
;	Entity:
;		The entity to generate a quaternion for.
; -------------------------------------------------------------------------------------------------------------------
Function EntityQuat(ThisEntity)

	Pitch#	= EntityPitch#(ThisEntity, True)
	Yaw# 	= EntityYaw#(ThisEntity, True)
	Roll# 	= EntityRoll#(ThisEntity, True)
	
	EulerToQuat(Pitch#, Yaw#, Roll#)

End Function