Make mesh shaders use the C4Shader slice machinery

issue1247
Armin Burgmeier 2014-12-22 17:15:46 +01:00
parent fdd94a3311
commit 8686441d45
19 changed files with 455 additions and 528 deletions

View File

@ -1,10 +0,0 @@
varying vec2 texcoord;
slice(init+1)
{
// TODO: Add emission part of the material. Note we cannot just
// add this to the color, but it would need to be handled separately,
// such that it is independent from the incident light direction.
// Could make it #ifdef MESH.
color = gl_FrontMaterial.diffuse * color;
}

View File

@ -3,8 +3,17 @@ uniform vec4 clrMod;
slice(init)
{
#define color gl_FragColor
#ifdef MESH
// TODO: Add emission part of the material. Note we cannot just
// add this to the color, but it would need to be handled separately,
// such that it is independent from the incident light direction.
color = gl_FrontMaterial.diffuse;
#else
vec4 baseColor = gl_Color;
color = baseColor;
#endif
}
slice(color)

View File

@ -21,7 +21,7 @@ slice(normal)
vec3 normal = normalize(gl_NormalMatrix * normalPxDir);
#else
#ifdef MESH
vec4 normal = normalize(gl_NormalMatrix * normalDir);
vec3 normal = normalDir; // Normal matrix is already applied in vertex shader
#else
vec3 normal = vec3(0.0, 0.0, 1.0);
#endif

View File

@ -32,14 +32,6 @@ material NormalMap
fragment_program_ref normal_map_fragment
{
// Will be configured automatically via slices:
// param_named_auto oc_Mod2 oc_mod2
// param_named_auto oc_ColorModulation oc_color_modulation
// param_named_auto oc_UseLight oc_use_light
// param_named_auto oc_Light oc_light
// param_named_auto oc_Ambient oc_ambient
// param_named_auto oc_AmbientBrightness oc_ambient_brightness
param_named basemap int 0
param_named normalTex int 1
}

View File

@ -1,4 +1,5 @@
uniform sampler2D basemap;
uniform sampler2D normalTex;
#ifndef OPENCLONK
#define slice(x)
@ -11,11 +12,10 @@ void main()
slice(init+1)
{
// This will make ObjectLightShader.glsl pick up the path that
// Looks up the direction from the normal map.
// This picks up the normal map lookup in ObjectLightShader.c:
#define HAVE_NORMALMAP
color = color * gl_FrontMaterial.diffuse * texture2D(basemap, texcoord);
color = color * texture2D(basemap, texcoord);
#ifndef OPENCLONK
// TODO: Could apply some default lighting here, for viewing the mesh in

View File

@ -1,7 +1,7 @@
varying vec2 texcoord;
#ifndef OPENCLONK
#define slice(x)
varying vec2 texcoord;
void main()
{
#endif

View File

@ -218,8 +218,7 @@ public:
void Grayscale(C4Surface * sfcSfc, int32_t iOffset = 0);
void LockingPrimary() { PrimaryLocked=true; }
void PrimaryUnlocked() { PrimaryLocked=false; }
virtual std::unique_ptr<StdMeshMaterialShader> CompileShader(const char* language, StdMeshMaterialShader::Type type, const char* text) = 0; // Compile shader of the given language
virtual bool PrepareMaterial(StdMeshMatManager& mat_manager, StdMeshMaterial &mat) = 0; // Find best technique, fail if there is none
virtual bool PrepareMaterial(StdMeshMatManager& mat_manager, StdMeshMaterialLoader& loader, StdMeshMaterial& mat) = 0; // Find best technique, fail if there is none
virtual bool PrepareRendering(C4Surface * sfcToSurface) = 0; // check if/make rendering possible to given surface
// Blit
virtual void BlitLandscape(C4Surface * sfcSource, float fx, float fy,

View File

@ -37,110 +37,6 @@
#include <math.h>
#include <limits.h>
C4DrawGLShader::C4DrawGLShader(Type shader_type)
{
GLint gl_type;
switch(shader_type)
{
case FRAGMENT: gl_type = GL_FRAGMENT_SHADER_ARB; break;
case VERTEX: gl_type = GL_VERTEX_SHADER_ARB; break;
case GEOMETRY: gl_type = GL_GEOMETRY_SHADER_ARB; break;
default: assert(false); break;
}
Shader = glCreateShaderObjectARB(gl_type);
if(!Shader) throw C4DrawGLError(FormatString("Failed to create shader")); // TODO: custom error class?
}
C4DrawGLShader::~C4DrawGLShader()
{
glDeleteObjectARB(Shader);
}
void C4DrawGLShader::Load(const char* code)
{
glShaderSourceARB(Shader, 1, &code, NULL);
glCompileShaderARB(Shader);
GLint compile_status;
glGetObjectParameterivARB(Shader, GL_OBJECT_COMPILE_STATUS_ARB, &compile_status);
if(compile_status != GL_TRUE)
{
const char* shader_type_str;
switch(GetType())
{
case VERTEX: shader_type_str = "vertex"; break;
case FRAGMENT: shader_type_str = "fragment"; break;
case GEOMETRY: shader_type_str = "geometry"; break;
default: assert(false); break;
}
GLint length;
glGetObjectParameterivARB(Shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length);
if(length > 0)
{
std::vector<char> error_message(length);
glGetInfoLogARB(Shader, length, NULL, &error_message[0]);
throw C4DrawGLError(FormatString("Failed to compile %s shader: %s", shader_type_str, &error_message[0]));
}
else
{
throw C4DrawGLError(FormatString("Failed to compile %s shader", shader_type_str));
}
}
}
StdMeshMaterialShader::Type C4DrawGLShader::GetType() const
{
GLint shader_type;
glGetObjectParameterivARB(Shader, GL_OBJECT_SUBTYPE_ARB, &shader_type);
switch(shader_type)
{
case GL_FRAGMENT_SHADER_ARB: return FRAGMENT;
case GL_VERTEX_SHADER_ARB: return VERTEX;
case GL_GEOMETRY_SHADER_ARB: return GEOMETRY;
default: assert(false); return static_cast<StdMeshMaterialShader::Type>(-1);
}
}
C4DrawGLProgram::C4DrawGLProgram(const C4DrawGLShader* fragment_shader, const C4DrawGLShader* vertex_shader, const C4DrawGLShader* geometry_shader)
{
Program = glCreateProgramObjectARB();
if(fragment_shader != NULL)
glAttachObjectARB(Program, fragment_shader->Shader);
if(vertex_shader != NULL)
glAttachObjectARB(Program, vertex_shader->Shader);
if(geometry_shader != NULL)
glAttachObjectARB(Program, geometry_shader->Shader);
glLinkProgramARB(Program);
GLint link_status;
glGetObjectParameterivARB(Program, GL_OBJECT_LINK_STATUS_ARB, &link_status);
if(link_status != GL_TRUE)
{
GLint length;
glGetObjectParameterivARB(Program, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length);
if(length > 0)
{
std::vector<char> error_message(length);
glGetInfoLogARB(Program, length, NULL, &error_message[0]);
glDeleteObjectARB(Program);
throw C4DrawGLError(FormatString("Failed to link program: %s", &error_message[0]));
}
else
{
glDeleteObjectARB(Program);
throw C4DrawGLError(StdStrBuf("Failed to link program"));
}
}
}
C4DrawGLProgram::~C4DrawGLProgram()
{
glDeleteObjectARB(Program);
}
CStdGL::CStdGL():
pMainCtx(0)
{
@ -362,24 +258,13 @@ void CStdGL::SetupMultiBlt(C4ShaderCall& call, const C4BltTransform* pTransform,
{
const C4Rect ClipRect = GetClipRect();
const C4Rect LightRect = pFoW->getRegion();
const int32_t iLightWdt = pFoW->getSurface()->Wdt;
const int32_t iLightHgt = pFoW->getSurface()->Hgt;
const float zx = static_cast<float>(LightRect.Wdt) / ClipRect.Wdt;
const float zy = static_cast<float>(LightRect.Hgt) / ClipRect.Hgt;
// Dynamic Light
call.AllocTexUnit(C4SSU_LightTex, GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, pFoW->getSurface()->ppTex[0]->texName);
// Transformation to go from fragment coordinates to light texture coordinates
// TODO: Should be moved to C4FoWRegion...
float lightTransform[6];
lightTransform[0] = zx / iLightWdt;
lightTransform[1] = 0.f;
lightTransform[2] = -ClipRect.x * zx / iLightWdt;
lightTransform[3] = 0.f;
lightTransform[4] = zy / iLightHgt;
lightTransform[5] = 1.0f - (LightRect.Hgt) / iLightHgt;
pFoW->GetFragTransform(ClipRect, lightTransform);
call.SetUniformMatrix2x3fv(C4SSU_LightTransform, 1, lightTransform);
// Ambient Light
@ -585,6 +470,7 @@ bool CStdGL::CreateSpriteShader(C4Shader& shader, const char* name, int ssc, C4G
shader.AddTexCoord("texcoord");
// Then load slices for fragment shader
shader.AddFragmentSlice(-1, "#define OPENCLONK");
if (ssc & C4SSC_MOD2) shader.AddFragmentSlice(-1, "#define CLRMOD_MOD2");
if (ssc & C4SSC_NORMAL) shader.AddFragmentSlice(-1, "#define HAVE_NORMALMAP");
if (ssc & C4SSC_LIGHT) shader.AddFragmentSlice(-1, "#define HAVE_LIGHT");

View File

@ -50,29 +50,6 @@ private:
StdCopyStrBuf Buf;
};
// GLSL shaders
class C4DrawGLShader: public StdMeshMaterialShader
{
public:
C4DrawGLShader(Type shader_type);
virtual ~C4DrawGLShader();
void Load(const char* code);
virtual Type GetType() const;
GLuint Shader;
};
class C4DrawGLProgram: public StdMeshMaterialProgram
{
public:
C4DrawGLProgram(const C4DrawGLShader* fragment_shader, const C4DrawGLShader* vertex_shader, const C4DrawGLShader* geometry_shader);
virtual ~C4DrawGLProgram();
GLuint Program;
};
// Shader combinations
static const int C4SSC_MOD2 = 1; // signed addition instead of multiplication for clrMod
static const int C4SSC_BASE = 2; // use a base texture instead of just a single color
@ -186,8 +163,7 @@ public:
virtual bool OnResolutionChanged(unsigned int iXRes, unsigned int iYRes); // reinit clipper for new resolution
// Clipper
bool UpdateClipper(); // set current clipper to render target
std::unique_ptr<StdMeshMaterialShader> CompileShader(const char* language, StdMeshMaterialShader::Type type, const char* text);
bool PrepareMaterial(StdMeshMatManager& mat_manager, StdMeshMaterial& mat);
bool PrepareMaterial(StdMeshMatManager& mat_manager, StdMeshMaterialLoader& loader, StdMeshMaterial& mat);
// Surface
bool PrepareRendering(C4Surface * sfcToSurface); // check if/make rendering possible to given surface
virtual CStdGLCtx *CreateContext(C4Window * pWindow, C4AbstractApp *pApp);

View File

@ -78,7 +78,7 @@ namespace
StdStrBuf alpha_source1 = FormatString("%s.a", TextureUnitSourceToCode(index, texunit.AlphaOpSources[0], texunit.ColorOpManualColor1, texunit.AlphaOpManualAlpha1).getData());
StdStrBuf alpha_source2 = FormatString("%s.a", TextureUnitSourceToCode(index, texunit.AlphaOpSources[1], texunit.ColorOpManualColor2, texunit.AlphaOpManualAlpha2).getData());
return FormatString("currentColor = vec4(%s, %s);", TextureUnitBlendToCode(index, texunit.ColorOpEx, color_source1.getData(), color_source2.getData(), texunit.ColorOpManualFactor).getData(), TextureUnitBlendToCode(index, texunit.AlphaOpEx, alpha_source1.getData(), alpha_source2.getData(), texunit.AlphaOpManualFactor).getData());
return FormatString("currentColor = vec4(%s, %s);\n", TextureUnitBlendToCode(index, texunit.ColorOpEx, color_source1.getData(), color_source2.getData(), texunit.ColorOpManualFactor).getData(), TextureUnitBlendToCode(index, texunit.AlphaOpEx, alpha_source1.getData(), alpha_source2.getData(), texunit.AlphaOpManualFactor).getData());
}
// Simple helper function
@ -105,19 +105,29 @@ namespace
StdStrBuf buf;
buf.Copy(
"varying vec3 normal;"
"varying vec2 texcoord;"
"void main()"
"{"
" normal = normalize(gl_NormalMatrix * gl_Normal);" // TODO: Do we need to normalize? I think we enable GL_NORMALIZE in cases we have to... note if we don't normalize, interpolation of normals won't work
" texcoord = gl_MultiTexCoord0.xy;"
" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;"
"}"
"varying vec3 normalDir;\n"
"\n"
"slice(position)\n"
"{\n"
" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
"}\n"
"\n"
"slice(texcoord)\n"
"{\n"
" texcoord = gl_MultiTexCoord0.xy;\n"
"}\n"
"\n"
"slice(normal)\n"
"{\n"
" normalDir = normalize(gl_NormalMatrix * gl_Normal);\n"
"}\n"
);
return buf;
}
// Note this only gets the code which inserts the slices specific for the pass
// -- other slices are independent from this!
StdStrBuf GetFragmentShaderCodeForPass(const StdMeshMaterialPass& pass, StdMeshMaterialShaderParameters& params)
{
StdStrBuf buf;
@ -134,68 +144,24 @@ namespace
if(texunit.HasTexture())
{
textureUnitDeclCode.Append(FormatString("uniform sampler2D oc_Texture%u;", texIndex).getData());
textureUnitDeclCode.Append(FormatString("uniform sampler2D oc_Texture%u;\n", texIndex).getData());
params.AddParameter(FormatString("oc_Texture%u", texIndex).getData(), StdMeshMaterialShaderParameter::INT).GetInt() = texIndex;
++texIndex;
}
}
// TODO: Only add this parameter if the player color is actually used in the shader -- otherwise
// it is optimized out anyway.
params.AddParameter("oc_PlayerColor", StdMeshMaterialShaderParameter::AUTO).GetAuto() = StdMeshMaterialShaderParameter::AUTO_OC_PLAYER_COLOR;
params.AddParameter("oc_ColorModulation", StdMeshMaterialShaderParameter::AUTO).GetAuto() = StdMeshMaterialShaderParameter::AUTO_OC_COLOR_MODULATION;
params.AddParameter("oc_Mod2", StdMeshMaterialShaderParameter::AUTO).GetAuto() = StdMeshMaterialShaderParameter::AUTO_OC_MOD2;
params.AddParameter("oc_UseLight", StdMeshMaterialShaderParameter::AUTO).GetAuto() = StdMeshMaterialShaderParameter::AUTO_OC_USE_LIGHT;
params.AddParameter("oc_Light", StdMeshMaterialShaderParameter::AUTO).GetAuto() = StdMeshMaterialShaderParameter::AUTO_OC_LIGHT;
params.AddParameter("oc_Ambient", StdMeshMaterialShaderParameter::AUTO).GetAuto() = StdMeshMaterialShaderParameter::AUTO_OC_AMBIENT;
params.AddParameter("oc_AmbientBrightness", StdMeshMaterialShaderParameter::AUTO).GetAuto() = StdMeshMaterialShaderParameter::AUTO_OC_AMBIENT_BRIGHTNESS;
return FormatString(
"varying vec3 normal;" // linearly interpolated -- not necessarily normalized
"varying vec2 texcoord;"
"%s" // Texture units with active textures, only if >0 texture units
"uniform vec3 oc_PlayerColor;"
"uniform vec4 oc_ColorModulation;"
"uniform int oc_Mod2;"
"uniform int oc_UseLight;"
"uniform sampler2D oc_Light;"
"uniform sampler2D oc_Ambient;"
"uniform float oc_AmbientBrightness;"
"void main()"
"{"
" vec4 lightClr;"
" vec3 normalDir = normalize(normal);"
" if(oc_UseLight != 0)"
" {"
// Light calculation
" vec4 lightPx = texture2D(oc_Light, (gl_TextureMatrix[%d] * gl_FragCoord).xy);"
" vec3 lightDir = normalize(vec3(vec2(1.0, 1.0) - lightPx.gb * 3.0, 0.3));"
" float lightIntensity = 2.0 * lightPx.r;"
" float ambient = texture2D(oc_Ambient, (gl_TextureMatrix[%d] * gl_FragCoord).xy).r * oc_AmbientBrightness;"
// Don't actually use the ambient part of the material and instead a diffuse light from the front, like in the master branch
// Because meshes are not tuned for ambient light at the moment, every mesh material would need to be fixed.
// Otherwise the first term would be ambient * gl_FrontMaterial.ambient
" lightClr = vec4(ambient * (gl_FrontMaterial.emission.rgb + gl_FrontMaterial.diffuse.rgb * (0.25 + 0.75 * max(dot(normalDir, vec3(0.0, 0.0, 1.0)), 0.0))) + (1.0 - min(ambient, 1.0)) * lightIntensity * (gl_FrontMaterial.emission.rgb + gl_FrontMaterial.diffuse.rgb * (0.25 + 0.75 * max(dot(normalDir, lightDir), 0.0))), gl_FrontMaterial.emission.a + gl_FrontMaterial.diffuse.a);"
" }"
" else"
" {"
// No light -- place a simple directional light from the front (equivalent to the behaviour in
// the master branch, modulo interpolated normals)
" vec3 lightDir = vec3(0.0, 0.0, 1.0);"
" lightClr = vec4(gl_FrontMaterial.emission.rgb + gl_FrontMaterial.diffuse.rgb * (0.25 + 0.75 * max(dot(normalDir, lightDir), 0.0)), gl_FrontMaterial.emission.a + gl_FrontMaterial.diffuse.a);"
" }"
// Texture units from material script
" vec4 diffuse = lightClr;"
" vec4 currentColor = diffuse;"
" %s"
// Output with color modulation and mod2
" if(oc_Mod2 != 0)"
" gl_FragColor = clamp(2.0 * currentColor * oc_ColorModulation - 0.5, 0.0, 1.0);"
" else"
" gl_FragColor = clamp(currentColor * oc_ColorModulation, 0.0, 1.0);"
"}",
"%s\n" // Texture units with active textures, only if >0 texture units
"uniform vec3 oc_PlayerColor;\n" // This needs to be in-sync with the naming in StdMeshMaterialProgram::CompileShader()
"\n"
"slice(texture)\n"
"{\n"
" vec4 diffuse = color;\n"
" vec4 currentColor = diffuse;\n"
" %s\n"
" color = currentColor;\n"
"}\n",
textureUnitDeclCode.getData(),
(int)texIndex, (int)texIndex + 1, // The light and ambient textures are added after all other textures
textureUnitCode.getData()
);
}
@ -211,48 +177,7 @@ namespace
}
} // anonymous namespace
class C4DrawMeshGLProgramInstance: public StdMeshMaterialPass::ProgramInstance
{
public:
C4DrawMeshGLProgramInstance(const C4DrawGLProgram* program);
void AddParameters(const StdMeshMaterialShaderParameters& parameters);
struct Parameter {
GLint Location;
const StdMeshMaterialShaderParameter* ShaderParameter;
};
std::vector<Parameter> Parameters;
};
C4DrawMeshGLProgramInstance::C4DrawMeshGLProgramInstance(const C4DrawGLProgram* program):
StdMeshMaterialPass::ProgramInstance(program)
{
}
void C4DrawMeshGLProgramInstance::AddParameters(const StdMeshMaterialShaderParameters& parameters)
{
const C4DrawGLProgram* program = static_cast<const C4DrawGLProgram*>(Program);
for(unsigned int i = 0; i < parameters.NamedParameters.size(); ++i)
{
const GLint location = glGetUniformLocationARB(program->Program, parameters.NamedParameters[i].first.getData());
Parameters.push_back(Parameter());
Parameters.back().Location = location;
Parameters.back().ShaderParameter = &parameters.NamedParameters[i].second;
}
}
std::unique_ptr<StdMeshMaterialShader> CStdGL::CompileShader(const char* language, StdMeshMaterialShader::Type type, const char* text)
{
if(strcmp(language, "glsl") != 0)
throw C4DrawGLError(StdStrBuf("Not a GLSL shader"));
std::unique_ptr<C4DrawGLShader> shader(new C4DrawGLShader(type));
shader->Load(text);
return std::move(shader);
}
bool CStdGL::PrepareMaterial(StdMeshMatManager& mat_manager, StdMeshMaterial& mat)
bool CStdGL::PrepareMaterial(StdMeshMatManager& mat_manager, StdMeshMaterialLoader& loader, StdMeshMaterial& mat)
{
// TODO: If a technique is not available, show an error message what the problem is
@ -409,43 +334,36 @@ bool CStdGL::PrepareMaterial(StdMeshMatManager& mat_manager, StdMeshMaterial& ma
} // loop over textures
} // loop over texture units
try
// Create fragment and/or vertex shader
// if a custom shader is not provided.
// Re-use existing programs if the generated
// code is the same (determined by SHA1 hash).
if(!pass.VertexShader.Shader)
{
// Create fragment and/or vertex shader
// if a custom shader is not provided.
// Re-use existing programs if the generated
// code is the same (determined by SHA1 hash).
if(!pass.VertexShader.Shader)
{
StdStrBuf buf = GetVertexShaderCodeForPass(pass);
StdStrBuf hash = GetSHA1HexDigest(buf.getData(), buf.getLength());
pass.VertexShader.Shader = mat_manager.AddShader(hash.getData(), "glsl", StdMeshMaterialShader::VERTEX, buf.getData(), true);
}
if(!pass.FragmentShader.Shader)
{
// TODO: Should use shared_params once we introduce them
StdStrBuf buf = GetFragmentShaderCodeForPass(pass, pass.FragmentShader.Parameters);
StdStrBuf hash = GetSHA1HexDigest(buf.getData(), buf.getLength());
pass.FragmentShader.Shader = mat_manager.AddShader(hash.getData(), "glsl", StdMeshMaterialShader::FRAGMENT, buf.getData(), true);
}
// Then, link the program, and resolve parameter locations
const C4DrawGLShader* fragment_shader = static_cast<const C4DrawGLShader*>(pass.FragmentShader.Shader);
const C4DrawGLShader* vertex_shader = static_cast<const C4DrawGLShader*>(pass.VertexShader.Shader);
const C4DrawGLShader* geometry_shader = static_cast<const C4DrawGLShader*>(pass.GeometryShader.Shader);
std::unique_ptr<C4DrawGLProgram> program(new C4DrawGLProgram(fragment_shader, vertex_shader, geometry_shader));
const StdMeshMaterialProgram* added_program = &mat_manager.AddProgram(fragment_shader, vertex_shader, geometry_shader, std::move(program));
std::unique_ptr<C4DrawMeshGLProgramInstance> program_instance(new C4DrawMeshGLProgramInstance(static_cast<const C4DrawGLProgram*>(added_program)));
if(pass.FragmentShader.Shader) program_instance->AddParameters(pass.FragmentShader.Parameters);
if(pass.VertexShader.Shader) program_instance->AddParameters(pass.VertexShader.Parameters);
if(pass.GeometryShader.Shader) program_instance->AddParameters(pass.GeometryShader.Parameters);
pass.Program = std::move(program_instance);
StdStrBuf buf = GetVertexShaderCodeForPass(pass);
StdStrBuf hash = GetSHA1HexDigest(buf.getData(), buf.getLength());
pass.VertexShader.Shader = mat_manager.AddShader("auto-generated vertex shader", hash.getData(), "glsl", SMMS_VERTEX, buf.getData(), true);
}
catch(const C4DrawGLError& error)
if(!pass.FragmentShader.Shader)
{
// TODO: Should use shared_params once we introduce them
StdStrBuf buf = GetFragmentShaderCodeForPass(pass, pass.FragmentShader.Parameters);
StdStrBuf hash = GetSHA1HexDigest(buf.getData(), buf.getLength());
pass.FragmentShader.Shader = mat_manager.AddShader("auto-generated fragment shader", hash.getData(), "glsl", SMMS_FRAGMENT, buf.getData(), true);
}
// Then, link the program, and resolve parameter locations
StdStrBuf name(FormatString("%s:%s:%s", mat.Name.getData(), technique.Name.getData(), pass.Name.getData()));
const StdMeshMaterialProgram* added_program = mat_manager.AddProgram(name.getData(), loader, pass.FragmentShader, pass.VertexShader, pass.GeometryShader);
if(!added_program)
{
technique.Available = false;
LogF("Failed to compile shader: %s\n", error.what());
}
else
{
std::unique_ptr<StdMeshMaterialPass::ProgramInstance> program_instance(new StdMeshMaterialPass::ProgramInstance(added_program, &pass.FragmentShader, &pass.VertexShader, &pass.GeometryShader));
pass.Program = std::move(program_instance);
}
}
@ -489,108 +407,50 @@ namespace
return true;
}
bool ResolveAutoParameter(StdMeshMaterialShaderParameter& parameter, StdMeshMaterialShaderParameter::Auto value, DWORD dwModClr, DWORD dwPlayerColor, DWORD dwBlitMode, const C4FoWRegion* pFoW, const C4Rect& clipRect, std::vector<GLint>& textures)
void SetStandardUniforms(C4ShaderCall& call, DWORD dwModClr, DWORD dwPlayerColor, DWORD dwBlitMode, const C4FoWRegion* pFoW, const C4Rect& clipRect)
{
float* out;
GLint texIndex;
C4Rect LightRect;
int32_t iLightWdt;
int32_t iLightHgt;
// Draw transform
const float fMod[4] = {
((dwModClr >> 16) & 0xff) / 255.0f,
((dwModClr >> 8) & 0xff) / 255.0f,
((dwModClr ) & 0xff) / 255.0f,
((dwModClr >> 24) & 0xff) / 255.0f
};
call.SetUniform4fv(C4SSU_ClrMod, 1, fMod);
switch(value)
// Player color
const float fPlrClr[3] = {
((dwPlayerColor >> 16) & 0xff) / 255.0f,
((dwPlayerColor >> 8) & 0xff) / 255.0f,
((dwPlayerColor ) & 0xff) / 255.0f,
};
call.SetUniform3fv(C4SSU_OverlayClr, 1, fPlrClr);
// Dynamic light
if(pFoW != NULL)
{
case StdMeshMaterialShaderParameter::AUTO_OC_PLAYER_COLOR:
parameter.SetType(StdMeshMaterialShaderParameter::FLOAT3);
out = parameter.GetFloatv();
out[0] = ((dwPlayerColor >> 16) & 0xff) / 255.0f;
out[1] = ((dwPlayerColor >> 8) & 0xff) / 255.0f;
out[2] = ((dwPlayerColor ) & 0xff) / 255.0f;
return true;
case StdMeshMaterialShaderParameter::AUTO_OC_COLOR_MODULATION:
parameter.SetType(StdMeshMaterialShaderParameter::FLOAT4);
out = parameter.GetFloatv();
out[0] = ((dwModClr >> 16) & 0xff) / 255.0f;
out[1] = ((dwModClr >> 8) & 0xff) / 255.0f;
out[2] = ((dwModClr ) & 0xff) / 255.0f;
out[3] = ((dwModClr >> 24) & 0xff) / 255.0f;
return true;
case StdMeshMaterialShaderParameter::AUTO_OC_MOD2:
parameter.SetType(StdMeshMaterialShaderParameter::INT);
parameter.GetInt() = (dwBlitMode & C4GFXBLIT_MOD2) != 0;
return true;
case StdMeshMaterialShaderParameter::AUTO_OC_USE_LIGHT:
parameter.SetType(StdMeshMaterialShaderParameter::INT);
parameter.GetInt() = (pFoW != NULL);
return true;
case StdMeshMaterialShaderParameter::AUTO_OC_LIGHT:
if(!pFoW) return false;
texIndex = textures.size();
textures.push_back(texIndex);
// Load the texture
glActiveTexture(GL_TEXTURE0+texIndex);
//glClientActiveTexture(GL_TEXTURE0+texIndex);
glEnable(GL_TEXTURE_2D);
call.AllocTexUnit(C4SSU_LightTex, GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, pFoW->getSurface()->ppTex[0]->texName);
float lightTransform[6];
pFoW->GetFragTransform(clipRect, lightTransform);
call.SetUniformMatrix2x3fv(C4SSU_LightTransform, 1, lightTransform);
// Transformation matrix for the texture coordinates
// TODO: Should maybe be a separate uniform variable?
LightRect = pFoW->getRegion();
iLightWdt = pFoW->getSurface()->Wdt;
iLightHgt = pFoW->getSurface()->Hgt;
glLoadIdentity();
glTranslatef(0.0f, 1.0f - (float)LightRect.Hgt/(float)iLightHgt, 0.0f);
glScalef(1.0f/iLightWdt, 1.0f/iLightHgt, 1.0f);
glScalef( (float)LightRect.Wdt / (float)clipRect.Wdt, (float)LightRect.Hgt / (float)clipRect.Hgt, 1.0f);
glTranslatef(-clipRect.x, 0.0f, 0.0f);
parameter.SetType(StdMeshMaterialShaderParameter::INT);
parameter.GetInt() = texIndex;
return true;
case StdMeshMaterialShaderParameter::AUTO_OC_AMBIENT:
if(!pFoW) return false;
texIndex = textures.size();
textures.push_back(texIndex);
// Load the texture
glActiveTexture(GL_TEXTURE0+texIndex);
//glClientActiveTexture(GL_TEXTURE0+texIndex);
glEnable(GL_TEXTURE_2D);
call.AllocTexUnit(C4SSU_AmbientTex, GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, pFoW->getFoW()->Ambient.Tex);
// Transformation matrix for the texture coordinates
// TODO: Should maybe be a separate uniform variable?
LightRect = pFoW->getRegion();
iLightWdt = pFoW->getSurface()->Wdt;
iLightHgt = pFoW->getSurface()->Hgt;
// Setup the texture matrix
glLoadIdentity();
glScalef(1.0f/pFoW->getFoW()->Ambient.GetLandscapeWidth(), 1.0f/pFoW->getFoW()->Ambient.GetLandscapeHeight(), 1.0f);
glTranslatef(LightRect.x, LightRect.y, 0.0f);
glScalef( (float)LightRect.Wdt / (float)clipRect.Wdt, (float)LightRect.Hgt / (float)clipRect.Hgt, 1.0f);
glTranslatef(-clipRect.x, clipRect.Hgt, 0.0f);
glScalef(1.0f, -1.0f, 1.0f);
parameter.SetType(StdMeshMaterialShaderParameter::INT);
parameter.GetInt() = texIndex;
return true;
case StdMeshMaterialShaderParameter::AUTO_OC_AMBIENT_BRIGHTNESS:
if(!pFoW) return false;
parameter.SetType(StdMeshMaterialShaderParameter::FLOAT);
parameter.GetFloat() = pFoW->getFoW()->Ambient.GetBrightness();
return true;
default:
assert(false);
return false;
call.SetUniform1f(C4SSU_AmbientBrightness, pFoW->getFoW()->Ambient.GetBrightness());
float ambientTransform[6];
pFoW->getFoW()->Ambient.GetFragTransform(pFoW->getRegion(), clipRect, ambientTransform);
call.SetUniformMatrix2x3fv(C4SSU_AmbientTransform, 1, ambientTransform);
}
}
bool ResolveAutoParameter(C4ShaderCall& call, StdMeshMaterialShaderParameter& parameter, StdMeshMaterialShaderParameter::Auto value, DWORD dwModClr, DWORD dwPlayerColor, DWORD dwBlitMode, const C4FoWRegion* pFoW, const C4Rect& clipRect)
{
// There are no auto parameters implemented yet
assert(false);
return false;
}
void RenderSubMeshImpl(const StdMeshInstance& mesh_instance, const StdSubMeshInstance& instance, DWORD dwModClr, DWORD dwBlitMode, DWORD dwPlayerColor, const C4FoWRegion* pFoW, const C4Rect& clipRect, bool parity)
{
const StdMeshMaterial& material = instance.GetMaterial();
@ -667,36 +527,31 @@ namespace
// TODO: Use vbo if available.
glTexCoordPointer(2, GL_FLOAT, sizeof(StdMeshVertex), &vertices->u);
glVertexPointer(3, GL_FLOAT, sizeof(StdMeshVertex), &vertices->x);
glNormalPointer(GL_FLOAT, sizeof(StdMeshVertex), &vertices->nx);
glMatrixMode(GL_TEXTURE);
std::vector<GLint> textures;
textures.reserve(pass.TextureUnits.size());
assert(pass.Program.get() != NULL);
// Upload all parameters to the shader (keep GL_TEXTURE matrix mode for this)
int ssc = 0;
if(dwBlitMode & C4GFXBLIT_MOD2) ssc |= C4SSC_MOD2;
if(pFoW != NULL) ssc |= C4SSC_LIGHT;
const C4Shader* shader = pass.Program->Program->GetShader(ssc);
C4ShaderCall call(shader);
call.Start();
for (unsigned int j = 0; j < pass.TextureUnits.size(); ++j)
{
const StdMeshMaterialTextureUnit& texunit = pass.TextureUnits[j];
const unsigned int texIndex = textures.size();
if (texunit.HasTexture())
{
// Array with texture indices set for passing the textures to the
// shader -- shader cannot use fixed texture image units before OGL 4.2.
textures.push_back(texIndex);
// Note that it is guaranteed that the GL_TEXTUREn
// constants are contiguous.
glActiveTexture(GL_TEXTURE0+texIndex);
glClientActiveTexture(GL_TEXTURE0+texIndex);
glEnable(GL_TEXTURE_2D);
call.AllocTexUnit(-1, GL_TEXTURE_2D);
const unsigned int Phase = instance.GetTexturePhase(i, j);
glBindTexture(GL_TEXTURE_2D, texunit.GetTexture(Phase).texName);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, sizeof(StdMeshVertex), &vertices->u);
// Setup texture coordinate transform
glLoadIdentity();
const double Position = instance.GetTexturePosition(i, j);
@ -748,22 +603,19 @@ namespace
}
}
assert(pass.Program.get() != NULL);
const C4DrawMeshGLProgramInstance& program_instance = static_cast<const C4DrawMeshGLProgramInstance&>(*pass.Program);
// Upload all parameters to the shader (keep GL_TEXTURE matrix mode, since we might initialize clrmodmap during this)
glUseProgramObjectARB(static_cast<const C4DrawGLProgram*>(program_instance.Program)->Program);
for(unsigned int i = 0; i < program_instance.Parameters.size(); ++i)
// Set uniforms and instance parameters
SetStandardUniforms(call, dwModClr, dwPlayerColor, dwBlitMode, pFoW, clipRect);
for(unsigned int i = 0; i < pass.Program->Parameters.size(); ++i)
{
const GLint location = program_instance.Parameters[i].Location;
if(location == -1) continue; // parameter optimized out, or misnamed
const int uniform = pass.Program->Parameters[i].UniformIndex;
if(!shader->HaveUniform(uniform)) continue; // optimized out
const StdMeshMaterialShaderParameter* parameter = program_instance.Parameters[i].ShaderParameter;
const StdMeshMaterialShaderParameter* parameter = pass.Program->Parameters[i].Parameter;
StdMeshMaterialShaderParameter auto_resolved;
if(parameter->GetType() == StdMeshMaterialShaderParameter::AUTO)
{
if(!ResolveAutoParameter(auto_resolved, parameter->GetAuto(), dwModClr, dwPlayerColor, dwBlitMode, pFoW, clipRect, textures))
if(!ResolveAutoParameter(call, auto_resolved, parameter->GetAuto(), dwModClr, dwPlayerColor, dwBlitMode, pFoW, clipRect))
continue;
parameter = &auto_resolved;
}
@ -771,22 +623,22 @@ namespace
switch(parameter->GetType())
{
case StdMeshMaterialShaderParameter::INT:
glUniform1iARB(location, parameter->GetInt());
call.SetUniform1i(uniform, parameter->GetInt());
break;
case StdMeshMaterialShaderParameter::FLOAT:
glUniform1fARB(location, parameter->GetFloat());
call.SetUniform1f(uniform, parameter->GetFloat());
break;
case StdMeshMaterialShaderParameter::FLOAT2:
glUniform2fvARB(location, 1, parameter->GetFloatv());
call.SetUniform2fv(uniform, 1, parameter->GetFloatv());
break;
case StdMeshMaterialShaderParameter::FLOAT3:
glUniform3fvARB(location, 1, parameter->GetFloatv());
call.SetUniform3fv(uniform, 1, parameter->GetFloatv());
break;
case StdMeshMaterialShaderParameter::FLOAT4:
glUniform4fvARB(location, 1, parameter->GetFloatv());
call.SetUniform4fv(uniform, 1, parameter->GetFloatv());
break;
case StdMeshMaterialShaderParameter::MATRIX_4X4:
glUniformMatrix4fvARB(location, 1, GL_TRUE, parameter->GetMatrix());
call.SetUniformMatrix4x4fv(uniform, 1, parameter->GetMatrix());
break;
default:
assert(false);
@ -796,15 +648,7 @@ namespace
glMatrixMode(GL_MODELVIEW);
glDrawElements(GL_TRIANGLES, instance.GetNumFaces()*3, GL_UNSIGNED_INT, instance.GetFaces());
// Clean-up, re-set default state
for (unsigned int j = 0; j < textures.size(); ++j)
{
glActiveTexture(GL_TEXTURE0+textures[j]);
glClientActiveTexture(GL_TEXTURE0+textures[j]);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
}
call.Finish();
if(!pass.DepthCheck)
glEnable(GL_DEPTH_TEST);
@ -955,11 +799,11 @@ void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float tw
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND); // TODO: Shouldn't this always be enabled? - blending does not work for meshes without this though.
glClientActiveTexture(GL_TEXTURE0); // our only texcoord corresponds to tex0
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_COLOR_ARRAY); // might still be active from a previous (non-mesh-rendering) GL operation
glClientActiveTexture(GL_TEXTURE0);
glDisableClientState(GL_TEXTURE_COORD_ARRAY); // same -- we enable this individually for every texture unit in RenderSubMeshImpl
glDisableClientState(GL_COLOR_ARRAY);
// TODO: We ignore the additive drawing flag for meshes but instead
// set the blending mode of the corresponding material. I'm not sure
@ -1132,8 +976,6 @@ void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float tw
clipRect.y = iClipY1; if(clipRect.y < 0) { clipRect.Hgt += clipRect.y; clipRect.y = 0; }
RenderMeshImpl(instance, dwModClr, dwBlitMode, dwPlayerColor, pFoW, clipRect, parity);
glUseProgramObjectARB(0);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
@ -1143,6 +985,7 @@ void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float tw
glClientActiveTexture(GL_TEXTURE0); // switch back to default
glDepthMask(GL_TRUE);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);

