;=====================================================
; VARIOUS PROCEDURES
;=====================================================


MACRO ATXPROC proclabel {
	align 16
	proclabel:
      }

; data

align 4
float_0 				dd	0.000000
float_minusone				dd	-1.000000
float_0_5				dd	0.500000
float_0_5_B				dd	0.500000
pidivtwo				dd	1.5707963267948966192313216916398
pi					dd	3.1415926535897932384626433832795
pi2					dd	6.28318530717958647692528676655901
minuspi2				dd	-6.28318530717958647692528676655901
pidiv180				dd	0.0174532925199432957692369076848861
d180divpi				dd	57.2957795130823208767981548141052
float_sqrt_0_5				dd	0.70710678118654752440084436210485

align 8
float_two				dd	2.000000
					dd	2.000000
float_one				dd	1.000000
					dd	1.000000

quatnormrange				dd	 0.001		; if abs(1-quat magnitude) below or equal to this value, no normalization
					dd	 0.001		; -> value 0.001 is taken from game, used in its own quat multiplication function

floatabsvaluemask			dq	 07FFFFFFF7FFFFFFFh

;============================================================
ATXPROC QuaternionMul_STD	; eax=q1 , ecx=q2, edx=new quat
;============================================================
; Quaternion multiplication (eax=q1, ecx=q2, edx=result)

virtual at eax
	w1	dd	?
	x1	dd	?
	y1	dd	?
	z1	dd	?
end virtual

virtual at ecx
	w2	dd	?
	x2	dd	?
	y2	dd	?
	z2	dd	?
end virtual

; (Q1 * Q2).w = (w1w2 - x1x2 - y1y2 - z1z2)
; (Q1 * Q2).x = (w1x2 + x1w2 + y1z2 - z1y2)
; (Q1 * Q2).y = (w1y2 - x1z2 + y1w2 + z1x2)
; (Q1 * Q2).z = (w1z2 + x1y2 - y1x2 + z1w2)

;------------- w

	fld [w1]
	fmul [w2]

	fld [x1]
	fmul [x2]
	fsubp st1,st0

	fld [y1]
	fmul [y2]
	fsubp st1,st0

	fld [z1]
	fmul [z2]
	fsubp st1,st0
	fstp DWORD [edx]

;------------- x

	fld [w1]
	fmul [x2]

	fld [x1]
	fmul [w2]
	faddp st1,st0

	fld [y1]
	fmul [z2]
	faddp st1,st0

	fld [z1]
	fmul [y2]
	fsubp st1,st0
	fstp DWORD [edx+4]

;------------- y

	fld [w1]
	fmul [y2]

	fld [x1]
	fmul [z2]
	fsubp st1,st0

	fld [y1]
	fmul [w2]
	faddp st1,st0

	fld [z1]
	fmul [x2]
	faddp st1,st0
	fstp DWORD [edx+8]

;------------- z

	fld [w1]
	fmul [z2]

	fld [x1]
	fmul [y2]
	faddp st1,st0

	fld [y1]
	fmul [x2]
	fsubp st1,st0

	fld [z1]
	fmul [w2]
	faddp st1,st0
	fstp DWORD [edx+12]

ret

; Note: I don't know if you need to normalize locally-generated quaternions. I believe
; not, but I'm leaving it here nevertheless because I'm no expert and I don't want
; to risk messing up any existing features.

;============================================================
ATXPROC ToLocalQuat_AxisX_STD			; ecx=pointer to radian angle to rotate by, edx=local_rotation pointer wxyz
;============================================================
; axis.x = 1.00000 , axis.y=0.0000, axis.z=0.00000

	fld DWORD [ecx]
	fmul [float_0_5]
	fld st0
	fsin
	fst DWORD [edx+4]			; x
	fmul st0,st0

	fxch
	fcos
	fst DWORD [edx] 			; w
	fmul st0,st0
	faddp st1,st0

	fsqrt					; magnitude
	xor eax,eax
	fdivr [float_one]			; 1/magnitude
	fld st0

	fmul DWORD [edx]			; normalize w
	      mov [edx+8],eax			; y
	fstp DWORD [edx]
	fmul DWORD [edx+4]			; normalize x
	      mov [edx+12],eax			; z
	fstp DWORD [edx+4]

ret

