                          - SEPTIC'S DEMOSKOLA -

                                Lektion 12
                                ¯¯¯¯¯¯¯¯¯¯
                        Skriven av Vicious / Septic

                                 4 Feb 94



                                 Inledning
                                 ¯¯¯¯¯¯¯¯¯
Jag hoppas verkligen ni följde med och förstod allt i förra lektionen, även
om det var lite krångligt. Den här gången ska vi nämligen fortsätta med
vektorobjekt, men lära oss hur man fyller dem! Med andra ord, fyllda
vektorer blir en barnlek efter att ni läst genom den här delen av
Demoskolan!


                                   Teori
                                   ¯¯¯¯¯
Nu ska vi äntligen ta upp blitterns skojiga fill-funktion, som kan användas
till mycket trevliga effekter. Vi ska dock endast fylla våra vektorobjekt
med den, och det är ju fullt tillräckligt!
  En fyllning går egentligen till så att man för varje rasterrad tar reda
på startpixeln för fyllningen och slutpixeln, och sedan drar man ett streck
mellan de två pixlarna. Detta får man sedan göra för alla 256 (eller vad
man nu har för skärmhöjd) raderna på bilden för att ett stort vektorobjekt
ska fyllas.
  Använder vi istället blitterns fyllning så sköter blittern helt och
hållet det här tråkiga (och rasterkrävande) arbetet. Vi måste dock göra på
samma sätt ungefär, dvs sätta en start och en slutpunkt. Blittern fungerar
nämligen så i fillmode, att den letar tills den hittar en punkt, slår på
fyllningen och fyller tills den hittar nästa punkt. När man ritar vanliga
linjer så kan det ju dessvärre hamna fler än en pixel per rad om linjen
t.ex. bara är tre pixlar i Y-led men 50 i X-led.
  Som tur är finns det till ens hjälp en bra funktion när man ritar linjer,
som heter SING. Sätter man SING-biten när man ritar linjen så kommer det
bara att hamna en pixel per rad, och detta är ju perfekt för fyllningen.
  För att fyllningen ska bli alldeles måste vi också se till att vi ritar
ut alla linjer åt samma vertikala håll, dvs alltid uppifrån och ner eller
tvärtom. Dessutom måste vi göra en EOR med linjens första pixel för att
undvika att spetsiga hörn som bara har en pixel slår på fyllningen och
bildar fula "trådar" ut mot kanten från vectorfacets hörn.
  Alltså, först och främst måste vi ändra lite i linjeritnings rutinen, men
sedan är det bara att fylla på!
  När man sysslar med konvexa vektorobjekt, dvs sådana objekt där en synlig
polygon ALDRIG överlappar en annan synlig polygon (som tex en kub), så kan
man använda den smidiga metoden att rita ut alla polygoner på en gång och
sedan fylla hela skärmsidan. Ska man ha inkonvexa objekt, som tex en torus
(ser ut som ett tjockt O), så får man sortera alla faces och sedan rita ut
dem med början på det längs bak, fylla varje för sig och dessutom kopiera
dem på bilden. Man kan använda sig av en kombinerad konvex/inkonvex rutin
som klarar att hantera inkonvexa objekt uppbyggda av konvexa. Jag kommer
dock inte gå in på inkonvexa vektorer i den här Demokursen, utan endast
konvexa.
  Som jag sa, så kan man alltså rita ut alla polygoner på en gång och sedan
fylla objektet med blittern. Detta är ju väldigt smidigt, men medför
samtidigt ett problem...De polygoner som INTE är synliga måste plockas bort
på något sätt, annars kommer ju blittern att fylla dem också, och då kommer
objektet att se mycket konstigt ut.
  Så, vi måste alltså på något sätt känna av när ett face är vänt bort
