Posted: 4th May 2021 10:59
Here's a robust function for loading a shader from a single text file, enjoy.

The Function:
+ Code Snippet
FUNCTION LoadGLSL(FilePath AS STRING)
    IF NOT GetFileExists(FilePath)
        Message("Error!" + chr(10)+chr(10) + "LoadGLSL("+chr(34)+FilePath+chr(34)+")" + chr(10)+chr(10) + "File not found.")
        EXITFUNCTION 0

    ELSE
        ShdrFile     AS INTEGER : ShdrFile     = OpenToRead(FilePath)
        ShdrFileSize AS INTEGER : ShdrFileSize = GetFileSize(ShdrFile)
        ShdrFileLine AS STRING  = ""

        ShdrVert  AS STRING  = ""
        ShdrFrag  AS STRING  = ""
        FoundVert AS INTEGER = 0
        FoundFrag AS INTEGER = 0

        DO
            IF GetFilePos(ShdrFile) >= ShdrFileSize THEN EXIT

            ShdrFileLine = ReadLine(ShdrFile)

            IF     FindString( ShdrFileLine, "//BEGIN_VERT" ) = 1 AND FoundVert
                Message("Error!" + chr(10)+chr(10) + "LoadGLSL("+chr(34)+FilePath+chr(34)+")" + chr(10)+chr(10) + "Found '//BEGIN_VERT' more than once.")
                CloseFile(ShdrFile)
                EXITFUNCTION 0
            ELSEIF FindString( ShdrFileLine, "//BEGIN_VERT" ) = 1 AND NOT FoundVert
                FoundVert = 1
                CONTINUE
            ELSEIF FindString( ShdrFileLine, "//BEGIN_FRAG" ) = 1 AND FoundFrag
                Message("Error!" + chr(10)+chr(10) + "LoadGLSL("+chr(34)+FilePath+chr(34)+")" + chr(10)+chr(10) + "Found '//BEGIN_FRAG' more than once.")
                CloseFile(ShdrFile)
                EXITFUNCTION 0
            ELSEIF FindString( ShdrFileLine, "//BEGIN_FRAG" ) = 1 AND NOT FoundFrag AND NOT FoundVert
                Message("Error!" + chr(10)+chr(10) + "LoadGLSL("+chr(34)+FilePath+chr(34)+")" + chr(10)+chr(10) + "Found '//BEGIN_FRAG' before '//BEGIN_VERT'." + chr(10)+chr(10) + "'//BEGIN_VERT' must be first.")
                CloseFile(ShdrFile)
                EXITFUNCTION 0
            ELSEIF FindString( ShdrFileLine, "//BEGIN_FRAG" ) = 1 AND NOT FoundFrag
                FoundFrag = 1
                CONTINUE
            ELSEIF FindString( ShdrFileLine, "//" ) = 1
                CONTINUE // Ignore commented lines.
            ELSEIF Len( ShdrFileLine ) <= 0
                CONTINUE // Ignore empty lines.
            ELSEIF FoundFrag
                ShdrFrag = ShdrFrag + ShdrFileLine + chr(10)
                CONTINUE
            ELSEIF FoundVert
                ShdrVert = ShdrVert + ShdrFileLine + chr(10)
                CONTINUE
            ELSE
                CONTINUE // We haven't found either BEGIN yet.
            ENDIF
        LOOP

        IF NOT FoundVert : Message("Error!" + chr(10)+chr(10) + "LoadGLSL("+chr(34)+FilePath+chr(34)+")" + chr(10)+chr(10) + "Failed to find '//BEGIN_VERT'.") : CloseFile(ShdrFile) : EXITFUNCTION 0 : ENDIF
        IF NOT FoundFrag : Message("Error!" + chr(10)+chr(10) + "LoadGLSL("+chr(34)+FilePath+chr(34)+")" + chr(10)+chr(10) + "Failed to find '//BEGIN_FRAG'.") : CloseFile(ShdrFile) : EXITFUNCTION 0 : ENDIF

        IF 0 // Debug
            ShdrVert = ReplaceString( ShdrVert, " ", "?", -1 )
            ShdrFrag = ReplaceString( ShdrFrag, " ", "?", -1 )
            ShdrVert = ReplaceString( ShdrVert, chr(10), "?"+chr(10), -1 )
            ShdrFrag = ReplaceString( ShdrFrag, chr(10), "?"+chr(10), -1 )
            DO
                Print("===============================================================================")
                Print(ShdrVert)
                Print("===============================================================================")
                Print(ShdrFrag)
                Print("===============================================================================")
                Sync()
            LOOP
        ENDIF

        iShdr AS INTEGER : iShdr = LoadShaderFromString(ShdrVert, ShdrFrag)
        EXITFUNCTION iShdr
    ENDIF
ENDFUNCTION 0


Example usage:
+ Code Snippet
MyShader = LoadGLSL("agk_default.glsl")


Example shader file:
+ Code Snippet
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//BEGIN_VERT///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
attribute highp vec3 position;
attribute mediump vec3 normal;
varying highp vec3 posVarying;
varying mediump vec3 normalVarying;
varying mediump vec3 lightVarying;
mediump vec3 GetVSLighting( mediump vec3 normal, highp vec3 pos );
uniform highp mat3 agk_WorldNormal;
uniform highp mat4 agk_World;
uniform highp mat4 agk_ViewProj;
attribute highp vec2 uv;
varying highp vec2 uvVarying;
uniform highp vec4 uvBounds0;