;============================================================
ATXPROC ToLocalQuat_AxisY_STD			; ecx=pointer to radian angle to rotate by, edx=local_rotation pointer wxyz
;============================================================
; axis.x = 0.00000 , axis.y=1.0000, axis.z=0.00000

	fld DWORD [ecx]
	fmul [float_0_5]
	fld st0
	fsin
	fst DWORD [edx+8]			; y
	fmul st0,st0

	fxch
	fcos
	fst DWORD [edx] 			;w
	fmul st0,st0
	faddp st1,st0

	fsqrt					; magnitude
	xor eax,eax
	fdivr [float_one]			; 1/magnitude
	fld st0

	fmul DWORD [edx]			; normalize w
	      mov [edx+4],eax			; x
	fstp DWORD [edx]
	fmul DWORD [edx+8]			; normalize y
	      mov [edx+12],eax			; z
	fstp DWORD [edx+8]

ret

;============================================================
ATXPROC ToLocalQuat_AxisZ_STD			; ecx=pointer to radian angle to rotate by, edx=local_rotation pointer wxyz
;============================================================
; axis.x = 0.00000 , axis.y=0.0000, axis.z=1.00000

	fld DWORD [ecx]
	fmul [float_0_5]
	fld st0
	fsin
	fst DWORD [edx+12]			; z
	fmul st0,st0

	fxch
	fcos
	fst DWORD [edx] 			; w
	fmul st0,st0
	faddp st1,st0

	fsqrt					; magnitude
	xor eax,eax
	fdivr [float_one]			; 1/magnitude
	fld st0

	fmul DWORD [edx]			; normalize w
	      mov [edx+8],eax			; y
	fstp DWORD [edx]
	fmul DWORD [edx+12]			; normalize z
	      mov [edx+4],eax			; x
	fstp DWORD [edx+12]

ret

;============================================================
ATXPROC ToLocalQuat_AxisX_NoNorm_STD		; ecx=pointer to radian angle to rotate by, edx=local_rotation pointer wxyz
;============================================================
; axis.x = 1.00000 , axis.y=0.0000, axis.z=0.00000

	fld DWORD [ecx]
	fmul [float_0_5]
	xor eax,eax
	fsincos
	mov DWORD [edx+8],eax
	mov DWORD [edx+12],eax
	fstp DWORD [edx]
	fstp DWORD [edx+4]

ret

;============================================================
ATXPROC ToLocalQuat_AxisMinusX_NoNorm_STD	; ecx=pointer to radian angle to rotate by, edx=local_rotation pointer wxyz
;============================================================
; axis.x = -1.00000 , axis.y=0.0000, axis.z=0.00000

	fld DWORD [ecx]
	fmul [float_0_5]
	xor eax,eax
	fsincos
	mov DWORD [edx+8],eax
	mov DWORD [edx+12],eax
	fstp DWORD [edx]
	fchs
	fstp DWORD [edx+4]

ret

;============================================================
ATXPROC ToLocalQuat_AxisY_NoNorm_STD		; ecx=pointer to radian angle to rotate by, edx=local_rotation pointer wxyz
;============================================================
; axis.x = 0.00000 , axis.y=1.0000, axis.z=0.00000

	fld DWORD [ecx]
	fmul [float_0_5]
	xor eax,eax
	fsincos
	mov DWORD [edx+4],eax
	mov DWORD [edx+12],eax
	fstp DWORD [edx]
	fstp DWORD [edx+8]

ret

;============================================================
ATXPROC ToLocalQuat_AxisMinusY_NoNorm_STD
;============================================================
; axis.x = 0.00000 , axis.y=-1.0000, axis.z=0.00000

	fld DWORD [ecx]
	fmul [float_0_5]
	xor eax,eax
	fsincos
	mov DWORD [edx+4],eax
	mov DWORD [edx+12],eax
	fstp DWORD [edx]
	fchs
	fstp DWORD [edx+8]

ret

;============================================================
ATXPROC ToLocalQuat_AxisZ_NoNorm_STD		; ecx=pointer to radian angle to rotate by, edx=local_rotation pointer wxyz
;============================================================
; axis.x = 0.00000 , axis.y=0.0000, axis.z=1.00000

	fld DWORD [ecx]
	fmul [float_0_5]
	xor eax,eax
	fsincos
	mov DWORD [edx+4],eax
	mov DWORD [edx+8],eax
	fstp DWORD [edx]
	fstp DWORD [edx+12]

