; initial psuedo code for polygon clipping / 68000 code
; this will occur after the drawing of the object has been completed - I think
; primary register functions :
; d0 - scratch space			a0 - data pointer
; d1 - x offset				a1 - 3d pointer
; d2 - z offset				a2 - temp store
; d3 - x co-ordinate short term		a3 - temp store 2
; d4 - z co-ordinate short term
; d5 - double level counter for boundary objects and points
; d6 - point number / offset and work space

;=====================================================
;		MACROS
;=====================================================

.macro	trace value
.iif Trace_on > 0,	move\! \value,(a6)+

.if Trace_on = 2
	cmpa.l	#$78000,a6
	bge	dropout		; kill if overshot
.endif
.endm

.macro	RunGPU	 program

	movea.w	#gpusem,a0
.ntc\~:	tst.l	(a0)
	bne.b	.ntc\~
	addq.l	#1,(a0)			; note addq.l to a known zero is same effect as move.l #1, but 2 bytes (not 6) and 20 ticks (not 28)
					; although moveq #1 + move.l would be quicker, use 4 bytes but need a register !!
;	bclr	#0,G_CTRL+3		; halt GPU - note +3 to force last byte
	move.l	#G_RAM,G_PC		; set pc to start
	move.l	#\program*GPUM,gpustop
	move.l	#$11,G_CTRL		; start GPU

.endm

.macro	waitgpu
	movea.w	#gpusem,a0
.wait\~:tst.l	(a0)
	bne.b	.wait\~
.endm

.macro	border colour,override
.iif Borders=1, move.l #b_\colour,BORD1
.iif \?override&!Borders, move.l #b_\colour,BORD1
.endm

;========================================
;         INCLUDES OF EQUATES
;========================================

.include	'memdefs.inc'
.include	'jaguar.inc'

pclip2::	movem.l	d0-d6/a0-a3,-(sp)	; stack stuff, since I'm going to mess up most registers

; first off we need to know the transformation details
; this is from viewpoint relative 3d space to car relative 3d space 
; because you can't find the real world co-ordinates of individual points - they only exist in viewpoint space

	bsr	get_a1_a2	; gets address of players car data into a1
	moveq	#0,d0
	move.w	Genstr+2,d0	; view dir is reverse of car dir
	tst.b	crash_flag.w
	beq.b	.no
	add.w	car_direction.w,d0
	bra.b	.gs
.no:	add.w	6(a1),d0	; hence add together
.gs:	bsr	get_sine	; note this is done first, because the output from sine routine takes two registers

	neg.l	d1		; reversal because negative transform done by mistake
	move.w	d1,xmul.w	; store the multipliers, because you run out of registers otherwise
	move.w	d2,zmul.w	; --

	moveq.l	#0,d2

; now need to transform - as per the transformation used in GPU collision code
; the points to transform are stored in a list generated during the drawing loop, with
; the number of points first, then the point coordinates for x and z (since only ground collision is considered)
; The first entry is a pointer to the address after the last item, so can be used as a end marker

	movea.l	#collide_table,a0	; get address of the boundary data 
	move.l	(a0)+,a1		; read end address
	moveq.l	#-1,d0
	move.l	d0,(a1)		; use -1 as an end of list marker
.ol0:	move.l	(a0)+,d5	; read number of points in the next boundary
	bmi	.exit
	move.l	d5,-(sp)	; stack it for use in clipping

	bsr	boundary_transform

; before starting the polygon clipping routines, add first point on the end to allow wrap around

	movea.l	#tmpstor1,a3
	move.l	(a3)+,(a2)+
	move.l	(a3)+,(a2)+	; write x + z onto end of list

	movea.l	#tmpstor1,a2	; input address
	movea.l	#tmpstor2,a3	; output address
	move.l	(sp)+,d0	; get count

	bsr	clip_all	; clip to all four sides of cars movement rectangle
	tst.w	d0		; check if all points have been removed
	beq	.ol0		; if so, then skip to the round again section

; having performed full four way polygon vs rectangle clipping, we now know that collision has occurred
; so must find out which side it hit first - ie which vertex is closest
; data is now in (a3) because of repeated swapping

	bsr	nearest_vertex

; now we have the closest point in d3,d4 so we can work out the side
; however first we need to determine the type of collision, because certain types will overide others
; this requires finding out which neighbouring point is appropriate to use for calculations.

