;****************************************************************************
;*
;* HORIZON.ASM
;*
;* Draw horizon.
;*
;* 11.12.1990 - KJB
;* 18.07.1991 - KJB - Graduated horizon added.
;* 18.12.1991 - KJB - UserHorizon routine added.
;* 16.03.1992 - KJB - Overcast sky added.
;* 28.03.1992 - KJB - Fog added.
;* 16.07.1992 - KJB - Night added.
;* 17.09.1992 - KJB - "fade" vars renamed.
;* 15.10.1992 - KJB - Individual polygon cloud fade added.
;*
;* Notes
;* -----
;*
;* The horizon "line" is generated by adjusting fixed lh and rh end points
;* for the line wrt pitch angle, then rotating the end points wrt roll angle.
;*
;* The horizon fill is broken down into four sections:-
;*
;*    a) solid top section
;*    b) horizon line section
;*    c) solid bottom section
;*    d) graduated horizon
;*
;****************************************************************************

		OPTION	M510		;<<<<< MASM v5.10 <<<<<

		PUBLIC	DrawHorizon
		PUBLIC	UserHorizon

		PUBLIC	Overcast
		PUBLIC	Fog
		PUBLIC	Night
		PUBLIC	GndCol
		PUBLIC	SkyCol
		PUBLIC	GradEnabled

		EXTRN	Tangent:FAR
		EXTRN	BlockFill:FAR
		EXTRN	HorizonFill:FAR
		EXTRN	PolyFill:FAR
		EXTRN	CloudCheck1:FAR
		EXTRN	CloudCheck2:FAR

		EXTRN	HorizonClip:NEAR
		EXTRN	CalcOutCodes:NEAR
		EXTRN	PolyClip:NEAR

		EXTRN	CloudBase:DWORD

		EXTRN	ClipWndwXOrg:WORD
		EXTRN	ClipWndwXMax:WORD
		EXTRN	LhsX:WORD
		EXTRN	CloudMode:WORD
		EXTRN	CloudDepth:WORD
		EXTRN	FadeStart1:WORD
		EXTRN	FadeCount1:WORD

		EXTRN	ClipWndwYOrg:BYTE
		EXTRN	ClipWndwYMax:BYTE
		EXTRN	VGA_RGB_Data:BYTE
		EXTRN	VGA_GND:BYTE
		EXTRN	VGA_GND1:BYTE
		EXTRN	VGA_GND2:BYTE
		EXTRN	VGA_SKY:BYTE
		EXTRN	VGA_SKY1:BYTE
		EXTRN	VGA_SKY2:BYTE
		EXTRN	SetPaletteFlag:BYTE
		EXTRN	FadeLevel1:BYTE
		EXTRN	FadeRCol1:BYTE
		EXTRN	FadeGCol1:BYTE
		EXTRN	FadeBCol1:BYTE
		EXTRN	R_GREY_CLOUD:BYTE
		EXTRN	G_GREY_CLOUD:BYTE
		EXTRN	B_GREY_CLOUD:BYTE
		EXTRN	R_WHITE_CLOUD:BYTE
		EXTRN	G_WHITE_CLOUD:BYTE
		EXTRN	B_WHITE_CLOUD:BYTE
		EXTRN	CloudsEnabled:BYTE

		EXTRN	CLOUD_MODE1:ABS

;============================================================================

		INCLUDE	VISEXTRN.INC
		INCLUDE	VISDATA.INC
		INCLUDE	PALETTES.INC

		INCLUDE	\LIB8086\USEFUL.INC
 		INCLUDE	\LIB8086\PALMACRO.INC

;============================================================================

DATA		SEGMENT PARA PUBLIC 'DATA'

;* weather conditions / time of day

Overcast	DB	0,0		;1 = overcast, 0 = clear

Fog		DB	0,0		;0 = clear
					;1 = light fog (Overcast = 1)
					;2 = pea souper (Overcast = 1)

Night		DB	0,0		;0 = day
					;1 = night 1 (dusk)
					;2 = night 2
					;3 = night 3
					;4 = night 4 (midnight)

;* logical colours for ground / sky

GndSkyCols	LABEL	WORD

GndCol		DB	COL_GND1
SkyCol		DB	COL_SKY12

TopBotCols	LABEL	WORD

TopCol		DB	0
BotCol		DB	0

LhsRhsCols	LABEL	WORD