ret

;============================================================
ATXPROC ToLocalQuat_AxisMinusZ_NoNorm_STD	; ecx=pointer to radian angle to rotate by, edx=local_rotation pointer wxyz
;============================================================
; axis.x = 0.00000 , axis.y=0.0000, axis.z=-1.00000

	fld DWORD [ecx]
	fmul [float_0_5]
	xor eax,eax
	fsincos
	mov DWORD [edx+4],eax
	mov DWORD [edx+8],eax
	fstp DWORD [edx]
	fchs
	fstp DWORD [edx+12]

ret

;============================================================
ATXPROC QuatToAxisAngle_STD			; eax = w,x,y,z quat ;ecx = angle+axis (angle in radians, axisx, axisy, axisz)
;============================================================

sub esp,16

	mov edx,[eax]
	mov [esp],edx
	mov edx,[eax+4]
	mov [esp+4],edx
	mov edx,[eax+8]
	mov [esp+8],edx
	mov edx,[eax+12]
	mov [esp+12],edx
	mov eax,esp
	call NormalizeQuat

	fld DWORD [eax] 			; cos
	fmul st0,st0
	fsubr [float_one]
	fsqrt					; sin = sqrt(1.0-cos^2)
	fld st0
	fld DWORD [eax]
	fpatan					; acos = tan_-1 (sin/cos)
	fmul [float_two]			; angle = acos*2
	fstp DWORD [ecx]

	fld st0
	fabs
	fcomp [float_0_0005]			; sin now in st0
	push eax
	fstsw ax
	test eax,100h				; sin < 0.0005 ?
	     jz @F

	fstp st0
	fld1					; if so, sin = 1.000000

	@@:
	pop eax

	fdivr [float_one]			; 1/sin

	fld DWORD [eax+4]			; x
	fmul st0,st1				; x/sin
	fstp DWORD [ecx+4]			; axisx

	fld DWORD [eax+8]			; y
	fmul st0,st1				; y/sin
	fstp DWORD [ecx+8]			; axisy

	fld DWORD [eax+12]			; z
	fmul st0,st1				; z/sin
	fstp DWORD [ecx+12]			; axisz

	fstp st0

add esp,16
ret

;============================================================
ATXPROC NormalizeQuat_STD			; eax = quat (w,x,y,z)
;============================================================
	fld DWORD [eax]
	fmul st0,st0
	fld DWORD [eax+4]
	fmul st0,st0
	fld DWORD [eax+8]
	fmul st0,st0
	fld DWORD [eax+12]
	fmul st0,st0
	faddp
	faddp
	faddp
	fsqrt

	fdivr [float_one]

	fld st0
	fmul DWORD [eax]
	fstp DWORD [eax]
	fld st0
	fmul DWORD [eax+4]
	fstp DWORD [eax+4]
	fld st0
	fmul DWORD [eax+8]
	fstp DWORD [eax+8]

	fmul DWORD [eax+12]
	fstp DWORD [eax+12]

ret

;============================================================
ATXPROC CONVERT_TO_FACING_PROC_2A_STD		; [esp+4] = destvar, [esp+8]=param2, source quat=ecx
;============================================================
;
; CONVERT_TO_FACING_PROC:
; - quaternion to convert in ecx (w = c0, x = c4, y = c8, z = cC)
; - param2 put into eax in original function ([eax+00h] = a0, [eax+04h] = a4, [eax+08h] = a8)
; - final values put into destvar (first parameter)
; - original function uses local mem to store data (4 DWORDs at esp)
; - qword constant or var stored in EXE used once (referred to as q); should be 0.500000
;
; -> new function uses edx for param2 and eax for destvar, because destvar must
;    be returned in eax anyway


virtual at ecx
	c0	dd	?
	c4	dd	?
	c8	dd	?
	cC	dd	?
end virtual

virtual at edx
	a0	dd	?
	a4	dd	?
	a8	dd	?