; to find the neighbouring points ...
	swap	d2		; regain # of points
	move.w	d2,d0		; put in d0
	swap	d2		; switch back to nearest point #
	neg.w	d2		; turn into forward counted
	add.w	d0,d2		; so that 1st point = 0
	subq.w	#1,d2		; correction

	pea.l	.use		; place a return address on stack to save so many branches

; to test whether a mainly left or right point ...

	move.l	#200,d1
	cmp.l	d3,d1
	blt.b	.right		; a right hand point
	neg.l	d1
	cmp.l	d3,d1
	bgt.b	.left		; a left hand point

; to test central point relationships
.tty	set	2

.if	.tty = 0
	bsr.b	.prev		; get previous point
	bsr	.grad		; preform gradient calcs - returns result in d1
	move.l	d1,-(sp)	; stack result whilst the next one is read in and overwrites it.
	bsr.b	.next
	bsr	.grad
	move.l	(sp),d6		; regain previous result, but leave on stack
	bpl.b	.sk1		; test and
	neg.l	d6		; make absolute
.sk1:	move.l	d1,-(sp)	; stack new result as well
	bpl.b	.sk2		; test and
	neg.l	d1		; make absolute
.sk2:	cmp.l	d1,d6		; see which is worse - ie biggest
	bpl.b	.d6bg
	move.l	(sp)+,d1
	addq.l	#4,sp
	rts			; use fake return address
.d6bg:	addq.l	#4,sp
	move.l	(sp)+,d1
.cont:	rts			; ditto

.endif

.if	.tty = 1
	bsr.b	.prev
	move.l	d1,-(sp)	; stack x
	bsr.b	.next
	move.l	(sp)+,d6	; get back x
	sub.l	d3,d1		; make dx
	bpl.b	.nne
	neg.l	d1		; adjust sign 
.nne:	sub.l	d3,d6		; make dx2
	bpl.b	.nnf
	neg.l	d6		; adjust sign
.nnf:	pea	.grad		; to force even central ones to get gradient
	cmp.l	d1,d6		; compare dx's
	bpl.b	.prev		; not as a subroutine, so will return to fake return address
	bra.b	.next		; ditto
.endif

.if	.tty = 2
	bsr.b	.prev
	bsr.b	.len
	move.l	d1,-(sp)	; stack length
	bsr.b	.next
	bsr.b	.len
	move.l	(sp)+,d6	; get back prev length
	pea	.grad		; to force even central ones to get gradient
	cmp.l	d1,d6		; compare lengths - take longer side
	bpl.b	.prev		; not as a subroutine, so will return to fake return address
	bra.b	.next		; ditto

.len:	sub.l	d3,d1		; performs fast length calc - |x|+|y|/2 - and returns in d1
	bpl.b	.nnd1
	neg.l	d1		; first get |dx|
.nnd1:	sub.l	d4,d6
	bpl.b	.nnd6
	neg.l	d6		; then |dz|
.nnd6:	cmp.l	d1,d6
	bpl.b	.nex1
	exg	d1,d6
.nex1:	asr.l	d1
	add.l	d6,d1		; and finally appx. length
	rts


.endif

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

.left:	bsr.b	.prev		; read previous point
	move.l	d6,-(sp)	; stack z value
	move.l	d1,-(sp)	; stack x value
	bsr.b	.next		; now read next point
	move.l	d6,-(sp)
	move.l	4(sp),d6	; recover previous x value, leaving on stack
	cmp.l	d1,d6		; test them - want the largest = rightmost
	bgt.b	.sk3		; if was larger already then need them back

	move.l	(sp)+,d6	; recover the z value and
	addq.l	#8,sp		; skip the others
	bra.b	.grad

.sk3:	move.l	d6,d1		; replace x in correct register
	addq.l	#8,sp		; repoint stack
	move.l	(sp)+,d6	; regain z value

.con1:	bra.b	.grad		; fake return address takes on to .use

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

.right:	bsr.b	.prev		; read previous point
	move.l	d6,-(sp)	; stack z value
	move.l	d1,-(sp)	; stack x value
	bsr.b	.next		; now read next point
	move.l	d6,-(sp)
	move.l	4(sp),d6	; recover previous x value, leaving on stack
	cmp.l	d6,d1		; test them - want the smallest = leftmost
	bgt.b	.sk4		; if was smaller already then need them back

	move.l	(sp)+,d6	; recover the z value and
	addq.l	#8,sp		; skip the others
	bra.b	.grad

.sk4:	move.l	d6,d1		; replace x in correct register
	addq.l	#8,sp		; repoint stack
	move.l	(sp)+,d6	; regain z value

.con2:	bra.b	.grad		; fake return address takes on to .use

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

