🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

GLSL #include

Started by
7 comments, last by Sword7 3 years, 9 months ago

I review some shaders and C++ code in Seed of Andromeda source codes (available on GitHub) and now noticed some '#include <files>' in some shader files. I googled and found some GLSL preprocessor but it does not support #include.

Does anyone know any C/C++ shader library for handling GLSL preprocessor routines before glCompileShader function call that support #include?

Tim

Advertisement

https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shading_language_include.txt​

I think thats what you want to look at.

Also, as basic #include is nothing but a text-replacement function, one could easily run that functionality themselves before compiling the shader (things only get complicated once you have recursive includes and/or multiple includes of the same file).

@Juliean I checked a link and got 404 error.

Uh, Gamedev.net seems to be doing an oupsie with the link. Just remove the garbage after “.txt” at the end of the link and it should work.

@Juliean I now got it. I was now able access that with just google. Thanks.

Same as above its single replacement routine but there should be header guard handling so you wont include the same file .

Theres a code with some cutouts

It uses std::string as CAnsiStting class and a vector of these as CStringList

#ifndef shaderclassH
#define shaderclassH

#include "/mnt/ext_sdcard/WNB/constvars.h"
//#include "windows_glext_fetcher.h"
#include "/mnt/ext_sdcard/WNB/txtfile/stringlist.cpp"
#include "/mnt/ext_sdcard/WNB/logme.cpp"
#include "/mnt/ext_sdcard/WNB/DxcMatrix.h"

//#include "/mnt/ext_sdcard/WNB/logme.cpp"
//#include <GLES/gl.h>
#include <GLES/gl.h>
#include <GLES3/gl3.h>



template <class T>
const T * readShaderFile( AnsiString FileName )
{

TStringList * s = new TStringList();

s->LoadFromFile(FileName);

AnsiString bollock = s->GetText();

//char* dest = new char[str.length() + 1];std::copy(str.begin(), str.end(), dest);

//now that would work

const T * buffer = new T[TextLength(bollock)];

buffer = bollock.c_str();


delete s;

return buffer;
}





struct TShaderObject
{
public:
// ~TShaderObject()
// {
//glDeleteProgram(gProgram);
// }


GLuint VERTEX;
GLuint FRAGMENT;
GLuint gProgram;



AnsiString Vertexdata;
AnsiString Fragmentdata;



GLint vertex_pos;
GLint texture_coord;
GLint vertex_normal;
GLint vertex_color;








void Enable()
{
glUseProgram(gProgram);
}


void Disable()
{
glUseProgram(0);
}

AnsiString LoadData(AnsiString fname, TStringList * cmd_list)
{
TStringList s;
s.LoadFromFile(fname);

//first apply dynamic consts
if (cmd_list != 0)
{
for (int i=0; i < cmd_list->Count; i++)
{
AnsiString var_name = get_before_char(cmd_list->Strings[i], ",", true );
AnsiString var_value = get_after_char(cmd_list->Strings[i], ",", true );

for (int n=0; n<s.Count;n++)
if (Pos(var_name, s.Strings[n]) > 0)
s.Strings[n] = StringReplace(s.Strings[n], var_name, var_value);


}

}




bool found = true;
// if (Pos("form", fname)>0) ALOG(s.GetText());
//parse file for #include directives and remove them
while (found)
for (int i=0; i<s.Count;i++)
{
found = false;
if (Pos("#include", s.Strings[i])>0)
{
TStringList p;
AnsiString fn = get_text_between("\"", "\"", s.Strings[i]);
ALOG("Including: "+fn);
if ( p.LoadFromFile(fn)== -1) ALOG("bad filename");

if (p.Count > 0)
{
s.Strings[i] = "";
int iline = i+1;
for (int l=0; l<p.Count;l++)
{
s.Strings.insert(s.Strings.begin()+iline, p.Strings[l]);
s.Count = s.Count+1;
iline = iline+1;
}

found = true;

break;
}
}

}


return s.GetText();

}
/*
template <class _t> bool LoadData(_t &data, const _t &filename)
{



std::ifstream file(filename.c_str(), std::ifstream::in | std::ifstream::ate);
if(file.is_open())
{
std::streampos size(file.tellg());
file.seekg(0, std::ios::beg);
data.reserve(size);
data.append((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
file.close();
return true;
}


// ALOG("err in file");

return false;
}
*/
GLuint LoadShader(GLenum shaderType, const char* pSource)
{

GLuint shader = glCreateShader(shaderType);

ShowGLERROR();

if (shader)
{

glShaderSource(shader, 1, &pSource, NULL);

ShowGLERROR();

glCompileShader(shader);
GLint compiled = 0;
ShowGLERROR();

glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
ShowGLERROR();
if (compiled == GL_FALSE)
{

GetShaderError(shader);


glDeleteShader(shader);
shader = 0;
}//!compiled

} //shader


return shader;
}


void GetShaderError(GLuint &shader)
{

int logSize = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logSize);
////alog("Error length: "+IntToStr(logSize));
std::vector<GLchar> error(logSize);

glGetShaderInfoLog(shader, logSize, &logSize, &error[0]);

if (logSize > 0)
{

AnsiString str = "";

int i;
for (i=0; i < logSize; i++)
str = str + error[i];


//str = "shader compile error: " + str;
ALOG(str);

}
//else //alog("bufflen 0 abroting - thers definetly an error during shader make");
}

void LoadVertexShader(AnsiString fname, TStringList * cmd_list)
{
ALOG("Loading vertex shader " + fname);
Vertexdata = "";
Vertexdata = LoadData(fname, cmd_list);
// LoadData(Vertexdata, fname);
VERTEX = LoadShader(GL_VERTEX_SHADER, Vertexdata.c_str());//readShaderFile<char>(fname));
// //alog("done");
}

void LoadFragmentShader(AnsiString fname, TStringList * cmd_list)
{
ALOG("Loading fragment shader " + fname);
Fragmentdata = "";
Fragmentdata = LoadData(fname, cmd_list);
// LoadData(Fragmentdata, fname);
FRAGMENT = LoadShader(GL_FRAGMENT_SHADER, Fragmentdata.c_str()); //readShaderFile<char>(fname));
// //alog("done");
}

void LoadVertexShaderStr(const char * source)
{
VERTEX = LoadShader(GL_VERTEX_SHADER, source);
}

void LoadFragmentShaderStr(const char * source)
{
FRAGMENT = LoadShader(GL_FRAGMENT_SHADER, source);
}

void LoadShaderProgramStr(const char * vs, const char * fs)
{
LoadVertexShaderStr(vs);

LoadFragmentShaderStr(fs);

}

void GetLinkerError()
{

GLint bufLength = 0;

glGetProgramiv(gProgram, GL_INFO_LOG_LENGTH, &bufLength);

//alog("Program error length: "+IntToStr(bufLength));
std::vector<GLchar> error(bufLength);

glGetProgramInfoLog(gProgram, bufLength, &bufLength, &error[0]);

if (bufLength > 0)
{
AnsiString str = "";
int i;
for (i=0; i < bufLength; i++)
str = str + error[i];

ALOG(str);

}

}

void createProgram()
{

gProgram = glCreateProgram();
if (gProgram) {
glAttachShader(gProgram, VERTEX);
glAttachShader(gProgram, FRAGMENT);


glLinkProgram(gProgram);


GLint linkStatus = GL_FALSE;

glGetProgramiv(gProgram, GL_LINK_STATUS, &linkStatus);


if (linkStatus == GL_TRUE)
{
//alog("LINK STATUS: good");
vertex_pos = glGetAttribLocation(gProgram,"Vpos");
texture_coord = glGetAttribLocation(gProgram,"Vtexcoord");
vertex_normal = glGetAttribLocation(gProgram,"Vnormal");
vertex_color = glGetAttribLocation(gProgram,"Vcolor");
vertex_height = glGetAttribLocation(gProgram,"Vheight");
displacement = glGetAttribLocation(gProgram,"Vdisplacement");
////alog("DISPLACEMENT POS: "+IntToStr(displacement));
//
// MVP[0] = glGetUniformLocation(program, "MVP1");
// MVP[1] = glGetUniformLocation(program, "MVP2");
// MVP[2] = glGetUniformLocation(program, "MVP3");
// MVP[3] = glGetUniformLocation(program, "MVP4");
}

else {
//alog("LINK STATUS FALSE");
GetLinkerError();

glDeleteProgram(gProgram);
gProgram = 0;
}//linkstatus

}//program

}

bool initialized;


TShaderObject()
{
initialized = false;
}

~TShaderObject()
{
if (initialized)
glDeleteProgram(gProgram);

}

bool LoadShaderProgram(AnsiString vp, AnsiString fp, TStringList * cmd_list )
{
// ALOG(fp);


LoadVertexShader(vp, cmd_list);

// ShowGLERROR();

LoadFragmentShader(fp, cmd_list);
// ShowGLERROR();

createProgram();
// ShowGLERROR();
if (!gProgram) return false;
GetAllUniformLocations();

initialized = true;
return true;
}

bool LoadShaderProgram(AnsiString vp, AnsiString fp)
{
return LoadShaderProgram(vp, fp, 0);
}