View File

@ -31,7 +31,7 @@ bool CStdNoGfx::CreatePrimarySurfaces(bool Fullscreen, unsigned int iXRes, unsig
return true;
}
bool CStdNoGfx::PrepareMaterial(StdMeshMatManager& mat_manager, StdMeshMaterial &mat)
bool CStdNoGfx::PrepareMaterial(StdMeshMatManager& mat_manager, StdMeshMaterialLoader& loader, StdMeshMaterial& mat)
{
mat.BestTechniqueIndex=0; return true;
mat.BestTechniqueIndex=0; return true;
}

View File

@ -30,7 +30,7 @@ public:
virtual void TaskIn() { }
virtual bool UpdateClipper() { return true; }
virtual bool OnResolutionChanged(unsigned int, unsigned int) { return true; }
virtual bool PrepareMaterial(StdMeshMatManager& mat_manager, StdMeshMaterial &mat);
virtual bool PrepareMaterial(StdMeshMatManager& mat_manager, StdMeshMaterialLoader& loader, StdMeshMaterial& mat);
virtual bool PrepareRendering(C4Surface *) { return true; }
virtual void FillBG(DWORD dwClr=0) { }
virtual void PerformMesh(StdMeshInstance &, float, float, float, float, DWORD, C4BltTransform* pTransform) { }

