CHDIR "programs\samples\open_gl"

' This example shows how models with textures or materials can be displayed with OpenGL using QB64
'
'IMPORTANT:
' Whilst the .X file loader is optimized for speed, it is very incomplete:
'  -only .X files in text file format
'  -only one object, not a cluster of objects
'  -if using a texture, use a single texture which will be applied to all materials
'  -all the 3D models in this example were exported from Blender, a free 3D creation tool
'   Blender tips: CTRL+J to amalgamate objects, select object to export first, in the UV/image-editor
'                 window you can export the textures built into your .blend file, apply the decimate
'                 modifier to reduce your polygon count to below 10000, preferably ~3000 or less
' This program is not a definitive guide to OpenGL in any way
' The GLH functions are something I threw together to stop people crashing their code by making
'  calls to OpenGL with incorrectly sized memory regions. The GLH... prefixed commands are not mandatory or
'  part of QB64, nor do they represent a complete library of helper commands.
' Lighting is not this example's strongest point, there's probably some work to do on light positioning
'  and vertex normals
'
'Finally, I hope you enjoy this program as much as I enjoyed piecing it together,
' Galleon

'###################################### GLH SETUP #############################################

'Used to manage textures
TYPE DONT_USE_GLH_Handle_TYPE
    in_use AS _BYTE
    handle AS LONG
END TYPE

'Used by GLH RGB/etc helper functions
DIM SHARED DONT_USE_GLH_COL_RGBA(1 TO 4) AS SINGLE

REDIM SHARED DONT_USE_GLH_Handle(1000) AS DONT_USE_GLH_Handle_TYPE

'.X Format Model Loading Data
TYPE VERTEX_TYPE
    X AS DOUBLE
    Y AS DOUBLE
    Z AS DOUBLE
    NX AS DOUBLE
    NY AS DOUBLE
    NZ AS DOUBLE
END TYPE
REDIM SHARED VERTEX(1) AS VERTEX_TYPE
DIM SHARED VERTICES AS LONG
TYPE FACE_CORNER_TYPE
    V AS LONG 'the vertex index
    TX AS SINGLE 'texture X coordinate
    TY AS SINGLE 'texture Y coordinate
END TYPE
TYPE FACE_TYPE
    V1 AS FACE_CORNER_TYPE
    V2 AS FACE_CORNER_TYPE
    V3 AS FACE_CORNER_TYPE
    Material AS LONG
    Index AS LONG
END TYPE
REDIM SHARED FACE(1) AS FACE_TYPE
DIM SHARED FACES AS LONG
TYPE MATERIAL_RGBAI_TYPE
    R AS SINGLE
    G AS SINGLE
    B AS SINGLE
    A AS SINGLE
    Intensity AS SINGLE
END TYPE
TYPE MATERIAL_TYPE
    Diffuse AS MATERIAL_RGBAI_TYPE 'regular col
    Specular AS MATERIAL_RGBAI_TYPE 'hightlight/shine col
    Texture_Image AS LONG 'both an image and a texture handle are held
    Texture AS LONG 'if 0, there is no texture
END TYPE
REDIM SHARED MATERIAL(1) AS MATERIAL_TYPE
DIM SHARED MATERIALS AS LONG

'##############################################################################################

DIM SHARED AllowSubGL

SCREEN _NEWIMAGE(1024, 768, 32)

backdrop = _LOADIMAGE("backdrop_tron.png")

DIM SHARED rot1
DIM SHARED rot2, rot3
DIM SHARED scale: scale = 1

'Load (default) model
GLH_Load_Model_Format_X "marty.x", "marty_tmap.png"
'draw backdrop
_PUTIMAGE , backdrop: _DONTBLEND: LINE (200, 200)-(500, 500), _RGBA(0, 255, 255, 0), BF: _BLEND

AllowSubGL = 1

