Index: ps/trunk/binaries/data/mods/public/shaders/arb/water_simple.fp =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/water_simple.fp (nonexistent) +++ ps/trunk/binaries/data/mods/public/shaders/arb/water_simple.fp (revision 24129) @@ -0,0 +1,17 @@ +!!ARBfp1.0 + +PARAM color = program.local[2]; + +ATTRIB v_coords = fragment.texcoord[0]; +ATTRIB v_losCoords = fragment.texcoord[1]; + +TEMP diffuse; +TEX diffuse, v_coords, texture[0], 2D; +MUL diffuse, diffuse, color; + +TEMP los; +TEX los, v_losCoords, texture[1], 2D; +MUL diffuse, diffuse, los.a; + +MOV result.color, diffuse; +END Property changes on: ps/trunk/binaries/data/mods/public/shaders/arb/water_simple.fp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/shaders/arb/water_simple.vp =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/water_simple.vp (nonexistent) +++ ps/trunk/binaries/data/mods/public/shaders/arb/water_simple.vp (revision 24129) @@ -0,0 +1,38 @@ +!!ARBvp1.0 + +ATTRIB position = vertex.position; + +PARAM transform[4] = { program.local[0..3] }; +PARAM losMatrix[4] = { program.local[4..7] }; +PARAM time = program.local[8]; + +OUTPUT v_coords = result.texcoord[0]; +OUTPUT v_losCoords = result.texcoord[1]; + +DP4 result.position.x, transform[0], position; +DP4 result.position.y, transform[1], position; +DP4 result.position.z, transform[2], position; +DP4 result.position.w, transform[3], position; + +TEMP tx; +MUL tx, time, -0.01235; // 1.0 / 81.0 +FRC tx, tx; +TEMP tz; +MUL tz, time, -0.02941; // 1.0 / 34.0 +FRC tz, tz; +TEMP offset; +MOV offset.x, tx; +MOV offset.z, tz; + +TEMP coords; +// Divide by period 16 and add offset. +MAD coords, position, 0.0625, offset; +ADD coords, coords, offset; +MOV v_coords, coords.xzxz; + +TEMP losCoords; +DP4 losCoords.x, losMatrix[0], position; +DP4 losCoords.y, losMatrix[1], position; +MOV v_losCoords, losCoords.xyxy; + +END Property changes on: ps/trunk/binaries/data/mods/public/shaders/arb/water_simple.vp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/shaders/arb/water_simple.xml =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/arb/water_simple.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/shaders/arb/water_simple.xml (revision 24129) @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + Property changes on: ps/trunk/binaries/data/mods/public/shaders/arb/water_simple.xml ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/shaders/effects/water_simple.xml =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/effects/water_simple.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/shaders/effects/water_simple.xml (revision 24129) @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + Property changes on: ps/trunk/binaries/data/mods/public/shaders/effects/water_simple.xml ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/shaders/glsl/water_high.fs =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/glsl/water_high.fs (revision 24128) +++ ps/trunk/binaries/data/mods/public/shaders/glsl/water_high.fs (revision 24129) @@ -1,362 +1,361 @@ #version 110 // Environment settings uniform vec3 ambient; uniform vec3 sunDir; uniform vec3 sunColor; uniform mat4 skyBoxRot; uniform vec3 cameraPos; uniform sampler2D losMap; uniform float waviness; // "Wildness" of the reflections and refractions; choose based on texture uniform vec3 color; // color of the water uniform vec3 tint; // Tint for refraction (used to simulate particles in water) uniform float murkiness; // Amount of tint to blend in with the refracted color uniform float windAngle; varying vec2 WindCosSin; uniform vec3 fogColor; uniform vec2 fogParams; uniform vec2 screenSize; -uniform float time; varying float moddedTime; varying vec3 worldPos; varying float waterDepth; varying vec2 waterInfo; varying vec3 v_eyeVec; varying vec4 normalCoords; #if USE_REFLECTION varying vec3 reflectionCoords; #endif #if USE_REFRACTION varying vec3 refractionCoords; #endif varying vec2 losCoords; varying float fwaviness; uniform samplerCube skyCube; uniform sampler2D normalMap; uniform sampler2D normalMap2; #if USE_FANCY_EFFECTS uniform sampler2D waterEffectsTex; #endif uniform vec4 waveParams1; // wavyEffect, BaseScale, Flattenism, Basebump uniform vec4 waveParams2; // Smallintensity, Smallbase, Bigmovement, Smallmovement #if USE_REFLECTION uniform sampler2D reflectionMap; #endif #if USE_REFRACTION uniform sampler2D refractionMap; #endif #if USE_REAL_DEPTH uniform sampler2D depthTex; uniform float zNear; uniform float zFar; #endif #if USE_SHADOWS_ON_WATER && USE_SHADOW varying vec4 v_shadow; #if USE_SHADOW_SAMPLER uniform sampler2DShadow shadowTex; #if USE_SHADOW_PCF uniform vec4 shadowScale; #endif #else uniform sampler2D shadowTex; #endif float get_shadow(vec4 coords) { #if USE_SHADOWS_ON_WATER && !DISABLE_RECEIVE_SHADOWS #if USE_SHADOW_SAMPLER #if USE_SHADOW_PCF vec2 offset = fract(coords.xy - 0.5); vec4 size = vec4(offset + 1.0, 2.0 - offset); vec4 weight = (vec4(1.0, 1.0, -0.5, -0.5) + (coords.xy - 0.5 * offset).xyxy) * shadowScale.zwzw; return (1.0 / 9.0) * dot(size.zxzx * size.wwyy, vec4(shadow2D(shadowTex, vec3(weight.zw, coords.z)).r, shadow2D(shadowTex, vec3(weight.xw, coords.z)).r, shadow2D(shadowTex, vec3(weight.zy, coords.z)).r, shadow2D(shadowTex, vec3(weight.xy, coords.z)).r)); #else return shadow2D(shadowTex, coords.xyz).r; #endif #else if (coords.z >= 1.0) return 1.0; return (coords.z <= texture2D(shadowTex, coords.xy).x ? 1.0 : 0.0); #endif #else return 1.0; #endif } #endif // TODO: convert this to something not only for AABBs struct Ray { vec3 Origin; vec3 Direction; }; float IntersectBox (in Ray ray, in vec3 minimum, in vec3 maximum) { vec3 OMIN = ( minimum - ray.Origin ) / ray.Direction; vec3 OMAX = ( maximum - ray.Origin ) / ray.Direction; vec3 MAX = max ( OMAX, OMIN ); return min ( MAX.x, min ( MAX.y, MAX.z ) ); } vec3 get_fog(vec3 color) { float density = fogParams.x; float maxFog = fogParams.y; const float LOG2 = 1.442695; float z = gl_FragCoord.z / gl_FragCoord.w; float fogFactor = exp2(-density * density * z * z * LOG2); fogFactor = fogFactor * (1.0 - maxFog) + maxFog; fogFactor = clamp(fogFactor, 0.0, 1.0); return mix(fogColor, color, fogFactor); } vec4 getReflection(vec3 normal) { // Reflections // 3 level of settings: // -If a player has refraction and reflection disabled, we return a gradient of blue based on the Y component. // -If a player has refraction OR reflection, we return a reflection of the actual skybox used. // -If a player has reflection enabled, we also return a reflection of actual entities where applicable. // reflMod reduces the intensity of reflections somewhat since they kind of wash refractions out otherwise. float reflMod = 0.75; vec3 eye = reflect(v_eyeVec, normal); #if USE_REFLECTION float refVY = clamp(v_eyeVec.y * 2.0, 0.05, 1.0); // Distort the reflection coords based on waves. vec2 reflCoords = (0.5 * reflectionCoords.xy - 15.0 * normal.zx / refVY) / reflectionCoords.z + 0.5; vec4 refTex = texture2D(reflectionMap, reflCoords); vec3 reflColor = refTex.rgb; // Interpolate between the sky color and nearby objects. // Only do this when alpha is rather low, or transparent leaves show up as extremely white. if (refTex.a < 0.4) reflColor = mix(textureCube(skyCube, (vec4(eye, 0.0) * skyBoxRot).xyz).rgb, refTex.rgb, refTex.a); // Let actual objects be reflected fully. reflMod = max(refTex.a, 0.75); #elif USE_REFRACTION vec3 reflColor = textureCube(skyCube, (vec4(eye, 0.0) * skyBoxRot).xyz).rgb; #else // !USE_REFLECTION && !USE_REFRACTION // Simplest case for reflection, return a gradient of blue based on Y component. vec3 reflColor = mix(vec3(0.76, 0.84, 0.92), vec3(0.24, 0.43, 0.71), -eye.y); #endif return vec4(reflColor, reflMod); } vec4 getRefraction(vec3 normal, float depthLimit) { float depth; #if USE_REAL_DEPTH // Compute real depth at the target point. float water_b = gl_FragCoord.z; float water_n = 2.0 * water_b - 1.0; float waterDBuffer = 2.0 * zNear * zFar / (zFar + zNear - water_n * (zFar - zNear)); float undisto_z_b = texture2D(depthTex, (gl_FragCoord.xy) / screenSize).x; float undisto_z_n = 2.0 * undisto_z_b - 1.0; float waterDepth_undistorted = (2.0 * zNear * zFar / (zFar + zNear - undisto_z_n * (zFar - zNear)) - waterDBuffer); // Set depth to the depth at the undistorted point. depth = waterDepth_undistorted; #else // fake depth computation: take the value at the vertex, add some if we are looking at a more oblique angle. depth = waterDepth / (min(0.5, v_eyeVec.y) * 1.5 * min(0.5, v_eyeVec.y) * 2.0); #endif #if USE_REFRACTION // for refraction we want to distort more as depth goes down. // 1) compute a distortion based on depth at the pixel. // 2) Re-sample the depth at the target point // 3) Sample refraction texture // distoFactor controls the amount of distortion relative to wave normals. float distoFactor = 0.5 + clamp(depth / 2.0, 0.0, 7.0); #if USE_REAL_DEPTH vec2 depthCoord = clamp((gl_FragCoord.xy) / screenSize - normal.xz * distoFactor / refractionCoords.z, 0.001, 0.999); float z_b = texture2D(depthTex, depthCoord).x; float z_n = 2.0 * z_b - 1.0; float newDepth = (2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)) - waterDBuffer); // try to correct for fish. In general they'd look weirder without this fix. if (depth > newDepth + 3.0) distoFactor /= 2.0; // this in general will not fall on the fish but still look distorted. else depth = newDepth; #endif #if USE_FANCY_EFFECTS depth = max(depth, depthLimit); if (waterDepth < 0.0) depth = 0.0; #endif // Distort the texture coords under where the water is to simulate refraction. vec2 refrCoords = (0.5 * refractionCoords.xy - normal.xz * distoFactor) / refractionCoords.z + 0.5; vec3 refColor = texture2D(refractionMap, refrCoords).rgb; // Note, the refraction map is cleared using (255, 0, 0), so pixels outside of the water plane are pure red. // If we get a pure red fragment, use an undistorted/less distorted coord instead. // blur the refraction map, distoring using normal so that it looks more random than it really is // and thus looks much better. float blur = (0.3 + clamp(normal.x, -0.1, 0.1)) / refractionCoords.z; vec4 blurColor = vec4(refColor, 1.0); vec4 tex = texture2D(refractionMap, refrCoords + vec2(blur + normal.x, blur + normal.z)); blurColor += vec4(tex.rgb * tex.a, tex.a); tex = texture2D(refractionMap, refrCoords + vec2(-blur, blur + normal.z)); blurColor += vec4(tex.rgb * tex.a, tex.a); tex = texture2D(refractionMap, refrCoords + vec2(-blur, -blur + normal.x)); blurColor += vec4(tex.rgb * tex.a, tex.a); tex = texture2D(refractionMap, refrCoords + vec2(blur + normal.z, -blur)); blurColor += vec4(tex.rgb * tex.a, tex.a); blurColor /= blurColor.a; float blurFactor = (distoFactor / 7.0); refColor = (refColor + blurColor.rgb * blurFactor) / (1.0 + blurFactor); #else // !USE_REFRACTION #if USE_FANCY_EFFECTS depth = max(depth, depthLimit); #endif vec3 refColor = color; #endif // for refraction, we want to adjust the value by v.y slightly otherwise it gets too different between "from above" and "from the sides". // And it looks weird (again, we are not used to seeing water from above). float fixedVy = max(v_eyeVec.y, 0.01); float murky = mix(200.0, 0.1, pow(murkiness, 0.25)); // Apply water tint and murk color. float extFact = max(0.0, 1.0 - (depth * fixedVy / murky)); float ColextFact = max(0.0, 1.0 - (depth * fixedVy / murky)); vec3 colll = mix(refColor * tint, refColor, ColextFact); vec3 refrColor = mix(color, colll, extFact); float alpha = clamp(depth, 0.0, 1.0); #if !USE_REFRACTION alpha = (1.4 - extFact) * alpha; #endif return vec4(refrColor, alpha); } void main() { // Calculate water normals. float wavyEffect = waveParams1.r; float baseScale = waveParams1.g; float flattenism = waveParams1.b; float baseBump = waveParams1.a; float BigMovement = waveParams2.b; // This method uses 60 animated water frames. We're blending between each two frames // Scale the normal textures by waviness so that big waviness means bigger waves. vec3 ww1 = texture2D(normalMap, (normalCoords.st + normalCoords.zw * BigMovement * waviness / 10.0) * (baseScale - waviness / wavyEffect)).xzy; vec3 ww2 = texture2D(normalMap2, (normalCoords.st + normalCoords.zw * BigMovement * waviness / 10.0) * (baseScale - waviness / wavyEffect)).xzy; vec3 wwInterp = mix(ww1, ww2, moddedTime) - vec3(0.5, 0.0, 0.5); ww1.x = wwInterp.x * WindCosSin.x - wwInterp.z * WindCosSin.y; ww1.z = wwInterp.x * WindCosSin.y + wwInterp.z * WindCosSin.x; ww1.y = wwInterp.y; // Flatten them based on waviness. vec3 normal = normalize(mix(vec3(0.0, 1.0, 0.0), ww1, clamp(baseBump + fwaviness / flattenism, 0.0, 1.0))); #if USE_FANCY_EFFECTS vec4 fancyeffects = texture2D(waterEffectsTex, gl_FragCoord.xy / screenSize); normal = mix(vec3(0.0, 1.0, 0.0), normal, 0.5 + waterInfo.r / 2.0); normal.xz = mix(normal.xz, fancyeffects.rb, fancyeffects.a / 2.0); #else normal = mix(vec3(0.0, 1.0, 0.0), normal, 0.5 + waterInfo.r / 2.0); #endif normal = vec3(-normal.x, normal.y, -normal.z); // The final wave normal vector. #if USE_FANCY_EFFECTS vec4 refrColor = getRefraction(normal, fancyeffects.a); #else vec4 refrColor = getRefraction(normal, 0.0); #endif vec4 reflColor = getReflection(normal); // How perpendicular to the normal our view is. Used for fresnel. float ndotv = clamp(dot(normal, v_eyeVec), 0.0, 1.0); // Fresnel for "how much reflection vs how much refraction". float fresnel = clamp(((pow(1.1 - ndotv, 2.0)) * 1.5), 0.1, 0.75); // Approximation. I'm using 1.1 and not 1.0 because it causes artifacts, see #1714 // Specular lighting vectors vec3 specVector = reflect(sunDir, ww1); // pow is undefined for null or negative values, except on intel it seems. float specIntensity = clamp(pow(abs(dot(specVector, v_eyeVec)), 100.0), 0.0, 1.0); vec3 specular = specIntensity * 1.2 * mix(vec3(1.5), sunColor, 0.5); vec3 color; #if USE_SHADOWS_ON_WATER && USE_SHADOW float shadow = get_shadow(vec4(v_shadow.xy, v_shadow.zw)); float fresShadow = mix(fresnel, fresnel * shadow, 0.05 + murkiness * 0.2); color = mix(refrColor.rgb, reflColor.rgb, fresShadow * reflColor.a); color += shadow * specular; #else color = mix(refrColor.rgb, reflColor.rgb, fresnel * reflColor.a); color += specular; #endif #if USE_FANCY_EFFECTS vec3 foam1 = texture2D(normalMap, (normalCoords.st + normalCoords.zw * BigMovement * waviness / 10.0) * (baseScale - waviness / wavyEffect)).aaa; vec3 foam2 = texture2D(normalMap2, (normalCoords.st + normalCoords.zw * BigMovement * waviness / 10.0) * (baseScale - waviness / wavyEffect)).aaa; vec3 foam3 = texture2D(normalMap, normalCoords.st / 6.0 - normalCoords.zw * 0.02).aaa; vec3 foam4 = texture2D(normalMap2, normalCoords.st / 6.0 - normalCoords.zw * 0.02).aaa; vec3 foaminterp = mix(foam1, foam2, moddedTime); foaminterp *= mix(foam3, foam4, moddedTime); foam1.x = abs(foaminterp.x * WindCosSin.x) + abs(foaminterp.z * WindCosSin.y); color += fancyeffects.g + pow(foam1.x * (3.0 + waviness), 2.6 - waviness / 5.5); #endif #if USE_FOG color = get_fog(color); #endif - + float alpha = refrColor.a; float losMod = texture2D(losMap, losCoords.st).a; losMod = losMod < 0.03 ? 0.0 : losMod; gl_FragColor = vec4(color * losMod, alpha); } Index: ps/trunk/binaries/data/mods/public/shaders/glsl/water_simple.fs =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/glsl/water_simple.fs (nonexistent) +++ ps/trunk/binaries/data/mods/public/shaders/glsl/water_simple.fs (revision 24129) @@ -0,0 +1,15 @@ +#version 110 + +uniform sampler2D baseTex; +uniform sampler2D losMap; +uniform vec3 color; + +varying vec2 v_coords; +varying vec2 v_losCoords; + +void main() +{ + float losMod = texture2D(losMap, v_losCoords.st).a; + losMod = losMod < 0.03 ? 0.0 : losMod; + gl_FragColor = vec4(texture2D(baseTex, v_coords).rgb * color * losMod, 1.0); +} Property changes on: ps/trunk/binaries/data/mods/public/shaders/glsl/water_simple.fs ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/shaders/glsl/water_simple.vs =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/glsl/water_simple.vs (nonexistent) +++ ps/trunk/binaries/data/mods/public/shaders/glsl/water_simple.vs (revision 24129) @@ -0,0 +1,22 @@ +#version 110 + +attribute vec3 a_vertex; + +uniform mat4 transform; +uniform mat4 losMatrix; +uniform float time; + +varying vec2 v_coords; +varying vec2 v_losCoords; + +void main() +{ + // Shift the texture coordinates by these amounts to make the water "flow" + float tx = -mod(time, 81.0) / 81.0; + float tz = -mod(time, 34.0) / 34.0; + float repeatPeriod = 16.0; + + v_coords = a_vertex.xz / repeatPeriod + vec2(tx, tz); + v_losCoords = (losMatrix * vec4(a_vertex, 1.0)).rg; + gl_Position = transform * vec4(a_vertex, 1.0); +} Property changes on: ps/trunk/binaries/data/mods/public/shaders/glsl/water_simple.vs ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/binaries/data/mods/public/shaders/glsl/water_simple.xml =================================================================== --- ps/trunk/binaries/data/mods/public/shaders/glsl/water_simple.xml (nonexistent) +++ ps/trunk/binaries/data/mods/public/shaders/glsl/water_simple.xml (revision 24129) @@ -0,0 +1,11 @@ + + + + + + + + + + + Property changes on: ps/trunk/binaries/data/mods/public/shaders/glsl/water_simple.xml ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: ps/trunk/source/ps/CStrInternStatic.h =================================================================== --- ps/trunk/source/ps/CStrInternStatic.h (revision 24128) +++ ps/trunk/source/ps/CStrInternStatic.h (revision 24129) @@ -1,162 +1,163 @@ /* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ // This file defines global CStrIntern variables, to avoid the cost of // constructing CStrInterns frequently at runtime. // // A line like // X(foo) // defines a variable str_foo with value "foo". // // A line like // X2(foo_0, "foo[0]") // defines a variable str_foo_0 with value "foo[0]". X(0) X(1) X(2) X(ALPHABLEND_PASS_BLEND) X(ALPHABLEND_PASS_OPAQUE) X(BLEND) X(BLOOM_NOP) X(BLOOM_PASS_H) X(BLOOM_PASS_V) X(DECAL) X(DISABLE_RECEIVE_SHADOWS) X(IGNORE_LOS) X(MINIMAP_BASE) X(MINIMAP_LINE) X(MINIMAP_LOS) X(MINIMAP_POINT) X(MODE_SHADOWCAST) X(MODE_SILHOUETTEDISPLAY) X(MODE_SILHOUETTEOCCLUDER) X(MODE_WIREFRAME) X(SYS_HAS_ARB) X(SYS_HAS_GLSL) X(SYS_PREFER_GLSL) X(USE_FANCY_EFFECTS) X(USE_FP_SHADOW) X(USE_GPU_SKINNING) X(USE_INSTANCING) X(USE_NORMALS) X(USE_OBJECTCOLOR) X(USE_REAL_DEPTH) X(USE_REFLECTION) X(USE_REFRACTION) X(USE_SHADOW) X(USE_SHADOW_PCF) X(USE_SHADOW_SAMPLER) X(USE_SHADOWS_ON_WATER) X(USE_FOG) X(WATERTYPE_CLAP) X(WATERTYPE_LAKE) X2(_emptystring, "") X(a_apexPosition) X(a_otherPosition) X(a_retreatPosition) X(a_skinJoints) X(a_skinWeights) X(a_splashPosition) X(a_tangent) X(a_waterInfo) X(ambient) X(baseTex) X(blendTex) X(bloom) X(blurTex2) X(blurTex4) X(blurTex8) X(brightness) X(cameraPos) X(color) X(colorAdd) X(colorMul) X(delta) X(depthTex) X(foamTex) X(fogColor) X(fogParams) X(foreground_overlay) X(gui_add) X(gui_basic) X(gui_grayscale) X(gui_solid) X(gui_solid_mask) X(gui_text) X(hdr) X(height) X(instancingTransform) X(losMap) X(losMatrix) X(losTex) X(losTex1) X(losTex2) X(losTransform) X(los_interp) X(mapSize) X(maskTex) X(minimap) X(modelViewMatrix) X(murkiness) X(normalMap) X(normalMap2) X(objectColor) X(overlay_solid) X(particle) X(particle_solid) X(playerColor) X(pointSize) X(qualityLevel) X(reflectionMap) X(reflectionMatrix) X(refractionMap) X(refractionMatrix) X(renderedTex) X(repeatScale) X2(sans_10, "sans-10"); X(saturation) X(screenSize) X(shadingColor) X(shadowScale) X(shadowTex) X(shadowTransform) X(sharpness) X(skinBlendMatrices) X2(skinBlendMatrices_0, "skinBlendMatrices[0]") X(skyBoxRot) X(skyCube) X(sky_simple) X(sunColor) X(sunDir) X(tex) X(texSize) X(textureTransform) X(time) X(tint) X(transform) X(translation) +X(water_simple) X(waterEffectsTex) X(waterTex) X(waveTex) X(waviness) X(waveParams1) X(waveParams2) X(width) X(windAngle) X(zFar) X(zNear) Index: ps/trunk/source/renderer/TerrainRenderer.cpp =================================================================== --- ps/trunk/source/renderer/TerrainRenderer.cpp (revision 24128) +++ ps/trunk/source/renderer/TerrainRenderer.cpp (revision 24129) @@ -1,999 +1,951 @@ /* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ /* * Terrain rendering (everything related to patches and water) is * encapsulated in TerrainRenderer */ #include "precompiled.h" #include "graphics/Camera.h" #include "graphics/Decal.h" #include "graphics/LightEnv.h" #include "graphics/LOSTexture.h" #include "graphics/Patch.h" #include "graphics/GameView.h" #include "graphics/Model.h" #include "graphics/ShaderManager.h" #include "renderer/ShadowMap.h" #include "renderer/SkyManager.h" #include "graphics/TerritoryTexture.h" #include "graphics/TextRenderer.h" #include "maths/MathUtil.h" #include "ps/Filesystem.h" #include "ps/CLogger.h" #include "ps/Game.h" #include "ps/Profile.h" #include "ps/World.h" #include "renderer/DecalRData.h" #include "renderer/PatchRData.h" #include "renderer/Renderer.h" #include "renderer/RenderingOptions.h" #include "renderer/ShadowMap.h" #include "renderer/TerrainRenderer.h" #include "renderer/VertexArray.h" #include "renderer/WaterManager.h" #include "tools/atlas/GameInterface/GameLoop.h" extern GameLoopState* g_AtlasGameLoop; /////////////////////////////////////////////////////////////////////////////////////////////// // TerrainRenderer implementation namespace { CShaderProgramPtr GetDummyShader() { const char* shaderName; if (g_RenderingOptions.GetRenderPath() == RenderPath::SHADER) { if (g_RenderingOptions.GetPreferGLSL()) shaderName = "glsl/dummy"; else shaderName = "arb/dummy"; } else shaderName = "fixed:dummy"; return g_Renderer.GetShaderManager().LoadProgram(shaderName, CShaderDefines()); } } // anonymous namespace /** * TerrainRenderer keeps track of which phase it is in, to detect * when Submit, PrepareForRendering etc. are called in the wrong order. */ enum Phase { Phase_Submit, Phase_Render }; /** * Struct TerrainRendererInternals: Internal variables used by the TerrainRenderer class. */ struct TerrainRendererInternals { /// Which phase (submitting or rendering patches) are we in right now? Phase phase; /// Patches that were submitted for this frame std::vector visiblePatches[CRenderer::CULL_MAX]; /// Decals that were submitted for this frame std::vector visibleDecals[CRenderer::CULL_MAX]; /// Fancy water shader CShaderProgramPtr fancyWaterShader; CSimulation2* simulation; }; /////////////////////////////////////////////////////////////////// // Construction/Destruction TerrainRenderer::TerrainRenderer() { m = new TerrainRendererInternals(); m->phase = Phase_Submit; } TerrainRenderer::~TerrainRenderer() { delete m; } void TerrainRenderer::SetSimulation(CSimulation2* simulation) { m->simulation = simulation; } /////////////////////////////////////////////////////////////////// // Submit a patch for rendering void TerrainRenderer::Submit(int cullGroup, CPatch* patch) { ENSURE(m->phase == Phase_Submit); CPatchRData* data = (CPatchRData*)patch->GetRenderData(); if (data == 0) { // no renderdata for patch, create it now data = new CPatchRData(patch, m->simulation); patch->SetRenderData(data); } data->Update(m->simulation); m->visiblePatches[cullGroup].push_back(data); } /////////////////////////////////////////////////////////////////// // Submit a decal for rendering void TerrainRenderer::Submit(int cullGroup, CModelDecal* decal) { ENSURE(m->phase == Phase_Submit); CDecalRData* data = (CDecalRData*)decal->GetRenderData(); if (data == 0) { // no renderdata for decal, create it now data = new CDecalRData(decal, m->simulation); decal->SetRenderData(data); } data->Update(m->simulation); m->visibleDecals[cullGroup].push_back(data); } /////////////////////////////////////////////////////////////////// // Prepare for rendering void TerrainRenderer::PrepareForRendering() { ENSURE(m->phase == Phase_Submit); m->phase = Phase_Render; } /////////////////////////////////////////////////////////////////// // Clear submissions lists void TerrainRenderer::EndFrame() { ENSURE(m->phase == Phase_Render || m->phase == Phase_Submit); for (int i = 0; i < CRenderer::CULL_MAX; ++i) { m->visiblePatches[i].clear(); m->visibleDecals[i].clear(); } m->phase = Phase_Submit; } /////////////////////////////////////////////////////////////////// // Full-featured terrain rendering with blending and everything void TerrainRenderer::RenderTerrainFixed(int cullGroup) { #if CONFIG2_GLES UNUSED2(cullGroup); #else ENSURE(m->phase == Phase_Render); std::vector& visiblePatches = m->visiblePatches[cullGroup]; std::vector& visibleDecals = m->visibleDecals[cullGroup]; if (visiblePatches.empty() && visibleDecals.empty()) return; CShaderProgramPtr dummyShader = GetDummyShader(); dummyShader->Bind(); dummyShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); dummyShader->Uniform(str_color, CColor(0.0f, 0.0f, 0.0f, 1.0f)); // render the solid black sides of the map first g_Renderer.BindTexture(0, 0); glEnableClientState(GL_VERTEX_ARRAY); glColor3f(0, 0, 0); PROFILE_START("render terrain sides"); for (size_t i = 0; i < visiblePatches.size(); ++i) visiblePatches[i]->RenderSides(dummyShader); PROFILE_END("render terrain sides"); // switch on required client states glEnableClientState(GL_TEXTURE_COORD_ARRAY); // render everything fullbright // set up texture environment for base pass pglActiveTextureARB(GL_TEXTURE0); pglClientActiveTextureARB(GL_TEXTURE0); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); // Set alpha to 1.0 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_CONSTANT); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); static const float one[4] = { 1.f, 1.f, 1.f, 1.f }; glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, one); PROFILE_START("render terrain base"); CPatchRData::RenderBases(visiblePatches, CShaderDefines(), NULL, true, dummyShader); PROFILE_END("render terrain base"); // render blends // switch on the composite alpha map texture (void)ogl_tex_bind(g_Renderer.m_hCompositeAlphaMap, 1); // switch on second uv set pglClientActiveTextureARB(GL_TEXTURE1); glEnableClientState(GL_TEXTURE_COORD_ARRAY); // setup additional texenv required by blend pass pglActiveTextureARB(GL_TEXTURE1); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_ONE_MINUS_SRC_ALPHA); // switch on blending glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // no need to write to the depth buffer a second time glDepthMask(0); // The decal color array contains lighting data, which we don't want in this non-shader mode glDisableClientState(GL_COLOR_ARRAY); // render blend passes for each patch PROFILE_START("render terrain blends"); CPatchRData::RenderBlends(visiblePatches, CShaderDefines(), NULL, true, dummyShader); PROFILE_END("render terrain blends"); // Disable second texcoord array pglClientActiveTextureARB(GL_TEXTURE1); glDisableClientState(GL_TEXTURE_COORD_ARRAY); // Render terrain decals g_Renderer.BindTexture(1, 0); pglActiveTextureARB(GL_TEXTURE0); pglClientActiveTextureARB(GL_TEXTURE0); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); PROFILE_START("render terrain decals"); CDecalRData::RenderDecals(visibleDecals, CShaderDefines(), NULL, true, dummyShader); PROFILE_END("render terrain decals"); // Now apply lighting const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); pglClientActiveTextureARB(GL_TEXTURE0); glEnableClientState(GL_COLOR_ARRAY); // diffuse lighting colors // The vertex color is scaled by 0.5 to permit overbrightness without clamping. // We therefore need to draw Clamp((texture*lighting)*2.0), where 'texture' // is what previous passes drew onto the framebuffer, and 'lighting' is the // color computed by this pass. // We can do that with blending by getting it to draw dst*src + src*dst: glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); // Scale the ambient color by 0.5 to match the vertex diffuse colors float terrainAmbientColor[4] = { lightEnv.m_TerrainAmbientColor.X * 0.5f, lightEnv.m_TerrainAmbientColor.Y * 0.5f, lightEnv.m_TerrainAmbientColor.Z * 0.5f, 1.f }; CLOSTexture& losTexture = g_Renderer.GetScene().GetLOSTexture(); int streamflags = STREAM_POS; pglActiveTextureARB(GL_TEXTURE0); // We're not going to use a texture here, but we have to have a valid texture // bound else the texture unit will be disabled. // We should still have a bound splat texture from some earlier rendering, // so assume that's still valid to use. // (TODO: That's a bit of an ugly hack.) // No shadows: (Ambient + Diffuse) * LOS glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_CONSTANT); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, terrainAmbientColor); losTexture.BindTexture(1); pglClientActiveTextureARB(GL_TEXTURE1); glEnableClientState(GL_TEXTURE_COORD_ARRAY); streamflags |= STREAM_POSTOUV1; glMatrixMode(GL_TEXTURE); glLoadMatrixf(&losTexture.GetTextureMatrix()._11); glMatrixMode(GL_MODELVIEW); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_ALPHA); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); pglActiveTextureARB(GL_TEXTURE0); pglClientActiveTextureARB(GL_TEXTURE0); PROFILE_START("render terrain streams"); CPatchRData::RenderStreams(visiblePatches, dummyShader, streamflags); PROFILE_END("render terrain streams"); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); // restore OpenGL state g_Renderer.BindTexture(1, 0); pglClientActiveTextureARB(GL_TEXTURE1); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); pglClientActiveTextureARB(GL_TEXTURE0); pglActiveTextureARB(GL_TEXTURE0); glDepthMask(1); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); dummyShader->Unbind(); #endif } void TerrainRenderer::RenderTerrainOverlayTexture(int cullGroup, CMatrix3D& textureMatrix) { #if CONFIG2_GLES #warning TODO: implement TerrainRenderer::RenderTerrainOverlayTexture for GLES UNUSED2(cullGroup); UNUSED2(textureMatrix); #else ENSURE(m->phase == Phase_Render); std::vector& visiblePatches = m->visiblePatches[cullGroup]; glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); pglActiveTextureARB(GL_TEXTURE0); glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthMask(0); glDisable(GL_DEPTH_TEST); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glMatrixMode(GL_TEXTURE); glLoadMatrixf(&textureMatrix._11); glMatrixMode(GL_MODELVIEW); CShaderProgramPtr dummyShader = GetDummyShader(); dummyShader->Bind(); dummyShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); dummyShader->Uniform(str_color, CColor(0.0f, 0.0f, 0.0f, 1.0f)); CPatchRData::RenderStreams(visiblePatches, dummyShader, STREAM_POS | STREAM_POSTOUV0); dummyShader->Unbind(); // To make the overlay visible over water, render an additional map-sized // water-height patch CBoundingBoxAligned waterBounds; for (size_t i = 0; i < visiblePatches.size(); ++i) { CPatchRData* data = visiblePatches[i]; waterBounds += data->GetWaterBounds(); } if (!waterBounds.IsEmpty()) { float h = g_Renderer.GetWaterManager()->m_WaterHeight + 0.05f; // add a delta to avoid z-fighting float waterPos[] = { waterBounds[0].X, h, waterBounds[0].Z, waterBounds[1].X, h, waterBounds[0].Z, waterBounds[0].X, h, waterBounds[1].Z, waterBounds[1].X, h, waterBounds[1].Z }; glVertexPointer(3, GL_FLOAT, 3*sizeof(float), waterPos); glTexCoordPointer(3, GL_FLOAT, 3*sizeof(float), waterPos); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } glMatrixMode(GL_TEXTURE); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glDepthMask(1); glEnable(GL_DEPTH_TEST); glDisable(GL_BLEND); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); #endif } /////////////////////////////////////////////////////////////////// /** * Set up all the uniforms for a shader pass. */ void TerrainRenderer::PrepareShader(const CShaderProgramPtr& shader, ShadowMap* shadow) { shader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); shader->Uniform(str_cameraPos, g_Renderer.GetViewCamera().GetOrientation().GetTranslation()); const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); if (shadow) { shader->BindTexture(str_shadowTex, shadow->GetTexture()); shader->Uniform(str_shadowTransform, shadow->GetTextureMatrix()); int width = shadow->GetWidth(); int height = shadow->GetHeight(); shader->Uniform(str_shadowScale, width, height, 1.0f / width, 1.0f / height); } CLOSTexture& los = g_Renderer.GetScene().GetLOSTexture(); shader->BindTexture(str_losTex, los.GetTextureSmooth()); shader->Uniform(str_losTransform, los.GetTextureMatrix()[0], los.GetTextureMatrix()[12], 0.f, 0.f); shader->Uniform(str_ambient, lightEnv.m_TerrainAmbientColor); shader->Uniform(str_sunColor, lightEnv.m_SunColor); shader->Uniform(str_sunDir, lightEnv.GetSunDir()); shader->Uniform(str_fogColor, lightEnv.m_FogColor); shader->Uniform(str_fogParams, lightEnv.m_FogFactor, lightEnv.m_FogMax, 0.f, 0.f); } void TerrainRenderer::RenderTerrainShader(const CShaderDefines& context, int cullGroup, ShadowMap* shadow) { ENSURE(m->phase == Phase_Render); std::vector& visiblePatches = m->visiblePatches[cullGroup]; std::vector& visibleDecals = m->visibleDecals[cullGroup]; if (visiblePatches.empty() && visibleDecals.empty()) return; // render the solid black sides of the map first CShaderTechniquePtr techSolid = g_Renderer.GetShaderManager().LoadEffect(str_gui_solid); techSolid->BeginPass(); CShaderProgramPtr shaderSolid = techSolid->GetShader(); shaderSolid->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); shaderSolid->Uniform(str_color, 0.0f, 0.0f, 0.0f, 1.0f); PROFILE_START("render terrain sides"); for (size_t i = 0; i < visiblePatches.size(); ++i) visiblePatches[i]->RenderSides(shaderSolid); PROFILE_END("render terrain sides"); techSolid->EndPass(); PROFILE_START("render terrain base"); CPatchRData::RenderBases(visiblePatches, context, shadow); PROFILE_END("render terrain base"); // no need to write to the depth buffer a second time glDepthMask(0); // render blend passes for each patch PROFILE_START("render terrain blends"); CPatchRData::RenderBlends(visiblePatches, context, shadow, false); PROFILE_END("render terrain blends"); PROFILE_START("render terrain decals"); CDecalRData::RenderDecals(visibleDecals, context, shadow, false); PROFILE_END("render terrain decals"); // restore OpenGL state g_Renderer.BindTexture(1, 0); g_Renderer.BindTexture(2, 0); g_Renderer.BindTexture(3, 0); glDepthMask(1); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); } /////////////////////////////////////////////////////////////////// // Render un-textured patches as polygons void TerrainRenderer::RenderPatches(int cullGroup, const CColor& color) { ENSURE(m->phase == Phase_Render); std::vector& visiblePatches = m->visiblePatches[cullGroup]; if (visiblePatches.empty()) return; #if CONFIG2_GLES #warning TODO: implement TerrainRenderer::RenderPatches for GLES #else CShaderProgramPtr dummyShader = GetDummyShader(); dummyShader->Bind(); dummyShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); dummyShader->Uniform(str_color, color); glEnableClientState(GL_VERTEX_ARRAY); CPatchRData::RenderStreams(visiblePatches, dummyShader, STREAM_POS); glDisableClientState(GL_VERTEX_ARRAY); dummyShader->Unbind(); #endif } /////////////////////////////////////////////////////////////////// // Render outlines of submitted patches as lines void TerrainRenderer::RenderOutlines(int cullGroup) { ENSURE(m->phase == Phase_Render); std::vector& visiblePatches = m->visiblePatches[cullGroup]; if (visiblePatches.empty()) return; #if CONFIG2_GLES #warning TODO: implement TerrainRenderer::RenderOutlines for GLES #else glEnableClientState(GL_VERTEX_ARRAY); for (size_t i = 0; i < visiblePatches.size(); ++i) visiblePatches[i]->RenderOutline(); glDisableClientState(GL_VERTEX_ARRAY); #endif } /////////////////////////////////////////////////////////////////// // Scissor rectangle of water patches CBoundingBoxAligned TerrainRenderer::ScissorWater(int cullGroup, const CMatrix3D &viewproj) { std::vector& visiblePatches = m->visiblePatches[cullGroup]; CBoundingBoxAligned scissor; for (size_t i = 0; i < visiblePatches.size(); ++i) { CPatchRData* data = visiblePatches[i]; const CBoundingBoxAligned& waterBounds = data->GetWaterBounds(); if (waterBounds.IsEmpty()) continue; CVector4D v1 = viewproj.Transform(CVector4D(waterBounds[0].X, waterBounds[1].Y, waterBounds[0].Z, 1.0f)); CVector4D v2 = viewproj.Transform(CVector4D(waterBounds[1].X, waterBounds[1].Y, waterBounds[0].Z, 1.0f)); CVector4D v3 = viewproj.Transform(CVector4D(waterBounds[0].X, waterBounds[1].Y, waterBounds[1].Z, 1.0f)); CVector4D v4 = viewproj.Transform(CVector4D(waterBounds[1].X, waterBounds[1].Y, waterBounds[1].Z, 1.0f)); CBoundingBoxAligned screenBounds; #define ADDBOUND(v1, v2, v3, v4) \ if (v1.Z >= -v1.W) \ screenBounds += CVector3D(v1.X, v1.Y, v1.Z) * (1.0f / v1.W); \ else \ { \ float t = v1.Z + v1.W; \ if (v2.Z > -v2.W) \ { \ CVector4D c2 = v1 + (v2 - v1) * (t / (t - (v2.Z + v2.W))); \ screenBounds += CVector3D(c2.X, c2.Y, c2.Z) * (1.0f / c2.W); \ } \ if (v3.Z > -v3.W) \ { \ CVector4D c3 = v1 + (v3 - v1) * (t / (t - (v3.Z + v3.W))); \ screenBounds += CVector3D(c3.X, c3.Y, c3.Z) * (1.0f / c3.W); \ } \ if (v4.Z > -v4.W) \ { \ CVector4D c4 = v1 + (v4 - v1) * (t / (t - (v4.Z + v4.W))); \ screenBounds += CVector3D(c4.X, c4.Y, c4.Z) * (1.0f / c4.W); \ } \ } ADDBOUND(v1, v2, v3, v4); ADDBOUND(v2, v1, v3, v4); ADDBOUND(v3, v1, v2, v4); ADDBOUND(v4, v1, v2, v3); #undef ADDBOUND if (screenBounds[0].X >= 1.0f || screenBounds[1].X <= -1.0f || screenBounds[0].Y >= 1.0f || screenBounds[1].Y <= -1.0f) continue; scissor += screenBounds; } return CBoundingBoxAligned(CVector3D(Clamp(scissor[0].X, -1.0f, 1.0f), Clamp(scissor[0].Y, -1.0f, 1.0f), -1.0f), CVector3D(Clamp(scissor[1].X, -1.0f, 1.0f), Clamp(scissor[1].Y, -1.0f, 1.0f), 1.0f)); } // Render fancy water bool TerrainRenderer::RenderFancyWater(const CShaderDefines& context, int cullGroup, ShadowMap* shadow) { PROFILE3_GPU("fancy water"); WaterManager* WaterMgr = g_Renderer.GetWaterManager(); CShaderDefines defines = context; // If we're using fancy water, make sure its shader is loaded if (!m->fancyWaterShader || WaterMgr->m_NeedsReloading) { if (WaterMgr->m_WaterRealDepth) defines.Add(str_USE_REAL_DEPTH, str_1); if (WaterMgr->m_WaterFancyEffects) defines.Add(str_USE_FANCY_EFFECTS, str_1); if (WaterMgr->m_WaterRefraction) defines.Add(str_USE_REFRACTION, str_1); if (WaterMgr->m_WaterReflection) defines.Add(str_USE_REFLECTION, str_1); if (shadow && WaterMgr->m_WaterShadows) defines.Add(str_USE_SHADOWS_ON_WATER, str_1); // haven't updated the ARB shader yet so I'll always load the GLSL /*if (!g_RenderingOptions.GetPreferGLSL() && !superFancy) m->fancyWaterShader = g_Renderer.GetShaderManager().LoadProgram("arb/water_high", defines); else*/ m->fancyWaterShader = g_Renderer.GetShaderManager().LoadProgram("glsl/water_high", defines); if (!m->fancyWaterShader) { LOGERROR("Failed to load water shader. Falling back to fixed pipeline water.\n"); WaterMgr->m_RenderWater = false; return false; } WaterMgr->m_NeedsReloading = false; } CLOSTexture& losTexture = g_Renderer.GetScene().GetLOSTexture(); // creating the real depth texture using the depth buffer. if (WaterMgr->m_WaterRealDepth) { if (WaterMgr->m_depthTT == 0) { GLuint depthTex; glGenTextures(1, (GLuint*)&depthTex); WaterMgr->m_depthTT = depthTex; glBindTexture(GL_TEXTURE_2D, WaterMgr->m_depthTT); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, g_Renderer.GetWidth(), g_Renderer.GetHeight(), 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE,NULL); 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); } else { glBindTexture(GL_TEXTURE_2D, WaterMgr->m_depthTT); glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 0, 0, g_Renderer.GetWidth(), g_Renderer.GetHeight(), 0); } glBindTexture(GL_TEXTURE_2D, 0); } // Calculating the advanced informations about Foam and all if the quality calls for it. /*if (WaterMgr->m_NeedInfoUpdate && (WaterMgr->m_WaterFoam || WaterMgr->m_WaterCoastalWaves)) { WaterMgr->m_NeedInfoUpdate = false; WaterMgr->CreateSuperfancyInfo(); }*/ double time = WaterMgr->m_WaterTexTimer; double period = 8; int curTex = (int)(time*60/period) % 60; int nexTex = (curTex + 1) % 60; float repeatPeriod = WaterMgr->m_RepeatPeriod; // Render normals and foam to a framebuffer if we're in fancy effects if (WaterMgr->m_WaterFancyEffects) { // Save the post-processing framebuffer. GLint fbo; glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &fbo); pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, WaterMgr->m_FancyEffectsFBO); glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glDisable(GL_CULL_FACE); // Overwrite waves that would be behind the ground. CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("glsl/gui_solid", CShaderDefines()); dummyShader->Bind(); dummyShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); dummyShader->Uniform(str_color, 0.0f, 0.0f, 0.0f, 0.0f); std::vector& visiblePatches = m->visiblePatches[cullGroup]; for (size_t i = 0; i < visiblePatches.size(); ++i) { CPatchRData* data = visiblePatches[i]; data->RenderWater(dummyShader, true, true); } dummyShader->Unbind(); glEnable(GL_CULL_FACE); pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); } glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); m->fancyWaterShader->Bind(); const CCamera& camera = g_Renderer.GetViewCamera(); m->fancyWaterShader->BindTexture(str_normalMap, WaterMgr->m_NormalMap[curTex]); m->fancyWaterShader->BindTexture(str_normalMap2, WaterMgr->m_NormalMap[nexTex]); if (WaterMgr->m_WaterFancyEffects) { m->fancyWaterShader->BindTexture(str_waterEffectsTex, WaterMgr->m_FancyTexture); } if (WaterMgr->m_WaterRealDepth) m->fancyWaterShader->BindTexture(str_depthTex, WaterMgr->m_depthTT); if (WaterMgr->m_WaterRefraction) m->fancyWaterShader->BindTexture(str_refractionMap, WaterMgr->m_RefractionTexture); if (WaterMgr->m_WaterReflection) m->fancyWaterShader->BindTexture(str_reflectionMap, WaterMgr->m_ReflectionTexture); m->fancyWaterShader->BindTexture(str_losMap, losTexture.GetTextureSmooth()); const CLightEnv& lightEnv = g_Renderer.GetLightEnv(); m->fancyWaterShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); //TODO: bind only what's needed if (WaterMgr->m_WaterRefraction || WaterMgr->m_WaterReflection) { m->fancyWaterShader->BindTexture(str_skyCube, g_Renderer.GetSkyManager()->GetSkyCube()); // TODO: check that this rotates in the right direction. CMatrix3D skyBoxRotation; skyBoxRotation.SetIdentity(); skyBoxRotation.RotateY(M_PI + lightEnv.GetRotation()); m->fancyWaterShader->Uniform(str_skyBoxRot, skyBoxRotation); if (WaterMgr->m_WaterRefraction) m->fancyWaterShader->Uniform(str_refractionMatrix, WaterMgr->m_RefractionMatrix); if (WaterMgr->m_WaterReflection) m->fancyWaterShader->Uniform(str_reflectionMatrix, WaterMgr->m_ReflectionMatrix); } m->fancyWaterShader->Uniform(str_sunDir, lightEnv.GetSunDir()); m->fancyWaterShader->Uniform(str_sunColor, lightEnv.m_SunColor); m->fancyWaterShader->Uniform(str_color, WaterMgr->m_WaterColor); m->fancyWaterShader->Uniform(str_tint, WaterMgr->m_WaterTint); m->fancyWaterShader->Uniform(str_waviness, WaterMgr->m_Waviness); m->fancyWaterShader->Uniform(str_murkiness, WaterMgr->m_Murkiness); m->fancyWaterShader->Uniform(str_windAngle, WaterMgr->m_WindAngle); m->fancyWaterShader->Uniform(str_repeatScale, 1.0f / repeatPeriod); m->fancyWaterShader->Uniform(str_losMatrix, losTexture.GetTextureMatrix()); m->fancyWaterShader->Uniform(str_cameraPos, camera.GetOrientation().GetTranslation()); if (WaterMgr->m_WaterRealDepth) { m->fancyWaterShader->Uniform(str_zNear, camera.GetNearPlane()); m->fancyWaterShader->Uniform(str_zFar, camera.GetFarPlane()); } m->fancyWaterShader->Uniform(str_fogColor, lightEnv.m_FogColor); m->fancyWaterShader->Uniform(str_fogParams, lightEnv.m_FogFactor, lightEnv.m_FogMax, 0.f, 0.f); m->fancyWaterShader->Uniform(str_time, (float)time); m->fancyWaterShader->Uniform(str_screenSize, (float)g_Renderer.GetWidth(), (float)g_Renderer.GetHeight(), 0.0f, 0.0f); if (WaterMgr->m_WaterType == L"clap") { m->fancyWaterShader->Uniform(str_waveParams1, 30.0f,1.5f,20.0f,0.03f); m->fancyWaterShader->Uniform(str_waveParams2, 0.5f,0.0f,0.0f,0.0f); } else if (WaterMgr->m_WaterType == L"lake") { m->fancyWaterShader->Uniform(str_waveParams1, 8.5f,1.5f,15.0f,0.03f); m->fancyWaterShader->Uniform(str_waveParams2, 0.2f,0.0f,0.0f,0.07f); } else { m->fancyWaterShader->Uniform(str_waveParams1, 15.0f,0.8f,10.0f,0.1f); m->fancyWaterShader->Uniform(str_waveParams2, 0.3f,0.0f,0.1f,0.3f); } if (shadow && WaterMgr->m_WaterShadows) { m->fancyWaterShader->BindTexture(str_shadowTex, shadow->GetTexture()); m->fancyWaterShader->Uniform(str_shadowTransform, shadow->GetTextureMatrix()); int width = shadow->GetWidth(); int height = shadow->GetHeight(); m->fancyWaterShader->Uniform(str_shadowScale, width, height, 1.0f / width, 1.0f / height); } std::vector& visiblePatches = m->visiblePatches[cullGroup]; for (size_t i = 0; i < visiblePatches.size(); ++i) { CPatchRData* data = visiblePatches[i]; data->RenderWater(m->fancyWaterShader); } m->fancyWaterShader->Unbind(); glDepthFunc(GL_LEQUAL); glDisable(GL_BLEND); return true; } void TerrainRenderer::RenderSimpleWater(int cullGroup) { #if CONFIG2_GLES UNUSED2(cullGroup); #else PROFILE3_GPU("simple water"); WaterManager* WaterMgr = g_Renderer.GetWaterManager(); CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture(); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); double time = WaterMgr->m_WaterTexTimer; double period = 1.6f; int curTex = (int)(time*60/period) % 60; - WaterMgr->m_WaterTexture[curTex]->Bind(); - - // Shift the texture coordinates by these amounts to make the water "flow" - float tx = -fmod(time, 81.0)/81.0; - float ty = -fmod(time, 34.0)/34.0; - float repeatPeriod = 16.0f; - - // Perform the shifting by using texture coordinate generation - GLfloat texgenS0[4] = { 1/repeatPeriod, 0, 0, tx }; - GLfloat texgenT0[4] = { 0, 0, 1/repeatPeriod, ty }; - glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); - glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); - glTexGenfv(GL_S, GL_OBJECT_PLANE, texgenS0); - glTexGenfv(GL_T, GL_OBJECT_PLANE, texgenT0); - glEnable(GL_TEXTURE_GEN_S); - glEnable(GL_TEXTURE_GEN_T); - - // Set up texture environment to multiply vertex RGB by texture RGB. - GLfloat waterColor[4] = { WaterMgr->m_WaterColor.r, WaterMgr->m_WaterColor.g, WaterMgr->m_WaterColor.b, 1.0f }; - glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, waterColor); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_CONSTANT); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); - - - // Multiply by LOS texture - losTexture.BindTexture(1); - CMatrix3D losMatrix = losTexture.GetTextureMatrix(); - GLfloat texgenS1[4] = { losMatrix[0], losMatrix[4], losMatrix[8], losMatrix[12] }; - GLfloat texgenT1[4] = { losMatrix[1], losMatrix[5], losMatrix[9], losMatrix[13] }; - glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); - glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); - glTexGenfv(GL_S, GL_OBJECT_PLANE, texgenS1); - glTexGenfv(GL_T, GL_OBJECT_PLANE, texgenT1); - glEnable(GL_TEXTURE_GEN_S); - glEnable(GL_TEXTURE_GEN_T); - - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_ALPHA); - - CShaderProgramPtr dummyShader = GetDummyShader(); - dummyShader->Bind(); - dummyShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); - dummyShader->Uniform(str_color, CColor(0.0f, 0.0f, 0.0f, 1.0f)); + CShaderTechniquePtr waterSimpleTech = + g_Renderer.GetShaderManager().LoadEffect(str_water_simple); + waterSimpleTech->BeginPass(); + CShaderProgramPtr waterSimpleShader = waterSimpleTech->GetShader(); + + waterSimpleShader->Bind(); + waterSimpleShader->BindTexture(str_baseTex, WaterMgr->m_WaterTexture[curTex]); + waterSimpleShader->BindTexture(str_losMap, losTexture.GetTextureSmooth()); + waterSimpleShader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection()); + waterSimpleShader->Uniform(str_losMatrix, losTexture.GetTextureMatrix()); + waterSimpleShader->Uniform(str_time, static_cast(time)); + waterSimpleShader->Uniform(str_color, WaterMgr->m_WaterColor); glEnableClientState(GL_VERTEX_ARRAY); std::vector& visiblePatches = m->visiblePatches[cullGroup]; for (size_t i = 0; i < visiblePatches.size(); ++i) { CPatchRData* data = visiblePatches[i]; - data->RenderWater(dummyShader, false, true); + data->RenderWater(waterSimpleShader, false, true); } glDisableClientState(GL_VERTEX_ARRAY); - dummyShader->Unbind(); - + waterSimpleShader->Unbind(); g_Renderer.BindTexture(1, 0); - glDisable(GL_TEXTURE_GEN_S); - glDisable(GL_TEXTURE_GEN_T); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - pglActiveTextureARB(GL_TEXTURE0_ARB); - - // Clean up the texture matrix and blend mode - glDisable(GL_TEXTURE_GEN_S); - glDisable(GL_TEXTURE_GEN_T); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glDisable(GL_TEXTURE_2D); + + waterSimpleTech->EndPass(); #endif } /////////////////////////////////////////////////////////////////// // Render water that is part of the terrain void TerrainRenderer::RenderWater(const CShaderDefines& context, int cullGroup, ShadowMap* shadow) { WaterManager* WaterMgr = g_Renderer.GetWaterManager(); WaterMgr->UpdateQuality(); if (!WaterMgr->WillRenderFancyWater()) RenderSimpleWater(cullGroup); else RenderFancyWater(context, cullGroup, shadow); } void TerrainRenderer::RenderPriorities(int cullGroup) { PROFILE("priorities"); ENSURE(m->phase == Phase_Render); CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_gui_text); tech->BeginPass(); CTextRenderer textRenderer(tech->GetShader()); textRenderer.Font(CStrIntern("mono-stroke-10")); textRenderer.Color(1.0f, 1.0f, 0.0f); std::vector& visiblePatches = m->visiblePatches[cullGroup]; for (size_t i = 0; i < visiblePatches.size(); ++i) visiblePatches[i]->RenderPriorities(textRenderer); textRenderer.Render(); tech->EndPass(); }