dust3d/thirdparty/carve-1.4.0/external/GLOOP/src/shader.cpp

298 lines
8.4 KiB
C++

// Copyright (c) 2006, Tobias Sargeant
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the
// distribution. The names of its contributors may be used to endorse
// or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
#include <gloop/shader.hpp>
namespace gloop {
int Shader::s_tag = 0;
static inline std::string glsl_log(GLuint handle) {
if (handle == 0) return "";
GLint log_len;
glGetObjectParameterivARB(handle, GL_OBJECT_INFO_LOG_LENGTH_ARB, &log_len);
if (log_len == 0) return "";
std::string log(log_len, '\0');
GLsizei written;
glGetInfoLogARB(handle, log_len, &written, (GLcharARB *)log.c_str());
log.resize(written);
return log;
}
void Shader::_source(std::list<std::string> &result) const {
if (tag == s_tag) return;
tag = s_tag;
for (std::list<Ptr>::const_iterator i = dependencies.begin(), e = dependencies.end(); i != e; ++i) {
(*i)->_source(result);
}
result.push_back(prog);
}
void Shader::_compile() {
if (shader != 0) return;
shader = glCreateShaderObjectARB(shaderType());
if (shader == 0) {
throw std::runtime_error("failed to create shader object");
}
const char *_prog = prog.c_str();
GLint length = prog.size();
glShaderSourceARB(shader, 1, &_prog, &length);
glCompileShaderARB(shader);
GLint compile_status = 0;
glGetObjectParameterivARB(shader, GL_OBJECT_COMPILE_STATUS_ARB, &compile_status);
if (compile_status == 0) {
std::ostringstream err;
err << "failed to compile shader:" << std::endl << glsl_log(shader) << std::endl;
glDeleteObjectARB(shader);
shader = 0;
throw std::runtime_error(err.str());
}
}
void Shader::_attachTo(GLuint program, bool compile) {
if (tag == s_tag) return;
tag = s_tag;
for (std::list<Ptr>::const_iterator i = dependencies.begin(), e = dependencies.end(); i != e; ++i) {
(*i)->_attachTo(program, compile);
}
if (compile && shader == 0) _compile();
if (shader != 0) {
glAttachObjectARB(program, shader);
}
}
Shader::Shader(const std::string &_name, const std::string &_prog) :
name(_name), prog(_prog), shader(0), tag(-1), dependencies() {
}
static inline std::string _read_stream(std::istream &_stream) {
char buf[1024];
std::string r;
while (_stream.good()) {
_stream.read(buf, 1024);
r.append(buf, _stream.gcount());
}
return r;
}
Shader::Shader(const std::string &_name, std::istream &_stream) :
name(_name), prog(_read_stream(_stream)), shader(0), tag(-1), dependencies() {
}
Shader::~Shader() {
destroy();
}
void Shader::addDependency(const Ptr &dep) {
dependencies.push_back(dep);
}
void Shader::compileFlat() {
if (shader != 0) return;
shader = glCreateShaderObjectARB(shaderType());
if (shader == 0) {
throw std::runtime_error("failed to create shader object");
}
std::list<std::string> all_source;
s_tag++;
_source(all_source);
std::ostringstream _prog;
std::copy(all_source.begin(), all_source.end(), std::ostream_iterator<std::string>(_prog, "\n"));
GLint length = _prog.str().size();
const char *_p = _prog.str().c_str();
glShaderSourceARB(shader, 1, &_p, &length);
glCompileShaderARB(shader);
GLint compile_status = 0;
glGetObjectParameterivARB(shader, GL_OBJECT_COMPILE_STATUS_ARB, &compile_status);
if (compile_status == 0) {
std::ostringstream err;
err << "failed to compile shader:" << std::endl << "----" << std::endl << glsl_log(shader) << std::endl << "----" << std::endl;
glDeleteObjectARB(shader);
shader = 0;
throw std::runtime_error(err.str());
}
}
void Shader::compile() {
if (shader != 0) return;
for (std::list<Ptr>::const_iterator i = dependencies.begin(), e = dependencies.end(); i != e; ++i) {
(*i)->compile();
}
_compile();
}
void Shader::attachFlat(GLuint program, bool compile) {
if (shader == 0 && compile) {
compileFlat();
}
if (shader != 0) {
glAttachObjectARB(program, shader);
}
}
void Shader::attachTo(GLuint program, bool compile) {
s_tag++;
_attachTo(program, compile);
}
void Shader::destroy() {
if (shader != 0) {
glDeleteObjectARB(shader);
shader = 0;
}
}
GLenum VertexShader::shaderType() {
return GL_VERTEX_SHADER_ARB;
}
VertexShader::VertexShader(const std::string &_name, const std::string &_prog) :
Shader(_name, _prog) {
}
VertexShader::VertexShader(const std::string &_name, std::istream &_stream) :
Shader(_name, _stream) {
}
VertexShader::~VertexShader() {
}
GLenum FragmentShader::shaderType() {
return GL_FRAGMENT_SHADER_ARB;
}
FragmentShader::FragmentShader(const std::string &_name, const std::string &_prog) :
Shader(_name, _prog) {
}
FragmentShader::FragmentShader(const std::string &_name, std::istream &_stream) :
Shader(_name, _stream) {
}
FragmentShader::~FragmentShader() {
}
ShaderProgram::ShaderProgram() : vertex_shader(NULL), fragment_shader(NULL), program(0) {
}
void ShaderProgram::destroy() {
if (program != 0) {
glDeleteObjectARB(program);
program = 0;
}
}
ShaderProgram::~ShaderProgram() {
destroy();
}
ShaderProgram &ShaderProgram::connect(VertexShader *shader) {
destroy();
vertex_shader = shader;
return *this;
}
ShaderProgram &ShaderProgram::connect(FragmentShader *shader) {
destroy();
fragment_shader = shader;
return *this;
}
ShaderProgram &ShaderProgram::connect(const VertexShader::Ptr &shader) {
destroy();
vertex_shader = shader;
return *this;
}
ShaderProgram &ShaderProgram::connect(const FragmentShader::Ptr &shader) {
destroy();
fragment_shader = shader;
return *this;
}
ShaderProgram &ShaderProgram::connect(Shader *shader) {
VertexShader *v;
FragmentShader *f;
v = dynamic_cast<VertexShader *>(shader); if (v) connect(v);
f = dynamic_cast<FragmentShader *>(shader); if (f) connect(f);
return *this;
}
ShaderProgram &ShaderProgram::connect(Shader::Ptr &shader) {
VertexShader *v;
FragmentShader *f;
v = dynamic_cast<VertexShader *>(shader.ptr()); if (v) connect(v);
f = dynamic_cast<FragmentShader *>(shader.ptr()); if (f) connect(f);
return *this;
}
ShaderProgram &ShaderProgram::link() {
if (vertex_shader != NULL) vertex_shader->compileFlat();
if (fragment_shader != NULL) fragment_shader->compileFlat();
program = glCreateProgramObjectARB();
if (program == 0) {
throw std::runtime_error("failed to create program object");
}
if (vertex_shader != NULL) vertex_shader->attachFlat(program);
if (fragment_shader != NULL) fragment_shader->attachFlat(program);
glLinkProgramARB(program);
GLint link_status = 0;
glGetObjectParameterivARB(program, GL_OBJECT_LINK_STATUS_ARB, &link_status);
if (link_status == 0) {
std::string err = glsl_log(program);
destroy();
throw std::runtime_error(err);
}
return *this;
}
}