end virtual

	fld DWORD [c0]
	fmul st0,st0
	mov eax,[CONVERTTOFACING_VAR1]
	fsub qword [eax]			; q (var or constant)

	fld [c0]
	fmul [cC]
	fld [c0]
	fmul [c8]
	fld [c4]
	fmul [cC]
	fld [c0]
	fmul [c4]

	fld [c8]
	fmul [cC]				; st2 when full stack

	fld [c4]
	fmul [c8]
	mov edx,[esp+8]
	mov eax,[esp+4]
	fstp DWORD [esp-4]			; (c4*c8)

; DWORD [destvar+00h] = [eax] = [esp+04h] (originally)
;                     = ((c4^2+(c0^2-q))*a0 + (c4*c8-c0*cC)*a4 + (c0*c8+c4*cC)*a8)*2

	fld [c4]
	fmul st0,st0
	fadd st0,st6
	fmul [a0]

	fld DWORD [esp-4]
	fsub st0,st6
	fmul [a4]
	faddp st1,st0

	fld st4
	fadd st0,st4
	fmul [a8]
	faddp st1,st0
	fadd st0,st0
	fstp DWORD [eax]

; DWORD [destvar+04h] = [eax+4] = [esp+08h] (originally)
;                     = ((c8^2+(c0^2-q))*a4 + (c8*cC-c0*c4)*a8 + (c0*cC+c4*c8)*a0)*2

	fld [c8]
	fmul st0,st0
	fadd st0,st6
	fmul [a4]

	fld st1
	fsub st0,st3
	fmul [a8]
	faddp st1,st0

	fld DWORD [esp-4]
	fadd st0,st6
	fmul [a0]
	faddp st1,st0
	fadd st0,st0
	fstp DWORD [eax+4]

; DWORD [destvar+08h] = [eax+8] = [esp+0Ch] (originally)
;                     = ((cC^2+(c0^2-q))*a8 + (c4*cC-c0*c8)*a0 + (c0*c4+c8*cC)*a4)*2

	faddp st1,st0				; c0*c4+c8*cC
	fmul [a4]
	fxch st2
	fsubp st1,st0				; c4*cC-c0*c8
	fmul [a0]
	faddp st1,st0

	fld [cC]
	fmul st0,st0
	fadd st0,st3
	fmul [a8]

	fxch st2
	fstp st0
	faddp st1,st0
	fadd st0,st0
	fxch st1
	fstp st0
	fstp DWORD [eax+8]


ret 8

;============================================================
ATXPROC CONVERT_TO_FACING_PROC_010_STD		; [esp+4] = destvar. (eax), [esp+8] = (unused) , ecx = quat
;============================================================
; This is used when the first param points to:
; DWORD 0.000000, DWORD 1.000000, DWORD 0.000000
; This is standard axis data (I believe), used for LOC cheat facing values,
; FLY cheat and others. Parameters are preserved for future implementation
; and compatibility.

; [destvar+00h] = ((c4^2+(c0^2-q))*a0 + (c4*c8-c0*cC)*a4 + (c0*c8+c4*cC)*a8)*2
; [destvar+04h] = ((c8^2+(c0^2-q))*a4 + (c8*cC-c0*c4)*a8 + (c0*cC+c4*c8)*a0)*2
; [destvar+08h] = ((cC^2+(c0^2-q))*a8 + (c4*cC-c0*c8)*a0 + (c0*c4+c8*cC)*a4)*2
; Where a0 = 0.000000, a4 = 1.000000, a8 = 0.000000
; Result (faster!):
; [destvar+00h] = (c4*c8-c0*cC)*2     (X facing value)
; [destvar+04h] = (c8^2+(c0^2-q))*2   (Y facing value)
; [destvar+08h] = (c0*c4+c8*cC)*2     (Z facing value)

	mov eax,[esp+4]

	fld [cC]
	fld [c8]
	fld [c4]
	fld [c0]

	fld st0
	fmul st0,st0				; c0^2
	fld st3 				; c8
	fmul st0,st0				; c8^2
	fxch st1
	fsub [float_0_5]
	fxch st1
	faddp st1,st0
	fadd st0,st0				; *2
	fstp DWORD [eax+4]

	fld st1 				; c4
	fmul st0,st3				; c4*c8
	fld st1 				; c0
	fmul st0,st5				; c0*cC
	fxch st1
	fsubrp st1,st0				; c4*c8-c0*cC
	fadd st0,st0				; *2
	fstp DWORD [eax]

	fmulp st1,st0				; c0*c4
	fxch st2
	fmulp st1,st0				; c8*cC
	fxch st1
	faddp st1,st0				; c0*c4+c8*cC
	fadd st0,st0				; *2
	fstp DWORD [eax+8]

