; THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
; SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
; END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
; ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
; IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
; SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
; FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
; CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
; AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
; COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
DONT_USE_UPOLY = 1

.386
	option	oldstructs

	.nolist
	include	pstypes.inc
	include	psmacros.inc
	include	gr.inc
	include	3d.inc
	.list

	assume	cs:_TEXT, ds:_DATA


_DATA	segment	dword public USE32 'DATA'

rcsid	db	"$Id: draw.asm 1.33 1996/02/14 09:59:13 matt Exp $"
	align	4

tempv	vms_vector <>

n_verts	dd	?
n_verts_4	dd	?	; added by mk, 11/29/94, point coding optimization.
bitmap_ptr	dd	?

uvl_list_ptr	dd	?

tmap_drawer_ptr	dd	draw_tmap_
flat_drawer_ptr	dd	gr_upoly_tmap_
line_drawer_ptr	dd	gr_line_

_DATA	ends



_TEXT	segment	dword public USE32 'CODE'

	extn	gr_upoly_tmap_
	extn	draw_tmap_

;specifies new routines to call to draw polygons
;takes eax=ptr to tmap routine, edx=ptr to flat routine, ebx=line rtn
g3_set_special_render:
	or	eax,eax
	jnz	got_tmap_ptr
	lea	eax,cs:draw_tmap_
got_tmap_ptr:	mov	tmap_drawer_ptr,eax

	or	edx,edx
	jnz	got_flat_ptr
	lea	edx,cs:gr_upoly_tmap_
got_flat_ptr:	mov	flat_drawer_ptr,edx

	or	ebx,ebx
	jnz	got_line_ptr
	lea	ebx,cs:gr_line_
got_line_ptr:	mov	line_drawer_ptr,ebx

	ret


;alternate entry takes pointers to points. esi,edi = point pointers
g3_draw_line:
	;;ifndef	NDEBUG
	;; mov	ax,_Frame_count	;curren frame
	;; cmp	ax,[esi].p3_frame	;rotated this frame?
	;; break_if	ne,'Point not rotated in draw_line'
	;; cmp	ax,[edi].p3_frame	;rotated this frame?
	;; break_if	ne,'Point not rotated in draw_line'
	;;endif

	mov	al,[esi].p3_codes
	mov	ah,[edi].p3_codes

;check codes for reject, clip, or no clip

	test	al,ah	;check codes_and
	jnz	no_draw_line	;both off same side

	or	al,ah	;al=codes_or
	js	must_clip_line	;neg z means must clip
	
	test	[esi].p3_flags,PF_PROJECTED
	jnz	p0_projected
	call	g3_project_point
p0_projected:	test	[esi].p3_flags,PF_OVERFLOW
	jnz	must_clip_line

	test	[edi].p3_flags,PF_PROJECTED
	jnz	p1_projected
	xchg	esi,edi	;get point in esi
	call	g3_project_point
	xchg	esi,edi
p1_projected:	test	[edi].p3_flags,PF_OVERFLOW
	jnz	must_clip_line

	pushm	ebx,ecx,edx	;save regs

	test	al,al	;check codes or

	mov	eax,[esi].p3_sx
	mov	edx,[esi].p3_sy

	mov	ebx,[edi].p3_sx
	mov	ecx,[edi].p3_sy

	jz	unclipped_line	;cc set from test above

;call clipping line drawer
;;	call	gr_line_	;takes eax,edx,ebx,ecx
 push esi
 mov esi,line_drawer_ptr
 call esi
 pop esi
	popm	ebx,ecx,edx
	ret	;return value from gr_line

;we know this one is on screen
unclipped_line:	;;call	gr_uline_	;takes eax,edx,ebx,ecx
;; call gr_line_
 push esi
 mov esi,line_drawer_ptr
 call esi
 pop esi
	popm	ebx,ecx,edx
	mov	al,1	;definitely drew
	ret

;both points off same side, no do draw
no_draw_line:	xor	al,al	;not drawn
	ret

;jumped here from out of g3_draw_line. esi,edi=points, al=codes_or
must_clip_line:	call	clip_line	;do the 3d clip
	call	g3_draw_line	;try draw again

