STARS.TXT

----------------------------- VLA.NFO -----------------------------------
        ╓────────── (% VLA Presents Intro To Starfields %) ──────────╖
        ║                                                            ║
        ╙────────────────── Written ßy : Draeden ────────────────────╜

───────────────────────────  VLA Members Are  ────────────────────────────────


                        (⌐ Draeden - Main Coder ¬)
                  (⌐ Lithium - Coder/Ideas/Ray Tracing ¬)
                   (⌐ The Kabal - Coder/Ideas/Artwork ¬)
                      (⌐ Desolation - Artwork/Ideas ¬)

────────────────────────── The Finn - Mods/Sounds ──────────────────────────────


   ╓─────────────────── Contact Us On These Boards: ──────────────────────╖
   ║                                                                      ║
   │   % Phantasm BBS .................................. (206) 232-5912   │
   │   * The Deep ...................................... (305) 888-7724   │
   │   * Dark Tanget Systems ........................... (206) 722-7357   │
   │   * Metro Holografix .............................. (619) 277-9016   │
   │                                                                      │
   ║       % - World Head Quarters      * - Distribution Site             ║
   ╙──────────────────────────────────────────────────────────────────────╜

     Or Via Internet Mail For The Group : tkabal@carson.u.washington.edu

                      Or to reach the other members :

                       - draeden@u.washington.edu -

                       - lithium@u.washington.edu -

                     - desolation@u.washington.edu -



┌───────────┬────────────────────────────────────────────────────────────────
│ STARS.TXT │
└───────────┘


────────────────────────────────────────────────────────────────────────────
;
;     TITLE: Star field
;WRITTEN BY: DRAEDEN
;      DATE: 03/15/93
;
;     NOTES:
;
;ASSOCIATED FILES:
;
;       STARGEN.BAS =>  Basic program that generates a set of 'randomized'
;                       numbers.  Creates STARRND.DW
;
;       STARS.ASM   =>  The asm file.
;
;       STARRND.DW  =>  File that contains a set of shuffled numbers order.
;                       Used to create 'random' star field.
;
────────────────────────────────────────────────────────────────────────────

    A star field is just a series of 3d point plotted onto a 2d plane (your
screen).  The movement effect is achieved by simply decreasing the Z
cordinate and redisplaying the results.  The formula for the 3d to 2d
conversion is:

────────────────
    ScreenX = ScreenDist * Xpos / Zpos
    ScreenY = ScreenDist * Ypos / Zpos
──────────────── 

    This should make perfect sense.  As the object gets futher away, (X,Y)
cordinates converge to (0,0).  The screen dist is how far away the 'eye' is
from the screen, or, as I like to think of it, the window.  Naturally, as you
get closer to the window, your field of view is greatly enhanced (you can see
more).  But, because we can't make the monitor bigger, we have to shrink the
data that is being displayed.  And when we have a large screen distance, we
should see less of the virtual world, and the objects should appear bigger.
When this formula is translated into assembler, you would immediatly decide
that 256 is the best screen distance.  Why?  Multiplying by 256 on the 386 is
as simple as this:

──────────────── 
;we want to multiply ax by 256 and put it into dx:ax to set up for division

    movsx   dx,ah   ;3 cycles
    shl     ax,8    ;3 cycles -- total 6

;or we could do it the 'normal way'...

    mov     dx,256  ;2 cycles, but we can have any screen distance
    imul    dx      ;9-22 cycles on a 386, 13-26 on a 486
                    ;a total of 11-28 cycles!
──────────────── 

    If you'll take note, the 6 cycle trick is AT LEAST 5 cycles faster than
the imul.  Anyway... I bet you really don't care about a few cycles at this
point, so I won't spend much more time on it... 
    So, as you can see, the math part of it is easy..  the hard part is the
