'===========================================================================
' Subject: WIREFRAME-3D                       Date: 04-28-93 (00:00)       
'  Author: Rich Geldreich                     Code: PDS, VBDOS             
'  Origin: FidoNet QUIK_BAS Echo            Packet: EGAVGA.ABC
'===========================================================================
'WIREF4.BAS By Rich Geldreich
'Last modified April 28, 1993.
'For PDS/VB-DOS only. This program isn't totally completed, and
'doesn't have much documentation, yet. I'd appreciate your
'feedback!
'
DEFINT A-Z
'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-
CONST True = -1, False = 0
CONST MaxObject = 9
CONST MaxPlane = 99
CONST MaxPoint = (MaxPlane + 1) * 3 - 1
CONST MaxLine = (MaxPlane + 1) * 3 - 1
CONST PerspectiveScaler& = 768
CONST RotateScaler = 2048
CONST UserPlane = 75
'Constants for SCREEN 9, the EGA's 640x350x16 two page mode.
CONST XScreenRatio = 5, YScreenRatio = 4
CONST MinX = 0, MinY = 0
CONST MaxX = 639, MaxY = 349
CONST MidX = 320, MidY = 175
CONST LineColor = 15
CONST ViewDistance& = 7000& * 7000
'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-
DEF FNV (A) = ((A MOD 360) + 360) MOD 360
'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-
TYPE VectType
    A1              AS INTEGER
    A2              AS INTEGER
    A3              AS INTEGER
END TYPE
 
TYPE OrgType
    Ox              AS INTEGER
    Oy              AS INTEGER
    Oz              AS INTEGER
END TYPE
 
TYPE SineCosType
    S1              AS LONG
    C1              AS LONG
    S2              AS LONG
    C2              AS LONG
    S3              AS LONG
    C3              AS LONG
END TYPE
 
TYPE PointType
    x               AS INTEGER
    y               AS INTEGER
    z               AS INTEGER
    Rx              AS INTEGER
    Ry              AS INTEGER
    Rz              AS INTEGER
END TYPE
 
TYPE ObjectType
    Org             AS OrgType
    Direction       AS VectType
    PlaneIndice     AS INTEGER
    PointIndice     AS INTEGER
    MaxPlane        AS INTEGER
    MaxPoint        AS INTEGER
    MaxCord         AS INTEGER
    CullFlag        AS INTEGER
END TYPE
 
TYPE PlaneType
    Points(0 TO 3)  AS INTEGER
    MaxPoint        AS INTEGER
END TYPE
 
TYPE LineType
    sx              AS INTEGER
    sy              AS INTEGER
    ex              AS INTEGER
    ey              AS INTEGER
END TYPE
'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-
'Number of objects to read.
DATA 8
'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-
'--Objects Follow--
 
'    To  speed  things  up  I  recommend that very large, complicated
'objects be split up  into  numerous  smaller objects because objects
'which are totally behind the viewer are ignored.  If most of a gaint
'object is visible, then the entire object must be processed.  If the
'object is built of smaller objects then less of it will be processed
'when part of it is behind the user.
 
'--Object 0, box.
'Object's starting XYZ coordinates.
DATA -60,0,-200
'Object's starting rotatation angles.
DATA 0,0,0
 
'    If this value not zero, then the object can be culled.  (Culling
'removes hidden polygons from the object to  speed  up  plotting  and
'improve  clarity.)  For  culling  to work properly all planes in the
'object must be specified  in  counter-clockwise  order as if you are
'looking directly at it!
DATA -1
 
'Planes
'    The first number specifies  the  number  of points in the plane.
'If the plane contains 4 points, then a 3 would appear as  the  first
'number  on  the  DATA statement(0-3=4 points).  Planes with 3 points
'would be proceeded with a  2.  The  remaining numbers point into the
'point list.  A plane may have 2,3,  or  4  points.   Planes  with  2
'points cannot be culled and are treated as single lines.
 
DATA 3,0,1,2,3
DATA 3,1,5,6,2
DATA 3,5,4,7,6
DATA 3,4,0,3,7
DATA 3,4,5,1,0
DATA 3,3,2,6,7
'End the plane list with -32768.
DATA -32768
 