LhsCol		DB	0
RhsCol		DB	0

;* horizon crt co-ords

Hx1		DW	0
Hy1		DB	0,0
Hx2		DW	0
Hy2		DB	0,0

;* horizon end point eye co-ords

XEYE		EQU	4096
YEYE		EQU	1024		;changed from  256 (grad hrzn mod)
ZEYE 		EQU	-2048		;changed fram -512 (grad hrzn mod)

;* modified pitch (wrt altitude)

NewPitch	DW	0

;* horizon octant vector routines

Octant	 	DW	Octant2		;Dx > 0, Dy > 0, Dy > Dx
		DW	Octant1		;Dx > 0, Dy > 0, Dx > Dy
		DW	Octant7		;Dx < 0, Dy > 0, Dy > Dx
		DW	Octant8		;Dx < 0, Dy > 0, Dx > Dy

;* graduated horizon vars

ZT		EQU	256		;translate horizon value

GradEnabled	DB	1,0		;1 = graduated horizon enabled

GradVisible	DB	0,0		;1 = graduated horizon visible

GradPoly	DB	0,0,0,0,-1	;graduation polygon

		EVEN

;* ground / sky RGB ptrs

GndRGBPtr	DW	OFFSET VGA_GND1
SkyRGBPtr	DW	OFFSET VGA_SKY1

;* draw overcast sky switch (wrt CloudMode)

SkySwitch	DW	BelowClouds	;CLOUD_MODE1
		DW	InClouds	;CLOUD_MODE2
		DW	AboveClouds  	;CLOUD_MODE3

DATA		ENDS

;============================================================================

VISCODE		SEGMENT BYTE PUBLIC 'CODE'
		ASSUME CS:VISCODE
		ASSUME DS:DATA

;* UserHorizon - draw user horizon (for artificial horizon etc.)
;*
;* pass: si -> VIEWPOINT data block
;*       al = logical ground colour
;*       ah = logical sky colour
;*       cx = sin(roll)
;*       dx = cos(roll)
;* ret : nothing
;* kill: assume all

UserHorizon	PROC	FAR

		push	V_SinR
		push	V_CosR

		push	V_VIEW.VP_PITCH
		push	V_VIEW.VP_ROLL

		push	GndSkyCols

		push	WORD PTR GradEnabled

		mov	GndSkyCols,ax
		mov	V_SinR,cx
		mov	V_CosR,dx

		mov	ax,VP_ROLL[si]
		mov	V_VIEW.VP_ROLL,ax
		mov	ax,VP_PITCH[si]
		mov	V_VIEW.VP_PITCH,ax

		mov	GradEnabled,0

		call	IntoHorizon

		pop	WORD PTR GradEnabled

		pop	GndSkyCols

		pop	V_VIEW.VP_ROLL
		pop	V_VIEW.VP_PITCH

		pop	V_CosR
		pop	V_SinR

		ret

UserHorizon	ENDP

;----------------------------------------------------------------------------

;* DrawHorizon - draw horizon (wrt view mode)
;* IntoHorizon - draw horizon, no graduations, no pitch modification
;*
;* pass: V_VIEW
;*       V_SinR
;*       V_CosR
;*       ax = pitch (IntoHorizon only)
;* ret : nothing
;* kill: assume all

DrawHorizon	PROC	NEAR

;* reset fade level and range

		mov	FadeLevel1,0

		mov	FadeStart1,FOG_COL_START
		mov	FadeCount1,FOG_COL_COUNT

;* sort overcast sky (wrt CloudMode)

		test	Overcast,1	;overcast?
		_JZ	PolyCloudFade	;no ->

		mov	bx,CloudMode
		jmp	SkySwitch[bx]

;---------------------------
BelowClouds	LABEL	NEAR
;---------------------------

		mov	si,OFFSET VGA_GND1
		call	SortRGBGnd

		mov	si,OFFSET VGA_SKY2
		call	SortRGBSky

;* if (cloud base - zft) < 512ft then fade to grey cloud
;*    
;* fade level = not((cloud base - zft) / 4) and 07fh

		mov	ax,WORD PTR CloudBase
		mov	dx,WORD PTR CloudBase+2

		sub	ax,WORD PTR V_VIEW.VP_ZFT_LO
		sbb	dx,WORD PTR V_VIEW.VP_ZFT_HI

		test	dx,dx		;(cloud base - zft) < 65536?
		jnz	SortFog		;no ->

		cmp	ax,512		;(cloud base - zft) < 512?
		jae	SortFog		;no ->

		REPT	2		;/4 (0 .. 127)
		shr	ax,1
		ENDM

		not	al		;127 .. 0

		and	al,07fh