what's left.  You need a routine that creates a star, presumably random, and
another routine that displays all the stars and advances them.  Well, that's
how I broke it into subroutines...

    For the routine that creates the star you need it to:

    1)  See if we already have enough stars going (is NUMSTARS > MAXSTARS ?)
    2)  If there's room, scan for the first open slot... 
    3)  Now that we've found where to put it, create a star by getting a set
        of random numbers for the (X,Y) and setting the Z to the maximum.
        Also select a color for the star.

    The display routine would need to:

    1)  Erase the old star.
    2)  Calculate the screen X & Y positions for the new position.  Are they 
        inside the screen boundries?  If not, 'kill' the star, otherwise 
        display it.  The shade of the color to use must be calculated by 
        using the Z cordinate. Color = BaseColor + Zpos / 256
    3)  Decrease the Zpos.

    And the main routine would:

    1)  Call MakeStars
    2)  Wait for verticle retrace
    3)  Call DisplayStars
    4)  Check for keypress, if there is one, handle it, if its not one we're
        looking for then exit program.
    5)  Loop to step 1

    To impliment this, we need to create an array of records which has enough
room for MAXSTARS.  The record would contain the (X,Y,Z) cordinates, the
OldDi and the base color for the star.  To create a star, it first checks to
see if there is room.  If there is, then we scan through the array
looking%wor an open slot.  If we don't find an empty space, then we don't
create a star.  We create the star by grabbing a pair of (X,Y) cordinates
from the list of 'random' numbers and set the Z to MAXZPOS.  Then, increase
NUMSTARS and return.

    In displaying the star, we would like to only have to calculate DI once.
So we save off a copy of DI in an array after we calculate it for the drawing
so that erasing the dot is really quick.  Next we calculate the new DI for
the dot.  This is done by using the formula mentioned above and this one: 

──────────────── 

    DI = ScreenY * ScreenWidth + ScreenX

──────────────── 

    When doing the math, care must be taken to make sure that:

        a) the Zpos is not zero and X*256/ZPOS is not greater than 32767.
            will cause a DIVIDE BY ZERO or a DIVIDE OVERFLOW

        b) SY and SX do not go outside the border of the screen.

    If either of these conditions are broken, the star must be terminated and
calculations for that star must be aborted.  Actually, Zpos = 0 is used to
signify a nonactive star.  To terminate the star, you'd simply change its
zpos to 0 and decrease NUMSTARS.

    To create the different shades, I used:

────────────────

  Color = BaseColor + Zpos/256

────────────────

    I used 256 as the number to divide by because that enables me to do no
dividing at all- I just use AH, because AH = AX / 256 (AH is the upper 8 bits
of AX). This relation suggests that the MAXZPOS shoul be 16*256 for 16
shades.  So, the MAXZPOS = 4096.  The palette will have to be set up so that
the shades go from light to black (lower # is lighter).  Simple enough. (I
hope.)

──────────────────────────────── 
        RANDOM NUMBERS
──────────────────────────────── 

    Well, not truly random numbers, but random enough for a starfield.

    The problem:
        There is no way on a PC to create truly random numbers with 
        great speed.

    Solution:
        Don't use truly random numbers.  Use a chart of non-repeating,
        shuffled numbers that fall within your desired range.  That way
        the stars will be evenly spread out and the creation of a new star
        is incredably fast. ( A few MOV instructions) All you have to is grab
        the number and increase the NEXTRANDOM pointer.  I chose to fill in
        the array half with positive numbers, half with negative with a
        minimum distance of 10 from 0.  I did this so that no stars will
        'hit' the screen and just vanish.  That doesn't look too good.

    Here's the BASIC file that made my numbers for me...

──────────────── 


    NumStars = 400
    dim     RndArray(NumStars)
    randomize (timer)

    'fill the array with numbers from -Numstars/2 to -10
    'and from 10 to Numstars/2

    i=10
    for r = 0 to NumStars/2
        RndArray(r)=i
        i=i+1
    next

    i=-10
    for r = NumStars/2 to NumStars
        RndArray(r)=i
        i=i-1
    next

    'randomly shuffle them..

    print "Total numbers: ";NumStars
    print "Shuffling - Please wait... "

    for q = 1 to numstars/5
        for r = 0 to NumStars
            swnum1 = int(rnd*NumStars+.5)
            swap RndArray(swnum1),RndArray(r)
        next
    next

    'write the numbers neatly to a file

    open "starrnd.dw" for output as 1
    cc= 0          ' CC is my "Column Control"
    print#1, "StarRnd dw ";:print#1, using"####";RndArray(0)
    for r = 1 to NumStars

        IF cc=0 THEN   ' is this the first one on the line?
            print#1, "dw ";:print#1, using"####" ;RndArray(r);
        ELSE 
            print#1, ",";:print#1, using"####"; RndArray(r);
        END IF

        cc=cc+1:if cc= 10 then cc=0:print#1," "   'goto the next line
    next
    close #1