DO
    'This is our program's main loop
    _LIMIT 100
    LOCATE 1, 1
    PRINT "Mouse Input:"
    PRINT "{Horizonal Movement}Spin"
    PRINT "{Vertical Movement}Flip"
    PRINT "{Wheel}Scale"
    PRINT
    PRINT "Keyboard comands:"
    PRINT "Switch rendering order: {1}GL behind, {2}GL on top, {3}GL only, good for speed"
    PRINT "Switch/Load model: {A}Zebra, {B}Pig, {C}Car"

    k$ = INKEY$
    IF k$ = "1" THEN _GLRENDER _BEHIND
    IF k$ = "2" THEN _GLRENDER _ONTOP
    IF k$ = "3" THEN _GLRENDER _ONLY


    PRINT "Angles:"; rot1, rot2, rot3


    IF UCASE$(k$) = "A" THEN
        AllowSubGL = 0
        GLH_Load_Model_Format_X "marty.x", "marty_tmap.png"
        _PUTIMAGE , backdrop: _DONTBLEND: LINE (200, 200)-(500, 500), _RGBA(0, 255, 255, 0), BF: _BLEND
        AllowSubGL = 1
    END IF

    IF UCASE$(k$) = "B" THEN
        AllowSubGL = 0
        GLH_Load_Model_Format_X "piggy_mini3.x", ""
        _PUTIMAGE , backdrop: _DONTBLEND: LINE (200, 200)-(500, 500), _RGBA(0, 255, 255, 0), BF: _BLEND
        AllowSubGL = 1
    END IF

    IF UCASE$(k$) = "C" THEN
        AllowSubGL = 0
        GLH_Load_Model_Format_X "gasprin.x", "gasprin_tmap.png"
        _PUTIMAGE , backdrop: _DONTBLEND: LINE (200, 200)-(500, 500), _RGBA(0, 255, 255, 0), BF: _BLEND
        AllowSubGL = 1
    END IF

    DO WHILE _MOUSEINPUT
        scale = scale * (1 - (_MOUSEWHEEL * .1))
        rot1 = _MOUSEX
        rot2 = _MOUSEY
    LOOP

    IF k$ = "." THEN rot3 = rot3 + 1
    IF k$ = "," THEN rot3 = rot3 - 1




LOOP UNTIL k$ = CHR$(27)
END

'this specially named sub "_GL" is detected by QB64 and adds support for OpenGL commands
'it is called automatically whenever the underlying software deems an update is possible
'usually/ideally, this is in sync with your monitor's refresh rate
SUB _GL STATIC
'STATIC was used above to make all variables in this sub maintain their values between calls to this sub

IF AllowSubGL = 0 THEN EXIT SUB 'we aren't ready yet!

'timing is everything, we don't know how fast the 3D renderer will call this sub to we use timers to smooth things out
T# = TIMER(0.001)
IF ETT# = 0 THEN ETT# = T#
ET# = T# - ETT#
ETT# = T#

IF sub_gl_called = 0 THEN
    sub_gl_called = 1 'we only need to perform the following code once
    '...
END IF

'These settings affect how OpenGL will render our content
'!!! THESE SETTINGS ARE TO SHOW HOW ALPHA CAN WORK, BUT IT IS 10x FASTER WHEN ALPHA OPTIONS ARE DISABLED !!!
'*** every setting must be reset because SUB _GL cannot guarantee settings have not changed since last time ***
_glMatrixMode _GL_PROJECTION 'Select The Projection Matrix
_glLoadIdentity 'Reset The Projection Matrix
_gluPerspective 45, _WIDTH(0) / _HEIGHT(0), 1, 100 'QB64 internally supports this GLU command for convenience sake, but does not support GLU
_glEnable _GL_TEXTURE_2D
_glEnable _GL_BLEND
_glBlendFunc _GL_SRC_ALPHA, _GL_ONE_MINUS_SRC_ALPHA 'how alpha values are interpretted
_glEnable _GL_DEPTH_TEST 'use the zbuffer
_glDepthMask _GL_TRUE
_glAlphaFunc _GL_GREATER, 0.5 'dont do anything if alpha isn't greater than 0.5 (or 128)
_glEnable _GL_ALPHA_TEST
_glTexParameteri _GL_TEXTURE_2D, _GL_TEXTURE_MAG_FILTER, _GL_LINEAR
_glTexParameteri _GL_TEXTURE_2D, _GL_TEXTURE_MIN_FILTER, _GL_LINEAR
'**************************************************************************************************************

_glMatrixMode _GL_MODELVIEW 'Select The Modelview Matrix
_glLoadIdentity 'Reset The Modelview Matrix



