FILLED VECTORS (PARTE 2)


Nella scorsa puntata abbiamo visto come fuziona il doppio buffer, come ruotare punti tridimensionali su 3 assi e come effettuare lo zoom. In questo articolo vedremo l'uso dello z-sorting e come disegnare con il blitter.

Z-SORTING

Questa routine serve per dare una priorita' di disegno alle facce; in qualunque modo il nostro oggetto sia ruotato verra' disegnato sempre correttamente.

La rimozione delle superfici nascoste e' basata su algoritmi ben precisi, non facili da implementare e raramente sono eseguibili alle velocita' richieste da un demo o da un gioco. La rimozione delle superfici nascoste si divide in due fasi. La prima prevede la rimozione delle facce completamente nascoste (HIDDEN FACES). Le faccie nascoste sono chiamate cosi' perche' non possono essere viste dall'osservatore; per scovarle e' sufficiente calcolare l'angolo formato dalla normale alla superficie e dal vettore della vista. Se questo angolo e' compreso tra 90 gradi e -90 gradi, la faccia e' visibile; se l'angolo e' superiore a 90 gradi o inferiore a -90 gradi, la faccia e' nascosta.

Per calcolare la normale alla superficie e' sufficiente calcolare il prodotto incrociato di due suoi lati. Il vettore della vista si calcola congiungendo il punto di osservazione con il punto di cui si e' calcolata la normale alla superficie.

Una volta eliminate le faccie completamente invisibili si dovra' disegnare le faccie visibili in modo corretto. Per questo compito esistono due algoritmi: l'algoritmo del buffer Z e quello del pittore. Entrambi gli algorimti non possono essere inseriti in un demo in maniera standard, vediamo quindi una piccola descrizione ed una possibile implementazione.

L'algoritmo del buffer Z e' cosi struttutturato:

- date le coordinate di una faccia, bisogna calcolare un valore Z per ogni
  coordinata X e Y, memorizzare in un buffer in valore Z calcolato e il
  colore della faccia
