Add ambient lighting

This introduces a new texture, an ambient light map, that is generated
automatically at the beginning of the round by the sky portion of the
landscape. This basically makes everything that is close to sky visible
by default.

The shaders have been adapted so that they deploy direction-independent
lighting for the ambient component, and the current (diffuse) behaviour
for the diffuse component. This makes the shaders use an additional
texture unit that represents the ambient light. We can think about merging
this information into the light texture, but the coordinate systems are
different at the moment, so this could be performed at the stage of light
texture generation.

For meshes, the ambient material is not actually used, but instead a
diffuse light from the front is used. This makes many meshes look more
interesting, maybe also because the ambient material setting of most
meshes are not set correctly at the moment.
issue1247
Armin Burgmeier 2014-11-17 09:35:50 -05:00
parent e64875d1c7
commit 88f8f75441
18 changed files with 355 additions and 78 deletions

View File

@ -291,6 +291,8 @@ set(OC_CLONK_SOURCES
src/gui/C4UpperBoard.h
src/landscape/fow/C4FoW.cpp
src/landscape/fow/C4FoW.h
src/landscape/fow/C4FoWAmbient.cpp
src/landscape/fow/C4FoWAmbient.h
src/landscape/fow/C4FoWBeam.cpp
src/landscape/fow/C4FoWBeam.h
src/landscape/fow/C4FoWLight.cpp

View File

@ -49,6 +49,11 @@
<literal_col>sampler2D</literal_col>
<col>Texture containing the light map. The red component of the map corresponds to the halfed light intensity, and the green and blue components the light direction in x-y, scaled by 1/3 and shifted by +1 in both directions. The fragment coordinates (<code>gl_FragCoord</code>) can be used as texture coordinates to look up the light information when transformed with the texture matrix of 1 plus the last texture unit. Therefore, if the pass uses one texture unit, the second texture unit will be used for the light texture, and the lookup can be performed with <code>texture2D(oc_Light, (gl_TextureMatrix[2] * gl_FragCoord).xy)</code>.</col>
</row>
<row>
<literal_col>oc_ambient</literal_col>
<literal_col>sampler2D</literal_col>
<col>Texture containing the ambient light map. The red component of the map corresponds to the intensity of ambient light. The texture contains only the red channel. The fragment coordinates (<code>gl_FragCoord</code>) can be used as texture coordinates to look up the ambient light information when transformed with the texture matrix of 2 plus the last texture unit. Therefore, if the pass uses one texture unit, the third texture unit will be used for the light texture, and the lookup can be performed with <code>texture2D(oc_Light, (gl_TextureMatrix[3] * gl_FragCoord).xy)</code>.</col>
</row>
</table>
</text>
<text>When a custom fragment shader is used, then that shader is responsible for applying the player color and color modulation to the object, since in that case the engine cannot take care of that. The additional parameters mentioned above can be used for that purpose. Also, when a custom fragment shader is used, the colour_op, colour_op_ex, alpha_op and alpha_op_ex directives in all texture units are ignored.</text>

View File

@ -4,6 +4,7 @@
// Input textures
uniform sampler2D landscapeTex[2];
uniform sampler2D lightTex;
uniform sampler2D ambientTex;
uniform sampler2D scalerTex;
uniform sampler3D materialTex;
@ -21,6 +22,9 @@ uniform float matMap[256];
uniform int materialDepth;
uniform vec2 materialSize;
// Factor between landscape coordinates and ambient map coordinates
uniform vec2 ambientScale;
// Expected parameters for the scaler
const vec2 scalerStepX = vec2(1.0 / 8.0, 0.0);
const vec2 scalerStepY = vec2(0.0, 1.0 / 32.0);
@ -119,7 +123,7 @@ void main()
// Brightness
vec4 lightPx = texture2D(lightTex, gl_TexCoord[1].st);
float ambientBright = lightPx.r, shadeBright = ambientBright;
float shadeBright = lightPx.r;
// Normal calculation
vec3 landscapeNormal = extend_normal(mix(realLandscapePx.yz, landscapePx.yz, scalerPx.a) - vec2(0.5, 0.5));
@ -128,16 +132,22 @@ void main()
vec3 normal = landscapeNormal + textureNormal;
vec3 normal2 = landscapeNormal2 + textureNormal;
// Light calculation
// Ambient light
float ambient = texture2D(ambientTex, ambientScale * texCoo).r;
float ambient2 = texture2D(ambientTex, ambientScale * otherCoo).r;
// Light calculation; the ambient part actually uses some shading with
// a light direction of (0.0, -1.0, 0.0) so that it does not look utterly
// boring at the sky/material edges.
vec3 lightDir = extend_normal(vec2(1.0, 1.0) - lightPx.yz * 3.0);
float bright = 2.0 * shadeBright * dot(normal, lightDir);
float bright2 = 2.0 * shadeBright * dot(normal2, lightDir);
float bright = ambient * (1.0 + 1.0 * dot(normal, vec3(0.0, -1.0, 0.0))) + (1.0 - ambient) * 2.0 * shadeBright * dot(normal, lightDir);
float bright2 = ambient2 * (1.0 + 1.0 * dot(normal2, vec3(0.0, -1.0, 0.0))) + (1.0 - ambient2) * 2.0 * shadeBright * dot(normal2, lightDir);
gl_FragColor = mix(
vec4(bright2 * otherMaterialPx.rgb, otherMaterialPx.a),
vec4(bright * materialPx.rgb, materialPx.a),
scalerPx.r);
// uncomment the following lines for debugging light directions:
// yellow: light up, blue: light down, turqoise: light right, pink: light left, opacity: light strength
//float lightYDir = lightPx.b - 1.0/3.0;

View File

@ -8,7 +8,7 @@ material Clonk_Body
ambient 0.500000 0.500000 0.500000 1.000000
diffuse 0.810000 0.810000 0.810000 1.000000
specular 0.000000 0.000000 0.000000 1.000000 3.000000
emissive 0.200000 0.200000 0.200000 1.000000
emissive 0.000000 0.000000 0.000000 1.000000
texture_unit Overlay
{

View File

@ -36,6 +36,7 @@ material NormalMap
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 basemap int 0
param_named normalmap int 1

View File

@ -6,28 +6,29 @@ uniform int oc_Mod2;
uniform vec4 oc_ColorModulation;
uniform int oc_UseLight;
uniform sampler2D oc_Light;
uniform sampler2D oc_Ambient;
// This is mostly copied from C4DrawMeshGL.cpp -- only the calculation
// of the normal has been replaced
void main()
{
vec3 lightDir;
float lightIntensity;
vec3 normal = normalize((texture2D(normalmap, texcoord).rgb * 2.0 - 1.0)); // TODO: This might be normalized already
vec4 diffuse;
if(oc_UseLight != 0)
{
vec4 lightPx = texture2D(oc_Light, (gl_TextureMatrix[2] * gl_FragCoord).xy);
lightDir = normalize(vec3(vec2(1.0, 1.0) - lightPx.gb * 3.0, 0.3));
lightIntensity = 2.0 * lightPx.r;
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[3] * gl_FragCoord).xy).r;
diffuse = ambient * (gl_FrontMaterial.emission + gl_FrontMaterial.diffuse * max(dot(normalize(normal), vec3(0.0, 0.0, 1.0)), 0.0)) + (1.0 - ambient) * lightIntensity * (gl_FrontMaterial.emission + gl_FrontMaterial.diffuse * max(dot(normalize(normal), lightDir), 0.0));
}
else
{
lightDir = vec3(0.0, 0.0, 1.0);
lightIntensity = 1.0;
vec3 lightDir = vec3(0.0, 0.0, 1.0);
diffuse = gl_FrontMaterial.emission + gl_FrontMaterial.diffuse * max(dot(normal, lightDir), 0.0);
}
vec3 normal = normalize((texture2D(normalmap, texcoord).rgb * 2.0 - 1.0)); // TODO: This might be normalized already
vec4 diffuse = gl_FrontMaterial.emission + 0.0 * gl_FrontMaterial.ambient + gl_FrontMaterial.diffuse * max(dot(normalize(normal), lightDir), 0.0);
// Modulate with base texture
vec4 finalColor = diffuse * texture2D(basemap, texcoord);

View File

@ -301,6 +301,7 @@ void CStdGL::SetupMultiBlt(const C4BltTransform* pTransform, GLuint baseTex, GLu
GLint clrModLocation = glGetUniformLocationARB(multi_blt_program->Program, "clrMod");
GLint overlayClrModLocation = glGetUniformLocationARB(multi_blt_program->Program, "overlayClrMod");
GLint lightLocation = glGetUniformLocationARB(multi_blt_program->Program, "Light");
GLint ambientLocation = glGetUniformLocationARB(multi_blt_program->Program, "Ambient");
GLint textureLocation = glGetUniformLocationARB(multi_blt_program->Program, "Texture");
GLint overlayLocation = glGetUniformLocationARB(multi_blt_program->Program, "Overlay");
GLint normalLocation = glGetUniformLocationARB(multi_blt_program->Program, "Normal");
@ -334,31 +335,49 @@ void CStdGL::SetupMultiBlt(const C4BltTransform* pTransform, GLuint baseTex, GLu
if(fUseNormal)
{
glActiveTexture(GL_TEXTURE3);
glActiveTexture(GL_TEXTURE4);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, normalTex);
glUniform1iARB(normalLocation, 3);
glUniform1iARB(normalLocation, 4);
}
if(fUseLight)
{
const C4Rect LightRect = pFoW->getRegion();
const int32_t iLightWdt = pFoW->getSurface()->Wdt;
const int32_t iLightHgt = pFoW->getSurface()->Hgt;
const int iVpWdt=Min(iClipX2, RenderTarget->Wdt-1)-iClipX1+1;
const int iVpHgt=Min(iClipY2, RenderTarget->Hgt-1)-iClipY1+1;
glMatrixMode(GL_TEXTURE);
// Ambient texture
glActiveTexture(GL_TEXTURE3);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, pFoW->getFoW()->Ambient.Tex);
glUniform1iARB(ambientLocation, 3);
// 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); // TODO: LightRect should have floating point accuracy for this to work best
glScalef( (float)LightRect.Wdt / (float)iVpWdt, (float)LightRect.Hgt / (float)iVpHgt, 1.0f);
glTranslatef(0.0f, iVpHgt, 0.0f);
glScalef(1.0f, -1.0f, 1.0f);
// Light texture
glActiveTexture(GL_TEXTURE2);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, pFoW->getSurface()->ppTex[0]->texName);
glUniform1iARB(lightLocation, 2);
// Setup the texture matrix such that fragment coordinates
// are converted to coordinates inside the light texture.
glMatrixMode(GL_TEXTURE);
// Setup the texture matrix
glLoadIdentity();
const C4Rect LightRect = pFoW->getRegion();
const int32_t iLightWdt = pFoW->getSurface()->Wdt;
const int32_t iLightHgt = pFoW->getSurface()->Hgt;
glTranslatef(0.0f, 1.0f - (float)LightRect.Hgt/(float)iLightHgt, 0.0f);
glScalef(1.0f/iLightWdt, 1.0f/iLightHgt, 1.0f);
glScalef(1.0f/Zoom, 1.0f/Zoom, 1.0f);
glScalef( (float)LightRect.Wdt / (float)iVpWdt, (float)LightRect.Hgt / (float)iVpHgt, 1.0f);
glMatrixMode(GL_MODELVIEW);
}
@ -395,8 +414,8 @@ void CStdGL::SetupMultiBlt(const C4BltTransform* pTransform, GLuint baseTex, GLu
void CStdGL::ResetMultiBlt(GLuint baseTex, GLuint overlayTex, GLuint normalTex)
{
glPopMatrix();
if(normalTex != 0) { glActiveTexture(GL_TEXTURE3); glDisable(GL_TEXTURE_2D); }
if(pFoW != NULL) { glActiveTexture(GL_TEXTURE2); glDisable(GL_TEXTURE_2D); }
if(normalTex != 0) { glActiveTexture(GL_TEXTURE4); glDisable(GL_TEXTURE_2D); }
if(pFoW != NULL) { glActiveTexture(GL_TEXTURE3); glDisable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE2); glDisable(GL_TEXTURE_2D); }
if(overlayTex != 0) { glActiveTexture(GL_TEXTURE1); glDisable(GL_TEXTURE_2D); }
glActiveTexture(GL_TEXTURE0);
if(baseTex != 0) glDisable(GL_TEXTURE_2D);
@ -568,7 +587,6 @@ bool CStdGL::RestoreDeviceObjects()
const char * linedata = byByteCnt == 2 ? "\xff\xf0\xff\xff" : "\xff\xff\xff\x00\xff\xff\xff\xff";
glTexImage2D(GL_TEXTURE_2D, 0, 4, 1, 2, 0, GL_BGRA, byByteCnt == 2 ? GL_UNSIGNED_SHORT_4_4_4_4_REV : GL_UNSIGNED_INT_8_8_8_8_REV, linedata);
MaxTexSize = 64;
GLint s = 0;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &s);
@ -606,6 +624,7 @@ bool CStdGL::RestoreDeviceObjects()
"uniform sampler2D Texture;"
"uniform sampler2D Overlay;"
"uniform sampler2D Light;"
"uniform sampler2D Ambient;"
"uniform sampler2D Normal;"
"varying vec2 texcoord;"
"void main()"
@ -624,41 +643,30 @@ bool CStdGL::RestoreDeviceObjects()
// Add alpha for base and overlay, and use weighted mean of clrmod alpha
" primaryColor.a = clamp(primaryColor.a + overlayColor.a, 0.0, 1.0) * (primaryColor.a * clrMod.a + overlayColor.a * overlayClrMod.a) / (primaryColor.a + overlayColor.a);"
// Add fog of war (light)
" vec3 lightDir, normalDir;"
" float lightIntensity;"
" vec3 lightClr = vec3(1.0, 1.0, 1.0);"
" if(fUseLight != 0)"
" {"
" vec4 lightPx = texture2D(Light, (gl_TextureMatrix[2] * gl_FragCoord).xy);"
" vec3 lightDir = normalize(vec3(vec2(1.0, 1.0) - lightPx.gb * 3.0, 0.3));"
" float lightIntensity = 2.0 * lightPx.r;"
" vec3 normalDir;"
" if(fUseNormal != 0)"
" {"
" lightDir = normalize(vec3(vec2(1.0, 1.0) - lightPx.gb * 3.0, 0.3));"
// Overwrite Z component of normal with 0.3 -- might make this configurable later
" vec4 normalPx = texture2D(Normal, texcoord);"
" normalDir = normalize(vec3( (normalPx.xy - vec2(0.5, 0.5))*2.0, 0.3));"
" lightIntensity = 2.0 * lightPx.r;"
" }"
" else"
" {"
// No normal map -- front-facing light and normals, only take light intensity into account
" lightDir = vec3(0.0, 0.0, 1.0);"
" normalDir = vec3(0.0, 0.0, 1.0);"
" lightIntensity = lightPx.r;"
" }"
" float ambient = texture2D(Ambient, (gl_TextureMatrix[3] * gl_FragCoord).xy).r;"
" lightClr = ambient * lightClr + (1.0 - ambient) * vec3(1.0, 1.0, 1.0) * lightIntensity * (0.25 + 0.75 * dot(normalDir, lightDir));"
" }"
" else"
" {"
// No light -- place a simple directional light from the front (equivalent to the behaviour in the main branch, modulo interpolated normals)
" lightDir = vec3(0.0, 0.0, 1.0);"
" normalDir = vec3(0.0, 0.0, 1.0);"
" lightIntensity = 1.0;"
" }"
" vec4 lightClr = vec4(vec3(1.0, 1.0, 1.0) * lightIntensity * dot(normalDir, lightDir), 1.0);"
// Final output, depending on blit mode
" if(fMod2 != 0)"
" gl_FragColor = clamp(2.0 * primaryColor * lightClr - 0.5, 0.0, 1.0);"
" gl_FragColor = clamp(2.0 * primaryColor * vec4(lightClr, 1.0) - 0.5, 0.0, 1.0);"
" else"
" gl_FragColor = clamp(primaryColor * lightClr, 0.0, 1.0);"
" gl_FragColor = clamp(primaryColor * vec4(lightClr, 1.0), 0.0, 1.0);"
"}";
C4DrawGLShader vertex_shader(StdMeshMaterialShader::VERTEX);