; subroutines to read previous/next points into d1,d6

.prev:	move.w	d2,d1		; copy for modifying
	subq.w	#1,d1		; to point at previous point
	bpl.b	.noad
	add.w	d0,d1		; wrap around if -ve
.noad:	asl.w	#3,d1		; scale up by 8 (2 longs)
.read:	movea.l	a2,a3		; regain pointer - note a2 not touched, so can be used again and again ...
	adda.w	d1,a3		; point to previous point data
	move.l	(a3)+,d1	; get x
	move.l	(a3),d6		; get z - no ()+ as no more reads
	rts

.next:	move.w	d2,d1		; copy
	addq.w	#1,d1		; next point
	cmp.w	d0,d1		; test
	bmi.b	.nosub		; limit
	sub.w	d0,d1		; adjust
.nosub:	asl.w	#3,d1		; scale up
	bra.b	.read		; use

; subroutine to calculate gradient for point pair

.grad:	sub.l	d3,d1		; x-change
	sub.l	d4,d6		; z-change

.if	1
	exg	d1,d3
	exg	d6,d4
	trace.w	#'dX'
	trace.l	d3
	trace.w	#'dZ'
	trace.l	d4
	trace.w	#'Zd'
	trace.l	d6

	bsr	get_atan	; returns true angle in d0 - rather than the old tan value, so instead of maximum 16 (=63 degrees)
				; we have maximum $100, so we need to scale down
	trace.w	#'An'
	trace.w	d0
	exg	d1,d3		; return all values to where they started
	exg	d6,d4		; because d3/d4 are used later
	asl.w	#6,d0		; to ensure sign kept
	asr.w	#8,d0		; thus giving a signed 16 bit value from -$80 to +$7f
	asr.w	#1,d0
	ext.l	d0
	move.l	d0,d1
	rts

.else 				; old version
	bne.b	.safe		; avoid divide by zero
	moveq.l	#127,d1		; by putting 127 in as result
	rts

.safe:	moveq.l	#1,d0
	asl.l	#3,d1		; = 8 * x-change
	beq.b	.gx
	divs	d6,d1		; = 8 * x-change / z-change ; >8 means >45 degrees
	tst.w	d1		; check for zero result, in which case the true sign is in the remainder
	bne.b	.nz
	tst.l	d1		; check sign in remainder
.nz:	bpl.b	.dp
	neg.l	d0
.dp:	add.w	d0,d1		; adjust so result is non-zero when small
	ext.l	d1		; this completes the gradient by removing the remainder section
.gx:	rts
.endif
;===============================

				; now use the result which is in d1 - the 'angle' and d4 - the distance

.use:	cmpi.l	#1100,d4	; check that distance to collision is more than one car length
	blt.b	.hlt		; if not then car is already in contact and must get off a.s.a.p.

	move.l	d1,d6		; copy angle
	bpl.b	.sk5		; test
	neg.l	d6		; make absolute
.sk5:	sub.l	#64,d6		; test limit - why 32 though ? trying 64 instead, could go to 128 and still be only 45 degrees
	bmi.b	.bnce		; if under then go to bounce routine

.hlt:	move.l	colhlt.w,d0	; check if current nearest halt event is nearer
	beq.b	.wri
	cmp.l	d0,d4
	bpl.b	.miss		; no change if it is - jump to loop round point
.wri:	move.l	d4,colhlt.w
;	move.l	#'Halt',(a6)+
;	move.l	d4,(a6)+
;	move.l	d1,(a6)+
	bra.b	.miss

.bnce:	move.l	colhlt.w,d0	; get halt distance if any
	beq.b	.nhs		; if none, skip test
	cmp.l	d0,d4		; check distance
	bpl.b	.miss
.nhs:	move.l	colmin.w,d0	; check no bounce has occurred nearer
	beq.b	.pin
	cmp.l	d0,d4
	bpl.b	.nchg		; note that we may still change the angle even if its not the nearest
.pin:	move.l	d4,colmin.w
	move.w	d1,colrot.w
.nchg:

.if 0	; replaced angle now always, when nearer distance is found, so that wierd effects less likely
.nchg:	move.w	colrot.w,d6	; get current 'rotation' value
	beq.b	.wro		; if zero so far, anything must be greater - so no testing
	bpl.b	.sk6
	neg.w	d6
.sk6:	move.w	d1,d4		; copy possible new value
	bpl.b	.sk7
	neg.w	d4
.sk7:	cmp.w	d4,d6		; compare new vs current
	bpl.b	.miss		; d4 is less means less bounce so don't replace
