From 8686441d45ffad5b84e0712f5d88ea61a4d11157 Mon Sep 17 00:00:00 2001 From: Armin Burgmeier Date: Mon, 22 Dec 2014 17:15:46 +0100 Subject: [PATCH] Make mesh shaders use the C4Shader slice machinery --- planet/Graphics.ocg/MeshShader.glsl | 10 - planet/Graphics.ocg/ObjectBaseShader.glsl | 9 + planet/Graphics.ocg/ObjectLightShader.glsl | 2 +- planet/Objects.ocd/normal_map.material | 8 - planet/Objects.ocd/normal_map_fragment.glsl | 6 +- planet/Objects.ocd/normal_map_vertex.glsl | 4 +- src/graphics/C4Draw.h | 3 +- src/graphics/C4DrawGL.cpp | 118 +----- src/graphics/C4DrawGL.h | 26 +- src/graphics/C4DrawMeshGL.cpp | 401 ++++++-------------- src/graphics/C4DrawT.cpp | 4 +- src/graphics/C4DrawT.h | 2 +- src/graphics/C4Shader.cpp | 3 +- src/graphics/C4Shader.h | 12 + src/landscape/fow/C4FoWRegion.cpp | 18 +- src/landscape/fow/C4FoWRegion.h | 3 + src/lib/StdMeshMaterial.cpp | 208 ++++++++-- src/lib/StdMeshMaterial.h | 121 ++++-- src/object/C4Def.cpp | 25 ++ 19 files changed, 455 insertions(+), 528 deletions(-) delete mode 100644 planet/Graphics.ocg/MeshShader.glsl diff --git a/planet/Graphics.ocg/MeshShader.glsl b/planet/Graphics.ocg/MeshShader.glsl deleted file mode 100644 index 3d00008c8..000000000 --- a/planet/Graphics.ocg/MeshShader.glsl +++ /dev/null @@ -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; -} diff --git a/planet/Graphics.ocg/ObjectBaseShader.glsl b/planet/Graphics.ocg/ObjectBaseShader.glsl index 7c9d34df8..cf9b7b376 100644 --- a/planet/Graphics.ocg/ObjectBaseShader.glsl +++ b/planet/Graphics.ocg/ObjectBaseShader.glsl @@ -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) diff --git a/planet/Graphics.ocg/ObjectLightShader.glsl b/planet/Graphics.ocg/ObjectLightShader.glsl index 8561f7148..c27cabd3f 100644 --- a/planet/Graphics.ocg/ObjectLightShader.glsl +++ b/planet/Graphics.ocg/ObjectLightShader.glsl @@ -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 diff --git a/planet/Objects.ocd/normal_map.material b/planet/Objects.ocd/normal_map.material index 2a1826f83..85723781c 100644 --- a/planet/Objects.ocd/normal_map.material +++ b/planet/Objects.ocd/normal_map.material @@ -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 } diff --git a/planet/Objects.ocd/normal_map_fragment.glsl b/planet/Objects.ocd/normal_map_fragment.glsl index f9e931da4..a540e497a 100644 --- a/planet/Objects.ocd/normal_map_fragment.glsl +++ b/planet/Objects.ocd/normal_map_fragment.glsl @@ -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 diff --git a/planet/Objects.ocd/normal_map_vertex.glsl b/planet/Objects.ocd/normal_map_vertex.glsl index 068579b36..38db358d7 100644 --- a/planet/Objects.ocd/normal_map_vertex.glsl +++ b/planet/Objects.ocd/normal_map_vertex.glsl @@ -1,7 +1,7 @@ -varying vec2 texcoord; - #ifndef OPENCLONK #define slice(x) +varying vec2 texcoord; + void main() { #endif diff --git a/src/graphics/C4Draw.h b/src/graphics/C4Draw.h index 94d3855ac..954902983 100644 --- a/src/graphics/C4Draw.h +++ b/src/graphics/C4Draw.h @@ -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 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, diff --git a/src/graphics/C4DrawGL.cpp b/src/graphics/C4DrawGL.cpp index 5222bb4e3..3154471c8 100644 --- a/src/graphics/C4DrawGL.cpp +++ b/src/graphics/C4DrawGL.cpp @@ -37,110 +37,6 @@ #include #include -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 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(-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 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(LightRect.Wdt) / ClipRect.Wdt; - const float zy = static_cast(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"); diff --git a/src/graphics/C4DrawGL.h b/src/graphics/C4DrawGL.h index f3e2f9bd3..0b493a36a 100644 --- a/src/graphics/C4DrawGL.h +++ b/src/graphics/C4DrawGL.h @@ -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 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); diff --git a/src/graphics/C4DrawMeshGL.cpp b/src/graphics/C4DrawMeshGL.cpp index 016c727c8..4bf0177c4 100644 --- a/src/graphics/C4DrawMeshGL.cpp +++ b/src/graphics/C4DrawMeshGL.cpp @@ -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 Parameters; -}; - -C4DrawMeshGLProgramInstance::C4DrawMeshGLProgramInstance(const C4DrawGLProgram* program): - StdMeshMaterialPass::ProgramInstance(program) -{ -} - -void C4DrawMeshGLProgramInstance::AddParameters(const StdMeshMaterialShaderParameters& parameters) -{ - const C4DrawGLProgram* program = static_cast(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 = ¶meters.NamedParameters[i].second; - } -} - -std::unique_ptr 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 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(pass.FragmentShader.Shader); - const C4DrawGLShader* vertex_shader = static_cast(pass.VertexShader.Shader); - const C4DrawGLShader* geometry_shader = static_cast(pass.GeometryShader.Shader); - std::unique_ptr 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 program_instance(new C4DrawMeshGLProgramInstance(static_cast(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 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& 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 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(*pass.Program); - - // Upload all parameters to the shader (keep GL_TEXTURE matrix mode, since we might initialize clrmodmap during this) - glUseProgramObjectARB(static_cast(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); diff --git a/src/graphics/C4DrawT.cpp b/src/graphics/C4DrawT.cpp index e7238fcf9..694dd988e 100644 --- a/src/graphics/C4DrawT.cpp +++ b/src/graphics/C4DrawT.cpp @@ -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; } diff --git a/src/graphics/C4DrawT.h b/src/graphics/C4DrawT.h index 9698ea1ef..b7b7e9790 100644 --- a/src/graphics/C4DrawT.h +++ b/src/graphics/C4DrawT.h @@ -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) { } diff --git a/src/graphics/C4Shader.cpp b/src/graphics/C4Shader.cpp index d5518ebee..a650fa62d 100644 --- a/src/graphics/C4Shader.cpp +++ b/src/graphics/C4Shader.cpp @@ -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); diff --git a/src/graphics/C4Shader.h b/src/graphics/C4Shader.h index a3fd16406..9c44b1d76 100644 --- a/src/graphics/C4Shader.h +++ b/src/graphics/C4Shader.h @@ -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(); diff --git a/src/landscape/fow/C4FoWRegion.cpp b/src/landscape/fow/C4FoWRegion.cpp index fb9d5f502..91e5d80c4 100644 --- a/src/landscape/fow/C4FoWRegion.cpp +++ b/src/landscape/fow/C4FoWRegion.cpp @@ -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(lightRect.Wdt) / clipRect.Wdt; + const float zy = static_cast(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) { -} \ No newline at end of file +} diff --git a/src/landscape/fow/C4FoWRegion.h b/src/landscape/fow/C4FoWRegion.h index c0231a227..35b389bad 100644 --- a/src/landscape/fow/C4FoWRegion.h +++ b/src/landscape/fow/C4FoWRegion.h @@ -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(); diff --git a/src/lib/StdMeshMaterial.cpp b/src/lib/StdMeshMaterial.cpp index 31d192f01..1fc9ef99b 100644 --- a/src/lib/StdMeshMaterial.cpp +++ b/src/lib/StdMeshMaterial.cpp @@ -17,7 +17,7 @@ #include "C4Include.h" #include #include -#include +#include #include #include @@ -54,15 +54,6 @@ namespace const Enumerator 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(0) } }; @@ -589,7 +580,7 @@ void StdMeshMaterialSubLoader::Load(StdMeshMaterialParserCtx& ctx, std::vector::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 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::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::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 shader = pDraw->CompileShader(language, type, text); + std::unique_ptr shader(new StdMeshMaterialShader(filename, name, language, type, text)); std::pair 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 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 key = std::make_tuple(fragment_shader, vertex_shader, geometry_shader); + std::tuple 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 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 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; diff --git a/src/lib/StdMeshMaterial.h b/src/lib/StdMeshMaterial.h index f1f54f1b4..d4fa52660 100644 --- a/src/lib/StdMeshMaterial.h +++ b/src/lib/StdMeshMaterial.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -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 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 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 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 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 > ShaderMap; + // Shader code for custom shaders. + typedef std::map> ShaderMap; ShaderMap FragmentShaders; ShaderMap VertexShaders; ShaderMap GeometryShaders; diff --git a/src/object/C4Def.cpp b/src/object/C4Def.cpp index 7290d2145..4fe22ecb9 100644 --- a/src/object/C4Def.cpp +++ b/src/object/C4Def.cpp @@ -19,6 +19,8 @@ #include #include +#include +#include #include #include @@ -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; };