# Initial GLSL rendering support

This was SVN commit r10978.
This commit is contained in:
Ykkrosh 2012-01-29 01:22:22 +00:00
parent 8ca59d418d
commit adbd7633dd
21 changed files with 782 additions and 104 deletions

View File

@ -52,6 +52,9 @@ force_s3tc_enable = true
; but will reduce performance and features when a modern graphics card is available.
renderpath = default
; Prefer GLSL shaders over ARB shaders (not recommended)
preferglsl = false
; Adjusts how OpenGL calculates mipmap level of detail. 0.0f is the default (blurry) value.
; Lower values sharpen/extend, and higher values blur/decrease. Clamped at -3.0 to 3.0.
; -1.0 to -1.5 recommended for good results.

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<effect>
<technique>
<require shaders="arb"/>
<pass shader="model_common_arb"/>
</technique>
<technique>
<require shaders="glsl"/>
<pass shader="model_common_glsl"/>
</technique>
</effect>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<effect>
<technique>
<require shaders="arb"/>
<pass shader="model_common_instancing_arb"/>
</technique>
<technique>
<require shaders="glsl"/>
<pass shader="model_common_instancing_glsl"/>
</technique>
</effect>

View File

@ -0,0 +1,54 @@
uniform sampler2D baseTex;
uniform sampler2DShadow shadowTex;
uniform sampler2D losTex;
uniform vec3 objectColor;
uniform vec3 shadingColor;
uniform vec3 ambient;
uniform vec4 shadowOffsets1;
uniform vec4 shadowOffsets2;
varying vec3 v_lighting;
varying vec2 v_tex;
varying vec4 v_shadow;
varying vec2 v_los;
void main()
{
vec4 tex = texture2D(baseTex, v_tex);
#ifdef USE_TRANSPARENT
gl_FragColor.a = tex.a;
#else
gl_FragColor.a = 1.0;
#endif
vec3 color = tex.rgb;
// Apply player-coloring based on texture alpha
#ifdef USE_OBJECTCOLOR
color *= mix(objectColor, 1.0, tex.a);
#endif
#ifdef USE_SHADOW
#ifdef USE_SHADOW_PCF
float shadow = (
shadow2D(shadowTex, vec3(v_shadow.xy + shadowOffsets1.xy, v_shadow.z)).a +
shadow2D(shadowTex, vec3(v_shadow.xy + shadowOffsets1.zw, v_shadow.z)).a +
shadow2D(shadowTex, vec3(v_shadow.xy + shadowOffsets2.xy, v_shadow.z)).a +
shadow2D(shadowTex, vec3(v_shadow.xy + shadowOffsets2.zw, v_shadow.z)).a
) * 0.25;
#else
float shadow = shadow2D(shadowTex, v_shadow).a;
#endif
#else
float shadow = 1.0;
#endif
color *= v_lighting * shadow + ambient;
color *= texture2D(losTex, v_los).a;
color *= shadingColor;
gl_FragColor.rgb = color;
}

View File

@ -0,0 +1,28 @@
uniform vec3 sunDir;
uniform vec3 sunColor;
uniform vec2 losTransform;
uniform mat4 shadowTransform;
uniform mat4 instancingTransform;
varying vec3 v_lighting;
varying vec2 v_tex;
varying vec4 v_shadow;
varying vec2 v_los;
void main()
{
#ifdef USE_INSTANCING
vec4 position = instancingTransform * gl_Vertex;
vec3 normal = instancingTransform * vec4(gl_Normal, 0.0);
#else
vec4 position = gl_Vertex;
vec3 normal = gl_Normal;
#endif
gl_Position = gl_ModelViewProjectionMatrix * position;
v_lighting = max(0.0, dot(normal, -sunDir)) * sunColor;
v_tex = gl_MultiTexCoord0.xy;
v_shadow = shadowTransform * position;
v_los = position.xz * losTransform.x + losTransform.y;
}

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<program type="glsl">
<vertex file="model_common.vs">
<uniform name="sunDir" type="vec3"/>
<uniform name="sunColor" type="vec3"/>
<uniform name="losTransform" type="vec2"/>
<uniform name="shadowTransform" type="mat4"/>
<stream name="pos"/>
<stream name="normal"/>
<stream name="uv0"/>
</vertex>
<fragment file="model_common.fs">
<uniform name="baseTex" type="sampler2D"/>
<uniform name="shadowTex" type="sampler2DShadow"/>
<uniform name="losTex" type="sampler2D"/>
<uniform name="objectColor" type="vec3"/>
<uniform name="shadingColor" type="vec3"/>
<uniform name="ambient" type="vec3"/>
<uniform name="shadowOffsets1" type="vec4"/>
<uniform name="shadowOffsets2" type="vec4"/>
</fragment>
</program>

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<program type="glsl">
<define name="USE_INSTANCING" value="1"/>
<vertex file="model_common.vs">
<uniform name="sunDir" type="vec3"/>
<uniform name="sunColor" type="vec3"/>
<uniform name="losTransform" type="vec2"/>
<uniform name="shadowTransform" type="mat4"/>
<uniform name="instancingTransform" type="mat4"/>
<stream name="pos"/>
<stream name="normal"/>
<stream name="uv0"/>
</vertex>
<fragment file="model_common.fs">
<uniform name="baseTex" type="sampler2D"/>
<uniform name="shadowTex" type="sampler2DShadow"/>
<uniform name="losTex" type="sampler2D"/>
<uniform name="objectColor" type="vec3"/>
<uniform name="shadingColor" type="vec3"/>
<uniform name="ambient" type="vec3"/>
<uniform name="shadowOffsets1" type="vec4"/>
<uniform name="shadowOffsets2" type="vec4"/>
</fragment>
</program>