;* if Fog = 1 then FadeLevel1 = FadeLevel1 / 2 + 64 (64 .. 127)

		cmp	Fog,1		;light fog?
		jne	@F		;no ->

		shr	al,1		;/2
		add	al,64
		jmp	FadeOk

;* if Fog = 2 then FadeLevel1 = FadeLevel1 / 4 + 96 (96 .. 127)

@@:		cmp	Fog,2		;pea souper?
		jne	FadeOk		;no ->

		shr	al,1		;/2
		shr	al,1		;/4
		add	al,96

FadeOk:		mov	FadeLevel1,al

		mov	al,R_GREY_CLOUD
		mov	FadeRCol1,al
		mov	al,G_GREY_CLOUD
		mov	FadeGCol1,al
		mov	al,B_GREY_CLOUD
		mov	FadeBCol1,al

		jmp	SortSkyCol

;* sort fog

SortFog:	cmp	Fog,0		;foggy?
		je	SkipFog		;no ->

		mov	al,64		;assume light fog

		cmp	Fog,1		;light fog?
		je	@F		;yes ->

		mov	al,96		;pea souper

@@:		mov	FadeLevel1,al

		mov	al,R_GREY_CLOUD
		mov	FadeRCol1,al
		mov	al,G_GREY_CLOUD
		mov	FadeGCol1,al
		mov	al,B_GREY_CLOUD
		mov	FadeBCol1,al

SkipFog:	jmp	SortSkyCol

;---------------------------
InClouds	LABEL	NEAR
;---------------------------

		mov	si,OFFSET VGA_SKY2
		call	SortRGBSky

		mov	al,COL_SKY16

		mov	cx,ClipWndwXOrg
		mov	bl,ClipWndwYOrg
		mov	dx,ClipWndwXMax
		mov	bh,ClipWndwYMax

		call	BlockFill

;* if (cloud base + cloud depth - zft) < 256ft then fade to white cloud
;*
;* fade level = not((cloud base + cloud depth - zft) / 2) and 07fh

		mov	ax,WORD PTR CloudBase
		mov	dx,WORD PTR CloudBase+2

		add	ax,CloudDepth
		adc	dx,0

		sub	ax,WORD PTR V_VIEW.VP_ZFT_LO
		sbb	dx,WORD PTR V_VIEW.VP_ZFT_HI

		test	dx,dx		;(cloud base + depth - zft) < 65536?
		jnz	@F		;no ->

		cmp	ax,256		;(cloud base + depth - zft) < 256?
		jae	@F		;no ->

		shr	ax,1		;/2

		not	al		;127 .. 0

		and	al,07fh

		mov	FadeLevel1,al

		mov	al,R_WHITE_CLOUD
		mov	FadeRCol1,al
		mov	al,G_WHITE_CLOUD
		mov	FadeGCol1,al
		mov	al,B_WHITE_CLOUD
		mov	FadeBCol1,al

@@:		jmp	DrawHrznExit

;---------------------------
AboveClouds	LABEL	NEAR
;---------------------------

		mov	si,OFFSET VGA_GND2
		call	SortRGBGnd

		mov	si,OFFSET VGA_SKY1
		call	SortRGBSky

;* if (zft - cloud base - cloud depth) < 512ft then fade to white cloud
;*
;* fade level = not((zft - cloud base - cloud depth) / 4) and 07fh

		mov	ax,WORD PTR V_VIEW.VP_ZFT_LO
		mov	dx,WORD PTR V_VIEW.VP_ZFT_HI

		sub	ax,WORD PTR CloudBase
		sbb	dx,WORD PTR CloudBase+2

		sub	ax,CloudDepth
		sbb	dx,0

		test	dx,dx		;(zft - cloud base - depth) < 65536?
		_JNZ	SortSkyCol	;no ->

		cmp	ax,512		;(zft - cloud base - depth) < 512?
		_JAE	SortSkyCol	;no ->

		REPT	2		;/4 (0 .. 127)
		shr	ax,1
		ENDM

		not	al		;127 .. 0

		and	al,07fh

		mov	FadeLevel1,al

		mov	al,R_WHITE_CLOUD
		mov	FadeRCol1,al
		mov	al,G_WHITE_CLOUD
		mov	FadeGCol1,al
		mov	al,B_WHITE_CLOUD
		mov	FadeBCol1,al

		jmp	SortSkyCol

