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
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. **************************************************************************
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
Scritto da: Alfredo Ornaghi e-mail: ted@intercom.it ITALY tel: