Attribute VB_Name = "EngineShell"

'This module is a simplifying syntax shell for David Piuva's graphics engine.
'By only using simple datatypes in the ActiveX interface, more languages are supported.

'Whenever a new method in the engine have complicated ways to get called, make
'  your own method in you own shell module for easy upgrading.
'Remember to replace frmMain.DGE with whatever your window and engine instance is called.

Option Explicit

'Break and show error messages from the graphics engine
'This should be placed after every call to the engine that can give error messages so that the application will break in debug mode when an error has occured.
'It will continue to break on each point until the error message is deleted by reading it.
'   String buffer methods don't give error messages because they are used to show the message.
'   Matrix buffer methods don't have any preconditions.
Public Sub RE()
    Static Messages As String
    If frmMain.DGE.Message_GetCount > 0 Then
        frmMain.DGE.Message_GetContent_OutSB
        Messages = GetStringFromEngine
        Do While frmMain.DGE.Message_GetCount > 0
            frmMain.DGE.Message_GetContent_OutSB
            Messages = Messages & vbNewLine & vbNewLine & GetStringFromEngine
        Loop
        MsgBox Messages, vbCritical, "Messages from the graphics engine!"
        Debug.Assert False 'Stop debug execution to see where the error occured using the call stack.
    End If
End Sub

Private Function ContainQuestionMark(T As String) As Boolean
    Static I As Integer
    For I = 1 To Len(T)
        If Mid(T, I, 1) = "?" Then
            ContainQuestionMark = True
            Exit Function
        End If
    Next I
    ContainQuestionMark = False
End Function

Public Sub SetRelativeWorkingDirectory(RelativePath As String)
    'The first method has the advantage of handling unicode application paths and the other has the advantage of working when Visual Basic 6 is simulating without an executable.
    'Both properties are not needed at the same time because Visual Basic 6 can't run code from non ansi paths.
    If ContainQuestionMark(App.Path) Then
        'A unicode character was found in the application path.
        'Assume that the game is running on the end user's computer.
        InsertStringToEngine RelativePath: frmMain.DGE.Engine_SetCurrentDirectoryRelativeToApplicationPath_InSB: RE
    Else
        'The application path show no sign of being converted from unicode and the application is either simulated in Visual Basic 6 or run from a folder without unicode characters.
        'We can use the application path given by Visual Basic 6 that works even when we don't run a real executable application.
        InsertStringToEngine App.Path & "\" & RelativePath: frmMain.DGE.Engine_SetCurrentDirectory_InSB: RE
    End If
End Sub

Public Sub InsertStringToEngine(T As String)
    Static I As Long
    frmMain.DGE.SetLengthOfStringBuffer Len(T)
    For I = 1 To Len(T)
        frmMain.DGE.WriteToStringBuffer I, Asc(Mid(T, I, 1))
    Next I
End Sub

Public Function GetStringFromEngine() As String
    Static I As Long
    GetStringFromEngine = ""
    For I = 1 To frmMain.DGE.GetLengthOfStringBuffer
        GetStringFromEngine = GetStringFromEngine & Chr(frmMain.DGE.ReadFromStringBuffer(I))
    Next I
End Function

'Loads a model and it's resources from FileName
Public Function LoadModel(FileName As String) As Long
    InsertStringToEngine FileName
    LoadModel = frmMain.DGE.Model_LoadFromFile_InSB: RE
    If LoadModel = -1 Then End
End Function

'Saves a model to FileName.
Public Function SaveModel(FileName As String, Model As Long) As Boolean
    InsertStringToEngine FileName
    SaveModel = frmMain.DGE.Model_SaveToFile_InSB(Model): RE
End Function

'Loads a texture from FileName.
Public Function LoadTexture(FileName As String) As Long
    InsertStringToEngine FileName
    LoadTexture = frmMain.DGE.Texture_Load_InSB: RE
End Function

'Loads a post effect shader from FileName.
Public Function LoadPostEffectShader(FileName As String) As Long
    InsertStringToEngine FileName
    LoadPostEffectShader = frmMain.DGE.Shader_LoadAsPostEffect_InSB: RE
End Function

'Loads a material shader from FileName.
Public Function LoadMaterialShader(FileName As String) As Long
    InsertStringToEngine FileName
    LoadMaterialShader = frmMain.DGE.Shader_LoadAsMaterial_InSB: RE
End Function

'Returns the number of bones in Model
Public Function GetNumberOfBones(Model As Long) As Long
    GetNumberOfBones = frmMain.DGE.Model_GetNumberOfBones(Model): RE
End Function

Public Function GetAxisSystemFromBoneInModel(Model As Long, Bone As Long) As Matrix3
    'The X axis is not stored in bones but can easily be calculated using a cross product
    frmMain.DGE.Model_Bone_GetYAxis_OutV3 Model, Bone: GetAxisSystemFromBoneInModel.YAxis = GetVector3FromMatrixBuffer: RE
    frmMain.DGE.Model_Bone_GetZAxis_OutV3 Model, Bone: GetAxisSystemFromBoneInModel.ZAxis = GetVector3FromMatrixBuffer: RE
    GetAxisSystemFromBoneInModel.XAxis = NormalVector3(CrosProduct(GetAxisSystemFromBoneInModel.YAxis, GetAxisSystemFromBoneInModel.ZAxis))
End Function

