/* 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_data.h" #include "stdlibincl.h" #include "compiler.h" //#include #include #include "debug.h" #ifndef PARSELINELENGTH #define PARSELINELENGTH 512 //!< how many chars to read at once #endif ObjectListDefinition(ShaderData); /** * standard constructor */ ShaderData::ShaderData () { this->registerObject(this, ShaderData::_objectList); this->shaderProgram = 0; this->vertexShader = 0; this->fragmentShader = 0; } /// TODO fix that shaders are unloaded first. then loaded bool ShaderData::load(const std::string& vertexShaderFile, const std::string& fragmentShaderFile) { if (GLEW_ARB_shader_objects && GLEW_ARB_shading_language_100) { this->shaderProgram = glCreateProgramObjectARB(); if (!vertexShaderFile.empty()) this->loadShaderProgramm(ShaderData::Vertex, vertexShaderFile); if (!fragmentShaderFile.empty()) this->loadShaderProgramm(ShaderData::Fragment, fragmentShaderFile); this->linkShaderProgram(); } else { PRINTF(2)("Shaders are not supported on your hardware\n"); } } /** * standard deconstructor */ ShaderData::~ShaderData () { if (this->shaderProgram == glGetHandleARB(GL_PROGRAM_OBJECT_ARB)) glUseProgramObjectARB(0); // delete what has to be deleted here this->deleteProgram(ShaderData::Vertex); this->deleteProgram(ShaderData::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); } } bool ShaderData::loadShaderProgramm(ShaderData::Type type, const std::string& fileName) { GLhandleARB shader = 0; if (type != ShaderData::Vertex && type != ShaderData::Fragment) return false; this->deleteProgram(type); std::string program; if (!readShader(fileName, program)) return false; if (type == ShaderData::Vertex && GLEW_ARB_vertex_shader) { this->vertexShaderFile = fileName; shader = this->vertexShader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); } if (type == ShaderData::Fragment && GLEW_ARB_fragment_shader) { this->fragmentShaderFile = fileName; shader = this->fragmentShader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); } if (shader != 0) { GLint status = 0; const char* prog = program.c_str(); glShaderSourceARB(shader, 1, &prog, 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); } return true; } void ShaderData::linkShaderProgram() { GLint status = 0; 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); } bool ShaderData::readShader(const std::string& fileName, std::string& output) { char lineBuffer[PARSELINELENGTH]; std::ifstream shader; shader.open(fileName.c_str()); if (!shader.is_open()) return false; while (!shader.eof()) { shader.getline(lineBuffer, PARSELINELENGTH); output += lineBuffer; output += "\n"; } shader.close(); return true; } void ShaderData::bindShader(const char* name, const float* value, size_t size) { if (likely (this->shaderProgram != 0)) { glUseProgramObjectARB(this->shaderProgram); unsigned int location = glGetUniformLocationARB(this->shaderProgram, name); /* This is EXPENSIVE and should be avoided. */ if (size == 1) glUniform1fvARB(location, 1, value); else if (size == 2) glUniform2fvARB(location, 1, value); else if (size == 3) glUniform3fvARB(location, 1, value); else if (size == 4) glUniform4fvARB(location, 1, value); else if (size == 9) glUniformMatrix3fvARB(location, 1, false, value); else if (size == 16) glUniformMatrix4fvARB(location, 1, false, value); } } void ShaderData::deleteProgram(ShaderData::Type type) { GLint status = 0; if (type == ShaderData::Vertex && this->vertexShader != 0) { this->vertexShaderFile = ""; 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) ShaderData::printError(this->vertexShader); this->vertexShader = 0; } else if (type == ShaderData::Fragment && this->fragmentShader != 0) { this->fragmentShaderFile = ""; 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) ShaderData::printError(this->fragmentShader); this->fragmentShader = 0; } else return; } void ShaderData::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; } } void ShaderData::debug() const { PRINT(3)("ShaderData 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));*/ } }