View File

@ -2,13 +2,6 @@
<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<start>
<element name="program">
<attribute name="type">
<choice>
<value>arb</value>
<value>glsl</value>
</choice>
</attribute>
<zeroOrMore>
<element name="define">
<attribute name="name"><text/></attribute>
@ -16,43 +9,67 @@
</element>
</zeroOrMore>
<element name="vertex">
<attribute name="file"><text/></attribute>
<zeroOrMore>
<choice>
<ref name="uniformContent"/>
<element name="attrib">
<attribute name="name"><text/></attribute>
<attribute name="loc"><data type="integer"/></attribute>
</element>
<element name="stream">
<attribute name="name">
<choice>
<value>pos</value>
<value>normal</value>
<value>color</value>
<value>uv0</value>
<value>uv1</value>
<value>uv2</value>
<value>uv3</value>
</choice>
</attribute>
</element>
</choice>
</zeroOrMore>
</element>
<choice>
<group>
<attribute name="type">
<value>arb</value>
</attribute>
<element name="fragment">
<attribute name="file"><text/></attribute>
<zeroOrMore>
<ref name="uniformContent"/>
</zeroOrMore>
</element>
<element name="vertex">
<attribute name="file"><text/></attribute>
<zeroOrMore>
<choice>
<ref name="uniformContentARB"/>
<element name="attrib">
<attribute name="name"><text/></attribute>
<attribute name="loc"><data type="integer"/></attribute>
</element>
<ref name="streamContent"/>
</choice>
</zeroOrMore>
</element>
<element name="fragment">
<attribute name="file"><text/></attribute>
<zeroOrMore>
<ref name="uniformContentARB"/>
</zeroOrMore>
</element>
</group>
<group>
<attribute name="type">
<value>glsl</value>
</attribute>
<element name="vertex">
<attribute name="file"><text/></attribute>
<zeroOrMore>
<choice>
<ref name="uniformContentGLSL"/>
<element name="attrib">
<attribute name="name"><text/></attribute>
<attribute name="loc"><data type="integer"/></attribute>
</element>
<ref name="streamContent"/>
</choice>
</zeroOrMore>
</element>
<element name="fragment">
<attribute name="file"><text/></attribute>
<zeroOrMore>
<ref name="uniformContentGLSL"/>
</zeroOrMore>
</element>
</group>
</choice>
</element>
</start>
<define name="uniformContent">
<define name="uniformContentARB">
<element name="uniform">
<attribute name="name"><text/></attribute>
<attribute name="loc"><data type="integer"/></attribute>
@ -73,4 +90,40 @@
</element>
</define>
<define name="uniformContentGLSL">
<element name="uniform">
<attribute name="name"><text/></attribute>
<attribute name="type">
<choice>
<value>float</value>
<value>vec2</value>
<value>vec3</value>
<value>vec4</value>
<value>mat2</value>
<value>mat3</value>
<value>mat4</value>
<value>sampler2D</value>
<value>sampler2DShadow</value>
<value>samplerCube</value>
</choice>
</attribute>
</element>
</define>
<define name="streamContent">
<element name="stream">
<attribute name="name">
<choice>
<value>pos</value>
<value>normal</value>
<value>color</value>
<value>uv0</value>
<value>uv1</value>
<value>uv2</value>
<value>uv3</value>
</choice>
</attribute>
</element>
</define>
</grammar>

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2011 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -19,6 +19,7 @@
#include "ShaderManager.h"
#include "graphics/ShaderTechnique.h"
#include "lib/timer.h"
#include "lib/utf8.h"
#include "ps/CLogger.h"
@ -30,6 +31,14 @@
TIMER_ADD_CLIENT(tc_ShaderValidation);
struct revcompare2nd
{
template<typename S, typename T> bool operator()(const std::pair<S, T>& a, const std::pair<S, T>& b) const
{
return b.second < a.second;
}
};
CShaderManager::CShaderManager()
{
#if USE_SHADER_XML_VALIDATION
@ -73,6 +82,21 @@ CShaderProgramPtr CShaderManager::LoadProgram(const char* name, const std::map<C
return program;
}
static GLenum GetGLSLType(const CStr type)
{
if (type == "float") return GL_FLOAT;
if (type == "vec2") return GL_FLOAT_VEC2;
if (type == "vec3") return GL_FLOAT_VEC3;
if (type == "vec4") return GL_FLOAT_VEC4;
if (type == "mat2") return GL_FLOAT_MAT2;
if (type == "mat3") return GL_FLOAT_MAT3;
if (type == "mat4") return GL_FLOAT_MAT4;
if (type == "sampler2D") return GL_SAMPLER_2D;
if (type == "sampler2DShadow") return GL_SAMPLER_2D;
if (type == "samplerCube") return GL_SAMPLER_CUBE;
return 0;
}
bool CShaderManager::NewProgram(const char* name, const std::map<CStr, CStr>& baseDefines, CShaderProgramPtr& program)
{
PROFILE2("loading shader");
@ -131,8 +155,9 @@ bool CShaderManager::NewProgram(const char* name, const std::map<CStr, CStr>& ba
VfsPath vertexFile;
VfsPath fragmentFile;
std::map<CStr, CStr> defines = baseDefines;
std::map<CStr, int> vertexUniforms;
std::map<CStr, int> fragmentUniforms;
std::map<CStr, int> arbVertexUniforms;
std::map<CStr, int> arbFragmentUniforms;
std::map<CStr, GLenum> glslUniforms;
int streamFlags = 0;
XERO_ITER_EL(Root, Child)
@ -149,7 +174,10 @@ bool CShaderManager::NewProgram(const char* name, const std::map<CStr, CStr>& ba
{
if (Param.GetNodeName() == el_uniform)
{
vertexUniforms[Param.GetAttributes().GetNamedItem(at_name)] = Param.GetAttributes().GetNamedItem(at_loc).ToInt();
if (isGLSL)
glslUniforms[Param.GetAttributes().GetNamedItem(at_name)] = GetGLSLType(Param.GetAttributes().GetNamedItem(at_type));
else
arbVertexUniforms[Param.GetAttributes().GetNamedItem(at_name)] = Param.GetAttributes().GetNamedItem(at_loc).ToInt();
}
else if (Param.GetNodeName() == el_stream)
{
@ -182,15 +210,21 @@ bool CShaderManager::NewProgram(const char* name, const std::map<CStr, CStr>& ba
XERO_ITER_EL(Child, Param)
{
if (Param.GetNodeName() == el_uniform)
fragmentUniforms[Param.GetAttributes().GetNamedItem(at_name)] = Param.GetAttributes().GetNamedItem(at_loc).ToInt();
{
if (isGLSL)
glslUniforms[Param.GetAttributes().GetNamedItem(at_name)] = GetGLSLType(Param.GetAttributes().GetNamedItem(at_type));
else
arbFragmentUniforms[Param.GetAttributes().GetNamedItem(at_name)] = Param.GetAttributes().GetNamedItem(at_loc).ToInt();
}
}
}
}
// TODO: add GLSL support
ENSURE(!isGLSL);
if (isGLSL)
program = CShaderProgramPtr(CShaderProgram::ConstructGLSL(vertexFile, fragmentFile, defines, glslUniforms, streamFlags));
else
program = CShaderProgramPtr(CShaderProgram::ConstructARB(vertexFile, fragmentFile, defines, arbVertexUniforms, arbFragmentUniforms, streamFlags));
program = CShaderProgramPtr(CShaderProgram::ConstructARB(vertexFile, fragmentFile, defines, vertexUniforms, fragmentUniforms, streamFlags));
program->Reload();
// m_HotloadFiles[xmlFilename].insert(program); // TODO: should reload somehow when the XML changes
@ -200,6 +234,88 @@ bool CShaderManager::NewProgram(const char* name, const std::map<CStr, CStr>& ba
return true;
}
CShaderTechnique CShaderManager::LoadEffect(const char* name, const std::map<CStr, CStr>& baseDefines)
{
PROFILE2("loading effect");
PROFILE2_ATTR("name: %s", name);
VfsPath xmlFilename = L"shaders/effects/" + wstring_from_utf8(name) + L".xml";
CXeromyces XeroFile;
PSRETURN ret = XeroFile.Load(g_VFS, xmlFilename);
if (ret != PSRETURN_OK)
return CShaderTechnique();
// Define all the elements and attributes used in the XML file
#define EL(x) int el_##x = XeroFile.GetElementID(#x)
#define AT(x) int at_##x = XeroFile.GetAttributeID(#x)
EL(pass);
EL(require);
AT(shaders);
AT(shader);
#undef AT
#undef EL
XMBElement Root = XeroFile.GetRoot();
// Find all the techniques that we can use, and their preference
std::vector<std::pair<XMBElement, int> > usableTechs;
XERO_ITER_EL(Root, Technique)
{
int preference = 0;
bool isUsable = true;
XERO_ITER_EL(Technique, Child)
{
if (Child.GetNodeName() == el_require)
{
if (Child.GetAttributes().GetNamedItem(at_shaders) == "arb")
{
if (!g_Renderer.GetCapabilities().m_ARBProgram)
isUsable = false;
}
else if (Child.GetAttributes().GetNamedItem(at_shaders) == "glsl")
{
if (!g_Renderer.GetCapabilities().m_VertexShader || !g_Renderer.GetCapabilities().m_FragmentShader)
isUsable = false;
if (g_Renderer.m_Options.m_PreferGLSL)
preference += 100;
else
preference -= 100;
}
}
}
if (isUsable)
usableTechs.push_back(std::make_pair(Technique, preference));
}
if (usableTechs.empty())
{
debug_warn(L"Can't find a usable technique");
return CShaderTechnique();
}
// Sort by preference, tie-break on order of specification
std::stable_sort(usableTechs.begin(), usableTechs.end(), revcompare2nd());
CShaderTechnique tech;
XERO_ITER_EL(usableTechs[0].first, Child)
{
if (Child.GetNodeName() == el_pass)
{
CShaderProgramPtr shader = LoadProgram(Child.GetAttributes().GetNamedItem(at_shader).c_str(), baseDefines);
CShaderPass pass(shader);
tech.AddPass(pass);
}
}
return tech;
}
/*static*/ Status CShaderManager::ReloadChangedFileCB(void* param, const VfsPath& path)
{
return static_cast<CShaderManager*>(param)->ReloadChangedFile(path);

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2011 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -31,6 +31,8 @@
#include <set>
class CShaderTechnique;
/**
* Shader manager: loads and caches shader programs.
*/
@ -48,6 +50,15 @@ public:
*/
CShaderProgramPtr LoadProgram(const char* name, const std::map<CStr, CStr>& defines);
/**
* Load a shader effect.
* Effects can be implemented via many techniques; this returns the best usable technique.
* @param name name of effect XML specification (file is loaded from shaders/effects/${name}.xml)
* @param defines key/value set of preprocessor definitions
* @return loaded technique, or empty technique on error
*/
CShaderTechnique LoadEffect(const char* name, const std::map<CStr, CStr>& defines);
private:
bool NewProgram(const char* name, const std::map<CStr, CStr>& defines, CShaderProgramPtr& program);

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2011 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -51,26 +51,6 @@ public:
pglDeleteProgramsARB(1, &m_FragmentProgram);
}
CStr Preprocess(CPreprocessor& preprocessor, const CStr& input)
{
size_t len = 0;
char* output = preprocessor.Parse(input.c_str(), input.size(), len);
if (!output)
{
LOGERROR(L"Shader preprocessing failed");
return "";
}
CStr ret(output, len);
// Free output if it's not inside the source string
if (!(output >= input.c_str() && output < input.c_str() + input.size()))
free(output);
return ret;
}
bool Compile(GLuint target, const char* targetName, GLuint program, const VfsPath& file, const CStr& code)
{
ogl_WarnIfError();
@ -199,29 +179,29 @@ public:
virtual void Uniform(Binding id, float v0, float v1, float v2, float v3)
{
if (id.vertex != -1)
pglProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.vertex, v0, v1, v2, v3);
if (id.first != -1)
pglProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.first, v0, v1, v2, v3);
if (id.fragment != -1)
pglProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.fragment, v0, v1, v2, v3);
if (id.second != -1)
pglProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.second, v0, v1, v2, v3);
}
virtual void Uniform(Binding id, const CMatrix3D& v)
{
if (id.vertex != -1)
if (id.first != -1)
{
pglProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.vertex+0, v._11, v._12, v._13, v._14);
pglProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.vertex+1, v._21, v._22, v._23, v._24);
pglProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.vertex+2, v._31, v._32, v._33, v._34);
pglProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.vertex+3, v._41, v._42, v._43, v._44);
pglProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.first+0, v._11, v._12, v._13, v._14);
pglProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.first+1, v._21, v._22, v._23, v._24);
pglProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.first+2, v._31, v._32, v._33, v._34);
pglProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, (GLuint)id.first+3, v._41, v._42, v._43, v._44);
}
if (id.fragment != -1)
if (id.second != -1)
{
pglProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.fragment+0, v._11, v._12, v._13, v._14);
pglProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.fragment+1, v._21, v._22, v._23, v._24);
pglProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.fragment+2, v._31, v._32, v._33, v._34);
pglProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.fragment+3, v._41, v._42, v._43, v._44);
pglProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.second+0, v._11, v._12, v._13, v._14);
pglProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.second+1, v._21, v._22, v._23, v._24);
pglProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.second+2, v._31, v._32, v._33, v._34);
pglProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, (GLuint)id.second+3, v._41, v._42, v._43, v._44);
}
}
@ -238,6 +218,273 @@ private:
};
class CShaderProgramGLSL : public CShaderProgram
{
public:
CShaderProgramGLSL(const VfsPath& vertexFile, const VfsPath& fragmentFile,
const std::map<CStr, CStr>& defines,
const std::map<CStr, GLenum>& uniformTypes,
int streamflags) :
CShaderProgram(streamflags),
m_VertexFile(vertexFile), m_FragmentFile(fragmentFile),
m_Defines(defines),
m_UniformTypes(uniformTypes)
{
m_Program = 0;
m_VertexShader = pglCreateShaderObjectARB(GL_VERTEX_SHADER);
m_FragmentShader = pglCreateShaderObjectARB(GL_FRAGMENT_SHADER);
}
~CShaderProgramGLSL()
{
Unload();
pglDeleteShader(m_VertexShader);
pglDeleteShader(m_FragmentShader);
}
bool Compile(GLuint shader, const VfsPath& file, const CStr& code)
{
ogl_WarnIfError();
const char* code_string = code.c_str();
GLint code_length = code.length();
pglShaderSourceARB(shader, 1, &code_string, &code_length);
pglCompileShaderARB(shader);
GLint ok = 0;
pglGetShaderiv(shader, GL_COMPILE_STATUS, &ok);
if (!ok)
{
GLint length = 0;
pglGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
// Apparently sometimes GL_INFO_LOG_LENGTH is incorrectly reported as 0
// (http://code.google.com/p/android/issues/detail?id=9953)
if (length == 0)
length = 4096;
char* infolog = new char[length];
pglGetShaderInfoLog(shader, length, NULL, infolog);
LOGERROR(L"Failed to compile shader '%ls':\n%hs", file.string().c_str(), infolog);
delete[] infolog;
return false;
}
ogl_WarnIfError();
return true;
}
bool Link()
{
ENSURE(!m_Program);
m_Program = pglCreateProgramObjectARB();
pglAttachObjectARB(m_Program, m_VertexShader);
ogl_WarnIfError();
pglAttachObjectARB(m_Program, m_FragmentShader);
ogl_WarnIfError();
pglLinkProgramARB(m_Program);
GLint ok = 0;
pglGetProgramiv(m_Program, GL_LINK_STATUS, &ok);
if (!ok)
{
GLint length = 0;
pglGetProgramiv(m_Program, GL_INFO_LOG_LENGTH, &length);
if (length == 0)
length = 4096;
char* infolog = new char[length];
pglGetProgramInfoLog(m_Program, length, NULL, infolog);
LOGERROR(L"Failed to link program '%ls'+'%ls':\n%hs", m_VertexFile.string().c_str(), m_FragmentFile.string().c_str(), infolog);
delete[] infolog;
return false;
}
ogl_WarnIfError();
m_UniformLocations.clear();
m_Samplers.clear();
Bind();
for (std::map<CStr, GLenum>::iterator it = m_UniformTypes.begin(); it != m_UniformTypes.end(); ++it)
{
int loc = pglGetUniformLocationARB(m_Program, it->first.c_str());
m_UniformLocations[it->first] = loc;
if (loc != -1)
{
// Assign in-use sampler uniforms to sequential texture units
if (it->second == GL_SAMPLER_2D || it->second == GL_SAMPLER_CUBE)
{
int unit = (int)m_Samplers.size();
m_Samplers[it->first].first = (it->second == GL_SAMPLER_CUBE ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D);
m_Samplers[it->first].second = unit;
pglUniform1iARB(loc, unit); // link uniform to unit
}
}
}
// TODO: verify that we're not using more samplers than is supported
Unbind();
ogl_WarnIfError();
return true;
}
virtual void Reload()
{
Unload();
CVFSFile vertexFile;
if (vertexFile.Load(g_VFS, m_VertexFile) != PSRETURN_OK)
return;
CVFSFile fragmentFile;
if (fragmentFile.Load(g_VFS, m_FragmentFile) != PSRETURN_OK)
return;
CPreprocessor preprocessor;
for (std::map<CStr, CStr>::iterator it = m_Defines.begin(); it != m_Defines.end(); ++it)
preprocessor.Define(it->first.c_str(), it->second.c_str());
CStr vertexCode = Preprocess(preprocessor, vertexFile.GetAsString());
CStr fragmentCode = Preprocess(preprocessor, fragmentFile.GetAsString());
if (!Compile(m_VertexShader, m_VertexFile, vertexCode))
return;
if (!Compile(m_FragmentShader, m_FragmentFile, fragmentCode))
return;
if (!Link())
return;
m_IsValid = true;
}
void Unload()
{
m_IsValid = false;
if (m_Program)
pglDeleteProgram(m_Program);
m_Program = 0;
// The shader objects can be reused and don't need to be deleted here
}
virtual void Bind()
{
pglUseProgramObjectARB(m_Program);
}
virtual void Unbind()
{
pglUseProgramObjectARB(0);
// TODO: should unbind textures, probably
}
int GetUniformLocation(uniform_id_t id)
{
std::map<CStr, int>::iterator it = m_UniformLocations.find(id);
if (it == m_UniformLocations.end())
return -1;
return it->second;
}
virtual bool HasTexture(texture_id_t id)
{
if (GetUniformLocation(id) != -1)
return true;
return false;
}
virtual void BindTexture(texture_id_t id, Handle tex)
{
std::map<CStr, std::pair<GLenum, int> >::iterator it = m_Samplers.find(id);
if (it == m_Samplers.end())
return;
GLuint h;
ogl_tex_get_texture_id(tex, &h);
pglActiveTextureARB(GL_TEXTURE0 + it->second.second);
glBindTexture(it->second.first, h);
}
virtual void BindTexture(texture_id_t id, GLuint tex)
{
std::map<CStr, std::pair<GLenum, int> >::iterator it = m_Samplers.find(id);
if (it == m_Samplers.end())
return;
pglActiveTextureARB(GL_TEXTURE0 + it->second.second);
glBindTexture(it->second.first, tex);
}
virtual Binding GetUniformBinding(uniform_id_t id)
{
int loc = GetUniformLocation(id);
if (loc == -1)
return Binding();
else
return Binding(loc, m_UniformTypes[id]);
}
virtual void Uniform(Binding id, float v0, float v1, float v2, float v3)
{
if (id.first != -1)
{
if (id.second == GL_FLOAT)
pglUniform1fARB(id.first, v0);
else if (id.second == GL_FLOAT_VEC2)
pglUniform2fARB(id.first, v0, v1);
else if (id.second == GL_FLOAT_VEC3)
pglUniform3fARB(id.first, v0, v1, v2);
else if (id.second == GL_FLOAT_VEC4)
pglUniform4fARB(id.first, v0, v1, v2, v3);
else
LOGERROR(L"CShaderProgramGLSL::Uniform(): Invalid uniform type (expected float, vec2, vec3, vec4)");
}
}
virtual void Uniform(Binding id, const CMatrix3D& v)
{
if (id.first != -1)
{
if (id.second == GL_FLOAT_MAT4)
pglUniformMatrix4fvARB(id.first, 1, GL_FALSE, &v._11);
else
LOGERROR(L"CShaderProgramGLSL::Uniform(): Invalid uniform type (expected mat4)");
}
}
private:
VfsPath m_VertexFile;
VfsPath m_FragmentFile;
std::map<CStr, CStr> m_Defines;
GLuint m_Program;
GLuint m_VertexShader;
GLuint m_FragmentShader;
std::map<CStr, GLenum> m_UniformTypes;
std::map<CStr, int> m_UniformLocations;
std::map<CStr, std::pair<GLenum, int> > m_Samplers; // texture target & unit chosen for each uniform sampler
};
CShaderProgram::CShaderProgram(int streamflags)
: m_IsValid(false), m_StreamFlags(streamflags)
@ -252,6 +499,14 @@ CShaderProgram::CShaderProgram(int streamflags)
return new CShaderProgramARB(vertexFile, fragmentFile, defines, vertexIndexes, fragmentIndexes, streamflags);
}
/*static*/ CShaderProgram* CShaderProgram::ConstructGLSL(const VfsPath& vertexFile, const VfsPath& fragmentFile,
const std::map<CStr, CStr>& defines,
const std::map<CStr, GLenum>& uniformTypes,
int streamflags)
{
return new CShaderProgramGLSL(vertexFile, fragmentFile, defines, uniformTypes, streamflags);
}
bool CShaderProgram::IsValid() const
{
return m_IsValid;
@ -311,3 +566,24 @@ void CShaderProgram::Uniform(uniform_id_t id, const CMatrix3D& v)
{
Uniform(GetUniformBinding(id), v);
}
CStr CShaderProgram::Preprocess(CPreprocessor& preprocessor, const CStr& input)
{
size_t len = 0;
char* output = preprocessor.Parse(input.c_str(), input.size(), len);
if (!output)
{
LOGERROR(L"Shader preprocessing failed");
return "";
}
CStr ret(output, len);
// Free output if it's not inside the source string
if (!(output >= input.c_str() && output < input.c_str() + input.size()))
free(output);
return ret;
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2011 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -28,6 +28,7 @@
struct CColor;
class CMatrix3D;
class CVector3D;
class CPreprocessor;
// Vertex data stream flags
enum
@ -68,6 +69,14 @@ public:
const std::map<CStr, int>& vertexIndexes, const std::map<CStr, int>& fragmentIndexes,
int streamflags);
/**
* Construct based on GLSL vertex/fragment shader files.
*/
static CShaderProgram* ConstructGLSL(const VfsPath& vertexFile, const VfsPath& fragmentFile,
const std::map<CStr, CStr>& defines,
const std::map<CStr, GLenum>& uniformTypes,
int streamflags);
/**
* Construct an instance of a pre-defined fixed-function pipeline setup.
*/
@ -79,21 +88,25 @@ public:
/**
* Represents a uniform attribute binding.
* ARB shaders store vertex location in 'first', fragment location in 'second'.
* GLSL shaders store uniform location in 'first', data type in 'second'.
* FFP shaders store -1 in 'first', index in 'second'.
* Non-existent bindings must store -1 in both.
*/
struct Binding
{
Binding(int v, int f) : vertex((i16)v), fragment((i16)f) { }
Binding(int a, int b) : first(a), second(b) { }
Binding() : vertex(-1), fragment(-1) { }
Binding() : first(-1), second(-1) { }
/**
* Returns whether this uniform attribute is active in the shader.
* If not then there's no point calling Uniform() to set its value.
*/
bool Active() { return vertex != -1 || fragment != -1; }
bool Active() { return first != -1 || second != -1; }
i16 vertex;
i16 fragment;
int first;
int second;
};
virtual ~CShaderProgram() { }
@ -157,6 +170,8 @@ public:
protected:
CShaderProgram(int streamflags);
CStr Preprocess(CPreprocessor& preprocessor, const CStr& input);
bool m_IsValid;
int m_StreamFlags;
};