View File

@ -147,6 +147,7 @@ namespace
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;
return FormatString(
"varying vec3 normal;" // linearly interpolated -- not necessarily normalized
@ -157,29 +158,32 @@ namespace
"uniform int oc_Mod2;"
"uniform int oc_UseLight;"
"uniform sampler2D oc_Light;"
"uniform sampler2D oc_Ambient;"
"void main()"
"{"
" vec3 lightDir;"
" float lightIntensity;"
" vec4 diffuse;"
" if(oc_UseLight != 0)"
" {"
// Light calculation
" vec4 lightPx = texture2D(oc_Light, (gl_TextureMatrix[%d] * gl_FragCoord).xy);"
" lightDir = normalize(vec3(vec2(1.0, 1.0) - lightPx.gb * 3.0, 0.3));"
" lightIntensity = 2.0 * lightPx.r;"
" 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;"
// 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
" diffuse = ambient * (gl_FrontMaterial.emission + gl_FrontMaterial.diffuse * (0.25 + 0.75 * max(dot(normalize(normal), vec3(0.0, 0.0, 1.0)), 0.0))) + (1.0 - ambient) * lightIntensity * (gl_FrontMaterial.emission + gl_FrontMaterial.diffuse * (0.25 + 0.75 * max(dot(normalize(normal), lightDir), 0.0)));"
" }"
" else"
" {"
// No light -- place a simple directional light from the front (equivalent to the behaviour in the main branch, modulo interpolated normals)
" lightDir = vec3(0.0, 0.0, 1.0);"
" lightIntensity = 1.0;"
// 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);"
" diffuse = gl_FrontMaterial.emission + gl_FrontMaterial.diffuse * max(dot(normalize(normal), lightDir), 0.0);"
" }"
" vec4 diffuse = gl_FrontMaterial.emission + 0.0 * gl_FrontMaterial.ambient + gl_FrontMaterial.diffuse * max(dot(normalize(normal), lightDir), 0.0);" // ambient is ignored since we don't have ambient lights
// Texture units from material script
" vec4 currentColor = diffuse;"
" %s"
// Intensity (lightPx.r) only applies to RGB components, not A
" currentColor = vec4(lightIntensity * currentColor.rgb, currentColor.a);"
// Output with color modulation and mod2
" if(oc_Mod2 != 0)"
" gl_FragColor = clamp(2.0 * currentColor * oc_ColorModulation - 0.5, 0.0, 1.0);"
@ -187,7 +191,7 @@ namespace
" gl_FragColor = clamp(currentColor * oc_ColorModulation, 0.0, 1.0);"
"}",
textureUnitDeclCode.getData(),
(int)texIndex, // The light texture is added after all other textures
(int)texIndex, (int)texIndex + 1, // The light and ambient textures are added after all other textures
textureUnitCode.getData()
);
}
@ -481,7 +485,7 @@ namespace
return true;
}
bool ResolveAutoParameter(StdMeshMaterialShaderParameter& parameter, StdMeshMaterialShaderParameter::Auto value, DWORD dwModClr, DWORD dwPlayerColor, DWORD dwBlitMode, const C4FoWRegion* pFoW, float zoom, std::vector<GLint>& textures)
bool ResolveAutoParameter(StdMeshMaterialShaderParameter& parameter, StdMeshMaterialShaderParameter::Auto value, DWORD dwModClr, DWORD dwPlayerColor, DWORD dwBlitMode, const C4FoWRegion* pFoW, const C4Rect& clipRect, std::vector<GLint>& textures)
{
float* out;
GLint texIndex;
@ -524,7 +528,7 @@ namespace
// Load the texture
glActiveTexture(GL_TEXTURE0+texIndex);
glClientActiveTexture(GL_TEXTURE0+texIndex);
//glClientActiveTexture(GL_TEXTURE0+texIndex);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, pFoW->getSurface()->ppTex[0]->texName);
@ -537,7 +541,36 @@ namespace
glLoadIdentity();
glTranslatef(0.0f, 1.0f - (float)LightRect.Hgt/(float)iLightHgt, 0.0f);
glScalef(1.0f/iLightWdt, 1.0f/iLightHgt, 1.0f);
glScalef(1.0f/zoom, 1.0f/zoom, 1.0f);
glScalef( (float)LightRect.Wdt / (float)clipRect.Wdt, (float)LightRect.Hgt / (float)clipRect.Hgt, 1.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);
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(0.0f, clipRect.Hgt, 0.0f);
glScalef(1.0f, -1.0f, 1.0f);
parameter.SetType(StdMeshMaterialShaderParameter::INT);
parameter.GetInt() = texIndex;
@ -548,7 +581,7 @@ namespace
}
}
void RenderSubMeshImpl(const StdMeshInstance& mesh_instance, const StdSubMeshInstance& instance, DWORD dwModClr, DWORD dwBlitMode, DWORD dwPlayerColor, const C4FoWRegion* pFoW, float zoom, bool parity)
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();
assert(material.BestTechniqueIndex != -1);
@ -720,7 +753,7 @@ namespace
StdMeshMaterialShaderParameter auto_resolved;
if(parameter->GetType() == StdMeshMaterialShaderParameter::AUTO)
{
if(!ResolveAutoParameter(auto_resolved, parameter->GetAuto(), dwModClr, dwPlayerColor, dwBlitMode, pFoW, zoom, textures))
if(!ResolveAutoParameter(auto_resolved, parameter->GetAuto(), dwModClr, dwPlayerColor, dwBlitMode, pFoW, clipRect, textures))
continue;
parameter = &auto_resolved;
}
@ -768,9 +801,9 @@ namespace
}
}
void RenderMeshImpl(StdMeshInstance& instance, DWORD dwModClr, DWORD dwBlitMode, DWORD dwPlayerColor, const C4FoWRegion* pFoW, float zoom, bool parity); // Needed by RenderAttachedMesh
void RenderMeshImpl(StdMeshInstance& instance, DWORD dwModClr, DWORD dwBlitMode, DWORD dwPlayerColor, const C4FoWRegion* pFoW, const C4Rect& clipRect, bool parity); // Needed by RenderAttachedMesh
void RenderAttachedMesh(StdMeshInstance::AttachedMesh* attach, DWORD dwModClr, DWORD dwBlitMode, DWORD dwPlayerColor, const C4FoWRegion* pFoW, float zoom, bool parity)
void RenderAttachedMesh(StdMeshInstance::AttachedMesh* attach, DWORD dwModClr, DWORD dwBlitMode, DWORD dwPlayerColor, const C4FoWRegion* pFoW, const C4Rect& clipRect, bool parity)
{
const StdMeshMatrix& FinalTrans = attach->GetFinalTransformation();
@ -797,7 +830,7 @@ namespace
// TODO: Take attach transform's parity into account
glPushMatrix();
glMultMatrixf(attach_trans_gl);
RenderMeshImpl(*attach->Child, dwModClr, dwBlitMode, dwPlayerColor, pFoW, zoom, parity);
RenderMeshImpl(*attach->Child, dwModClr, dwBlitMode, dwPlayerColor, pFoW, clipRect, parity);
glPopMatrix();
#if 0
@ -817,7 +850,7 @@ namespace
#endif
}
void RenderMeshImpl(StdMeshInstance& instance, DWORD dwModClr, DWORD dwBlitMode, DWORD dwPlayerColor, const C4FoWRegion* pFoW, float zoom, bool parity)
void RenderMeshImpl(StdMeshInstance& instance, DWORD dwModClr, DWORD dwBlitMode, DWORD dwPlayerColor, const C4FoWRegion* pFoW, const C4Rect& clipRect, bool parity)
{
const StdMesh& mesh = instance.GetMesh();
@ -825,7 +858,7 @@ namespace
StdMeshInstance::AttachedMeshIter attach_iter = instance.AttachedMeshesBegin();
for (; attach_iter != instance.AttachedMeshesEnd() && ((*attach_iter)->GetFlags() & StdMeshInstance::AM_DrawBefore); ++attach_iter)
RenderAttachedMesh(*attach_iter, dwModClr, dwBlitMode, dwPlayerColor, pFoW, zoom, parity);
RenderAttachedMesh(*attach_iter, dwModClr, dwBlitMode, dwPlayerColor, pFoW, clipRect, parity);
GLint modes[2];
// Check if we should draw in wireframe or normal mode
@ -838,7 +871,7 @@ namespace
// Render each submesh
for (unsigned int i = 0; i < mesh.GetNumSubMeshes(); ++i)
RenderSubMeshImpl(instance, instance.GetSubMeshOrdered(i), dwModClr, dwBlitMode, dwPlayerColor, pFoW, zoom, parity);
RenderSubMeshImpl(instance, instance.GetSubMeshOrdered(i), dwModClr, dwBlitMode, dwPlayerColor, pFoW, clipRect, parity);
// reset old mode to prevent rendering errors
if(dwBlitMode & C4GFXBLIT_WIREFRAME)
@ -868,7 +901,7 @@ namespace
// Render non-AM_DrawBefore attached meshes
for (; attach_iter != instance.AttachedMeshesEnd(); ++attach_iter)
RenderAttachedMesh(*attach_iter, dwModClr, dwBlitMode, dwPlayerColor, pFoW, zoom, parity);
RenderAttachedMesh(*attach_iter, dwModClr, dwBlitMode, dwPlayerColor, pFoW, clipRect, parity);
}
}
@ -1082,7 +1115,12 @@ void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float tw
DWORD dwModClr = BlitModulated ? BlitModulateClr : 0xffffffff;
RenderMeshImpl(instance, dwModClr, dwBlitMode, dwPlayerColor, pFoW, Zoom, parity);
C4Rect clipRect;
clipRect.Wdt = Min(iClipX2, RenderTarget->Wdt-1)-iClipX1+1;
clipRect.Hgt = Min(iClipY2, RenderTarget->Hgt-1)-iClipY1+1;
clipRect.x = iClipX1; if(clipRect.x < 0) { clipRect.Wdt += clipRect.x; clipRect.x = 0; }
clipRect.y = iClipY1; if(clipRect.y < 0) { clipRect.Hgt += clipRect.y; clipRect.y = 0; }
RenderMeshImpl(instance, dwModClr, dwBlitMode, dwPlayerColor, pFoW, clipRect, parity);
glUseProgramObjectARB(0);