'Points
'       Each  XYZ  point in the object follows.  The plane list above
'refers to this list.  A point  may  be shared by more than one plane
'in its object, but points cannot be shared between objects.
DATA -100,-100,100
DATA 100,-100,100
DATA 100,100,100
DATA -100,100,100
DATA -100,-100,-100
DATA 100,-100,-100
DATA 100,100,-100
DATA -100,100,-100
'End the point list with -32768,0,0.
DATA -32768,0,0
'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-
'--Object 1, box.
DATA 0,0,300, 0,0,0, -1
DATA 3,0,1,2,3, 3,1,5,6,2, 3,5,4,7,6, 3,4,0,3,7, 3,4,5,1,0,3,3,2,6,7
DATA -32768
DATA -100,-100,100, 100,-100,100, 100,100,100, -100,100,100
DATA -100,-100,-100, 100,-100,-100, 100,100,-100, -100,100,-100
DATA -32768,0,0
'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-
'--Object 2, house.
DATA 0,0,0, 0,0,0, 0
DATA 3,0,1,2,3, 3,4,5,6,7, 1,1,5, 1,0,4, 1,3,7, 1,2,6
DATA -32768
DATA -1000,-400,1000, 1000,-400,1000, 1000,100,1000,-1000,100,1000
DATA -1000,-400,-1000, 1000,-400,-1000, 1000,100,-1000,-1000,100,-1000
DATA -32768,0,0
'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-
'--Object 3, doorway and sidewalk.
 
DATA 0,0,0, 0,0,0, 0
DATA 1,0,1, 1,2,3, 1,4,5, 1,6,7, 1,8,9
DATA -32768
DATA 150,100,1000, 150,-250,1000, -150,100,1000, -150,-250,1000
DATA -150,-250,1000, 150,-250,1000, -150,100,1000, -150,100,2500
DATA 150,100,1000, 150,100,2500
DATA -32768,0,0
'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-
'--Object 4, first window.
DATA 0,0,0, 0,0,0, 0
DATA 3,0,1,2,3
DATA -32768
DATA -500,-100,1000, -300,-100,1000, -300,-250,1000,-500,-250,1000
DATA -32768,0,0
'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-
'--Object 5, second window.
DATA 0,0,0, 0,0,0, 0
DATA 3,0,1,2,3
DATA -32768
DATA 500,-100,1000, 300,-100,1000, 300,-250,1000, 500,-250,1000
DATA -32768,0,0
'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-
'--Object 6, pyramid.
DATA 0,100,3000, 0,0,0, -1
DATA 3,3,2,1,0, 2,4,2,3, 2,4,1,2, 2,4,0,1, 2,4,3,0
DATA -32768
DATA -300,0,-300, 300,0,-300, 300,0,300, -300,0,300, 0,-350,0
DATA -32768,0,0
'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-
'Object 7, roof
DATA 0,0,0, 0,0,0, 0
DATA 1,0,4, 1,1,4, 1,2,4, 1,3,4
DATA -32768
DATA 1000,-400,-1000, -1000,-400,-1000
DATA -1000,-400,1000, 1000,-400,1000, 0,-800,0
DATA -32768,0,0
'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-
DIM SHARED SineTable(0 TO 359 + 90) AS LONG
DIM SHARED Objects(0 TO MaxObject) AS ObjectType
DIM SHARED Planes(0 TO MaxPlane) AS PlaneType
DIM SHARED Points(0 TO MaxPoint) AS PointType
DIM SHARED PlaneIndice, ObjectIndice, PointIndice
 
DIM SHARED WorldOrg AS OrgType
DIM SHARED WorldVect AS VectType
DIM SHARED WorldSineCos AS SineCosType
DIM SHARED WorldSpeed
 
DIM SHARED LinesToErase(0 TO 1, 0 TO MaxLine) AS LineType
DIM SHARED MaxLineToErase(0 TO 1)
 
DIM SHARED LowX, LowY, HighX, HighY
 
DIM SHARED VidPage
'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-
PrintInstructions
InitializeTables
ReadObjects
InitializeWorld
 
LOCATE , 1
PRINT "Press any key!";
A$ = INPUT$(1)
DO
    A$ = INKEY$
    ProcessKey A$
    DoFrame
LOOP UNTIL A$ = CHR$(27)
END
 
'This function checks a plane to see if it can be  seen.   There  are
'faster  ways that don't involve all of this math, but this method is
'very elegant and easy to implement.
FUNCTION Check4Cull (BYVAL x0, BYVAL y0, BYVAL x1, BYVAL y1,BYVAL x2, BYVAL y2)
    Check4Cull = (CLNG(x1 - x0) * (y2 - y0) - CLNG(x2 - x0) *(y1 - y0)) >= 0