ifrån oss, och det kan man göra genom att ta fram polygonens normal.
Normalen är den vektor som pekar rakt ut ifrån ett face, och denna blir
(vanligtvis) negativ när ytan är vänd bort från oss. Att ta fram normalen
för en yta är inte så värst svårt.
  Först och främst behöver man polygonens tre första koordinater. Det är
viktigt att koordinaterna ligger medurs runt objektet, annars kan normalen
få motsatt betydelse, så att den är negativ när den är synlig. Hur som
helst är det inte hela världen, eftersom det då är lätt att vända på
koordinaterna, så att tex koordinatföljden 0,1,2,3 blir 3,2,1,0. Detta
kommer ni säkerligen stöta på när ni gör egna objekt.
  Vi gör beräkningen EFTER att vi konverterat 3d till 2d. Genom att
subtrahera koordinat 1 och sedan koordinat 2 med koordinat 3 så får man ut
hur polygonen är vänd. Genom följande formel får vi sedan fram normalens
värde:

   Vx1=X1-X3
   Vy1=Y1-Y3
   Vx2=X2-X3
   Vy2=Y2-Y3

   Normal=(Vx1*Vx2)-(Vy1*Vy2)

Om denna normal är negativ så är polygonen alltså osynlig. Låt oss gå
vidare...


                                 Register
                                 ¯¯¯¯¯¯¯¯
Registren som används för fyllning är desamma som vid vanlig kopiering med
blittern. Man kan säga att fyllning ÄR en vanlig kopiering, eftersom man
kopierar det man ska fylla på sig självt, så att säga...(Man kan
visserligen också kopiera från ett ställe till ett annat samtidigt som man
fyller, men jag har valt att ta upp endast det enkla fyllsättet.)
  Precis som vid den lätta, vanliga kopieringen behöver vi endast Source A
och Destination D i blittern. Vi måste också sätta en speciell mode kallad
Descending mode som jag inte gått in på så mycket än. Descending mode krävs
av fyllrutinen, men kan användas även vid vanlig kopiering. Vad moden gör
är helt enkelt att den vänder på hela blitteroperationens utförande, så att
blittern börjar kopiera i slutet på blocket och sedan MINSKAR pekarna till
nästa word, så att den alltså kopierar det framförvarande ordet. Modulo
värdena SUBTRAHERAS också, istället för att adderas vid ett radslut.
Shiftningar sker åt VÄNSTER istället för åt höger. Och blitter maskningen
fungerar också bakvänt, så att FIRST WORD MASK maskar SISTA wordet på raden
(men detta blir ju egentligen det första eftersom det hämtas först när allt
är baklänges :-)   Pekarna ställes alltså in så att de pekar på sista
wordet i det man ska kopiera.
  Descending mode är bra om man ska kopiera tex en bob som sitter på
skärmen till en position längre ner som överlappar bobens nuvarande
position. Problemet med en vanlig blittning blir ju då att bobens övre data
kommer att skriva över de understa raderna innan man hunnit kopiera dem,
och boben förstörs alltså. Kopierar man istället baklänges så slipper man
detta problem.
  Vi har tittat på BLTCON1 ($DFF042) tidigare, men för säkerhets skull tar
vi det en gång till, för att kika närmare på fill-bitarna...

BIT#   BLTCON1
----   -------
 15    BSH3
 14    BSH2
 13    BSH1
 12    BSH0
 11     x
 10     x
 09     x
 08     x
 07     x
 06     x
 05     x
 04    EFE
 03    IFE
 02    FCI
 01    DESC
 00    LINE

Som ni ser är Descending mode bit #1, men sen finns det även några andra
skojiga bitar kvar.
  EFE står för Exclusive Fill Enable och med det menas att blittern fyller
arean mellan två pixlar och tar bort den vänstra av dem. Ett word med
följande utseende:

   %0001000010000000

Skulle alltså bli:

   %0000111110000000