ret 8

;============================================================
ATXPROC VectorDotProduct_STD
;============================================================
; performs: (vector1 -dot product- vector2)

; ecx = pointer to source vector [x,y,z]
; edx = pointer to dest./directional vector [x,y,z]
; eax = address of 1 variable to contain vector value (signed, float)

	fld DWORD [edx] 			; begin dot product
	fmul DWORD [ecx]
	fld DWORD [edx+4]
	fmul DWORD [ecx+4]
	fld DWORD [edx+8]
	fmul DWORD [ecx+8]
	faddp
	faddp					;end dot product
	fstp DWORD [eax]

ret

;============================================================
ATXPROC FDConvAnglesToQuat_STD			; converts two rotation angles to a quat
;============================================================
; ecx = pointer to 2 angle rotation values already converted to radians (x,z) (y calculated from x)
;       x component = angle with positive side of y axis (will be negated)
;       z component = angle with x-y plane
; [esp+4] = pointer to 4 DWORD struc to receive (implicitely normalized) quat wxyz

sub esp,36

	mov eax,[ecx]
	mov edx,esp
	mov [esp+32],eax
	add ecx,4
	call ToLocalQuat_AxisX_NoNorm		; calculate z rotation quat (around x axis)

	fld DWORD [esp+32]
	lea ecx,[esp+32]
	fchs
	lea edx,[esp+16]
	fstp DWORD [ecx]
	call ToLocalQuat_AxisZ_NoNorm		; calculate x rotation quat (around z axis)

	mov ecx,esp
	lea eax,[esp+16]
	mov edx,[esp+36+4]
	call QuaternionMul

add esp,36

ret 4

;============================================================
ATXPROC FacingRad2CompToVector_Sub_STD		; assumes dest. vector size = 1
;============================================================
; ecx = pointer to 2 angle rotation values already converted to radians (x,z) (y calculated from x)
;       x component = angle with positive side of y axis
;       z component = angle with x-y plane
; edx = pointer to 3 position values [x,y,z] to add vector to
; eax = pointer to distance multiplier
; note: input angles are based on but do not follow the standard facing values. The X component
;       is NOT limited to the -1.000000 to +1.0000000 interval here, which eliminates the need
;       for the y angle (semi-indirectly).
;
; (note2 : fcos of x = same as fsin of y facing for standard facing values)

	fld DWORD [ecx+4]			; Z facing
	fsincos 				; st0 = cos value (vector on X-Y plane), st1 = sin value (Z vector)
	;fabs
	fmul DWORD [eax]			; distance multiplier for vector on X-Y plane from z angle

	fld DWORD [ecx] 			; X facing
	fsincos

	fld st2 				; (vector on X-Y plane from fcos of z angle w/distance mul.)*fcos of x
	fmulp
	fsubr DWORD [edx+4]			; sub from y comp. of vector
	fstp DWORD [edx+4]

	fmulp					; (vector on X-Y plane from fcos of z angle w/distance mul.)*fsin of x
	fsubr DWORD [edx]			; sub from x comp. of vector
	fstp DWORD [edx]

	fmul DWORD [eax]			; distance multiplier*(fsin of z angle)
	fsubr DWORD [edx+8]			; sub from z comp. of vector
	fstp DWORD [edx+8]

ret

;============================================================
ATXPROC ToLocalQuat_NoNorm_STD
;============================================================
; ecx=pointer to 4 DWORD struc: angle (rad), x axis, y axis, z axis values
; edx=local_rotation pointer wxyz

	fld DWORD [ecx]
	fmul [float_0_5]
	fsincos
	fstp DWORD [edx]

	fld st0
	fmul DWORD [ecx+4]
	fstp DWORD [edx+4]
	fld st0
	fmul DWORD [ecx+8]
	fstp DWORD [edx+8]

	fmul DWORD [ecx+12]
	fstp DWORD [edx+12]

ret