END FUNCTION
 
'This subroutine displays each object in the scene.
 
SUB DisplayObjects
    DIM ObjectVect AS VectType
    DIM ObjectSineCos AS SineCosType
    DIM ObjOrg AS PointType
 
    FOR A = 0 TO ObjectIndice
 
        ObjOrg.x = Objects(A).Org.Ox - WorldOrg.Ox
        ObjOrg.y = Objects(A).Org.Oy - WorldOrg.Oy
        ObjOrg.z = Objects(A).Org.Oz - WorldOrg.Oz
        RotateOrigPoint ObjOrg, WorldSineCos
 
        IF ObjOrg.Rz >= Objects(A).MaxCord THEN
 
            IF (CLNG(ObjOrg.x) * ObjOrg.x) + (CLNG(ObjOrg.y) *ObjOrg.y) + (CLNG(ObjOrg.z) * ObjOrg.z) < ViewDistance& THEN
 
            ObjectVect = Objects(A).Direction
            IF ObjectVect.A1 = 0 AND ObjectVect.A2 = 0 AND ObjectVect.A3 = 0 THEN
                FOR B = Objects(A).PointIndice TO Objects(A).PointIndice + Objects(A).MaxPoint
                    RotateOrigPoint Points(B), WorldSineCos
                    Points(B).Rx = Points(B).Rx + ObjOrg.Rx
                    Points(B).Ry = Points(B).Ry + ObjOrg.Ry
                    Points(B).Rz = Points(B).Rz + ObjOrg.Rz
                NEXT
            ELSE
                LookUpSineCos ObjectVect, ObjectSineCos
                FOR B = Objects(A).PointIndice TO Objects(A).PointIndice + Objects(A).MaxPoint
                    RotateOrigPoint Points(B), ObjectSineCos
                    Points(B).Rx = Points(B).Rx + ObjOrg.x
                    Points(B).Ry = Points(B).Ry + ObjOrg.y
                    Points(B).Rz = Points(B).Rz + ObjOrg.z
                    RotateNewPoint Points(B), WorldSineCos
                NEXT
            END IF
 
            I = MaxLineToErase(VidPage)
            LowX = 32767: LowY = 32767
            HighX = -32768: HighY = -32768
 
            IF Objects(A).CullFlag = True THEN
                FOR B = Objects(A).PlaneIndice TO Objects(A).PlaneIndice + Objects(A).MaxPlane
                    ProjectPlane Planes(B)
                NEXT
            ELSE
                FOR B = Objects(A).PlaneIndice TO Objects(A).PlaneIndice + Objects(A).MaxPlane
                    SELECT CASE Planes(B).MaxPoint
                    CASE 1
                        ProjectLine Points(Planes(B).Points(0)), Points(Planes(B).Points(1))
                    CASE 2
                        ProjectLine Points(Planes(B).Points(0)),Points(Planes(B).Points(1))
                        ProjectLine Points(Planes(B).Points(1)),Points(Planes(B).Points(2))
                        ProjectLine Points(Planes(B).Points(2)),Points(Planes(B).Points(0))
                    CASE ELSE
                        ProjectLine Points(Planes(B).Points(0)),Points(Planes(B).Points(1))
                        ProjectLine Points(Planes(B).Points(1)),Points(Planes(B).Points(2))
                        ProjectLine Points(Planes(B).Points(2)),Points(Planes(B).Points(3))
                        ProjectLine Points(Planes(B).Points(3)),Points(Planes(B).Points(0))
                    END SELECT
                NEXT
            END IF
 
            IF (LowX <= MaxX) AND (LowY <= MaxY) AND (HighX >=0) AND (HighY >= 0) THEN
                FOR B = I + 1 TO MaxLineToErase(VidPage)
                    LINE (LinesToErase(VidPage, B).sx,LinesToErase(VidPage, B).sy)-(LinesToErase(VidPage, B).ex,LinesToErase(VidPage, B).ey), LineColor
                NEXT
            ELSE
                MaxLineToErase(VidPage) = I
            END IF
 
            END IF
 
        END IF
 
    NEXT
 
END SUB
 
SUB DoFrame
 
    UpdateObjects
    SwitchScreen
    EraseScreen
    DisplayObjects
 
END SUB
 
