                          - SEPTIC'S DEMOSKOLA -

                                Lektion 11
                                
                        Skriven av Vicious / Septic

                                 7 Jan 94



                                 Inledning
                                 
Vlkommen till rets frsta lektion i Demoskolan! Nu brjar vi ntligen
komma in p de intressanta delarna, tycker ni inte det? Idag ska jag
nmligen frklara tredimensionell vektor-rotation som nr det kom fr ngra
r sen skapade en revolution i demovrlden och som nnu idag anvnds
flitigt i demos.
  Om den hr texten ter sig svr eller rentav omjlig att begripa ska ni
inte bli oroliga. Det kan ta ett tag (och ngra genomlsningar) om man ska
fullt frst hur det hela fungerar, och man har mycket nytta av att ha lst
matte p gymnasiet i ett par r.
  Men, nu kr vi igng istllet fr att snacka...


                                   Teori
                                   
Nr man pratar om vektorer inom demovrlden menar man oftast ett "vektor-
objekt", dvs ett objekt uppbygggt av vektorer. En vektor r egentligen
(matematiskt sett) en visare med viss bestmd lngd som pekar p en punkt.
Visaren kan beskrivas genom att man anger lngd och vinkel, och det kan se
ut ungefr s hr:

     9|
     8|
     7|
     6|
     5|
     4|   _
     3|   /|
     2|  /
     1| /
     0|/)a_________________
       0 1 2 3 4 5 6 7 8 9 

Den hr vektorn pekar p en punkt som har koordinaterna 2;4. Om vektorn
skulle beskrivas normalt skulle man sga att den hade lngden 4.47 och
vinkeln 63.4. Lngden fr man genom Pythagoras sats hr, och vinkeln genom
tangens.
  Sna hr vektorer med lngd och vinkel r inte vanligt nr man gr
vektor-rotationer p datorn (vilket det logiskt sett borde vara, eftersom
det heter VEKTOR-rotation). Istllet lter man vektor presenteras med hjlp
av dess ndpunkt, som i vrt fall ligger i punkten 2;4, dvs X-koordinaten
r 2, och Y-koordinaten r 4.

     9|
     8|
     7|
     6|
     5|
     4|    * (2;4)
     3|
     2|
     1|
     0|____________________
       0 1 2 3 4 5 6 7 8 9 

Som ni ser s har vi allts nu en vanlig punkt i vrt tvdimensionella
koordinatsystem. Om vi nu skulle ta och rotera den runt origo (0;0) s att
den flyttar sig p en cirkulr bana runt nollstllet s frstr vi snart
varfr vi inte anvnder en vektors normala representations system med
lngd, vinkel. D skulle vi fr varje punkt bli tvungna att ta fram X och Y
hjd med Pythagoras sats, och allts behva berkna tv kvadrater och en
kvadratrot fr att f fram den nya punktens koordinater.
  Nu kommer istllet vra kunskaper i trigonometri till anvndning! Om vi
utgr frn att vr punkt befinner sig i sitt utgngslge kan vi ltt rkna
om detta utgngslge till 360 olika vinklar med hjlp av sinus och cosinus.
Lt mig frst gra en ndring i koordinatsystemet:

     9|
     8|
     7|
     6|
     5|
     4|
     3|
     2|
     1|
     0|____*_______________
       0 1 2 3 4 5 6 7 8 9 

Hmmmm, vad skulle det dr vara bra fr? Jag nollstllde helt enkelt
Y-koordinaten men behll X-koordinaten och punkten hamnade sledes nere p
2;0 istllet fr 2;4. Jo, snart ska du f se att det hela blir mycket
lttare att tnka ut om man flyttar ner punkten s hr, eftersom vi d fr
reda p hur lng ut punkten ligger. Vi kan tnka oss att punkten r fast-
bunden med en trd i origo, och att trden har lngden L. (L blir ju allts
2 i det hr fallet...)
  Vi utgr frn att noll grader ligger parallellt med vr X-axel hr, och
s sger vi att vi ska rotera punkten till vinkel a. Figuren skulle d se
ut s hr ungefr:

     9|
     8|
     7|
     6|
     5|
     4|
     3|
     2|  *
     1| /
     0|/)a_________________
       0 1 2 3 4 5 6 7 8 9 