View File

@ -124,7 +124,7 @@ public:
virtual void Uniform(Binding id, float v0, float v1, float v2, float v3)
{
if (id.fragment == ID_losTransform)
if (id.second == ID_losTransform)
{
pglActiveTextureARB(GL_TEXTURE2);
GLfloat texgenS1[4] = { v0, 0, 0, v1 };
@ -132,7 +132,7 @@ public:
glTexGenfv(GL_S, GL_OBJECT_PLANE, texgenS1);
glTexGenfv(GL_T, GL_OBJECT_PLANE, texgenT1);
}
else if (id.fragment == ID_objectColor)
else if (id.second == ID_objectColor)
{
float c[] = { v0, v1, v2, v3 };
pglActiveTextureARB(GL_TEXTURE1);

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2011 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -111,6 +111,10 @@ void CShaderPass::DepthFunc(GLenum func)
}
CShaderTechnique::CShaderTechnique()
{
}
CShaderTechnique::CShaderTechnique(const CShaderPass& pass)
{
m_Passes.push_back(pass);

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2011 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -79,6 +79,7 @@ private:
class CShaderTechnique
{
public:
CShaderTechnique();
CShaderTechnique(const CShaderPass& pass);
void AddPass(const CShaderPass& pass);

View File

@ -1074,6 +1074,13 @@ Status ogl_tex_bind(Handle ht, size_t unit)
return INFO::OK;
}
Status ogl_tex_get_texture_id(Handle ht, GLuint* id)
{
*id = 0;
H_DEREF(ht, OglTex, ot);
*id = ot->id;
return INFO::OK;
}
// apply the specified transforms (as in tex_transform) to the image.
// must be called before uploading (raises a warning if called afterwards).

View File

@ -432,6 +432,11 @@ extern Status ogl_tex_get_average_colour(Handle ht, u32* p);
*/
extern Status ogl_tex_bind(Handle ht, size_t unit = 0);
/**
* Return the GL handle of the loaded texture in *id, or 0 on failure.
*/
extern Status ogl_tex_get_texture_id(Handle ht, GLuint* id);
/**
* (partially) Transform pixel format of the texture.
*

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2011 Wildfire Games.
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -36,6 +36,7 @@
#include "maths/Matrix3D.h"
#include "maths/MathUtil.h"
#include "ps/CLogger.h"
#include "ps/ConfigDB.h"
#include "ps/Game.h"
#include "ps/Profile.h"
#include "ps/Filesystem.h"
@ -447,6 +448,10 @@ CRenderer::CRenderer()
m_Options.m_ShadowAlphaFix = true;
m_Options.m_ARBProgramShadow = true;
m_Options.m_ShadowPCF = false;
m_Options.m_PreferGLSL = false;
// TODO: be more consistent in use of the config system
CFG_GET_USER_VAL("preferglsl", Bool, m_Options.m_PreferGLSL);
m_ShadowZBias = 0.02f;
m_ShadowMapSize = 0;
@ -565,12 +570,12 @@ void CRenderer::ReloadShaders()
defTransparent["USE_TRANSPARENT"] = "1";
// TODO: it'd be nicer to load this technique from an XML file or something
CShaderPass passTransparentOpaque(m->shaderManager.LoadProgram("model_common", defTransparent));
CShaderPass passTransparentOpaque(m->shaderManager.LoadProgram("model_common_arb", defTransparent));
passTransparentOpaque.AlphaFunc(GL_GREATER, 0.9375f);
passTransparentOpaque.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
CShaderTechnique techTransparentOpaque(passTransparentOpaque);
CShaderPass passTransparentBlend(m->shaderManager.LoadProgram("model_common", defTransparent));
CShaderPass passTransparentBlend(m->shaderManager.LoadProgram("model_common_arb", defTransparent));
passTransparentBlend.AlphaFunc(GL_GREATER, 0.0f);
passTransparentBlend.DepthFunc(GL_LESS);
passTransparentBlend.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@ -597,15 +602,15 @@ void CRenderer::ReloadShaders()
m->Model.ModShaderSolidTex = RenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
"solid_tex", defNull))));
m->Model.ModShaderNormal = LitRenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
"model_common", defBasic))));
m->Model.ModShaderNormalInstancing = LitRenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
"model_common_instancing", defBasic))));
m->Model.ModShaderNormal =
LitRenderModifierPtr(new ShaderRenderModifier(m->shaderManager.LoadEffect("model_normal", defBasic)));
m->Model.ModShaderNormalInstancing =
LitRenderModifierPtr(new ShaderRenderModifier(m->shaderManager.LoadEffect("model_normal_instancing", defBasic)));
m->Model.ModShaderPlayer = LitRenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
"model_common", defColored))));
m->Model.ModShaderPlayerInstancing = LitRenderModifierPtr(new ShaderRenderModifier(CShaderTechnique(m->shaderManager.LoadProgram(
"model_common_instancing", defColored))));
m->Model.ModShaderPlayer =
LitRenderModifierPtr(new ShaderRenderModifier(m->shaderManager.LoadEffect("model_normal", defColored)));
m->Model.ModShaderPlayerInstancing =
LitRenderModifierPtr(new ShaderRenderModifier(m->shaderManager.LoadEffect("model_normal_instancing", defColored)));
m->Model.ModShaderTransparent = LitRenderModifierPtr(new ShaderRenderModifier(
techTransparent));
@ -2072,6 +2077,19 @@ void CRenderer::JSI_SetShadowPCF(JSContext* ctx, jsval newval)
ReloadShaders();
}
jsval CRenderer::JSI_GetPreferGLSL(JSContext*)
{
return ToJSVal(m_Options.m_PreferGLSL);
}
void CRenderer::JSI_SetPreferGLSL(JSContext* ctx, jsval newval)
{
if (!ToPrimitive(ctx, newval, m_Options.m_PreferGLSL))
return;
ReloadShaders();
}
jsval CRenderer::JSI_GetSky(JSContext*)
{
return ToJSVal(m->skyManager.GetSkySet());
@ -2097,6 +2115,7 @@ void CRenderer::ScriptingInit()
AddProperty(L"depthTextureBits", &CRenderer::JSI_GetDepthTextureBits, &CRenderer::JSI_SetDepthTextureBits);
AddProperty(L"shadowAlphaFix", &CRenderer::JSI_GetShadowAlphaFix, &CRenderer::JSI_SetShadowAlphaFix);
AddProperty(L"shadowPCF", &CRenderer::JSI_GetShadowPCF, &CRenderer::JSI_SetShadowPCF);
AddProperty(L"preferGLSL", &CRenderer::JSI_GetPreferGLSL, &CRenderer::JSI_SetPreferGLSL);
AddProperty(L"skipSubmit", &CRenderer::m_SkipSubmit);
AddProperty(L"skySet", &CRenderer::JSI_GetSky, &CRenderer::JSI_SetSky);

View File

@ -121,6 +121,7 @@ public:
bool m_ShadowAlphaFix;
bool m_ARBProgramShadow;
bool m_ShadowPCF;
bool m_PreferGLSL;
} m_Options;
struct Caps {
@ -317,6 +318,8 @@ protected:
void JSI_SetShadowAlphaFix(JSContext* ctx, jsval newval);
jsval JSI_GetShadowPCF(JSContext*);
void JSI_SetShadowPCF(JSContext* ctx, jsval newval);
jsval JSI_GetPreferGLSL(JSContext*);
void JSI_SetPreferGLSL(JSContext* ctx, jsval newval);
jsval JSI_GetSky(JSContext*);
void JSI_SetSky(JSContext* ctx, jsval newval);