View File

@ -1306,7 +1306,7 @@ bool C4Landscape::Init(C4Group &hGroup, bool fOverloadCurrent, bool fLoadSky, bo
}
// progress
Game.SetInitProgress(80);
Game.SetInitProgress(75);
// copy noscan-var
NoScan=Game.C4S.Landscape.NoScan!=0;
@ -1338,7 +1338,7 @@ bool C4Landscape::Init(C4Group &hGroup, bool fOverloadCurrent, bool fLoadSky, bo
// Init out-of-landscape pixels for bottom
InitTopAndBottomRowPix();
Game.SetInitProgress(84);
Game.SetInitProgress(80);
if (Config.General.DebugRec)
{
@ -1404,6 +1404,9 @@ bool C4Landscape::Init(C4Group &hGroup, bool fOverloadCurrent, bool fLoadSky, bo
// and not creating the map
Game.FixRandom(Game.RandomSeed);
// Create ambient light map after landscape creation
pFoW->Ambient.CreateFromLandscape(*this, 10., 50., 0.25);
Game.SetInitProgress(84);
// Success
rfLoaded=true;

View File

@ -52,12 +52,14 @@ static const char *GetUniformName(int iUniform)
case C4LRU_ScalerTex: return "scalerTex";
case C4LRU_MaterialTex: return "materialTex";
case C4LRU_LightTex: return "lightTex";
case C4LRU_AmbientTex: return "ambientTex";
case C4LRU_Resolution: return "resolution";
case C4LRU_Center: return "center";
case C4LRU_MatMap: return "matMap";
case C4LRU_MatMapTex: return "matMapTex";
case C4LRU_MaterialDepth:return "materialDepth";
case C4LRU_MaterialSize: return "materialSize";
case C4LRU_AmbientScale: return "ambientScale";
}
assert(false);
return "mysterious";
@ -1011,7 +1013,16 @@ void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo, const C4FoWRegion &Ligh
if (hUniforms[C4LRU_MaterialSize] != GLhandleARB(-1))
glUniform2fARB(hUniforms[C4LRU_MaterialSize], float(iMaterialWidth) / ::Game.C4S.Landscape.MaterialZoom,
float(iMaterialHeight) / ::Game.C4S.Landscape.MaterialZoom);
if (hUniforms[C4LRU_AmbientScale] != GLhandleARB(-1))
{
// factor between actual landscape size and surface size, so that we can use the landscape
// coordinates for the ambient map lookup.
// TODO: These could actually be shader constants, and do not really need to be uniforms...
const float sx = static_cast<float>(Surfaces[0]->Wdt) / iWidth;
const float sy = static_cast<float>(Surfaces[0]->Hgt) / iHeight;
glUniform2fARB(hUniforms[C4LRU_AmbientScale], sx, sy);
}
// Setup facilities for texture unit allocation (gimme local functions...)
int iUnit = 0; int iUnitMap[32]; ZeroMem(iUnitMap, sizeof(iUnitMap));
#define ALLOC_UNIT(hUniform, iType) do { \
@ -1052,6 +1063,13 @@ void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo, const C4FoWRegion &Ligh
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
if(hUniforms[C4LRU_AmbientTex] != GLhandleARB(-1))
{
ALLOC_UNIT(hUniforms[C4LRU_AmbientTex], GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, Light.getFoW()->Ambient.Tex);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
if(hUniforms[C4LRU_ScalerTex] != GLhandleARB(-1))
{
ALLOC_UNIT(hUniforms[C4LRU_ScalerTex], GL_TEXTURE_2D);

View File

@ -24,6 +24,7 @@ enum C4LR_Uniforms
C4LRU_ScalerTex,
C4LRU_MaterialTex,
C4LRU_LightTex,
C4LRU_AmbientTex,
C4LRU_Resolution,
C4LRU_Center,
@ -31,6 +32,7 @@ enum C4LR_Uniforms
C4LRU_MatMapTex,
C4LRU_MaterialDepth,
C4LRU_MaterialSize,
C4LRU_AmbientScale,
C4LRU_Count
};

View File

@ -39,7 +39,6 @@ void C4FoW::Add(C4Object *pObj)
pLight->pNext = pLights;
pLights = pLight;
}
}
void C4FoW::Remove(C4Object *pObj)
@ -73,4 +72,4 @@ void C4FoW::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen)
{
for (C4FoWLight *pLight = pLights; pLight; pLight = pLight->getNext())
pLight->Render(pRegion, pOnScreen);
}
}

View File

@ -7,6 +7,7 @@
#include "C4Rect.h"
#include "C4Object.h"
#include "C4FoWLight.h"
#include "C4FoWAmbient.h"
/**
This class holds all lights for the objects. It forwards the update, invalidation and render calls each to the
@ -22,6 +23,8 @@ private:
class C4FoWLight *pLights;
public:
C4FoWAmbient Ambient;
void Clear();
/** Updates the view range of the given object in its associated light or create a new light if none exists yet. */
@ -37,4 +40,4 @@ public:
void Render(class C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen = NULL);
};
#endif // C4FOW_H
#endif // C4FOW_H

View File

@ -0,0 +1,142 @@
#include "C4Include.h"
#include "C4FoWAmbient.h"
namespace
{
template<typename IFT>
double AmbientForPix(int x0, int y0, int R, const IFT& ift)
{
double d = 0.;
for(int y = y0 - R; y <= y0 + R; ++y)
{
for(int x = x0 - R; x <= x0 + R; ++x)
{
const double l2 = (x - x0) * (x - x0) + (y - y0) * (y - y0);
if(l2 > R*R || ift(x, y)) continue;
if(x == x0 && y == y0)
{
// Central pixel, this corresponds to the integral
// for a round pixel with the same area as a square pixel
d += 2 * sqrt(M_PI);
}
else
{
// Non-central pixel; assume same weight over the full
// pixel.
d += 1. / sqrt(l2);
}
}
}
return d;
}
// Everything is sky, independent of the landscape
// This is used to obtain the normalization factor
struct IFTFull {
IFTFull() {}
bool operator()(int x, int y) const { return false; }
};
struct IFTZoom {
IFTZoom(const C4Landscape& landscape, double sx, double sy):
Landscape(landscape), sx(sx), sy(sy) {}
// Returns whether zoomed coordinate is IFT or not
bool operator()(int x, int y) const
{
// Landscape coordinates
const int lx = static_cast<int>((x + 0.5) * sx);
const int ly = static_cast<int>((y + 0.5) * sy);
// IFT check
//return (Landscape.GetPix(lx, ly) & IFT) != 0;
return Landscape.GetPix(lx, ly) != 0;
}
const C4Landscape& Landscape;
const double sx;
const double sy;
};
} // anonymous namespace
C4FoWAmbient::C4FoWAmbient()
: Tex(0), SizeX(0), SizeY(0), LandscapeX(0), LandscapeY(0)
{
}
C4FoWAmbient::~C4FoWAmbient()
{
Clear();
}
void C4FoWAmbient::Clear()
{
if(Tex != 0) glDeleteTextures(1, &Tex);
Tex = 0;
SizeX = SizeY = 0;
LandscapeX = LandscapeY = 0;
}
void C4FoWAmbient::CreateFromLandscape(const C4Landscape& landscape, double resolution, double radius, double full_coverage)
{
// Clear old map
if(Tex != 0) Clear();
// Number of zoomed pixels
LandscapeX = landscape.Width;
LandscapeY = landscape.Height;
SizeX = Min<unsigned int>(static_cast<unsigned int>(ceil(LandscapeX / resolution)), pDraw->MaxTexSize);
SizeY = Min<unsigned int>(static_cast<unsigned int>(ceil(LandscapeY / resolution)), pDraw->MaxTexSize);
// Factor to go from zoomed to landscape coordinates
const double zoom_x = static_cast<double>(landscape.Width) / (float)(SizeX);
const double zoom_y = static_cast<double>(landscape.Height) / (float)(SizeY);
// Zoomed radius
const double R = (radius / sqrt(zoom_x * zoom_y) + 0.5);
// Normalization factor with the full circle
// The analytic result is 2*R*M_PI, and this number is typically close to it
const double norm = AmbientForPix(0, 0, R, IFTFull()) * full_coverage;
// Create the ambient map
IFTZoom iftZoom(landscape, zoom_x, zoom_y);
float* ambient = new float[SizeX * SizeY];
for(unsigned int y = 0; y < SizeY; ++y)
{
for(unsigned int x = 0; x < SizeX; ++x)
{
ambient[y * SizeX + x] = Min(AmbientForPix(x, y, R, iftZoom) / norm, 1.0);
}
}
#if 0
CSurface8 debug(SizeX, SizeY);
for(unsigned int y = 0; y < SizeY; ++y)
{
for(unsigned int x = 0; x < SizeX; ++x)
{
debug.SetPix(x, y, int(ambient[y * SizeX + x] * 255. + 0.5));
CStdPalette pal;
for(int i = 0; i < 256; ++i)
pal.Colors[i] = i + (i << 8) + (i << 16);
debug.Save("Ambient.bmp", &pal);
}
}
#endif
// Store it in a GL texture
glGenTextures(1, &Tex);
glBindTexture(GL_TEXTURE_2D, Tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, 1, SizeX, SizeY, 0, GL_RED, GL_FLOAT, ambient);
delete[] ambient;
LogF("Created %ux%u ambient map", SizeX, SizeY);
}