Eftersom vi vet lngden p vektorn (det blir ju i sjlva verket bara
X-koordinaten nr vi satte Y till noll) s ser vi att den nya X-koordinaten
kan berknas med cosinus:

     2|          *
      |         /|
      |        / |
      |       /  |
      |    L /   | y
     1|     /    |
      |    /     |
      |   /      |
      |  /       |
      | /       _|
     0|/)a_____|_|__________
       0    x    1         2

Lngden L r ju, som jag sa, likadan hela tiden, eftersom trden som vi har
hngt punkten i alltid mste vara lika lng. Fr att f fram det nya
X-vrdet nu anvnder vi det tigonometriska sambandet som sger att cos
a=nrliggande katet dividerat med hypotenusan. Vi fr allts X=L*COS(a).
  Vi kan sen gra nstan samma sak fr att rkna ut den nya Y-koordinaten,
genom att anvnda sambandet sin a=motstende katet dividerat med
hypotenusan. D fr vi Y=L*SIN(a).
  Som jag nmnde innan s r ju lngden ekvivalent med X, eftersom vi satte
Y-koordinaten till noll. Allts kan vi skriva om de tv sambanden till:

   X=X*COS(a)
   Y=X*SIN(a)

Nu har vi lrt oss att rotera en punkt som ligger p X-axeln, men oftast r
ju inte fallet s enkelt. Frn brjan hade vi ju vr vektorpunkt p
koordinaten 2;4, s d mste vi gra ngot t Y-delen ocks.
  Vi gr p samma stt (fast tvrtom :-) och stter X till noll istllet:

     9|
     8|
     7|
     6|
     5|
     4|*
     3|
     2|
     1|
     0|____________________
       0 1 2 3 4 5 6 7 8 9 

Nu blir lngden L istllet lika med Y, och vr rotationsvinkel a blir a+90
eftersom Y-axeln ligger 90 grader ifrn X-axeln. Om vi stoppar in detta i
samma koordinatsystem som innan, s fr vi de nya formlerna:

   X=Y*COS(90+a)
   Y=Y*SIN(90+a)

Vi kan optimera de tv uttrycken lite om vi fljer ngra trigonometriska
lagar som sger att COS(90+a)=-SIN(a) och SIN(90+a)=COS(a). Om vi ndrar
detta i de tv uttrycken fr vi:

   X=Y*-SIN(a) <=> X=-Y*SIN(a)
   Y=Y*COS(a)

Sdr...S lngt allt vl! Nu har vi tv uttryck fr att rotera en punkt
som ligger p X-axeln, och tv uttryck fr att rotera en punkt som ligger
p Y-axeln. Vill man rotera en punkt som ligger fritt ute i den
tvdimensionella rymden nu, s fr man anvnda sig av alla uttrycken fr
att rkna ut den totala komposanten fr X och Y. Vad man gr r allts att
man lgger ihop formlerna fr X och formlerna fr Y:

   Ny X=X*COS(a)-Y*SIN(a)
   Ny Y=X*SIN(a)+Y*COS(a)

Detta r nu en grundformel fr tvdimensionell rotation (vilket r samma
sak som rotation runt Z-axeln i ett tredimensionellt koordinatsystem) och
om vi anvnder det p vr punkt 2;4 och roterar den till vinkeln 30, s
fr vi fljande:

   Ny X=2*COS(30)-4*SIN(30)
   Ny Y=2*SIN(30)+4*COS(30)

   Ny X=-0.27
   Ny Y=+4.46

Punktens nya koordinat blir allts -0.27;4.46 nr vi roterar den 30 grader.

Men, hur blir det nu med TRE dimensioner istllet? Jo, i ett
tredimensionellt koordinatsystem lgger man till en axel, Z-axeln, som
pekar rakt in i skrmen. En punkt i ett sdant system krver tre stycken
koordinater istllet fr tv. En som beskriver dess position i X-led, en i
Y-led och en i Z-led.

       y
     9|
     8|
     7|
     6|
     5|
     4|    z
     3|   /3
     2|  /2
     1| /1
     0|/0__________________x
       0 1 2 3 4 5 6 7 8 9 

  Den rotation vi gjort tidigare (i tv dimensioner) r egentligen en
Z-rotation eftersom vi roterat runt Z-axeln. Vill man rotera runt Y-axeln
istllet, s kan man tnka sig att man snurrar sitt koordinat-system s att
Y pekar int istllet fr Z p det hr viset:

       z
     9|
     8|
     7|
     6|
     5|
     4|
     3|
     2|
     1|
     0|____________________x
     / 0 1 2 3 4 5 6 7 8 9 
    /1
   /2
  /3
 y