View File

@ -18,7 +18,7 @@ C4ShaderPosName C4SH_PosNames[] = {
{ C4Shader_Vertex_TexCoordPos, "texcoord" },
{ C4Shader_Vertex_NormalPos, "normal" },
{ C4Shader_Vertxe_PositionPos, "position" }
{ C4Shader_Vertex_PositionPos, "position" }
};
C4Shader::C4Shader()
@ -516,6 +516,7 @@ GLint C4ShaderCall::AllocTexUnit(int iUniform, GLenum iType)
void C4ShaderCall::Start()
{
assert(!fStarted);
assert(pShader->hProg != 0); // Shader must be initialized
// Activate shader
glUseProgramObjectARB(pShader->hProg);

View File

@ -144,6 +144,14 @@ public:
if (pShader->HaveUniform(iUniform))
glUniform1fvARB(pShader->GetUniform(iUniform), iLength, pVals);
}
void SetUniform2fv(int iUniform, int iLength, const float *pVals) const {
if (pShader->HaveUniform(iUniform))
glUniform2fvARB(pShader->GetUniform(iUniform), iLength, pVals);
}
void SetUniform3fv(int iUniform, int iLength, const float *pVals) const {
if (pShader->HaveUniform(iUniform))
glUniform3fvARB(pShader->GetUniform(iUniform), iLength, pVals);
}
void SetUniform4fv(int iUniform, int iLength, const float *pVals) const {
if (pShader->HaveUniform(iUniform))
glUniform4fvARB(pShader->GetUniform(iUniform), iLength, pVals);
@ -154,6 +162,10 @@ public:
if (pShader->HaveUniform(iUniform))
glUniformMatrix3x2fv(pShader->GetUniform(iUniform), iLength, GL_TRUE, pVals);
}
void SetUniformMatrix4x4fv(int iUniform, int iLength, const float* pVals) const {
if (pShader->HaveUniform(iUniform))
glUniformMatrix4fvARB(pShader->GetUniform(iUniform), iLength, GL_TRUE, pVals);
}
void Start();
void Finish();