'This subroutine plots one line to the screen and enters it into the
'current page's erase list.
SUB DrawLine (BYVAL x0, BYVAL y0, BYVAL x1, BYVAL y1)
    DIM LineToPlot AS LineType
 
    IF x0 < LowX THEN LowX = x0
    IF y0 < LowY THEN LowY = y0
    IF x1 < LowX THEN LowX = x1
    IF y1 < LowY THEN LowY = y1
 
    IF x0 > HighX THEN HighX = x0
    IF y0 > HighY THEN HighY = y0
    IF x1 > HighX THEN HighX = x1
    IF y1 > HighY THEN HighY = y1
 
    LineToPlot.sx = x0
    LineToPlot.sy = y0
    LineToPlot.ex = x1
    LineToPlot.ey = y1
 
    A = MaxLineToErase(VidPage) + 1
    LinesToErase(VidPage, A) = LineToPlot
    MaxLineToErase(VidPage) = A
 
END SUB
 
'This subroutine erases all lines previously drawn on the current
'video page.
SUB EraseScreen
 
    FOR A = 0 TO MaxLineToErase(VidPage)
        LINE (LinesToErase(VidPage, A).sx, LinesToErase(VidPage,A).sy)-(LinesToErase(VidPage, A).ex, LinesToErase(VidPage,A).ey), 0
    NEXT
 
    MaxLineToErase(VidPage) = -1
END SUB
 
'This subroutine initializes the  sine  &  cosine lookup table.  Each
'entry is scaled up by a constant to avoid  floating  point  math  at
'runtime(in  assembly  this would already be calculated and stored as
'data.)
SUB InitializeTables
    FOR A = 0 TO 359
        SineTable(A) = INT(SIN(A / 57.2957) * RotateScaler + .5)
    NEXT
    FOR A = 360 TO 360 + 90 - 1
        SineTable(A) = SineTable(A - 360)
    NEXT
END SUB
 
'This subroutine initializes the variables used to describe the user's
'location and viewing angle.
SUB InitializeWorld
 
    WorldOrg.Ox = 0
    WorldOrg.Oy = -230
    WorldOrg.Oz = -600
    WorldVect.A1 = 0
    WorldVect.A2 = 0
    WorldVect.A3 = 0
 
    MaxLineToErase(0) = -1
    MaxLineToErase(1) = -1
    VidPage = 1
 
END SUB
 
'This function limits an integer into an inclusive range.
FUNCTION Limit (BYVAL A, BYVAL l, BYVAL H)
    IF A < l THEN A = l
    IF A > H THEN A = H
    Limit = A
END FUNCTION
 
FUNCTION LongLimit (BYVAL A&)
 
    IF A& >= -32768 AND A& <= 32767 THEN
        LongLimit = A&
    ELSE
 
        IF A& < 0 THEN
            LongLimit = -1
        ELSE
            LongLimit = MaxX + 1
        END IF
 
    END IF
 
END FUNCTION
 
'This subroutine takes a vector (3 angles) and looks them up in the
'sine & cosine table.
SUB LookUpSineCos (V AS VectType, A AS SineCosType)
    A.S1 = SineTable(V.A1)
    A.C1 = SineTable(V.A1 + 90)
 
    A.S2 = SineTable(V.A2)
    A.C2 = SineTable(V.A2 + 90)
 
    A.S3 = SineTable(V.A3)
    A.C3 = SineTable(V.A3 + 90)
END SUB
 
'This subroutine prints the instructions to the screen. (duh <g>)
SUB PrintInstructions
    PRINT "Simple Object Based Wireframe 3-D"
    PRINT "By Rich Geldreich (Zeek of Renaissance) April, 1993"
    PRINT "Use these keys to control position: (Turn NUMLOCK on!)"
    PRINT "4.............Turn Head Left"
    PRINT "6.............Turn Head Right"
    PRINT "8.............Go Forward"
    PRINT "9.............Go Backward"
    PRINT "5.............Stop motion"
    PRINT "A.............Total Reset"
    PRINT "Up Arrow......Turn Head Upward"
    PRINT "Down Arrow....Turn Head Downward"
    PRINT "Left Arrow....Tilt Head"
    PRINT "Right Arrow...Tilt Head"
    PRINT "U.............Y Coordinate -"
    PRINT "D.............Y Coordinate +"
    PRINT "L.............X Coordinate -"
    PRINT "R.............X Coordinate +"
    PRINT "ESC...........Quits"
    PRINT
    PRINT "Wait a sec!";