Vad jag har gjort hr r allts bara att Y-axeln har fallit framt och
Z-axeln rest sig bakom och stllt sig lodrtt upp p bilden. Nu har vi
precis samma koordinatsystem som nr vi roterade tvdimensionellt med det
undantaget att Z har ersatt Y.
  Frgan r d nu bara: Kan man strunta i Y koordinaten och rotera Z och X
med samma formler som vi gjorde med X och Y nr vi struntade i Z?
  Svaret r JA! Givetvis kan vi det! Vi byter bara ut Y mot Z i vra
formler, och sledes blir formeln fr Y-rotation:

   X=X*COS(a)-Z*SIN(a)
   Z=X*SIN(a)+Z*COS(a)

Mycket enkelt och smidigt, allts. Hr r formlerna fr att rotera i X, Y
och Z led:

Rotera runt X-axeln:
-------------------
Z=Z*COS(a)-Y*SIN(a)
Y=Z*SIN(a)+Y*COS(a)

Rotera runt Y-axeln:
-------------------
X=X*COS(a)-Z*SIN(a)
Z=X*SIN(a)+Z*COS(a)

Rotera runt Z-axeln:
-------------------
X=X*COS(a)-Y*SIN(a)
Y=X*SIN(a)+Y*COS(a)

Nu har vi bara ett problem kvar att lsa, nmligen HUR SKA MAN KUNNA VISA
TREDIMENSIONELLA KOORDINATER P EN TVDIMENSIONELL SKRM?

Tja, det KAN lsas med Pythagoras sats, men eftersom Pythagoras sats skall
undvikas nr man vill gras snabba berkningar (eftersom det innehller
kvadratrtter och kvadreringar) s mste vi hitta p ett annat stt.
  Om vi tnker oss att vi har vr punkt en bra bit in i skrmen och bort
frn oss, s kan vi tnka oss att det gr en trd frn vrt ga ut till
punkten och samtidigt skr skrmens plan. Dr trden skr skrmen blir
allts punktens riktiga projicerade koordinat.
  Hur ska man nu ta reda p var trden skr skrmen nu d? Jo, genom
likformighet kan vi utan strre problem plocka fram ett enkelt samband.
  Lt oss rita upp en bild av hur det hela kan se ut om vi tnker oss att
vi tittar p vr rymd uppifrn:

                 X-led
     <--------------------------->
                   x1
          ------------------*  - Vr punkt r hr
          |                 /
          |                /
          |               /         /|\
          |              /           |            X1=Punktens X-koordinat
        z |             /            |             Z=Punktens Z-koordinat
          |            /             |             d=Distans till skrmen
          |           /              |  Z-led     X2=Punktens nya X-koord.
          |          /               |
          |   x2    /                |
     _____|________/________         |
          |       /          Skrm   |
          |      /                   |
          |     /                    |
        d |    /                     |
          |   /                      |
          |  /                       |
          | /                       \|/
          |/
          o -Hr befinner sig betraktaren

Om vi tnker oss att den hr vyn r ritad uppifrn, s att vi kikar ner p
vr punkt, s ser vi att tv likvinkliga trianglar bildats, och allts r
likformiga.
  Som ni ser har jag lagt in en bokstav D mellan betraktaren och skrmen.
Detta r DISTANSEN. Om vi gr en jmfrelse med att titta p en tavla s
ser vi inte s bra om vi tittar nda inp den. Allts behver vi en distans
till skrmen som vi tittar p.
  Vi r allts intresserade av vrdet X2, vilket r punktens aktuella
vrdet projicerat p bildskrmen. Likformigheten sger oss att sidan X1
frhller sig till Z+d som X2 till d:

      X1     X2
     ---- = ----
     Z+d     d

X2 blir allts:

            X1*d
       X2 = ----
             Z+d

Detta gller givetvis ocks fr Y-koordinaten. Man byter bara ut X1 och X2
mot Y1 och Y2 i formeln istllet.
  Fr att kunna zooma bilden kan man dock byta ut distansen i tljaren mot
ett DJUP-vrde. Formlerna fr X och Y ser d ut s hr istllet:

            X1*DJUP
       X2 = -------
            Z+DIST

            Y1*DJUP
       Y2 = -------
            Z+DIST