;---------------------------
PolyCloudFade	LABEL	NEAR		;individual polygon cloud fade
;---------------------------

		test	CloudsEnabled,1
		_JZ	SortSkyCol	;no polygon clouds ->

;* if abs(zft - cloud base) <= 511 then
;*    if directly above / below polygon cloud then
;*       fade palette
;*    endif
;* endif

		mov	ax,WORD PTR V_VIEW.VP_ZFT_LO
		mov	dx,WORD PTR V_VIEW.VP_ZFT_HI

		sub	ax,WORD PTR CloudBase
		sbb	dx,WORD PTR CloudBase+2

		jns	@F

		NEG32	dx,ax

@@:		test	dx,dx		;< 65536?
		jnz	@F		;no ->

		cmp	ax,511		;<= 511?
		ja	@F		;no ->

		mov	si,OFFSET V_VIEW
		push	ax
		call	CloudCheck1
		pop	ax
		jc	ContPolyFade

;* (note: If cloud base test fails try cloud top in case depth is shallow.)

;* if abs(zft - cloud base - cloud depth) <= 511 then
;*    if directly above / below polygon cloud then
;*       fade palette
;*    endif
;* endif

@@:		mov	ax,WORD PTR V_VIEW.VP_ZFT_LO
		mov	dx,WORD PTR V_VIEW.VP_ZFT_HI

		sub	ax,WORD PTR CloudBase
		sbb	dx,WORD PTR CloudBase+2

		sub	ax,CloudDepth
		sbb	dx,0

		jns	@F

		NEG32	dx,ax

@@:		test	dx,dx		;< 65536?
		jnz	SortSkyCol	;no ->

		cmp	ax,511		;<= 511?
		ja	SortSkyCol	;yes ->

		mov	si,OFFSET V_VIEW
		push	ax
		call	CloudCheck2
		pop	ax
		jnc	SortSkyCol

ContPolyFade:	mov	dx,511		;increase fade nearer cloud
		sub	dx,ax

		shr	dx,1		;0 .. 255
		shr	dx,1		;0 .. 127

		mov	FadeLevel1,dl

		mov	al,R_WHITE_CLOUD
		mov	FadeRCol1,al
		mov	al,G_WHITE_CLOUD
		mov	FadeGCol1,al
		mov	al,B_WHITE_CLOUD
		mov	FadeBCol1,al

;* sort sky col wrt graduations enabled

SortSkyCol:	test	GradEnabled,1	;enabled?
		mov	al,COL_SKY16	;assume so
		jnz	@F		;yes ->
		mov	al,COL_SKY12
@@:		mov	SkyCol,al

		mov	GndCol,COL_GND1	;set ground col

;* modify pitch wrt altitude (pitch up to force ground visual to the horizon)
;*
;* pitch up (p degs) = z scaled / 8192 = 0 .. 3

		mov	ax,ZScaled
		rol	ax,1
		rol	ax,1
		rol	ax,1
		and	ax,0011b	;z scaled / 8192 = 0 .. 3
		add	ax,V_VIEW.VP_PITCH
		and	ax,001ffh	;wrap around

;---------------------------
IntoHorizon	LABEL	NEAR		;<<<<< entry from UserHorizon <<<<<
;---------------------------

		mov	NewPitch,ax

;* determine if draw horizon, only ground or only sky

		mov	GradVisible,0	;assume graduations not visible

		cmp	ax,90		;pitch <= max tangent value?
		jbe	SortHorizon	;yes ->
		cmp	ax,422		;pitch >= max tangent value?
		jae	SortHorizon	;yes ->

HrznNonVisible:	mov	ax,NewPitch
		cmp	ax,256		;only sky?
		mov	al,SkyCol	;assume so
		jb	OnlySky		;yes ->

;* draw ground / sky only

OnlyGnd:	mov	al,GndCol

OnlySky:	mov	cx,ClipWndwXOrg
		mov	bl,ClipWndwYOrg
		mov	dx,ClipWndwXMax
		mov	bh,ClipWndwYMax

		call	BlockFill

		jmp	DrawGradHrzn