View File

@ -0,0 +1,42 @@
#ifndef C4FOWAMBIENT_H
#define C4FOWAMBIENT_H
//#include <GL/gl.h>
#include <C4Landscape.h>
/**
This class manages a texture that holds the ambient light intensity
*/
class C4FoWAmbient
{
public:
C4FoWAmbient();
~C4FoWAmbient();
GLuint Tex;
private:
// Landscape size
unsigned int LandscapeX;
unsigned int LandscapeY;
// Size of ambient map
unsigned int SizeX;
unsigned int SizeY;
public:
void Clear();
// High resolution will make the map coarser, but speed up the generation process
// and save video memory.
// The radius specifies the radius of landscape pixels that are sampled around each pixel
// to obtain the ambient light.
// full_coverage is a number between 0 and 1, and it specifies what portion of the full circle
// needs to be illuminated for full ambient light intensity.
void CreateFromLandscape(const C4Landscape& landscape, double resolution, double radius, double full_coverage);
unsigned int GetLandscapeWidth() const { return LandscapeX; }
unsigned int GetLandscapeHeight() const { return LandscapeY; }
};
#endif // C4FOWAMBIENT_H

View File

@ -21,6 +21,7 @@ private:
GLuint hFrameBufDraw, hFrameBufRead;
public:
const C4FoW* getFoW() const { return pFoW; }
const C4Rect &getRegion() const { return Region; }
const C4Surface *getSurface() const { return pSurface; }
const C4Surface *getBackSurface() const { return pBackSurface; }

View File

@ -61,6 +61,7 @@ namespace
{ "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 },
{ NULL, static_cast<StdMeshMaterialShaderParameter::Auto>(0) }
};

View File

@ -59,7 +59,8 @@ public:
AUTO_OC_COLOR_MODULATION,
AUTO_OC_MOD2,
AUTO_OC_USE_LIGHT,
AUTO_OC_LIGHT
AUTO_OC_LIGHT,
AUTO_OC_AMBIENT
// TODO: Other ogre auto values
};