bool LoadShaderStrProgram(const char * vp, const char * fp)
{
LoadVertexShaderStr(vp);
LoadFragmentShaderStr(fp);

createProgram();
if (!gProgram) return false;
GetAllUniformLocations();

initialized = true;
return true;
}


};






#endif

inline int Pos(AnsiString sub, AnsiString str)
{
std::size_t found = str.find(sub,0);
if (found!=AnsiString ::npos)
return int(found)+1;
else
return 0;
}


inline int pstrtoint(AnsiString num)
{
return ::atoi(num.c_str());
}


inline int StrToInt(AnsiString num)
{
return ::atoi(num.c_str());
}


inline AnsiString StringReplace(AnsiString str, AnsiString substr, AnsiString with)
{
AnsiString s = str;
while (Pos(substr, s) > 0)
{
int pos = Pos(substr, s)-1;
s.erase(pos,substr.length());
s.insert(pos, with);
}
return s;
}


struct TStringList
{

int Count;
std::vector<AnsiString> Strings;

void Delete(int line_index)
{
Strings.erase(Strings.begin() + line_index);
Count = Count - 1;
}




void Add(AnsiString text)
{
Strings.push_back(text);
Count = Count + 1;
}

void Add(TStringList * ins)
{
int ssize = ins->Count;
for (int i=0; i < ssize; i++)
Add(ins->Strings[i]);
}

AnsiString GetText()
{
AnsiString res = "";
int i;
for (i=0; i < Count; i++)
res = res + Strings[i] + "\n";
return res;
}

int GetSize()
{
AnsiString l = GetText();
return int ( l.length() ) * int ( sizeof ( unsigned char ) );
}




void Clear()
{
Count = 0;
Strings.clear();
}


TStringList()
{
Clear();
}

~TStringList()
{
Clear();
}

#define STRLST "WNB_LOG"
int LoadFromFile(AnsiString fname)
{
char delimiter ='\n';


std::ifstream in(fname.c_str());
if (in.good() == false) {
in.close();
return -1;
}
std::string contents((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());

if (Pos("\n", contents) > 0) delimiter = '\n';
if (Pos("\r", contents) > 0) delimiter = '\r';
//if (Pos("\n\r", contents) > 0) delimiter = '\n\r';
in.close();

std::ifstream file(fname.c_str());
if (file.good() == false) {
file.close();
//__android_log_print(ANDROID_LOG_VERBOSE, STRLST, "!Not good text file, aborting", 1+1);
return -1;
}



Clear();
AnsiString str;

// Count = 0;
// if (Strings.size() > 0) Strings.clear();
// __android_log_print(ANDROID_LOG_VERBOSE, STRLST, "!Loading file", 1+1);
while (std::getline(file, str, delimiter))
{
Strings.push_back(str);
// __android_log_print(ANDROID_LOG_VERBOSE, STRLST, str.c_str(), 1+1);
Count = Count + 1;
}

file.close();
//return 0;
for (int i=0; i < Count; i++)
{
AnsiString str = Strings[i];
if (str.length() == 0) continue;
char lastChar = str.at( str.length() - 1 );

if ( (lastChar == '\r') || (lastChar == '\n') )
{
str.erase(str.length()-1);
Strings[i] = str;
}

}
return 1;
}

//AnsiString p;
AnsiString pc;

void SaveToFile(AnsiString fname)
{
pc = GetText();


FILE* f = fopen(fname.c_str(),"w+");

int len = pc.length();
char * buff = new char[ len ];
memcpy(buff, pc.c_str(), sizeof ( char ) * len);

fwrite(&buff[0], sizeof ( char ) * len,1,f);

fclose(f);



/*
std::ofstream outfile (fname.c_str(),std::ofstream::binary);
int len = pc.length();
char * buff = new char[ len ];
memcpy(buff, pc.c_str(), sizeof(char) * len);
outfile.write (buff, len);
outfile.close();
*/
}




};

That is an extension feature that is rarely supported by any GLSL shader compiler( preprocessor). However, you have 2 options available in order to support includes in your GLSL shader files:
1. Roll your own preprocessor for include directive.
2. Use the glslang preprocessor. This route still requires you to write code to handle stuff like locating the actual include file and providing the source tokens etc.

I've used both options, but have finally settled on the #2 as that is much easier to support.

Ok, I now got it. I have to write my own GLSL preprocessor in my ShaderManager class package.

Also I now found glslang preprocessor package from my books, etc..

I searched for similar routines through Seed of Andromeda source codes and found its own preprocessor routines that handle #include commands.

Thanks,
Tim

This topic is closed to new replies.

Advertisement