View File

@ -182,6 +182,22 @@ void C4FoWRegion::Render(const C4TargetFacet *pOnScreen)
}
void C4FoWRegion::GetFragTransform(const C4Rect& clipRect, float lightTransform[6]) const
{
const C4Rect& lightRect = getRegion();
const int32_t iLightWdt = getSurface()->Wdt;
const int32_t iLightHgt = getSurface()->Hgt;
const float zx = static_cast<float>(lightRect.Wdt) / clipRect.Wdt;
const float zy = static_cast<float>(lightRect.Hgt) / clipRect.Hgt;
lightTransform[0] = zx / iLightWdt;
lightTransform[1] = 0.f;
lightTransform[2] = -clipRect.x * zx / iLightWdt;
lightTransform[3] = 0.f;
lightTransform[4] = zy / iLightHgt;
lightTransform[5] = 1.0f - (lightRect.Hgt) / iLightHgt;
}
C4FoWRegion::C4FoWRegion(C4FoW *pFoW, C4Player *pPlayer)
: pFoW(pFoW)
, pPlayer(pPlayer)
@ -189,4 +205,4 @@ C4FoWRegion::C4FoWRegion(C4FoW *pFoW, C4Player *pPlayer)
, Region(0,0,0,0), OldRegion(0,0,0,0)
, pSurface(NULL), pBackSurface(NULL)
{
}
}

