'Code supplement for the projection article at QBCM
'3d projection test
'applied to starfields
'SetVideoSEG by Plasma357
'Relsoft 2004
'rel.betterwebber.com
DECLARE SUB BubbleSort (Model() AS ANY)
DECLARE SUB StretchSprite (px%, py%, NewWid%, NewHei%, Sprite%(), Buffer%())
DECLARE SUB AF.Print (Xpos%, Ypos%, Text$, col%)
DECLARE SUB SetVideoSeg (Segment%)

DEFINT A-Z
REM $DYNAMIC


TYPE Point3d
        x       AS SINGLE           '3d x coord
        y       AS SINGLE           'ditto
        z       AS SINGLE           'ditto
        zvel    AS SINGLE           'ZVelocity
END TYPE


CONST LENS = 256                    'camera lens(FOV)
CONST XCENTER = 160                 'middle coords of screen 13
CONST YCENTER = 100                 'our center of projection
CONST MAXSTARS = 100                'number of stars
CONST MAXVEL = 2                    'Maximum speed of star


RANDOMIZE TIMER
REDIM SHARED Vpage(32009) AS INTEGER            'our buffer

DIM SHARED Stars(MAXSTARS) AS Point3d           'the stars


'$STATIC
DIM SHARED Scanline(199) AS INTEGER             'For our scanline buffer


'////Initialize the starting values of our stars
FOR i = 0 TO UBOUND(Stars)
    Stars(i).x = -160 + INT(RND * 320)           'x
    Stars(i).y = -160 + INT(RND * 320)            'y
    Stars(i).z = -256 + INT(RND * 255)               'z
    Stars(i).zvel = .1 + RND * MAXVEL           'speed of each star
NEXT i


CLS
SCREEN 13

size = ((16 ^ 2) + 4) \ 2               '16*16 sprite
DIM SHARED Ball(size)


'Grey Scale the Palette
FOR i = 0 TO 255
    OUT &H3C8, i
    OUT &H3C9, i \ 4
    OUT &H3C9, i \ 6
    OUT &H3C9, i \ 8
NEXT i

FOR y = -7 TO 8
FOR x = -7 TO 8
    dist! = SQR(x ^ 2 + y ^ 2)
    c% = (7 - dist!) * 32
    IF SQR(x ^ 2 + y ^ 2) < 7 THEN
        PSET (7 + x, 7 + y), c%
    END IF
NEXT x
NEXT y


GET (0, 0)-(15, 15), Ball(0)    'get sprite

Vpage(6) = 2560                      'Width 320*8
Vpage(7) = 200                       'Height
LAYER = VARSEG(Vpage(0)) + 1         'Buffer Seg(Ask Plasma)
SetVideoSeg LAYER                    'Set Draw to Buffer


T# = TIMER
Frame& = 0

DO
        Frame& = Frame& + 1
        SetVideoSeg LAYER                    'Set Draw to Buffer
        LINE (0, 0)-(319, 199), 0, BF       'clear the screen

        '///Move the stars
        '//Z=0 is 256 units away from the screen
        '//Adding values to Z moves the pixel towards us
        '//if Z > 256, the star is over our screen so reinitialize
        '//the stars Z value to 0(256 units away).
        FOR i = 0 TO UBOUND(Stars)
            Stars(i).z = Stars(i).z + Stars(i).zvel     'move it
            IF Stars(i).z > 255 THEN                'check for camera LENS
                Stars(i).z = -256                      'ReInit Z value
            END IF
        NEXT i
        BubbleSort Stars()
        FOR i = 0 TO UBOUND(Stars)
            sx! = Stars(i).x                        'StarX
            sy! = Stars(i).y                        'cleans the projectioon
            sz! = Stars(i).z                        'algo. ;*)
            Distance% = (LENS - sz!)                'get Distance
            IF Distance% > 50 THEN                     'if dist>10 then
                'Projection formula
                x% = XCENTER + (LENS * sx! / Distance%)
                y% = YCENTER - (LENS * sy! / Distance%)
                NewHei% = 16 * LENS / Distance%        'scale sprite
                NewWid% = 16 * LENS / Distance%        'new wid/hei
                StretchSprite x%, y%, NewWid%, NewHei%, Ball(), Vpage()
            ELSE
                                        'do nothing
                                        'you wouldn't wan't to
                                        'divide by 0 would ya? :*)
            END IF
        NEXT i
        SetVideoSeg &HA000              'Set Draw to screen
        WAIT &H3DA, 8                   'Vsynch
        PUT (0, 0), Vpage(6), PSET      'Blit out buffer to screen

LOOP UNTIL INKEY$ <> ""

DEF SEG