;free up temp points
	test	[esi].p3_flags,PF_TEMP_POINT
	jz	not_temp_esi
	call	free_temp_point
not_temp_esi:	test	[edi].p3_flags,PF_TEMP_POINT
	jz	not_temp_edi
	xchg	esi,edi
	call	free_temp_point
not_temp_edi:
	check_free_points

	ret	;ret code set from g3_draw_line


;returns true if a plane is facing the viewer. takes the unrotated surface 
;normal of the plane, and a point on it.  The normal need not be normalized
;takes esi=vector (unrotated point), edi=normal. 
;returns al=true if facing, cc=g if facing
;trashes esi,edi
g3_check_normal_facing:
	push	edi	;save normal
	lea	eax,tempv
	mov	edi,esi
	lea	esi,View_position
	call	vm_vec_sub

	mov	esi,eax	;view vector
	pop	edi	;normal
	call	vm_vec_dotprod

	or	eax,eax	;check sign
	setg	al	;true if positive

	ret

;see if face is visible and draw if it is.
;takes ecx=nv, esi=point list, edi=normal, ebx=point.
;normal can be NULL, which will for compution here (which will be slow).
;returns al=-1 if not facing, else 0
;Trashes ecx,esi,edx,edi
g3_check_and_draw_poly:
	call	do_facing_check
	or	al,al
	jnz	g3_draw_poly
	mov	al,-1
	ret

g3_check_and_draw_tmap:
	push	ebx
	mov	ebx,eax
	call	do_facing_check
	pop	ebx
	or	al,al
	jnz	g3_draw_tmap
	mov	al,-1
	ret


;takes edi=normal or NULL, esi=list of vert nums, ebx=pnt
;returns al=facing?
do_facing_check:
	test	edi,edi	;normal passed?
	jz	compute_normal	;..nope

;for debug, check for NULL normal
	ifndef	NDEBUG
	 mov	eax,[edi].x
	 or	eax,[edi].y
	 or	eax,[edi].z
	 break_if	z,'Zero-length normal in do_facing_check'
	endif

;we have the normal. check if facing
	push	esi
	mov	esi,ebx
	call	g3_check_normal_facing	;edi=normal
	pop	esi
	setg	al	;set al true if facing
	ret

;normal not specified, so must compute
compute_normal:
	pushm	ebx,ecx,edx,esi

;get three points (rotated) and compute normal

	mov	eax,[esi]	;get point
	mov	edi,8[esi]	;get point
	mov	esi,4[esi]	;get point

	lea	ebx,tempv
	call	vm_vec_perp	;get normal
	mov	edi,eax	;normal in edi
	call	vm_vec_dotprod	;point in esi

	or	eax,eax	;check result
	popm	ebx,ecx,edx,esi
	sets	al	;al=true if facing
	ret


;draw a flat-shaded face.  returns true if drew
;takes ecx=nv, esi=list of pointers to points
;returns al=0 if called 2d, 1 if all points off screen  
;Trashes ecx,esi,edx
g3_draw_poly:
	pushm	ebx,edi

	lea	edi,Vbuf0	;list of ptrs
	xor	ebx,ebx	;counter
	mov	dx,0ff00h	;init codes

codes_loop:	mov	eax,[esi+ebx*4]	;get point number
	mov	[edi+ebx*4],eax	;store in ptr array

	;;ifndef	NDEBUG
	;; push	ebx
	;; mov	bx,_Frame_count	;curren frame
	;; cmp	bx,[eax].p3_frame	;rotated this frame?
	;; break_if	ne,'Point not rotated in draw_poly'
	;; pop	ebx
	;;endif

	and	dh,[eax].p3_codes	;update codes_and
	or	dl,[eax].p3_codes	;update codes_or

	inc	ebx
	cmp	ebx,ecx	;done?
	jne	codes_loop	;..nope

;now dx = codes
	test	dh,dh	;check codes_and
	jnz	face_off_screen	;not visible at all

	test	dl,dl	;how about codes_or
	js	must_clip_face	;neg z means must clip
 jnz must_clip_face

;reentry point for jump back from clipper
draw_poly_reentry:
	push	edx	;save codes_or