;* calc end points for horizon line wrt pitch and roll (2D rotation):-
;*
;* Xe1 = (cosr * XEYE) - (sinr * ZEYE * tan(pitch))
;* Ye1 = YEYE
;* Ze1 = (sinr * XEYE) + (cosr * ZEYE * tan(pitch))
;*
;* Xe2 = (cosr * -XEYE) - (sinr * ZEYE * tan(pitch))
;* Ye2 = YEYE
;* Ze2 = (sinr * -XEYE) + (cosr * ZEYE * tan(pitch))

SortHorizon:	mov	GradVisible,1	;graduations visible
		
		call	Tangent		;dx = tan(pitch)

		mov	ax,ZEYE
		imul	dx		;dx, ax = ZEYE * tan(pitch)
		FRACADJ	bp		;bp = ZEYE * tan(pitch)

		mov	ax,V_SinR
		mov	bx,ax

		imul	bp		;dx, ax = sinr * ZEYE * tan(pitch)
		FRACADJ	si		;si = sinr * ZEYE * tan(pitch)

		mov	ax,XEYE
		imul	bx   		;dx, ax = sinr * XEYE
		FRACADJ	bx		;bx = sinr * XEYE

		mov	ax,V_CosR
		mov	cx,ax

		imul	bp		;dx, ax = cosr * ZEYE * tan(pitch)
		FRACADJ	di		;di = cosr * ZEYE * tan(pitch)

		mov	ax,XEYE
		imul	cx		;dx, ax = cosr * XEYE
		FRACADJ	cx		;cx = cosr * XEYE

		mov	ax,cx
		sub	ax,si		;(cosr * XEYE) - (sinr * ZEYE * tan(pitch))
		mov	Xe[0],ax
		mov	Xe[4],ax	;preserve for grad hozn

		mov	ax,bx
		add	ax,di		;(sinr * XEYE) + (cosr * ZEYE * tan(pitch))
		mov	Ze[0],ax
		mov	Ze[4],ax	;preserve for grad hozn

		mov	ax,cx
		neg	ax		;-(cosr * XEYE) = (cosr * -XEYE)
		sub	ax,si		;(cosr * -XEYE) - (sinr * ZEYE * tan(pitch))
		mov	Xe[2],ax
		mov	Xe[6],ax	;preserve for grad hozn

		mov	ax,bx
		neg	ax		;-(sinr * XEYE) = (sinr * -XEYE)
		add	ax,di		;(sinr * -XEYE) + (cosr * ZEYE * tan(pitch))
		mov	Ze[2],ax
		mov	Ze[6],ax	;preserve for grad hozn

		mov	ax,YEYE
		mov	Ye[0],ax
		mov	Ye[2],ax

		call	HorizonClip  	;clip horizon line and test visibility
		jnc	HrznVisible	;visible ->

		jmp	HrznNonVisible

HrznVisible:	mov	ax,GndSkyCols
		mov	bx,ax		;lhs / rhs cols
		mov	cx,ax		;top / bottom cols

;* sort lhs / rhs colours wrt roll (for horizon line fill)

		mov	ax,V_VIEW.VP_ROLL
		cmp	ax,256		;roll >= 180?
		jae	SortTopBotCols	;yes ->
		xchg	bl,bh		;swap lhs / rhs cols

;* sort top / bottom colours wrt roll (for solid top / bottom fill)

SortTopBotCols:	sub	ax,128 		;roll 90 degs
		and	ax,001ffh	;roll 0 .. 511 p degs
		cmp	ax,256		;inverted?
		jb	SetHrznCols	;no ->
		xchg	cl,ch		;swap top / bottom cols

SetHrznCols:	mov	LhsRhsCols,bx
		mov	TopBotCols,cx

;* if y1 > y2 then swap co-ords

		mov	bl,YCrt[0]
		mov	cx,XCrt[0]
		mov	bh,YCrt[2]
		mov	dx,XCrt[2]

		cmp	bl,bh		;y1 <= y2?
		jbe	NoSwap		;yes ->
		xchg	bl,bh
		xchg	cx,dx

NoSwap:		mov	Hx1,cx		;store crt co-ords
		mov	Hy1,bl
		mov	Hx2,dx
		mov	Hy2,bh