'setup our light
_glEnable _GL_LIGHTING
_glEnable _GL_LIGHT0
_glLightfv _GL_LIGHT0, _GL_DIFFUSE, GLH_RGB(.8, .8, .8)
_glLightfv _GL_LIGHT0, _GL_AMBIENT, GLH_RGB(0.1, 0.1, 0.1)
_glLightfv _GL_LIGHT0, _GL_SPECULAR, GLH_RGB(0.3, 0.3, 0.3)

light_rot = light_rot + ET#
_glLightfv _GL_LIGHT0, _GL_POSITION, GLH_RGBA(SIN(light_rot) * 20, COS(light_rot) * 20, 20, 1)


_glTranslatef 0, 0, -20 'Translate Into The Screen
_glRotatef rot1, 0, 1, 0
_glRotatef rot2, 1, 0, 0
_glRotatef rot3, 0, 0, 1



current_m = -1
FOR F = 1 TO FACES

    m = FACE(F).Material
    IF m <> current_m THEN 'we don't switch materials unless we have to
        IF current_m <> -1 THEN _glEnd 'stop rendering triangles so we can change some settings
        current_m = m
        IF MATERIAL(m).Texture_Image THEN

            _glEnable _GL_TEXTURE_2D
            _glDisable _GL_COLOR_MATERIAL
            _glTexParameteri _GL_TEXTURE_2D, _GL_TEXTURE_MAG_FILTER, _GL_LINEAR 'seems these need to be respecified
            _glTexParameteri _GL_TEXTURE_2D, _GL_TEXTURE_MIN_FILTER, _GL_LINEAR


            IF MATERIAL(m).Texture = 0 THEN
                MATERIAL(m).Texture = GLH_Image_to_Texture(MATERIAL(m).Texture_Image)
            END IF
            GLH_Select_Texture MATERIAL(m).Texture

            _glMaterialfv _GL_FRONT, _GL_DIFFUSE, GLH_RGBA(1, 1, 1, 1)

        ELSE
            'use materials, disable textures
            _glDisable _GL_TEXTURE_2D
            _glDisable _GL_COLOR_MATERIAL

            mult = MATERIAL(m).Diffuse.Intensity 'otherwise known as "power"
            r = MATERIAL(m).Diffuse.R * mult
            g = MATERIAL(m).Diffuse.G * mult
            b = MATERIAL(m).Diffuse.B * mult
            '            _glColor3f r, g, b
            _glMaterialfv _GL_FRONT, _GL_DIFFUSE, GLH_RGBA(r, g, b, 1)

            mult = MATERIAL(m).Specular.Intensity
            r = MATERIAL(m).Specular.R * mult
            g = MATERIAL(m).Specular.G * mult
            b = MATERIAL(m).Specular.B * mult
            _glMaterialfv _GL_FRONT, _GL_SPECULAR, GLH_RGBA(r, g, b, 1)

        END IF

        _glBegin _GL_TRIANGLES

    END IF

    FOR s = 1 TO 3

        IF s = 1 THEN v = FACE(F).V1.V
        IF s = 2 THEN v = FACE(F).V2.V
        IF s = 3 THEN v = FACE(F).V3.V
        v = v + 1

        'vertex
        x = (VERTEX(v).X + 0) * scale
        y = (VERTEX(v).Y + 0) * scale
        z = (VERTEX(v).Z + 0) * scale
        'normal direction from vertex
        nx = VERTEX(v).NX: ny = VERTEX(v).NY: nz = VERTEX(v).NZ


        'corner's texture coordinates
        IF MATERIAL(m).Texture THEN
            IF s = 1 THEN tx = FACE(F).V1.TX: ty = FACE(F).V1.TY
            IF s = 2 THEN tx = FACE(F).V2.TX: ty = FACE(F).V2.TY
            IF s = 3 THEN tx = FACE(F).V3.TX: ty = FACE(F).V3.TY
            _glTexCoord2f tx, ty
        END IF

        _glNormal3d nx, my, nz
        _glVertex3f x, y, z

    NEXT

NEXT
_glEnd

END SUB



'QB64 OPEN-GL HELPER MACROS (aka. GLH macros) #######################################################################