.wro:	move.w	d1,colrot.w	; replace if more bounce
.endif

.miss:	bra	.ol0

.exit:
.if	Trace_on=1
	tst.l	colmin.w
	bne.b	.po
	tst.l	colhlt.w
	beq.b	.ntd
.po:	trace.w	#'Bo'
	trace.w	colmin+2.w
	trace.w	#'Ha'
	trace.w	colhlt+2.w
.endif
.ntd:	movem.l	(sp)+,d0-d6/a0-a3	; restore

	rts

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

cliprot::	; clip a list of (d0) points in 2D, starting at (a2), against z=d1, and rotate and output into (a3)
		; return modified number of points in d0, and leave a2,a3 unchanged (ie restore)
		; all the points are written out as (z,-x)

	tst.w	d0
	beq.b	.ex2

	movem.l	d1-d6/a2-a3,-(sp)	; stack most registers
	move.l	4(a2),d3		; get first z
	moveq.l	#0,d2			; high word of d2 is flags, low word is counter for points output - allowed to run round to d0
	cmp.l	d1,d3			; test for inside or outside
	bpl.b	.ins			; if d3>d1 then inside
	bset.l	#16,d2			; set flag for outside rather than inside
.ins:	cmp.w	d2,d0			; test that points remain to be done
	ble.b	.exit			; if already done all then abandon and return

	move.l	(a2)+,d3		; get an x coordinate
	move.l	(a2)+,d4		; and a z one
.tst:	cmp.l	d1,d4			; test the z
	bpl.b	.inx			; inside
	btst.l	#16,d2			; check flag
	beq.b	.cross			; if was inside then crossover has occurred
	subq.w	#1,d0			; otherwise we have an outside point and so can drop it from the count and get the next
	bne.b	.ins			; read next point if this isn't zero 
					; otherwise we have cleared to no points and can return
.exit:	movea.l	28(sp),a2		; recover the original a3 address - start of list
	move.l	(a2)+,(a3)+		; move the first point onto the end - for wrap around
	move.l	(a2)+,(a3)+
	movem.l	(sp)+,d1-d6/a2-a3	; restore everything except point count
.ex2:	rts

.inx:	btst.l	#16,d2
	bne.b	.cross			; if was outside then crossover has occurred
	move.l	d4,(a3)+		; write out z as new x co-ordinate
	neg.l	d3			; get -x
	move.l	d3,(a3)+		; write -x as new z co-ordinate
	addq.w	#1,d2
	bra.b	.ins

.cross:	move.l	d1,(a3)+		; because z is output as new x, and z is now clipz
	neg.l	d3			; get -x as part of calculation for new clipped x value
	move.l	d3,(a3)			; note not ()+ because need to apply adjustment

	move.l	-12(a2),d5		; get previous z value - for calcing the fractional position
	sub.l	d4,d5			; = zold - znew  
	bsr	scale_down_to_15	; puts scaling amount into d6
	sub.l	d1,d4			; = znew - zclip
	add.l	-16(a2),d3		; = - xnew + xold
	muls	d3,d4			; = (xold - xnew) * (znew - zclip)
	beq.b	.write			; if there is no change anyway, don't bother to continue
	tst.w	d5
	beq.b	.write
	divs	d5,d4			; = ^ / (zold - znew)
	ext.l	d4			; abandon remainder
	asr.l	d6,d4			; re-scale
.write:	add.l	d4,(a3)+		; to give final result
	subq.l	#8,a2			; correct address to force re-read
	addq.w	#1,d2			; increment counter
	bchg.l	#16,d2			; flip flag - now we have changed sides
	bset.l	#17,d2			; test and set flag for having added 2 to point count - always required when cross boundary
	bne.b	.ins
	addq.w	#2,d0			; increment # of points
	bra.b	.ins			; loop round again.


scale_down_to_15:
	moveq.l	#0,d6
	move.l	d5,-(sp)
	bpl.b	.nn
	neg.l	d5
.nn:	asl.l	#2,d5
	swap	d5
.lp:	addq.l	#1,d6
	lsr.w	#1,d5
	bne.b	.lp
	subq.l	#1,d6
	move.l	(sp)+,d5
	asr.l	d6,d5
	rts

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

clip_all:

; prevent z=0 by clipping to 1 - because z = 0 regarded as no collision

	moveq.l	#1,d1		; clip limit (always in z)
	bsr	cliprot		; clip points to limit and rotate

	move.l	#-1024,d1	; next limit
	exg.l	a2,a3		; swap direction
	bsr	cliprot	

	move.l	Gpu_dist,d1	; next limit
	neg.l	d1		; reverse sign
	exg.l	a2,a3
	bsr	cliprot

	move.l	#-1024,d1	; next limit
	exg.l	a2,a3		; swap direction
	bsr	cliprot	

	rts

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