Djupet kan man sedan variera fr att gra objekt strre eller mindre.
Distansen skall dock inte ndras nn gng, utan stts till ett fast vrde.
Z+Distansen fr inte vara noll, eftersom det betyder att den projicerade
punkten har distansen noll till gat, allts befinner sig inuti gat, och
det skulle ju vara en fysisk omjlighet. Dessutom ger det DIVISION by ZERO,
hehe....

  Sdr, det var allt man behver fr att kunna rotera punkter i en
tredimensionell rymd!


                                 Register
                                 
Eftersom vektorer r ren och skr matematik finns det inga register att ta
upp idag. Alla berkningar tas om hand av processorn!


                              Programexemplet
                              
Vad har vi fr ngot smtt och gott hr idag d? Jo, en gammal goding frn
demos av ldre klass, nmligen en line-vektorkub! Det kanske ni tycker r
surt, men ngonstans mste man ju brja nr man visar vektorgrafik.
  Okej, ska vi kika p hur programmet funkar nu d...
  Frst och frmst har vi som vanligt en main-rutin som frst vntar p en
speciell rasterposition fr att synca vektorn till 50 frames/sek. Nr
skrmstrlen kommit till rtt stlle flippar vi fram sidan vi ritat ut vr
vektor p med rutinen SwapPages. Sidan vi just visat blir d istllet
"ritsida", allts den sida vi ska rita p.
  Sedan hoppar vi till en helt vanlig clear-rutin som rensar vr ritsida
och sedan den frsta intressanta rutinen, Rotate.
  Vi hoppar ner och kikar p vad den innehller direkt...
  Tja, vad har vi hr fr smtt och gott? Jo, frst stller jag in lite
pekare, dels p vra koordinater och en 2d-koordinatplats, plus sinus och
cosinustabellen. 2d-koordinatplatsen behvs eftersom vi inte ska frstra
vra original-koordinater. I 2d-koordinatplatsen lagras de koordinater som
vi ska anvnda nr vi ritar ut linjerna sedan.
  Efter att jag stllt in pekarna lser jag X, Y och Z koordinaterna i ett
enda svep till D0, D1 och D2. Drefter lagrar jag (temporrt) X-vrdet och
hmtar sedan vinkeln fr X-rotation. Jag multiplicerar den med 2 och hmtar
sedan cosinus fr vinkeln * 16384. Jag multiplicerar detta med Z och sen
gr jag likadant med Y fast med sinus. De tv vrdena (som r 16384 fr
stora) behandlas sedan s att Y dras ifrn Z och sedan skiftar jag med 14
(fr att dividera med 16384) och d fr vi den nya Z-koordinaten.
  Sedan gr jag p ungefr samma stt med Y-koordinaten (fljer formlen fr
X-rotation) och fr p s stt ett nytt Y-vrde. De hr nya vrdena ska
anvndas sedan i de bgge andra rotationerna som gr till p ungefr samma
stt.
  Efter alla rotationer kommer en liten kodsnutt som konverterar 3d
koordinater till 2d, och den fljer exakt formeln som jag bevisade i frra
kapitlet. Frst adderar vi en distans till Z, hmtar djup-vrdet och
multiplicerar det med X. Sedan dividerar vi med Z+distansen och fr det
tvdimensionella X-et smidigt och enkelt. Det fungerar p samma stt med Y
sedan, s det behver jag heller inte frklara nrmare...
  Om vi hoppar tillbaka och kollar i main-rutinen s ser vi att vi ska
hoppa till en ny spnnande rutin kallad DrawCube.
  I DrawCube initierar jag frst ngra adress-register som linjerutinen
krver. (Detta r samma linjerutin som vi konstruerade frra gngen som nu
kommer till nytta :-) Sedan pekar jag p 2d-koordinaterna (vi behver
allts inte 3d-koordinaterna hr, eftersom linjeritningen inte sker i tre
dimensioner) och p en speciell linjedata-tabell som jag ska frklara
lngre ner.
  Vi lser frn linjetabellen antal linjer i polygonen fr att veta hur
mnga gnger vi ska loopa vr linjeritningsrutin. Sen subtraherar vi med 2
eftersom den sista linjen alltid ska ritas tillbaka till den frsta s
behver den inte vara med i linjedata-tabellen, och fr att DBF krver
loopvrdet-1 fr att loopa rtt antal gnger.
  Sedan - i linjeutritnings rutinen som brjar med .DrawPoly - hmtar jag
frst ett koordinatnummer frn linjedatan. Jag tar det gnger 8 eftersom
det ligger en X och en Y-koordinat som varsitt longword i tabellen. Andra
punkten brjar allts p start+(2*4), och drav mste man multiplicera
koordinatnummer med 8.
  Efter det hmtar jag X och Y p den positionen vi fick fram och adderar