SUB GLH_Select_Texture (texture_handle AS LONG) 'turn an image handle into a texture handle
IF texture_handle < 1 OR texture_handle > UBOUND(DONT_USE_GLH_HANDLE) THEN ERROR 258: EXIT FUNCTION
IF DONT_USE_GLH_Handle(texture_handle).in_use = 0 THEN ERROR 258: EXIT FUNCTION
_glBindTexture _GL_TEXTURE_2D, DONT_USE_GLH_Handle(texture_handle).handle
END SUB

FUNCTION GLH_Image_to_Texture (image_handle AS LONG) 'turn an image handle into a texture handle
IF image_handle >= 0 THEN ERROR 258: EXIT FUNCTION 'don't allow screen pages
DIM m AS _MEM
m = _MEMIMAGE(image_handle)
DIM h AS LONG
h = DONT_USE_GLH_New_Texture_Handle
GLH_Image_to_Texture = h
_glBindTexture _GL_TEXTURE_2D, DONT_USE_GLH_Handle(h).handle
_glTexImage2D _GL_TEXTURE_2D, 0, _GL_RGBA, _WIDTH(image_handle), _HEIGHT(image_handle), 0, &H80E1&&, _GL_UNSIGNED_BYTE, m.OFFSET
_MEMFREE m
END FUNCTION

FUNCTION DONT_USE_GLH_New_Texture_Handle
handle&& = 0
_glGenTextures 1, _OFFSET(handle&&)
DONT_USE_GLH_New_Texture_Handle = handle&&
FOR h = 1 TO UBOUND(DONT_USE_GLH_Handle)
    IF DONT_USE_GLH_Handle(h).in_use = 0 THEN
        DONT_USE_GLH_Handle(h).in_use = 1
        DONT_USE_GLH_Handle(h).handle = handle&&
        DONT_USE_GLH_New_Texture_Handle = h
        EXIT FUNCTION
    END IF
NEXT
REDIM _PRESERVE DONT_USE_GLH_Handle(UBOUND(DONT_USE_GLH_HANDLE) * 2) AS DONT_USE_GLH_Handle_TYPE
DONT_USE_GLH_Handle(h).in_use = 1
DONT_USE_GLH_Handle(h).handle = handle&&
DONT_USE_GLH_New_Texture_Handle = h
END FUNCTION




SUB GLH_Load_Model_Format_X (Filename$, Optional_Texture_Filename$)

_AUTODISPLAY 'so loading messages can be seen

DEFLNG A-Z

IF LEN(Optional_Texture_Filename$) THEN
    texture_image = _LOADIMAGE(Optional_Texture_Filename$, 32)
    IF texure_image = -1 THEN texure_image = 0
END IF

'temporary arrays
DIM SIDE_LIST(10000) AS LONG 'used for wrangling triangle-fans/triangle-strips
REDIM TEXCO_TX(1) AS SINGLE
REDIM TEXCO_TY(1) AS SINGLE
REDIM POLY_FACE_INDEX_FIRST(1) AS LONG
REDIM POLY_FACE_INDEX_LAST(1) AS LONG

'buffer file
fh = FREEFILE: OPEN Filename$ FOR BINARY AS #fh: file_data$ = SPACE$(LOF(fh)): GET #fh, , file_data$: CLOSE #fh

file_x = 1
file_data$ = UCASE$(file_data$)

ASC_COMMA = 44
ASC_SEMICOLON = 59
ASC_LBRAC = 123
ASC_RBRAC = 125
ASC_SPACE = 32
ASC_TAB = 9
ASC_CR = 13
ASC_LF = 10
ASC_FSLASH = 47
ASC_DOT = 46
ASC_MINUS = 45

DIM WhiteSpace(255) AS LONG
WhiteSpace(ASC_LF) = -1
WhiteSpace(ASC_CR) = -1
WhiteSpace(ASC_SPACE) = -1
WhiteSpace(ASC_TAB) = -1

DIM FormattingCharacter(255) AS LONG
FormattingCharacter(ASC_COMMA) = -1
FormattingCharacter(ASC_SEMICOLON) = -1
FormattingCharacter(ASC_LBRAC) = -1
FormattingCharacter(ASC_RBRAC) = -1

DIM Numeric(255) AS LONG
FOR a = 48 TO 57
    Numeric(a) = -1
