// load and update shader #include "shader.h" // Apple's annoying non-standard GL include location #if defined(__APPLE__) || defined(MACOSX) #include #else #include #endif #include #include #include using namespace std; // info on vertex and fragment shaders ShaderInfo vertInfo(ShaderInfo::VERTEX); ShaderInfo fragInfo(ShaderInfo::FRAGMENT); // GL enum code for low-level shader code static int programEnum[ShaderInfo::NUM_SHADER_TYPES] = { GL_VERTEX_PROGRAM_ARB, GL_FRAGMENT_PROGRAM_ARB }; static int glslEnum[ShaderInfo::NUM_SHADER_TYPES] = { GL_VERTEX_SHADER_ARB, GL_FRAGMENT_SHADER_ARB }; // first few characters of file identifying low-level shader code static char *shaderMagic[ShaderInfo::NUM_SHADER_TYPES] = { "!!ARBvp", "!!ARBfp" }; // create a new object, remembering type ShaderInfo::ShaderInfo(ShaderInfo::ShaderType t) : type(t), glsl(0) { struct stat reset={0}; time=reset; } // change shader file name void ShaderInfo::setName(string n) { name = n; } // read a vertex or fragment shader if it's changed since last update void ShaderInfo::updateShader() { GLint err; // nothing to do if name isn't set if (! name.length()) return; // open shader file int fd = open(name.c_str(), O_RDONLY, 0); static bool openerr=false; if (fd == -1) { // report error just onece if (! openerr) printf("error opening %s\n", name.c_str()); openerr=true; return; } openerr = false; // check modification time (and file length) struct stat sb; fstat(fd,&sb); if (time.st_mtime != sb.st_mtime) { time = sb; // remember new change time // load using memory mapping of file char *sh = static_cast(mmap(0,sb.st_size,PROT_READ, MAP_FILE,fd,0)); // low-level code recognized by string at start of file if (strncmp(sh,shaderMagic[type],strlen(shaderMagic[type])) == 0) { // disable high-level shader if (glsl) { glDeleteObjectARB(glsl); glsl=0; } // load and report errors glProgramStringARB(programEnum[type], GL_PROGRAM_FORMAT_ASCII_ARB, sb.st_size, sh); glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB,&err); if (err != -1) { string estr = (const char*)(glGetString(GL_PROGRAM_ERROR_STRING_ARB)); printf("%s: %s\n",name.c_str(), estr.c_str()); } // enable low-level shader else glEnable(programEnum[type]); } // high-level code else { // disable low-level glDisable(programEnum[type]); // load and compile if (! glsl) glsl = glCreateShaderObjectARB(glslEnum[type]); GLint shlen = sb.st_size; glShaderSourceARB(glsl, 1, (const GLchar**)(&sh), &shlen); glCompileShaderARB(glsl); glGetObjectParameterivARB(glsl, GL_OBJECT_COMPILE_STATUS_ARB, &err); if (! err) { glGetObjectParameterivARB(glsl, GL_OBJECT_INFO_LOG_LENGTH_ARB, &err); if (err > 1) { GLchar log[err]; glGetInfoLogARB(glsl, err+1, 0, log); printf("%s:\n%s\n", name.c_str(), log); } glDeleteObjectARB(glsl); glsl=0; } // link GLSL program object glUseProgramObjectARB(0); if (vertInfo.glsl || fragInfo.glsl) { GLhandleARB prog = glCreateProgramObjectARB(); if (vertInfo.glsl) glAttachObjectARB(prog, vertInfo.glsl); if (fragInfo.glsl) glAttachObjectARB(prog, fragInfo.glsl); glLinkProgramARB(prog); // report link errors glGetObjectParameterivARB(glsl, GL_OBJECT_LINK_STATUS_ARB, &err); if (! err) { glGetObjectParameterivARB(glsl, GL_OBJECT_INFO_LOG_LENGTH_ARB, &err); if (err > 1) { GLchar log[err]; glGetInfoLogARB(glsl, err+1, 0, log); printf("GLSL link:\n%s\n", name.c_str(), log); } } else glUseProgramObjectARB(prog); } } glutPostRedisplay(); munmap(sh,sb.st_size); } close(fd); } // read and load shader update (set*name must have already been called) extern "C" void updateShaders() { vertInfo.updateShader(); fragInfo.updateShader(); }