;---------------------------------
;* fill solid top section (if any)
;--------------------------------- 

		cmp	bl,ClipWndwYOrg	;y1 > y origin?
		je	FillMiddle	;no ->

		mov	bh,bl
		dec	bh   
		mov	bl,ClipWndwYOrg
		mov	cx,ClipWndwXOrg
		mov	dx,ClipWndwXMax

		mov	al,TopCol
		call	BlockFill

;------------------------------
;* fill middle section (if any)
;------------------------------

FillMiddle:	mov	ax,WORD PTR Hy1
		mov	bx,WORD PTR Hy2
		cmp	ax,bx		;y1 = y2?	
		jne	ContMidFill	;no ->

;* flat horizon line, but does it extend across the entire clip window?

		mov	cx,Hx1
		mov	dx,Hx2
		cmp	cx,dx		;x1 < x2
		jbe	NoXSwap		;yes ->
		xchg	cx,dx
NoXSwap:	cmp	cx,ClipWndwXOrg	;x1 on lhs?
		jne	SetLhX		;no ->
		cmp	dx,ClipWndwXMax	;x2 on rhs?
		je	FlatHorizon	;yes ->

SetRhX:		mov	ax,dx
		shl	bx,1
		mov	LhsX[bx],ax	;store rh x value
		jmp	ContHrznFill

SetLhX:       	mov	ax,cx
		shl	bx,1
		mov	LhsX[bx],ax	;store lh x value
		jmp	ContHrznFill

ContMidFill:	shl	ax,1		;y1 * 2
		mov	di,OFFSET Lhsx	;di -> LhsX[]
		add	di,ax		;di -> LhsX[y1 * 2]

		mov	ax,Hx2
		mov	si,ax		;si = x2
		sub	ax,Hx1		;Dx = Hx2 - Hx1

		ABSV	ax		;ax = abs(Dx), dx = sign(Dx)

		mov	bx,dx		;bx = sign(Dx) (00000h or 0ffffh)
		
		mov	dx,ax		;dx = abs(Dx)

		mov	ax,WORD PTR Hy2
		sub	ax,WORD PTR Hy1	;Dy = Hy2 - Hy1 (always +ve)

		mov	bp,ax		;bp = abs(Dy)

		mov	ax,Hx1		;ax = x1

 		cmp	bp,dx		;cf = 1 if Dx > Dy
		rcl	bx,1		;b1 = sign(Dx), b0 = (Dx > Dy)
		and	bx,11b		;clear unwanted bits
		shl	bx,1		;*2 index
		
		call	Octant[bx]	;call the octant

ContHrznFill:	mov	bl,Hy1
		mov	bh,Hy2
		mov	cx,ClipWndwXOrg
		mov	dx,ClipWndwXMax

		mov	ax,LhsRhsCols
		call	HorizonFill
		
;------------------------------------
;* fill solid bottom section (if any)
;------------------------------------ 

		mov	bl,Hy2
		cmp	bl,ClipWndwYMax	;y2 < y max?
		je	DrawGradHrzn	;no ->

		inc	bl
FlatHorizon:	mov	bh,ClipWndwYMax
		mov	cx,ClipWndwXOrg
		mov	dx,ClipWndwXMax

		mov	al,BotCol
		call	BlockFill

;------------------------
;* draw graduated horizon
;------------------------

DrawGradHrzn:	mov	al,GradEnabled
		test	al,GradVisible	;grad horizon enabled and visible?
		_JZ	DrawHrznExit	;no ->

;* ignore grad horizon under some conditions (visual effect is not pleasing)
;*
;* if overcast then
;*    if below clouds then
;*       select night
;*       case 0
;*          draw grad horizon
;*       case 1
;*          if fog <= 1 then
;*             draw grad horizon
;*          endif
;*       case 2
;*          if fog = 0 then
;*             draw grad horizon
;*          endif
;*       case else
;*       endselect
;*    else
;*       draw grad horizon
;*    endif
;* else
;*    draw grad horizon
;* endif

		test	Overcast,1	;overcast sky?
		jz	ContGradHrzn	;no ->

		cmp	CloudMode,CLOUD_MODE1	;below clouds?
		jne	ContGradHrzn		;no ->

		mov	al,Night

		test	al,al		;night?
		jz	ContGradHrzn	;no ->

		cmp	al,1		;night 1?
		jne	@F		;no ->

		cmp	Fog,1		;clear or light fog?
		jbe	ContGradHrzn	;yes ->

		jmp	DrawHrznExit