NEXT
Numeric(ASC_DOT) = -1
Numeric(ASC_MINUS) = -1

PRINT "Loading model:"

DO

    skip_comment:

    'find start of element
    x1 = -1
    FOR x = file_x TO LEN(file_data$)
        IF WhiteSpace(ASC(file_data$, x)) = 0 THEN x1 = x: EXIT FOR
    NEXT
    IF x1 = -1 THEN EXIT DO 'no more data

    a = ASC(file_data$, x1)
    IF a = ASC_FSLASH THEN 'commend
        IF ASC(file_data$, x1 + 1) = ASC_FSLASH THEN
            FOR x = x1 TO LEN(file_data$)
                a = ASC(file_data$, x)
                IF a = ASC_CR OR a = ASC_LF THEN file_x = x + 1: GOTO skip_comment '//.....
            NEXT
        END IF
    END IF

    'find end of element
    x2 = x1
    FOR x = x1 TO LEN(file_data$)
        a = ASC(file_data$, x)
        IF WhiteSpace(a) THEN
            IF a = ASC_CR OR a = ASC_LF THEN EXIT FOR 'it is the end
        ELSE
            'not whitespace
            IF FormattingCharacter(a) THEN EXIT FOR
            x2 = x
        END IF
    NEXT
    file_x = x2 + 1

    a2$ = MID$(file_data$, x1, x2 - x1 + 1)

    IF LEN(skip_until$) THEN
        IF a2$ <> skip_until$ THEN GOTO skip_comment
        skip_until$ = ""
    END IF



    a = ASC(a2$)

    IF Numeric(a) AND a <> ASC_DOT THEN 'faster than VAL, value conversion
        v = 0
        dp = 0
        div = 1
        IF a = ASC_MINUS THEN neg = 1: x1 = 2 ELSE neg = 0: x1 = 1
        FOR x = x1 TO LEN(a2$)
            a2 = ASC(a2$, x)
            IF a2 = ASC_DOT THEN
                dp = 1
            ELSE
                v = v * 10 + (a2 - 48)
                IF dp THEN div = div * 10
            END IF
        NEXT

        IF dp = 1 THEN
            v# = v
            div# = div
            IF neg THEN value# = (-v#) / div# ELSE value# = v# / div#
        ELSE
            IF neg THEN value# = -v ELSE value# = v
        END IF

    END IF

    IF face_input THEN
        IF face_input = 3 THEN
            IF a2$ = ";" THEN
                IF last_a2$ = ";" THEN face_input = 0
                SLI = SLI + 1
            ELSEIF a2$ = "," THEN
                face_input = 2
                polygon = polygon + 1
            ELSE
                SIDE_LIST(SLI) = value#
                IF SLI >= 3 THEN
                    FACES = FACES + 1
                    IF FACES > UBOUND(FACE) THEN REDIM _PRESERVE FACE(UBOUND(FACE) * 2) AS FACE_TYPE
                    FACE(FACES).V1.V = SIDE_LIST(1)
                    FACE(FACES).V2.V = SIDE_LIST(SLI - 1)
                    FACE(FACES).V3.V = SIDE_LIST(SLI)
                    IF POLY_FACE_INDEX_FIRST(polygon) = 0 THEN POLY_FACE_INDEX_FIRST(polygon) = FACES
                    POLY_FACE_INDEX_LAST(polygon) = FACES
                    FACE(FACES).Index = polygon
                END IF

                file_x = file_x + 1: a2$ = ";": a = ASC_SEMICOLON: SLI = SLI + 1


            END IF
            GOTO done
        END IF
        IF face_input = 2 THEN
            SIDES = value#
            SLI = 0
            face_input = 3
            GOTO done
        END IF
        IF face_input = 1 THEN
            POLYGONS = value#
            REDIM _PRESERVE FACE(POLYGONS * 4) AS FACE_TYPE 'estimate triangles in polygons
            REDIM POLY_FACE_INDEX_FIRST(POLYGONS) AS LONG
            REDIM POLY_FACE_INDEX_LAST(POLYGONS) AS LONG
            polygon = 1
            face_input = 2
            FACES = 0
            GOTO done
        END IF
    END IF

    IF mesh_input THEN
        IF mesh_input = 5 THEN
            IF a = ASC_SEMICOLON THEN
                mesh_input = 0: face_input = 1
                IF normals_input = 1 THEN
                    face_input = 0 'face input is unrequired on 2nd pass
                    skip_until$ = "MESHMATERIALLIST"
                END IF
            END IF
            GOTO done
        END IF
        IF mesh_input = 4 THEN
            IF a = ASC_SEMICOLON THEN
                'ignore
            ELSEIF a = ASC_COMMA THEN
                vertex = vertex + 1
            ELSE
                IF normals_input = 1 THEN
                    IF plane = 1 THEN VERTEX(vertex).NX = value#
                    IF plane = 2 THEN VERTEX(vertex).NY = value#
                    IF plane = 3 THEN VERTEX(vertex).NZ = value#
                ELSE
                    IF plane = 1 THEN VERTEX(vertex).X = value#
                    IF plane = 2 THEN VERTEX(vertex).Y = value#
                    IF plane = 3 THEN VERTEX(vertex).Z = value#
                END IF

                plane = plane + 1
                IF plane = 4 THEN
                    plane = 1
                    IF vertex = VERTICES THEN mesh_input = 5
                END IF

                file_x = file_x + 1 'skip next character (semicolon)

            END IF
            GOTO done
        END IF
        IF mesh_input = 3 THEN
            IF a2$ = ";" THEN mesh_input = 4
            GOTO done
        END IF
        IF mesh_input = 2 THEN
            VERTICES = value#
            IF normals_input = 0 THEN
                REDIM VERTEX(VERTICES) AS VERTEX_TYPE
                REDIM TEXCO_TX(VERTICES) AS SINGLE
                REDIM TEXCO_TY(VERTICES) AS SINGLE
            END IF
            mesh_input = 3
            GOTO done
        END IF
        IF mesh_input = 1 THEN
            IF a2$ = "{" THEN mesh_input = 2: plane = 1: vertex = 1
            GOTO done
        END IF
        GOTO done
    END IF

    IF matlist_input THEN
        IF matlist_input = 6 THEN
            IF a2$ = "," THEN
                'do nothing
            ELSEIF a2$ = ";" THEN
                matlist_input = 0
            ELSE
                polygon = polygon + 1: m = value#
                FOR f = POLY_FACE_INDEX_FIRST(polygon) TO POLY_FACE_INDEX_LAST(polygon)
                    FACE(f).Material = m + 1
                NEXT
            END IF
            GOTO done
        END IF
        IF matlist_input = 5 AND a2$ = ";" THEN matlist_input = 6: polygon = 0: face_search_start = 1: GOTO done
        IF matlist_input = 4 THEN matlist_input = 5: GOTO done
        IF matlist_input = 3 AND a2$ = ";" THEN matlist_input = 4: GOTO done
        IF matlist_input = 2 THEN MATERIALS = value#: REDIM MATERIAL(MATERIALS) AS MATERIAL_TYPE: matlist_input = 3: GOTO done
        IF matlist_input = 1 AND a2$ = "{" THEN matlist_input = 2: GOTO done
        GOTO done
    END IF

    IF material_input THEN
        IF material_input = 2 THEN
            IF a2$ = ";" THEN
                'do nothing
            ELSEIF a2$ = "}" THEN
                material_input = 0
            ELSE
                N = material_n
                IF N = 1 THEN MATERIAL(MATERIAL).Diffuse.R = value#
                IF N = 2 THEN MATERIAL(MATERIAL).Diffuse.G = value#
                IF N = 3 THEN MATERIAL(MATERIAL).Diffuse.B = value#
                IF N = 4 THEN MATERIAL(MATERIAL).Diffuse.A = value#
                IF N = 5 THEN MATERIAL(MATERIAL).Diffuse.Intensity = value# / 100
                IF N = 6 THEN MATERIAL(MATERIAL).Specular.R = value#
                IF N = 7 THEN MATERIAL(MATERIAL).Specular.G = value#
                IF N = 8 THEN MATERIAL(MATERIAL).Specular.B = value#
                IF N = 9 THEN MATERIAL(MATERIAL).Specular.A = value#
                IF N = 10 THEN MATERIAL(MATERIAL).Specular.Intensity = MATERIAL(MATERIAL).Diffuse.Intensity

                'if texture_image
                material_n = N + 1

            END IF
            GOTO done
        END IF
        IF material_input = 1 AND a2$ = "{" THEN material_input = 2: material_n = 1: GOTO done
        GOTO done
    END IF

    IF texco_input THEN
        IF texco_input = 4 THEN
            IF a2$ = ";" THEN
                IF last_a2$ = ";" THEN
                    texco_input = 0
                    GOTO finished
                END IF
                plane = plane + 1: IF plane = 3 THEN plane = 1
            ELSEIF a2$ = "," THEN
                vertex = vertex + 1
            ELSE
                IF plane = 1 THEN
                    TEXCO_TX(vertex) = value#
                ELSE
                    TEXCO_TY(vertex) = value#
                END IF
            END IF
            GOTO done
        END IF
        IF texco_input = 3 THEN
            IF a2$ = ";" THEN texco_input = 4: plane = 1: vertex = 1
            GOTO done
        END IF
        IF texco_input = 2 THEN
            'vertices already known
            texco_input = 3
            GOTO done
        END IF
        IF texco_input = 1 THEN
            IF a2$ = "{" THEN texco_input = 2
            GOTO done
        END IF

        GOTO done
    END IF

    'mode switch?
    IF a2$ = "MESHTEXTURECOORDS" THEN texco_input = 1: PRINT "[Texture Coordinates]";: GOTO done
    IF a2$ = "MESHNORMALS" THEN normals_input = 1: mesh_input = 1: face_input = 0: PRINT "[Normals]";: GOTO done
    IF a2$ = "MESH" THEN mesh_input = 1: PRINT "[Mesh Vertices & Faces]";: GOTO done
    IF a2$ = "MESHMATERIALLIST" THEN matlist_input = 1: PRINT "[Face Material Indexes]";: GOTO done
    IF LEFT$(a2$, 9) = "MATERIAL " THEN
        material_input = 1: MATERIAL = MATERIAL + 1
        MATERIAL(MATERIAL).Texture = 0: MATERIAL(MATERIAL).Texture_Image = texture_image
        PRINT "[Material]";: GOTO done
    END IF
    done:

    progress = progress + 1: IF progress > 5000 THEN PRINT ".";: progress = 0

    IF a = ASC_SEMICOLON THEN
        last_a2$ = a2$
    ELSE
        IF LEN(last_a2$) THEN last_a2$ = ""
    END IF