CLS
SCREEN 0
WIDTH 80
PRINT Frame& / (TIMER - T#)
c$ = INPUT$(1)


END

SUB AF.Print (Xpos%, Ypos%, Text$, col%)
'Prints the standard 8*8 CGA font
'Paramenters:
'Segment=the Layer to print to
'Xpos,Ypos=the coordinates of the text
'Text$=the string to print
'col= is the color to print(gradient)

x% = Xpos%
y% = Ypos%
Spacing% = 8
  FOR i% = 0 TO LEN(Text$) - 1
    x% = x% + Spacing%
    Offset% = 8 * ASC(MID$(Text$, i% + 1, 1)) + 14
    FOR j% = 0 TO 7
      DEF SEG = &HFFA6
      Bit% = PEEK(Offset% + j%)
      IF Bit% AND 1 THEN PSET (x%, y% + j%), col% + j%
      IF Bit% AND 2 THEN PSET (x% - 1, y% + j%), col% + j%
      IF Bit% AND 4 THEN PSET (x% - 2, y% + j%), col% + j%
      IF Bit% AND 8 THEN PSET (x% - 3, y% + j%), col% + j%
      IF Bit% AND 16 THEN PSET (x% - 4, y% + j%), col% + j%
      IF Bit% AND 32 THEN PSET (x% - 5, y% + j%), col% + j%
      IF Bit% AND 64 THEN PSET (x% - 6, y% + j%), col% + j%
      IF Bit% AND 128 THEN PSET (x% - 7, y% + j%), col% + j%
    NEXT j%
  NEXT i%

END SUB

SUB BubbleSort (Model() AS Point3d)
'Not the best sorting but gets the job done. ;*)
'don't you fret, I will teach you 3 more sorting algos. :*)

min = LBOUND(Model)
max = UBOUND(Model)
FOR i = min TO max      'loop through all the balls
FOR j = i TO max - 1
    IF Model(j).z > Model(j + 1).z THEN  'Swap if not in order
        SWAP Model(j), Model(j + 1)
    END IF
NEXT j
NEXT i

END SUB

SUB SetVideoSeg (Segment) STATIC

DEF SEG

IF VideoAddrOff& = 0 THEN ' First time the sub is called

' We need to find the location of b$AddrC, which holds the graphics
' offset (b$OffC) and segment (b$SegC). Since b$AddrC is in the default
' segment, we can find it by setting it to a certain value, and then
' searching for that value.

SCREEN 13 ' Set b$SegC to A000 (00A0 in memory)
PSET (160, 100), 0 ' Set b$OffC to 7DA0 (not needed in the IDE)

FOR Offset& = 0 TO 32764 ' Search for b$AddrC, which is
IF PEEK(Offset&) = &HA0 THEN ' in the default segment and
IF PEEK(Offset& + 1) = &H7D THEN ' should have a value of
IF PEEK(Offset& + 2) = &H0 THEN ' A0 7D 00 A0.
IF PEEK(Offset& + 3) = &HA0 THEN
VideoAddrOff& = Offset& + 2 ' If we found it, record the
EXIT FOR ' offset of b$SegC and quit
END IF ' looking. (Oddly, changing
END IF ' the b$OffC doesn't seem to
END IF ' do anything, so this is why
END IF ' this sub only changes b$SegC)
NEXT

END IF

' Change b$SegC to the specified Segment

POKE VideoAddrOff&, Segment AND &HFF
POKE VideoAddrOff& + 1, (Segment AND &HFF00&) \ &H100




END SUB

SUB StretchSprite (px%, py%, NewWid%, NewHei%, Sprite(), Buffer())

'px and py are the coordinates of the sprite
'Newwid and Newhei are the new dimensions of the sprite
'Sprite is a GET/PUT array
'Buffer is the virtual page
'uses 8.8 fixed point math for lil speed inside the IDE
'clipping supported
'Uses a Static array called scanline() for speed issues.

wid% = Sprite(0) \ 8
Hei% = Sprite(1)
xstep% = (wid% * 128 \ NewWid%)
ystep% = (Hei% * 128 \ NewHei%)

y% = py%
x% = px%
'Clip/Crop it
IF y% < 0 THEN
        CY = -y%
        NewHei% = NewHei% - CY
        y% = 0
        miny% = CY
ELSEIF y% > 199 THEN
        EXIT SUB
ELSE
        Ndy = y% + NewHei%
        IF Ndy > 199 THEN
                NewHei% = NewHei% - (Ndy - (200))
        END IF
END IF

IF x% < 0 THEN
        cx = -x%
        NewWid% = NewWid% - cx
        x% = 0
        minx% = cx
ELSEIF x% > 319 THEN
        EXIT SUB
ELSE
        Ndx = x% + NewWid%
        IF Ndx > 319 THEN
                NewWid% = NewWid% - (Ndx - 320)
        END IF
END IF

'ax=x
'bx=wid
'cx=y
'dx=hei


Vseg% = VARSEG(Buffer(0))
Voff% = VARPTR(Buffer(8))
SprSeg% = VARSEG(Sprite(0))
SprOff% = VARPTR(Sprite(2))
u& = 0
v& = 0
T20Mw = 320 - NewWid%
di& = Voff% + y% * 320& + x%        'start coords

v& = miny% * ystep%
minxstep& = minx% * xstep%

FOR y% = 0 TO NewHei% - 1
    u& = minxstep&
    ya = v& \ 128
    Temp& = ya * wid% + SprOff%
    Offset& = Temp&
    DEF SEG = SprSeg%
    FOR x% = 0 TO NewWid% - 1
        xa = u& \ 128
        Offset& = Temp& + xa
        Scanline%(x%) = PEEK(Offset&)
        u& = u& + xstep%
    NEXT x%
    DEF SEG = Vseg%
    FOR x% = 0 TO NewWid% - 1
        c% = Scanline(x%)
        IF c% THEN
            POKE di&, c%
        END IF
        di& = di& + 1
    NEXT x%
    v& = v& + ystep%
    di& = di& + T20Mw
NEXT y%

END SUB