END SUB
 
'This subroutine gets called after each frame. It processes any
'keystrokes from the user and updates the location & viewing
'parameters.
SUB ProcessKey (A$) STATIC
 
    IF A$ <> "" THEN
        A = (ASC(RIGHT$(UCASE$(A$), 1))) * ((LEN(A$) > 1) OR 1)
        SELECT CASE A
        CASE 85
            WorldOrg.Oy = WorldOrg.Oy - 30
        CASE 68
            WorldOrg.Oy = WorldOrg.Oy + 30
        CASE 76
            WorldOrg.Ox = WorldOrg.Ox - 30
        CASE 82
            WorldOrg.Ox = WorldOrg.Ox + 30
        CASE 65
            WorldSpeed = 0
            A1 = 0: A2 = 0: A3 = 0
            WorldVect.A1 = 0
            WorldVect.A2 = 0
            WorldVect.A3 = 0
            WorldOrg.Ox = 0
            WorldOrg.Oy = -130
            WorldOrg.Oz = 0
        CASE -72 'up
            A3 = A3 - 3
        CASE -80 'down
            A3 = A3 + 3
        CASE -75 'left
            A2 = A2 + 3
        CASE -77 'right
            A2 = A2 - 3
        CASE 56  '8
            WorldSpeed = WorldSpeed - 15
        CASE 50  '2
            WorldSpeed = WorldSpeed + 15
        CASE 53, 32
            WorldSpeed = 0
            A1 = 0: A2 = 0: A3 = 0
        CASE 48
            A1 = 0: A2 = 0: A3 = 0
        CASE 52
            A1 = A1 - 3
        CASE 54
            A1 = A1 + 3
        END SELECT
        A1 = Limit(A1, -30, 30)
        A2 = Limit(A2, -30, 30)
        A3 = Limit(A3, -30, 30)
        WorldSpeed = Limit(WorldSpeed, -160, 160)
    END IF
 
    WorldVect.A1 = FNV(WorldVect.A1 + A1)
    WorldVect.A2 = FNV(WorldVect.A2 + A2)
    WorldVect.A3 = FNV(WorldVect.A3 + A3)
 
END SUB
 
'This  subroutine  takes  a  line  in  3-D  space  and calculates the
'starting and ending points of  the  line  on the viewing screen.  If
'the line passes the viewing plane, it is cut so the entire  line  is
'visible before the perspective calculation is applied.
SUB ProjectLine (p1 AS PointType, p2 AS PointType)
 
    x1 = p1.Rx
    y1 = p1.Ry
    z1 = p1.Rz
 
    x2 = p2.Rx
    y2 = p2.Ry
    z2 = p2.Rz
 
    IF (z1 < UserPlane) OR (z2 < UserPlane) THEN
        IF (z1 < UserPlane) AND (z2 < UserPlane) THEN EXIT SUB
 
        IF z1 < UserPlane THEN
            SWAP x1, x2
            SWAP y1, y2
            SWAP z1, z2
        END IF
 
        Xd& = x2 - x1
        Yd& = y2 - y1
        Zd& = z2 - z1
 
        z2 = UserPlane
        x2 = x1 + (Xd& * (UserPlane - z1)) \ Zd&
        y2 = y1 + (Yd& * (UserPlane - z1)) \ Zd&
    END IF
    sx = LongLimit(MidX + XScreenRatio * ((x1 *PerspectiveScaler&) \ z1) \ 10)
    sy = LongLimit(MidY + YScreenRatio * ((y1 *PerspectiveScaler&) \ z1) \ 10)
    ex = LongLimit(MidX + XScreenRatio * ((x2 *PerspectiveScaler&) \ z2) \ 10)
    ey = LongLimit(MidY + YScreenRatio * ((y2 *PerspectiveScaler&) \ z2) \ 10)
 
    DrawLine sx, sy, ex, ey
 
END SUB
 
