/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. ### File Specific: main-programmer: Benjamin Grauer co-programmer: ... */ //#define DEBUG_SPECIAL_MODULE DEBUG_MODULE_ #include "shader.h" #include "stdlibincl.h" #include "compiler.h" #include #include "debug.h" #include "array.h" #include "resource_manager.h" #ifndef PARSELINELENGHT #define PARSELINELENGHT 512 //!< how many chars to read at once #endif using namespace std; /** * standard constructor */ Shader::Shader (const char* vertexShaderFile, const char* fragmentShaderFile) { this->setClassID(CL_SHADER, "Shader"); this->fragmentShaderFile = NULL; this->vertexShaderFile = NULL; this->shaderProgram = 0; this->vertexShader = 0; this->fragmentShader = 0; if (GLEW_ARB_shader_objects && GLEW_ARB_shading_language_100) { GLint status = 0; this->shaderProgram = glCreateProgramObjectARB(); if (vertexShaderFile != NULL) this->loadShaderProgramm(SHADER_VERTEX, vertexShaderFile); if (fragmentShaderFile != NULL) this->loadShaderProgramm(SHADER_FRAGMENT, fragmentShaderFile); glLinkProgramARB(this->shaderProgram); // link error checking glGetObjectParameterivARB(this->shaderProgram, GL_OBJECT_LINK_STATUS_ARB, &status); if (status == GL_INVALID_VALUE || status == GL_INVALID_OPERATION) this->printError(this->shaderProgram); } else { PRINTF(2)("Shaders are not supported on your hardware\n"); } } /** * standard deconstructor */ Shader::~Shader () { if (this->shaderProgram == glGetHandleARB(GL_PROGRAM_OBJECT_ARB)) Shader::deactivateShader(); // delete what has to be deleted here this->deleteProgram(SHADER_VERTEX); this->deleteProgram(SHADER_FRAGMENT); if (this->fragmentShader != 0) { glDetachObjectARB(this->shaderProgram, this->fragmentShader); glDeleteObjectARB(this->fragmentShader); } if (this->vertexShader != 0) { glDetachObjectARB(this->shaderProgram, this->vertexShader); glDeleteObjectARB(this->vertexShader); } if (this->shaderProgram != 0) { GLint status = 0; //glLinkProgramARB(this->shaderProgram); glDeleteObjectARB(this->shaderProgram); // link error checking glGetObjectParameterivARB(this->shaderProgram, GL_OBJECT_DELETE_STATUS_ARB, &status); if (status == GL_INVALID_VALUE || status == GL_INVALID_OPERATION) this->printError(this->shaderProgram); } } Shader* Shader::getShader(const char* vertexShaderFile, const char* fragmentShaderFile) { return (Shader*)ResourceManager::getInstance()->load(vertexShaderFile, SHADER, RP_LEVEL, fragmentShaderFile); } bool Shader::unload(Shader* shader) { return ResourceManager::getInstance()->unload(shader); } Shader* Shader::storedShader = NULL; bool Shader::loadShaderProgramm(SHADER_TYPE type, const char* fileName) { GLhandleARB shader = 0; if (type != SHADER_VERTEX && type != SHADER_FRAGMENT) return false; this->deleteProgram(type); tArray* program = fileReadArray(fileName); if (program == NULL) return false; if (type == SHADER_VERTEX && GLEW_ARB_vertex_shader) { this->vertexShaderFile = new char[strlen(fileName)+1]; strcpy(this->vertexShaderFile, fileName); shader = this->vertexShader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); } if (type == SHADER_FRAGMENT && GLEW_ARB_fragment_shader) { this->fragmentShaderFile = new char[strlen(fileName)+1]; strcpy(this->fragmentShaderFile, fileName); shader = this->fragmentShader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); } if (shader != 0) { GLint status = 0; glShaderSourceARB(shader, program->getCount(), (const char**)program->getArray(), NULL); glCompileShaderARB(shader); // checking on error. glGetObjectParameterivARB(shader, GL_OBJECT_COMPILE_STATUS_ARB, &status); if (status == GL_INVALID_VALUE || status == GL_INVALID_OPERATION) this->printError(shader); else glAttachObjectARB(this->shaderProgram, shader); } for (unsigned int i=0; i< program->getCount(); i++) delete[] program->getArray()[i]; delete program; } char* Shader::fileRead(const char* fileName) { FILE* fileHandle; char* content = NULL; int count = 0; if (fileName == NULL) return NULL; fileHandle = fopen(fileName, "rt"); if (fileHandle == NULL) return NULL; fseek(fileHandle, 0, SEEK_END); count = ftell(fileHandle); rewind(fileHandle); if (count > 0) { content = new char[count+1]; count = fread(content, sizeof(char), count, fileHandle); content[count] = '\0'; } fclose(fileHandle); return content; } tArray* Shader::fileReadArray(const char* fileName) { FILE* stream; //< The stream we use to read the file. if( (stream = fopen (fileName, "rt")) == NULL) { PRINTF(1)("Shader could not open %s\n", fileName); return NULL; } tArray* file = new tArray; char lineBuffer[PARSELINELENGHT]; char* addString; while(fgets (lineBuffer, PARSELINELENGHT, stream) != NULL) { addString = new char[strlen(lineBuffer)+1]; strcpy(addString, lineBuffer); file->addEntry(addString); } fclose(stream); file->finalizeArray(); return file; } void Shader::activateShader() { if (likely (this->shaderProgram != 0)) { glUseProgramObjectARB(this->shaderProgram); Shader::storedShader = this; } } void Shader::deactivateShader() { if (storedShader != NULL) glUseProgramObjectARB(0); Shader::storedShader = NULL; } void Shader::deleteProgram(SHADER_TYPE type) { GLint status = 0; if (type == SHADER_VERTEX && this->vertexShader != 0) { delete[] this->vertexShaderFile; this->vertexShaderFile = NULL; glDetachObjectARB(this->shaderProgram, this->vertexShader); glDeleteObjectARB(this->vertexShader); glGetObjectParameterivARB(this->vertexShader, GL_OBJECT_DELETE_STATUS_ARB, &status); if (status == GL_INVALID_VALUE || status == GL_INVALID_OPERATION) Shader::printError(this->vertexShader); this->vertexShader = 0; } else if (type == SHADER_FRAGMENT && this->fragmentShader != 0) { delete[] this->fragmentShaderFile; this->fragmentShaderFile = NULL; glDetachObjectARB(this->shaderProgram, this->fragmentShader); glDeleteObjectARB(this->fragmentShader); glGetObjectParameterivARB(this->fragmentShader, GL_OBJECT_DELETE_STATUS_ARB, &status); if (status == GL_INVALID_VALUE || status == GL_INVALID_OPERATION) Shader::printError(this->fragmentShader); this->fragmentShader = 0; } else return; } void Shader::printError(GLhandleARB program) { if (program == 0) return; int infologLength = 0; int charsWritten = 0; char *infoLog; glGetObjectParameterivARB(program, GL_OBJECT_INFO_LOG_LENGTH_ARB, &infologLength); if (infologLength > 0) { infoLog = new char[infologLength+1]; glGetInfoLogARB(program, infologLength, &charsWritten, infoLog); printf("%s\n", infoLog); delete[] infoLog; } } bool Shader::checkShaderAbility() { if (GLEW_ARB_shader_objects && GLEW_ARB_shading_language_100 && GLEW_ARB_vertex_shader && GLEW_ARB_fragment_shader) return true; else return false; } void Shader::debug() const { PRINT(3)("Shader info: (SHADER: %d)\n", this->shaderProgram); if (this->vertexShader != 0) { /* PRINT(3)("VertexShaderProgramm: number=%d, file='%s'\n", this->vertexShader, this->vertexShaderFile); if (this->vertexShaderSource != NULL) for (unsigned int i = 0; i < this->vertexShaderSource->getCount(); i++) PRINT(3)("%d: %s\n", i, this->vertexShaderSource->getEntry(i)); } if (this->fragmentShader != 0) { PRINT(3)("FragmentShaderProgramm: number=%d, file='%s'\n", this->fragmentShader, this->fragmentShaderFile); if (this->fragmentShaderSource != NULL) for (unsigned int i = 0; i < this->fragmentShaderSource->getCount(); i++) PRINT(3)("%d: %s\n", i, this->fragmentShaderSource->getEntry(i));*/ } }