;now make list of 2d coords (& check for overflow)

	mov	edx,edi	;edx=Vbuf0
	xor	ebx,ebx
	lea	edi,Vertex_list

coords_loop:	mov	esi,[edx+ebx*4]	;esi = point
	test	[esi].p3_flags,PF_PROJECTED
	jnz	pnt_projected
	call	g3_project_point
pnt_projected:	test	[esi].p3_flags,PF_OVERFLOW
	jnz	must_clip_face2
	mov	eax,[esi].p3_sx
	mov	[edi+ebx*8],eax
	mov	eax,[esi].p3_sy
	mov	4[edi+ebx*8],eax

	inc	ebx
	cmp	ebx,ecx
	jne	coords_loop

	mov	eax,ecx	;eax=nverts
	mov	edx,edi	;edx=vertslist

;check for trivial accept
	pop	ebx	;get codes_or
ife DONT_USE_UPOLY
	test	bl,bl
	jz	no_clip_face
endif
	;;call	gr_poly_	;takes eax,edx
;;; call gr_upoly_tmap_
 push ecx
 mov ecx,flat_drawer_ptr
 call ecx
 pop ecx

	popm	ebx,edi
	xor	al,al	;say it drew
	ret

;all on screen.  call non-clipping version
no_clip_face:	;;call	gr_upoly_	;takes eax,edx
;; call gr_poly_
 call gr_upoly_tmap_
	popm	ebx,edi
	xor	al,al	;say it drew
	ret

;all the way off screen. return
face_off_screen:	popm	ebx,edi
	mov	al,1	;no draw
	ret

;we require a 3d clip
must_clip_face2:	pop	edx	;get codes back
must_clip_face:	lea	esi,Vbuf0	;src
	lea	edi,Vbuf1	;dest
	mov	al,dl	;codes in al
	call	clip_polygon	;count in ecx

	mov	edi,esi	;new list in edi

;clipped away?
	jecxz	clipped_away
	or	dh,dh	;check codes_and
	jnz	clipped_away
 test dl,dl	;check codes or
 js clipped_away	;some points still behind eye

	push	edi	;need edi to free temp pnts
	push	offset cs:reentry_return
	pushm	ebx,edi	;match what draw has pushed
	jmp	draw_poly_reentry

reentry_return:
	pop	edi
clipped_away:	
	push	eax	;save ret codes
;free temp points
	xor	ebx,ebx
free_loop:	mov	esi,[edi+ebx*4]	;get point
	test	[esi].p3_flags,PF_TEMP_POINT
	jz	not_temp
	call	free_temp_point
not_temp:	inc	ebx
	cmp	ebx,ecx
	jne	free_loop

	check_free_points

	pop	eax	;get ret codes back

	popm	ebx,edi
	ret


;draw a texture-mapped face.  returns true if drew
;takes ecx=nv, esi=point list, ebx=uvl_list, edx=bitmap
;returns al=0 if called 2d, 1 if all points off screen  
;Trashes ecx,esi,edx
g3_draw_tmap:
	mov	bitmap_ptr,edx	;save

	pushm	ebx,edi

	lea	edi,Vbuf0	;list of ptrs
	push	ebp	;save ebp
	mov	ebp,ebx	;ebp=uvl list
 mov uvl_list_ptr, ebx
	mov	n_verts,ecx	;save in memory
	shl	ecx, 2

;loop to check codes, make list of point ptrs, and copy uvl's into points
	mov	dh, 0ffh	; init codes (this pipelines nicely)
	mov	n_verts_4, ecx
	xor	ebx, ebx
	xor	dl, dl	; init codes (this pipelines nicely)

t_codes_loop:	mov	eax,[esi+ebx]	;get point number
	mov	[edi+ebx],eax	;store in ptr array

	and	dh,[eax].p3_codes	;update codes_and
	or	dl,[eax].p3_codes	;update codes_or

	mov	ecx,[ebp]	;get u
	add	ebp, 4	; (this pipelines better ..mk, 11/29/94 ((my 33rd birthday...)))
	mov	[eax].p3_u,ecx
	mov	ecx,[ebp]	;get v
	add	ebp, 4
	mov	[eax].p3_v,ecx
	mov	ecx,[ebp]	;get l
	add	ebp, 4
	mov	[eax].p3_l,ecx
	or	[eax].p3_flags,PF_UVS + PF_LVS	;this point's got em

	add	ebx,4
	cmp	ebx, n_verts_4
	jne	t_codes_loop	;..nope

	pop	ebp	;restore ebp