Arean är fylld men pixlen längst till vänster är borttagen. Detta är bra
att använda vid fyllda vektorer, eftersom alla faces som ligger bredvid
varandra annars skulle överlappa varandra precis i kanterna och bilda
störande buggar. Vi ska alltså slå på EFE i vår vektorrutin.
  IFE är, som ni kanske listat ut, Inclusive Fill Enable, och den lämnar
de yttre gränserna intakta:

   %0001000010000000

Blir med Inclusive Fill Mode:

   %0001111110000000

Man måste välja antingen EFE eller IFE för att fylla en yta. Den sista
intressanta biten, FCI, betyder Fill Carry In. Med det menas att blittern
fyller utanför ytan istället. Vårt word:

   %0001000010000000

Blir alltså då (med IFE):

   %1111000011111111

Och med EFE blir det:

   %1111000001111111

Svårare är det alltså inte att fylla. Resten är bara en standard SRCA->DEST
kopiering som vi lärde oss i Lektion 7.


                              Programexemplet
                              ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Programmet den här gången är en vidareutveckligen av förra lektionens
exempel. Vår kub är nu istället FYLLD!
  Eftersom programmet i princip är identiskt med det förra så går jag bara
igenom de ändringar jag gjort.
  För det första har jag gjort om programmet så att det använder två
bitplan istället för ett. Detta medför att vi kan använda tre olika färger
vilket är nödvändigt för att visa en kub (tre sidor maximalt visas
samtidigt).
  De första ändringarna kommer i rutinen DrawCube. En bit nedanför labeln
.nxtpoly ser ni att jag hoppar till en rutin som kallas Hiddenface. Den
rutinen räknar fram normalen som jag beskrev tidigare. Om den är positiv
hoppar vi till .visible för att rita ut polygonen.
  För att veta i vilket bitplan jag ska rita ut linjerna gör jag så att jag
shiftar runt färgnumret och kollar om det är negativt, dvs när högsta biten
i byten är satt. Är den det betyder det att en linje ska ritas. Det kanske
verkar lite krångligt gjort, och jag borde kanske anpassat rutinen för ett
valfritt antal färger men då hade jag fått krångla lite mer med registren
så jag tyckte det fick gå ändå.
  I DrawLine rutinen har jag lagt till vid labeln .x2gx1 lite smart kod som
gör att ett spetsigt hörn i en polygon trubbas av så att blitterns
fyllrutin inte skapar fula ränder i kanterna. Prova gärna att plocka bort
koden fram till .notfix så ser ni vad jag menar.
  Jag har också ändrat mintermen en bit under labeln .signnl till att
använda EOR mode. Detta är nödvändigt i vektorrutiner för att kunna fylla
faces som hänger ihop korrekt. Ytor som ligger precis kant i kant och ska
fyllas kommer då att rita ut linjen två gånger där de överlappar varandra
och således invertera linjen (=plocka bort den) så att fyllningen fungerar
perfekt.
  I tabellen Octant_Table har jag bytt ut +1 mot +3, vilket sätter SING
biten i oktanten. SING betyder SINGle point per line och krävs vid fyllning
för att slippa flera punkter på samma rad för samma linje.
  Rutinen FillPage är också ny. Den liknar alltså en vanlig kopiering, men
som ni ser adderar jag (Screensize*Nrofbpl)-2 för att peka på det allra
sista wordet i det jag fyller. I BLTCON1 skriver jag in Exclusive Fill
Enable och Descending mode som vi diskuterade tidigare. Resten är normalt
för SRCA!DEST-kopiering.
  Jag har lagt till de två sidorna som inte behövdes i förra program
exemplet vid labeln Lines. De sex raderna beskriver vektorobjektets alla
polygon-ytor. Det enda nya är att jag infogat ett färgnummer före koordinat
numren. Genom att ange samma färgvärde för sidorna som sitter mitt emot
varandra så undviker man att de hamnar så att man inte ser gränsen mellan
ytorna. Prova gärna att sätta alla färger till 1 så ser ni att det inte är
så lyckat.