@@:		cmp	al,2		;night 2?
		_JNE	DrawHrznExit	;no ->

		cmp	Fog,0		;clear?
		_JNE	DrawHrznExit	;no ->

;* translate horizon line
;*
;* x tranlate (xt) = 0, z translate (zt) = ZT
;*
;* x offset = (cosr * xt) - (sinr * zt) = -(sinr * ZT) 
;* z offset = (sinr * xt) + (cosr * zt) = cosr * ZT

;* calc x eye's

ContGradHrzn:	mov	ax,V_SinR
		mov	dx,ZT
		imul	dx
		FRACADJ	dx 		;dx = sinr * ZT

		neg	dx		;dx = -(sinr * ZT)

		xor	ax,ax		;clear fraction

		REPT	4		;/16
		sar	dx,1
		rcr	ax,1
		ENDM

		mov	cx,Xe[4]	;rh x eye
		xor	bx,bx		;clear fraction

		mov	di,Xe[6]	;lh x eye
		xor	si,si		;clear fraction

INDEX		=	0

		REPT	16

		mov	Xe[INDEX+0],cx	;store rh x eye
		mov	Xe[INDEX+2],di	;store lh x eye

		IF INDEX NE 15*4

		add	bx,ax		;update rh x eye
		adc	cx,dx

		add	si,ax		;update lh x eye
		adc	di,dx

		ENDIF

INDEX		=	INDEX+4

		ENDM

;* calc z eye's

		mov	ax,V_CosR
		mov	dx,ZT
		imul	dx
		FRACADJ	dx 		;dx = cosr * ZT

		xor	ax,ax		;clear fraction

		REPT	4		;/16
		sar	dx,1
		rcr	ax,1
		ENDM

		mov	cx,Ze[4]	;rh z eye
		xor	bx,bx		;clear fraction

		mov	di,Ze[6]	;lh z eye
		xor	si,si		;clear fraction

INDEX		=	0

		REPT	16

		mov	Ze[INDEX+0],cx	;store rh z eye
		mov	Ze[INDEX+2],di	;store lh z eye

		IF INDEX NE 15*4

		add	bx,ax		;update rh z eye
		adc	cx,dx			    

		add	si,ax		;update lh z eye
		adc	di,dx

		ENDIF

INDEX		=	INDEX+4

		ENDM

;* calc y eye's

		mov	ax,YEYE
		mov	cx,16*2
		mov	di,OFFSET Ye
		rep	stosw

;* test if graduated horizon is visible

		mov	si,62
		call	CalcOutCodes
		jc	DrawHrznExit	;non-visible ->

;* draw graduations

		mov	GradPoly[0],0
		mov	GradPoly[1],2
		mov	GradPoly[2],6
		mov	GradPoly[3],4

		mov	Col1,COL_SKY1

		mov	cx,15		;num graduations
GradLoop:	push	cx

		mov	si,OFFSET GradPoly
		mov	di,OFFSET CrtBuffer
		call	PolyClip
		jc	@F		;non-visible ->

		mov	al,Col1
		mov	si,OFFSET CrtBuffer
		call	PolyFill

@@:		mov	al,GradPoly[2]	;next graduation
		mov	GradPoly[1],al
		add	al,4
		mov	GradPoly[2],al

		mov	al,GradPoly[3]	;next graduation
		mov	GradPoly[0],al
		add	al,4
		mov	GradPoly[3],al

		inc	Col1

		pop	cx
		loop	GradLoop

;* draw "anti-aliasing" ground line (also helps sky breakthrough at low altitudes)

		mov	GradPoly[0],0
		mov	GradPoly[1],2
		mov	GradPoly[2],-1

		mov	si,OFFSET GradPoly
		mov	di,OFFSET CrtBuffer
		call	PolyClip
		jc	DrawHrznExit	;non-visible ->

		mov	al,COL_GND2
		mov	si,OFFSET CrtBuffer
		call	PolyFill

DrawHrznExit:	ret
	
DrawHorizon	ENDP		

;----------------------------------------------------------------------------

;* SortRGBGnd
;*
;* pass: si -> required ground RGB overlay data
;* ret : GndRGBPtr
;* kill: assume all

SortRGBGnd	PROC	NEAR

		cmp	si,GndRGBPtr	;ground cols ok?
		je	@F		;yes ->

		mov	GndRGBPtr,si

		OVLYRGB	VGA_GND,si,2

		mov	SetPaletteFlag,1