SUB ProjectPlane (Plane AS PlaneType)
    DIM PointCut(0 TO 3)
 
    MP = Plane.MaxPoint
 
    IF MP = 1 THEN
        ProjectLine Points(Plane.Points(0)),Points(Plane.Points(1))
        EXIT SUB
    END IF
 
    FOR A = 0 TO MP
        IF Points(Plane.Points(A)).Rz < UserPlane THEN
            PointCut(A) = True
            PointsCut = PointsCut + 1
        END IF
    NEXT
 
    IF PointsCut = 0 THEN
        p0 = Plane.Points(0): p1 = Plane.Points(1)
        p2 = Plane.Points(2): p3 = Plane.Points(3)
        SELECT CASE MP
        CASE 2
            ProjectPoint Points(p0), x0, y0
            ProjectPoint Points(p1), x1, y1
            ProjectPoint Points(p2), x2, y2
        CASE 3
            ProjectPoint Points(p0), x0, y0
            ProjectPoint Points(p1), x1, y1
            ProjectPoint Points(p2), x2, y2
            ProjectPoint Points(p3), x3, y3
        END SELECT
        IF Check4Cull(x0, y0, x1, y1, x2, y2) = False THEN
            SELECT CASE MP
            CASE 2
             DrawLine x0, y0, x1, y1
             DrawLine x1, y1, x2, y2
             DrawLine x2, y2, x0, y0
            CASE ELSE
             DrawLine x0, y0, x1, y1
             DrawLine x1, y1, x2, y2
             DrawLine x2, y2, x3, y3
             DrawLine x3, y3, x0, y0
            END SELECT
        END IF
    ELSEIF PointsCut - 1 = MP THEN
        EXIT SUB
    ELSE
 
        FOR A = 0 TO MP
            IF PointCut(A) = False THEN EXIT FOR
        NEXT
 
        LastP = (A - 1): IF LastP < 0 THEN LastP = MP
        NextP = (A + 1): IF NextP > MP THEN NextP = 0
 
        A = Plane.Points(A)
        LastP = Plane.Points(LastP)
        NextP = Plane.Points(NextP)
 
        SplitLine Points(A), Points(LastP), XL, YL
        SplitLine Points(A), Points(NextP), XN, YN
        ProjectPoint Points(A), XM, YM
 
        IF Check4Cull(XM, YM, XN, YN, XL, YL) = False THEN
            p0 = Plane.Points(0): p1 = Plane.Points(1)
            p2 = Plane.Points(2): p3 = Plane.Points(3)
            SELECT CASE MP
            CASE 2
                ProjectLine Points(p0), Points(p1)
                ProjectLine Points(p1), Points(p2)
                ProjectLine Points(p2), Points(p0)
            CASE ELSE
                ProjectLine Points(p0), Points(p1)
                ProjectLine Points(p1), Points(p2)
                ProjectLine Points(p2), Points(p3)
                ProjectLine Points(p3), Points(p0)
            END SELECT
        END IF
    END IF
END SUB
 
SUB ProjectPoint (P AS PointType, nx, ny)
 
    nx = LongLimit(MidX + XScreenRatio * ((P.Rx *PerspectiveScaler&) \ P.Rz) \ 10)
    ny = LongLimit(MidY + YScreenRatio * ((P.Ry *PerspectiveScaler&) \ P.Rz) \ 10)
 
END SUB
 
SUB ReadObjects
    RESTORE
    READ ObjectIndice
 
    PlaneIndice = 0
    PointIndice = 0
 
    FOR Object = 0 TO ObjectIndice - 1
        READ Objects(Object).Org.Ox
        READ Objects(Object).Org.Oy
        READ Objects(Object).Org.Oz
        READ Objects(Object).Direction.A1
        READ Objects(Object).Direction.A2
        READ Objects(Object).Direction.A3
        READ Objects(Object).CullFlag
 
        Objects(Object).PlaneIndice = PlaneIndice
        Objects(Object).PointIndice = PointIndice
 
        NumPlanes = 0
        DO
            READ A
            IF A <> -32768 THEN
                Planes(PlaneIndice).MaxPoint = A
                FOR B = 0 TO A
                    READ A
                    Planes(PlaneIndice).Points(B) = A +PointIndice
                NEXT
                PlaneIndice = PlaneIndice + 1
                NumPlanes = NumPlanes + 1
            ELSE
                EXIT DO
            END IF
        LOOP
        Objects(Object).MaxPlane = NumPlanes - 1
 
        NumPoints = 0
        Max = -32768
        DO
            READ A, B, C
            IF A <> -32768 THEN
                IF ABS(A) > Max THEN Max = ABS(A)
                IF ABS(B) > Max THEN Max = ABS(B)
                IF ABS(C) > Max THEN Max = ABS(C)
                Points(PointIndice).x = A
                Points(PointIndice).y = B
                Points(PointIndice).z = C
                PointIndice = PointIndice + 1
                NumPoints = NumPoints + 1
            ELSE
                EXIT DO
            END IF
        LOOP
        Objects(Object).MaxPoint = NumPoints - 1
        Objects(Object).MaxCord = -(Max + 200)
    NEXT
    ObjectIndice = ObjectIndice - 1
 
    PlaneIndice = PlaneIndice - 1
    PointIndice = PointIndice - 1
