             3d-starfield tutorial by Zox / Storm 1997

    MIN, ERIK J SEESJRVI EN KIRJOITA TT DISCLAIMERIA, VAAN SYY SIIT
    KUULUU ILKELLE JOONAKSELLE JOKA KIRJOITTAA TT. ELI EJS EI OLE
    VALITETTAVASTI LUVANNUT MYSKN OLLA VASTUUSSA TUTORIAALISTA AIHEU-
    TUNEISTA ONGELMISTA, JOTEN TMNKIN TIEDOSTON SISLT KUULUU
    READJUST.NOW -DOKUMENTIN DISCLAIMERIN ALAISUUTEEN. KIITOKSIA HUOMIOSTA.

 3D-Starfield, eli naapurigalaksiin ja takaisin
 ----------------------------------------------

Jokainen on varmaan jossain yhteydess nhnyt sellaisen pienen,
yksikertaisen efektin nimelt 3D-Starfield? Jos nimi ei sano mitn,
niin kuvaus viimeistn kaiken: Eli siin lssi thti vilahtaa
perspektiivin ohitse, ja saa aikaan efektin, ett katsoja "liikkuisi"
kuvitteellisessa avaruusaluksessaan tai vastaavassa. Itseasiassa
tm efekti on helppoakin helpompi tehd, vaikein osa siit on
3D-maailmassa sijaitsevan pisteen projisointi 2D-pinnalle elikk
nytlle.

Eli ensimmiseksi meidn pit saada 3D-avaruuden piste "oikealle"
kohdalle ruudulla. Periaatetta en ryhdy tss selittmn, mutta
jos 3D-kiinnostaa yleenskin, kannattaa imuroida Ilkka Pelkosen
kirjoittama 3dica, joka on mainio 3D:t ksittelev tutoriaali, josta
tmkin kaava on kopioitu. Eli, saamme X,Y koordinaatit nin:

X = X * PERSPECTIVE / Z + ORIGOX;
Y = Y * PERSPECTIVE / Z + ORIGOY;

Tt varten pit mritell kolme vakiota: PERSPECTIVE, ORIGOX ja ORIGOY.
PERSPECTIVEn sopiva arvo riippuu tilanteesta, kytn esimerkkiohjelmassa
arvoa 256. ORIGOX on taas origon sijainti x-akselilla, eli yleens
160, kun kytetn tilaa 320x200. ORIGOY on vastaava y-akselille, ja
kokeillaampa sitkin ruudun keskipisteen eli arvoksi 100.

Ohjelman muut osat ovat erittin yksikertaisia. Tarvitsee vain
siirty tilaan 13h, ja toistaa tt looppia, kunnes jokainen thti
on ksitelty (pseudoa):

for(stop == TRUE)
{
  x = x * PERSPECTIVE / z + ORIGOX;         /* Tss thti pyyhitn     */
  y = y * PERSPECTIVE / z + ORIGOX;
  if(0 < x > 320 && 0 < y > 200) putpixel(x,y,0);

  z--;                                      /* Tss sijaintia muutetaan */
  if(z <= 0) z = 256;

  x = x * PERSPECTIVE / z + ORIGOX;         /* Tss thti piirretn    */
  y = y * PERSPECTIVE / z + ORIGOX;         /* uusiin koordinaatteihin   */
  if(0 < x > 320 && 0 < y > 200) putpixel(x,y,15);
}

Yksikertaista, eik vaan. Kaikkein parhaiten idean saa kaaliinsa
skippaamalla oheisen koodin (ainakin siksi aikaa kunnes eptoivo
saa vallan ja on _pakko vilkaista =), ja sheltmll itse oman
starfieldins.

#include <go32.h>
#include <sys/farptr.h>

#define PERSPECTIVE    256 /* niden tarkoitukset        */
#define ORIGOX         160 /* varmaan muistatkin         */
#define ORIGOY         100 /* jo nyt?                    */
#define NUMBEROFSTARS 2000 /* tm st thtien mrn */
#define SPEEDOFSTARS     1 /* thtien nopeus, yleens 1  */

#define putpixel(x,y,c) _farpokeb(_dos_ds, 0xA0000+(y*320+x), c)
#define waitsync()     while ( (inportb(0x03DA) & 0x08))\
                       while (!(inportb(0x03DA) & 0x08))
struct {
  int x,y,z;
 }starinfo[NUMBEROFSTARS];

void randomize_star(int value)
{
  starinfo[value].x = rand() % 320 - 160;
  starinfo[value].y = rand() % 200 - 100;
  starinfo[value].z = rand() % 256 + 1;
}

void main(void)
{
  int x,y,i;             /* koordinaattimuuttujat sek apumuuttuja */
  textmode(0x13);        /* siirrymme tilaan 13h */
  srand(time(0));        /* alustamme randomgeneraattorimme */

  for(i=0;i<NUMBEROFSTARS;i++)
    randomize_star(i);   /* joka thdelle koordinaatit */

  while(!kbhit()) {
    for(i=0;i<NUMBEROFSTARS;i++) {

      x = starinfo[i].x * PERSPECTIVE / starinfo[i].z + ORIGOX;
      y = starinfo[i].y * PERSPECTIVE / starinfo[i].z + ORIGOY;
      if(x > 0 && x < 320 && y > 0 && y < 200) putpixel(x,y,0);

      starinfo[i].z--;
      if(0 >= starinfo[i].z) {
        randomize_star(i);
        starinfo[i].z=256;
      }

      x = starinfo[i].x * PERSPECTIVE / starinfo[i].z + ORIGOX;
      y = starinfo[i].y * PERSPECTIVE / starinfo[i].z + ORIGOY;
      if(x > 0 && x < 320 && y > 0 && y < 200) putpixel(x,y,15);
      }
    waitsync();       /* niden pitisi vhn hidastaa */
    waitsync();       /* pyrimisnopeutta Pentiumeilla */
    }
  textmode(0x3);      /* paluu tekstimoodiin           */
}

Siin. Eihn ole yhtn monimutkaisempaa koodinakaan (no, minun tekem
koodiahan tm silti edelleen on ;)? Ohjelmassa esiteltiin muuten
makroversio wainsyncist, joka saattaa olla pienen aavistuksen
entist nopeampi.

Ja miksik tuo oli noin huononnkinen, karkea ja ruma? Siksi,
ett sit ei ole hyvksi tarkoitettukaan. Siihen on tarkoituksella
jtetty monta kohtaa, jotta lukijalle jisi rauhassa kehiteltv.
Min esimerkiksi tein omaani nopeusstimen nppimill (SPEEDOFSTARS),
pienen katseluefektin (ORIGOX/Y:ll ON sittenkin vaikutusta, vink vink ;),
sopivasti feidatun paletin (mit kauempana thti on, sit tummempi se on)
ja muuta mukavaa. Kannattaakin nauttia ajasta, jota voi viett
efektien parissa ennen kuin siirtyy nikortin ohjelmointiin =)