@@:		ret

SortRGBGnd	ENDP

;----------------------------------------------------------------------------

;* SortRGBSky
;*
;* pass: si -> required sky RGB overlay data
;* ret : SkyRGBPtr
;* kill: assume all

SortRGBSky	PROC	NEAR

		cmp	si,SkyRGBPtr	;sky cols ok?
		je	@F		;yes ->

		mov	SkyRGBPtr,si

		OVLYRGB	VGA_SKY,si,16

		mov	SetPaletteFlag,1

@@:		ret

SortRGBSky	ENDP

;----------------------------------------------------------------------------

;* DrawHorizon - octant routines
;*
;* pass: ax = x1
;*       si = x2
;*       dx = abs(Dx)
;*       bp = abs(Dy) (never 0)
;*       di -> LhsX[y1 * 2]
;* ret : LhsX[y1] .. LhsX[y2] = x values for horizon line 
;* kill: assume all

;==================================
;* Octant1: Dx > 0, Dy > 0, Dx > Dy
;==================================

Octant1		PROC	NEAR

		mov	cx,bp		;count = Dy

		mov	bx,dx
		shr	bx,1		;error = Dx / 2
		adc	bx,0		;1/2 bit round up

		jmp	IntoOct1

Oct1Lp:		inc	ax		;x = x + 1
IntoOct1:	sub	bx,bp		;error = error - Dy
		ja	Oct1Lp		;if error > 1 then no y change ->

		add	bx,dx		;error = error + Dx
		stosw			;store x, y = y + 1
		loop	Oct1Lp		;count = count - 1, count > 0 ->

		mov	ax,si		;fetch last point
		stosw			;store last point

		ret
		
Octant1		ENDP

;==================================
;* Octant2: Dx > 0, Dy > 0, Dy > Dx
;==================================

Octant2		PROC	NEAR

		mov	cx,dx		;count = Dx

		mov	bx,bp
		shr	bx,1		;error = Dy / 2
		adc	bx,0		;1/2 bit round up

		mov	si,bp
		inc	si		;y count = Dy + 1

		jcxz	Oct2LpB		;vertical line ->

Oct2LpA:	stosw			;store x, y = y + 1
		dec	si		;y count = y count - 1
		sub	bx,dx		;error = error - Dx
		ja	Oct2LpA		;if error > 0 then no x change ->

		add	bx,bp		;error = error + Dy
		inc	ax		;x = x + 1
		loop	Oct2LpA		;count = count - 1, count > 0 ->

Oct2LpB:	mov	cx,si		;loop count for remaining y
		rep 	stosw

		ret

Octant2		ENDP

;==================================
;* Octant7: Dx < 0, Dy > 0, Dy > Dx
;==================================

Octant7		PROC	NEAR

		mov	cx,dx		;count = Dx

		mov	bx,bp
		shr	bx,1		;error = Dy / 2
		adc	bx,0		;1/2 bit round up

		mov	si,bp
		inc	si		;y count = Dy + 1

		jcxz	Oct7LpB		;vertical line ->

Oct7LpA:	stosw			;store x, y = y + 1
		dec	si		;y count = y count - 1
		sub	bx,dx		;error = error - Dx
		ja	Oct7LpA		;if error > 0 then no x change ->

		add	bx,bp		;error = error - Dy
		dec	ax		;x = x - 1
		loop	Oct7LpA		;count = count - 1, count > 0 ->

Oct7LpB:	mov	cx,si		;loop count for remaining y
		rep	stosw

		ret

Octant7		ENDP

;==================================
;* Octant8: Dx < 0, Dy > 0, Dx > Dy
;==================================

Octant8		PROC	NEAR

		mov	cx,bp		;count = Dy

		mov	bx,dx
		shr	bx,1		;error = Dx / 2
		adc	bx,0		;1/2 bit round up

		jmp	IntoOct8

Oct8Lp:		dec	ax		;x = x - 1
IntoOct8:	sub	bx,bp		;error = error - Dy
		ja	Oct8Lp		;if error > 1 then no y change ->

		add	bx,dx		;error = error + Dx
		stosw			;store x, y = y + 1
		loop	Oct8Lp		;count = count - 1, count > 0 ->

		mov	ax,si		;fetch last point
		stosw			;store last point

		ret

Octant8		ENDP

VISCODE		ENDS

;============================================================================

		END