END SUB
 
SUB RotateNewPoint (P AS PointType, A AS SineCosType)
 
    x1 = (P.Rx * A.C1 - P.Rz * A.S1) \ RotateScaler
    z1 = (P.Rx * A.S1 + P.Rz * A.C1) \ RotateScaler
 
    P.Rz = (z1 * A.C3 - P.Ry * A.S3) \ RotateScaler
    y2 = (z1 * A.S3 + P.Ry * A.C3) \ RotateScaler
 
    P.Rx = (x1 * A.C2 + y2 * A.S2) \ RotateScaler
    P.Ry = (y2 * A.C2 - x1 * A.S2) \ RotateScaler
 
END SUB
 
SUB RotateOrigPoint (P AS PointType, A AS SineCosType)
 
    x1 = (P.x * A.C1 - P.z * A.S1) \ RotateScaler
    z1 = (P.x * A.S1 + P.z * A.C1) \ RotateScaler
 
    P.Rz = (z1 * A.C3 - P.y * A.S3) \ RotateScaler
    y2 = (z1 * A.S3 + P.y * A.C3) \ RotateScaler
 
    P.Rx = (x1 * A.C2 + y2 * A.S2) \ RotateScaler
    P.Ry = (y2 * A.C2 - x1 * A.S2) \ RotateScaler
 
END SUB
 
SUB SplitLine (p1 AS PointType, p2 AS PointType, x0, y0)
 
    x1 = p1.Rx
    y1 = p1.Ry
    z1 = p1.Rz
 
    x2 = p2.Rx
    y2 = p2.Ry
    z2 = p2.Rz
 
    IF (z2 < UserPlane) THEN
 
        Xd& = x2 - x1
        Yd& = y2 - y1
        Zd& = z2 - z1
 
        z2 = UserPlane
        x2 = x1 + (Xd& * (UserPlane - z1)) \ Zd&
        y2 = y1 + (Yd& * (UserPlane - z1)) \ Zd&
 
    END IF
 
    x0 = LongLimit(MidX + XScreenRatio * ((x2 *PerspectiveScaler&) \ z2) \ 10)
    y0 = LongLimit(MidY + YScreenRatio * ((y2 *PerspectiveScaler&) \ z2) \ 10)
 
END SUB
 
SUB SwitchScreen
    VidPage = 1 - VidPage
    SCREEN 9, , VidPage, 1 - VidPage
    WAIT &H3DA, 8, 8
    WAIT &H3DA, 8
END SUB
 
SUB UpdateObjects
    STATIC Init, XMove, ZMove
 
    IF Init = False THEN
        XMove = 6
        ZMove = -4
        Init = True
    END IF
 
    Objects(1).Direction.A3 = FNV(Objects(1).Direction.A3 + 4)
    Objects(6).Direction.A1 = FNV(Objects(6).Direction.A1 - 4)
 
    x = Objects(1).Org.Ox
    x = x + XMove
    IF x < -800 OR x > 800 THEN XMove = -XMove: x = x + XMove
 
    Objects(1).Org.Ox = x
    z = Objects(1).Org.Oz
    z = z + ZMove
    IF z < -800 OR z > 800 THEN ZMove = -ZMove: z = z + ZMove
    Objects(1).Org.Oz = z
 
    mx = (WorldSpeed * WorldSineCos.C3) \ RotateScaler
    my = (WorldSpeed * WorldSineCos.S3) \ RotateScaler
    Mz = (mx * WorldSineCos.C1) \ RotateScaler
    mx = (mx * WorldSineCos.S1) \ RotateScaler
    WorldOrg.Ox = Limit(WorldOrg.Ox - mx, -16000, 16000)
    WorldOrg.Oy = Limit(WorldOrg.Oy + my, -16000, 16000)
    WorldOrg.Oz = Limit(WorldOrg.Oz - Mz, -16000, 16000)
    LookUpSineCos WorldVect, WorldSineCos
 
END SUB
 
'    That's all! Sorry that it only works under PDS or VB/DOS, but I love
'using BYVAL and having arrays in TYPE structures.