void main() {
    uvVarying = uv * uvBounds0.xy + uvBounds0.zw;
    highp vec4 pos = agk_World * vec4(position,1.0);
    mediump vec3 norm = normalize(agk_WorldNormal * normal);
    posVarying = pos.xyz;
    normalVarying = norm;
    lightVarying = GetVSLighting( norm, posVarying );

    gl_Position = agk_ViewProj * pos;
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//BEGIN_FRAG///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
varying highp vec2 uvVarying;
uniform sampler2D texture0;
varying mediump vec3 normalVarying;
varying mediump vec3 lightVarying;
varying highp vec3 posVarying;
mediump vec3 GetPSLighting( mediump vec3 normal, highp vec3 pos );
uniform mediump vec4 agk_MeshDiffuse;
uniform mediump vec4 agk_MeshEmissive;

void main() {
    mediump vec3 norm = normalize(normalVarying);
    mediump vec3 light = lightVarying + GetPSLighting( norm, posVarying );
    mediump vec4 texColor = texture2D(texture0, uvVarying);

    gl_FragColor = texColor * vec4(light,1.0) * agk_MeshDiffuse + agk_MeshEmissive;
}



Edit 2022-06-10

A function to quickly dump object shaders:
+ Code Snippet
FUNCTION DumpObjectShader(ObjID AS INTEGER)
    FileName AS STRING
    FileName = "shaderdump__obj" + str(ObjID) + "__" + GetCurrentDate() + "_" + GetCurrentTime() + ".glsl"
    FileName = ReplaceString( FileName , chr(58) , chr(46) , -1 ) // Replace ":" with ".", ":" is not allowed in file names.
    FileID AS INTEGER
    FileID = OpenToWrite(FileName, 0)
    WriteLine(FileID, "///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////")
    WriteLine(FileID, "//BEGIN_VERT///////////////////////////////////////////////////////////////////////////////////////////////////////////////////")
    WriteLine(FileID, "///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////")
    WriteLine(FileID,  GetObjectMeshVSSource(ObjID,1)  )
    WriteLine(FileID, "")
    WriteLine(FileID, "///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////")
    WriteLine(FileID, "//BEGIN_FRAG///////////////////////////////////////////////////////////////////////////////////////////////////////////////////")
    WriteLine(FileID, "///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////")
    WriteLine(FileID,  GetObjectMeshPSSource(ObjID,1)  )
    CloseFile(FileID)
ENDFUNCTION

FUNCTION DumpObjectShaderMulti(ObjID AS INTEGER)
    iMesh AS INTEGER
    FOR iMesh = 1 TO GetObjectNumMeshes( ObjID )
        FileName AS STRING
        FileName = "shaderdump__obj" + str(ObjID) + "_mesh" + str(iMesh) + "__" + GetCurrentDate() + "_" + GetCurrentTime() + ".glsl"
        FileName = ReplaceString( FileName , chr(58) , chr(46) , -1 ) // Replace ":" with ".", ":" is not allowed in file names.
        FileID AS INTEGER
        FileID = OpenToWrite(FileName, 0)
        WriteLine(FileID, "///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////")
        WriteLine(FileID, "//BEGIN_VERT///////////////////////////////////////////////////////////////////////////////////////////////////////////////////")
        WriteLine(FileID, "///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////")
        WriteLine(FileID,  GetObjectMeshVSSource(ObjID,iMesh)  )
        WriteLine(FileID, "")
        WriteLine(FileID, "///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////")
        WriteLine(FileID, "//BEGIN_FRAG///////////////////////////////////////////////////////////////////////////////////////////////////////////////////")
        WriteLine(FileID, "///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////")
        WriteLine(FileID,  GetObjectMeshPSSource(ObjID,iMesh)  )
        CloseFile(FileID)
    NEXT iMesh
ENDFUNCTION


You can set these before using it if you don't care to go digging around to find the file:
+ Code Snippet
SetFolder("")
SetRawWritePath( GetReadPath() )
Posted: 10th May 2021 15:06
Can I suggest you try to fallback to the default shader given by AppGameKit when no Vertex or fragment shader is found ?
And why do you want to put it all into one single file ?
Posted: 11th May 2021 0:20
Can I suggest you try to fallback to the default shader given by AppGameKit when no Vertex or fragment shader is found ?

The default shader in AppGameKit is dynamically generated based on properties of the 3D-object and other 3D-settings, probably best not to assume what the user will need.
Also, the function will return 0 in any error case. If you do SetObjectShader( objID, 0 ) it will give you a default shader.

And why do you want to put it all into one single file ?

I don't like switching between files while working on shaders.
Posted: 13th May 2021 18:11
For example you set the object up with SetObjectMeshNormalMap which generates a vertex shader with tangent bitangent and normal varyings and you don't want to type it yourself.
But you might not want to do that neccesarily when it fails I see..
Just thought if it doesnt have one you try your best to make it run by using the default shader ^^


I don't like switching between files while working on shaders.

Okay
Posted: 14th May 2021 1:09
Hmm, I think I follow what you are looking for. However, this would require the shader-loader-function be aware of what object(s) it's being applied to. Something that behaves similar to how AppGameKit auto generates an objects shader is certainly possible, but implementation & usage would be quite a bit more complex.
An interesting idea. However, this is not a feature I need, so I can't justify spending time to implement it.
Posted: 11th Jun 2022 1:58
Updated main post to include a function to quickly dump object shaders.