;============================================================
ATXPROC VectorNormalize_STD			; eax = vector (must not be null)
;============================================================
; eax unchanged
; not optimized

	fld DWORD [eax]
	fmul st0,st0
	fld DWORD [eax+4]
	fmul st0,st0
	fld DWORD [eax+8]
	fmul st0,st0
	faddp st1,st0
	faddp st1,st0
	fsqrt

	fdivr [float_one]

	fld DWORD [eax]
	fmul st0,st1
	fstp DWORD [eax]

	fld DWORD [eax+4]
	fmul st0,st1
	fstp DWORD [eax+4]

	fld DWORD [eax+8]
	fmulp st1,st0
	fstp DWORD [eax+8]

ret

;============================================================
ATXPROC QuaternionMulLocalQ2AxisX_STD		; eax=q1 , ecx=q2, edx=new quat
;============================================================
; Limited Quaternion multiplication (eax=q1, ecx=q2, edx=result)
; supposes second quaternion is rotation around x axis only (y2 and z2 null)
; eax = source quat (all valid)
; ecx = around-x quat
; w2 = non-zero
; x2 = non-zero
; y2 = null
; z2 = null
; (Q1 * Q2).w = (w1w2 - x1x2)
; (Q1 * Q2).x = (w1x2 + x1w2)
; (Q1 * Q2).y = (y1w2 + z1x2)
; (Q1 * Q2).z = (z1w2 - y1x2)

;------------- w

	fld [w1]
	fmul [w2]

	fld [x1]
	fmul [x2]
	fsubp st1,st0

	fstp DWORD [edx]

;------------- x

	fld [w1]
	fmul [x2]

	fld [x1]
	fmul [w2]
	faddp st1,st0

	fstp DWORD [edx+4]

;------------- y

	fld [y1]
	fmul [w2]

	fld [z1]
	fmul [x2]
	faddp st1,st0
	fstp DWORD [edx+8]

;------------- z

	fld [z1]
	fmul [w2]

	fld [y1]
	fmul [x2]

	fsubp st1,st0
	fstp DWORD [edx+12]

ret

;============================================================
ATXPROC VectorMagnitude_STD			; eax = pointer to vector, edx = pointer for result
;============================================================

	fld DWORD [eax]
	fmul st0,st0
	fld DWORD [eax+4]
	fmul st0,st0
	fld DWORD [eax+8]
	fmul st0,st0
	faddp st1,st0
	faddp st1,st0
	fsqrt
	fstp DWORD [edx]

ret

;============================================================
ATXPROC VectorToQuaternion_STD			; ecx = pointer to 3 XYZ source vector values, edx = destination for WXYZ quat (returned in eax)
;============================================================
push esi
push edi
	mov esi,ecx
	mov edi,edx

sub esp,36

; calculate horizontal rotation quaternion (x-y)

	fld DWORD [esi+0]			; x
	fld DWORD [esi+4]			; y (note: y is base axis)
	fpatan					; arctan(x/y) (let processor handle problems)
						; if 0/0, return value makes no difference (will point upward - z only)
	mov ecx,esp
	lea edx,[esp+4] 			; for local quat
	fstp DWORD [ecx]
	call [PTRToLocalQuat_AxisMinusZ_NoNorm] ; generate horizontal rotation quaternion

; calculate vertical rotation quaternion (z)  - SIGN ERROR (?)

	fld DWORD [esi+8]			; z vector component
	fld DWORD [esi+4]
	fmul st0,st0
	fld DWORD [esi+0]
	fmul st0,st0
	faddp st1,st0
	fsqrt					; sqrt(x^2+y^2)
	fpatan					; arctan(z/sqrt(x^2+y^2))

	mov ecx,esp
	lea edx,[esp+4+16]			; for local quat
	fstp DWORD [ecx]
	call [PTRToLocalQuat_AxisX_NoNorm]

	lea ecx,[esp+4+16]
	lea eax,[esp+4]
	mov edx,edi
	call QuaternionMul			; multiply quaternions

	mov eax,edi
add esp,36
pop edi
pop esi
ret

;============================================================
ATXPROC XYZDegreesToQuaternion			; converts x,y,z angles to a quat
;============================================================
; ecx = pointer to x angle, y angle, z angle
; edx = pointer to dest. for wxyz quaternion

push ebp
mov ebp,esp
sub esp,64
push esi
push edi
	mov esi,ecx
	mov edi,edx