- cercare il valore Z piu' vicino all'osservatore, si confronta questo valore
  con quello contenuto nel buffer Z (a parita' di X e Y) e se piu' vicino
  all'osservatore si sovrascive al precedente; in caso contrario si salta
  l'operazione. Ripetere l'operazione per tutti i pixel nello screen.
- a questo punto per ogni coordinata X e Y abbiamo una coordianta Z (quella
  piu' vicina all'osservatore) ed un colore (quello del pixel), sara'
  sufficiente disegnarlo

L'algoritmo del pittore e' basato su una serie di controlli:

1- le estensioni lungo l'asse X di due facce si sovrappongono ?
   se no -> fine controlli, la priorita' di disegno non conta
   se si -> controllo 2
2- le estensioni lungo l'asse X di due facce si sovrappongono ?
   se no -> fine controlli
   se si -> controllo 3
3e4 - estendete un poligono all'infinito e avrete un piano: l'altro poligono
      si trova interamente da una sola parte (non importa quale) ?
   se si -> fine controlli
   se no -> controllo 5
5- ora sappiamo che le faccie sono in posizione scorretta; sara' necessario
   invertire l'ordine di disegno di queste due faccie.

Data la quantita' di calcoli e visto che i nostri amati Amiga non hanno ancora un PowerPC 604 a 200MHz, sconsiglio a chiunque di usare tali algoritmi mentre consiglio di dare un'occhiata a quello che presento io.

Quello che segue non e' un mio algoritmo ma solo l'algoritmo del buffer Z modificato in modo che sia il piu' veloce possibile.

Possiamo dividere la routine in due parti. La prima prende tutti i punti che compongono una determinata faccia, somma tutte le Z e divide il risultato per il numero di punti (Z media dei punti che conpongono la faccia).

La seconda riordina le faccia in modo che quelle piu' lontane dall'osservatore siano visualizzate per prime. In questo modo se un oggetto in primo piano ne oscura un altro, non avremo problemi di nessun genere; vedremo il nostro oggetto in primo piano e in lontananza tutto il resto (magari un paesaggio).

Immaginate un caso in cui non fosse presente questa routine: le faccie sarebbero disegnate sempre nello stesso ordine (imposto da noi!). Immaginiamo ora un bosco pieno di alberi; l'albero in primo piano deve coprire gli alberi piu' lontani (quindi deve essere disegnato per ultimo!). Se impostassimo questo ordine di disegno gia' dalla partenza tutto funzionerebbe solo fino a quando non ruotiamo il nostro bosco. Dato che l'ordine di disegno non cambia, avremo la sequenza di disegno delle faccie uguale a prima; nel caso ipotetico in cui ci troviamo dalla parte opposta del bosco e guardiamo il punto da cui siamo partiti (Y=180 gradi), sempre con il medesimo ordine di disegno: cosa accadra'? Innanzitutto verrebbero disegnati gli alberi piu' lontani (essendo tutto ruotato di 180 gradi, quelli piu' vicini a noi!!!) e per ultimi i piu' vicini (quelli piu' lontano da noi!!!). La schermata risultante potrebbe essere la seguente: alberi in primo piano coperti parzialmente (per le dimensioni, piu' sono lontano e piu' sono piccoli) da alberi in lontananza. Come vedete non quadra nulla. Questo e' sono un piccolo esempio delle potenzialita' del riordinamento in base al buffer Z.

Attenzione : non sempre e' necessario.

Esistono molti casi in cui si puo' eliminare il riordinamento in base al buffer Z, risparmiando parecchio tempo macchima: in questi casi l'ordine di disegno delle faccie e' irrilevante. Abbiamo detto che si puo' evitare il riordinamento su alcuni oggetti: innanzitutto devono essere CHIUSI e in secondo luogo devono essere semplici. Facciamo un esempio con una sfera: prima di tutto le faccie nascoste non verranno mai disegnate (importante!!!) e successivamente non avra' nessuna importanza l'ordine di disegno (in tutti i casi possibili le faccie non si sovrapporranno mai). Gli oggetti devono essere chiusi: se un lato fosse aperto, si renderebbe necessario disegnare anche le faccie nascoste, di conseguenza essendoci le faccie in primo piano che coprono quelle dietro, si renderebbe necessario lo Z-buffer. Infine devono essere semplici in quanto se disegnassimo 2 sfere anziche' 1, non possiamo permetterci che le faccie siano disegnate a caso.

**************************** parte 1 *********************************
CalcPoints:
; media delle Z di tutte le facce
	move.l	Dlines,a0		; come unire le faccie
	move.l	dplane,a1		; numero punti per faccia, colore
	lea	Intz(pc),a2		; valore di Z di tutti i punti
	lea	BufferFace(pc),a3	; buffer vuoto
	move.w	dface,d6		; numero faccie
	moveq	#0,d0
	moveq	#0,d1
LpLps:	move.w	(a1),d0			; numero punti che formano faccia
	add.w	#4,a1
	move.w	d0,d1			; d1 = d0
	subq.w	#1,d0			; d0 = -1 per dbf
	moveq	#0,d3
SomaLp:	move.w	(a0),d2			; numero punto
	add.w	#4,a0
	add.w	d2,d2			; * 2 perche' word
	add.w	(a2,d2.w),d3		; coordinata Z
	dbf	d0,SomaLp
	ext.l	d3
	divs.w	d1,d3			; numero punti / somma delle Z
	move.w	d3,(a3)+		; salva media
	dbf	d6,LpLps


**************************** parte 2 *********************************
; calcola la Z ma delle facce (non punti)
	lea	BufferFace(pc),a0	; media Z di tutte le faccie
	lea	FacceSeq(pc),a1		; buffer vuoto
	movea.l	a0,a2
	move.w	dface,d4		; numero faccie
	move.w	d4,d5			; numero faccie
	move.w	d5,d6			; numero faccie
	subq.w	#1,d6			; numero faccia -1 per dbf
	move.w	d6,d7			; numero faccie -1 per dbf
	add.w	d5,d5			; numero faccie *2
	addq.w	#2,d5			; +2 perche' -(a1) prima decremento
					; poi inserimento valore in a1
	add.w	d5,a1			; ultimo valore
	move.w	#$9999,d5		; $9999 = -26215 valore Z piccolissimo
Calcs:	move.w	d7,d6			; numero faccie -1 per dbf
	moveq	#2,d2			; prima faccia da inserire (1)(2=WORD)
	moveq	#0,d3			, faccia di partenza (0)
	move.w	(a0)+,d0		; valore Z
Calc:	move.w	(a0)+,d1		; valore Z 2
	cmp.w	d0,d1			; se Z2 piu' piccolo di Z1
	blt.s	NoEx			; salta NoEx
	move.w	d1,d0			; valore Z piu' grande di prima
	move.w	d2,d3			; numero faccia con Z grande
NoEx:	addq.w	#2,d2			; numero faccia +2 (word)
	dbf	d6,Calc
	move.w	d3,-(a1)		; numero faccia da disegnare
	movea.l	a2,a0
	move.w	d5,(a0,d3.w)		; valore Z piccolissimo
	dbf	d4,Calcs
	rts

DISEGNO DELL'OGGETTO

Gli indirizzi assoluti intermedi tra $100 e $200 non devono mai essere usati !! Sono stati usati da me in questa vecchia routine perche' quando l'ho scritta (1990) avevo un Amiga 500 e, su A500 - A2000 e A3000 questi indirizzi erano sempre vuoti e quindi usabili come accesso a word anziche a long (piu' veloce). Ora con gli Amiga AGA, questi indirizzi sono usati, pena un possibile malfunzionamento del sistema all'uscita del demo.

Data la quantita' di tempo passata (circa 6 anni), non ricordo esattamente come funzionano tutte le routine, cerchero' di spiegarle come meglio posso.

Display:
	clr.w	$120.w
	clr.w	$122.w
	clr.w	$124.w
	move.l	#0,a1
	move.w	ScrClrNow,a1
	move.w	#0,(a1)+		; cancella $150
	move.w	#$7fff,(a1)+		; max. valore in $152
	move.w	#0,(a1)+		; cancella $154
	move.w	#$7fff,(a1)+		; max. valore in $156
	move.l	Dlines,a6


** questa e' la ruotine principale, viene eseguita 1 volta per ogni faccia

	lea	$dff000,a6
	move.l	Bm2_PL1,d1
	move.w	dpunti,d6		; numero punti
	lea	UltimateFace(pc),a1	; buffer vuoto per mettere il numero
                                        ; corrispondente alle faccie che
                                        ; saranno disegnate per ultime
	lea	IntY1(pc),a2		; Y
	lea	IntX1(pc),a3		; X
	lea	FacceSeq(pc),a0		; sequenza delle faccie
	moveq	#0,d5
	move.w	dface,d5
Face:
	move.l	dplane,a5
	move.l	Dlines,a4
	move.w	(a0)+,d0
	lsr.w	#1,d0
	subq.w	#1,d0
	moveq	#0,d3
	moveq	#0,d4
	cmp.w	#-1,d0
	beq	EAddr
Addr:	add.w	(a5),d3
	add.w	#4,a5
	addq	#1,d4
	dbf	d0,Addr
EAddr:	add.w	d3,d3
	add.w	d3,d3
	add.w	d4,d4
	move.w	d4,$130.w
	bsr	Againdopo
	dbf	d5,Face
	tst.w	$120.w
	bne	HoHo
	rts

** questa routine viene eseguita solo se sono presenti alcune faccie che devono
** essere disegnate per ultime. La ruotine viene eseguita per ultima.

HoHo:	move.w	#1,$122.w
	move.w	$124.w,d5
	subq	#1,d5
	lea	UltimateFace(pc),a1
Face2:	move.l	dplane,a5
	move.l	Dlines,a4
	move.w	(a1)+,d0
	lsr.w	#1,d0
	subq.w	#1,d0
	moveq	#0,d3
	moveq	#0,d4
	cmp.w	#-1,d0
	beq	EAddr2
Addr2:	add.w	(a5),d3
	add.w	#4,a5
	addq	#1,d4
	dbf	d0,Addr2
EAddr2:	add.w	d3,d3
	add.w	d3,d3
	add.w	d4,d4
	move.w	d4,$130.w
	bsr	Againdopo
	dbf	d5,Face2
	rts

againdopo:	; calcola il punto di partenza per i dati in Lines2
	movem.l	a0-a3/d5,-(sp)
	clr.w	Out
	lea	BufferLine(pc),a1
	add.w	d3,a4
	move.w	d3,$104.w
	move.w	2(a5),$106.w
	move.w	(a5),d6
	move.w	d6,$10e.w
	subq.w	#1,d6

; Mette in BufferLine le coordinate X,Y di tutti i punti che compongono
; una faccia im modo da averli tutti in sequenza

DlpLop:	movem.w	(a4)+,d2/d3
	add.w	d2,d2
	add.w	d3,d3
	move.w	(a3,d2.w),(a1)+	; x1
	move.w	(a2,d2.w),(a1)+	; y1
	move.w	(a3,d3.w),(a1)+	; x2
	move.w	(a2,d3.w),(a1)+	; y2
	dbf	d6,DlpLop

; controlla se in Priority c'e' 0 o 1
; se 1 la faccia e' disegnata per ultima

	move.l	DPriority,a1
	move.w	$130.w,d2
	move.w	(a1,d2.w),d2
	cmp.w	#1,d2		; disegna tutto
	beq	Dloop2
	tst.w	$122.w		; se deve disegnare
	bne	Saltoo		; le faccia davanti
	cmp.w	#2,d2		; ultime le facce davanti
	beq	Ultima
; calcola se e' una linea
Saltoo:	cmp.w	#1,$10e.w	; disegna solo 1 linea
	beq	OnlyLine

; calcola se la faccia e nascosta (YEAAA!!!)
	lea	BufferLine(pc),a1
	movem.w	(a1),d0-d3	; d0=x0 d1=y0 d2=x1 d3=y1 d4=x2 d5=y2
	movem.w	12(a1),d4-d5
	sub.w	d0,d2		;
	sub.w	d0,d4		;
	sub.w	d1,d3		; prodotto incrociato di 2 lati adiacenti
	sub.w	d1,d5		;
	muls.w	d2,d5		;
	muls.w	d3,d4		;

** in base a cui e' disegnato esegue: BLT (less than) oppure BGT (great than)

	tst.w	$132.w
	bne.s	blts
	sub.l	d4,d5
	bgt.s	dloop2
	lea	$dff000,a6
	bra	NoFace		; faccia nascosta !
blts:	sub.l	d4,d5
	blt.s	dloop2
	lea	$dff000,a6
	bra	NoFace		; faccia nascosta !

** la faccia e' visibile !!! Disegnala ora !

Dloop2:	; ora si hanno tutti i punti della faccia in BufferLine
	bsr	CalcScr		; calcola X e Y nello schermo per clipping
	bsr.l	CalcPoits	; calcola Xmax,Ymax,Xmin,Ymin
	bsr.l	TCalcAll	; Calcola Xmax,Ymax,Xmin,Ymin di tutto
	bsr.L	CalcArea	; calcola size,mod.,....
	bsr.l	Lineeee		; disegna linee
	bsr.l	Fillare		; filla tra le linee
	bsr.l	Copyarea	; calcola disegno
	lea	-$58(a6),a6	; a6 = $dff000
	bsr.l	Clerorno	; cancello buffer dove ho disegnato
NoFace:	move.w	#$400,$96(a6)
	movem.l	(sp)+,a0-a3/d5
	rts
Ultima:
	movem.l	(sp)+,a0-a3/d5
	move.w	-2(a0),(a1)+
	move.w	#1,$120.w
	add.w	#1,$124.w
	rts

*** calcola il clipping per lo screen ( max size = 320*256 )
CalcScr:
	move.w	$10e,d6
	add.w	d6,d6
	subq.w	#1,d6
	lea	BufferLine(pc),a0
	lea	BufferLine2(pc),a1
Clip:	movem.w	(a0)+,d0-d1
	cmp.w	#0,d0
	bge.s	Ok0
	move.w	#0,(a1)+
	move.w	#1,Out
	bra.s	Di1
Ok0:	cmp.w	#320,d0
	ble.s	Ok2
	move.w	#320,(a1)+
	bra.s	Di1
Ok2:	move.w	d0,(a1)+
Di1:	cmp.w	#0,d1
	bge.s	Ok3
	move.w	#0,(a1)+
	bra.s	Di2
Ok3:	cmp.w	#256,d1
	ble.s	Ok4
	move.w	#256,(a1)+
	bra.s	Di2
Ok4:	move.w	d1,(a1)+
Di2:	dbf	d6,Clip
	rts


*** calcola Xmax, Xmin, Ymax, Ymin di tutto l'oggetto, serve per sapere la
*** dimensione da cancellare (inutile cancellare tutto)
TCalcAll:
	lea	$140.w,a2
	move.w	ScrClrNow,a3
	moveq	#0,d3
	move.w	(a2)+,d3
	cmp.w	(a3)+,d3
	blt	TCalcPoits2
	move.w	d3,-2(a3)
TCalcPoits2:
	move.w	(a2)+,d3
	cmp.w	(a3)+,d3
	bgt	TCalcPoits3
	move.w	d3,-2(a3)
TCalcPoits3:
	move.w	(a2)+,d3
	cmp.w	(a3)+,d3
	blt	TCalcPoits4
	move.w	d3,-2(a3)
TCalcPoits4:
	move.w	(a2)+,d3
	cmp.w	(a3)+,d3
	bgt	TCalcPoits5
	move.w	d3,-2(a3)
TCalcPoits5:
	rts


*** calcola la SIZE del blitter e i suoi moduli
CalcArea:
; calcola size
	lea	Dax(pc),a3
	movem.w	$140.w,d0-d3
	sub.w	d3,d2		; x
	sub.w	d1,d0		; y
	lsr.w	#4,d2
	addq	#2,d2		; world size
	move.w	d2,$100.w
	lsr.w	#2,d0
	addq	#1,d0		; hight screen
	lsl.l	#8,d0		; muls	#$100,d0
	add.w	d2,d0		; size $dff058
	move.w	d0,18(a3)
; Calcola screen
	move.l	Fill,a1
	move.l	Bm2_pl1,a2
; calcola zona di memoria da copiare (metodo: Discendente)
	move.w	$140.w,d1
	move.w	d1,d3
	muls	#$2c,d3
	muls	#$78,d1
	move.w	$144.w,d2
	lsr.w	#3,d2
	add.w	d2,d1
	add.w	d2,d3
	add.w	d3,a1
	add.l	d1,a2
	move.l	a1,(a3)
	move.l	a2,4(a3)
	move.l	a2,8(a3)
; calcola moduli (metodo discendente)
	moveq	#0,d1
	move.w	$100.w,d1	; num. world da copiare
	add.w	d1,d1		; in byte
	moveq	#44,d2		; 40 byte in 320 pixel
	sub.w	d1,d2		; modulo a
	move.w	#$78,d3		; $78 byte
	sub.w	d1,d3		; modulo d
	move.w	d2,12(a3)
	move.w	d3,14(a3)
	move.w	d3,16(a3)
	rts



*** Copia l'area creata e la mette nei plane corrispondenti

Copyarea:	; funzione d=a+c
	lea	$dff000,a6
	move.w	#$8400,$96(a6)
	lea	Jump(pc),a1
	lea	Dax(pc),a3
	move.w	$106.w,d1
	add.w	d1,d1
	move.w	d1,d2
	add.w	d2,d2
	add.w	d2,d1
	add.w	d1,a1
	moveq	#0,d0
	move.w	18(a3),d0	; size $dff058
	moveq	#$28,d1
	moveq	#0,d2
	move.l	4(a3),d2	; $dff048 - $dff054
	moveq	#0,d3
	move.l	(a3),d3		; $dff050
	move.l	#AddGriglia+[$2c*256]-2,d4
	lea	$40(a6),a2
	lea	$54(a6),a4
	lea	$48(a6),a5
	move.l	#$0b0a0002,d5
	move.l	#$0bfa0002,d6
	move.l	#$0fca0002,d7
Altola:	btst	#6,2(a6)
	bne	Altola
	move.w	12(a3),$64(a6)
	move.w	12(a3),$62(a6)
	move.w	14(a3),$66(a6)
	move.w	16(a3),$60(a6)
	lea	$50(a6),a3
	lea	$58(a6),a6
	jmp	(a1)

In base al bitplane in cui deve andare, la routine salta in ordine qui' sotto. Questo perche' se la faccia deve essere disegnata con il colore 1 si avra' il bitplane 1 con il disegno della faccia e tutti gli altri vuoti.

Nel caso di un altro colore:


       ______ plane 4
      | _____ plane 3
      || ____ plane 2
      ||| ___ plane 1
      ||||
0 -> %0000    non usato
1 -> %0001    plane 1   pieno, 2-3-4 vuoti
2 -> %0010    plane 2   pieno, 1-3-4 vuoti
3 -> %0011    plane 1-2 pieni,   3-4 vuoti
4 -> %0100    plane 3   pieno, 1-2-4 vuoti
. -> %....

In base al colore usato sara' necessario copiare o cancellare l'aria interessata. Per la copia e' sufficiente sommare lo screen con la faccia in questione. Per cio' che riguarda la cancellazione bisogna effettuare una moltiplicatione della faccia NEGATA per lo screen destinazione.

In base al colore da noi assegnato sara' eseguita UNA SOLA delle routine che seguono.

Jump:	jmp	zeros
	jmp	unos
	jmp	dues
	jmp	tres
	jmp	quattros
	jmp	cinques
	jmp	seis
	jmp	settes
	jmp	ottos
	jmp	noves
	jmp	diecis
	jmp	undicis
	jmp	dodicis
	jmp	tredicis
zeros:
	bsr	disegnare		; disegna plane 0
	add.w	d1,d2
	bsr	cancellare		; cancella plane 1
	add.w	d1,d2
	bsr	cancellare		; cancella plane 2
	rts
unos:
	bsr	cancellare		; disegna plane 0
	add.w	d1,d2
	bsr	disegnare		; disegna plane 1
	add.w	d1,d2
	bsr	cancellare		; cancella plane 2
	rts
dues:
	bsr	cancellare		; cancella plane 0
	add.w	d1,d2
	bsr	cancellare		; cancella plane 1
	add.w	d1,d2
	bsr	disegnare		; disegna plane 2
	rts
tres:
	bsr	disegnare
	add.w	d1,d2
	bsr	disegnare
	add.w	d1,d2
	bsr	cancellare
	rts
quattros:
	bsr	disegnare
	add.w	d1,d2
	bsr	cancellare
	add.w	d1,d2
	bsr	disegnare
	rts
cinques:
	bsr	cancellare
	add.w	d1,d2
	bsr	disegnare
	add.w	d1,d2
 	bsr	disegnare
	rts
seis:
	bsr	disegnare
	add.w	d1,d2
	bsr	disegnare
	add.w	d1,d2
	bsr	disegnare
	rts
settes:
	bsr	griglia
	add.w	d1,d2
	bsr	cancellare
	add.w	d1,d2
	bsr	cancellare
	rts
ottos:
	bsr	cancellare
	add.w	d1,d2
	bsr	griglia
	add.w	d1,d2
	bsr	cancellare
	rts
noves:
	bsr	cancellare
	add.w	d1,d2
	bsr	cancellare
	add.w	d1,d2
	bsr	griglia
	rts
diecis:
	bsr	griglia
	add.w	d1,d2
	bsr	griglia
	add.w	d1,d2
	bsr	cancellare
	rts
undicis:
	bsr	griglia
	add.w	d1,d2
	bsr	cancellare
	add.w	d1,d2
	bsr	griglia
	rts
dodicis:
	bsr	cancellare
	add.w	d1,d2
	bsr	griglia
	add.w	d1,d2
	bsr	griglia
	rts
tredicis:
	bsr	griglia
	add.w	d1,d2
	bsr	griglia
	add.w	d1,d2
	bsr	griglia
	rts

cancellare:                              ; funzione D=A(neg)*C per cancellare
	btst	#6,-$56(a6)
	bne	cancellare
	move.l	d5,(a2)
	move.l	d3,(a3)
	move.l	d2,(a4)
	move.l	d2,(a5)
	move.w	d0,(a6)
	rts

disegnare:
	btst	#6,-$56(a6)
	bne	disegnare
	move.l	d6,(a2)
	move.l	d3,(a3)
	move.l	d2,(a4)
	move.l	d2,(a5)
	move.w	d0,(a6)
	rts

Griglia:
	btst	#6,-$56(a6)
	bne	griglia
	move.l	d7,(a2)                  ; funzione D=A(neg)*C+A*B
	move.l	d4,-$c(a6)
	move.l	d3,(a3)
	move.l	d2,(a4)
	move.l	d2,(a5)
	move.w	d0,(a6)
	rts

** Questa routine cancella la zona usata precedentemente come buffer in cui
** e' stata creata la faccia.

Clerorno:	; zona da cancellare per disegnare linee
	btst	#6,$2(a6)
	bne	Clerorno
	lea	Dax(pc),a3
	move.l	#$01000002,$40(a6)
	move.w	12(a3),$66(a6)
	move.l	(a3),$54(a6)
	move.w	18(a3),$58(a6)
	rts


** Dopo aver tracciato tutte le linee che compongono la faccia e' necessario
** fillarle con il blitter (usando il metodo DISCENDENTE.
** Il metodo discendente funziona in modo contrario a tutti gli altri.
** Il blitter riempira' la zona assegnata partendo dal basso a sinistra e
** finendo in alto a destra.

Fillare:
	lea	Dax(pc),a3
	tst.w	out
	beq	doppld
	move.w	12(a3),d0
	addq.w	#2,d0
	move.w	18(a3),d1
	subq.w	#1,d1
	bra.s	FillWait
doppld:
	move.w	12(a3),d0
	move.w	18(a3),d1
FillWait:                         ; zona da fillare per riempire linee
	btst	#6,$2(a6)
	bne	FillWait
	move.l	#$09f00012,$40(a6)
	move.w	d0,$64(a6)
	move.w	d0,$66(a6)
	move.l	(a3),$50(a6)
	move.l	(a3),$54(a6)
	move.w	d1,$58(a6)
	rts

** Questa routine disegna SOLO UNA LINEA con il blitter, verra' disegnata
** direttamente sullo screen senza passare da nessun buffer.

Onlyline:
	bsr	CalcScr		; calcola le X e Y nello schermo
	lea	BufferLine(pc),a1
	lea	jump2(pc),a2
	move.l	BM2_PL1,a0
	move.l	BM2_PL1,a3
	lea	$dff000,a6
	move.w	$106.w,d1
	add.w	d1,d1
	move.w	d1,d2
	add.w	d2,d2
	add.w	d2,d1
	add.w	d1,a2
	jmp	(a2)

** Il JMP funziona nelle stesso modo descritto sopra, serve per assegnare il
** colore alla linea.

Jump2:	jmp	linep0
	jmp	linep1
	jmp	linep2
	jmp	linep3
	jmp	linep4
	jmp	linep5
	jmp	linep6

Linep0:	move.l	#$ffff8000,d7	; metodo TEXTURE = tutto pieno
	bsr	Line00
	move.l	#$00008000,d7	; metodo TEXTURE = vuoto = cancella linea
	add.w	#$28,a3
	bsr	Line00
	add.w	#$28,a3
	bsr	Line00
	bra	CalcMax
Linep1:	move.l	#$00008000,d7
	bsr	Line00
	move.l	#$ffff8000,d7
	add.w	#$28,a3
	bsr	Line00
	move.l	#$00008000,d7
	add.w	#$28,a3
	bsr	Line00
	bra	CalcMax
Linep2:	move.l	#$00008000,d7
	bsr	Line00
	add.w	#$28,a3
	bsr	Line00
	move.l	#$ffff8000,d7
	add.w	#$28,a3
	bsr	Line00
	bra	CalcMax
Linep3:	move.l	#$ffff8000,d7
	bsr	Line00
	add.w	#$28,a3
	bsr	Line00
	add.w	#$28,a3
	move.l	#$00008000,d7
	bsr	Line00
	bra	CalcMax
Linep4:	move.l	#$ffff8000,d7
	bsr	Line00
	move.l	#$00008000,d7
	add.w	#$28,a3
	bsr	Line00
	move.l	#$ffff8000,d7
	add.w	#$28,a3
	bsr	Line00
	bra	CalcMax
Linep5:	move.l	#$00008000,d7
	bsr	Line00
	move.l	#$ffff8000,d7
	add.w	#$28,a3
	bsr	Line00
	add.w	#$28,a3
	bsr	Line00
	bra	CalcMax
Linep6:	move.l	#$ffff8000,d7
	bsr	Line00
	add.w	#$28,a3
	bsr	Line00
	add.w	#$28,a3
	bsr	Line00
	bra	CalcMax


** Questa routine traccia le linee usando il blitter

Line00:
	movem.w	(a1),d0-d3	; coordinate x0,y0,x1,y1


****** QUI DEVE ESSERE INSERITA LA ROUTINE DI CLIPPING


Line:
	cmp.w	d1,d3
	bne	encode
	rts
encode:
	move.l	a3,a0
	move.w	#$78,d4
	sub.w	d0,d2
	bpl.s	Oct1278
	neg.w	d2
	sub.w	d1,d3
	bpl.s	Oct34
	neg.w	d3
	cmp.w	d2,d3
	bpl.s	Oct6
	moveq.l	#28+1,d5
	bra.s	Lline
Oct6:	exg	d2,d3
	moveq.l	#12+1,d5
	bra.s	Lline
Oct34:	cmp.w	d2,d3
	bpl.s	Oct3
	moveq.l	#20+1,d5
	bra.s	Lline
Oct3:	exg	d2,d3
	moveq.l	#8+1,d5
	bra.s	Lline
Oct1278:sub.w	d1,d3
	bpl.s	Oct12
	neg.w	d3
	cmp.w	d2,d3
	bpl.s	Oct7
	moveq.l	#24+1,d5
	bra.s	Lline
Oct7:	exg	d2,d3
	moveq.l	#4+1,d5
	bra.s	Lline
Oct12:	cmp.w	d2,d3
	bpl.s	Oct2
	moveq.l	#16+1,d5
	bra.s	Lline
Oct2:	exg	d2,d3
	moveq.l	#0+1,d5
Lline:	mulu.w	d4,d1
	ror.l	#4,d0
	add.w	d0,d0
	add.l	d1,a0
	add.w	d0,a0
	swap	d0
	or.w	#$bca,d0
	add.w	d3,d3
	add.w	d3,d3
	add.w	d2,d2
	move.w	d2,d1
	lsl.w	#5,d1
	add.w	#$42,d1
	swap	d5
	move.w	d0,d5
	swap	d5
	move.w	d3,d0
	sub.w	d2,d0
	bpl	waitb1
	bset	#6,d5
waitb1:	move.w	d0,d6
	sub.w	d2,d6
Drawl2:	btst	#6,2(a6)
	bne.s	Drawl2
	move.w	d3,$62(a6)
	move.l	d0,$50(a6)
	move.l	d5,$40(a6)
	move.w	d4,$60(a6)
	move.w	d4,$66(a6)
	move.w	d6,$64(a6)
	move.l	#-1,$44(a6)
	move.l	d7,$72(a6)
	move.l	a0,$48(a6)
	move.l	a0,$54(a6)
	move.w	d1,$58(a6)
	rts


** Qui vengono calcolati i valori Xmax,Xmin,Ymax,Ymin per ogli linea

CalcMax:	; calcola
	lea	BufferLine2(pc),a1
	lea	$140.w,a2
	moveq	#0,d0
	moveq	#0,d1
	moveq	#0,d2
	moveq	#0,d3
	movem.w	(a1),d0-d3
	cmp.w	d0,d2
	ble.s	Less
	move.w	d0,6(a2)
	move.w	d2,4(a2)
	bra.s	Great
Less:	move.w	d0,4(a2)
	move.w	d2,6(a2)
Great:	cmp.w	d1,d3
	ble.s	Less2
	move.w	d1,2(a2)
	move.w	d3,(a2)
	bra.s	Great2
Less2:	move.w	d1,(a2)
	move.w	d3,2(a2)
Great2:	bsr	TCalcAll
	bra	NoFace


CalcPoits:	; Calcola Xmax,Xmin,Ymax,Ymin
	lea	BufferLine2+2(pc),a2
	move.w	$10e.w,d6
	add.w	d6,d6
	subq.w	#1,d6
	move.w	d6,d7
	moveq	#0,d1
	move.w	(a2),d2
	moveq	#0,d3
CalcPoits4:
	move.w	(a2),d1
	cmp.w	d1,d3
	bgt.s	CalcPoits2
	move.w	d1,d3
	addq	#4,a2
	dbf	d6,CalcPoits4
	bra.s	CalcPoitsend
CalcPoits2:
	cmp.w	d1,d2
	blt.s	CalcPoits3
	move.w	d1,d2
CalcPoits3:
	addq	#4,a2
	dbf	d6,CalcPoits4
CalcPoitsend:
	move.w	d3,$140.w
	move.w	d2,$142.w
	lea	BufferLine2(pc),a2
	moveq	#0,d0
	moveq	#0,d1
	move.w	(a2),d2
	moveq	#0,d3
	move.w	d7,d6
CalcPoit5:
	move.w	(a2),d1
	cmp.w	d1,d3
	bgt.s	CalcPoit6
	move.w	d1,d3
	addq	#4,a2
	dbf	d6,CalcPoit5
	move.w	d3,$144.w
	move.w	d2,$146.w
	rts
CalcPoit6:
	cmp.w	d1,d2
	blt.s	CalcPoit7
	move.w	d1,d2
CalcPoit7:
	addq	#4,a2
	dbf	d6,CalcPoit5
	move.w	d3,$144.w
	move.w	d2,$146.w
	rts


*** Questa ruotine disegna tutte le linee nel buffer

Lineeee:
	lea	BufferLine(pc),a5	; sequanza delle linee da disegnare
	move.w	$10e.w,d6
	subq.w	#1,d6
Linene:	movem.w	(a5)+,d0-d3
	bsr.s	SWline
	dbf	d6,Linene
	rts

** Qui deve essere inserita la routine di CLIPPING


SWLine:
	move.l	Fill,a0
	lea	$dff000,a6
	cmp.w	d1,d3
	bne.s	Sencode			; linea orizzontale
	rts
Sencode:bmi.s	SLine0A
	exg.l	d0,d2
	exg.l	d1,d3
SLine0A:addq.w	#1,d3
	clr.w	d4
	sub.w	d1,d3
	bge.s	SLine16
	neg.w	d3
	bra.s	SLine1A
SLine16:or.w	#1,d4
SLine1A:sub.w	d0,d2
	bge.s	SLine22
	neg.w	d2
	bra.s	SLine26
SLine22:or.w	#2,d4
SLine26:cmp.w	d3,d2
	bge.s	SLine2E
	exg.l	d2,d3
	bra.s	SLine32
SLine2E:or.w	#4,d4
SLine32:clr.w	d5
	ror.w	#4,d0
	or.w	#$B00,d0
	move.b	d0,d5
	move.b	#$5A,d0
	add.w	d5,d5
	mulu.w	#$2c,d1
	and.w	#$FFFF,d1
	add.w	d5,d1
	add.l	a0,d1
	move.b	oct(pc,d4.w),d4
	add.l	d3,d3
SLine5C:btst	#6,2(a6)
	bne.s	SLine5C
	move.w	d3,$62(a6)
	sub.w	d2,d3
	bge.s	SLine70
	or.b	#$40,d4
SLine70:
	move.l	d3,$50(a6)
	sub.w	d2,d3
	move.l	#$ffff8000,$72(a6)
	moveq	#-1,d5
	move.l	d5,$44(a6)
	moveq	#44,d5
	move.w	d5,$60(a6)
	move.w	d5,$66(a6)
	move.w	d3,$64(a6)
	move.w	d4,$42(a6)
	move.w	d0,$40(a6)
	move.l	d1,$48(a6)
	move.l	d1,$54(a6)
	lsl.w	#6,d2
	add.w	#$42,d2
	move.w	d2,$58(a6)
	rts

; octant codes
oct:	dc.b	$0f,$0b,$07,$03,$1f,$17,$1b,$13

Chi ha controllato il sorgente avra' sicuramente notato che la routine di CLIPPING non e' presente. Questo solo perche' ho cercato di facilitare i meno esperti, tralasciando le routine piu' conplesse. Non vi preoccupate!!! Troverete tale routine nel prossimo articolo con una spiegazione dettagliata sul funzionamento. Dato che il clipping serve per eliminare la parte del disegno al di fuori dello screen, avrete (solo per questo mese) una sola limitazione; infatti non dovete per nessun motivo far uscire gli oggetti dalle coordinate dello screen. In effetti, sarebbe possibile farli uscire verso l'alto o verso il basso, solo se la memoria sopra o sotto il buffer di disegno, e' MEMORIA PULITA, infatti gli oggetti verranno disegnati in questa zona di memoria.

Esaminiamo ora le parti di sorgente restanti.

	jmp	START
	org	$130000
	load	$130000

START:
	................

Questi comandi servono per rendere il codice NON RILOCABILE, ovvero sara' necessario che la ruotine sia caricata sempre a partire dall'indirizzo $130000.


*************************************************************************


	lea	$150000,a0
Clear:	clr.l	(a0)+
	cmp.l	#$180000,a0
	bne	clear
	lea	$100.w,a0
dsf:	clr.b	(a0)+
	cmp.l	#$200,a0
	bne	dsf


Il loop sopra serve per cancellare la memoria tra gli indirizzi :
$150000 e $180000 e tra $100 e $200.

*************************************************************************

	lea	SphLines(pc),a0
	lea	Plane9(pc),a1
addiaz:	add.w	#96,(a0)+
	cmp.l	a0,a1
	bne	addiaz

Non serve a nulla!!! Serve solo per sommare il valore 96 alla sfera presente nel satellite; i due oggetti erano stati creati separatemente ed ora unendoli e' necessario sommare questo valore alla sfera, atrementi si avrebbero piu' poligoni con gli stessi valori (numero di poligono uguale).

*************************************************************************


	lea	AddGriglia,a0
	move.w	#$80,d1		; 1/2 height
gri1:	moveq	#10,d0		; num. screen in long
gri2:	move.l	#$55555555,(a0)+
	move.l	#$aaaaaaaa,$28(a0)	; $28=modulo-$4[1 long]
	dbra	d0,gri2
	add.l	#$2c,a0			; modulo
	dbra	d1,gri1

** Crea una griglia per usare l'effetto dithering, la mette in AddGriglia

**************************************************************************

	move.w	#npoints2-1,dpunti
	move.w	#nface2-1,dface
	move.l	#wx2,dx
	move.l	#wy2,dy
	move.l	#wz2,dz
	move.l	#lines2,dlines
	move.l	#plane2,dplane
	move.l	#Priotity2,Dpriority
	move.w	#1,$132.w	; 1 se blt.s ; 0 se bgt.s

** Le coordinate delle faccie sono messe nelle variabili : dpunti,dface,dx,dy,
** dz,dlines,dplane,Dpriority. Questo per facilitare il cambiamento di
** oggetto durante l'eseguzione del demo.

*************************************************************************

Inter:
	btst	#5,$dff01f
	beq.w	Int
	move.w	#$20,$dff09c
	add.w	#4,ZANGLE
	add.w	#2,YANGLE
	add.w	#4,XANGLE
	move.w	#1,IntOk
Int:	rte
IntOk:	dc.w	0


** Routine in INTERRUPT $6c. Questo perche' il nostro oggetto girera' sempre
** alla medesima velocita', anche se cio' comporta piu' o meno fluidita' di
** muovimanto.

**************************************************************************

COME DISEGNARE GLI OGGETTI

Dpunti:	dc.w	0		; numero punti
Dface:	dc.w	0		; numero faccie
DX:	dc.l	0		; coords X
DY:	dc.l	0		; coords Y
DZ:	dc.l	0		; coords Z
Dlines:	dc.l	0		; come unire le linee
Dplane:	dc.l	0		; come colorare l'oggetto
Dpriority:	dc.l	0	; come disegnare l'oggetto (vedi dopo!)

* ATTENZIONE: le 3 variabili sotto sono per l'oggetto + grande. Dato che la
* routine deve gestire diversi oggetti, e' bene definire qui' le dimensioni
* del piu' grande.


Blkpoints=200		;	num.punti
BlkLine=26		;	la faccia che ha piu' linee ne ha ?
Blkface=200		;	num.facce

; cubo con scritte
NPOINTS=76		; numero punti
Nface=12		; numero faccie
Wx:
 dc.w	-120,120,120,-120,-120,120,120,-120
 dc.w	-100,100,100,35,35,-35,-35,-100
 dc.w	120,120,120,120,120,120,120,120,120,120,120,120
 dc.w	100,-20,-100,-100,-20,100,45,5,-45,-45,5,45
 dc.w	-120,-120,-120,-120,-120,-120,-120,-120,-120,-120,-120,-120,-120,-120
 dc.w	-120,-120,-120,-120,-120,-120,-120,-120,-120,-120,-120,-120
 dc.w	0,-80,100,-100,80
 dc.w	0,-80,100,-100,80
Wy:
 dc.w	120,120,-120,-120,120,120,-120,-120
 dc.w	100,100,40,40,-100,-100,40,40
 dc.w	100,100,60,60,20,20,-20,-20,-60,-60,-100,-100
 dc.w	100,100,40,-40,-100,-100,45,45,10,-10,-45,-45
 dc.w	60,60,40,40,-40,-40,-20,-20,0,0,-20,-20,-60,-60
 dc.w	60,60,40,40,10,10,-60,-60,-40,-40,-10,-10
 dc.w	120,120,120,120,120
 dc.w	-120,-120,-120,-120,-120
Wz:
 dc.w	120,120,120,120,-120,-120,-120,-120
 dc.w	120,120,120,120,120,120,120,120
 dc.w	100,-100,-100,40,40,-40,-40,40,40,-100,-100,100
 dc.w	-120,-120,-120,-120,-120,-120,-120,-120,-120,-120,-120,-120
 dc.w	-100,-20,-20,-80,-80,-40,-40,-50,-50,-10,-10,-20,-20,-100
 dc.w	10,100,100,30,30,100,100,10,10,80,80,10
 dc.w	100,-100,20,20,-100
 dc.w	100,-100,20,20,-100
Lines:
 dc.w	0,1,1,2,2,3,3,0
 dc.w	5,4,4,7,7,6,6,5
 dc.w	1,5,5,6,6,2,2,1
 dc.w	4,0,0,3,3,7,7,4
 dc.w	4,5,5,1,1,0,0,4
 dc.w	3,2,2,6,6,7,7,3
 dc.w	8,9,9,10,10,11,11,12,12,13,13,14,14,15,15,8
 dc.w	16,17,17,18,18,19,19,20,20,21,21,22,22,23,23,24,24,25,25,26,26,27,27,16
 dc.w	28,29,29,30,30,31,31,32,32,33,33,28,34,35,35,36,36,37,37,38,38,39,39,34
 dc.w	40,41,41,42,42,43,43,44,44,45,45,46,46,47,47,48,48,49,49,50,50,51,51,52,52,53,53,40
 dc.w	54,55,55,56,56,57,57,58,58,59,59,60,60,61,61,62,62,63,63,64,64,65,65,54
 dc.w	66,67,67,68,68,69,69,70,70,66
 dc.w	75,74,74,73,73,72,72,71,71,75
Plane:
 dc.w	4,0,4,0,4,1,4,1,4,2,4,2,8,3,12,4,12,6,26,5,5,5,5,1
Priotity:	; 0 se le calcola ; 1 se le disegna sempre ; 2 se ultima
 dc.w	0,0,0,0,0,0,2,2,2,2,2,2

Per disegnare un oggetto e' necessario :

- mettere le coordinate X dei punti nella variabile Wx
- mettere le coordinate Y dei punti nella variabile Wy
- mettere le coordinate Z dei punti nella variabile Wz
- stabilire come unire i punti e mettere i valori in Lines
- stabilire come colorare le faccie e fornire il numero di linee della faccia
- definire la priorita' della faccia

Per essere piu' veloci nella spiegazione e' meglio fare un esempio.

NON E' POSSIBILE DISEGNARE LE FACCIE A CASO SE E' PRESENTE LA ROUTINE PER ELIMINARE LE FACCIE NASCOSTE!!!!!!!

Se vogliamo disegnare un cubo, la prima faccia sara' cosi' strutturata:


    0----1
    |    |
    |    |
    |    |
    3----2

Ora e' necessario dare un verso di percorrenza alla faccia:


    0--------1
    |   ->   |
    |        |
 /\ |        |||
 || |        |||
 || |        |\/
    |        |
    |        |
    3--------2
       <-

Come si nota la faccia va' dal punto 0 al 1, dal 1 al 2, dal 2 al 3 e dal 3 allo 0. IMPORTANTE iniziare sempre dallo 0.

LA FACCIA ADIACENTE DEVE AVERE VERSO DI PERCORRENZA OPPOSTO !!!

                  4
                 /|
                / |
               /  |
    0--------1/   |
    |   ->   |    |
    |        |    |
 /\ |        |    5
 || |      |||   /
 || |      |||  /
    |      \/| /
    |        |/
    3--------2
       <-

La faccia formato dai punti : 1,4,5,2 deve avere verso di percorrenza opposto alla precedente (solo se 1 lato la tocca). Quindi:


                  4
                 /|
             -> / |||
               /  |||
    0--------1/   |\/
    |        |    |
    |        |    |
    |      /\|    5
    |      |||   /
    |      |||  /
    |        | /<-
    |        |/
    3--------2

La faccia sara' unita in questo modo: dal 2 al 1, dal 1 al 4, dal 4 al 5, dal 5 al 2. Ora date le coordinate nello spazio 3D, e data la simmetria centrale:



                                                 Y
    (-100,-100,100)         (100,-100,100)        /\
                  0---------1                      |
                  |         |                      |
                  |         |                      |
                  |    |    |                      |   / Z
                  |   - -   |                      |  /
                  |    |    |                      | /
                  |         |                      |/
                  |         |                      ----------> X
                  3---------2
     (-100,100,100)         (100,100,100)

La faccia in primo piano ha Z=100 e quella dietro Z=-100.

Abbiamo per le prime 2 faccie:

Wx:  dc.w   -100,100,100,-100,100,100
Wy:  dc.w   -100,-100,100,100,-100,-100
Wz:  dc.w   100,100,100,100,-100,-100
Lines:
 dc.w	0,1,1,2,2,3,3,0
 dc.w	5,2,2,1,1,4,4,5
Plane:
 dc.w	4,0,4,1
Priotity:	; 0 se le calcola ; 1 se le disegna sempre ; 2 se ultima
 dc.w	0,0

Plane definisce:

4,0 = faccia composta da 4 linee con colore 0

4,1 = faccia composta da 4 linee con colore 1

esempio :13,89 = faccia composta da 13 linee con colore 89

Priotity definisce se:

0 = la faccia deve essere calcolata (se visibile o no) tramite routine che calcola la faccie nascoste

1 = la faccia deve essere disegnata sempre, dimentica passaggio sopra

2 = la faccia deve essere disegnata per ultima, dopo tutte le altre.

Colori :
; 0= primo plane
; 1= secondo plane
; 2= terzo plane
; 3= primo + secondo plane
; 4= primo + terzo plane
; 5= secondo + terzo plane
; 6= primo + secondo + terzo plane
; uso della griglia (stencil vector !!!!)
; 7= primo plane
; 8= secondo plane
; 9= terzo plane
; 10= primo + secondo plane
; 11= primo + terzo plane
; 12= secondo + terzo plane
; 13= primo + secondo + terzo plane

P.S.: I dati necessari al programa si possono prelevare dall'archivio dello scorso mese. P.S.2: Se ho dimenticato di spiegare qualcosa , mandatemi una E-Mail


Pagina Principale


    Scritto da: Alfredo Ornaghi      e-mail: ted@intercom.it
                ITALY                   tel: