VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "Application"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit

' Define the name of this class/module for error-trap reporting.
Private Const mc_ModuleName As String = "Peters3DEngine7.Application"

' ============================
' Simulation State / Settings.
' ============================
Public GameState As String
Private m_blnVideoFullScreen As Boolean
Private m_blnDetailClipBoundary As Boolean
Private m_blnDetailPartialPolygons As Boolean
Private m_blnDetailPolygonsOutside As Boolean
Private m_blnDetailLCDScreen As Boolean
Private m_intDetailDrawMode As Integer
Private m_blnDetailEdgedFaces As Boolean
Private m_blnDetailDayMode As Boolean
Private m_blnDetailShadows As Boolean
Private m_intInputMouseSensitivity As Integer
Private m_blnIgnoreEvent As Boolean

' This is where most of the drawing occurs.
' =========================================
Public WithEvents m_frmCanvas As frmCanvas
Attribute m_frmCanvas.VB_VarHelpID = -1
Private WithEvents m_frmSettings As frmSettings
Attribute m_frmSettings.VB_VarHelpID = -1


' This big-fat array holds ALL 3D objects in this aplication!
' (Arrays are much faster than VB Classes & Collections)
' ===========================================================
Private m_AllObjects()          As mdr3DObject
Private m_Primitives()          As mdrPrimitive

Private m_Camera                As mdr3DTargetCamera
Private m_matViewOrientation    As mdrMatrix4
Private m_matViewMapping        As mdrMatrix4
Private m_matViewPort           As mdrMatrix4

Private m_PlayerOne             As mdrPlayer

Private m_blnMouseCapture As Boolean
Private m_RenderOptions As Integer ' Temporary rendering options.

Private Sub Init_MIDISoundtrack()

    ' ==========================================
    ' Init. Multimedia Control & Open MIDI File.
    ' ==========================================
    With m_frmCanvas.MMControl1
        .Visible = False
        .DeviceType = "Sequencer"
        .FileName = App.Path & "\Europe - The Final Countdown.mid"
        .UpdateInterval = 1
        .Command = "OPEN"
'        .From = 575
'        .To = 630
        .Command = "PLAY"
    End With
    
End Sub
Private Sub DoMasterReset()
    
    ' =======================================
    ' General Settings. Change these for fun!
    ' =======================================
    m_blnDetailClipBoundary = True
    m_intInputMouseSensitivity = 16
    m_blnDetailLCDScreen = True
    m_blnVideoFullScreen = True
    m_blnDetailClipBoundary = False
    m_intDetailDrawMode = 1 ' << Wireframe, Flat, Gouraud.
    m_blnDetailEdgedFaces = True
    m_blnDetailDayMode = False
    m_blnDetailShadows = False
    
    ' ====================
    ' Setup form / canvas.
    ' ====================
    m_frmCanvas.ScaleMode = vbPixels
    
    ' ====================================
    ' Load Animation Scene from Text File.
    ' ====================================
    Call ReadAnimationFile
    
    ' ==========================================
    ' This routine is used to load some objects.
    ' ==========================================
    Dim objTestGrid As mdr3DObject
    Dim obj3DStudio As mdr3DObject
    
    ' ================
    ' Load "TestGrid".
    ' ================
    objTestGrid = LoadGroundGrid()
    objTestGrid.ID = "TestGrid"
    objTestGrid.Class = "GeometryDots"
    objTestGrid.Caption = "TestGrid"
    objTestGrid.Description = "TestGrid"
    objTestGrid.UniformScale = 1
    
    
    ' Load "3DStudio" Text.
    ' =====================
    obj3DStudio = mDirectXParser2.LoadXFile(App.Path & "\xFiles\3DStudio_only.X")
    obj3DStudio.ID = "3DStudio_only.X"
    obj3DStudio.Class = "Geometry"
    obj3DStudio.Caption = "3DStudio_only.X"
    obj3DStudio.Description = "3DStudio_only.X"
    obj3DStudio.UniformScale = 1
    obj3DStudio.WorldPosition.Y = 16


    ' ==============================
    ' Reserve space for the objects.
    ' ==============================
    ReDim m_AllObjects(1) As mdr3DObject
          m_AllObjects(0) = objTestGrid
          m_AllObjects(1) = obj3DStudio
    
    
    ' ===============================
    ' Reset Camera / Player Position.
    ' ===============================
    m_PlayerOne.WorldPosition.X = 0
    m_PlayerOne.WorldPosition.Y = 0
    m_PlayerOne.WorldPosition.Z = -2000
    m_PlayerOne.VPN.X = 0
    m_PlayerOne.VPN.Y = 0
    m_PlayerOne.VPN.Z = -1
    
    m_Camera.FreeCamera = False
    
    m_Camera.WorldPosition.X = 0
    m_Camera.WorldPosition.Y = 0
    m_Camera.WorldPosition.Z = -2000
    m_Camera.WorldPosition.w = 1
    
    m_Camera.LookAtPoint.X = 0
    m_Camera.LookAtPoint.Y = 0
    m_Camera.LookAtPoint.Z = -2001
    m_Camera.LookAtPoint.w = 1
    
    m_Camera.VPN.X = 0
    m_Camera.VPN.Y = 0
    m_Camera.VPN.Z = 1
    m_Camera.VPN.w = 1
    
    m_Camera.VUP.X = 0
    m_Camera.VUP.Y = 1
    m_Camera.VUP.Z = 0
    m_Camera.VUP.w = 1#
    
    ' Remember: PRP is specified in Camera Coordinates.
    m_Camera.PRP.X = 0 ' u
    m_Camera.PRP.Y = 0 ' v
    m_Camera.PRP.Z = 2.666 ' n << Change this value to distort the perspective!
    m_Camera.PRP.Z = 1.333
    m_Camera.PRP.w = 1#
    
    
    ' ==========================================================================
    ' The UV coordinate system is our 'virtual window' into the 3D world.
    ' --------------------------------------------------------------------------
    ' All objects in our scene are clipped to this 'virtual window'. After we
    ' have finished doing all the 3D calculations, we then transform everything
    ' within this 'virtual window' into the 'actual window'; this is our Windows
    ' Form. If the 'virtual window' is a perfect square, then we need to make
    ' sure the form we are drawing too is also a perfect square, otherwise we'll
    ' get an image that looks squashed. The normal aspect ratio of a computer
    ' monitor is 1:1.333  Some typicaly ratios are 640x480, 800x600, 1024x768
    '   Example: 1024 / 768 = 1.333
    ' For this reason, I often make the 'virtual window' the same shape as our
    ' form by making it the same aspect ratio.
    ' ==========================================================================
    m_Camera.Umin = (-1 + m_Camera.UPan) * 1.333
    m_Camera.Umax = (1 + m_Camera.UPan) * 1.333
    m_Camera.Vmin = -1 + m_Camera.VPan
    m_Camera.Vmax = 1 + m_Camera.VPan
    
    m_Camera.ClipNear = 0
    m_Camera.ClipFar = -2048 ' (any negative number)
        
    ' =================================================================
    ' The ViewPort (VP) bounds must be specified in pixels (not twips).
    ' -----------------------------------------------------------------
    ' Typicall values are as follows:
    '       VPXmin = 0
    '       VPXmax = 1024
    '       VPYmin = 0
    '       VPYmax = 768
    ' Note: I usually put a 128 pixel border around this for testing
    '       the clipping procedure.
    ' =================================================================
    m_Camera.VPXmin = 0
    m_Camera.VPXmax = m_frmCanvas.ScaleWidth
    m_Camera.VPYmin = m_frmCanvas.ScaleHeight
    m_Camera.VPYmax = 0
        
End Sub

Private Sub DoOutputStatistics(withHandle As Form, DayMode As Boolean)

    Dim lngTempOnly As Long
    Dim curTemp64BitInteger As Currency
    Dim lngColBright As Long
    Dim lngColDim As Long
    
    
    If DayMode = True Then
        lngColBright = RGB(0, 0, 255)
        lngColDim = RGB(160, 160, 255)
    Else
        lngColBright = RGB(255, 0, 0)
        lngColDim = RGB(128, 0, 0)
    End If
    
    
    ' Update debug & info. counters (conditionally compiled)
    ' NOTE: Putting this part in the display loop, isn't really a great idea, it slows things down.
    #If g_blnCompiledDebugInfo = True Then
    
        withHandle.Font = "Courier"
        withHandle.FontSize = 7
        withHandle.FontBold = False
        withHandle.ForeColor = lngColDim
        withHandle.CurrentY = withHandle.ScaleTop
        withHandle.CurrentX = withHandle.ScaleLeft

        lngTempOnly = g_lngMultiplicationCount + g_lngDivisionCount + g_lngAdditionCount + g_lngSubtractionCount + g_lngSquareRootCount
        If lngTempOnly = 0 Then lngTempOnly = 1
        
        withHandle.Print "Multiplications /frame : " & Format(g_lngMultiplicationCount, "###,000,000") & "  " & Format(g_lngMultiplicationCount / lngTempOnly, "00%")
        withHandle.Print "Divisions              : " & Format(g_lngDivisionCount, "###,000,000") & "  " & Format(g_lngDivisionCount / lngTempOnly, "00%")
        withHandle.Print "Additions              : " & Format(g_lngAdditionCount, "###,000,000") & "  " & Format(g_lngAdditionCount / lngTempOnly, "00%")
        withHandle.Print "Subtractions           : " & Format(g_lngSubtractionCount, "###,000,000") & "  " & Format(g_lngSubtractionCount / lngTempOnly, "00%")
        withHandle.Print "Square Roots           : " & Format(g_lngSquareRootCount, "###,000,000") & "  " & Format(g_lngSquareRootCount / lngTempOnly, "00%")
        withHandle.ForeColor = lngColBright
        withHandle.Print "Polygons Drawn         : " & Format(g_lngPolygonCount, "###,000,000")
        withHandle.ForeColor = lngColDim
        
        curTemp64BitInteger = (GetPerformanceCounter - g_curFramePerfCount) / g_curPerformanceFrequency
        withHandle.Print "Frame Draw Time (sec.) : " & curTemp64BitInteger
        
        withHandle.ForeColor = lngColBright
        withHandle.Print "Frame Rate (approx.)   : " & Format(1 / curTemp64BitInteger, "#,##0.0")
        
    #End If

End Sub

Private Sub DoUpdateSettingsWindow()

    If (m_frmSettings Is Nothing) Then Exit Sub
    If m_frmSettings.IsFormLoaded = False Then Exit Sub
    
    With m_frmSettings
        .txtCameraX.Text = m_Camera.WorldPosition.X
        .txtCameraY.Text = m_Camera.WorldPosition.Y
        .txtCameraZ.Text = m_Camera.WorldPosition.Z
        
        .txtClipNear.Text = m_Camera.ClipNear
        .txtClipFar.Text = m_Camera.ClipFar
        
        .txtDocResWidth.Text = m_Camera.VPXmax - m_Camera.VPXmin
        .txtDocResHeight.Text = m_Camera.VPYmin - m_Camera.VPYmax
        
        .txtFOV.Text = m_Camera.FOV
        .txtZoom.Text = m_Camera.Zoom
        .txtPRPz.Text = m_Camera.PRP.Z
        
    End With
    
End Sub

Private Sub PrintAnimationText(FontSize As Integer, Text As String)

    m_frmCanvas.Font = "Arial Bold"
    m_frmCanvas.FontSize = FontSize
    If m_blnDetailDayMode = True Then
        m_frmCanvas.ForeColor = vbBlack
    Else
        m_frmCanvas.ForeColor = vbWhite
    End If
    
    Dim sngTextWidth As Single
    Dim sngTextHeight As Single
    
    sngTextWidth = m_frmCanvas.TextWidth(Text)
    sngTextHeight = m_frmCanvas.TextHeight(Text)
    
    m_frmCanvas.CurrentX = (m_frmCanvas.ScaleWidth - sngTextWidth) / 2
    m_frmCanvas.CurrentY = (m_frmCanvas.ScaleHeight - sngTextHeight) / 2
    
    m_frmCanvas.Print Text
    
End Sub

Private Sub zTest()

    Dim intObject As Integer
    Dim intPart As Integer
    Dim intFace As Integer
    
    For intObject = LBound(m_AllObjects) To (UBound(m_AllObjects) - 1)
    
        For intPart = 0 To UBound(m_AllObjects(intObject).Parts)
            For intFace = 0 To UBound(m_AllObjects(intObject).Parts(intPart).Faces)
            
            Next intFace
        Next intPart
    Next intObject

End Sub

Private Function LoadGroundGrid() As mdr3DObject
    
    Dim sngX As Double
    Dim sngY As Double
    Dim sngZ As Double
    Dim lngVertexCount As Long
    
    ReDim LoadGroundGrid.Parts(0)
    
    Const sngMax = 1000!
    Const sngStep = 100!
    
    For sngZ = -sngMax To sngMax Step sngStep
        For sngX = -sngMax To sngMax Step sngStep
            For sngY = 0 To sngMax Step sngStep
                With LoadGroundGrid.Parts(0)
                    If (Abs(sngX) = sngMax) And (Abs(sngY) = sngMax) Then
                        ReDim Preserve LoadGroundGrid.Parts(0).Vertices(lngVertexCount)
                        .Vertices(lngVertexCount).Pxyz.X = sngX
                        .Vertices(lngVertexCount).Pxyz.Y = sngY
                        .Vertices(lngVertexCount).Pxyz.Z = sngZ
                        .Vertices(lngVertexCount).Pxyz.w = 1
                        .Vertices(lngVertexCount).RGB_Red = 255: .Vertices(lngVertexCount).RGB_Green = 127: .Vertices(lngVertexCount).RGB_Blue = 127
                        lngVertexCount = lngVertexCount + 1
                    ElseIf (Abs(sngY) = sngMax) And (Abs(sngZ) = sngMax) Then
                        ReDim Preserve LoadGroundGrid.Parts(0).Vertices(lngVertexCount)
                        .Vertices(lngVertexCount).Pxyz.X = sngX
                        .Vertices(lngVertexCount).Pxyz.Y = sngY
                        .Vertices(lngVertexCount).Pxyz.Z = sngZ
                        .Vertices(lngVertexCount).Pxyz.w = 1
                        .Vertices(lngVertexCount).RGB_Red = 127: .Vertices(lngVertexCount).RGB_Green = 255: .Vertices(lngVertexCount).RGB_Blue = 127
                        lngVertexCount = lngVertexCount + 1
                    ElseIf (Abs(sngX) = sngMax) And (Abs(sngZ) = sngMax) Then
                        ReDim Preserve LoadGroundGrid.Parts(0).Vertices(lngVertexCount)
                        .Vertices(lngVertexCount).Pxyz.X = sngX
                        .Vertices(lngVertexCount).Pxyz.Y = sngY
                        .Vertices(lngVertexCount).Pxyz.Z = sngZ
                        .Vertices(lngVertexCount).Pxyz.w = 1
                        .Vertices(lngVertexCount).RGB_Red = 127: .Vertices(lngVertexCount).RGB_Green = 127: .Vertices(lngVertexCount).RGB_Blue = 255
                        lngVertexCount = lngVertexCount + 1
                    ElseIf (sngX = 0) Then
                        ReDim Preserve LoadGroundGrid.Parts(0).Vertices(lngVertexCount)
                        .Vertices(lngVertexCount).Pxyz.X = sngX
                        .Vertices(lngVertexCount).Pxyz.Y = sngY
                        .Vertices(lngVertexCount).Pxyz.Z = sngZ
                        .Vertices(lngVertexCount).Pxyz.w = 1
                        .Vertices(lngVertexCount).RGB_Red = 255: .Vertices(lngVertexCount).RGB_Green = 0: .Vertices(lngVertexCount).RGB_Blue = 0
                        lngVertexCount = lngVertexCount + 1
                    ElseIf (sngY = 0) Then
                        ReDim Preserve LoadGroundGrid.Parts(0).Vertices(lngVertexCount)
                        .Vertices(lngVertexCount).Pxyz.X = sngX ' Rnd * (sngMax * 2) - sngMax 'sngX
                        .Vertices(lngVertexCount).Pxyz.Y = sngY
                        .Vertices(lngVertexCount).Pxyz.Z = sngZ ' Rnd * (sngMax * 2) - sngMax 'sngZ
                        .Vertices(lngVertexCount).Pxyz.w = 1
                        .Vertices(lngVertexCount).RGB_Red = 0: .Vertices(lngVertexCount).RGB_Green = 255: .Vertices(lngVertexCount).RGB_Blue = 0
                        lngVertexCount = lngVertexCount + 1
                    ElseIf (sngZ = 0) Then
                        ReDim Preserve LoadGroundGrid.Parts(0).Vertices(lngVertexCount)
                        .Vertices(lngVertexCount).Pxyz.X = sngX
                        .Vertices(lngVertexCount).Pxyz.Y = sngY
                        .Vertices(lngVertexCount).Pxyz.Z = sngZ
                        .Vertices(lngVertexCount).Pxyz.w = 1
                        .Vertices(lngVertexCount).RGB_Red = 127: .Vertices(lngVertexCount).RGB_Green = 127: .Vertices(lngVertexCount).RGB_Blue = 255
                        lngVertexCount = lngVertexCount + 1
                    End If
                End With
            Next sngY
        Next sngX
    Next sngZ
        
End Function

Private Sub RefreshF5()
    
    #If g_blnCompiledDebugInfo = True Then
        ' Reset debug/info. counters (conditionally compiled)
        g_lngMultiplicationCount = 0
        g_lngDivisionCount = 0
        g_lngAdditionCount = 0
        g_lngSubtractionCount = 0
        g_lngSquareRootCount = 0
        g_lngPolygonCount = 0
        g_curFramePerfCount = GetPerformanceCounter
    #End If
    
    ' =================
    ' Erase the screen.
    ' =================
    If m_blnDetailDayMode = True Then
        m_frmCanvas.BackColor = vbWhite
    Else
        m_frmCanvas.BackColor = vbBlack
    End If
    m_frmCanvas.Cls
    
    
    ' =================================================
    ' Place a 128 pixel border around the viewing area.
    ' =================================================
    If m_blnVideoFullScreen = True Then
        m_Camera.VPXmin = m_frmCanvas.ScaleLeft
        m_Camera.VPXmax = m_frmCanvas.ScaleWidth
        m_Camera.VPYmin = m_frmCanvas.ScaleHeight
        m_Camera.VPYmax = m_frmCanvas.ScaleTop
    Else
        m_Camera.VPXmin = m_frmCanvas.ScaleLeft + 64
        m_Camera.VPXmax = m_frmCanvas.ScaleWidth - 64
        m_Camera.VPYmin = m_frmCanvas.ScaleHeight - 64
        m_Camera.VPYmax = m_frmCanvas.ScaleTop + 128
    End If
    
    
    m_Camera.WorldPosition = m_PlayerOne.WorldPosition
    Call DoSetDrawingParamaters(m_Camera)
    m_Camera.FOV = ConvertRad2Deg(2 * Atn((m_Camera.Umax - m_Camera.Umin) / m_Camera.PRP.Z))
    m_Camera.Zoom = Round(1 / Tan(ConvertDeg2Rad(m_Camera.FOV) / 2), 5)
    
    
    ' =======================================================
    ' Map canonical view volume into 3D viewport view volume.
    ' =======================================================
    With m_frmCanvas
        m_matViewPort = Matrix_vv3dv(m_frmCanvas, m_Camera.VPXmin, m_Camera.VPXmax, m_Camera.VPYmin, m_Camera.VPYmax, -1, 0, m_blnDetailClipBoundary, m_blnDetailDayMode)
    End With
    
    ' =====================================================================================
    ' Draw shadows first. Note: To draw shadows, you have to traverse through the render
    ' pipeline twice, thus slowing everything down and doubling the number of calculations.
    ' =====================================================================================
    If (m_blnDetailShadows = True) And (m_Camera.WorldPosition.Y > 0) Then
        m_RenderOptions = 1
        Call RenderPipeline0
    End If
    
    ' ==============
    ' Draw normally.
    ' ==============
    m_RenderOptions = 0
    Call RenderPipeline0
    
    
    ' ==========================================
    ' Output some text to the screen. (optional)
    ' ==========================================
    Call DoOutputStatistics(m_frmCanvas, m_blnDetailDayMode)
    
    
    ' ==============================================
    ' Update settings window, but only if it's open.
    ' ==============================================
    Call DoUpdateSettingsWindow
    

    Screen.MousePointer = vbDefault

End Sub

Private Sub RenderPipeline0()

    On Error GoTo errTrap
    
    Dim intObject As Integer
    For intObject = LBound(m_AllObjects) To UBound(m_AllObjects)
        With m_AllObjects(intObject)
            
            ' Draw each Part within the 3DObject.
            Dim lngPart As Long
            For lngPart = 0 To UBound(.Parts)
            
                Dim vectLight As mdrVector4
                vectLight.X = -64
                vectLight.Y = 128
                vectLight.Z = 256
                vectLight.w = 1
                
                Dim vectPlaneEq As mdrVector4
                vectPlaneEq.X = 0
                vectPlaneEq.Y = 100
                vectPlaneEq.Z = 0
                vectPlaneEq.w = 1
                
                Dim matShadow As mdrMatrix4
                matShadow = MatrixShadow(vectLight, vectPlaneEq)
                
                Dim matOutput As mdrMatrix4
                Dim matWorld As mdrMatrix4
                matOutput = MatrixIdentity()
                
                .Parts(lngPart).IdentityMatrix = MatrixIdentity
                matOutput = MakeWorldMatrix(.Parts(lngPart).IdentityMatrix, .WorldPosition, 0, 0, 0, .UniformScale)
                
                If m_RenderOptions <> 0 Then matOutput = MatrixMultiply(matOutput, matShadow)
                matOutput = MatrixMultiply(matOutput, m_matViewOrientation)
                matOutput = MatrixMultiply(matOutput, m_matViewMapping)
                
                ' For each vertex in the Part, recalculate.
                Dim blnClipMe As Boolean
                Dim sngW As Double
                
                Dim intJ As Integer
                For intJ = 0 To UBound(.Parts(lngPart).Vertices)
                    
                    With .Parts(lngPart).Vertices(intJ)
                        
                        ' Do some heavy number crunching here.
                        .Wxyz = MatrixMultiplyVector(matOutput, .Pxyz)
                        
                        .Clipped = False
                        sngW = .Wxyz.w
                        If sngW > 0 Then
                            ' W is positive.
                            If (.Wxyz.X < -sngW) Or (.Wxyz.X > sngW) Then .Clipped = True
                            If (.Wxyz.Y < -sngW) Or (.Wxyz.Y > sngW) Then .Clipped = True
                            If (.Wxyz.Z < -sngW) Or (.Wxyz.Z > 0) Then .Clipped = True
                        Else
                            ' W is negative
                            If (.Wxyz.X < -sngW) Or (.Wxyz.X > sngW) Then .Clipped = True
                            If (.Wxyz.Y < -sngW) Or (.Wxyz.Y > sngW) Then .Clipped = True
                            If (.Wxyz.Z < -sngW) Or (.Wxyz.Z > 0) Then .Clipped = True
                        End If
                        
                        If (.Clipped = False) Then
                            .Brightness = Abs(1 - .Wxyz.w)
                            If .Brightness > 1 Then .Brightness = 1
                        End If
                        
                    End With
                Next intJ
                
                
            ' ==========================
            ' >>> Clip Polygons Here <<<
            ' ==========================
''            Call DoClipping(.Parts(lngPart))
            
            
SkipOver:
            Next lngPart
            
'            m_matViewPort = MatrixIdentity
            ' Convert canonical view volume into 3D viewport view volume.
            ' ClipSpace -> ViewPort (ie. to pixels)
            On Error GoTo errTrap2
            For lngPart = 0 To UBound(.Parts)
                For intJ = 0 To UBound(.Parts(lngPart).Vertices)
                    With .Parts(lngPart).Vertices(intJ)
                        .Txyz = MatrixMultiplyVector(m_matViewPort, .Wxyz)
'                        .Txyz.Y = -.Txyz.Y
                    End With
                Next intJ
SkipOver2:
            Next lngPart

            
            ' Convert from 4D down to 3D
            ' ==========================
            Call Convert4Dto3D(m_AllObjects(intObject))
            
            
            ' ==============
            ' Draw 3D Object
            ' ==============
            Select Case m_AllObjects(intObject).Class
                Case "GeometryDots"
                    Call Draw_DotsOnly(m_AllObjects(intObject))
                
                Case Else
                    Call Draw_SimpleWireframe(m_AllObjects(intObject))
                    
            End Select
            
            
''            ' Display Hollowed Lines
''            m_frmCanvas.DrawMode = vbCopyPen
''            m_frmCanvas.DrawStyle = vbDot
''            m_frmCanvas.DrawWidth = 1
''            m_frmCanvas.ForeColor = RGB(0, 128, 0)
''            Call Draw_SimpleWireframeH(m_AllObjects(intObject), -1)
''
''            m_frmCanvas.DrawMode = vbCopyPen
''            m_frmCanvas.DrawStyle = vbSolid
''            m_frmCanvas.DrawWidth = 8
''            m_frmCanvas.ForeColor = RGB(0, 0, 0)
''            Call Draw_SimpleWireframeH(m_AllObjects(intObject), 1)
''
''            m_frmCanvas.DrawMode = vbCopyPen
''            m_frmCanvas.DrawStyle = vbSolid
''            m_frmCanvas.DrawWidth = 1
''            m_frmCanvas.ForeColor = RGB(0, 255, 0)
''            Call Draw_SimpleWireframeH(m_AllObjects(intObject), 1)
'            m_frmCanvas.Refresh
            
        End With
    Next intObject
    
    
    Exit Sub
errTrap:
    Resume SkipOver
    
errTrap2:
    Resume SkipOver2
    
End Sub

Private Sub RenderPipeline00()
   
   ' =======================================================================================
   ' -------------------[ Double-Zero is the default wire-frame renderer ]------------------
   '
   ' * Backfaces are included. Nothing complicated.
   ' * Worse-case rendering used to measure performance against improved rendering piplines.
   ' =======================================================================================
   On Error GoTo errTrap
   
    Dim intObject As Integer
    Dim lngPart As Long
    Dim lngFace As Long
    Dim matOutput As mdrMatrix4
    Dim matWorld As mdrMatrix4
    Dim matIWorld As mdrMatrix4
    Dim vectLight As mdrVector4
    Dim vectPlaneEq As mdrVector4
    Dim matShadow As mdrMatrix4
    Dim blnClipMe As Boolean
    Dim sngW As Double
    Dim intJ As Integer
    Dim vectCamera As mdrVector4
    Dim vectVertex As mdrVector4
    Dim lngIndex0 As Long, lngIndex1 As Long, lngIndex2 As Long
    Dim vect0 As mdrVector4, vect1 As mdrVector4, vect2 As mdrVector4
    Dim sngDotProduct As Single
    
    For intObject = LBound(m_AllObjects) To (UBound(m_AllObjects))
        With m_AllObjects(intObject)
            
            ' Draw each Part within the 3DObject.
            For lngPart = 0 To UBound(.Parts)
            
                matWorld = MakeWorldMatrix(.Parts(lngPart).IdentityMatrix, .WorldPosition, 0, 0, 0, 1)
'                matIWorld = MatrixInverse(matWorld)
'                vectCamera = MatrixMultiplyVector(matIWorld, m_Camera.WorldPosition)
                
                matOutput = MatrixMultiply(matWorld, m_matViewOrientation)
                matOutput = MatrixMultiply(matOutput, m_matViewMapping)
                matOutput = MatrixMultiply(matOutput, m_matViewPort)
                
                ' Draw each polygon for the given polyhedra.
                For lngFace = 0 To UBound(.Parts(lngPart).Faces)
                
                    lngIndex0 = .Parts(lngPart).Faces(lngFace)(0)
                    lngIndex1 = .Parts(lngPart).Faces(lngFace)(1)
                    lngIndex2 = .Parts(lngPart).Faces(lngFace)(2)
                    
                    vect0 = .Parts(lngPart).Vertices(lngIndex0).Pxyz
                    vect1 = .Parts(lngPart).Vertices(lngIndex1).Pxyz
                    vect2 = .Parts(lngPart).Vertices(lngIndex2).Pxyz
                    
                    .Parts(lngPart).Vertices(lngIndex0).Txyz = MatrixMultiplyVector(matOutput, .Parts(lngPart).Vertices(lngIndex0).Pxyz)
                    .Parts(lngPart).Vertices(lngIndex1).Txyz = MatrixMultiplyVector(matOutput, .Parts(lngPart).Vertices(lngIndex1).Pxyz)
                    .Parts(lngPart).Vertices(lngIndex2).Txyz = MatrixMultiplyVector(matOutput, .Parts(lngPart).Vertices(lngIndex2).Pxyz)
                    
                    .Parts(lngPart).Vertices(lngIndex0).Txyz.X = .Parts(lngPart).Vertices(lngIndex0).Txyz.X / .Parts(lngPart).Vertices(lngIndex0).Txyz.w
                    .Parts(lngPart).Vertices(lngIndex0).Txyz.Y = .Parts(lngPart).Vertices(lngIndex0).Txyz.Y / .Parts(lngPart).Vertices(lngIndex0).Txyz.w
                    .Parts(lngPart).Vertices(lngIndex0).Txyz.Z = .Parts(lngPart).Vertices(lngIndex0).Txyz.Z / .Parts(lngPart).Vertices(lngIndex0).Txyz.w
                    
                    .Parts(lngPart).Vertices(lngIndex1).Txyz.X = .Parts(lngPart).Vertices(lngIndex1).Txyz.X / .Parts(lngPart).Vertices(lngIndex1).Txyz.w
                    .Parts(lngPart).Vertices(lngIndex1).Txyz.Y = .Parts(lngPart).Vertices(lngIndex1).Txyz.Y / .Parts(lngPart).Vertices(lngIndex1).Txyz.w
                    .Parts(lngPart).Vertices(lngIndex1).Txyz.Z = .Parts(lngPart).Vertices(lngIndex1).Txyz.Z / .Parts(lngPart).Vertices(lngIndex1).Txyz.w
                    
                    .Parts(lngPart).Vertices(lngIndex2).Txyz.X = .Parts(lngPart).Vertices(lngIndex2).Txyz.X / .Parts(lngPart).Vertices(lngIndex2).Txyz.w
                    .Parts(lngPart).Vertices(lngIndex2).Txyz.Y = .Parts(lngPart).Vertices(lngIndex2).Txyz.Y / .Parts(lngPart).Vertices(lngIndex2).Txyz.w
                    .Parts(lngPart).Vertices(lngIndex2).Txyz.Z = .Parts(lngPart).Vertices(lngIndex2).Txyz.Z / .Parts(lngPart).Vertices(lngIndex2).Txyz.w
                    
                    vect0 = .Parts(lngPart).Vertices(lngIndex0).Txyz
                    vect1 = .Parts(lngPart).Vertices(lngIndex1).Txyz
                    vect2 = .Parts(lngPart).Vertices(lngIndex2).Txyz
                    
                    Call DrawFlatTriangle(m_frmCanvas, vect0.X, vect0.Y, vect1.X, vect1.Y, vect2.X, vect2.Y, RGB(0, 0, 255), -1, vbSolid, vbFSSolid)
                    g_lngPolygonCount = g_lngPolygonCount + 1
                    
                Next lngFace
ResumePoint:
            Next lngPart
        End With
    Next intObject
    
    Exit Sub
errTrap:
    Resume ResumePoint
   
End Sub
Private Sub Draw_SimpleWireframe(withObject As mdr3DObject)
    
    ' Displays 3D usin Simple Wireframe

    On Error GoTo errTrap

    Dim intN As Long, intJ As Long, intK As Long
    Dim blnClipped As Boolean
    Dim intClipCount As Integer

    Dim lngIndex0 As Long
    Dim vect0 As mdrVector4
    Dim Red0 As Double
    Dim Green0 As Double
    Dim Blue0 As Double
    Dim sngBrightness0 As Double
    
    Dim lngIndex1 As Long
    Dim vect1 As mdrVector4
    Dim Red1 As Double
    Dim Green1 As Double
    Dim Blue1 As Double
    Dim sngBrightness1 As Double
    
    Dim lngIndex2 As Long
    Dim vect2 As mdrVector4
    Dim Red2 As Double
    Dim Green2 As Double
    Dim Blue2 As Double
    Dim sngBrightness2 As Double

    
    If m_blnDetailLCDScreen = True Then
        m_frmCanvas.DrawWidth = 2
    Else
        m_frmCanvas.DrawWidth = 1
    End If
    If m_intDetailDrawMode <> 0 Then m_frmCanvas.DrawWidth = 1
    
    With withObject

        ' Draw each object's polyhedra
        For intN = 0 To UBound(.Parts)
            m_frmCanvas.ForeColor = vbBlack
            
            ' Draw each polygon for the given polyhedra.
            For intJ = 0 To UBound(.Parts(intN).Faces)

                If .Parts(intN).Selected = True Then
                    m_frmCanvas.ForeColor = vbWhite
                End If

                With .Parts(intN)
                
                    lngIndex0 = .Faces(intJ)(0)
                    lngIndex1 = .Faces(intJ)(1)
                    lngIndex2 = .Faces(intJ)(2)
                    
                    ' Enough faces for a Triangle?
                    If (UBound(.Faces(intJ)) > 1) Then
                    
                        ' Should the triangle be clipped?
                        blnClipped = False
                        blnClipped = blnClipped Or .Vertices(lngIndex0).Clipped
                        blnClipped = blnClipped Or .Vertices(lngIndex1).Clipped
                        blnClipped = blnClipped Or .Vertices(lngIndex2).Clipped
                        
                        intClipCount = 0
                        If .Vertices(lngIndex0).Clipped = True Then intClipCount = intClipCount + 1
                        If .Vertices(lngIndex1).Clipped = True Then intClipCount = intClipCount + 1
                        If .Vertices(lngIndex2).Clipped = True Then intClipCount = intClipCount + 1
                        
                        If blnClipped = True Then
                            vect0 = .Vertices(lngIndex0).Txyz
                            vect1 = .Vertices(lngIndex1).Txyz
                            vect2 = .Vertices(lngIndex2).Txyz
                            If intClipCount = 3 Then
                                If m_blnDetailPolygonsOutside = True Then
                                    Call DrawFlatTriangle(m_frmCanvas, vect0.X, vect0.Y, vect1.X, vect1.Y, vect2.X, vect2.Y, RGB(64, 32, 32), 0, vbSolid, vbFSTransparent)
                                End If
                            Else
                                If m_blnDetailPartialPolygons = True Then
                                    Call DrawFlatTriangle(m_frmCanvas, vect0.X, vect0.Y, vect1.X, vect1.Y, vect2.X, vect2.Y, RGB(255, 128, 0), 0, vbSolid, vbFSTransparent)
                                End If
                            End If
                            
                        Else
                            vect0 = .Vertices(lngIndex0).Txyz
                            vect1 = .Vertices(lngIndex1).Txyz
                            vect2 = .Vertices(lngIndex2).Txyz
                            
                            ' ==================
                            ' Back-Face Culling.
                            ' ==================
                            Dim vectA As mdrVector4, vectB As mdrVector4, vectC As mdrVector4
                            vectA = .Vertices(lngIndex0).Txyz
                            vectB = .Vertices(lngIndex1).Txyz
                            vectC = .Vertices(lngIndex2).Txyz

                            Dim sngDotProduct As Single
                            sngDotProduct = IsBackFace(m_Camera.PRP, vectA, vectB, vectC)
                            If sngDotProduct < 0 Then
                                
' Update debug & info. counters (conditionally compiled)
#If g_blnCompiledDebugInfo = True Then
    g_lngPolygonCount = g_lngPolygonCount + 1
#End If

                                If m_RenderOptions = 0 Then
                                
                                    ' ============================================================================================
                                    ' This colour part is much more complicated that it needs to be. You could probably delete all
                                    ' this colour stuff and it would not effect the program at all.
                                    ' ============================================================================================
                                    Dim sngRed0 As Single, sngGreen0 As Single, sngBlue0 As Single
                                    sngBrightness0 = .Vertices(lngIndex0).Brightness
                                    If m_blnDetailDayMode = True Then
                                        Call HSV2(sngRed0, sngGreen0, sngBlue0, 360 * sngBrightness0 * 12, sngBrightness0, 1)
                                    Else
                                        Call HSV2(sngRed0, sngGreen0, sngBlue0, 360 * sngBrightness0 * 12, 1, sngBrightness0)
                                    End If
                                    Red0 = 255 * sngRed0
                                    Green0 = 255 * sngGreen0
                                    Blue0 = 255 * sngBlue0

                                    Dim sngRed1 As Single, sngGreen1 As Single, sngBlue1 As Single
                                    sngBrightness1 = .Vertices(lngIndex1).Brightness
                                    If m_blnDetailDayMode = True Then
                                        Call HSV2(sngRed1, sngGreen1, sngBlue1, 360 * sngBrightness1 * 12, sngBrightness1, 1)
                                    Else
                                        Call HSV2(sngRed1, sngGreen1, sngBlue1, 360 * sngBrightness1 * 12, 1, sngBrightness1)
                                    End If
                                    Red1 = 255 * sngRed1
                                    Green1 = 255 * sngGreen1
                                    Blue1 = 255 * sngBlue1

                                    Dim sngRed2 As Single, sngGreen2 As Single, sngBlue2 As Single
                                    sngBrightness2 = .Vertices(lngIndex2).Brightness
                                    If m_blnDetailDayMode = True Then
                                        Call HSV2(sngRed2, sngGreen2, sngBlue2, 360 * sngBrightness2 * 12, sngBrightness2, 1)
                                    Else
                                        Call HSV2(sngRed2, sngGreen2, sngBlue2, 360 * sngBrightness2 * 12, 1, sngBrightness2)
                                    End If
                                    Red2 = 255 * sngRed2
                                    Green2 = 255 * sngGreen2
                                    Blue2 = 255 * sngBlue2
                                    
                                    Dim sngRedAvg As Single
                                    Dim sngGrnAvg As Single
                                    Dim sngBluAvg As Single
                                    sngRedAvg = (Red0 + Red1 + Red2) / 3
                                    sngGrnAvg = (Green0 + Green1 + Green2) / 3
                                    sngBluAvg = (Blue0 + Blue1 + Blue2) / 3
                                    
                                    ' Draw Wireframe.
                                    If m_intDetailDrawMode = 0 Then
                                        Call DrawFlatTriangle(m_frmCanvas, vect0.X, vect0.Y, vect1.X, vect1.Y, vect2.X, vect2.Y, RGB(sngRedAvg * 0.9, sngGrnAvg * 0.9, sngBluAvg * 0.9), RGB(sngRedAvg, sngGrnAvg, sngBluAvg), vbSolid, vbFSTransparent)
                                    End If
                                    
                                    ' Flat-Shaded Polygon
                                    If m_intDetailDrawMode = 1 Then
                                        If m_blnDetailEdgedFaces = True Then
                                            Call DrawFlatTriangle(m_frmCanvas, vect0.X, vect0.Y, vect1.X, vect1.Y, vect2.X, vect2.Y, RGB(sngRedAvg * 0.9, sngGrnAvg * 0.9, sngBluAvg * 0.9), RGB(sngRedAvg, sngGrnAvg, sngBluAvg), vbSolid, vbFSSolid)
                                        Else
                                            Call DrawFlatTriangle(m_frmCanvas, vect0.X, vect0.Y, vect1.X, vect1.Y, vect2.X, vect2.Y, 0, RGB(sngRedAvg, sngGrnAvg, sngBluAvg), vbInvisible, vbFSSolid)
                                        End If
                                    End If
                                    
                                    ' Gouraud Shaded
                                    If m_intDetailDrawMode = 2 Then
                                        Call DrawTriangle(m_frmCanvas, vect0.X, vect0.Y, vect1.X, vect1.Y, vect2.X, vect2.Y, Red0, Green0, Blue0, Red1, Green1, Blue1, Red2, Green2, Blue2)
                                        If m_blnDetailEdgedFaces = True Then
                                            Call DrawFlatTriangle(m_frmCanvas, vect0.X, vect0.Y, vect1.X, vect1.Y, vect2.X, vect2.Y, RGB(sngRedAvg * 0.9, sngGrnAvg * 0.9, sngBluAvg * 0.9), RGB(sngRedAvg, sngGrnAvg, sngBluAvg), vbSolid, vbFSTransparent)
                                        End If
                                    End If
                                    
                                Else
                                    ' Draw Shadow..
                                    If m_intDetailDrawMode <> 0 Then ' if wireframe
                                        Call DrawFlatTriangle(m_frmCanvas, vect0.X, vect0.Y, vect1.X, vect1.Y, vect2.X, vect2.Y, RGB(1, 1, 1), RGB(Blue0, Blue0, Blue0), vbSolid, vbFSSolid)
                                    Else
                                        Call DrawFlatTriangle(m_frmCanvas, vect0.X, vect0.Y, vect1.X, vect1.Y, vect2.X, vect2.Y, RGB(1, 1, 1), RGB(Blue0, Blue0, Blue0), vbSolid, vbFSTransparent)
                                    End If
                                End If
                            Else
                                'Call DrawFlatTriangle(m_frmCanvas, vect0.X, vect0.Y, vect1.X, vect1.Y, vect2.X, vect2.Y, RGB(0, 128, 0), -1, vbDot, vbFSSolid)
                            End If
                            
                        End If ' Clipped=False
                    End If ' Enough faces for a Triangle?
                End With
SkipOver1:
            Next intJ
        Next intN
    End With
    
    Exit Sub
errTrap:
    Select Case Err.Number
        Case 9 ' Subscript Error
            Resume SkipOver1

        Case Else
            MsgBox Err.Number & " - " & Err.Description, vbCritical
            ' Do nothing

    End Select
    
End Sub

Private Sub Draw_DotsOnly(withObject As mdr3DObject)
    
    Dim intPart As Long, intVertex As Long
    Dim blnClipped As Boolean
    Dim intClipCount As Integer

    Dim lngIndex0 As Long
    Dim vect0 As mdrVector4
    Dim Red0 As Double
    Dim Green0 As Double
    Dim Blue0 As Double
    Dim sngBrightness0 As Double
    
    Dim sngX As Single
    Dim sngY As Single
    
    If m_blnDetailLCDScreen = True Then
        m_frmCanvas.DrawWidth = 4
    Else
        m_frmCanvas.DrawWidth = 1
    End If
    
    With withObject

        ' Draw each object's part.
        For intPart = 0 To UBound(.Parts)
        
            ' Loop through all vertices for the part.
            For intVertex = 0 To UBound(.Parts(intPart).Vertices)
            
                If .Parts(intPart).Vertices(intVertex).Clipped = False Then
                
                    If m_blnDetailDayMode = True Then
                        sngBrightness0 = 1 - .Parts(intPart).Vertices(intVertex).Brightness
                    Else
                        sngBrightness0 = .Parts(intPart).Vertices(intVertex).Brightness
                    End If
                    
                    Red0 = .Parts(intPart).Vertices(intVertex).RGB_Red * sngBrightness0
                    Green0 = .Parts(intPart).Vertices(intVertex).RGB_Green * sngBrightness0
                    Blue0 = .Parts(intPart).Vertices(intVertex).RGB_Blue * sngBrightness0
                    
                    sngX = .Parts(intPart).Vertices(intVertex).Txyz.X
                    sngY = .Parts(intPart).Vertices(intVertex).Txyz.Y
                    
                    m_frmCanvas.PSet (sngX, sngY), RGB(Red0, Green0, Blue0)
                    
                End If
                
            Next intVertex
        Next intPart
        
    End With
        
End Sub

Private Sub Convert4Dto3D(p_3DObject As mdr3DObject)

    On Error GoTo errTrap
    
    Dim lngN As Long, lngJ As Long
    
    With p_3DObject
    
        ' For each object, calculate it's Parts
        For lngN = 0 To UBound(.Parts)
        
            For lngJ = 0 To UBound(.Parts(lngN).Vertices)
                With .Parts(lngN).Vertices(lngJ)
                        
                        ' Ignore invalid co-ordinates
                        If (.Txyz.w = 0) = False Then
                            ' Convert from 4D down to 3D
                            ' ==========================
                            ' Apply the Perspective transformation by converting 4 dimensions down to
                            ' 3 dimensions, by dividing the x, y & z co-ordinates with w. Don't forget the w
                            ' value was previous calculated using the 'MatrixPerspective' routine.
                            ' I personally like to call this part of the code, 'dimensionally downshifting'.
                            .Txyz.X = .Txyz.X / .Txyz.w
                            .Txyz.Y = .Txyz.Y / .Txyz.w
                            .Txyz.Z = .Txyz.Z / .Txyz.w
                            
' Update debug & info. counters (conditionally compiled)
#If g_blnCompiledDebugInfo = True Then
    ' Probably a bad idea to have this code here in a loop... oh well.
    g_lngDivisionCount = g_lngDivisionCount + 3
#End If
                            
                    End If
                End With
SkipPointA:
            Next lngJ ' .Parts(lngN).Vertices
        Next lngN ' .Parts
    End With ' p_3DObject
    
    
    Exit Sub
errTrap:
    Select Case Err.Number
        Case 9 ' Subscript out of range
            Resume SkipPointA
            
        Case Else
            MsgBox Err.Number & " - " & Err.Description, vbExclamation
            
    End Select
    
End Sub

Private Sub DoSetDrawingParamaters(p_Camera As mdr3DTargetCamera)
    
    ' ======================================================================================
    '                       *** Synthetic [Virtual] Camera Values ***
    ' The four basic Viewing Parameters that define the View Reference Coordinates (VRC).
    ' They are all specified in World Coordinates (WC) except PRP; this is specified in VRC.
    '                       *** Synthetic [Virtual] Camera Values ***
    '
    ' ======================================================================================
    Dim vectVRP As mdrVector4   ' View Reference Point (VRP) - The world position of the virtual camera AND the virtual film!
    Dim vectVPN As mdrVector4   ' View Plane Normal (VPN) - The direction that the virtual camera is pointing "away from"!
    Dim vectPRP As mdrVector4   ' Projection Reference Point (PRP), also known as Centre Of Projection (COP) - This is the distance between the virtual camera's film, and the pin-hole lens of the virtual camera.
    Dim vectVUP As mdrVector4   ' View UP direction (VUP) - Which way is up? This is used for tilting (or not tilting) the camera.
    
    
    ' ====================================================
    ' Define the View Reference Point (VRP)
    ' This is defined in the World Coordinate (WC) system.
    ' ====================================================
    vectVRP = p_Camera.WorldPosition
    
    
    If p_Camera.FreeCamera = True Then
        ' Programmer is responsible for creating and rotating View Plane Normal (VPN) manually.
        ' This is a last minute hack... I'll probably clean this up a little so that I'm not
        ' mixing up cameras with human players; their code should be kept separate. Note, this
        ' is a 'camera' routine, not a 'human player' routine.
        vectVPN = m_PlayerOne.VPN
    Else
        ' =============================================================================================================
        ' Subtract the Camera's world position (VRP) from the 'LookingAt' point to give us the View Plane Normal (VPN).
        ' In this application, the VPN points in the opposite direction that the camera is facing!! I said, Opposite!
        ' Fig. 6.14 to 6.16
        ' =============================================================================================================
        vectVPN = VectorSubtract(vectVRP, p_Camera.LookAtPoint)
    End If
    If (vectVPN.X = 0) And (vectVPN.Y = 0) And (vectVPN.Z = 0) Then
        vectVPN.X = 0# ' Do not allow VPN to be all zero's (shouldn't happen anyway, but still check)
        vectVPN.Y = 0#
        vectVPN.Z = 1#
    End If
    
    
    ' ==============================================================================================
    ' PRP is specified in the View Reference Coordinate system (and NOT the world coordinate system)
    ' ==============================================================================================
    vectPRP = p_Camera.PRP
    
    
    ' ==================================================================================
    ' The VUP vector is usually x=0,y=1,z=0. This is used to tilt the camera.
    ' If you had a camera firmly mounted on the handle bars of a motorcycle, you would
    ' expect to see the camera tilt when the motorcycle rounded the corners.
    ' (To achieve this, multiply the vectVUP vector with a rotation matrix)
    ' ==================================================================================
    vectVUP = p_Camera.VUP
    
    ' ============================================================================
    ' View Orientation.
    ' ============================================================================
    m_matViewOrientation = MatrixViewOrientation(vectVPN, vectVUP, vectVRP)
    
    
    ' ============================================================================
    ' Map projection view volume into canonical view volume suitable for clipping.
    ' ============================================================================
    m_matViewMapping = MatrixViewMapping_Per(p_Camera)
    
    
End Sub
Public Sub ShowApplication()
    
    ' Create the Canvas window (which is now 'owned' by the Application class.)
    If (m_frmCanvas Is Nothing) Then
    
        Set m_frmCanvas = New frmCanvas
        Load m_frmCanvas
        m_frmCanvas.Show
        
    End If
    
End Sub


Private Sub m_frmCanvas_OnAnimate()
    
    ' ===========================================================================
    ' The entire program is controlled from here.
    ' --------------------------------------------
    ' The game is always in a known state (ie. the GameState). If you program in
    ' this manner, you will be able to expand your game as time goes on without
    ' the headache of increasing the complexity (a common problem with newbie's).
    ' Each "game state" can have it's own "sub-state". That is, when the game is
    ' in "run_game" state, I could then sub-divide this state into various parts
    ' such as "do enemies", "do A.I", "do check for collisions" and so on.
    ' ===========================================================================
    Select Case Me.GameState
        Case ""
            ' ==========================================
            ' Master Reset - first time program has run.
            ' ==========================================
            Call DoMasterReset
            Call GetMouseInput(m_intInputMouseSensitivity, Me.GameState, m_PlayerOne, m_Camera, m_frmCanvas)
            
            Me.GameState = "run_demo"
            
            ' ======================================================================================
            ' Initilize the MIDI Soundtrack.
            ' This will also initilize the 'Sequencer' timer that will double-up as a Timer control.
            ' The Multimedia Control helps us Synchronize the Music to the action... very cool!
            ' ======================================================================================
            Call Init_MIDISoundtrack
            m_frmCanvas.lblPleaseWait.Visible = False
            m_frmCanvas.TimerDoAnimation.Enabled = False
        
        
        Case "show_settings"
            Me.GameState = "settings"
            If m_frmSettings Is Nothing Then Set m_frmSettings = New frmSettings
            m_frmSettings.Show vbModeless, m_frmCanvas
            
            m_frmSettings.chkVideoFullScreen.Value = m_blnVideoFullScreen
            m_frmSettings.chkDetailClipBoundry.Value = m_blnDetailClipBoundary
            m_frmSettings.chkDetailEdgedFaces.Value = m_blnDetailEdgedFaces
            m_frmSettings.chkDetailDayMode.Value = m_blnDetailDayMode
            m_frmSettings.chkDetailShadows.Value = m_blnDetailShadows
            m_frmSettings.chkDetailPartialPolygons.Value = m_blnDetailPartialPolygons
            m_frmSettings.chkDetailPolygonsOutside.Value = m_blnDetailPolygonsOutside
            m_frmSettings.chkDetailLCDScreen.Value = m_blnDetailLCDScreen
            m_frmSettings.optDetailDrawMode.Item(rmSettings.chkDetaiting m_frmSItem(rmSettings.chkDetaiting m_frmSItem(rmSettings.chkDetaiting m_frmSItem(rmSettings.chkDetaiting m_frmSItem(rliting m_frmSItem(rmSettsing m_frmSItem(rliti em(rmSettings.chkDetaitio( ettings.chkDebDg Red2 = 255 * =====F&:sS
            'S
  ra' routineSettings.chk======   _Per(p_Camera_frmSettings.chkDetailPolygonsOuttin=
  show_sett
             uvl         m_frmSett e       If m_frmSettvate Sub DoSetDrawingParamaters(p_Camera As mdr3DTargetCamera)
    
    ' =====dr3DTargetl
    
    ' =====dr3DTargetl
    
    ' =====dr3DTargetl
    
    ' =====dr3DTargetl
    
  ===
  oi ThePrzlygonoolean
    Dim intClipCouetailIf m_frmSettings Is Nothing T oi ThePrzlygonoolean
     If m_frmSet oi ThePrzlygrtiblnDetailClipBoundary
            m_frmSet(blnDetfrmSettinontrol.
          F_Per(p_Camera)
 x)
    ' ===    ng m_frmSItem(rliting m_frmSIte F_Per(p_Camera)
 x)
VUP As mdrVector4   ' View l sparrrrr' ===    ng m_frmSItem(rliting m_frmSIte F_Per(p_Camera)
 x)
VUP As mdrVector4   ' Vieulle, sngG    rack.
rld position (V sngBluAvg0---         **=====m6*.C sngBluAvg0---             s    (p_Camera)
          ' ========eta          lipCouetailIf m_frmSettings Is Nothingt = 255 * =====F&:sS
            'S
  ra' routineSettings.chk=====+iSSolid)
     ,===dr3DTaSphtoe
     ,===dr3DTaSpht===========================
            ' Initilize the MIDI Soundtrack.
      tp tilt .P As mdrVector4   ' View l spau      m_frmCanvas.PSetnvaGObject As mdr3DObject)
    icr3DObjec  Il ' =======    ' Initilize the MIDIt      o seePSetnvaGObject As mdr3nhtect Aois uAs mdr3nhtect Aois uAs mdr3nhtect Aois uAs md2IDIt      o seePSetnvaGObject As mdr3nhtects    (p_Camer=======ehtects    (p_Camer===kVRP) - ThrY===W) OrAs t                         .undtrack.
            ' This will also initilize the 'Sequencer' timer that will double-up as aquencer'up 1,          .undize the 'Sequencer' timer thattttttc the 'Se(2O === As mdroM't ftor is usual(2O === As mdog This  thattttttc the 'Se(2O ===ing     (tc the 'Se( oiean
    Dim 'Sequeo!lean
     If m_frmSet oi ThePrz as aquencer'up 1,          .undize the 'SequenV sngBluAvg0-----------=_frmSePSN)

=============================
            ' Inilue k=====+iSSohtects +     EVUP, vas window (whict"(sL                 sng
VUP As mdrVec_frmN'ocn.Enablt(0---              o sr  EVUP, vas === d======== End If
                        
               Next intPart
        
    E.Value = m_blnD.raseWait.Visible = False
            m_frmCanvas.TteApplication()
    
    ' Create theLu ' Fig. 6.14 to 6.16
        ' ===============================mSIte  m_frmCanvas.TteA)KvaGObj--         **=====m6*.C sngBl' Fig.o====bj--tCall DraN mdr3nhtect AoirmCanvas.PSetlue = m_blnD.raseWidth m============================
 vValue = m_blnDetailShadows
   hole lens of the virtual camera.
    Dim vectVUP As mdrVector4   ' View UP direction (VUP) - Which way is up      een
            m_f     =================ual c========ual c==== pointing "awayPart)                
    End Select
  
    pointitheLu ' , sngra.
    Dim vectVUP As mdrVector4   ' View UP direction (VUP) - Which way is up      een
  mePrz as aqtCliptlize t m_frmSet oi    =================ual c========ual c==== pointing-----
    ' The game- Whi=ap:
    Sel                     .Txyz.Z  ' The gaQ
    D===========ual c========uaI couldsp n sub-divier1

        ndize P) - Which r)a (.TxyzwNe
  =======uaI couldsp n sub-dquencer'up 1, uaI couldsp n sub-dquencer'u-divMt=====ictor4 time goeInilueiduencer'u-dicer'u-dicer'u-dicer'u-dicerctos(intVexyz.X = .Txyz.X / .Txyz.w
            ===ictor4 t , sngtt enemies", "do A.I", "do c ,yste' ,.I", "do c ,yste' ,.I", "do c ,yste' ,.I",frmSePlygon -=====, Opposite!
      )
         uvl        mSettvateeeeeeeetings.......................yste# o c ,yste' ,.I",frmSePlygon -=====, Opposite!
      )_demo"nvaGOb= m_bte# o c ,yste' ,.I",   downe.rate. Note, this
V   alse
  G*=====oieagon       downe.1I t e kleaseWait.VisibleBiaD========== = vbSolile = Fal   ' r3DTaSph Note,       gon ngs.cSph NoteectVPrdzr==========
    ' TentVexyz.X = .a.Y *A = .P    wyngle  m_frmCanvte,       gon ngs.cSph  End Selecz.X / .Txyz.w
            ===ictor4 t , sngtt enemies",lefrmSIc  G*=====oieagon       downe.1I t e kleaseWait.Vi                           ' ===d_t e kleasao  gon n_t e kleasao  gon n_t e kleasao  gonee        on n e kleasaosett
    er
  
    f<e,Y(p_C As Longle"Bz.w Casaopo"ct2.X, vect2.Y,,,,,,S_t e kleasao  u- Which way i   f"ay i   f"ay i   f"ay i   f"ay i   f"ay i   f"ay i     f"ay i    ouble====iiRoi Thh' =====dr3DTs ' ==p ' ======5 * s  f"ay i(art).Vertl_C As uoa ' ==p ' ======5    
    ' =====drQ      
    ' =====drQ     =====em2
    Dim e ae4 to  Dim e ae4 to  Dim e ae4 to  Dim e ae4 to  Dim e a     ' Initilize the     ========CouetailIfl'em2
    Dim e ae4 to  Di=====intVexyanvas.PSetlue = m_blnD.raPSetlue = m_

slnD.raPSetlue = m_

slnD.raPSetlue = m_

slnD.raPSetlue = m_

slnD.raPSetlue = m_

slnD.ra=====eV  _

slnf==eV mem_

s... s ae4 to  Dim e ae4=====eVtyz.X =         **=====m6*.C P) - ThrY===W)sd1 Asc( + Red1 ng tilize the MIDIt      o seePSetnvaGObjeRe the ' This is defined in  = 255 * sngonee   S  D   Dlositesao      mSettvateeGlrr S)m_Plett.llll==W)sd1 Asc( + Red1 Objec  Il ' ====!lIfl'em2
    Dim e ae4=============================
    m_matViewmct0.Y, IDrqlnD.raPSetlue==================================================
    m  Dim e ae4 to  Dim e ae4 to  Dim e ae4 to  Dim e a     is uAs md2IDIt   le a     is uAs md2IDIt   le a     is uAs md2IDIt   le a     is uAs md2IDIt   le a     is uAs md2IDIt   le a     is p' m_mdIfl'em2
    Dim e ae4 to  Di=====intVexyanv Dim vectPlaneEq As mdrVector4r    lrzlygrtiblnDetailCli'easi     is uAs md2ID    * s  f"ay i(artc etl + Red1 ng tilizeSProduct = IsBack0 As mdrVector4
  C As Lo.Parts(/===          
       p
  C ASe( rtc etl + Red1 ng tililector4
(e Lo.P      rtack0 As mdBlue0 = 255ng tilizeSProduct = Inir   Sel             HroduGeect
   aces(ll*=====_mdIflroduGeect      uGeect
t2         As Loar0 = 255ng SProdued1 n. 6.14 to 6.16
        ' =============  uAs mesub-dquencer'u-dimesssso 6.16
        ' r"5ng SProdued1 n. 6.14 to 6.16
       qndex0).Txyz.Z qndex0).Txyz.Z qngPart).Vertices(lngIndex2).Txyz.Y / .Parts(lngPart).Vertices(lngIndex2).Txyz.w
               A.Z qneer'up 1, utpcTr= qneer'usu+ect1.X, ttvateeGlrr S)m_Plett.l      cDim e ae4=====eVtyz.X =         **=====m6*.C P) - ThrY===W)sd1 Asc( + Red1 ng tilize the MIDIt      o seePSetnvaGOOOOOMIDIt     .Z qngPart).Vertices(lngIndex2).Txyz. _intInightness1, 1)
    ePart=====Inilue k=====+iSSohtects +  o'P) - lble
    Diall DrawF=ssntIn(.Y, Red0, Green0, Blue0, Red1, Green1     (p_C As Longle"Bz.w Casaopo"ct2.X, vecn_

qndex0).ii            =Inilue km oe = F
    ' =====drQ      
      =Inilue km oe          ' ===Inilue km oe  ============probaeeeeeeeeeeer          l.eeeeeeeeobaeeeeeeeeeeer eeeer          l.eeeeeeeeobaeeeeeeeeeeer eee=lb-dquencerYObjec.....).Txyz Sin".eeer eeeCou      l.eeeeeeeeobaeeeeeeeyz. _intInightness1, 1)
    ePart=====Inilue k=====+iSSohtects +  o'P) - lble
    Diall DrawF=ssntIn(.Y, Red0, Green0, Blue0, Red1, Green1     (p_C Aseeeer      lkAseeeeeeeeee f intC ==p es
r    ngra.
   gParntIn(.Y, Red0, Greee 9 ' cs,d0, Greee 9 ' C) excendex2).       
      32), 0, vbSolid, DimeInightnesl_1, Green1     (p_C Aseeeer      lkAseeeeeeeeee f intC ==p es
r    ngra.
   gParntIn(.Y, Red0, Greee 9 ' cs,d0, Greee 9 ' C) excpnv DimeSetnvaGObject As mdr3DObject)
 e=======frBmmcC) l2  ePart=====InnnnnnnnseeeeeeeCamer=======ehtects    (p_Camer==If m_= cePar  phPartialU.X = .mdr3DObjoi ThePrzorBmmcC)t Su   (p_C Aseeel=======ual c= le a     is uAsc= le Su   (p_Ce(p_Ce(p_e' ,.I",frmSePlPc=====frBmmcC) l2  ePart==========uaP) l2  ePart==========uaP) l) l2  ePart====xl(2O === As ======   _Per(p_C  ra' ro
        ' This is!ne a     is uAs md2IDIt   l uAs md2IDIt   l uAs md2IDIt   l uAs mN(r=======ehteA"llllllllllloeelllllUriaaaaaaaaaauAs md2is!ne YeagoparntIn(.Yt====xlC  ra' ro
        ' This is!neai  End r gaRGB(sngRaBu.eeeee 0, Greeeplication()
laaaaaaauAs md2s is!neai  EnaaaauAs e4 to  DimWmr=======eht eeeer y====C As Longle"BlGrue Th6.16
 IfTh6.16
 IfTheht eeeer=eeer y====mesub-dquteee
   
-dqu"===C As LctVPN.X = 0) And (vectVPN.Y = 0) And (vectVPN.Z = 0) Then
        vectVPN.X = 0# ' D     'ir   .dctVRP  eeeer      P  eeeer      P  eeeer      P  eeeer      P  eeeer       P awF=ssntIn(.Y, Red0, Green0, Blue0, Red1, Green1   PSeVectorrheeeer eeeer=eeertorrheeeer eeRed0, Green0, Blue0, Red1, Green1   PSeVectorrheeee And (vecttttsYl'up 1, uaI coulds                             End If
en1   PS neen1  dex0).1  Dimcttt     
B)n
    
        Set m_frmt    lllr4   ' VieulleAs mda/ VieulleiDimcttt     
B)n
    
        Set m_frmt    lllr4   ' Vm_frerticeset m_frmt  plication()
laaaaaaauAs md2s tFalse
           ex0).Txyz.Z qnDoAnimation.Enabled =  am is contr qndexgnugh elygrtiblnDetailkS awF=ssntIntion()
laaaaaaauAs aaau2uf  - Which wDDDDDDus"FZAsc=Setlue_Ce(pztightness0)
      x0).T , gh elygrtiblnDe m_

slnD.ra=====eV m    to  DimWmr=======eht eeeer y====C As Longle"BlGrue Thm_frm1tu         MsgBox Err.Number &       MsgBox Err.Number &=====_mdIflroduGeecRz==========fhm_frhm_frhm_frhm_frhm_frhm_frhm_Gaodu!Red1,ngRedAvg, sngGrnAvg, WDetailatu         CouerA ====,====*.C P)o====,====*.C P)o====,====*.C P)o====,elate    m      Co   Co   Co===fhhhhhhhhhhhhhhh===D     'ir   .dctVRP  eeeer      P  eeeer      P  eeeer   Vi         ro        ro        ro        = p ' ======5         rrmSIte  
B)n
    
  r   Vi         reZ qWmr=======eht eeeerp  dex0).1 f  - Wh=eeertorrheeeer eeRed0, Green0, Blue0, Red1,  manner,=oar.;g_blnComystem.
    ' ==============  
    avg, sngGr 
r    ngra.
   gParntIn(.Y, Red0, Greee ,Mr==Dyz. _intIneer      P  eee_blnDetSllD.raPSetlue = m_

slnD.rai _iiiiiiiiitlue = ==ual c= le a     is uAsc= le Su   (p_Ce(p_Ce(p_e' ,C P)o====,===C P)l + Red1 ng tililector4
(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e L(e