; create three local rotation quats, one per axis

	lea ecx,[esi]
	lea edx,[ebp-48]
	call [PTRToLocalQuat_AxisX_NoNorm]

	lea ecx,[esi+4]
	lea edx,[ebp-32]
	call [PTRToLocalQuat_AxisY_NoNorm]

	lea ecx,[esi+8]
	lea edx,[ebp-16]
	call [PTRToLocalQuat_AxisZ_NoNorm]


	lea eax,[ebp-16]
	lea ecx,[ebp-32]
	lea edx,[ebp-64]
	call [PTRQuaternionMul] 		; z quat * y quat

	lea eax,[ebp-64]
	lea ecx,[ebp-48]
	lea edx,[edi]
	call [PTRQuaternionMul] 		; (z quat * y quat) * x quat

pop edi
pop esi
mov esp,ebp
pop ebp
ret

;============================================================
proc CheckFacingBetweenInstanceAndTarget
;============================================================
; (on x-y plane only)
; esi = source location + quaternion
; edi = dest location (target)
; ebx = pointer to cosine of maximum allowed angle between source-dest
;       vector and source instance orientation vector
; returns al = TRUE if within or equal to angle, FALSE otherwise

local FacingVector:VECTOR,DistanceVector:VECTOR,DotProductValue:DWORD

; a = angle to get, b = limit angle, vectors are X-Y only (horizontal only)
;                   -> assumed 0 <= b <= pi
; -> limit angle is for each side of head, not total view angle
; cos(a) = vector1.vector2 / (sizeof.vector1*sizeof.vector2)     ;. = dot product ; both vectors assumed non-zero
; validity equation:      a <= b  (for any angle between 0 and 90)
;                    cos(a) >= cos(b)
;                    vector1.vector2 / (sizeof.vector1*sizeof.vector2) >= cos(b)
;                    vector1.vector2 >= cos(b)*(sizeof.vector1*sizeof.vector2)      (valid since vector sizes will never change sign, always positive)
;                    vector1.vector2 >= cos(b)*sizeof.vector1*sizeof.vector2
;                    cos(b)*sizeof.vector1*sizeof.vector2 <= vector1.vector2

	lea ecx,[esi+sizeof.VECTOR]
	lea edx,[FacingVector]
	stdcall CONVERT_TO_FACING_PROC_010,edx,0

	fld [edi+LOCATION.X]
	fsub [esi+LOCATION.X]
	fst [DistanceVector.X]
	fmul [FacingVector.X]
	fld [edi+LOCATION.Y]
	fsub [esi+LOCATION.Y]
	fst [DistanceVector.Y]
	fmul [FacingVector.Y]
	faddp st1,st0
	fstp [DotProductValue]

	fld [FacingVector.X]
	fmul st0,st0
	fld [FacingVector.Y]
	fmul st0,st0
	faddp st1,st0
	fsqrt

	fld [DistanceVector.X]
	fmul st0,st0
	fld [DistanceVector.Y]
	fmul st0,st0
	faddp st1,st0
	fsqrt

	fmulp st1,st0
	fld DWORD [ebx]
	fmulp st1,st0

	fcomp [DotProductValue]
	fstsw ax
	test ah,FPU_VALUE_BELOW_OR_EQUAL	; is cos(b)*sizeof.vector1*sizeof.vector2 <= vector1.vector2 ?
	mov eax,0
	setnz al				; if true, we're within angle

ret
endp

;============================================================
ATXPROC GetDistanceBetweenInstances		; ecx = CInstance 1, edx = CInstance 2 (base addresses)
;============================================================
; uses CVector3
; RETURNS VALUE IN ST0

	fld [ecx+CInstance.Location.X]
	fsub [edx+CInstance.Location.X]
	fmul st0,st0
	fld [ecx+CInstance.Location.Y]
	fsub [edx+CInstance.Location.Y]
	fmul st0,st0
	fld [ecx+CInstance.Location.Z]
	fsub [edx+CInstance.Location.Z]
	fmul st0,st0
	faddp st1,st0
	faddp st1,st0
	fsqrt

ret

;============================================================
ATXPROC VectorMagnitude_st0			; eax = pointer to vector, returns in st0
;============================================================

	fld DWORD [eax]
	fmul st0,st0
	fld DWORD [eax+4]
	fmul st0,st0
	fld DWORD [eax+8]
	fmul st0,st0
	faddp st1,st0
	faddp st1,st0
	fsqrt

ret