──────────────── 

    This brings up another point.  Whenever you can write a program in a
higher level language to create data for you, do it.  It sure beats typing
then in by hand.  For instance, the palette was made using the REPT macro,
the actual data is created by the compiler at compile time.  Doing it that 
way happens to be a whole lot easier than typing in every byte.

    Last minute note: I rigged the plus and minus keys up so that they
control the 'Warpspeed' can be from 0 - MaxWarp, which I set to 90 or 
something like that.

─────────────────────────────────────────────────────────────────────────────

   Well, that's it for now.  See INFO.VLA for information on contacting us.

   I would like some suggestions on what to write code for.  What would you
   like to see done?  What code would you like to get your hands on?

   Send question, comments, suggestions to draeden@u.washington.edu or post
    on Phantasm BBS.


┌───────────┬────────────────────────────────────────────────────────────────
│ STARS.ASM │
└───────────┘


─────────────────────────────────────────────────────────────────────────────
;
;     TITLE: Star field
;WRITTEN BY: DRAEDEN
;      DATE: 03/15/93
;
;     NOTES: Need 386 to execute.
;
;ASSOCIATED FILES:
;
;       STARGEN.BAS =>  Basic program that generates a set of 'randomized'
;                       numbers.  Creates STARRND.DW
;
;       STARS.TXT   =>  The text file that explains starfields...
;
;       STARRND.DW  =>  File that contains a set of shuffled numbers.
;                       Used to create 'random' star field.
;
────────────────────────────────────────────────────────────────────────────

    DOSSEG
    .MODEL SMALL
    .STACK 200h
    .CODE
    .386
    ASSUME CS:@CODE, DS:@CODE
    LOCALS

;=== GLOBALS
;=== Data Includes

INCLUDE starrnd.dw      ;file that has label StarRnd numbers

;=== DATA Structures

    Star_Struc      STRUC
        X       dw  0
        Y       dw  0
        Z       dw  0
        OldDi   dw  0       ;where to erase last dot
        Color   db  0       ;BASE color. a number 0-16 is added to it
    Star_Struc      ENDS

    StarStrucSize = 9       ;number of bytes per entry

;=== DATA

ScreenWidth EQU 320
ScreenHeight EQU 200

NumRnds     EQU 400     ;number of random numbers defined

MaxZpos     EQU 4096
MinZpos     EQU 2
MaxStars    EQU 190
NumColors   EQU 5       ;number of Base colors in the Color Chart

WarpSpeed   dw  15      ;how quickly the stars move toward ya
MaxWarp     EQU 90

Xindex      dw  30      ;index into the StarRnd chart for X & Y
Yindex      dw  230     ; -note they must be different; set em the same to
                        ;see why
Cindex      dw  0       ;index into ColorChart

ColorChart  db  0,16,32,48,64,80    ;a list of base colors (-1)

Stars       Star_Struc MaxStars DUP (<>) ;where all the data is held
NumActive   dw  0       ;number of stars active

Palette     db  3 dup (0)   ;the palette.. first entrie is BG color (black)
    i = 15
    REPT    16
            db  2*i,3*i,4*i
        i=i-1
    ENDM
    i = 15
    REPT    16
            db  2*i,2*i,4*i
        i=i-1
    ENDM
    i = 15
    REPT    16
            db  3*i,3*i,4*i
        i=i-1
    ENDM
    i = 15
    REPT    16
            db  3*i,2*i,4*i
        i=i-1
    ENDM
    i = 15
    REPT    16
            db  3*i,3*i,3*i
        i=i-1
    ENDM
    i = 15
    REPT    16
            db  2*i,4*i,3*i
        i=i-1
    ENDM

