2522 lines
71 KiB
C++
2522 lines
71 KiB
C++
/*
|
|
Copyright (C) 2001-2006, William Joseph.
|
|
All Rights Reserved.
|
|
|
|
This file is part of GtkRadiant.
|
|
|
|
GtkRadiant 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 of the License, or
|
|
(at your option) any later version.
|
|
|
|
GtkRadiant is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GtkRadiant; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "renderstate.h"
|
|
|
|
#include "debugging/debugging.h"
|
|
#include "warnings.h"
|
|
|
|
#include "ishaders.h"
|
|
#include "irender.h"
|
|
#include "itextures.h"
|
|
#include "igl.h"
|
|
#include "iglrender.h"
|
|
#include "renderable.h"
|
|
#include "qerplugin.h"
|
|
|
|
#include <set>
|
|
#include <vector>
|
|
#include <list>
|
|
#include <map>
|
|
|
|
#include "math/matrix.h"
|
|
#include "math/aabb.h"
|
|
#include "generic/callback.h"
|
|
#include "texturelib.h"
|
|
#include "string/string.h"
|
|
#include "container/hashfunc.h"
|
|
#include "container/cache.h"
|
|
#include "generic/reference.h"
|
|
#include "moduleobservers.h"
|
|
#include "stream/filestream.h"
|
|
#include "stream/stringstream.h"
|
|
#include "os/file.h"
|
|
#include "preferences.h"
|
|
|
|
#include "xywindow.h"
|
|
|
|
|
|
#define DEBUG_RENDER 0
|
|
|
|
inline void debug_string(const char *string)
|
|
{
|
|
#if (DEBUG_RENDER)
|
|
globalOutputStream() << string << "\n";
|
|
#endif
|
|
}
|
|
|
|
inline void debug_int(const char *comment, int i)
|
|
{
|
|
#if (DEBUG_RENDER)
|
|
globalOutputStream() << comment << " " << i << "\n";
|
|
#endif
|
|
}
|
|
|
|
inline void debug_colour(const char *comment)
|
|
{
|
|
#if (DEBUG_RENDER)
|
|
Vector4 v;
|
|
glGetFloatv( GL_CURRENT_COLOR, reinterpret_cast<float*>( &v ) );
|
|
globalOutputStream() << comment << " colour: "
|
|
<< v[0] << " "
|
|
<< v[1] << " "
|
|
<< v[2] << " "
|
|
<< v[3];
|
|
if ( glIsEnabled( GL_COLOR_ARRAY ) ) {
|
|
globalOutputStream() << " ARRAY";
|
|
}
|
|
if ( glIsEnabled( GL_COLOR_MATERIAL ) ) {
|
|
globalOutputStream() << " MATERIAL";
|
|
}
|
|
globalOutputStream() << "\n";
|
|
#endif
|
|
}
|
|
|
|
#include "timer.h"
|
|
|
|
StringOutputStream g_renderer_stats;
|
|
std::size_t g_count_prims;
|
|
std::size_t g_count_states;
|
|
std::size_t g_count_transforms;
|
|
Timer g_timer;
|
|
|
|
inline void count_prim()
|
|
{
|
|
++g_count_prims;
|
|
}
|
|
|
|
inline void count_state()
|
|
{
|
|
++g_count_states;
|
|
}
|
|
|
|
inline void count_transform()
|
|
{
|
|
++g_count_transforms;
|
|
}
|
|
|
|
void Renderer_ResetStats()
|
|
{
|
|
g_count_prims = 0;
|
|
g_count_states = 0;
|
|
g_count_transforms = 0;
|
|
g_timer.start();
|
|
}
|
|
|
|
const char *Renderer_GetStats()
|
|
{
|
|
g_renderer_stats.clear();
|
|
g_renderer_stats << "prims: " << Unsigned(g_count_prims)
|
|
<< " | states: " << Unsigned(g_count_states)
|
|
<< " | transforms: " << Unsigned(g_count_transforms)
|
|
<< " | msec: " << g_timer.elapsed_msec();
|
|
return g_renderer_stats.c_str();
|
|
}
|
|
|
|
|
|
void printShaderLog(GLhandleARB object)
|
|
{
|
|
GLint log_length = 0;
|
|
glGetObjectParameterivARB(object, GL_OBJECT_INFO_LOG_LENGTH_ARB, &log_length);
|
|
|
|
Array<char> log(log_length);
|
|
glGetInfoLogARB(object, log_length, &log_length, log.data());
|
|
|
|
globalErrorStream() << StringRange(log.begin(), log.begin() + log_length) << "\n";
|
|
}
|
|
|
|
void createShader(GLhandleARB program, const char *filename, GLenum type)
|
|
{
|
|
GLhandleARB shader = glCreateShaderObjectARB(type);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
|
|
// load shader
|
|
{
|
|
std::size_t size = file_size(filename);
|
|
FileInputStream file(filename);
|
|
ASSERT_MESSAGE(!file.failed(), "failed to open " << makeQuoted(filename));
|
|
Array<GLcharARB> buffer(size);
|
|
size = file.read(reinterpret_cast<StreamBase::byte_type *>( buffer.data()), size);
|
|
|
|
const GLcharARB *string = buffer.data();
|
|
GLint length = GLint(size);
|
|
glShaderSourceARB(shader, 1, &string, &length);
|
|
}
|
|
|
|
// compile shader
|
|
{
|
|
glCompileShaderARB(shader);
|
|
|
|
GLint compiled = 0;
|
|
glGetObjectParameterivARB(shader, GL_OBJECT_COMPILE_STATUS_ARB, &compiled);
|
|
|
|
if (!compiled) {
|
|
printShaderLog(shader);
|
|
}
|
|
|
|
ASSERT_MESSAGE(compiled, "shader compile failed: " << makeQuoted(filename));
|
|
}
|
|
|
|
// attach shader
|
|
glAttachObjectARB(program, shader);
|
|
|
|
glDeleteObjectARB(shader);
|
|
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
|
|
void GLSLProgram_link(GLhandleARB program)
|
|
{
|
|
glLinkProgramARB(program);
|
|
|
|
GLint linked = false;
|
|
glGetObjectParameterivARB(program, GL_OBJECT_LINK_STATUS_ARB, &linked);
|
|
|
|
if (!linked) {
|
|
printShaderLog(program);
|
|
}
|
|
|
|
ASSERT_MESSAGE(linked, "program link failed");
|
|
}
|
|
|
|
void GLSLProgram_validate(GLhandleARB program)
|
|
{
|
|
glValidateProgramARB(program);
|
|
|
|
GLint validated = false;
|
|
glGetObjectParameterivARB(program, GL_OBJECT_VALIDATE_STATUS_ARB, &validated);
|
|
|
|
if (!validated) {
|
|
printShaderLog(program);
|
|
}
|
|
|
|
ASSERT_MESSAGE(validated, "program validation failed");
|
|
}
|
|
|
|
bool g_bumpGLSLPass_enabled = false;
|
|
bool g_depthfillPass_enabled = false;
|
|
|
|
class GLSLBumpProgram : public GLProgram {
|
|
public:
|
|
GLhandleARB m_program;
|
|
qtexture_t *m_light_attenuation_xy;
|
|
qtexture_t *m_light_attenuation_z;
|
|
GLint u_view_origin;
|
|
GLint u_light_origin;
|
|
GLint u_light_color;
|
|
GLint u_bump_scale;
|
|
GLint u_specular_exponent;
|
|
|
|
GLSLBumpProgram() : m_program(0), m_light_attenuation_xy(0), m_light_attenuation_z(0)
|
|
{
|
|
}
|
|
|
|
void create()
|
|
{
|
|
// create program
|
|
m_program = glCreateProgramObjectARB();
|
|
|
|
// create shader
|
|
{
|
|
StringOutputStream filename(256);
|
|
filename << GlobalRadiant().getAppPath() << "gl/lighting_DBS_omni_vp.glsl";
|
|
createShader(m_program, filename.c_str(), GL_VERTEX_SHADER_ARB);
|
|
filename.clear();
|
|
filename << GlobalRadiant().getAppPath() << "gl/lighting_DBS_omni_fp.glsl";
|
|
createShader(m_program, filename.c_str(), GL_FRAGMENT_SHADER_ARB);
|
|
}
|
|
|
|
GLSLProgram_link(m_program);
|
|
GLSLProgram_validate(m_program);
|
|
|
|
glUseProgramObjectARB(m_program);
|
|
|
|
glBindAttribLocationARB(m_program, c_attr_TexCoord0, "attr_TexCoord0");
|
|
glBindAttribLocationARB(m_program, c_attr_Tangent, "attr_Tangent");
|
|
glBindAttribLocationARB(m_program, c_attr_Binormal, "attr_Binormal");
|
|
|
|
glUniform1iARB(glGetUniformLocationARB(m_program, "u_diffusemap"), 0);
|
|
glUniform1iARB(glGetUniformLocationARB(m_program, "u_bumpmap"), 1);
|
|
glUniform1iARB(glGetUniformLocationARB(m_program, "u_specularmap"), 2);
|
|
glUniform1iARB(glGetUniformLocationARB(m_program, "u_attenuationmap_xy"), 3);
|
|
glUniform1iARB(glGetUniformLocationARB(m_program, "u_attenuationmap_z"), 4);
|
|
|
|
u_view_origin = glGetUniformLocationARB(m_program, "u_view_origin");
|
|
u_light_origin = glGetUniformLocationARB(m_program, "u_light_origin");
|
|
u_light_color = glGetUniformLocationARB(m_program, "u_light_color");
|
|
u_bump_scale = glGetUniformLocationARB(m_program, "u_bump_scale");
|
|
u_specular_exponent = glGetUniformLocationARB(m_program, "u_specular_exponent");
|
|
|
|
glUseProgramObjectARB(0);
|
|
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
|
|
void destroy()
|
|
{
|
|
glDeleteObjectARB(m_program);
|
|
m_program = 0;
|
|
}
|
|
|
|
void enable()
|
|
{
|
|
glUseProgramObjectARB(m_program);
|
|
|
|
glEnableVertexAttribArrayARB(c_attr_TexCoord0);
|
|
glEnableVertexAttribArrayARB(c_attr_Tangent);
|
|
glEnableVertexAttribArrayARB(c_attr_Binormal);
|
|
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
|
|
debug_string("enable bump");
|
|
g_bumpGLSLPass_enabled = true;
|
|
}
|
|
|
|
void disable()
|
|
{
|
|
glUseProgramObjectARB(0);
|
|
|
|
glDisableVertexAttribArrayARB(c_attr_TexCoord0);
|
|
glDisableVertexAttribArrayARB(c_attr_Tangent);
|
|
glDisableVertexAttribArrayARB(c_attr_Binormal);
|
|
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
|
|
debug_string("disable bump");
|
|
g_bumpGLSLPass_enabled = false;
|
|
}
|
|
|
|
void setParameters(const Vector3 &viewer, const Matrix4 &localToWorld, const Vector3 &origin, const Vector3 &colour,
|
|
const Matrix4 &world2light)
|
|
{
|
|
Matrix4 world2local(localToWorld);
|
|
matrix4_affine_invert(world2local);
|
|
|
|
Vector3 localLight(origin);
|
|
matrix4_transform_point(world2local, localLight);
|
|
|
|
Vector3 localViewer(viewer);
|
|
matrix4_transform_point(world2local, localViewer);
|
|
|
|
Matrix4 local2light(world2light);
|
|
matrix4_multiply_by_matrix4(local2light, localToWorld); // local->world->light
|
|
|
|
glUniform3fARB(u_view_origin, localViewer.x(), localViewer.y(), localViewer.z());
|
|
glUniform3fARB(u_light_origin, localLight.x(), localLight.y(), localLight.z());
|
|
glUniform3fARB(u_light_color, colour.x(), colour.y(), colour.z());
|
|
glUniform1fARB(u_bump_scale, 1.0);
|
|
glUniform1fARB(u_specular_exponent, 32.0);
|
|
|
|
glActiveTexture(GL_TEXTURE3);
|
|
glClientActiveTexture(GL_TEXTURE3);
|
|
|
|
glMatrixMode(GL_TEXTURE);
|
|
glLoadMatrixf(reinterpret_cast<const float *>( &local2light ));
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
};
|
|
|
|
GLSLBumpProgram g_bumpGLSL;
|
|
|
|
|
|
class GLSLDepthFillProgram : public GLProgram {
|
|
public:
|
|
GLhandleARB m_program;
|
|
|
|
void create()
|
|
{
|
|
// create program
|
|
m_program = glCreateProgramObjectARB();
|
|
|
|
// create shader
|
|
{
|
|
StringOutputStream filename(256);
|
|
filename << GlobalRadiant().getAppPath() << "gl/zfill_vp.glsl";
|
|
createShader(m_program, filename.c_str(), GL_VERTEX_SHADER_ARB);
|
|
filename.clear();
|
|
filename << GlobalRadiant().getAppPath() << "gl/zfill_fp.glsl";
|
|
createShader(m_program, filename.c_str(), GL_FRAGMENT_SHADER_ARB);
|
|
}
|
|
|
|
GLSLProgram_link(m_program);
|
|
GLSLProgram_validate(m_program);
|
|
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
|
|
void destroy()
|
|
{
|
|
glDeleteObjectARB(m_program);
|
|
m_program = 0;
|
|
}
|
|
|
|
void enable()
|
|
{
|
|
glUseProgramObjectARB(m_program);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
debug_string("enable depthfill");
|
|
g_depthfillPass_enabled = true;
|
|
}
|
|
|
|
void disable()
|
|
{
|
|
glUseProgramObjectARB(0);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
debug_string("disable depthfill");
|
|
g_depthfillPass_enabled = false;
|
|
}
|
|
|
|
void setParameters(const Vector3 &viewer, const Matrix4 &localToWorld, const Vector3 &origin, const Vector3 &colour,
|
|
const Matrix4 &world2light)
|
|
{
|
|
}
|
|
};
|
|
|
|
GLSLDepthFillProgram g_depthFillGLSL;
|
|
|
|
|
|
// ARB path
|
|
|
|
void createProgram(const char *filename, GLenum type)
|
|
{
|
|
std::size_t size = file_size(filename);
|
|
FileInputStream file(filename);
|
|
ASSERT_MESSAGE(!file.failed(), "failed to open " << makeQuoted(filename));
|
|
Array<GLcharARB> buffer(size);
|
|
size = file.read(reinterpret_cast<StreamBase::byte_type *>( buffer.data()), size);
|
|
|
|
glProgramStringARB(type, GL_PROGRAM_FORMAT_ASCII_ARB, GLsizei(size), buffer.data());
|
|
|
|
if (GL_INVALID_OPERATION == glGetError()) {
|
|
GLint errPos;
|
|
glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errPos);
|
|
const GLubyte *errString = glGetString(GL_PROGRAM_ERROR_STRING_ARB);
|
|
|
|
globalErrorStream() << reinterpret_cast<const char *>( filename ) << ":" << errPos << "\n"
|
|
<< reinterpret_cast<const char *>( errString );
|
|
|
|
ERROR_MESSAGE("error in gl program");
|
|
}
|
|
}
|
|
|
|
class ARBBumpProgram : public GLProgram {
|
|
public:
|
|
GLuint m_vertex_program;
|
|
GLuint m_fragment_program;
|
|
|
|
void create()
|
|
{
|
|
glEnable(GL_VERTEX_PROGRAM_ARB);
|
|
glEnable(GL_FRAGMENT_PROGRAM_ARB);
|
|
|
|
{
|
|
glGenProgramsARB(1, &m_vertex_program);
|
|
glBindProgramARB(GL_VERTEX_PROGRAM_ARB, m_vertex_program);
|
|
StringOutputStream filename(256);
|
|
filename << GlobalRadiant().getAppPath() << "gl/lighting_DBS_omni_vp.glp";
|
|
createProgram(filename.c_str(), GL_VERTEX_PROGRAM_ARB);
|
|
|
|
glGenProgramsARB(1, &m_fragment_program);
|
|
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_fragment_program);
|
|
filename.clear();
|
|
filename << GlobalRadiant().getAppPath() << "gl/lighting_DBS_omni_fp.glp";
|
|
createProgram(filename.c_str(), GL_FRAGMENT_PROGRAM_ARB);
|
|
}
|
|
|
|
glDisable(GL_VERTEX_PROGRAM_ARB);
|
|
glDisable(GL_FRAGMENT_PROGRAM_ARB);
|
|
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
|
|
void destroy()
|
|
{
|
|
glDeleteProgramsARB(1, &m_vertex_program);
|
|
glDeleteProgramsARB(1, &m_fragment_program);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
|
|
void enable()
|
|
{
|
|
glEnable(GL_VERTEX_PROGRAM_ARB);
|
|
glEnable(GL_FRAGMENT_PROGRAM_ARB);
|
|
glBindProgramARB(GL_VERTEX_PROGRAM_ARB, m_vertex_program);
|
|
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_fragment_program);
|
|
|
|
glEnableVertexAttribArrayARB(8);
|
|
glEnableVertexAttribArrayARB(9);
|
|
glEnableVertexAttribArrayARB(10);
|
|
glEnableVertexAttribArrayARB(11);
|
|
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
|
|
void disable()
|
|
{
|
|
glDisable(GL_VERTEX_PROGRAM_ARB);
|
|
glDisable(GL_FRAGMENT_PROGRAM_ARB);
|
|
|
|
glDisableVertexAttribArrayARB(8);
|
|
glDisableVertexAttribArrayARB(9);
|
|
glDisableVertexAttribArrayARB(10);
|
|
glDisableVertexAttribArrayARB(11);
|
|
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
|
|
void setParameters(const Vector3 &viewer, const Matrix4 &localToWorld, const Vector3 &origin, const Vector3 &colour,
|
|
const Matrix4 &world2light)
|
|
{
|
|
Matrix4 world2local(localToWorld);
|
|
matrix4_affine_invert(world2local);
|
|
|
|
Vector3 localLight(origin);
|
|
matrix4_transform_point(world2local, localLight);
|
|
|
|
Vector3 localViewer(viewer);
|
|
matrix4_transform_point(world2local, localViewer);
|
|
|
|
Matrix4 local2light(world2light);
|
|
matrix4_multiply_by_matrix4(local2light, localToWorld); // local->world->light
|
|
|
|
// view origin
|
|
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 4, localViewer.x(), localViewer.y(), localViewer.z(), 0);
|
|
|
|
// light origin
|
|
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 2, localLight.x(), localLight.y(), localLight.z(), 1);
|
|
|
|
// light colour
|
|
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 3, colour.x(), colour.y(), colour.z(), 0);
|
|
|
|
// bump scale
|
|
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 1, 1, 0, 0, 0);
|
|
|
|
// specular exponent
|
|
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 5, 32, 0, 0, 0);
|
|
|
|
|
|
glActiveTexture(GL_TEXTURE3);
|
|
glClientActiveTexture(GL_TEXTURE3);
|
|
|
|
glMatrixMode(GL_TEXTURE);
|
|
glLoadMatrixf(reinterpret_cast<const float *>( &local2light ));
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
};
|
|
|
|
class ARBDepthFillProgram : public GLProgram {
|
|
public:
|
|
GLuint m_vertex_program;
|
|
GLuint m_fragment_program;
|
|
|
|
void create()
|
|
{
|
|
glEnable(GL_VERTEX_PROGRAM_ARB);
|
|
glEnable(GL_FRAGMENT_PROGRAM_ARB);
|
|
|
|
{
|
|
glGenProgramsARB(1, &m_vertex_program);
|
|
glBindProgramARB(GL_VERTEX_PROGRAM_ARB, m_vertex_program);
|
|
StringOutputStream filename(256);
|
|
filename << GlobalRadiant().getAppPath() << "gl/zfill_vp.glp";
|
|
createProgram(filename.c_str(), GL_VERTEX_PROGRAM_ARB);
|
|
|
|
glGenProgramsARB(1, &m_fragment_program);
|
|
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_fragment_program);
|
|
filename.clear();
|
|
filename << GlobalRadiant().getAppPath() << "gl/zfill_fp.glp";
|
|
createProgram(filename.c_str(), GL_FRAGMENT_PROGRAM_ARB);
|
|
}
|
|
|
|
glDisable(GL_VERTEX_PROGRAM_ARB);
|
|
glDisable(GL_FRAGMENT_PROGRAM_ARB);
|
|
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
|
|
void destroy()
|
|
{
|
|
glDeleteProgramsARB(1, &m_vertex_program);
|
|
glDeleteProgramsARB(1, &m_fragment_program);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
|
|
void enable()
|
|
{
|
|
glEnable(GL_VERTEX_PROGRAM_ARB);
|
|
glEnable(GL_FRAGMENT_PROGRAM_ARB);
|
|
glBindProgramARB(GL_VERTEX_PROGRAM_ARB, m_vertex_program);
|
|
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, m_fragment_program);
|
|
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
|
|
void disable()
|
|
{
|
|
glDisable(GL_VERTEX_PROGRAM_ARB);
|
|
glDisable(GL_FRAGMENT_PROGRAM_ARB);
|
|
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
|
|
void setParameters(const Vector3 &viewer, const Matrix4 &localToWorld, const Vector3 &origin, const Vector3 &colour,
|
|
const Matrix4 &world2light)
|
|
{
|
|
}
|
|
};
|
|
|
|
ARBBumpProgram g_bumpARB;
|
|
ARBDepthFillProgram g_depthFillARB;
|
|
|
|
|
|
#if 0
|
|
// NV20 path (unfinished)
|
|
|
|
void createProgram( GLint program, const char* filename, GLenum type ){
|
|
std::size_t size = file_size( filename );
|
|
FileInputStream file( filename );
|
|
ASSERT_MESSAGE( !file.failed(), "failed to open " << makeQuoted( filename ) );
|
|
Array<GLubyte> buffer( size );
|
|
size = file.read( reinterpret_cast<StreamBase::byte_type*>( buffer.data() ), size );
|
|
|
|
glLoadProgramNV( type, program, GLsizei( size ), buffer.data() );
|
|
|
|
if ( GL_INVALID_OPERATION == glGetError() ) {
|
|
GLint errPos;
|
|
glGetIntegerv( GL_PROGRAM_ERROR_POSITION_NV, &errPos );
|
|
const GLubyte* errString = glGetString( GL_PROGRAM_ERROR_STRING_NV );
|
|
|
|
globalErrorStream() << filename << ":" << errPos << "\n" << errString;
|
|
|
|
ERROR_MESSAGE( "error in gl program" );
|
|
}
|
|
}
|
|
|
|
GLuint m_vertex_program;
|
|
GLuint m_fragment_program;
|
|
qtexture_t* g_cube = 0;
|
|
qtexture_t* g_specular_lookup = 0;
|
|
qtexture_t* g_attenuation_xy = 0;
|
|
qtexture_t* g_attenuation_z = 0;
|
|
|
|
void createVertexProgram(){
|
|
{
|
|
glGenProgramsNV( 1, &m_vertex_program );
|
|
glBindProgramNV( GL_VERTEX_PROGRAM_NV, m_vertex_program );
|
|
StringOutputStream filename( 256 );
|
|
filename << GlobalRadiant().getAppPath() << "gl/lighting_DBS_omni_vp.nv30";
|
|
createProgram( m_vertex_program, filename.c_str(), GL_VERTEX_PROGRAM_NV );
|
|
|
|
glGenProgramsNV( 1, &m_fragment_program );
|
|
glBindProgramNV( GL_FRAGMENT_PROGRAM_NV, m_fragment_program );
|
|
filename.clear();
|
|
filename << GlobalRadiant().getAppPath() << "gl/lighting_DBS_omni_fp.nv30";
|
|
createProgram( m_fragment_program, filename.c_str(), GL_FRAGMENT_PROGRAM_NV );
|
|
}
|
|
|
|
g_cube = GlobalTexturesCache().capture( "generated/cube" );
|
|
g_specular_lookup = GlobalTexturesCache().capture( "generated/specular" );
|
|
|
|
g_attenuation_xy = GlobalTexturesCache().capture( "lights/squarelight1" );
|
|
glActiveTexture( GL_TEXTURE0 );
|
|
glBindTexture( GL_TEXTURE_2D, g_attenuation_xy->texture_number );
|
|
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER );
|
|
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER );
|
|
|
|
g_attenuation_z = GlobalTexturesCache().capture( "lights/squarelight1a" );
|
|
glActiveTexture( GL_TEXTURE0 );
|
|
glBindTexture( GL_TEXTURE_2D, g_attenuation_z->texture_number );
|
|
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
|
|
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
|
|
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
|
|
void destroyVertexProgram(){
|
|
glDeleteProgramsNV( 1, &m_vertex_program );
|
|
glDeleteProgramsNV( 1, &m_fragment_program );
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
|
|
GlobalTexturesCache().release( g_cube );
|
|
GlobalTexturesCache().release( g_specular_lookup );
|
|
GlobalTexturesCache().release( g_attenuation_xy );
|
|
GlobalTexturesCache().release( g_attenuation_z );
|
|
}
|
|
|
|
bool g_vertexProgram_enabled = false;
|
|
|
|
void enableVertexProgram(){
|
|
//set up the register combiners
|
|
//two general combiners
|
|
glCombinerParameteriNV( GL_NUM_GENERAL_COMBINERS_NV, 2 );
|
|
|
|
//combiner 0 does tex0+tex1 -> spare0
|
|
glCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV, GL_TEXTURE0_ARB,
|
|
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
|
glCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV, GL_ZERO,
|
|
GL_UNSIGNED_INVERT_NV, GL_RGB );
|
|
glCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV, GL_TEXTURE1_ARB,
|
|
GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
|
glCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV, GL_ZERO,
|
|
GL_UNSIGNED_INVERT_NV, GL_RGB );
|
|
glCombinerOutputNV( GL_COMBINER0_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE0_NV,
|
|
GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );
|
|
|
|
//combiner 1 does tex2 dot tex3 -> spare1
|
|
glCombinerInputNV( GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_A_NV, GL_TEXTURE2_ARB,
|
|
GL_EXPAND_NORMAL_NV, GL_RGB );
|
|
glCombinerInputNV( GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_B_NV, GL_TEXTURE3_ARB,
|
|
GL_EXPAND_NORMAL_NV, GL_RGB );
|
|
glCombinerOutputNV( GL_COMBINER1_NV, GL_RGB, GL_SPARE1_NV, GL_DISCARD_NV, GL_DISCARD_NV,
|
|
GL_NONE, GL_NONE, GL_TRUE, GL_FALSE, GL_FALSE );
|
|
|
|
|
|
|
|
//final combiner outputs (1-spare0)*constant color 0*spare1
|
|
//do constant color 0*spare1 in the EF multiplier
|
|
glFinalCombinerInputNV( GL_VARIABLE_E_NV, GL_SPARE1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
|
glFinalCombinerInputNV( GL_VARIABLE_F_NV, GL_CONSTANT_COLOR0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
|
|
|
//now do (1-spare0)*EF
|
|
glFinalCombinerInputNV( GL_VARIABLE_A_NV, GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
|
glFinalCombinerInputNV( GL_VARIABLE_B_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
|
glFinalCombinerInputNV( GL_VARIABLE_C_NV, GL_E_TIMES_F_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
|
glFinalCombinerInputNV( GL_VARIABLE_D_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
|
|
|
|
glEnable( GL_VERTEX_PROGRAM_NV );
|
|
glEnable( GL_REGISTER_COMBINERS_NV );
|
|
glBindProgramNV( GL_VERTEX_PROGRAM_NV, m_vertex_program );
|
|
glBindProgramNV( GL_FRAGMENT_PROGRAM_NV, m_fragment_program );
|
|
|
|
glActiveTexture( GL_TEXTURE0 );
|
|
glEnable( GL_TEXTURE_2D );
|
|
glActiveTexture( GL_TEXTURE1 );
|
|
glEnable( GL_TEXTURE_1D );
|
|
glActiveTexture( GL_TEXTURE2 );
|
|
glEnable( GL_TEXTURE_2D );
|
|
glActiveTexture( GL_TEXTURE3 );
|
|
glEnable( GL_TEXTURE_2D );
|
|
|
|
glEnableClientState( GL_VERTEX_ATTRIB_ARRAY8_NV );
|
|
glEnableClientState( GL_VERTEX_ATTRIB_ARRAY9_NV );
|
|
glEnableClientState( GL_VERTEX_ATTRIB_ARRAY10_NV );
|
|
glEnableClientState( GL_VERTEX_ATTRIB_ARRAY11_NV );
|
|
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
g_vertexProgram_enabled = true;
|
|
}
|
|
|
|
void disableVertexProgram(){
|
|
glDisable( GL_VERTEX_PROGRAM_NV );
|
|
glDisable( GL_REGISTER_COMBINERS_NV );
|
|
|
|
glActiveTexture( GL_TEXTURE0 );
|
|
glDisable( GL_TEXTURE_2D );
|
|
glActiveTexture( GL_TEXTURE1 );
|
|
glDisable( GL_TEXTURE_1D );
|
|
glActiveTexture( GL_TEXTURE2 );
|
|
glDisable( GL_TEXTURE_2D );
|
|
glActiveTexture( GL_TEXTURE3 );
|
|
glDisable( GL_TEXTURE_2D );
|
|
|
|
glDisableClientState( GL_VERTEX_ATTRIB_ARRAY8_NV );
|
|
glDisableClientState( GL_VERTEX_ATTRIB_ARRAY9_NV );
|
|
glDisableClientState( GL_VERTEX_ATTRIB_ARRAY10_NV );
|
|
glDisableClientState( GL_VERTEX_ATTRIB_ARRAY11_NV );
|
|
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
g_vertexProgram_enabled = false;
|
|
}
|
|
|
|
class GLstringNV
|
|
{
|
|
public:
|
|
const GLubyte* m_string;
|
|
const GLint m_length;
|
|
GLstringNV( const char* string ) : m_string( reinterpret_cast<const GLubyte*>( string ) ), m_length( GLint( string_length( string ) ) ){
|
|
}
|
|
};
|
|
|
|
GLstringNV g_light_origin( "light_origin" );
|
|
GLstringNV g_view_origin( "view_origin" );
|
|
GLstringNV g_light_color( "light_color" );
|
|
GLstringNV g_bumpGLSL_scale( "bump_scale" );
|
|
GLstringNV g_specular_exponent( "specular_exponent" );
|
|
|
|
void setVertexProgramEnvironment( const Vector3& localViewer ){
|
|
Matrix4 local2light( g_matrix4_identity );
|
|
matrix4_translate_by_vec3( local2light, Vector3( 0.5, 0.5, 0.5 ) );
|
|
matrix4_scale_by_vec3( local2light, Vector3( 0.5, 0.5, 0.5 ) );
|
|
matrix4_scale_by_vec3( local2light, Vector3( 1.0 / 512.0, 1.0 / 512.0, 1.0 / 512.0 ) );
|
|
matrix4_translate_by_vec3( local2light, vector3_negated( localViewer ) );
|
|
|
|
glActiveTexture( GL_TEXTURE3 );
|
|
glClientActiveTexture( GL_TEXTURE3 );
|
|
|
|
glMatrixMode( GL_TEXTURE );
|
|
glLoadMatrixf( reinterpret_cast<const float*>( &local2light ) );
|
|
glMatrixMode( GL_MODELVIEW );
|
|
|
|
glTrackMatrixNV( GL_VERTEX_PROGRAM_NV, 0, GL_MODELVIEW_PROJECTION_NV, GL_IDENTITY_NV );
|
|
glTrackMatrixNV( GL_VERTEX_PROGRAM_NV, 4, GL_TEXTURE0_ARB, GL_IDENTITY_NV );
|
|
|
|
// view origin
|
|
//qglProgramNamedParameter4fNV(m_fragment_program, g_view_origin.m_length, g_view_origin.m_string, localViewer.x(), localViewer.y(), localViewer.z(), 0);
|
|
|
|
// light origin
|
|
glProgramParameter4fNV( GL_VERTEX_PROGRAM_NV, 8, localViewer.x(), localViewer.y(), localViewer.z(), 1.0f );
|
|
|
|
// light colour
|
|
glCombinerParameterfNV( GL_CONSTANT_COLOR0_NV, 1, 1, 1, 1 )
|
|
|
|
// bump scale
|
|
//qglProgramNamedParameter4fNV(m_fragment_program, g_bumpGLSL_scale.m_length, g_bumpGLSL_scale.m_string, 1, 0, 0, 0);
|
|
|
|
// specular exponent
|
|
//qglProgramNamedParameter4fNV(m_fragment_program, g_specular_exponent.m_length, g_specular_exponent.m_string, 32, 0, 0, 0);
|
|
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
bool g_vertexArray_enabled = false;
|
|
bool g_normalArray_enabled = false;
|
|
bool g_texcoordArray_enabled = false;
|
|
bool g_colorArray_enabled = false;
|
|
|
|
inline bool OpenGLState_less(const OpenGLState &self, const OpenGLState &other)
|
|
{
|
|
//! Sort by sort-order override.
|
|
if (self.m_sort != other.m_sort) {
|
|
return self.m_sort < other.m_sort;
|
|
}
|
|
//! Sort by texture handle.
|
|
if (self.m_texture != other.m_texture) {
|
|
return self.m_texture < other.m_texture;
|
|
}
|
|
if (self.m_texture1 != other.m_texture1) {
|
|
return self.m_texture1 < other.m_texture1;
|
|
}
|
|
if (self.m_texture2 != other.m_texture2) {
|
|
return self.m_texture2 < other.m_texture2;
|
|
}
|
|
if (self.m_texture3 != other.m_texture3) {
|
|
return self.m_texture3 < other.m_texture3;
|
|
}
|
|
if (self.m_texture4 != other.m_texture4) {
|
|
return self.m_texture4 < other.m_texture4;
|
|
}
|
|
if (self.m_texture5 != other.m_texture5) {
|
|
return self.m_texture5 < other.m_texture5;
|
|
}
|
|
if (self.m_texture6 != other.m_texture6) {
|
|
return self.m_texture6 < other.m_texture6;
|
|
}
|
|
if (self.m_texture7 != other.m_texture7) {
|
|
return self.m_texture7 < other.m_texture7;
|
|
}
|
|
//! Sort by state bit-vector.
|
|
if (self.m_state != other.m_state) {
|
|
return self.m_state < other.m_state;
|
|
}
|
|
//! Comparing address makes sure states are never equal.
|
|
return &self < &other;
|
|
}
|
|
|
|
void OpenGLState_constructDefault(OpenGLState &state)
|
|
{
|
|
state.m_state = RENDER_DEFAULT;
|
|
|
|
state.m_texture = 0;
|
|
state.m_texture1 = 0;
|
|
state.m_texture2 = 0;
|
|
state.m_texture3 = 0;
|
|
state.m_texture4 = 0;
|
|
state.m_texture5 = 0;
|
|
state.m_texture6 = 0;
|
|
state.m_texture7 = 0;
|
|
|
|
state.m_colour[0] = 1;
|
|
state.m_colour[1] = 1;
|
|
state.m_colour[2] = 1;
|
|
state.m_colour[3] = 1;
|
|
|
|
state.m_depthfunc = GL_LESS;
|
|
|
|
state.m_blend_src = GL_SRC_ALPHA;
|
|
state.m_blend_dst = GL_ONE_MINUS_SRC_ALPHA;
|
|
|
|
state.m_alphafunc = GL_ALWAYS;
|
|
state.m_alpharef = 0;
|
|
|
|
state.m_linewidth = 1;
|
|
state.m_pointsize = 1;
|
|
|
|
state.m_linestipple_factor = 1;
|
|
state.m_linestipple_pattern = 0xaaaa;
|
|
|
|
state.m_fog = OpenGLFogState();
|
|
}
|
|
|
|
|
|
/// \brief A container of Renderable references.
|
|
/// May contain the same Renderable multiple times, with different transforms.
|
|
class OpenGLStateBucket {
|
|
public:
|
|
struct RenderTransform {
|
|
const Matrix4 *m_transform;
|
|
const OpenGLRenderable *m_renderable;
|
|
const RendererLight *m_light;
|
|
|
|
RenderTransform(const OpenGLRenderable &renderable, const Matrix4 &transform, const RendererLight *light)
|
|
: m_transform(&transform), m_renderable(&renderable), m_light(light)
|
|
{
|
|
}
|
|
};
|
|
|
|
typedef std::vector<RenderTransform> Renderables;
|
|
|
|
private:
|
|
|
|
OpenGLState m_state;
|
|
Renderables m_renderables;
|
|
|
|
public:
|
|
OpenGLStateBucket()
|
|
{
|
|
}
|
|
|
|
void addRenderable(const OpenGLRenderable &renderable, const Matrix4 &modelview, const RendererLight *light = 0)
|
|
{
|
|
m_renderables.push_back(RenderTransform(renderable, modelview, light));
|
|
}
|
|
|
|
OpenGLState &state()
|
|
{
|
|
return m_state;
|
|
}
|
|
|
|
void render(OpenGLState ¤t, unsigned int globalstate, const Vector3 &viewer);
|
|
};
|
|
|
|
#define LIGHT_SHADER_DEBUG 0
|
|
|
|
#if LIGHT_SHADER_DEBUG
|
|
typedef std::vector<Shader*> LightDebugShaders;
|
|
LightDebugShaders g_lightDebugShaders;
|
|
#endif
|
|
|
|
class OpenGLStateLess {
|
|
public:
|
|
bool operator()(const OpenGLState &self, const OpenGLState &other) const
|
|
{
|
|
return OpenGLState_less(self, other);
|
|
}
|
|
};
|
|
|
|
typedef ConstReference<OpenGLState> OpenGLStateReference;
|
|
typedef std::map<OpenGLStateReference, OpenGLStateBucket *, OpenGLStateLess> OpenGLStates;
|
|
OpenGLStates g_state_sorted;
|
|
|
|
class OpenGLStateBucketAdd {
|
|
OpenGLStateBucket &m_bucket;
|
|
const OpenGLRenderable &m_renderable;
|
|
const Matrix4 &m_modelview;
|
|
public:
|
|
using func = void (const RendererLight &);
|
|
|
|
OpenGLStateBucketAdd(OpenGLStateBucket &bucket, const OpenGLRenderable &renderable, const Matrix4 &modelview) :
|
|
m_bucket(bucket), m_renderable(renderable), m_modelview(modelview)
|
|
{
|
|
}
|
|
|
|
void operator()(const RendererLight &light)
|
|
{
|
|
m_bucket.addRenderable(m_renderable, m_modelview, &light);
|
|
}
|
|
};
|
|
|
|
class CountLights {
|
|
std::size_t m_count;
|
|
public:
|
|
using func = void (RendererLight &);
|
|
|
|
CountLights() : m_count(0)
|
|
{
|
|
}
|
|
|
|
void operator()(const RendererLight &light)
|
|
{
|
|
++m_count;
|
|
}
|
|
|
|
std::size_t count() const
|
|
{
|
|
return m_count;
|
|
}
|
|
};
|
|
|
|
class OpenGLShader : public Shader {
|
|
typedef std::list<OpenGLStateBucket *> Passes;
|
|
Passes m_passes;
|
|
IShader *m_shader;
|
|
std::size_t m_used;
|
|
ModuleObservers m_observers;
|
|
public:
|
|
OpenGLShader() : m_shader(0), m_used(0)
|
|
{
|
|
}
|
|
|
|
~OpenGLShader()
|
|
{
|
|
}
|
|
|
|
void construct(const char *name);
|
|
|
|
void destroy()
|
|
{
|
|
if (m_shader) {
|
|
m_shader->DecRef();
|
|
}
|
|
m_shader = 0;
|
|
|
|
for (Passes::iterator i = m_passes.begin(); i != m_passes.end(); ++i) {
|
|
delete *i;
|
|
}
|
|
m_passes.clear();
|
|
}
|
|
|
|
void addRenderable(const OpenGLRenderable &renderable, const Matrix4 &modelview, const LightList *lights)
|
|
{
|
|
for (Passes::iterator i = m_passes.begin(); i != m_passes.end(); ++i) {
|
|
#if LIGHT_SHADER_DEBUG
|
|
if ( ( ( *i )->state().m_state & RENDER_BUMP ) != 0 ) {
|
|
if ( lights != 0 ) {
|
|
CountLights counter;
|
|
lights->forEachLight( makeCallback1( counter ) );
|
|
globalOutputStream() << "count = " << counter.count() << "\n";
|
|
for ( std::size_t i = 0; i < counter.count(); ++i )
|
|
{
|
|
g_lightDebugShaders[counter.count()]->addRenderable( renderable, modelview );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
#else
|
|
if (((*i)->state().m_state & RENDER_BUMP) != 0) {
|
|
if (lights != 0) {
|
|
OpenGLStateBucketAdd add(*(*i), renderable, modelview);
|
|
lights->forEachLight(makeCallback(add));
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
(*i)->addRenderable(renderable, modelview);
|
|
}
|
|
}
|
|
}
|
|
|
|
void incrementUsed()
|
|
{
|
|
if (++m_used == 1 && m_shader != 0) {
|
|
m_shader->SetInUse(true);
|
|
}
|
|
}
|
|
|
|
void decrementUsed()
|
|
{
|
|
if (--m_used == 0 && m_shader != 0) {
|
|
m_shader->SetInUse(false);
|
|
}
|
|
}
|
|
|
|
bool realised() const
|
|
{
|
|
return m_shader != 0;
|
|
}
|
|
|
|
void attach(ModuleObserver &observer)
|
|
{
|
|
if (realised()) {
|
|
observer.realise();
|
|
}
|
|
m_observers.attach(observer);
|
|
}
|
|
|
|
void detach(ModuleObserver &observer)
|
|
{
|
|
if (realised()) {
|
|
observer.unrealise();
|
|
}
|
|
m_observers.detach(observer);
|
|
}
|
|
|
|
void realise(const CopiedString &name)
|
|
{
|
|
construct(name.c_str());
|
|
|
|
if (m_used != 0 && m_shader != 0) {
|
|
m_shader->SetInUse(true);
|
|
}
|
|
|
|
for (Passes::iterator i = m_passes.begin(); i != m_passes.end(); ++i) {
|
|
g_state_sorted.insert(OpenGLStates::value_type(OpenGLStateReference((*i)->state()), *i));
|
|
}
|
|
|
|
m_observers.realise();
|
|
}
|
|
|
|
void unrealise()
|
|
{
|
|
m_observers.unrealise();
|
|
|
|
for (Passes::iterator i = m_passes.begin(); i != m_passes.end(); ++i) {
|
|
g_state_sorted.erase(OpenGLStateReference((*i)->state()));
|
|
}
|
|
|
|
destroy();
|
|
}
|
|
|
|
qtexture_t &getTexture() const
|
|
{
|
|
ASSERT_NOTNULL(m_shader);
|
|
return *m_shader->getTexture();
|
|
}
|
|
|
|
unsigned int getFlags() const
|
|
{
|
|
ASSERT_NOTNULL(m_shader);
|
|
return m_shader->getFlags();
|
|
}
|
|
|
|
IShader &getShader() const
|
|
{
|
|
ASSERT_NOTNULL(m_shader);
|
|
return *m_shader;
|
|
}
|
|
|
|
OpenGLState &appendDefaultPass()
|
|
{
|
|
m_passes.push_back(new OpenGLStateBucket);
|
|
OpenGLState &state = m_passes.back()->state();
|
|
OpenGLState_constructDefault(state);
|
|
return state;
|
|
}
|
|
};
|
|
|
|
|
|
inline bool lightEnabled(const RendererLight &light, const LightCullable &cullable)
|
|
{
|
|
return cullable.testLight(light);
|
|
}
|
|
|
|
typedef std::set<RendererLight *> RendererLights;
|
|
|
|
#define DEBUG_LIGHT_SYNC 0
|
|
|
|
class LinearLightList : public LightList {
|
|
LightCullable &m_cullable;
|
|
RendererLights &m_allLights;
|
|
Callback<void()> m_evaluateChanged;
|
|
|
|
typedef std::list<RendererLight *> Lights;
|
|
mutable Lights m_lights;
|
|
mutable bool m_lightsChanged;
|
|
public:
|
|
LinearLightList(LightCullable &cullable, RendererLights &lights, const Callback<void()> &evaluateChanged) :
|
|
m_cullable(cullable), m_allLights(lights), m_evaluateChanged(evaluateChanged)
|
|
{
|
|
m_lightsChanged = true;
|
|
}
|
|
|
|
void evaluateLights() const
|
|
{
|
|
m_evaluateChanged();
|
|
if (m_lightsChanged) {
|
|
m_lightsChanged = false;
|
|
|
|
m_lights.clear();
|
|
m_cullable.clearLights();
|
|
for (RendererLights::const_iterator i = m_allLights.begin(); i != m_allLights.end(); ++i) {
|
|
if (lightEnabled(*(*i), m_cullable)) {
|
|
m_lights.push_back(*i);
|
|
m_cullable.insertLight(*(*i));
|
|
}
|
|
}
|
|
}
|
|
#if (DEBUG_LIGHT_SYNC)
|
|
else
|
|
{
|
|
Lights lights;
|
|
for ( RendererLights::const_iterator i = m_allLights.begin(); i != m_allLights.end(); ++i )
|
|
{
|
|
if ( lightEnabled( *( *i ), m_cullable ) ) {
|
|
lights.push_back( *i );
|
|
}
|
|
}
|
|
ASSERT_MESSAGE(
|
|
!std::lexicographical_compare( lights.begin(), lights.end(), m_lights.begin(), m_lights.end() )
|
|
&& !std::lexicographical_compare( m_lights.begin(), m_lights.end(), lights.begin(), lights.end() ),
|
|
"lights out of sync"
|
|
);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void forEachLight(const RendererLightCallback &callback) const
|
|
{
|
|
evaluateLights();
|
|
|
|
for (Lights::const_iterator i = m_lights.begin(); i != m_lights.end(); ++i) {
|
|
callback(*(*i));
|
|
}
|
|
}
|
|
|
|
void lightsChanged() const
|
|
{
|
|
m_lightsChanged = true;
|
|
}
|
|
};
|
|
|
|
inline void setFogState(const OpenGLFogState &state)
|
|
{
|
|
glFogi(GL_FOG_MODE, state.mode);
|
|
glFogf(GL_FOG_DENSITY, state.density);
|
|
glFogf(GL_FOG_START, state.start);
|
|
glFogf(GL_FOG_END, state.end);
|
|
glFogi(GL_FOG_INDEX, state.index);
|
|
glFogfv(GL_FOG_COLOR, vector4_to_array(state.colour));
|
|
}
|
|
|
|
#define DEBUG_SHADERS 0
|
|
|
|
class OpenGLShaderCache : public ShaderCache, public TexturesCacheObserver, public ModuleObserver {
|
|
class CreateOpenGLShader {
|
|
OpenGLShaderCache *m_cache;
|
|
public:
|
|
explicit CreateOpenGLShader(OpenGLShaderCache *cache = 0)
|
|
: m_cache(cache)
|
|
{
|
|
}
|
|
|
|
OpenGLShader *construct(const CopiedString &name)
|
|
{
|
|
OpenGLShader *shader = new OpenGLShader;
|
|
if (m_cache->realised()) {
|
|
shader->realise(name);
|
|
}
|
|
return shader;
|
|
}
|
|
|
|
void destroy(OpenGLShader *shader)
|
|
{
|
|
if (m_cache->realised()) {
|
|
shader->unrealise();
|
|
}
|
|
delete shader;
|
|
}
|
|
};
|
|
|
|
typedef HashedCache<CopiedString, OpenGLShader, HashString, std::equal_to<CopiedString>, CreateOpenGLShader> Shaders;
|
|
Shaders m_shaders;
|
|
std::size_t m_unrealised;
|
|
|
|
bool m_lightingEnabled;
|
|
bool m_lightingSupported;
|
|
bool m_useShaderLanguage;
|
|
|
|
public:
|
|
OpenGLShaderCache()
|
|
: m_shaders(CreateOpenGLShader(this)),
|
|
m_unrealised(
|
|
3), // wait until shaders, gl-context and textures are realised before creating any render-states
|
|
m_lightingEnabled(true),
|
|
m_lightingSupported(false),
|
|
m_useShaderLanguage(false),
|
|
m_lightsChanged(true),
|
|
m_traverseRenderablesMutex(false)
|
|
{
|
|
}
|
|
|
|
~OpenGLShaderCache()
|
|
{
|
|
for (Shaders::iterator i = m_shaders.begin(); i != m_shaders.end(); ++i) {
|
|
globalOutputStream() << "leaked shader: " << makeQuoted((*i).key.c_str()) << "\n";
|
|
}
|
|
}
|
|
|
|
Shader *capture(const char *name)
|
|
{
|
|
ASSERT_MESSAGE(name[0] == '$'
|
|
|| *name == '['
|
|
|| *name == '<'
|
|
|| *name == '('
|
|
|| strchr(name, '\\') == 0, "shader name contains invalid characters: \"" << name << "\"");
|
|
#if DEBUG_SHADERS
|
|
globalOutputStream() << "shaders capture: " << makeQuoted( name ) << '\n';
|
|
#endif
|
|
return m_shaders.capture(name).get();
|
|
}
|
|
|
|
void release(const char *name)
|
|
{
|
|
#if DEBUG_SHADERS
|
|
globalOutputStream() << "shaders release: " << makeQuoted( name ) << '\n';
|
|
#endif
|
|
m_shaders.release(name);
|
|
}
|
|
|
|
void
|
|
render(RenderStateFlags globalstate, const Matrix4 &modelview, const Matrix4 &projection, const Vector3 &viewer)
|
|
{
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadMatrixf(reinterpret_cast<const float *>( &projection ));
|
|
#if 0
|
|
//qglGetFloatv(GL_PROJECTION_MATRIX, reinterpret_cast<float*>(&projection));
|
|
#endif
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadMatrixf(reinterpret_cast<const float *>( &modelview ));
|
|
#if 0
|
|
//qglGetFloatv(GL_MODELVIEW_MATRIX, reinterpret_cast<float*>(&modelview));
|
|
#endif
|
|
|
|
ASSERT_MESSAGE(realised(), "render states are not realised");
|
|
|
|
// global settings that are not set in renderstates
|
|
glFrontFace(GL_CW);
|
|
glCullFace(GL_BACK);
|
|
glPolygonOffset(-1, 1);
|
|
{
|
|
const GLubyte pattern[132] = {
|
|
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
|
|
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
|
|
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
|
|
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
|
|
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
|
|
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
|
|
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
|
|
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
|
|
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
|
|
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
|
|
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
|
|
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
|
|
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
|
|
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
|
|
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
|
|
0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55
|
|
};
|
|
glPolygonStipple(pattern);
|
|
}
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
g_vertexArray_enabled = true;
|
|
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
|
|
|
|
if (GlobalOpenGL().GL_1_3()) {
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glClientActiveTexture(GL_TEXTURE0);
|
|
}
|
|
|
|
if (GlobalOpenGL().ARB_shader_objects()) {
|
|
glUseProgramObjectARB(0);
|
|
glDisableVertexAttribArrayARB(c_attr_TexCoord0);
|
|
glDisableVertexAttribArrayARB(c_attr_Tangent);
|
|
glDisableVertexAttribArrayARB(c_attr_Binormal);
|
|
}
|
|
|
|
if (globalstate & RENDER_TEXTURE) {
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
}
|
|
|
|
OpenGLState current;
|
|
OpenGLState_constructDefault(current);
|
|
current.m_sort = OpenGLState::eSortFirst;
|
|
|
|
// default renderstate settings
|
|
glLineStipple(current.m_linestipple_factor, current.m_linestipple_pattern);
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
glDisable(GL_LIGHTING);
|
|
glDisable(GL_TEXTURE_2D);
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
g_texcoordArray_enabled = false;
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
g_colorArray_enabled = false;
|
|
glDisableClientState(GL_NORMAL_ARRAY);
|
|
g_normalArray_enabled = false;
|
|
glDisable(GL_BLEND);
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
glDisable(GL_CULL_FACE);
|
|
glShadeModel(GL_FLAT);
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDepthMask(GL_FALSE);
|
|
glDisable(GL_ALPHA_TEST);
|
|
glDisable(GL_LINE_STIPPLE);
|
|
glDisable(GL_POLYGON_STIPPLE);
|
|
glDisable(GL_POLYGON_OFFSET_LINE);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
glColor4f(1, 1, 1, 1);
|
|
glDepthFunc(GL_LESS);
|
|
glAlphaFunc(GL_ALWAYS, 0);
|
|
glLineWidth(1);
|
|
glPointSize(1);
|
|
|
|
glHint(GL_FOG_HINT, GL_NICEST);
|
|
glDisable(GL_FOG);
|
|
setFogState(OpenGLFogState());
|
|
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
|
|
debug_string("begin rendering");
|
|
for (OpenGLStates::iterator i = g_state_sorted.begin(); i != g_state_sorted.end(); ++i) {
|
|
(*i).second->render(current, globalstate, viewer);
|
|
}
|
|
debug_string("end rendering");
|
|
}
|
|
|
|
void realise()
|
|
{
|
|
if (--m_unrealised == 0) {
|
|
if (lightingSupported() && lightingEnabled()) {
|
|
if (useShaderLanguage()) {
|
|
g_bumpGLSL.create();
|
|
g_depthFillGLSL.create();
|
|
} else {
|
|
g_bumpARB.create();
|
|
g_depthFillARB.create();
|
|
}
|
|
}
|
|
|
|
for (Shaders::iterator i = m_shaders.begin(); i != m_shaders.end(); ++i) {
|
|
if (!(*i).value.empty()) {
|
|
(*i).value->realise(i->key);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void unrealise()
|
|
{
|
|
if (++m_unrealised == 1) {
|
|
for (Shaders::iterator i = m_shaders.begin(); i != m_shaders.end(); ++i) {
|
|
if (!(*i).value.empty()) {
|
|
(*i).value->unrealise();
|
|
}
|
|
}
|
|
if (GlobalOpenGL().contextValid && lightingSupported() && lightingEnabled()) {
|
|
if (useShaderLanguage()) {
|
|
g_bumpGLSL.destroy();
|
|
g_depthFillGLSL.destroy();
|
|
} else {
|
|
g_bumpARB.destroy();
|
|
g_depthFillARB.destroy();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool realised()
|
|
{
|
|
return m_unrealised == 0;
|
|
}
|
|
|
|
|
|
bool lightingEnabled() const
|
|
{
|
|
return m_lightingEnabled;
|
|
}
|
|
|
|
bool lightingSupported() const
|
|
{
|
|
return m_lightingSupported;
|
|
}
|
|
|
|
bool useShaderLanguage() const
|
|
{
|
|
return m_useShaderLanguage;
|
|
}
|
|
|
|
void setLighting(bool supported, bool enabled)
|
|
{
|
|
bool refresh = (m_lightingSupported && m_lightingEnabled) != (supported && enabled);
|
|
|
|
if (refresh) {
|
|
unrealise();
|
|
GlobalShaderSystem().setLightingEnabled(supported && enabled);
|
|
}
|
|
|
|
m_lightingSupported = supported;
|
|
m_lightingEnabled = enabled;
|
|
|
|
if (refresh) {
|
|
realise();
|
|
}
|
|
}
|
|
|
|
void extensionsInitialised()
|
|
{
|
|
setLighting(GlobalOpenGL().GL_1_3()
|
|
&& GlobalOpenGL().ARB_vertex_program()
|
|
&& GlobalOpenGL().ARB_fragment_program()
|
|
&& GlobalOpenGL().ARB_shader_objects()
|
|
&& GlobalOpenGL().ARB_vertex_shader()
|
|
&& GlobalOpenGL().ARB_fragment_shader()
|
|
&& GlobalOpenGL().ARB_shading_language_100(),
|
|
m_lightingEnabled
|
|
);
|
|
|
|
if (!lightingSupported()) {
|
|
globalOutputStream() << "Lighting mode requires OpenGL features not supported by your graphics drivers:\n";
|
|
if (!GlobalOpenGL().GL_1_3()) {
|
|
globalOutputStream() << " GL version 1.3 or better\n";
|
|
}
|
|
if (!GlobalOpenGL().ARB_vertex_program()) {
|
|
globalOutputStream() << " GL_ARB_vertex_program\n";
|
|
}
|
|
if (!GlobalOpenGL().ARB_fragment_program()) {
|
|
globalOutputStream() << " GL_ARB_fragment_program\n";
|
|
}
|
|
if (!GlobalOpenGL().ARB_shader_objects()) {
|
|
globalOutputStream() << " GL_ARB_shader_objects\n";
|
|
}
|
|
if (!GlobalOpenGL().ARB_vertex_shader()) {
|
|
globalOutputStream() << " GL_ARB_vertex_shader\n";
|
|
}
|
|
if (!GlobalOpenGL().ARB_fragment_shader()) {
|
|
globalOutputStream() << " GL_ARB_fragment_shader\n";
|
|
}
|
|
if (!GlobalOpenGL().ARB_shading_language_100()) {
|
|
globalOutputStream() << " GL_ARB_shading_language_100\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
void setLightingEnabled(bool enabled)
|
|
{
|
|
setLighting(m_lightingSupported, enabled);
|
|
}
|
|
|
|
// light culling
|
|
|
|
RendererLights m_lights;
|
|
bool m_lightsChanged;
|
|
typedef std::map<LightCullable *, LinearLightList> LightLists;
|
|
LightLists m_lightLists;
|
|
|
|
const LightList &attach(LightCullable &cullable)
|
|
{
|
|
return (*m_lightLists.insert(LightLists::value_type(&cullable, LinearLightList(cullable, m_lights,
|
|
EvaluateChangedCaller(
|
|
*this)))).first).second;
|
|
}
|
|
|
|
void detach(LightCullable &cullable)
|
|
{
|
|
m_lightLists.erase(&cullable);
|
|
}
|
|
|
|
void changed(LightCullable &cullable)
|
|
{
|
|
LightLists::iterator i = m_lightLists.find(&cullable);
|
|
ASSERT_MESSAGE(i != m_lightLists.end(), "cullable not attached");
|
|
(*i).second.lightsChanged();
|
|
}
|
|
|
|
void attach(RendererLight &light)
|
|
{
|
|
ASSERT_MESSAGE(m_lights.find(&light) == m_lights.end(), "light could not be attached");
|
|
m_lights.insert(&light);
|
|
changed(light);
|
|
}
|
|
|
|
void detach(RendererLight &light)
|
|
{
|
|
ASSERT_MESSAGE(m_lights.find(&light) != m_lights.end(), "light could not be detached");
|
|
m_lights.erase(&light);
|
|
changed(light);
|
|
}
|
|
|
|
void changed(RendererLight &light)
|
|
{
|
|
m_lightsChanged = true;
|
|
}
|
|
|
|
void evaluateChanged()
|
|
{
|
|
if (m_lightsChanged) {
|
|
m_lightsChanged = false;
|
|
for (LightLists::iterator i = m_lightLists.begin(); i != m_lightLists.end(); ++i) {
|
|
(*i).second.lightsChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
typedef MemberCaller<OpenGLShaderCache, void (), &OpenGLShaderCache::evaluateChanged> EvaluateChangedCaller;
|
|
|
|
typedef std::set<const Renderable *> Renderables;
|
|
Renderables m_renderables;
|
|
mutable bool m_traverseRenderablesMutex;
|
|
|
|
// renderables
|
|
void attachRenderable(const Renderable &renderable)
|
|
{
|
|
ASSERT_MESSAGE(!m_traverseRenderablesMutex, "attaching renderable during traversal");
|
|
ASSERT_MESSAGE(m_renderables.find(&renderable) == m_renderables.end(), "renderable could not be attached");
|
|
m_renderables.insert(&renderable);
|
|
}
|
|
|
|
void detachRenderable(const Renderable &renderable)
|
|
{
|
|
ASSERT_MESSAGE(!m_traverseRenderablesMutex, "detaching renderable during traversal");
|
|
ASSERT_MESSAGE(m_renderables.find(&renderable) != m_renderables.end(), "renderable could not be detached");
|
|
m_renderables.erase(&renderable);
|
|
}
|
|
|
|
void forEachRenderable(const RenderableCallback &callback) const
|
|
{
|
|
ASSERT_MESSAGE(!m_traverseRenderablesMutex, "for-each during traversal");
|
|
m_traverseRenderablesMutex = true;
|
|
for (Renderables::const_iterator i = m_renderables.begin(); i != m_renderables.end(); ++i) {
|
|
callback(*(*i));
|
|
}
|
|
m_traverseRenderablesMutex = false;
|
|
}
|
|
};
|
|
|
|
static OpenGLShaderCache *g_ShaderCache;
|
|
|
|
void ShaderCache_extensionsInitialised()
|
|
{
|
|
g_ShaderCache->extensionsInitialised();
|
|
}
|
|
|
|
void ShaderCache_setBumpEnabled(bool enabled)
|
|
{
|
|
g_ShaderCache->setLightingEnabled(enabled);
|
|
}
|
|
|
|
|
|
Vector3 g_DebugShaderColours[256];
|
|
Shader *g_defaultPointLight = 0;
|
|
|
|
void ShaderCache_Construct()
|
|
{
|
|
g_ShaderCache = new OpenGLShaderCache;
|
|
GlobalTexturesCache().attach(*g_ShaderCache);
|
|
GlobalShaderSystem().attach(*g_ShaderCache);
|
|
}
|
|
|
|
void ShaderCache_Destroy()
|
|
{
|
|
GlobalShaderSystem().detach(*g_ShaderCache);
|
|
GlobalTexturesCache().detach(*g_ShaderCache);
|
|
delete g_ShaderCache;
|
|
}
|
|
|
|
ShaderCache *GetShaderCache()
|
|
{
|
|
return g_ShaderCache;
|
|
}
|
|
|
|
inline void setTextureState(GLint ¤t, const GLint &texture, GLenum textureUnit)
|
|
{
|
|
if (texture != current) {
|
|
glActiveTexture(textureUnit);
|
|
glClientActiveTexture(textureUnit);
|
|
glBindTexture(GL_TEXTURE_2D, texture);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
current = texture;
|
|
}
|
|
}
|
|
|
|
inline void setTextureState(GLint ¤t, const GLint &texture)
|
|
{
|
|
if (texture != current) {
|
|
glBindTexture(GL_TEXTURE_2D, texture);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
current = texture;
|
|
}
|
|
}
|
|
|
|
inline void setState(unsigned int state, unsigned int delta, unsigned int flag, GLenum glflag)
|
|
{
|
|
if (delta & state & flag) {
|
|
glEnable(glflag);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
} else if (delta & ~state & flag) {
|
|
glDisable(glflag);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
}
|
|
|
|
void OpenGLState_apply(const OpenGLState &self, OpenGLState ¤t, unsigned int globalstate)
|
|
{
|
|
debug_int("sort", int(self.m_sort));
|
|
debug_int("texture", self.m_texture);
|
|
debug_int("state", self.m_state);
|
|
debug_int("address", int(std::size_t(&self)));
|
|
|
|
count_state();
|
|
|
|
if (self.m_state & RENDER_OVERRIDE) {
|
|
globalstate |= RENDER_FILL | RENDER_DEPTHWRITE;
|
|
}
|
|
|
|
const unsigned int state = self.m_state & globalstate;
|
|
const unsigned int delta = state ^current.m_state;
|
|
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
|
|
GLProgram *program = (state & RENDER_PROGRAM) != 0 ? self.m_program : 0;
|
|
|
|
if (program != current.m_program) {
|
|
if (current.m_program != 0) {
|
|
current.m_program->disable();
|
|
glColor4fv(vector4_to_array(current.m_colour));
|
|
debug_colour("cleaning program");
|
|
}
|
|
|
|
current.m_program = program;
|
|
|
|
if (current.m_program != 0) {
|
|
current.m_program->enable();
|
|
}
|
|
}
|
|
|
|
if (delta & state & RENDER_FILL) {
|
|
//qglPolygonMode (GL_BACK, GL_LINE);
|
|
//qglPolygonMode (GL_FRONT, GL_FILL);
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
} else if (delta & ~state & RENDER_FILL) {
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
|
|
setState(state, delta, RENDER_OFFSETLINE, GL_POLYGON_OFFSET_LINE);
|
|
|
|
if (delta & state & RENDER_LIGHTING) {
|
|
glEnable(GL_LIGHTING);
|
|
glEnable(GL_COLOR_MATERIAL);
|
|
glEnable(GL_RESCALE_NORMAL);
|
|
glEnableClientState(GL_NORMAL_ARRAY);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
g_normalArray_enabled = true;
|
|
} else if (delta & ~state & RENDER_LIGHTING) {
|
|
glDisable(GL_LIGHTING);
|
|
glDisable(GL_COLOR_MATERIAL);
|
|
glDisable(GL_RESCALE_NORMAL);
|
|
glDisableClientState(GL_NORMAL_ARRAY);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
g_normalArray_enabled = false;
|
|
}
|
|
|
|
if (delta & state & RENDER_TEXTURE) {
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
|
|
if (GlobalOpenGL().GL_1_3()) {
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glClientActiveTexture(GL_TEXTURE0);
|
|
}
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
glColor4f(1, 1, 1, self.m_colour[3]);
|
|
debug_colour("setting texture");
|
|
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
g_texcoordArray_enabled = true;
|
|
} else if (delta & ~state & RENDER_TEXTURE) {
|
|
if (GlobalOpenGL().GL_1_3()) {
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glClientActiveTexture(GL_TEXTURE0);
|
|
}
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
g_texcoordArray_enabled = false;
|
|
}
|
|
|
|
if (delta & state & RENDER_BLEND) {
|
|
// FIXME: some .TGA are buggy, have a completely empty alpha channel
|
|
// if such brushes are rendered in this loop they would be totally transparent with GL_MODULATE
|
|
// so I decided using GL_DECAL instead
|
|
// if an empty-alpha-channel or nearly-empty texture is used. It will be blank-transparent.
|
|
// this could get better if you can get glTexEnviv (GL_TEXTURE_ENV, to work .. patches are welcome
|
|
glEnable(GL_BLEND);
|
|
if (GlobalOpenGL().GL_1_3()) {
|
|
glActiveTexture(GL_TEXTURE0);
|
|
}
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
} else if (delta & ~state & RENDER_BLEND) {
|
|
glDisable(GL_BLEND);
|
|
if (GlobalOpenGL().GL_1_3()) {
|
|
glActiveTexture(GL_TEXTURE0);
|
|
}
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
|
|
setState(state, delta, RENDER_CULLFACE, GL_CULL_FACE);
|
|
|
|
if (delta & state & RENDER_SMOOTH) {
|
|
glShadeModel(GL_SMOOTH);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
} else if (delta & ~state & RENDER_SMOOTH) {
|
|
glShadeModel(GL_FLAT);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
|
|
setState(state, delta, RENDER_SCALED, GL_RESCALE_NORMAL); // not GL_RESCALE_NORMAL
|
|
setState(state, delta, RENDER_DEPTHTEST, GL_DEPTH_TEST);
|
|
|
|
if (delta & state & RENDER_DEPTHWRITE) {
|
|
glDepthMask(GL_TRUE);
|
|
|
|
#if DEBUG_RENDER
|
|
GLboolean depthEnabled;
|
|
glGetBooleanv( GL_DEPTH_WRITEMASK, &depthEnabled );
|
|
ASSERT_MESSAGE( depthEnabled, "failed to set depth buffer mask bit" );
|
|
#endif
|
|
debug_string("enabled depth-buffer writing");
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
} else if (delta & ~state & RENDER_DEPTHWRITE) {
|
|
glDepthMask(GL_FALSE);
|
|
|
|
#if DEBUG_RENDER
|
|
GLboolean depthEnabled;
|
|
glGetBooleanv( GL_DEPTH_WRITEMASK, &depthEnabled );
|
|
ASSERT_MESSAGE( !depthEnabled, "failed to set depth buffer mask bit" );
|
|
#endif
|
|
debug_string("disabled depth-buffer writing");
|
|
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
|
|
if (delta & state & RENDER_COLOURWRITE) {
|
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
} else if (delta & ~state & RENDER_COLOURWRITE) {
|
|
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
|
|
setState(state, delta, RENDER_ALPHATEST, GL_ALPHA_TEST);
|
|
|
|
if (delta & state & RENDER_COLOURARRAY) {
|
|
glEnableClientState(GL_COLOR_ARRAY);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
debug_colour("enabling color_array");
|
|
g_colorArray_enabled = true;
|
|
} else if (delta & ~state & RENDER_COLOURARRAY) {
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
glColor4fv(vector4_to_array(self.m_colour));
|
|
debug_colour("cleaning color_array");
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
g_colorArray_enabled = false;
|
|
}
|
|
|
|
if (delta & ~state & RENDER_COLOURCHANGE) {
|
|
glColor4fv(vector4_to_array(self.m_colour));
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
|
|
setState(state, delta, RENDER_LINESTIPPLE, GL_LINE_STIPPLE);
|
|
setState(state, delta, RENDER_LINESMOOTH, GL_LINE_SMOOTH);
|
|
setState(state, delta, RENDER_POLYGONSTIPPLE, GL_POLYGON_STIPPLE);
|
|
setState(state, delta, RENDER_POLYGONSMOOTH, GL_POLYGON_SMOOTH);
|
|
setState(state, delta, RENDER_FOG, GL_FOG);
|
|
|
|
if ((state & RENDER_FOG) != 0) {
|
|
setFogState(self.m_fog);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
current.m_fog = self.m_fog;
|
|
}
|
|
|
|
if (state & RENDER_DEPTHTEST && self.m_depthfunc != current.m_depthfunc) {
|
|
glDepthFunc(self.m_depthfunc);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
current.m_depthfunc = self.m_depthfunc;
|
|
}
|
|
|
|
if (state & RENDER_POLYOFS) {
|
|
glEnable(GL_POLYGON_OFFSET_FILL);
|
|
glPolygonOffset(-0.05, -25 * self.m_polygonoffset);
|
|
} else {
|
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
|
}
|
|
|
|
if (state & RENDER_LINESTIPPLE
|
|
&& (self.m_linestipple_factor != current.m_linestipple_factor
|
|
|| self.m_linestipple_pattern != current.m_linestipple_pattern)) {
|
|
glLineStipple(self.m_linestipple_factor, self.m_linestipple_pattern);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
current.m_linestipple_factor = self.m_linestipple_factor;
|
|
current.m_linestipple_pattern = self.m_linestipple_pattern;
|
|
}
|
|
|
|
if (state & RENDER_ALPHATEST
|
|
&& (self.m_alphafunc != current.m_alphafunc
|
|
|| self.m_alpharef != current.m_alpharef)) {
|
|
glAlphaFunc(self.m_alphafunc, self.m_alpharef);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
current.m_alphafunc = self.m_alphafunc;
|
|
current.m_alpharef = self.m_alpharef;
|
|
}
|
|
|
|
GLint texture0 = 0;
|
|
GLint texture1 = 0;
|
|
GLint texture2 = 0;
|
|
GLint texture3 = 0;
|
|
GLint texture4 = 0;
|
|
GLint texture5 = 0;
|
|
GLint texture6 = 0;
|
|
GLint texture7 = 0;
|
|
|
|
texture0 = self.m_texture;
|
|
texture1 = self.m_texture1;
|
|
texture2 = self.m_texture2;
|
|
texture3 = self.m_texture3;
|
|
texture4 = self.m_texture4;
|
|
texture5 = self.m_texture5;
|
|
texture6 = self.m_texture6;
|
|
texture7 = self.m_texture7;
|
|
|
|
if (GlobalOpenGL().GL_1_3()) {
|
|
setTextureState(current.m_texture, texture0, GL_TEXTURE0);
|
|
setTextureState(current.m_texture1, texture1, GL_TEXTURE1);
|
|
setTextureState(current.m_texture2, texture2, GL_TEXTURE2);
|
|
setTextureState(current.m_texture3, texture3, GL_TEXTURE3);
|
|
setTextureState(current.m_texture4, texture4, GL_TEXTURE4);
|
|
setTextureState(current.m_texture5, texture5, GL_TEXTURE5);
|
|
setTextureState(current.m_texture6, texture6, GL_TEXTURE6);
|
|
setTextureState(current.m_texture7, texture7, GL_TEXTURE7);
|
|
} else {
|
|
setTextureState(current.m_texture, texture0);
|
|
}
|
|
|
|
if (state & RENDER_TEXTURE && self.m_colour[3] != current.m_colour[3]) {
|
|
debug_colour("setting alpha");
|
|
glColor4f(1, 1, 1, self.m_colour[3]);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
|
|
if (!(state & RENDER_TEXTURE)
|
|
&& (self.m_colour[0] != current.m_colour[0]
|
|
|| self.m_colour[1] != current.m_colour[1]
|
|
|| self.m_colour[2] != current.m_colour[2]
|
|
|| self.m_colour[3] != current.m_colour[3])) {
|
|
glColor4fv(vector4_to_array(self.m_colour));
|
|
debug_colour("setting non-texture");
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
current.m_colour = self.m_colour;
|
|
|
|
if (state & RENDER_BLEND
|
|
&& (self.m_blend_src != current.m_blend_src || self.m_blend_dst != current.m_blend_dst)) {
|
|
glBlendFunc(self.m_blend_src, self.m_blend_dst);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
current.m_blend_src = self.m_blend_src;
|
|
current.m_blend_dst = self.m_blend_dst;
|
|
}
|
|
|
|
if (!(state & RENDER_FILL)
|
|
&& self.m_linewidth != current.m_linewidth) {
|
|
glLineWidth(self.m_linewidth);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
current.m_linewidth = self.m_linewidth;
|
|
}
|
|
|
|
if (!(state & RENDER_FILL)
|
|
&& self.m_pointsize != current.m_pointsize) {
|
|
glPointSize(self.m_pointsize);
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
current.m_pointsize = self.m_pointsize;
|
|
}
|
|
|
|
current.m_state = state;
|
|
|
|
GlobalOpenGL_debugAssertNoErrors();
|
|
}
|
|
|
|
void Renderables_flush(OpenGLStateBucket::Renderables &renderables, OpenGLState ¤t, unsigned int globalstate,
|
|
const Vector3 &viewer)
|
|
{
|
|
const Matrix4 *transform = 0;
|
|
glPushMatrix();
|
|
for (OpenGLStateBucket::Renderables::const_iterator i = renderables.begin(); i != renderables.end(); ++i) {
|
|
//qglLoadMatrixf(i->m_transform);
|
|
if (!transform || (transform != (*i).m_transform && !matrix4_affine_equal(*transform, *(*i).m_transform))) {
|
|
count_transform();
|
|
transform = (*i).m_transform;
|
|
glPopMatrix();
|
|
glPushMatrix();
|
|
glMultMatrixf(reinterpret_cast<const float *>( transform ));
|
|
glFrontFace(
|
|
((current.m_state & RENDER_CULLFACE) != 0 && matrix4_handedness(*transform) == MATRIX4_RIGHTHANDED)
|
|
? GL_CW : GL_CCW);
|
|
}
|
|
|
|
count_prim();
|
|
|
|
if (current.m_program != 0 && (*i).m_light != 0) {
|
|
const IShader &lightShader = static_cast<OpenGLShader *>((*i).m_light->getShader())->getShader();
|
|
if (lightShader.firstLayer() != 0) {
|
|
GLuint attenuation_xy = lightShader.firstLayer()->texture()->texture_number;
|
|
GLuint attenuation_z = lightShader.lightFalloffImage() != 0
|
|
? lightShader.lightFalloffImage()->texture_number
|
|
: static_cast<OpenGLShader *>( g_defaultPointLight )->getShader().lightFalloffImage()->texture_number;
|
|
|
|
setTextureState(current.m_texture3, attenuation_xy, GL_TEXTURE3);
|
|
glActiveTexture(GL_TEXTURE3);
|
|
glBindTexture(GL_TEXTURE_2D, attenuation_xy);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
|
|
|
setTextureState(current.m_texture4, attenuation_z, GL_TEXTURE4);
|
|
glActiveTexture(GL_TEXTURE4);
|
|
glBindTexture(GL_TEXTURE_2D, attenuation_z);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
|
|
AABB lightBounds((*i).m_light->aabb());
|
|
|
|
Matrix4 world2light(g_matrix4_identity);
|
|
|
|
if ((*i).m_light->isProjected()) {
|
|
world2light = (*i).m_light->projection();
|
|
matrix4_multiply_by_matrix4(world2light, matrix4_transposed((*i).m_light->rotation()));
|
|
matrix4_translate_by_vec3(world2light, vector3_negated(lightBounds.origin)); // world->lightBounds
|
|
}
|
|
if (!(*i).m_light->isProjected()) {
|
|
matrix4_translate_by_vec3(world2light, Vector3(0.5f, 0.5f, 0.5f));
|
|
matrix4_scale_by_vec3(world2light, Vector3(0.5f, 0.5f, 0.5f));
|
|
matrix4_scale_by_vec3(world2light,
|
|
Vector3(1.0f / lightBounds.extents.x(), 1.0f / lightBounds.extents.y(),
|
|
1.0f / lightBounds.extents.z()));
|
|
matrix4_multiply_by_matrix4(world2light, matrix4_transposed((*i).m_light->rotation()));
|
|
matrix4_translate_by_vec3(world2light, vector3_negated(lightBounds.origin)); // world->lightBounds
|
|
}
|
|
|
|
current.m_program->setParameters(viewer, *(*i).m_transform, lightBounds.origin + (*i).m_light->offset(),
|
|
(*i).m_light->colour(), world2light);
|
|
debug_string("set lightBounds parameters");
|
|
}
|
|
}
|
|
|
|
(*i).m_renderable->render(current.m_state);
|
|
}
|
|
glPopMatrix();
|
|
renderables.clear();
|
|
}
|
|
|
|
void OpenGLStateBucket::render(OpenGLState ¤t, unsigned int globalstate, const Vector3 &viewer)
|
|
{
|
|
if ((globalstate & m_state.m_state & RENDER_SCREEN) != 0) {
|
|
OpenGLState_apply(m_state, current, globalstate);
|
|
debug_colour("screen fill");
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPushMatrix();
|
|
glLoadMatrixf(reinterpret_cast<const float *>( &g_matrix4_identity ));
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPushMatrix();
|
|
glLoadMatrixf(reinterpret_cast<const float *>( &g_matrix4_identity ));
|
|
|
|
glBegin(GL_QUADS);
|
|
glVertex3f(-1, -1, 0);
|
|
glVertex3f(1, -1, 0);
|
|
glVertex3f(1, 1, 0);
|
|
glVertex3f(-1, 1, 0);
|
|
glEnd();
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPopMatrix();
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPopMatrix();
|
|
} else if (!m_renderables.empty()) {
|
|
OpenGLState_apply(m_state, current, globalstate);
|
|
Renderables_flush(m_renderables, current, globalstate, viewer);
|
|
}
|
|
}
|
|
|
|
|
|
class OpenGLStateMap : public OpenGLStateLibrary {
|
|
typedef std::map<CopiedString, OpenGLState> States;
|
|
States m_states;
|
|
public:
|
|
~OpenGLStateMap()
|
|
{
|
|
ASSERT_MESSAGE(m_states.empty(), "OpenGLStateMap::~OpenGLStateMap: not empty");
|
|
}
|
|
|
|
typedef States::iterator iterator;
|
|
|
|
iterator begin()
|
|
{
|
|
return m_states.begin();
|
|
}
|
|
|
|
iterator end()
|
|
{
|
|
return m_states.end();
|
|
}
|
|
|
|
void getDefaultState(OpenGLState &state) const
|
|
{
|
|
OpenGLState_constructDefault(state);
|
|
}
|
|
|
|
void insert(const char *name, const OpenGLState &state)
|
|
{
|
|
bool inserted = m_states.insert(States::value_type(name, state)).second;
|
|
ASSERT_MESSAGE(inserted, "OpenGLStateMap::insert: " << name << " already exists");
|
|
}
|
|
|
|
void erase(const char *name)
|
|
{
|
|
std::size_t count = m_states.erase(name);
|
|
ASSERT_MESSAGE(count == 1, "OpenGLStateMap::erase: " << name << " does not exist");
|
|
}
|
|
|
|
iterator find(const char *name)
|
|
{
|
|
return m_states.find(name);
|
|
}
|
|
};
|
|
|
|
OpenGLStateMap *g_openglStates = 0;
|
|
|
|
inline GLenum convertBlendFactor(BlendFactor factor)
|
|
{
|
|
switch (factor) {
|
|
case BLEND_ZERO:
|
|
return GL_ZERO;
|
|
case BLEND_ONE:
|
|
return GL_ONE;
|
|
case BLEND_SRC_COLOUR:
|
|
return GL_SRC_COLOR;
|
|
case BLEND_ONE_MINUS_SRC_COLOUR:
|
|
return GL_ONE_MINUS_SRC_COLOR;
|
|
case BLEND_SRC_ALPHA:
|
|
return GL_SRC_ALPHA;
|
|
case BLEND_ONE_MINUS_SRC_ALPHA:
|
|
return GL_ONE_MINUS_SRC_ALPHA;
|
|
case BLEND_DST_COLOUR:
|
|
return GL_DST_COLOR;
|
|
case BLEND_ONE_MINUS_DST_COLOUR:
|
|
return GL_ONE_MINUS_DST_COLOR;
|
|
case BLEND_DST_ALPHA:
|
|
return GL_DST_ALPHA;
|
|
case BLEND_ONE_MINUS_DST_ALPHA:
|
|
return GL_ONE_MINUS_DST_ALPHA;
|
|
case BLEND_SRC_ALPHA_SATURATE:
|
|
return GL_SRC_ALPHA_SATURATE;
|
|
}
|
|
return GL_ZERO;
|
|
}
|
|
|
|
/// \todo Define special-case shaders in a data file.
|
|
void OpenGLShader::construct(const char *name)
|
|
{
|
|
OpenGLState &state = appendDefaultPass();
|
|
switch (name[0]) {
|
|
case '(':
|
|
sscanf(name, "(%g %g %g)", &state.m_colour[0], &state.m_colour[1], &state.m_colour[2]);
|
|
state.m_colour[3] = 1.0f;
|
|
state.m_state = RENDER_FILL | RENDER_LIGHTING | RENDER_DEPTHTEST | RENDER_CULLFACE | RENDER_COLOURWRITE |
|
|
RENDER_DEPTHWRITE;
|
|
state.m_sort = OpenGLState::eSortFullbright;
|
|
break;
|
|
|
|
case '[':
|
|
sscanf(name, "[%g %g %g]", &state.m_colour[0], &state.m_colour[1], &state.m_colour[2]);
|
|
state.m_colour[3] = 0.5f;
|
|
state.m_state = RENDER_FILL | RENDER_LIGHTING | RENDER_DEPTHTEST | RENDER_CULLFACE | RENDER_COLOURWRITE |
|
|
RENDER_DEPTHWRITE | RENDER_BLEND;
|
|
state.m_sort = OpenGLState::eSortTranslucent;
|
|
break;
|
|
|
|
case '<':
|
|
sscanf(name, "<%g %g %g>", &state.m_colour[0], &state.m_colour[1], &state.m_colour[2]);
|
|
state.m_colour[3] = 1;
|
|
state.m_state = RENDER_DEPTHTEST | RENDER_COLOURWRITE | RENDER_DEPTHWRITE;
|
|
state.m_sort = OpenGLState::eSortFullbright;
|
|
state.m_depthfunc = GL_LESS;
|
|
state.m_linewidth = 1;
|
|
state.m_pointsize = 1;
|
|
break;
|
|
|
|
case '$': {
|
|
OpenGLStateMap::iterator i = g_openglStates->find(name);
|
|
if (i != g_openglStates->end()) {
|
|
state = (*i).second;
|
|
break;
|
|
}
|
|
}
|
|
if (string_equal(name + 1, "POINT")) {
|
|
state.m_state = RENDER_COLOURARRAY | RENDER_COLOURWRITE | RENDER_DEPTHWRITE;
|
|
state.m_sort = OpenGLState::eSortControlFirst;
|
|
state.m_pointsize = 4;
|
|
} else if (string_equal(name + 1, "SELPOINT")) {
|
|
state.m_state = RENDER_COLOURARRAY | RENDER_COLOURWRITE | RENDER_DEPTHWRITE;
|
|
state.m_sort = OpenGLState::eSortControlFirst + 1;
|
|
state.m_pointsize = 4;
|
|
} else if (string_equal(name + 1, "BIGPOINT")) {
|
|
state.m_state = RENDER_COLOURARRAY | RENDER_COLOURWRITE | RENDER_DEPTHWRITE;
|
|
state.m_sort = OpenGLState::eSortControlFirst;
|
|
state.m_pointsize = 6;
|
|
} else if (string_equal(name + 1, "PIVOT")) {
|
|
state.m_state = RENDER_COLOURARRAY | RENDER_COLOURWRITE | RENDER_DEPTHTEST | RENDER_DEPTHWRITE;
|
|
state.m_sort = OpenGLState::eSortGUI1;
|
|
state.m_linewidth = 2;
|
|
state.m_depthfunc = GL_LEQUAL;
|
|
|
|
OpenGLState &hiddenLine = appendDefaultPass();
|
|
hiddenLine.m_state = RENDER_COLOURARRAY | RENDER_COLOURWRITE | RENDER_DEPTHTEST | RENDER_LINESTIPPLE;
|
|
hiddenLine.m_sort = OpenGLState::eSortGUI0;
|
|
hiddenLine.m_linewidth = 2;
|
|
hiddenLine.m_depthfunc = GL_GREATER;
|
|
} else if (string_equal(name + 1, "LATTICE")) {
|
|
state.m_colour[0] = 1;
|
|
state.m_colour[1] = 0.5;
|
|
state.m_colour[2] = 0;
|
|
state.m_colour[3] = 1;
|
|
state.m_state = RENDER_COLOURWRITE | RENDER_DEPTHWRITE;
|
|
state.m_sort = OpenGLState::eSortControlFirst;
|
|
} else if (string_equal(name + 1, "WIREFRAME")) {
|
|
state.m_state = RENDER_DEPTHTEST | RENDER_COLOURWRITE | RENDER_DEPTHWRITE;
|
|
state.m_sort = OpenGLState::eSortFullbright;
|
|
} else if (string_equal(name + 1, "CAM_HIGHLIGHT")) {
|
|
state.m_colour[0] = 1;
|
|
state.m_colour[1] = 0;
|
|
state.m_colour[2] = 0;
|
|
state.m_colour[3] = 0.5f;
|
|
state.m_state = RENDER_FILL | RENDER_DEPTHTEST | RENDER_CULLFACE | RENDER_BLEND | RENDER_COLOURWRITE |
|
|
RENDER_DEPTHWRITE;
|
|
state.m_sort = OpenGLState::eSortHighlight;
|
|
state.m_depthfunc = GL_LEQUAL;
|
|
} else if (string_equal(name + 1, "CAM_OVERLAY")) {
|
|
#if 0
|
|
state.m_state = RENDER_CULLFACE | RENDER_COLOURWRITE | RENDER_DEPTHWRITE;
|
|
state.m_sort = OpenGLState::eSortOverlayFirst;
|
|
#else
|
|
state.m_state =
|
|
RENDER_CULLFACE | RENDER_DEPTHTEST | RENDER_COLOURWRITE | RENDER_DEPTHWRITE | RENDER_OFFSETLINE;
|
|
state.m_sort = OpenGLState::eSortOverlayFirst + 1;
|
|
state.m_depthfunc = GL_LEQUAL;
|
|
|
|
OpenGLState &hiddenLine = appendDefaultPass();
|
|
hiddenLine.m_colour[0] = 0.75;
|
|
hiddenLine.m_colour[1] = 0.75;
|
|
hiddenLine.m_colour[2] = 0.75;
|
|
hiddenLine.m_colour[3] = 1;
|
|
hiddenLine.m_state = RENDER_CULLFACE | RENDER_DEPTHTEST | RENDER_COLOURWRITE | RENDER_OFFSETLINE |
|
|
RENDER_LINESTIPPLE;
|
|
hiddenLine.m_sort = OpenGLState::eSortOverlayFirst;
|
|
hiddenLine.m_depthfunc = GL_GREATER;
|
|
hiddenLine.m_linestipple_factor = 2;
|
|
#endif
|
|
} else if (string_equal(name + 1, "XY_OVERLAY")) {
|
|
state.m_colour[0] = g_xywindow_globals.color_selbrushes[0];
|
|
state.m_colour[1] = g_xywindow_globals.color_selbrushes[1];
|
|
state.m_colour[2] = g_xywindow_globals.color_selbrushes[2];
|
|
state.m_colour[3] = 1;
|
|
state.m_state = RENDER_COLOURWRITE | RENDER_LINESTIPPLE;
|
|
state.m_sort = OpenGLState::eSortOverlayFirst;
|
|
state.m_linewidth = 2;
|
|
state.m_linestipple_factor = 3;
|
|
} else if (string_equal(name + 1, "DEBUG_CLIPPED")) {
|
|
state.m_state = RENDER_COLOURARRAY | RENDER_COLOURWRITE | RENDER_DEPTHWRITE;
|
|
state.m_sort = OpenGLState::eSortLast;
|
|
} else if (string_equal(name + 1, "POINTFILE")) {
|
|
state.m_colour[0] = 1;
|
|
state.m_colour[1] = 0;
|
|
state.m_colour[2] = 0;
|
|
state.m_colour[3] = 1;
|
|
state.m_state = RENDER_DEPTHTEST | RENDER_COLOURWRITE | RENDER_DEPTHWRITE;
|
|
state.m_sort = OpenGLState::eSortFullbright;
|
|
state.m_linewidth = 4;
|
|
} else if (string_equal(name + 1, "LIGHT_SPHERE")) {
|
|
state.m_colour[0] = .15f * .95f;
|
|
state.m_colour[1] = .15f * .95f;
|
|
state.m_colour[2] = .15f * .95f;
|
|
state.m_colour[3] = 1;
|
|
state.m_state = RENDER_CULLFACE | RENDER_DEPTHTEST | RENDER_BLEND | RENDER_FILL | RENDER_COLOURWRITE |
|
|
RENDER_DEPTHWRITE;
|
|
state.m_blend_src = GL_ONE;
|
|
state.m_blend_dst = GL_ONE;
|
|
state.m_sort = OpenGLState::eSortTranslucent;
|
|
} else if (string_equal(name + 1, "Q3MAP2_LIGHT_SPHERE")) {
|
|
state.m_colour[0] = .05f;
|
|
state.m_colour[1] = .05f;
|
|
state.m_colour[2] = .05f;
|
|
state.m_colour[3] = 1;
|
|
state.m_state = RENDER_CULLFACE | RENDER_DEPTHTEST | RENDER_BLEND | RENDER_FILL;
|
|
state.m_blend_src = GL_ONE;
|
|
state.m_blend_dst = GL_ONE;
|
|
state.m_sort = OpenGLState::eSortTranslucent;
|
|
} else if (string_equal(name + 1, "WIRE_OVERLAY")) {
|
|
#if 0
|
|
state.m_state = RENDER_COLOURARRAY | RENDER_COLOURWRITE | RENDER_DEPTHWRITE | RENDER_DEPTHTEST | RENDER_OVERRIDE;
|
|
state.m_sort = OpenGLState::eSortOverlayFirst;
|
|
#else
|
|
state.m_state = RENDER_COLOURARRAY | RENDER_COLOURWRITE | RENDER_DEPTHWRITE | RENDER_DEPTHTEST |
|
|
RENDER_OVERRIDE;
|
|
state.m_sort = OpenGLState::eSortGUI1;
|
|
state.m_depthfunc = GL_LEQUAL;
|
|
|
|
OpenGLState &hiddenLine = appendDefaultPass();
|
|
hiddenLine.m_state = RENDER_COLOURARRAY | RENDER_COLOURWRITE | RENDER_DEPTHWRITE | RENDER_DEPTHTEST |
|
|
RENDER_OVERRIDE | RENDER_LINESTIPPLE;
|
|
hiddenLine.m_sort = OpenGLState::eSortGUI0;
|
|
hiddenLine.m_depthfunc = GL_GREATER;
|
|
#endif
|
|
} else if (string_equal(name + 1, "FLATSHADE_OVERLAY")) {
|
|
state.m_state = RENDER_CULLFACE | RENDER_LIGHTING | RENDER_SMOOTH | RENDER_SCALED | RENDER_COLOURARRAY |
|
|
RENDER_FILL | RENDER_COLOURWRITE | RENDER_DEPTHWRITE | RENDER_DEPTHTEST |
|
|
RENDER_OVERRIDE;
|
|
state.m_sort = OpenGLState::eSortGUI1;
|
|
state.m_depthfunc = GL_LEQUAL;
|
|
|
|
OpenGLState &hiddenLine = appendDefaultPass();
|
|
hiddenLine.m_state =
|
|
RENDER_CULLFACE | RENDER_LIGHTING | RENDER_SMOOTH | RENDER_SCALED | RENDER_COLOURARRAY |
|
|
RENDER_FILL | RENDER_COLOURWRITE | RENDER_DEPTHWRITE | RENDER_DEPTHTEST | RENDER_OVERRIDE |
|
|
RENDER_POLYGONSTIPPLE;
|
|
hiddenLine.m_sort = OpenGLState::eSortGUI0;
|
|
hiddenLine.m_depthfunc = GL_GREATER;
|
|
} else if (string_equal(name + 1, "CLIPPER_OVERLAY")) {
|
|
state.m_colour[0] = g_xywindow_globals.color_clipper[0];
|
|
state.m_colour[1] = g_xywindow_globals.color_clipper[1];
|
|
state.m_colour[2] = g_xywindow_globals.color_clipper[2];
|
|
state.m_colour[3] = 1;
|
|
state.m_state =
|
|
RENDER_CULLFACE | RENDER_COLOURWRITE | RENDER_DEPTHWRITE | RENDER_FILL | RENDER_POLYGONSTIPPLE;
|
|
state.m_sort = OpenGLState::eSortOverlayFirst;
|
|
} else if (string_equal(name + 1, "OVERBRIGHT")) {
|
|
const float lightScale = 2;
|
|
state.m_colour[0] = lightScale * 0.5f;
|
|
state.m_colour[1] = lightScale * 0.5f;
|
|
state.m_colour[2] = lightScale * 0.5f;
|
|
state.m_colour[3] = 0.5;
|
|
state.m_state = RENDER_FILL | RENDER_BLEND | RENDER_COLOURWRITE | RENDER_SCREEN;
|
|
state.m_sort = OpenGLState::eSortOverbrighten;
|
|
state.m_blend_src = GL_DST_COLOR;
|
|
state.m_blend_dst = GL_SRC_COLOR;
|
|
} else {
|
|
// default to something recognisable.. =)
|
|
ERROR_MESSAGE("hardcoded renderstate not found");
|
|
state.m_colour[0] = 1;
|
|
state.m_colour[1] = 0;
|
|
state.m_colour[2] = 1;
|
|
state.m_colour[3] = 1;
|
|
state.m_state = RENDER_COLOURWRITE | RENDER_DEPTHWRITE;
|
|
state.m_sort = OpenGLState::eSortFirst;
|
|
}
|
|
break;
|
|
default:
|
|
// construction from IShader
|
|
m_shader = QERApp_Shader_ForName(name);
|
|
|
|
if (g_ShaderCache->lightingSupported() && g_ShaderCache->lightingEnabled() && m_shader->getBump() != 0 &&
|
|
m_shader->getBump()->texture_number != 0) { // is a bump shader
|
|
state.m_state = RENDER_FILL | RENDER_CULLFACE | RENDER_TEXTURE | RENDER_DEPTHTEST | RENDER_DEPTHWRITE |
|
|
RENDER_COLOURWRITE | RENDER_PROGRAM;
|
|
state.m_colour[0] = 0;
|
|
state.m_colour[1] = 0;
|
|
state.m_colour[2] = 0;
|
|
state.m_colour[3] = 1;
|
|
state.m_sort = OpenGLState::eSortOpaque;
|
|
|
|
if (g_ShaderCache->useShaderLanguage()) {
|
|
state.m_program = &g_depthFillGLSL;
|
|
} else {
|
|
state.m_program = &g_depthFillARB;
|
|
}
|
|
|
|
OpenGLState &bumpPass = appendDefaultPass();
|
|
bumpPass.m_texture = m_shader->getDiffuse()->texture_number;
|
|
bumpPass.m_texture1 = m_shader->getBump()->texture_number;
|
|
bumpPass.m_texture2 = m_shader->getSpecular()->texture_number;
|
|
|
|
bumpPass.m_state =
|
|
RENDER_BLEND | RENDER_FILL | RENDER_CULLFACE | RENDER_DEPTHTEST | RENDER_COLOURWRITE |
|
|
RENDER_SMOOTH | RENDER_BUMP | RENDER_PROGRAM;
|
|
|
|
if (g_ShaderCache->useShaderLanguage()) {
|
|
bumpPass.m_state |= RENDER_LIGHTING;
|
|
bumpPass.m_program = &g_bumpGLSL;
|
|
} else {
|
|
bumpPass.m_program = &g_bumpARB;
|
|
}
|
|
|
|
bumpPass.m_depthfunc = GL_LEQUAL;
|
|
bumpPass.m_sort = OpenGLState::eSortMultiFirst;
|
|
bumpPass.m_blend_src = GL_ONE;
|
|
bumpPass.m_blend_dst = GL_ONE;
|
|
} else {
|
|
state.m_texture = m_shader->getTexture()->texture_number;
|
|
|
|
state.m_state = RENDER_FILL | RENDER_TEXTURE | RENDER_DEPTHTEST | RENDER_COLOURWRITE | RENDER_LIGHTING |
|
|
RENDER_SMOOTH;
|
|
if ((m_shader->getFlags() & QER_CULL) != 0) {
|
|
if (m_shader->getCull() == IShader::eCullBack) {
|
|
state.m_state |= RENDER_CULLFACE;
|
|
}
|
|
} else {
|
|
state.m_state |= RENDER_CULLFACE;
|
|
}
|
|
|
|
if ((m_shader->getFlags() & QER_POLYOFS) != 0) {
|
|
state.m_polygonoffset = m_shader->getPolygonOffset();
|
|
state.m_state |= RENDER_POLYOFS;
|
|
}
|
|
|
|
if ((m_shader->getFlags() & QER_ALPHATEST) != 0) {
|
|
state.m_state |= RENDER_ALPHATEST;
|
|
IShader::EAlphaFunc alphafunc;
|
|
m_shader->getAlphaFunc(&alphafunc, &state.m_alpharef);
|
|
switch (alphafunc) {
|
|
case IShader::eAlways:
|
|
state.m_alphafunc = GL_ALWAYS;
|
|
break;
|
|
case IShader::eEqual:
|
|
state.m_alphafunc = GL_EQUAL;
|
|
break;
|
|
case IShader::eLess:
|
|
state.m_alphafunc = GL_LESS;
|
|
break;
|
|
case IShader::eGreater:
|
|
state.m_alphafunc = GL_GREATER;
|
|
break;
|
|
case IShader::eLEqual:
|
|
state.m_alphafunc = GL_LEQUAL;
|
|
break;
|
|
case IShader::eGEqual:
|
|
state.m_alphafunc = GL_GEQUAL;
|
|
break;
|
|
}
|
|
}
|
|
reinterpret_cast<Vector3 &>( state.m_colour ) = m_shader->getTexture()->color;
|
|
state.m_colour[3] = 1.0f;
|
|
|
|
if ((m_shader->getFlags() & QER_TRANS) != 0) {
|
|
state.m_state |= RENDER_BLEND;
|
|
state.m_colour[3] = m_shader->getTrans();
|
|
state.m_sort = OpenGLState::eSortTranslucent;
|
|
BlendFunc blendFunc = m_shader->getBlendFunc();
|
|
state.m_blend_src = convertBlendFactor(blendFunc.m_src);
|
|
state.m_blend_dst = convertBlendFactor(blendFunc.m_dst);
|
|
if (state.m_blend_src == GL_SRC_ALPHA || state.m_blend_dst == GL_SRC_ALPHA) {
|
|
state.m_state |= RENDER_DEPTHWRITE;
|
|
}
|
|
} else {
|
|
state.m_state |= RENDER_DEPTHWRITE;
|
|
state.m_sort = OpenGLState::eSortFullbright;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#include "modulesystem/singletonmodule.h"
|
|
#include "modulesystem/moduleregistry.h"
|
|
|
|
class OpenGLStateLibraryAPI {
|
|
OpenGLStateMap m_stateMap;
|
|
public:
|
|
typedef OpenGLStateLibrary Type;
|
|
|
|
STRING_CONSTANT(Name, "*");
|
|
|
|
OpenGLStateLibraryAPI()
|
|
{
|
|
g_openglStates = &m_stateMap;
|
|
}
|
|
|
|
~OpenGLStateLibraryAPI()
|
|
{
|
|
g_openglStates = 0;
|
|
}
|
|
|
|
OpenGLStateLibrary *getTable()
|
|
{
|
|
return &m_stateMap;
|
|
}
|
|
};
|
|
|
|
typedef SingletonModule<OpenGLStateLibraryAPI> OpenGLStateLibraryModule;
|
|
typedef Static<OpenGLStateLibraryModule> StaticOpenGLStateLibraryModule;
|
|
StaticRegisterModule staticRegisterOpenGLStateLibrary(StaticOpenGLStateLibraryModule::instance());
|
|
|
|
class ShaderCacheDependencies
|
|
: public GlobalShadersModuleRef, public GlobalTexturesModuleRef, public GlobalOpenGLStateLibraryModuleRef {
|
|
public:
|
|
ShaderCacheDependencies() :
|
|
GlobalShadersModuleRef(GlobalRadiant().getRequiredGameDescriptionKeyValue("shaders"))
|
|
{
|
|
}
|
|
};
|
|
|
|
class ShaderCacheAPI {
|
|
ShaderCache *m_shaderCache;
|
|
public:
|
|
typedef ShaderCache Type;
|
|
|
|
STRING_CONSTANT(Name, "*");
|
|
|
|
ShaderCacheAPI()
|
|
{
|
|
ShaderCache_Construct();
|
|
|
|
m_shaderCache = GetShaderCache();
|
|
}
|
|
|
|
~ShaderCacheAPI()
|
|
{
|
|
ShaderCache_Destroy();
|
|
}
|
|
|
|
ShaderCache *getTable()
|
|
{
|
|
return m_shaderCache;
|
|
}
|
|
};
|
|
|
|
typedef SingletonModule<ShaderCacheAPI, ShaderCacheDependencies> ShaderCacheModule;
|
|
typedef Static<ShaderCacheModule> StaticShaderCacheModule;
|
|
StaticRegisterModule staticRegisterShaderCache(StaticShaderCacheModule::instance());
|