Public Function GetAxisSystemFromBoneInFrame(BoneFrame As Long, Bone As Long) As Matrix3
    'The X axis is not stored in bones but can easily be calculated using a cross product
    frmMain.DGE.BoneFrame_GetYAxis_OutV3 BoneFrame, Bone: GetAxisSystemFromBoneInFrame.YAxis = GetVector3FromMatrixBuffer: RE
    frmMain.DGE.BoneFrame_GetZAxis_OutV3 BoneFrame, Bone: GetAxisSystemFromBoneInFrame.ZAxis = GetVector3FromMatrixBuffer: RE
    GetAxisSystemFromBoneInFrame.XAxis = NormalVector3(CrosProduct(GetAxisSystemFromBoneInFrame.YAxis, GetAxisSystemFromBoneInFrame.ZAxis))
End Function

Public Function GetBoneStartPos_PS_Model(Model As Long, Bone As Long) As Vector3
    frmMain.DGE.Model_Bone_GetParentSpacePos_OutV3 Model, Bone: GetBoneStartPos_PS_Model = GetVector3FromMatrixBuffer(): RE
End Function

Public Function GetVector2FromMatrixBuffer() As Vector2
    GetVector2FromMatrixBuffer = MakeVector2(frmMain.DGE.GetX1, frmMain.DGE.GetY1)
End Function

Public Function GetVector3FromMatrixBuffer() As Vector3
    GetVector3FromMatrixBuffer = MakeVector3(frmMain.DGE.GetX1, frmMain.DGE.GetY1, frmMain.DGE.GetZ1)
End Function

Public Function GetVector4FromMatrixBuffer() As Vector4
    GetVector4FromMatrixBuffer = MakeVector4(frmMain.DGE.GetX1, frmMain.DGE.GetY1, frmMain.DGE.GetZ1, frmMain.DGE.GetW1)
End Function

Public Function GetMatrix3FromMatrixBuffer() As Matrix3
    GetMatrix3FromMatrixBuffer = MakeMatrix3(MakeVector3(frmMain.DGE.GetX1, frmMain.DGE.GetY1, frmMain.DGE.GetZ1), MakeVector3(frmMain.DGE.GetX2, frmMain.DGE.GetY2, frmMain.DGE.GetZ2), MakeVector3(frmMain.DGE.GetX3, frmMain.DGE.GetY3, frmMain.DGE.GetZ3))
End Function

Public Function GetMatrix4FromMatrixBuffer() As Matrix4
    GetMatrix4FromMatrixBuffer = MakeMatrix4(MakeVector4(frmMain.DGE.GetX1, frmMain.DGE.GetY1, frmMain.DGE.GetZ1, frmMain.DGE.GetW1), MakeVector4(frmMain.DGE.GetX2, frmMain.DGE.GetY2, frmMain.DGE.GetZ2, frmMain.DGE.GetW2), MakeVector4(frmMain.DGE.GetX3, frmMain.DGE.GetY3, frmMain.DGE.GetZ3, frmMain.DGE.GetW3), MakeVector4(frmMain.DGE.GetX4, frmMain.DGE.GetY4, frmMain.DGE.GetZ4, frmMain.DGE.GetW4))
End Function

Public Sub SetInstanceAxisSystem(Instance As Long, System As Matrix3)
    frmMain.DGE.Instance_SetXAxis Instance, System.XAxis.X, System.XAxis.Y, System.XAxis.Z
    frmMain.DGE.Instance_SetYAxis Instance, System.YAxis.X, System.YAxis.Y, System.YAxis.Z
    frmMain.DGE.Instance_SetZAxis Instance, System.ZAxis.X, System.ZAxis.Y, System.ZAxis.Z
    RE
End Sub

'The result is normalized and orthogonal so that it's transpose equals it's inverse.
Public Function MakeAxisSystem_Polar(Longitude As Single, Lattitude As Single) As Matrix3
    'Right
    MakeAxisSystem_Polar.XAxis.X = Cos(Longitude)
    MakeAxisSystem_Polar.XAxis.Y = 0
    MakeAxisSystem_Polar.XAxis.Z = -Sin(Longitude)
    'Up
    MakeAxisSystem_Polar.YAxis.X = Sin(Longitude) * -Sin(Lattitude)
    MakeAxisSystem_Polar.YAxis.Y = Cos(Lattitude)
    MakeAxisSystem_Polar.YAxis.Z = Cos(Longitude) * -Sin(Lattitude)
    'Forward
    MakeAxisSystem_Polar.ZAxis.X = Sin(Longitude) * Cos(Lattitude)
    MakeAxisSystem_Polar.ZAxis.Y = Sin(Lattitude)
    MakeAxisSystem_Polar.ZAxis.Z = Cos(Longitude) * Cos(Lattitude)
End Function

'The result is normalized and orthogonal so that it's transpose equals it's inverse.
Public Function MakeAxisSystem_Orthogonalized(Forward As Vector3, Up As Vector3) As Matrix3
    Static Forward_Normalized As Vector3
    Forward_Normalized = NormalVector3(Forward)
    MakeAxisSystem_Orthogonalized.ZAxis = Forward_Normalized
    MakeAxisSystem_Orthogonalized.XAxis = NormalVector3(CrosProduct(NormalVector3(Up), Forward_Normalized))
    MakeAxisSystem_Orthogonalized.YAxis = NormalVector3(CrosProduct(Forward_Normalized, MakeAxisSystem_Orthogonalized.XAxis))
End Function

'The result is normalized and orthogonal so that it's transpose equals it's inverse.
Public Function MakeAxisSystem_Directed(Direction As Vector3) As Matrix3
    MakeAxisSystem_Directed = MakeAxisSystem_Orthogonalized(Direction, MakeVector3(0, 1, 0))
End Function