LOOP
finished:
'change texture coords (with are organised per vertex to be organised by face side
'that way one vertex can share multiple materials without duplicating the vertex
PRINT "[Attaching Texture Coordinates to Face Cornders]";
f = 1
DO UNTIL f > FACES
    v = FACE(f).V1.V + 1: FACE(f).V1.TX = TEXCO_TX(v): FACE(f).V1.TY = TEXCO_TY(v)
    v = FACE(f).V2.V + 1: FACE(f).V2.TX = TEXCO_TX(v): FACE(f).V2.TY = TEXCO_TY(v)
    v = FACE(f).V3.V + 1: FACE(f).V3.TX = TEXCO_TX(v): FACE(f).V3.TY = TEXCO_TY(v)
    f = f + 1
LOOP
PRINT
PRINT "Model loaded!"

DEFSNG A-Z

END SUB

FUNCTION GLH_RGB%& (r AS SINGLE, g AS SINGLE, b AS SINGLE)
DONT_USE_GLH_COL_RGBA(1) = r
DONT_USE_GLH_COL_RGBA(2) = g
DONT_USE_GLH_COL_RGBA(3) = b
DONT_USE_GLH_COL_RGBA(4) = 1
GLH_RGB = _OFFSET(DONT_USE_GLH_COL_RGBA())
END FUNCTION

FUNCTION GLH_RGBA%& (r AS SINGLE, g AS SINGLE, b AS SINGLE, a AS SINGLE)
DONT_USE_GLH_COL_RGBA(1) = r
DONT_USE_GLH_COL_RGBA(2) = g
DONT_USE_GLH_COL_RGBA(3) = b
DONT_USE_GLH_COL_RGBA(4) = a
GLH_RGBA = _OFFSET(DONT_USE_GLH_COL_RGBA())
END FUNCTION