View File

@ -31,6 +31,9 @@ public:
void Update(C4Rect r);
void Render(const C4TargetFacet *pOnScreen = NULL);
// Fills a 2x3 matrix to transform fragment coordinates to light texture coordinates
// TODO: This might be more precise for highly zoomed cases if the lightRect was using floating point accuracy
void GetFragTransform(const C4Rect& clipRect, float lightTransform[6]) const;
private:
bool BindFramebuf();

View File

@ -17,7 +17,7 @@
#include "C4Include.h"
#include <StdMeshMaterial.h>
#include <StdMeshUpdate.h>
#include <C4Draw.h>
#include <C4DrawGL.h>
#include <cctype>
#include <memory>
@ -54,15 +54,6 @@ namespace
const Enumerator<StdMeshMaterialShaderParameter::Auto> ShaderParameterAutoEnumerators[] =
{
{ "oc_player_color", StdMeshMaterialShaderParameter::AUTO_OC_PLAYER_COLOR },
{ "oc_player_colour", StdMeshMaterialShaderParameter::AUTO_OC_PLAYER_COLOR },
{ "oc_color_modulation", StdMeshMaterialShaderParameter::AUTO_OC_COLOR_MODULATION },
{ "oc_colour_modulation", StdMeshMaterialShaderParameter::AUTO_OC_COLOR_MODULATION },
{ "oc_mod2", StdMeshMaterialShaderParameter::AUTO_OC_MOD2 },
{ "oc_use_light", StdMeshMaterialShaderParameter::AUTO_OC_USE_LIGHT },
{ "oc_light", StdMeshMaterialShaderParameter::AUTO_OC_LIGHT },
{ "oc_ambient", StdMeshMaterialShaderParameter::AUTO_OC_AMBIENT },
{ "oc_ambient_brightness", StdMeshMaterialShaderParameter::AUTO_OC_AMBIENT_BRIGHTNESS },
{ NULL, static_cast<StdMeshMaterialShaderParameter::Auto>(0) }
};
@ -589,7 +580,7 @@ void StdMeshMaterialSubLoader::Load(StdMeshMaterialParserCtx& ctx, std::vector<S
}
}
void LoadShader(StdMeshMaterialParserCtx& ctx, StdMeshMaterialShader::Type type)
void LoadShader(StdMeshMaterialParserCtx& ctx, StdMeshMaterialShaderType type)
{
StdStrBuf token_name;
StdStrBuf name, language;
@ -621,14 +612,7 @@ void LoadShader(StdMeshMaterialParserCtx& ctx, StdMeshMaterialShader::Type type)
if (token != TOKEN_BRACE_CLOSE)
ctx.Error(StdCopyStrBuf("'") + token_name.getData() + "' unexpected");
try
{
ctx.Manager.AddShader(name.getData(), language.getData(), type, code.getData(), false);
}
catch(const std::exception& ex)
{
ctx.Error(StdCopyStrBuf("Failed to compile shader: ") + ex.what());
}
ctx.Manager.AddShader(source.getData(), name.getData(), language.getData(), type, code.getData(), false);
}
StdMeshMaterialShaderParameter::StdMeshMaterialShaderParameter():
@ -829,6 +813,97 @@ StdMeshMaterialShaderParameter& StdMeshMaterialShaderParameters::AddParameter(co
return NamedParameters.back().second;
}
StdMeshMaterialProgram::StdMeshMaterialProgram(const char* name, const StdMeshMaterialShader* fragment_shader, const StdMeshMaterialShader* vertex_shader, const StdMeshMaterialShader* geometry_shader):
Name(name), FragmentShader(fragment_shader), VertexShader(vertex_shader), GeometryShader(geometry_shader)
{
assert(FragmentShader != NULL);
assert(VertexShader != NULL);
// Geometry shader is optional (and not even implemented at the moment!)
}
bool StdMeshMaterialProgram::AddParameterNames(const StdMeshMaterialShaderParameters& parameters)
{
// TODO: This is O(n^2) -- not optimal!
bool added = false;
for (unsigned int i = 0; i < parameters.NamedParameters.size(); ++i)
{
const std::vector<StdCopyStrBuf>::const_iterator iter = std::find(ParameterNames.begin(), ParameterNames.end(), parameters.NamedParameters[i].first);
if (iter == ParameterNames.end())
{
ParameterNames.push_back(parameters.NamedParameters[i].first);
added = true;
}
}
return added;
}
bool StdMeshMaterialProgram::CompileShader(StdMeshMaterialLoader& loader, C4Shader& shader, int ssc)
{
// Add standard slices
shader.AddFragmentSlice(-1, "#define MESH");
loader.AddShaderSlices(shader, ssc);
// Add our slices
shader.AddVertexSlice(-1, "varying vec2 texcoord;");
shader.AddFragmentSlice(-1, "varying vec2 texcoord;");
shader.AddVertexSlices(VertexShader->GetFilename(), VertexShader->GetCode(), VertexShader->GetFilename());
shader.AddFragmentSlices(FragmentShader->GetFilename(), FragmentShader->GetCode(), FragmentShader->GetFilename());
// Construct the list of uniforms
std::vector<const char*> uniformNames(C4SSU_Count + ParameterNames.size() + 1);
uniformNames[C4SSU_ClrMod] = "clrMod";
uniformNames[C4SSU_BaseTex] = "baseTex"; // unused
uniformNames[C4SSU_OverlayTex] = "overlayTex"; // unused
uniformNames[C4SSU_OverlayClr] = "oc_PlayerColor";
uniformNames[C4SSU_LightTex] = "lightTex";
uniformNames[C4SSU_LightTransform] = "lightTransform";
uniformNames[C4SSU_NormalTex] = "normalTex"; // unused
uniformNames[C4SSU_AmbientTex] = "ambientTex";
uniformNames[C4SSU_AmbientTransform] = "ambientTransform";
uniformNames[C4SSU_AmbientBrightness] = "ambientBrightness";
for (unsigned int i = 0; i < ParameterNames.size(); ++i)
uniformNames[C4SSU_Count + i] = ParameterNames[i].getData();
uniformNames[C4SSU_Count + ParameterNames.size()] = NULL;
// Compile the shader
StdCopyStrBuf name(Name);
if (ssc != 0) name.Append(":");
if (ssc & C4SSC_LIGHT) name.Append("Light");
if (ssc & C4SSC_MOD2) name.Append("Mod2");
return shader.Init(name.getData(), &uniformNames[0]);
}
bool StdMeshMaterialProgram::Compile(StdMeshMaterialLoader& loader)
{
if (!CompileShader(loader, Shader, 0)) return false;
if (!CompileShader(loader, ShaderMod2, C4SSC_MOD2)) return false;
if (!CompileShader(loader, ShaderLight, C4SSC_LIGHT)) return false;
if (!CompileShader(loader, ShaderLightMod2, C4SSC_LIGHT | C4SSC_MOD2)) return false;
return true;
}
const C4Shader* StdMeshMaterialProgram::GetShader(int ssc) const
{
const C4Shader* shaders[4] = {
&Shader,
&ShaderMod2,
&ShaderLight,
&ShaderLightMod2
};
int index = 0;
if(ssc & C4SSC_MOD2) index += 1;
if(ssc & C4SSC_LIGHT) index += 2;
assert(index < 4);
return shaders[index];
}
int StdMeshMaterialProgram::GetParameterIndex(const char* name) const
{
std::vector<StdCopyStrBuf>::const_iterator iter = std::find(ParameterNames.begin(), ParameterNames.end(), name);
if(iter == ParameterNames.end()) return -1;
return C4SSU_Count + std::distance(ParameterNames.begin(), iter);
}
double StdMeshMaterialTextureUnit::Transformation::GetWaveXForm(double t) const
{
assert(TransformType == T_WAVE_XFORM);
@ -1081,6 +1156,44 @@ void StdMeshMaterialTextureUnit::Load(StdMeshMaterialParserCtx& ctx)
ctx.Error(StdCopyStrBuf("'") + token_name.getData() + "' unexpected");
}
StdMeshMaterialPass::ProgramInstance::ProgramInstance(const StdMeshMaterialProgram* program, const ShaderInstance* fragment_instance, const ShaderInstance* vertex_instance, const ShaderInstance* geometry_instance):
Program(program)
{
// Consistency check
assert(Program->GetFragmentShader() == fragment_instance->Shader);
assert(Program->GetVertexShader() == vertex_instance->Shader);
assert(Program->GetGeometryShader() == geometry_instance->Shader);
// Load instance parameters, i.e. connect parameter values with uniform index
LoadParameterRefs(fragment_instance);
LoadParameterRefs(vertex_instance);
LoadParameterRefs(geometry_instance);
}
void StdMeshMaterialPass::ProgramInstance::LoadParameterRefs(const ShaderInstance* instance)
{
for(unsigned int i = 0; i < instance->Parameters.NamedParameters.size(); ++i)
{
const int index = Program->GetParameterIndex(instance->Parameters.NamedParameters[i].first.getData());
assert(index != -1);
const std::vector<ParameterRef>::const_iterator parameter_iter =
std::find_if(Parameters.begin(), Parameters.end(), [index](const ParameterRef& ref) { return ref.UniformIndex == index; });
if(parameter_iter != Parameters.end())
{
// TODO: Check that the current parameter has the same value as the found one
continue;
}
else
{
ParameterRef ref;
ref.Parameter = &instance->Parameters.NamedParameters[i].second;
ref.UniformIndex = index;
Parameters.push_back(ref);
}
}
}
StdMeshMaterialPass::StdMeshMaterialPass():
DepthCheck(true), DepthWrite(true), CullHardware(CH_Clockwise)
{
@ -1094,7 +1207,7 @@ StdMeshMaterialPass::StdMeshMaterialPass():
VertexShader.Shader = FragmentShader.Shader = GeometryShader.Shader = NULL;
}
void StdMeshMaterialPass::LoadShaderRef(StdMeshMaterialParserCtx& ctx, StdMeshMaterialShader::Type type)
void StdMeshMaterialPass::LoadShaderRef(StdMeshMaterialParserCtx& ctx, StdMeshMaterialShaderType type)
{
StdStrBuf program_name, token;
ctx.AdvanceRequired(program_name, TOKEN_IDTF);
@ -1105,17 +1218,17 @@ void StdMeshMaterialPass::LoadShaderRef(StdMeshMaterialParserCtx& ctx, StdMeshMa
switch(type)
{
case StdMeshMaterialShader::FRAGMENT:
case SMMS_FRAGMENT:
cur_shader = &FragmentShader;
shader = ctx.Manager.GetFragmentShader(program_name.getData());
shader_type_name = "fragment";
break;
case StdMeshMaterialShader::VERTEX:
case SMMS_VERTEX:
cur_shader = &VertexShader;
shader = ctx.Manager.GetVertexShader(program_name.getData());
shader_type_name = "vertex";
break;
case StdMeshMaterialShader::GEOMETRY:
case SMMS_GEOMETRY:
cur_shader = &GeometryShader;
shader = ctx.Manager.GetGeometryShader(program_name.getData());
shader_type_name = "geometry";
@ -1254,15 +1367,15 @@ void StdMeshMaterialPass::Load(StdMeshMaterialParserCtx& ctx)
}
else if (token_name == "vertex_program_ref")
{
LoadShaderRef(ctx, StdMeshMaterialShader::VERTEX);
LoadShaderRef(ctx, SMMS_VERTEX);
}
else if (token_name == "fragment_program_ref")
{
LoadShaderRef(ctx, StdMeshMaterialShader::FRAGMENT);
LoadShaderRef(ctx, SMMS_FRAGMENT);
}
else if (token_name == "geometry_program_ref")
{
LoadShaderRef(ctx, StdMeshMaterialShader::GEOMETRY);
LoadShaderRef(ctx, SMMS_GEOMETRY);
}
else
ctx.ErrorUnexpectedIdentifier(token_name);
@ -1399,7 +1512,7 @@ void StdMeshMatManager::Parse(const char* mat_script, const char* filename, StdM
Materials[material_name] = mat;
// To Gfxspecific setup of the material; choose working techniques
if (!pDraw->PrepareMaterial(*this, Materials[material_name]))
if (!pDraw->PrepareMaterial(*this, loader, Materials[material_name]))
{
Materials.erase(material_name);
ctx.Error(StdCopyStrBuf("No working technique for material '") + material_name + "'");
@ -1407,15 +1520,15 @@ void StdMeshMatManager::Parse(const char* mat_script, const char* filename, StdM
}
else if (token_name == "vertex_program")
{
LoadShader(ctx, StdMeshMaterialShader::VERTEX);
LoadShader(ctx, SMMS_VERTEX);
}
else if (token_name == "fragment_program")
{
LoadShader(ctx, StdMeshMaterialShader::FRAGMENT);
LoadShader(ctx, SMMS_FRAGMENT);
}
else if (token_name == "geometry_program")
{
LoadShader(ctx, StdMeshMaterialShader::GEOMETRY);
LoadShader(ctx, SMMS_GEOMETRY);
}
else
ctx.ErrorUnexpectedIdentifier(token_name);
@ -1462,18 +1575,18 @@ const StdMeshMaterialShader* StdMeshMatManager::GetGeometryShader(const char* na
return iter->second.get();
}
const StdMeshMaterialShader* StdMeshMatManager::AddShader(const char* name, const char* language, StdMeshMaterialShader::Type type, const char* text, bool success_if_exists)
const StdMeshMaterialShader* StdMeshMatManager::AddShader(const char* filename, const char* name, const char* language, StdMeshMaterialShaderType type, const char* text, bool success_if_exists)
{
ShaderMap* map = NULL;
switch(type)
{
case StdMeshMaterialShader::FRAGMENT:
case SMMS_FRAGMENT:
map = &FragmentShaders;
break;
case StdMeshMaterialShader::VERTEX:
case SMMS_VERTEX:
map = &VertexShaders;
break;
case StdMeshMaterialShader::GEOMETRY:
case SMMS_GEOMETRY:
map = &GeometryShaders;
break;
}
@ -1490,7 +1603,7 @@ const StdMeshMaterialShader* StdMeshMatManager::AddShader(const char* name, cons
}
else
{
std::unique_ptr<StdMeshMaterialShader> shader = pDraw->CompileShader(language, type, text);
std::unique_ptr<StdMeshMaterialShader> shader(new StdMeshMaterialShader(filename, name, language, type, text));
std::pair<ShaderMap::iterator, bool> inserted = map->insert(std::make_pair(name_buf, std::move(shader)));
assert(inserted.second == true);
iter = inserted.first;
@ -1499,12 +1612,29 @@ const StdMeshMaterialShader* StdMeshMatManager::AddShader(const char* name, cons
}
}
const StdMeshMaterialProgram& StdMeshMatManager::AddProgram(const StdMeshMaterialShader* fragment_shader, const StdMeshMaterialShader* vertex_shader, const StdMeshMaterialShader* geometry_shader, std::unique_ptr<StdMeshMaterialProgram> RREF program)
const StdMeshMaterialProgram* StdMeshMatManager::AddProgram(const char* name, StdMeshMaterialLoader& loader, const StdMeshMaterialPass::ShaderInstance& fragment_shader, const StdMeshMaterialPass::ShaderInstance& vertex_shader, const StdMeshMaterialPass::ShaderInstance& geometry_shader)
{
std::tuple<const StdMeshMaterialShader*, const StdMeshMaterialShader*, const StdMeshMaterialShader*> key = std::make_tuple(fragment_shader, vertex_shader, geometry_shader);
std::tuple<const StdMeshMaterialShader*, const StdMeshMaterialShader*, const StdMeshMaterialShader*> key = std::make_tuple(fragment_shader.Shader, vertex_shader.Shader, geometry_shader.Shader);
ProgramMap::iterator iter = Programs.find(key);
if(iter == Programs.end())
{
std::unique_ptr<StdMeshMaterialProgram> program(new StdMeshMaterialProgram(name, fragment_shader.Shader, vertex_shader.Shader, geometry_shader.Shader));
iter = Programs.insert(std::make_pair(key, std::move(program))).first;
}
std::pair<ProgramMap::iterator, bool> inserted = Programs.insert(std::make_pair(key, std::move(program)));
return *inserted.first->second;
StdMeshMaterialProgram& inserted_program = *iter->second;
const bool fragment_added = inserted_program.AddParameterNames(fragment_shader.Parameters);
const bool vertex_added = inserted_program.AddParameterNames(vertex_shader.Parameters);
const bool geometry_added = inserted_program.AddParameterNames(geometry_shader.Parameters);
// Re-compile the program (and assign new uniform locations if new
// parameters were encountered).
if(!inserted_program.IsCompiled() || fragment_added || vertex_added || geometry_added)
if(!inserted_program.Compile(loader))
return NULL;
return &inserted_program;
}
StdMeshMatManager MeshMaterialManager;

View File

@ -19,6 +19,7 @@
#include <StdBuf.h>
#include <C4Surface.h>
#include <C4Shader.h>
#include <vector>
#include <map>
@ -55,15 +56,8 @@ public:
};
enum Auto {
AUTO_OC_PLAYER_COLOR,
AUTO_OC_COLOR_MODULATION,
AUTO_OC_MOD2,
AUTO_OC_USE_LIGHT,
AUTO_OC_LIGHT,
AUTO_OC_AMBIENT,
AUTO_OC_AMBIENT_BRIGHTNESS
// TODO: Other ogre auto values
// TODO: OGRE auto values
AUTO_DUMMY
};
StdMeshMaterialShaderParameter(); // type=FLOAT, value uninitialized
@ -121,25 +115,10 @@ private:
StdMeshMaterialShaderParameter LoadAutoParameter(StdMeshMaterialParserCtx& ctx);
};
// An abstract shader class. This is supposed to be implemented by the
// GFX implementation, such as C4DrawGL.
class StdMeshMaterialShader
{
public:
enum Type {
FRAGMENT,
VERTEX,
GEOMETRY
};
virtual ~StdMeshMaterialShader() {}
virtual Type GetType() const = 0;
};
class StdMeshMaterialProgram
{
public:
virtual ~StdMeshMaterialProgram() {}
enum StdMeshMaterialShaderType {
SMMS_FRAGMENT,
SMMS_VERTEX,
SMMS_GEOMETRY
};
// Interface to load additional resources.
@ -152,9 +131,66 @@ class StdMeshMaterialLoader
public:
virtual C4Surface* LoadTexture(const char* filename) = 0;
virtual StdStrBuf LoadShaderCode(const char* filename) = 0;
virtual void AddShaderSlices(C4Shader& shader, int ssc) = 0; // add default shader slices
virtual ~StdMeshMaterialLoader() {}
};
// This is just a container class to hold the shader code; the C4Shader
// objects are later created from that code by mixing them with the default
// slices.
class StdMeshMaterialShader
{
public:
StdMeshMaterialShader(const char* filename, const char* name, const char* language, StdMeshMaterialShaderType type, const char* code):
Filename(filename), Name(name), Language(language), Type(type), Code(code) {}
const char* GetFilename() const { return Filename.getData(); }
const char* GetCode() const { return Code.getData(); }
private:
StdCopyStrBuf Filename;
StdCopyStrBuf Name;
StdCopyStrBuf Language;
StdMeshMaterialShaderType Type;
StdCopyStrBuf Code;
};
class StdMeshMaterialProgram
{
public:
StdMeshMaterialProgram(const char* name, const StdMeshMaterialShader* fragment_shader, const StdMeshMaterialShader* vertex_shader, const StdMeshMaterialShader* geometry_shader);
bool AddParameterNames(const StdMeshMaterialShaderParameters& parameters); // returns true if some parameter names were not yet registered.
bool IsCompiled() const { return Shader.Initialised(); }
bool Compile(StdMeshMaterialLoader& loader);
const C4Shader* GetShader(int ssc) const;
int GetParameterIndex(const char* name) const;
const StdMeshMaterialShader* GetFragmentShader() const { return FragmentShader; }
const StdMeshMaterialShader* GetVertexShader() const { return VertexShader; }
const StdMeshMaterialShader* GetGeometryShader() const { return GeometryShader; }
private:
bool CompileShader(StdMeshMaterialLoader& loader, C4Shader& shader, int ssc);
// Human-readable program name
const StdCopyStrBuf Name;
// Program components
const StdMeshMaterialShader* FragmentShader;
const StdMeshMaterialShader* VertexShader;
const StdMeshMaterialShader* GeometryShader;
// Compiled shaders
C4Shader Shader;
C4Shader ShaderMod2;
C4Shader ShaderLight;
C4Shader ShaderLightMod2;
// Filled as program references are encountered;
std::vector<StdCopyStrBuf> ParameterNames;
};
class StdMeshMaterialTextureUnit
{
public:
@ -389,17 +425,27 @@ public:
class ProgramInstance
{
public:
ProgramInstance(const StdMeshMaterialProgram* program):
Program(program) {}
virtual ~ProgramInstance() {}
ProgramInstance(const StdMeshMaterialProgram* program, const ShaderInstance* fragment_instance, const ShaderInstance* vertex_instance, const ShaderInstance* geometry_instance);
// This points into the StdMeshMatManager map
const StdMeshMaterialProgram* const Program;
// Parameters for this instance
struct ParameterRef {
const StdMeshMaterialShaderParameter* Parameter;
int UniformIndex; // Index into parameter table for this program
};
std::vector<ParameterRef> Parameters;
private:
void LoadParameterRefs(const ShaderInstance* instance);
};
ShaderInstance FragmentShader;
ShaderInstance VertexShader;
ShaderInstance GeometryShader;
// This is a shared_ptr and not a unique_ptr so that this class is
// copyable, so it can be inherited. However, when the inherited
// material is prepared, the ProgramInstance will be overwritten
@ -408,8 +454,9 @@ public:
// provide inheritance by copying all other fields, and letting
// PrepareMaterial fill the program instance.
std::shared_ptr<ProgramInstance> Program;
private:
void LoadShaderRef(StdMeshMaterialParserCtx& ctx, StdMeshMaterialShader::Type type);
void LoadShaderRef(StdMeshMaterialParserCtx& ctx, StdMeshMaterialShaderType type);
};
class StdMeshMaterialTechnique
@ -497,8 +544,8 @@ public:
Iterator End() { return Iterator(Materials.end()); }
Iterator Remove(const Iterator& iter, class StdMeshMaterialUpdate* update);
const StdMeshMaterialShader* AddShader(const char* name, const char* language, StdMeshMaterialShader::Type type, const char* text, bool success_if_exists); // if pass_if_exists is TRUE, the function returns the existing shader, otherwise returns NULL.
const StdMeshMaterialProgram& AddProgram(const StdMeshMaterialShader* fragment_shader, const StdMeshMaterialShader* vertex_shader, const StdMeshMaterialShader* geometry_shader, std::unique_ptr<StdMeshMaterialProgram> RREF program);
const StdMeshMaterialShader* AddShader(const char* filename, const char* name, const char* language, StdMeshMaterialShaderType type, const char* text, bool success_if_exists); // if pass_if_exists is TRUE, the function returns the existing shader, otherwise returns NULL.
const StdMeshMaterialProgram* AddProgram(const char* name, StdMeshMaterialLoader& loader, const StdMeshMaterialPass::ShaderInstance& fragment_shader, const StdMeshMaterialPass::ShaderInstance& vertex_shader, const StdMeshMaterialPass::ShaderInstance& geometry_shader); // returns NULL if shader code cannot be compiled
const StdMeshMaterialShader* GetFragmentShader(const char* name) const;
const StdMeshMaterialShader* GetVertexShader(const char* name) const;
@ -506,10 +553,8 @@ public:
private:
MaterialMap Materials;
// Compiled shaders
// TODO: Some sort of post-init should delete compiled shaders after all programs have been linked
// c.f. http://stackoverflow.com/questions/9113154/proper-way-to-delete-glsl-shader
typedef std::map<StdCopyStrBuf, std::unique_ptr<StdMeshMaterialShader> > ShaderMap;
// Shader code for custom shaders.
typedef std::map<StdCopyStrBuf, std::unique_ptr<StdMeshMaterialShader>> ShaderMap;
ShaderMap FragmentShaders;
ShaderMap VertexShaders;
ShaderMap GeometryShaders;

View File

@ -19,6 +19,8 @@
#include <C4Include.h>
#include <C4Def.h>
#include <C4DrawGL.h>
#include <C4GraphicsResource.h>
#include <C4Components.h>
#include <C4Config.h>
@ -54,6 +56,29 @@ public:
return ret;
}
virtual void AddShaderSlices(C4Shader& shader, int ssc)
{
// Add mesh-independent slices
shader.AddFragmentSlice(-1, "#define OPENCLONK");
shader.AddVertexSlice(-1, "#define OPENCLONK");
if (ssc & C4SSC_MOD2) shader.AddFragmentSlice(-1, "#define CLRMOD_MOD2");
if (ssc & C4SSC_LIGHT) shader.AddFragmentSlice(-1, "#define HAVE_LIGHT");
shader.LoadSlices(&::GraphicsResource.Files, "UtilShader.glsl");
shader.LoadSlices(&::GraphicsResource.Files, "ObjectBaseShader.glsl");
shader.LoadSlices(&::GraphicsResource.Files, "MeshShader.glsl");
if (ssc & C4SSC_BASE) shader.LoadSlices(&::GraphicsResource.Files, "SpriteTextureShader.glsl");
if (ssc & C4SSC_OVERLAY) shader.LoadSlices(&::GraphicsResource.Files, "SpriteOverlayShader.glsl");
if (ssc & C4SSC_LIGHT)
{
shader.LoadSlices(&::GraphicsResource.Files, "ObjectLightShader.glsl");
shader.LoadSlices(&::GraphicsResource.Files, "LightShader.glsl");
shader.LoadSlices(&::GraphicsResource.Files, "AmbientShader.glsl");
}
}
private:
C4Group& Group;
};