;=== Code Includes
;=== SUBROUTINES

    ;finds 1st available slot for a star and puts it there
MakeStar PROC NEAR
    pusha
    mov     ax,cs
    mov     es,ax
    mov     ds,ax

    cmp     [NumActive],MaxStars    ;is there room for another star?
    jae     NoEmptySpace            

    ;search for 1st available slot

    mov     si,0
TryAgain:
    cmp     word ptr [Stars.Z+si],0     ;is this slot empty?
    je      GotOne                      ;yes, go fill it

    add     si,StarStrucSize
    cmp     si,MaxStars*StarStrucSize
    jb      TryAgain
    jmp     NoEmptySpace

GotOne:         ;si points to the record for the star to fill
    mov     di,[Yindex]         ;grab index for Ypos
    add     di,di               ;multiply by 2 to make it a WORD index
    mov     ax,[StarRnd+di]     ;get the number
    shl     ax,3                ;multiply by 8- could been done in BAS file
    mov     [Stars.Y+si],ax     ;and save off the number
    
    mov     di,[Xindex]         ;grab index for Xpos
    add     di,di               ;... same as above, but for Xpos
    mov     ax,[StarRnd+di]
    shl     ax,3
    mov     [Stars.X+si],ax

    mov     [Stars.Z+si],MaxZpos    ;reset Zpos to the max
    inc     [NumActive]             ;we added a star so increase the counter

    mov     di,[Cindex]             ;grab the color index
    mov     al,[ColorChart+di]      ;grab the BaseColor for the star
    mov     [Stars.Color+si],al     ;save it in the record

    ;increase all the index pointers

    inc     [Cindex]                ;increases the color counter
    cmp     [Cindex],NumColors
    jb      OkColor
    mov     [Cindex],0
OkColor:
    inc     [Yindex]                ;increases Yindex
    cmp     [Yindex],NumRnds        ;note that for this one we
    jb      YindNotZero             ; subtract NumRnds from Yindex if we
    sub     [Yindex],NumRnds        ; go off the end of the chart
YindNotZero:
    inc     [Xindex]                ;increase Xindex
    cmp     [Xindex],NumRnds        ;have we gone through the entire chart?
    jb      XindNotZero             ;nope...

;This clever bit of code makes more use out of the chart by increasing Yindex
; one additional unit each time Xindex goes through the entire chart... the
; result is nearly NumRND^2 random non-repeating points
        
    inc     [Yindex]                ;yes, so change Yindex so that we get a
    mov     ax,[Yindex]             ;new set of random (x,y)
    cmp     ax,[Xindex]             ;does Xindex = Yindex?
    jne     NotTheSame              ;if the index were the same, you'd see 
                                    ;a graph of the line Y = X, not good...
    inc     [Yindex]                ;if they are the same, inc Yindex again
NotTheSame:
    mov     [Xindex],0              ;reset Xindex to 0
XindNotZero:                        ;all done making the star...

NoEmptySpace:
    popa
    ret
MakeStar ENDP

DisplayStars PROC NEAR
    pusha
    mov     ax,cs
    mov     ds,ax
    mov     ax,0a000h
    mov     es,ax

    mov     si,0