;now dx = codes
	test	dh,dh	;check codes_and
	jnz	t_face_off_screen	;not visible at all

	test	dl,dl	;how about codes_or
	jnz	t_must_clip_face	;non-zero means must clip

;reentry point for jump back from clipper
t_draw_poly_reentry:

;make sure all points projected
	mov	edx,edi	;edx=Vbuf0
	xor	ebx,ebx

t_proj_loop:	mov	esi,[edx+ebx*4]	;esi = point

	;;ifndef	NDEBUG
	;; mov	ax,_Frame_count	;curren frame
	;; cmp	ax,[esi].p3_frame	;rotated this frame?
	;; break_if	ne,'Point not rotated in draw_tmap'
	;;endif

	test	[esi].p3_flags,PF_PROJECTED
	jnz	t_pnt_projected
	call	g3_project_point
t_pnt_projected:
	test	[esi].p3_flags,PF_OVERFLOW
	break_if	nz,'Should not overflow after clip'
	jnz	t_face_off_screen

	inc	ebx
	cmp	ebx,n_verts
	jne	t_proj_loop

;now call the texture mapper
	mov	eax,bitmap_ptr	;eax=bitmap ptr
	mov	edx,n_verts	;edx=count
	mov	ebx,edi	;ebx=points
;;;	call	draw_tmap_
 push ecx
 mov ecx,tmap_drawer_ptr
 call ecx
 pop ecx
	popm	ebx,edi
	xor	al,al	;say it drew
	ret

;all the way off screen. return
t_face_off_screen:	popm	ebx,edi
	mov	al,1	;no draw
	ret

;we require a 3d clip
t_must_clip_face:	lea	esi,Vbuf0	;src
	lea	edi,Vbuf1	;dest
	mov	al,dl	;codes in al
	mov	ecx,n_verts
	call	clip_polygon	;count in ecx
	mov	n_verts,ecx

	mov	edi,esi	;new list in edi

;clipped away?
	jecxz	t_clipped_away
	or	dh,dh	;check codes_and
	jnz	t_clipped_away
 test dl,dl	;check codes or
 js t_clipped_away	;some points still behind eye

	push	edi	;need edi to free temp pnts
	push	offset cs:t_reentry_return
	pushm	ebx,edi	;match what draw has pushed
	jmp	t_draw_poly_reentry

t_reentry_return:
	pop	edi
t_clipped_away:	
; free temp points
	mov	ebx, ecx
	push	eax	;save ret code
	dec	ebx
	shl	ebx, 2

t_free_loop:	mov	esi,[edi+ebx]	;get point
	test	[esi].p3_flags,PF_TEMP_POINT
	jz	t_not_temp
	call	free_temp_point
t_not_temp:	sub	ebx, 4
	jns	t_free_loop

	check_free_points

	pop	eax	;get ret code

	popm	ebx,edi
	ret



;draw a sortof-sphere. takes esi=point, ecx=radius
g3_draw_sphere:	test	[esi].p3_codes,CC_BEHIND
	jnz	reject_sphere

	test	[esi].p3_flags,PF_PROJECTED
	jnz	sphere_p_projected
	call	g3_project_point
sphere_p_projected:
	test	[esi].p3_flags,PF_OVERFLOW
	jnz	reject_sphere


;calc radius.  since disk doesn't take width & height, let's just
;say the the radius is the width

	pushm	eax,ebx,ecx,edx

	mov	eax,ecx
	fixmul	Matrix_scale.x
	imul	Canv_w2
sphere_proj_div:	divcheck	[esi].z,sphere_div_overflow_handler
	idiv	[esi].z

	mov	ebx,eax

	mov	eax,[esi].p3_sx
	mov	edx,[esi].p3_sy
	call	gr_disk_

sphere_div_overflow_handler:

	popm	eax,ebx,ecx,edx


reject_sphere:	ret


_TEXT	ends

	end