boundary_transform::			; take a list of d5 points from (a0) onwards and output to tmpstor1
					; transformed from viewpoint space to car space - this needs to be generalised for other vehicles
	movea.l	#tmpstor1,a2
	move.w	trailer.w,d2	; offset when viewpoint is not in car
	ext.l	d2
	bra	.dbx

.il:	move.l	(a0)+,d3	; read x co-ordinate
	move.l	(a0)+,d4	; read z co-ordinate
	sub.l	d2,d4		; viewpoint offset in z only

.if 0
	tst.b	rotmode.w
	bne.b	.old

	movea.l	#Gpu_data,a1
	move.l	d3,(a1)+
	move.l	d4,(a1)

	move.l	a0,-(sp)
	RunGPU	16
	waitgpu
	move.l	(sp)+,a0

	subq.l	#4,a1
	move.l	(a1)+,(a2)+
	move.l	(a1)+,(a2)+
	bra.b	.dbx
.endif

.old:	move.l	#$7fff,d0
	cmp.l	d3,d0
	bpl.b	.f1
	move.l	d0,d3
.f1:	not.l	d0
	cmp.l	d3,d0
	bmi.b	.f2
	move.l	d0,d3

.f2:	cmp.l	d4,d0
	bmi.b	.f3		; note that minus test is first, because 
	move.l	d0,d4		; we didn't reverse the NOT in the first bit
.f3:	not.l	d0
	cmp.l	d4,d0
	bpl.b	.f4
	move.l	d0,d4

.f4:	move.l	d3,d0		; copy for multiplication
	move.l	d4,d6
	muls	zmul.w,d0
	muls	xmul.w,d6
	sub.l	d6,d0
	swap	d0
	ext.l	d0
	asl.l	#2,d0		; final rotated x coordinate
	move.l	d0,(a2)+	; write out
	move.l	d3,d0
	move.l	d4,d6
	muls	xmul.w,d0
	muls	zmul.w,d6
	add.l	d6,d0
	swap	d0
	ext.l	d0
	asl.l	#2,d0		; final rotated z coordinate
	move.l	d0,(a2)+	; write out

.dbx:	dbra	d5,.il		; continue with remaining points	

	rts


nearest_vertex:

	move.l	a3,a2		; copy for subsequent use
	move.l	d0,d2		; keep copy of point count
	swap	d2		; push away until needed
	move.w	d0,d2		; copy as a counter/pointer
	subq.w	#1,d2		; reduce for logical reasons
	subq.l	#2,d0		; because of dbra use and self-contained testing
	move.l	(a3)+,d3	; first and thus closest so far - x
	move.l	(a3)+,d4	; do - z

.lp:	move.l	(a3)+,d1	; get another x value, but its only to replace if the new z is lower
	move.l	(a3)+,d6	; get another z value, for testing
	cmp.l	d6,d4
	bmi.b	.nzs		; if bigger, ignore
	move.w	d0,d2		; change pointer/counter
	move.l	d1,d3		; replace current closest points
	move.l	d6,d4	
.nzs:	dbra	d0,.lp

	rts

.if 0
pclip::	movem.l	d0-d2/a0-a3,-(sp) ;not used

	addq.w	#1,collision_test.w

	movea.l	collide_table,a1	; collision zones table - for all 'possible' hits
					; note first entry is always pointer to first empty hole
	movea.l	#point_3d,a3		; copy retained for rapid reuse
	move.l	(a2)+,a0		; pointer to boundary data
	move.l	(a0)+,d0		; number of bounding polygons included
	moveq.l	#0,d1			; so words can be used as longs
	bra	.dbl1

.ol0:	move.w	(a0)+,d1		; read point count
	move.l	d1,(a1)+

	bra	.dbl2

.ol1:	move.w	(a0)+,d2		; read point number
	asl.w	#4,d2			; scale up
	movea.l	a3,a2
	adda.w	d2,a2
	move.l	(a2),(a1)+		; store x coord
	addq.l	#8,a2
	move.l	(a2),(a1)+		; store z coord

.dbl2:	dbra	d1,.ol1

.dbl1:	dbra	d0,.ol0

	move.l	a1,collide_table	; update pointer
	movem.l	(sp)+,d0-d2/a0-a3
	rts

.endif