DispLoop:
    mov     cx,[Stars.Z+si]
    or      cx,cx                   ;if Zpos = 0 then this star is dead...
    je      Cont                    ;continue to the next one- skip this one

    mov     di,[Stars.OldDi+si]     ;grab old Di
    mov     byte ptr es:[di],0      ;erase the star
    
    cmp     cx,MinZpos
    jl      TermStar                ;if Zpos < MinZpos then kill the star

    mov     ax,[Stars.Y+si]
    movsx   dx,ah                   ;'multiply' Ypos by 256
    shl     ax,8
    
    idiv    cx                      ;and divide by Zpos
    add     ax,ScreenHeight/2       ;center it on the screen
    mov     di,ax
    cmp     di,ScreenHeight         ;see if the star is in range. 
    jae     PreTermStar             ; If not, kill it
    imul    di,ScreenWidth          ; DI = Y*ScreenWidth

    mov     ax,[Stars.X+si]
    movsx   dx,ah                   ;multiply Xpos by 256
    shl     ax,8

    idiv    cx                      ;and divide by Zpos
    add     ax,ScreenWidth/2        ;center it on the screen
    cmp     ax,ScreenWidth          ;are we inside the screen boundries?
    jae     PreTermStar
    add     di,ax                   ; DI = Y * ScreenWidth + X

    mov     [Stars.OldDi+si],di     ;save old di

    ;calculate the color below

    add     ch,cs:[Stars.Color+si]  ;i'm dividing cx (the zpos) by 256 and
                                    ; putting the result in ch and adding
                                    ; the base color to it in one instruction
    mov     es:[di],ch              ;put the dot on the screen

    mov     ax,cs:[WarpSpeed]
    sub     cs:[Stars.Z+si],ax      ;move the stars inward at WarpSpeed

Cont:
    add     si,StarStrucSize        ;point to next record
    cmp     si,MaxStars*StarStrucSize   ;are we done yet?
    jb      DispLoop
    popa
    ret

PreTermStar:
    mov     [Stars.Z+si],1  ;this is here so that the star will get erased
    jmp     short Cont      ;next time through if I just went off and killed
                            ;the star, it would leave a dot on the screen
TermStar:
    mov     [Stars.Z+si],0  ;this actually kills the star, after it has
    dec     [NumActive]     ;been erased
    jmp     short Cont

DisplayStars ENDP

;=== CODE

START:
    mov     ax,cs
    mov     ds,ax
    mov     es,ax

    mov     ax,0013h                ;set vid mode 320x200x256 graph
    int     10h
    
    mov     dx,offset Palette
    mov     ax,1012h                ; WRITE palette 
    mov     bx,0                    
    mov     cx,256                  ;write entire palette
    int     10h                     ;doesn't matter if we didnt define it all

StarLoop:
    call    MakeStar        ;make stars 2x as thick
    call    MakeStar

    mov     dx,3dah
VRT:
    in      al,dx
    test    al,8
    jnz     VRT             ;wait until Verticle Retrace starts

NoVRT:
    in      al,dx
    test    al,8
    jz      NoVRT           ;wait until Verticle Retrace Ends

    call    DisplayStars

    mov     ah,1            ;check to see if a char is ready
    int     16h
    jz      StarLoop        ;nope, continue
    
    mov     ah,0
    int     16h             ;get the character & put in AX

    cmp     al,"+"          ;compare ASCII part (al) to see what was pressed
    jne     NotPlus

    inc     [WarpSpeed]
    cmp     [WarpSpeed],MaxWarp
    jbe     StarLoop

    mov     [WarpSpeed],MaxWarp
    jmp     StarLoop

NotPlus:
    cmp     al,"-"
    jne     NotMinus

    dec     [WarpSpeed]
    cmp     [WarpSpeed],0
    jge     StarLoop

    mov     [WarpSpeed],0
    Jmp     StarLoop

NotMinus:

    mov     ax,0003h        ;set 80x25x16 char mode
    int     10h
    mov     ax,4c00h        ;return control to DOS
    int     21h
END START



┌────────────┬───────────────────────────────────────────────────────────────
│ STARRND.DW │
└────────────┘