skrmmitten till dem bda. Detta blir linjens startpunkt.
  Sedan gr jag p samma stt med linjens slutpunkt, fast observera att jag
INTE flyttar fram linjedata-pekaren nr jag lst koordinatnumret! Detta gr
att nr vi ritar nsta linje kommer den att hnga ihop med fregende och
vi fr en sammanhngande polygon.
  Nr vi ritat AntalLinjer-1 anvnds den frsta koordinaten igen fr att
kunna rita frn den allra sista punkten till den frsta vilket gr att
polygonen stngs.
  Tillbaks till main-rutinen nu d igen...Hr hoppar vi terigen till en
rutin kallad NewAngle.
  Rutinen NewAngle r ganska simpel. Den addera helt enkelt bara rtt
hastighet till rtt vinkel och kollar om vinkeln verstiger 359 grader. Om
den gr det ska den "wrappas" runt s att 360 blir 0, 361 blir 1
osv...Detta stadkommer man genom att subtrahera 360 frn vinkeln.
  Allra sist i main-rutinen har vi en liten kodsnutt (vilket man egentligen
inte ska ha i mainloopen, eftersom koden d ltt blir grtig och
overskdlig :-) Den hr kodsnutten adderar bara talet 5 till djupet fr
att skapa en zoomeffekt nr kuben vxer. Nr den ntt vrdet 360 ska vi
inte addera mer, och d hoppas ADD-instruktionen ver...
  Om vi hoppar ner till slutet av sourcen nstan, till en label som heter
Coords, s ser vi kubens koordinater. Dessa r lagrade som X, Y, Z. Frsta
punkten ligger allts i -50,-50,-50. Antal punkter r lika med antal hrn
som vrt objekt har. En kub har tta hrn, och sledes fr man allts tta
punkter. Att jag anvnder negativa tal beror p att jag vill att roterings-
axeln ska ligga i mitten p objektet, eftersom roteringen alltid sker runt
punkten 0,0,0. Om man stter en punkt lngst ut p -50 i X-led, och sedan
en punkt lngst ut p 50 i X-led fr vi en 100 enheter bred kub, dr mitten
ligger p koordinaten 0. Allts ligger axeln dr ocks, och kuben roterar
runt sin mittpunkt.
  Man kan ndra axeln s att den tex ligger i hrnet istllet genom att
addera 50 till alla koordinater. Kuben blir d lika stor, men roterar p
ett helt annat, skojigt stt.
  Efter koordinatdatan ligger linjedatan. Den ligger lagrad s att frst
kommer det ett longword som talar om hur mnga linjer polygonen bestr av
och drefter kommer punkternas nummer. P frsta raden ser vi siffrorna
4,0,1,2,3. Detta betyder att vi ska rita en fyrhrnad polygon. Frsta
linjen ritas mellan punkt 0 (allts den frsta i koordinattabellen) och
punkt 1 (den andra punkten i koordinattabellen). Nsta linje ritas mellan 1
och 2, nsta mellan 2 och 3, och sedan den sista, ritas mellan 3 och 0
(eftersom vi binder ihop polygonen i DrawCube-rutinen ovan, om ni kommer
ihg det...).
  Vr kub har bara ftt fyra sidor hr i vr linjedata-tabell med
polygoner, trots att den egentligen har sex sidor. Anledningen till det r
att alla linjer som anvnds av de tv terstende sidorna r utritade redan
(eftersom de sammanfaller med de redan utritade polygonernas sidor) s
drfr behvde de inte ritas ut.
  Lngre ner, efter linjedata-tabellen, ligger sinus och cosinustabellen.
De hr tabellerna fr ni grna anvnda sjlva hur ni vill. Annars kan nya
tabeller (om man vill multiplicera med ett hgre eller lgre vrde n
16384, tex fr bttre decimal-noggrannhet) skapas i Basic. Det r bara att
generera sinus och cosinusvrden frn 0 till 359 (0-2pi) och innan man
lagrar dem i tabellen multiplicera med det vrdet man vill anvnda. Se bara
till att talen avrundas rtt, eftersom en del program, som anvnder heltal,
inte avrundar korrekt utan bara struntar i decimalerna om man inte anvnder
ett speciellt kommando som skter avrundningen.
  Japp, det var nog allt jag behvde bertta om programmet, eftersom det
andra har gtts igenom i tidigare lektioner.