StarRnd dw  166
dw   67, 102,  46,-173,-154,-210,-192, 173,-196, -81 
dw  -50,  36,  50,-200, -95, 209, -16,-179, -30,  18 
dw  174, 197, 127,  71,  29,-121,-160,-176,  19, -52 
dw -185,  89, 172,  74,-156, 157,-125, 144, -34,  69 
dw   17, -40,  64, -98,-153, 125, 160, 140,-204, 141 
dw  137,-165, -14, 154,-146, 119, 123, 165,-130, 168 
dw -180, 143,  52, 107,-107,-102,  57,  27, 117,  37 
dw  126,  15, -89, 184, 116, 183, -99,-139, 150, 188 
dw   38,  90,  93,-194, 207,-187,  62,  59, 196,  12 
dw -174,  54, 146,-137, 198, 162, 155,-163, -77,-144 
dw  191,-132, -43, 151,-103,  20, -46,  13,-140,  31 
dw  130,-169,-188, 109, -33,-150,-170,  68, -75,-201 
dw -100,-171, -19, -61,-206, 149,  99, -76,-186, -44 
dw -178,  34,  61,  28, 114, 199, 201, -83, -27,  63 
dw  -38, 204, 208,-112,-208, 122, -90,  23,-122, 161 
dw   35,-168, 170,-164,-151,  75, -60,-109,  85, 193 
dw   45,-175,-134, 205, -21,  49, 133, -85, -47, -37 
dw  -29, -96, -66,  73,-118, 147, -53, 120, 153,-155 
dw  -11,  11,  95, -26, 134,-145, -49, -74,  42,-124
dw  189, -42,  92,-167,  88,-126,-129,-108,-193, 195 
dw  190,-106,-117, 203,  84, 139,-123, -94, -88,-158 
dw  181, -97, -20,  82, -57, 112, -35,  14, -56, -58 
dw  200,  80,-183, 106,  87,  30,  51, -28,  98, -12 
dw -191,-128, -13,-184, 136,  43,-166, -62, -73,-116 
dw  -31,-135,-101,  25,  41, -82, 110,  10, -45, -41 
dw   97, 175, 138, 171,  72,-133,-157,  58,-104, 187 
dw  192, -68, -87, 169,-110,  91, 129, 104, -70,-114 
dw -138,-115,-141, -67,-195, -79, -69,  40,-147, -80 
dw -119, 128, 152,-209,  83,  53, 159,  66,-190,  81 
dw  -92, -10,-181, 135,  60,  33, -25,  70,  22, -72 
dw  103, -23, 131,  79, -64,  55, -86, -32,-182,-136 
dw   26, -54,-172,-148, 148, -65,-152,-207, -39, -71 
dw   65, 179,-177,  24, 118, -59, -63,  44, 105, 206 
dw  178, -84,-202, 132, 186, -17,  76, 176, -22, 177 
dw -198,-159,-162,  78,  77, -55,-120,-203,-113, 156 
dw -189,-197, 124, 121,-142, -15,-205,  56, 158, -18 
dw  -93,-161,  39,  48, 101, -91, 182,-127, 108, 111 
dw  -36,-143,  21,-149, -78, -48, 164, 202, 185, 180 
dw  -51,-199, 100, 194,  32, -24, 142,  86,-111,  47
dw  115,-105,  16, 167,  94, 163,  96, 113,-131, 145


┌─────────────┬──────────────────────────────────────────────────────────────
│ STARGEN.BAS │
└─────────────┘


'
'Written by: Draeden /VLA
'      Date: 03/15/93
'
'     Notes: Used for generating 'random' data for Stars.asm
'


        NumStars = 400
        dim     RndArray(NumStars)
        randomize (timer)

        'fill the array with numbers from -Numstars/2 to -10
        'and from 10 to Numstars/2

        i=10
        for r = 0 to NumStars/2
                RndArray(r)=i
                i=i+1
        next
        i=-10
        for r = NumStars/2 to NumStars
                RndArray(r)=i
                i=i-1
        next

        'randomly shuffle them..

        print "Total numbers: ";NumStars
        print "Shuffling - Please wait... "

        for q = 1 to numstars/5
                for r = 0 to NumStars
                        swnum1 = int(rnd*NumStars+.5)
                        swap RndArray(swnum1),RndArray(r)
                next
        next

        'write the numbers neatly to a file

        open "starrnd.dw" for output as 1
        cc= 0
        print#1, "StarRnd dw ";:print#1, using"####";RndArray(0)
        for r = 1 to NumStars

                IF cc=0 THEN
                        print#1, "dw ";:print#1, using"####" ;RndArray(r);
                ELSE 
                        print#1, ",";:print#1, using"####"; RndArray(r);
                END IF

                cc=cc+1:if cc= 10 then cc=0:print#1," "
        next
        close #1