Index: ps/trunk/binaries/data/mods/public/maps/random/rmgen/environment.js
===================================================================
--- ps/trunk/binaries/data/mods/public/maps/random/rmgen/environment.js (revision 14513)
+++ ps/trunk/binaries/data/mods/public/maps/random/rmgen/environment.js (revision 14514)
@@ -1,142 +1,136 @@
////////////////////////////////////////////////////////////////////////////
// Sky + lighting
////////////////////////////////////////////////////////////////////////////
// Set skyset
function setSkySet(set)
{
g_Environment.SkySet = set;
}
// Set sun colour RGB
function setSunColour(r, g, b)
{
g_Environment.SunColour = { "r" : r, "g" : g, "b" : b, "a" : 0};
}
// Set sun elevation
function setSunElevation(e)
{
g_Environment.SunElevation = e;
}
// Set sun rotation
function setSunRotation(r)
{
g_Environment.SunRotation = r;
}
// Set terrain ambient colour RGB (0-1)
function setTerrainAmbientColour(r, g, b)
{
g_Environment.TerrainAmbientColour = { "r" : r, "g" : g, "b" : b, "a" : 0};
}
// Set terrain ambient colour RGB (0-1)
function setUnitsAmbientColour(r, g, b)
{
g_Environment.UnitsAmbientColour = { "r" : r, "g" : g, "b" : b, "a" : 0};
}
////////////////////////////////////////////////////////////////////////////
// Water
////////////////////////////////////////////////////////////////////////////
// Set water colour RGB (0,1)
function setWaterColour(r, g, b)
{
g_Environment.Water.WaterBody.Colour = { "r" : r, "g" : g, "b" : b, "a" : 0};
}
// Set water height
function setWaterHeight(h)
{
g_Environment.Water.WaterBody.Height = h;
WATER_LEVEL_CHANGED = true;
}
-// Set water shininess
-function setWaterShininess(s)
-{
- g_Environment.Water.WaterBody.Shininess = s;
-}
-
// Set water waviness
function setWaterWaviness(w)
{
g_Environment.Water.WaterBody.Waviness = w;
}
// Set water murkiness
function setWaterMurkiness(m)
{
g_Environment.Water.WaterBody.Murkiness = m;
}
// Set water tint RGB (0,1)
function setWaterTint(r, g, b)
{
g_Environment.Water.WaterBody.Tint = { "r" : r, "g" : g, "b" : b, "a" : 0};
}
// Set water reflection tint RGB (0,1)
function setWaterReflectionTint(r, g, b)
{
g_Environment.Water.WaterBody.WaterReflectionTint = { "r" : r, "g" : g, "b" : b, "a" : 0};
}
// Set water reflection tint strength (0,1)
function setWaterReflectionTintStrength(s)
{
g_Environment.Water.WaterBody.WaterReflectionTintStrength = s;
}
// Set fog factor (0,1)
function setFogFactor(s)
{
g_Environment.Fog.FogFactor = s / 100.0;
}
// Set fog thickness (0,1)
function setFogThickness(s)
{
g_Environment.Fog.FogThickness = s;
}
// Set fog color RGB (0,1)
function setFogColor(r, g, b)
{
g_Environment.Fog.FogColor = { "r" : r, "g" : g, "b" : b, "a" : 0};
}
// Set postproc brightness (0,1)
function setPPBrightness(s)
{
g_Environment.Postproc.Brightness = s - 0.5;
}
// Set postproc contrast (0,1)
function setPPContrast(s)
{
g_Environment.Postproc.Contrast = s + 0.5;
}
// Set postproc saturation (0,1)
function setPPSaturation(s)
{
g_Environment.Postproc.Saturation = s * 2;
}
// Set postproc bloom (0,1)
function setPPBloom(s)
{
g_Environment.Postproc.Bloom = (1 - s) * 0.2;
}
// Set postproc effect ("default", "hdr", "DOF", "HQDOF")
function setPPEffect(s)
{
g_Environment.Postproc.PostprocEffect = s;
}
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/public/maps/random/rmgen/mapgen.js
===================================================================
--- ps/trunk/binaries/data/mods/public/maps/random/rmgen/mapgen.js (revision 14513)
+++ ps/trunk/binaries/data/mods/public/maps/random/rmgen/mapgen.js (revision 14514)
@@ -1,97 +1,96 @@
var TILE_CENTERED_HEIGHT_MAP = false;
var WATER_LEVEL_CHANGED = false;
var g_Map;
var g_Environment = {
SkySet: "default",
SunColour: {r: 0.749020, g: 0.749020, b: 0.749020, a: 0},
SunElevation: 0.785398,
SunRotation: 5.49779,
TerrainAmbientColour: {r: 0.501961, g: 0.501961, b: 0.501961, a: 0},
UnitsAmbientColour: {r: 0.501961, g: 0.501961, b: 0.501961, a: 0},
Water: {
WaterBody: {
Type: "default",
Colour: {r: 0.3, g: 0.35, b: 0.7, a: 0},
Height: 5,
- Shininess: 150,
Waviness: 8,
Murkiness: 0.45,
Tint: {r: 0.28, g: 0.3, b: 0.59, a: 0},
ReflectionTint: {r: 0.28, g: 0.3, b: 0.59, a: 0},
ReflectionTintStrength: 0.0
}
},
Fog: {
FogFactor: 0.0,
FogThickness: 0.5,
FogColor: {r: 0.8, g: 0.8, b: 0.8, a: 0}
},
Postproc: {
Brightness: 0.0,
Contrast: 1.0,
Saturation: 1.0,
Bloom: 0.2,
PostprocEffect: "default"
}
};
var g_Camera = {
Position: {x: 100, y: 150, z: -100},
Rotation: 0,
Declination: 0.523599
};
var g_CivData = {};
/////////////////////////////////////////////////////////////////////////////////////
function InitMap()
{
if (g_MapSettings === undefined)
{
// Should never get this far, failed settings would abort prior to loading scripts
throw("InitMapGen: settings missing");
}
// Get civ data as array of JSON strings
var data = RMS.GetCivData();
if (!data || !data.length)
{
throw("InitMapGen: error reading civ data");
}
for (var i = 0; i < data.length; ++i)
{
var civData = JSON.parse(data[i]);
g_CivData[civData.Code] = civData;
}
// Create new map
log("Creating new map...");
var terrain = createTerrain(g_MapSettings.BaseTerrain);
g_Map = new Map(g_MapSettings.Size, g_MapSettings.BaseHeight);
g_Map.initTerrain(terrain);
}
function ExportMap()
{ // Wrapper for engine function
log("Saving map...");
// Get necessary data from map
var data = g_Map.getMapData();
// Add environment and camera settings
if (!WATER_LEVEL_CHANGED)
{
g_Environment.Water.WaterBody.Height = SEA_LEVEL - 0.1;
}
data.Environment = g_Environment;
// Adjust default cam to roughly center of the map - useful for Atlas
g_Camera.Position = {x: g_MapSettings.Size*2, y: g_MapSettings.Size*2, z: -g_MapSettings.Size*2};
data.Camera = g_Camera;
RMS.ExportMap(data);
}
Index: ps/trunk/binaries/data/mods/public/shaders/glsl/model_water.fs
===================================================================
--- ps/trunk/binaries/data/mods/public/shaders/glsl/model_water.fs (revision 14513)
+++ ps/trunk/binaries/data/mods/public/shaders/glsl/model_water.fs (revision 14514)
@@ -1,152 +1,150 @@
#version 120
uniform sampler2D baseTex;
uniform sampler2D losTex;
uniform sampler2D aoTex;
uniform sampler2D normTex;
uniform sampler2D specTex;
uniform sampler2D waterTex;
uniform samplerCube skyCube;
#if USE_SHADOW
#if USE_SHADOW_SAMPLER
uniform sampler2DShadow shadowTex;
#if USE_SHADOW_PCF
uniform vec4 shadowScale;
#endif
#else
uniform sampler2D shadowTex;
#endif
#endif
#if USE_OBJECTCOLOR
uniform vec3 objectColor;
#else
#if USE_PLAYERCOLOR
uniform vec3 playerColor;
#endif
#endif
uniform vec3 shadingColor;
uniform vec3 ambient;
uniform vec3 sunColor;
uniform vec3 sunDir;
uniform vec3 cameraPos;
-
-uniform float shininess;
uniform float specularStrength;
uniform float waviness;
uniform vec3 waterTint;
uniform float murkiness;
uniform vec3 reflectionTint;
uniform float reflectionTintStrength;
float waterDepth = 4.0;
float fullDepth = 5.0; // Depth at which to use full murkiness (shallower water will be clearer)
varying vec4 worldPos;
varying vec4 v_tex;
varying vec4 v_shadow;
varying vec2 v_los;
float get_shadow(vec4 coords)
{
#if USE_SHADOW && !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
}
void main()
{
vec3 n, l, h, v; // Normal, light vector, half-vector and view vector (vector to eye)
float ndotl, ndoth, ndotv;
float fresnel;
float t; // Temporary variable
vec2 reflCoords, refrCoords;
vec3 reflColor, refrColor, specular;
float losMod;
//vec4 wtex = textureGrad(waterTex, vec3(fract(v_tex.xy), v_tex.z), dFdx(v_tex.xy), dFdy(v_tex.xy));
vec4 wtex = texture2D(waterTex, fract(v_tex.xy));
n = normalize(wtex.xzy - vec3(0.5, 0.5, 0.5));
l = -sunDir;
v = normalize(cameraPos - worldPos.xyz);
h = normalize(l + v);
ndotl = dot(n, l);
ndoth = dot(n, h);
ndotv = dot(n, v);
fresnel = pow(1.0 - ndotv, 0.8); // A rather random Fresnel approximation
//refrCoords = (0.5*gl_TexCoord[2].xy - 0.8*waviness*n.xz) / gl_TexCoord[2].w + 0.5; // Unbias texture coords
//reflCoords = (0.5*gl_TexCoord[1].xy + waviness*n.xz) / gl_TexCoord[1].w + 0.5; // Unbias texture coords
//vec3 dir = normalize(v + vec3(waviness*n.x, 0.0, waviness*n.z));
vec3 eye = reflect(v, n);
vec3 tex = textureCube(skyCube, eye).rgb;
reflColor = mix(tex, sunColor * reflectionTint,
reflectionTintStrength);
//waterDepth = 4.0 + 2.0 * dot(abs(v_tex.zw - 0.5), vec2(0.5));
waterDepth = 4.0;
//refrColor = (0.5 + 0.5*ndotl) * mix(texture2D(refractionMap, refrCoords).rgb, sunColor * tint,
refrColor = (0.5 + 0.5*ndotl) * mix(vec3(0.3), sunColor * waterTint,
murkiness * clamp(waterDepth / fullDepth, 0.0, 1.0)); // Murkiness and tint at this pixel (tweaked based on lighting and depth)
- specular = pow(max(0.0, ndoth), shininess) * sunColor * specularStrength;
+ specular = pow(max(0.0, ndoth), 150.0f) * sunColor * specularStrength;
losMod = texture2D(losTex, v_los).a;
//losMod = texture2D(losMap, gl_TexCoord[3].st).a;
#if USE_SHADOW
float shadow = get_shadow(vec4(v_shadow.xy - 8*waviness*n.xz, v_shadow.zw));
float fresShadow = mix(fresnel, fresnel*shadow, dot(sunColor, vec3(0.16666)));
#else
float fresShadow = fresnel;
#endif
vec3 colour = mix(refrColor + 0.3*specular, reflColor + specular, fresShadow);
gl_FragColor.rgb = colour * losMod;
//gl_FragColor.rgb = mix(refrColor + 0.3*specular, reflColor + specular, fresnel) * losMod;
// Make alpha vary based on both depth (so it blends with the shore) and view angle (make it
// become opaque faster at lower view angles so we can't look "underneath" the water plane)
t = 18.0 * max(0.0, 0.7 - v.y);
gl_FragColor.a = 0.15 * waterDepth * (1.2 + t + fresnel);
}
Index: ps/trunk/binaries/data/mods/public/shaders/glsl/water_high.fs
===================================================================
--- ps/trunk/binaries/data/mods/public/shaders/glsl/water_high.fs (revision 14513)
+++ ps/trunk/binaries/data/mods/public/shaders/glsl/water_high.fs (revision 14514)
@@ -1,317 +1,315 @@
#version 110
uniform vec3 ambient;
uniform vec3 sunDir;
uniform vec3 sunColor;
uniform vec3 cameraPos;
uniform sampler2D losMap;
-uniform float shininess; // Blinn-Phong specular strength
uniform float specularStrength; // Scaling for specular reflection (specular color is (this,this,this))
uniform float waviness; // "Wildness" of the reflections and refractions; choose based on texture
uniform vec3 tint; // Tint for refraction (used to simulate particles in water)
uniform float murkiness; // Amount of tint to blend in with the refracted colour
uniform vec3 reflectionTint; // Tint for reflection (used for really muddy water)
uniform float reflectionTintStrength; // Strength of reflection tint (how much of it to mix in)
uniform vec3 color; // color of the water
uniform vec3 fogColor;
uniform vec2 fogParams;
uniform vec2 screenSize;
uniform float time;
varying vec3 worldPos;
varying float waterDepth;
varying vec4 waterInfo;
uniform samplerCube skyCube;
uniform sampler2D normalMap;
uniform sampler2D normalMap2;
#if USE_REFLECTION
uniform sampler2D reflectionMap;
#endif
#if USE_REFRACTION
uniform sampler2D refractionMap;
#endif
#if USE_REAL_DEPTH
uniform sampler2D depthTex;
#endif
#if USE_FOAM || USE_WAVES
uniform sampler2D Foam;
uniform sampler2D waveTex;
#endif
#if USE_SHADOWS && 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 && !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
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);
}
void main()
{
#if USE_FOAM || USE_WAVES
vec4 heightmapval = waterInfo;
vec2 beachOrientation = heightmapval.rg;
float distToShore = heightmapval.b;
#endif
-
-
+
vec3 n, l, h, v; // Normal, light vector, half-vector and view vector (vector to eye)
float ndotl, ndoth, ndotv;
float fresnel;
float t; // Temporary variable
vec2 reflCoords, refrCoords;
vec3 reflColor, refrColor, specular;
float losMod;
float wavyFactor = waviness * 0.125;
l = -sunDir;
v = normalize(cameraPos - worldPos);
h = normalize(l + v);
// always done cause this is also used, even when not using normals, by the refraction.
vec3 ww = texture2D(normalMap, (gl_TexCoord[0].st) * mix(2.0,0.8,waviness/10.0) +gl_TexCoord[0].zw).xzy;
#if USE_NORMALS
vec3 ww2 = texture2D(normalMap2, (gl_TexCoord[0].st) * mix(2.0,0.8,waviness/10.0) +gl_TexCoord[0].zw).xzy;
ww = mix(ww, ww2, mod(time * 60.0, 8.0) / 8.0);
#if USE_WAVES
vec3 waves = texture2D(waveTex, gl_FragCoord.xy/screenSize).rbg - vec3(0.5,0.5,0.5);
float waveFoam = 0.0;//texture2D(waveTex, gl_FragCoord.xy/screenSize).a;
n = normalize(mix(waves, ww - vec3(0.5, 0.5, 0.5) , clamp(distToShore*3.0,0.4,1.0)));
#else
n = normalize(ww - vec3(0.5, 0.5, 0.5));
#endif
ndoth = dot( mix(vec3(0.0,1.0,0.0),n,clamp(wavyFactor * v.y * 8.0,0.05,1.0)) ,h);
n = mix(vec3(0.0,1.0,0.0),n,wavyFactor);
#else
ndoth = dot(vec3(0.0,1.0,0.0), h);
n = vec3(0.0,1.0,0.0);
#endif
ndotl = (dot(n, l) + 1.0)/2.0;
ndotv = clamp(dot(n, v),0.0,1.0);
#if USE_REAL_DEPTH
// Don't change these two. They should match the values in the config (TODO: dec uniforms).
float zNear = 2.0;
float zFar = 4096.0;
// Okay so here it's a tad complicated. I want to distort the depth buffer along the waves for a nice effect.
// However this causes a problem around underwater objects (think fishes): on some pixels, the depth will be seen as the same as the fishes'
// and the color will be grass ( cause I don't distort the refraction coord by exactly the same stuff)
// Also, things like towers with the feet in water would cause the buffer to see the depth as actually negative in some places.
// So what I do is first check the undistorted depth, then I compare with the distorted value and fix.
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 undistortedBuffer = texture2D(depthTex, (gl_FragCoord.xy) / screenSize).x;
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);
vec2 depthCoord = clamp((gl_FragCoord.xy) / screenSize - n.xz*clamp( waterDepth_undistorted/400.0,0.0,0.05) , 0.001, 0.999);
float z_b = texture2D(depthTex, depthCoord).x;
if (z_b < undisto_z_b)
z_b = undisto_z_b;
float z_n = 2.0 * z_b - 1.0;
float waterDepth2 = (2.0 * zNear * zFar / (zFar + zNear - z_n * (zFar - zNear)) - waterDBuffer);
float distoFactor = clamp(waterDepth2/3.0,0.0,7.0);
#else
float perceivedDepth = waterDepth / (v.y*v.y);
float distoFactor = clamp(perceivedDepth/4.0,0.0,7.0);
#endif
fresnel = pow(1.05 - ndotv, 1.3333); // approximation. I'm using 1.05 and not 1.0 because it causes artifacts, see #1714
#if USE_FOAM
// texture is rotated 90°, moves slowly.
vec2 foam1RC = vec2(-gl_TexCoord[0].t,gl_TexCoord[0].s)*1.3 - 0.012*n.xz + vec2(time*0.004,time*0.003);
// texture is not rotated, moves twice faster in the opposite direction, translated.
vec2 foam2RC = gl_TexCoord[0].st*1.8 + vec2(time*-0.019,time*-0.012) - 0.012*n.xz + vec2(0.4,0.2);
- vec2 WaveRocking = cos(time*1.2566) * beachOrientation * clamp(1.0 - distToShore,0.1,1.0)/3.0;
+ vec2 WaveRocking = cos(time*1.2566) * beachOrientation * clamp(1.0 - distToShore*0.8,0.1,1.0)/3.0;
vec4 foam1 = texture2D(Foam, foam1RC + vec2(-WaveRocking.t,WaveRocking.s));
vec4 foam2 = foam1.r*texture2D(Foam, foam2RC + WaveRocking);
vec3 finalFoam = min((foam2).rrr * waterInfo.a,1.0);
if ((1.0 - finalFoam.r) >= wavyFactor)
finalFoam = vec3(0.0);
#if USE_WAVES && USE_NORMALS
// waves bypass the regular foam restrictions.
finalFoam += min( max(0.0,-waves.b) * texture2D(Foam, foam1RC).r, 1.0)*3.0 * max(0.0,wavyFactor-0.1);
#endif
finalFoam *= sunColor;
#endif
#if USE_SHADOWS && USE_SHADOW
float shadow = get_shadow(vec4(v_shadow.xy, v_shadow.zw));
#endif
#if USE_REFRACTION
#if USE_REAL_DEPTH
refrCoords = clamp( (0.5*gl_TexCoord[2].xy - n.xz * distoFactor) / gl_TexCoord[2].w + 0.5,0.0,1.0); // Unbias texture coords
vec3 refColor = texture2D(refractionMap, refrCoords).rgb;
float luminance = (1.0 - clamp((waterDepth2/mix(300.0,1.0, pow(murkiness,0.2) )), 0.0, 1.0));
float colorExtinction = clamp(waterDepth2*murkiness/5.0,0.0,1.0);
#if USE_SHADOWS && USE_SHADOW
refrColor = (0.5 + 0.5*ndotl) * mix(color * (0.5 + shadow/2.0),mix(refColor,refColor*tint,colorExtinction),luminance*luminance);
#else
refrColor = (0.5 + 0.5*ndotl) * mix(color,mix(refColor,refColor*tint,colorExtinction),luminance*luminance);
#endif
#else
refrCoords = clamp( (0.5*gl_TexCoord[2].xy - n.xz * distoFactor) / gl_TexCoord[2].w + 0.5,0.0,1.0); // Unbias texture coords
// cleverly get the perceived depth based on camera tilting (if horizontal, it's likely we will have more water to look at).
vec3 refColor = texture2D(refractionMap, refrCoords).rgb;
float luminance = (1.0 - clamp((perceivedDepth/mix(300.0,1.0, pow(murkiness,0.2) )), 0.0, 1.0));
float colorExtinction = clamp(perceivedDepth*murkiness/5.0,0.0,1.0);
#if USE_SHADOWS && USE_SHADOW
refrColor = (0.5 + 0.5*ndotl) * mix(color * (0.5 + shadow/2.0),mix(refColor,refColor*tint,colorExtinction),luminance*luminance);
#else
refrColor = (0.5 + 0.5*ndotl) * mix(color,mix(refColor,refColor*tint,colorExtinction),luminance*luminance);
#endif
#endif
#else
float alphaCoeff = 0.0;
#if USE_REAL_DEPTH
float luminance = clamp((waterDepth2/mix(150.0,2.0, pow(murkiness,0.2) )), 0.0, 1.0);
alphaCoeff = mix(mix(0.0,3.0 - (tint.r + tint.g + tint.b),clamp(waterDepth2*murkiness/5.0,0.0,1.0)),1.0,luminance*luminance);
#else
float luminance = clamp(((waterDepth / v.y)/mix(150.0,2.0, pow(murkiness,0.2) )), 0.0, 1.0);
alphaCoeff = mix(mix(0.0,3.0 - (tint.r + tint.g + tint.b),clamp(perceivedDepth*murkiness/5.0,0.0,1.0)),1.0,luminance*luminance);
#endif
refrColor = color;
#endif
#if !USE_NORMALS
// we're not using normals. Simulate by applying a B&W effect.
refrColor *= (ww*2.0).x;
#endif
#if USE_REFLECTION
reflCoords = clamp( (0.5*gl_TexCoord[1].xy + distoFactor*1.5*n.xz) / gl_TexCoord[1].w + 0.5,0.0,1.0); // Unbias texture coords
reflColor = mix(texture2D(reflectionMap, reflCoords).rgb, sunColor * reflectionTint, reflectionTintStrength);
#else
vec3 eye = reflect(v, mix(vec3(0.0,1.0,0.0),n,0.2));
vec3 tex = textureCube(skyCube, eye).rgb;
reflColor = mix(tex, sunColor * reflectionTint, reflectionTintStrength);
#endif
#if USE_NORMALS
specular = pow(ndoth, mix(100.0,450.0, v.y*2.0)) * sunColor * 1.5;
#else
specular = pow(ndoth, mix(100.0,450.0, v.y*2.0)) * sunColor * 1.5 * ww.r;
#endif
losMod = texture2D(losMap, gl_TexCoord[3].st).a;
losMod = losMod < 0.03 ? 0.0 : losMod;
vec3 colour;
#if USE_SHADOWS && USE_SHADOW
float fresShadow = mix(fresnel, fresnel*shadow, 0.05 + murkiness*0.2);
#if USE_FOAM
colour = mix(refrColor, reflColor, fresShadow) + max(ndotl,0.4)*(finalFoam)*(shadow/2.0 + 0.5);
#else
colour = mix(refrColor, reflColor, fresShadow);
#endif
#else
#if USE_FOAM
colour = mix(refrColor, reflColor, fresnel) + max(ndotl,0.4)*(finalFoam);
#else
colour = mix(refrColor, reflColor, fresnel);
#endif
#endif
#if USE_REFRACTION
#if USE_REAL_DEPTH
colour = mix(texture2D(refractionMap, (0.5*gl_TexCoord[2].xy) / gl_TexCoord[2].w + 0.5).rgb ,colour, clamp(waterDepth2,0.0,1.0));
#else
colour = mix(texture2D(refractionMap, (0.5*gl_TexCoord[2].xy) / gl_TexCoord[2].w + 0.5).rgb ,colour, clamp(perceivedDepth,0.0,1.0));
#endif
#endif
#if USE_SHADOWS && USE_SHADOW
colour += shadow*specular;
#else
colour += specular;
#endif
gl_FragColor.rgb = get_fog(colour) * losMod;
#if USE_REAL_DEPTH
float alpha = clamp(waterDepth2*(5.0*murkiness),0.0,1.0);
#if !USE_REFRACTION
alpha *= alphaCoeff;
#endif
#if USE_FOAM
alpha += finalFoam.r * losMod;
#endif
gl_FragColor.a = alpha;
#else
// Make alpha vary based on both depth (so it blends with the shore) and view angle (make it
// become opaque faster at lower view angles so we can't look "underneath" the water plane)
t = 30.0 * max(0.0, 0.9 - v.y);
float alpha = clamp(0.15 * waterDepth * (1.2 + t + fresnel),0.0,1.0);
#if !USE_REFRACTION
gl_FragColor.a = alpha * alphaCoeff;
#else
gl_FragColor.a = alpha;
#endif
#endif
}
Index: ps/trunk/source/graphics/MapReader.cpp
===================================================================
--- ps/trunk/source/graphics/MapReader.cpp (revision 14513)
+++ ps/trunk/source/graphics/MapReader.cpp (revision 14514)
@@ -1,1544 +1,1545 @@
/* Copyright (C) 2013 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 .
*/
#include "precompiled.h"
#include "MapReader.h"
#include "graphics/Camera.h"
#include "graphics/CinemaTrack.h"
#include "graphics/Entity.h"
#include "graphics/GameView.h"
#include "graphics/MapGenerator.h"
#include "graphics/Patch.h"
#include "graphics/Terrain.h"
#include "graphics/TerrainTextureEntry.h"
#include "graphics/TerrainTextureManager.h"
#include "lib/timer.h"
#include "lib/external_libraries/libsdl.h"
#include "maths/MathUtil.h"
#include "ps/CLogger.h"
#include "ps/Loader.h"
#include "ps/LoaderThunks.h"
#include "ps/World.h"
#include "ps/XML/Xeromyces.h"
#include "renderer/PostprocManager.h"
#include "renderer/SkyManager.h"
#include "renderer/WaterManager.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpObstruction.h"
#include "simulation2/components/ICmpOwnership.h"
#include "simulation2/components/ICmpPlayer.h"
#include "simulation2/components/ICmpPlayerManager.h"
#include "simulation2/components/ICmpPosition.h"
#include "simulation2/components/ICmpTerrain.h"
#include "simulation2/components/ICmpVisual.h"
#include "simulation2/components/ICmpWaterManager.h"
#include
CMapReader::CMapReader()
: xml_reader(0), m_PatchesPerSide(0), m_MapGen(0)
{
cur_terrain_tex = 0; // important - resets generator state
// Maps that don't override the default probably want the old lighting model
//m_LightEnv.SetLightingModel("old");
//pPostproc->SetPostEffect(L"default");
}
// LoadMap: try to load the map from given file; reinitialise the scene to new data if successful
void CMapReader::LoadMap(const VfsPath& pathname, const CScriptValRooted& settings, CTerrain *pTerrain_,
WaterManager* pWaterMan_, SkyManager* pSkyMan_,
CLightEnv *pLightEnv_, CGameView *pGameView_, CCinemaManager* pCinema_, CTriggerManager* pTrigMan_, CPostprocManager* pPostproc_,
CSimulation2 *pSimulation2_, const CSimContext* pSimContext_, int playerID_, bool skipEntities)
{
// latch parameters (held until DelayedLoadFinished)
pTerrain = pTerrain_;
pLightEnv = pLightEnv_;
pGameView = pGameView_;
pWaterMan = pWaterMan_;
pSkyMan = pSkyMan_;
pCinema = pCinema_;
pTrigMan = pTrigMan_;
pPostproc = pPostproc_;
pSimulation2 = pSimulation2_;
pSimContext = pSimContext_;
m_PlayerID = playerID_;
m_SkipEntities = skipEntities;
m_StartingCameraTarget = INVALID_ENTITY;
m_ScriptSettings = settings;
filename_xml = pathname.ChangeExtension(L".xml");
// In some cases (particularly tests) we don't want to bother storing a large
// mostly-empty .pmp file, so we let the XML file specify basic terrain instead.
// If there's an .xml file and no .pmp, then we're probably in this XML-only mode
only_xml = false;
if (!VfsFileExists(pathname) && VfsFileExists(filename_xml))
{
only_xml = true;
}
file_format_version = CMapIO::FILE_VERSION; // default if there's no .pmp
if (!only_xml)
{
// [25ms]
unpacker.Read(pathname, "PSMP");
file_format_version = unpacker.GetVersion();
}
// check oldest supported version
if (file_format_version < FILE_READ_VERSION)
throw PSERROR_File_InvalidVersion();
// delete all existing entities
if (pSimulation2)
pSimulation2->ResetState();
// reset post effects
if (pPostproc)
pPostproc->SetPostEffect(L"default");
// load map or script settings script
if (settings.undefined())
RegMemFun(this, &CMapReader::LoadScriptSettings, L"CMapReader::LoadScriptSettings", 50);
else
RegMemFun(this, &CMapReader::LoadRMSettings, L"CMapReader::LoadRMSettings", 50);
// load player settings script (must be done before reading map)
RegMemFun(this, &CMapReader::LoadPlayerSettings, L"CMapReader::LoadPlayerSettings", 50);
// unpack the data
if (!only_xml)
RegMemFun(this, &CMapReader::UnpackMap, L"CMapReader::UnpackMap", 1200);
// read the corresponding XML file
RegMemFun(this, &CMapReader::ReadXML, L"CMapReader::ReadXML", 5800);
// apply data to the world
RegMemFun(this, &CMapReader::ApplyData, L"CMapReader::ApplyData", 5);
// load map settings script (must be done after reading map)
RegMemFun(this, &CMapReader::LoadMapSettings, L"CMapReader::LoadMapSettings", 5);
RegMemFun(this, &CMapReader::DelayLoadFinished, L"CMapReader::DelayLoadFinished", 5);
}
// LoadRandomMap: try to load the map data; reinitialise the scene to new data if successful
void CMapReader::LoadRandomMap(const CStrW& scriptFile, const CScriptValRooted& settings, CTerrain *pTerrain_,
WaterManager* pWaterMan_, SkyManager* pSkyMan_,
CLightEnv *pLightEnv_, CGameView *pGameView_, CCinemaManager* pCinema_, CTriggerManager* pTrigMan_, CPostprocManager* pPostproc_,
CSimulation2 *pSimulation2_, int playerID_)
{
// latch parameters (held until DelayedLoadFinished)
m_ScriptFile = scriptFile;
m_ScriptSettings = settings;
pTerrain = pTerrain_;
pLightEnv = pLightEnv_;
pGameView = pGameView_;
pWaterMan = pWaterMan_;
pSkyMan = pSkyMan_;
pCinema = pCinema_;
pTrigMan = pTrigMan_;
pPostproc = pPostproc_;
pSimulation2 = pSimulation2_;
pSimContext = pSimulation2 ? &pSimulation2->GetSimContext() : NULL;
m_PlayerID = playerID_;
m_SkipEntities = false;
m_StartingCameraTarget = INVALID_ENTITY;
// delete all existing entities
if (pSimulation2)
pSimulation2->ResetState();
only_xml = false;
// copy random map settings (before entity creation)
RegMemFun(this, &CMapReader::LoadRMSettings, L"CMapReader::LoadRMSettings", 50);
// load player settings script (must be done before reading map)
RegMemFun(this, &CMapReader::LoadPlayerSettings, L"CMapReader::LoadPlayerSettings", 50);
// load map generator with random map script
RegMemFun(this, &CMapReader::GenerateMap, L"CMapReader::GenerateMap", 5000);
// parse RMS results into terrain structure
RegMemFun(this, &CMapReader::ParseTerrain, L"CMapReader::ParseTerrain", 500);
// parse RMS results into environment settings
RegMemFun(this, &CMapReader::ParseEnvironment, L"CMapReader::ParseEnvironment", 5);
// parse RMS results into camera settings
RegMemFun(this, &CMapReader::ParseCamera, L"CMapReader::ParseCamera", 5);
// parse RMS results into entities
RegMemFun(this, &CMapReader::ParseEntities, L"CMapReader::ParseEntities", 1000);
// apply data to the world
RegMemFun(this, &CMapReader::ApplyData, L"CMapReader::ApplyData", 5);
// load map settings script (must be done after reading map)
RegMemFun(this, &CMapReader::LoadMapSettings, L"CMapReader::LoadMapSettings", 5);
RegMemFun(this, &CMapReader::DelayLoadFinished, L"CMapReader::DelayLoadFinished", 5);
}
// UnpackMap: unpack the given data from the raw data stream into local variables
int CMapReader::UnpackMap()
{
// now unpack everything into local data
int ret = UnpackTerrain();
if (ret != 0) // failed or timed out
{
return ret;
}
return 0;
}
// UnpackTerrain: unpack the terrain from the end of the input data stream
// - data: map size, heightmap, list of textures used by map, texture tile assignments
int CMapReader::UnpackTerrain()
{
// yield after this time is reached. balances increased progress bar
// smoothness vs. slowing down loading.
const double end_time = timer_Time() + 200e-3;
// first call to generator (this is skipped after first call,
// i.e. when the loop below was interrupted)
if (cur_terrain_tex == 0)
{
m_PatchesPerSide = (ssize_t)unpacker.UnpackSize();
// unpack heightmap [600us]
size_t verticesPerSide = m_PatchesPerSide*PATCH_SIZE+1;
m_Heightmap.resize(SQR(verticesPerSide));
unpacker.UnpackRaw(&m_Heightmap[0], SQR(verticesPerSide)*sizeof(u16));
// unpack # textures
num_terrain_tex = unpacker.UnpackSize();
m_TerrainTextures.reserve(num_terrain_tex);
}
// unpack texture names; find handle for each texture.
// interruptible.
while (cur_terrain_tex < num_terrain_tex)
{
CStr texturename;
unpacker.UnpackString(texturename);
ENSURE(CTerrainTextureManager::IsInitialised()); // we need this for the terrain properties (even when graphics are disabled)
CTerrainTextureEntry* texentry = g_TexMan.FindTexture(texturename);
m_TerrainTextures.push_back(texentry);
cur_terrain_tex++;
LDR_CHECK_TIMEOUT(cur_terrain_tex, num_terrain_tex);
}
// unpack tile data [3ms]
ssize_t tilesPerSide = m_PatchesPerSide*PATCH_SIZE;
m_Tiles.resize(size_t(SQR(tilesPerSide)));
unpacker.UnpackRaw(&m_Tiles[0], sizeof(STileDesc)*m_Tiles.size());
// reset generator state.
cur_terrain_tex = 0;
return 0;
}
// ApplyData: take all the input data, and rebuild the scene from it
int CMapReader::ApplyData()
{
if (m_PatchesPerSide == 0)
{
// we'll probably crash when trying to use this map later
throw PSERROR_Game_World_MapLoadFailed("Error loading map: no terrain data.\nCheck application log for details.");
}
if (!only_xml)
{
// initialise the terrain
pTerrain->Initialize(m_PatchesPerSide, &m_Heightmap[0]);
// setup the textures on the minipatches
STileDesc* tileptr = &m_Tiles[0];
for (ssize_t j=0; jGetPatch(i,j)->m_MiniPatches[m][k]; // can't fail
mp.Tex = m_TerrainTextures[tileptr->m_Tex1Index];
mp.Priority = tileptr->m_Priority;
tileptr++;
}
}
}
}
}
// copy over the lighting parameters
if (pLightEnv)
*pLightEnv = m_LightEnv;
CmpPtr cmpPlayerManager(*pSimContext, SYSTEM_ENTITY);
if (pGameView && cmpPlayerManager)
{
// Default to global camera (with constraints)
pGameView->ResetCameraTarget(pGameView->GetCamera()->GetFocus());
// TODO: Starting rotation?
CmpPtr cmpPlayer(*pSimContext, cmpPlayerManager->GetPlayerByID(m_PlayerID));
if (cmpPlayer && cmpPlayer->HasStartingCamera())
{
// Use player starting camera
CFixedVector3D pos = cmpPlayer->GetStartingCameraPos();
pGameView->ResetCameraTarget(CVector3D(pos.X.ToFloat(), pos.Y.ToFloat(), pos.Z.ToFloat()));
}
else if (m_StartingCameraTarget != INVALID_ENTITY)
{
// Point camera at entity
CmpPtr cmpPosition(*pSimContext, m_StartingCameraTarget);
if (cmpPosition)
{
CFixedVector3D pos = cmpPosition->GetPosition();
pGameView->ResetCameraTarget(CVector3D(pos.X.ToFloat(), pos.Y.ToFloat(), pos.Z.ToFloat()));
}
}
}
CmpPtr cmpTerrain(*pSimContext, SYSTEM_ENTITY);
if (cmpTerrain)
cmpTerrain->ReloadTerrain();
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
PSRETURN CMapSummaryReader::LoadMap(const VfsPath& pathname)
{
VfsPath filename_xml = pathname.ChangeExtension(L".xml");
CXeromyces xmb_file;
if (xmb_file.Load(g_VFS, filename_xml) != PSRETURN_OK)
return PSRETURN_File_ReadFailed;
// Define all the relevant elements used in the XML file
#define EL(x) int el_##x = xmb_file.GetElementID(#x)
#define AT(x) int at_##x = xmb_file.GetAttributeID(#x)
EL(scenario);
EL(scriptsettings);
#undef AT
#undef EL
XMBElement root = xmb_file.GetRoot();
ENSURE(root.GetNodeName() == el_scenario);
XERO_ITER_EL(root, child)
{
int child_name = child.GetNodeName();
if (child_name == el_scriptsettings)
{
m_ScriptSettings = child.GetText();
}
}
return PSRETURN_OK;
}
CScriptValRooted CMapSummaryReader::GetMapSettings(ScriptInterface& scriptInterface)
{
CScriptValRooted data;
scriptInterface.Eval("({})", data);
if (!m_ScriptSettings.empty())
scriptInterface.SetProperty(data.get(), "settings", scriptInterface.ParseJSON(m_ScriptSettings), false);
return data;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Holds various state data while reading maps, so that loading can be
// interrupted (e.g. to update the progress display) then later resumed.
class CXMLReader
{
NONCOPYABLE(CXMLReader);
public:
CXMLReader(const VfsPath& xml_filename, CMapReader& mapReader)
: m_MapReader(mapReader)
{
Init(xml_filename);
}
CStr ReadScriptSettings();
// return semantics: see Loader.cpp!LoadFunc.
int ProgressiveRead();
private:
CXeromyces xmb_file;
CMapReader& m_MapReader;
int el_entity;
int el_tracks;
int el_template, el_player;
int el_position, el_orientation, el_obstruction;
int el_actor;
int at_x, at_y, at_z;
int at_group, at_group2;
int at_id;
int at_angle;
int at_uid;
int at_seed;
XMBElementList nodes; // children of root
// loop counters
int node_idx;
int entity_idx;
// # entities+nonentities processed and total (for progress calc)
int completed_jobs, total_jobs;
// maximum used entity ID, so we can safely allocate new ones
entity_id_t max_uid;
void Init(const VfsPath& xml_filename);
void ReadTerrain(XMBElement parent);
void ReadEnvironment(XMBElement parent);
void ReadCamera(XMBElement parent);
void ReadCinema(XMBElement parent);
void ReadTriggers(XMBElement parent);
int ReadEntities(XMBElement parent, double end_time);
};
void CXMLReader::Init(const VfsPath& xml_filename)
{
// must only assign once, so do it here
node_idx = entity_idx = 0;
if (xmb_file.Load(g_VFS, xml_filename) != PSRETURN_OK)
throw PSERROR_File_ReadFailed();
// define the elements and attributes that are frequently used in the XML file,
// so we don't need to do lots of string construction and comparison when
// reading the data.
// (Needs to be synchronised with the list in CXMLReader - ugh)
#define EL(x) el_##x = xmb_file.GetElementID(#x)
#define AT(x) at_##x = xmb_file.GetAttributeID(#x)
EL(entity);
EL(tracks);
EL(template);
EL(player);
EL(position);
EL(orientation);
EL(obstruction);
EL(actor);
AT(x); AT(y); AT(z);
AT(group); AT(group2);
AT(angle);
AT(uid);
AT(seed);
#undef AT
#undef EL
XMBElement root = xmb_file.GetRoot();
ENSURE(xmb_file.GetElementString(root.GetNodeName()) == "Scenario");
nodes = root.GetChildNodes();
// find out total number of entities+nonentities
// (used when calculating progress)
completed_jobs = 0;
total_jobs = 0;
for (int i = 0; i < nodes.Count; i++)
total_jobs += nodes.Item(i).GetChildNodes().Count;
// Find the maximum entity ID, so we can safely allocate new IDs without conflicts
max_uid = SYSTEM_ENTITY;
XMBElement ents = nodes.GetFirstNamedItem(xmb_file.GetElementID("Entities"));
XERO_ITER_EL(ents, ent)
{
CStr uid = ent.GetAttributes().GetNamedItem(at_uid);
max_uid = std::max(max_uid, (entity_id_t)uid.ToUInt());
}
}
CStr CXMLReader::ReadScriptSettings()
{
XMBElement root = xmb_file.GetRoot();
ENSURE(xmb_file.GetElementString(root.GetNodeName()) == "Scenario");
nodes = root.GetChildNodes();
XMBElement settings = nodes.GetFirstNamedItem(xmb_file.GetElementID("ScriptSettings"));
return settings.GetText();
}
void CXMLReader::ReadTerrain(XMBElement parent)
{
#define AT(x) int at_##x = xmb_file.GetAttributeID(#x)
AT(patches);
AT(texture);
AT(priority);
AT(height);
#undef AT
ssize_t patches = 9;
CStr texture = "grass1_spring";
int priority = 0;
u16 height = 16384;
XERO_ITER_ATTR(parent, attr)
{
if (attr.Name == at_patches)
patches = attr.Value.ToInt();
else if (attr.Name == at_texture)
texture = attr.Value;
else if (attr.Name == at_priority)
priority = attr.Value.ToInt();
else if (attr.Name == at_height)
height = (u16)attr.Value.ToInt();
}
m_MapReader.m_PatchesPerSide = patches;
// Load the texture
ENSURE(CTerrainTextureManager::IsInitialised()); // we need this for the terrain properties (even when graphics are disabled)
CTerrainTextureEntry* texentry = g_TexMan.FindTexture(texture);
m_MapReader.pTerrain->Initialize(patches, NULL);
// Fill the heightmap
u16* heightmap = m_MapReader.pTerrain->GetHeightMap();
ssize_t verticesPerSide = m_MapReader.pTerrain->GetVerticesPerSide();
for (ssize_t i = 0; i < SQR(verticesPerSide); ++i)
heightmap[i] = height;
// Fill the texture map
for (ssize_t pz = 0; pz < patches; ++pz)
{
for (ssize_t px = 0; px < patches; ++px)
{
CPatch* patch = m_MapReader.pTerrain->GetPatch(px, pz); // can't fail
for (ssize_t z = 0; z < PATCH_SIZE; ++z)
{
for (ssize_t x = 0; x < PATCH_SIZE; ++x)
{
patch->m_MiniPatches[z][x].Tex = texentry;
patch->m_MiniPatches[z][x].Priority = priority;
}
}
}
}
}
void CXMLReader::ReadEnvironment(XMBElement parent)
{
#define EL(x) int el_##x = xmb_file.GetElementID(#x)
#define AT(x) int at_##x = xmb_file.GetAttributeID(#x)
EL(lightingmodel);
EL(posteffect);
EL(skyset);
EL(suncolour);
EL(sunelevation);
EL(sunrotation);
EL(terrainambientcolour);
EL(unitsambientcolour);
EL(water);
EL(waterbody);
EL(type);
EL(colour);
EL(height);
- EL(shininess);
+ EL(shininess); // for compatibility
EL(waviness);
EL(murkiness);
EL(tint);
EL(reflectiontint);
EL(reflectiontintstrength);
EL(fog);
EL(fogcolour);
EL(fogfactor);
EL(fogthickness);
EL(postproc);
EL(brightness);
EL(contrast);
EL(saturation);
EL(bloom);
AT(r); AT(g); AT(b);
#undef AT
#undef EL
XERO_ITER_EL(parent, element)
{
int element_name = element.GetNodeName();
XMBAttributeList attrs = element.GetAttributes();
if (element_name == el_lightingmodel)
{
// NOP - obsolete.
}
else if (element_name == el_skyset)
{
if (m_MapReader.pSkyMan)
m_MapReader.pSkyMan->SetSkySet(element.GetText().FromUTF8());
}
else if (element_name == el_suncolour)
{
m_MapReader.m_LightEnv.m_SunColor = RGBColor(
attrs.GetNamedItem(at_r).ToFloat(),
attrs.GetNamedItem(at_g).ToFloat(),
attrs.GetNamedItem(at_b).ToFloat());
}
else if (element_name == el_sunelevation)
{
m_MapReader.m_LightEnv.m_Elevation = attrs.GetNamedItem(at_angle).ToFloat();
}
else if (element_name == el_sunrotation)
{
m_MapReader.m_LightEnv.m_Rotation = attrs.GetNamedItem(at_angle).ToFloat();
}
else if (element_name == el_terrainambientcolour)
{
m_MapReader.m_LightEnv.m_TerrainAmbientColor = RGBColor(
attrs.GetNamedItem(at_r).ToFloat(),
attrs.GetNamedItem(at_g).ToFloat(),
attrs.GetNamedItem(at_b).ToFloat());
}
else if (element_name == el_unitsambientcolour)
{
m_MapReader.m_LightEnv.m_UnitsAmbientColor = RGBColor(
attrs.GetNamedItem(at_r).ToFloat(),
attrs.GetNamedItem(at_g).ToFloat(),
attrs.GetNamedItem(at_b).ToFloat());
}
else if (element_name == el_fog)
{
XERO_ITER_EL(element, fog)
{
int element_name = fog.GetNodeName();
if (element_name == el_fogcolour)
{
XMBAttributeList attrs = fog.GetAttributes();
m_MapReader.m_LightEnv.m_FogColor = RGBColor(
attrs.GetNamedItem(at_r).ToFloat(),
attrs.GetNamedItem(at_g).ToFloat(),
attrs.GetNamedItem(at_b).ToFloat());
}
else if (element_name == el_fogfactor)
{
m_MapReader.m_LightEnv.m_FogFactor = fog.GetText().ToFloat();
}
else if (element_name == el_fogthickness)
{
m_MapReader.m_LightEnv.m_FogMax = fog.GetText().ToFloat();
}
}
}
else if (element_name == el_postproc)
{
XERO_ITER_EL(element, postproc)
{
int element_name = postproc.GetNodeName();
if (element_name == el_brightness)
{
m_MapReader.m_LightEnv.m_Brightness = postproc.GetText().ToFloat();
}
else if (element_name == el_contrast)
{
m_MapReader.m_LightEnv.m_Contrast = postproc.GetText().ToFloat();
}
else if (element_name == el_saturation)
{
m_MapReader.m_LightEnv.m_Saturation = postproc.GetText().ToFloat();
}
else if (element_name == el_bloom)
{
m_MapReader.m_LightEnv.m_Bloom = postproc.GetText().ToFloat();
}
else if (element_name == el_posteffect)
{
if (m_MapReader.pPostproc)
m_MapReader.pPostproc->SetPostEffect(postproc.GetText().FromUTF8());
}
}
}
else if (element_name == el_water)
{
XERO_ITER_EL(element, waterbody)
{
ENSURE(waterbody.GetNodeName() == el_waterbody);
XERO_ITER_EL(waterbody, waterelement)
{
int element_name = waterelement.GetNodeName();
if (element_name == el_height)
{
CmpPtr cmpWaterManager(*m_MapReader.pSimContext, SYSTEM_ENTITY);
ENSURE(cmpWaterManager);
cmpWaterManager->SetWaterLevel(entity_pos_t::FromString(waterelement.GetText()));
continue;
}
// The rest are purely graphical effects, and should be ignored if
// graphics are disabled
if (!m_MapReader.pWaterMan)
continue;
+ float this_avoids_a_warning_about_unused_variables = 0;
+
if (element_name == el_type)
{
// TODO: implement this, when WaterManager supports it
}
#define READ_COLOUR(el, out) \
else if (element_name == el) \
{ \
XMBAttributeList attrs = waterelement.GetAttributes(); \
out = CColor( \
attrs.GetNamedItem(at_r).ToFloat(), \
attrs.GetNamedItem(at_g).ToFloat(), \
attrs.GetNamedItem(at_b).ToFloat(), \
1.f); \
}
#define READ_FLOAT(el, out) \
else if (element_name == el) \
{ \
out = waterelement.GetText().ToFloat(); \
} \
READ_COLOUR(el_colour, m_MapReader.pWaterMan->m_WaterColor)
- READ_FLOAT(el_shininess, m_MapReader.pWaterMan->m_Shininess)
+ READ_FLOAT(el_shininess, this_avoids_a_warning_about_unused_variables)
READ_FLOAT(el_waviness, m_MapReader.pWaterMan->m_Waviness)
READ_FLOAT(el_murkiness, m_MapReader.pWaterMan->m_Murkiness)
READ_COLOUR(el_tint, m_MapReader.pWaterMan->m_WaterTint)
READ_COLOUR(el_reflectiontint, m_MapReader.pWaterMan->m_ReflectionTint)
READ_FLOAT(el_reflectiontintstrength, m_MapReader.pWaterMan->m_ReflectionTintStrength)
#undef READ_FLOAT
#undef READ_COLOUR
else
debug_warn(L"Invalid map XML data");
}
}
}
else
debug_warn(L"Invalid map XML data");
}
m_MapReader.m_LightEnv.CalculateSunDirection();
}
void CXMLReader::ReadCamera(XMBElement parent)
{
// defaults if we don't find player starting camera
#define EL(x) int el_##x = xmb_file.GetElementID(#x)
#define AT(x) int at_##x = xmb_file.GetAttributeID(#x)
EL(declination);
EL(rotation);
EL(position);
AT(angle);
AT(x); AT(y); AT(z);
#undef AT
#undef EL
float declination = DEGTORAD(30.f), rotation = DEGTORAD(-45.f);
CVector3D translation = CVector3D(100, 150, -100);
XERO_ITER_EL(parent, element)
{
int element_name = element.GetNodeName();
XMBAttributeList attrs = element.GetAttributes();
if (element_name == el_declination)
{
declination = attrs.GetNamedItem(at_angle).ToFloat();
}
else if (element_name == el_rotation)
{
rotation = attrs.GetNamedItem(at_angle).ToFloat();
}
else if (element_name == el_position)
{
translation = CVector3D(
attrs.GetNamedItem(at_x).ToFloat(),
attrs.GetNamedItem(at_y).ToFloat(),
attrs.GetNamedItem(at_z).ToFloat());
}
else
debug_warn(L"Invalid map XML data");
}
if (m_MapReader.pGameView)
{
m_MapReader.pGameView->GetCamera()->m_Orientation.SetXRotation(declination);
m_MapReader.pGameView->GetCamera()->m_Orientation.RotateY(rotation);
m_MapReader.pGameView->GetCamera()->m_Orientation.Translate(translation);
m_MapReader.pGameView->GetCamera()->UpdateFrustum();
}
}
void CXMLReader::ReadCinema(XMBElement parent)
{
#define EL(x) int el_##x = xmb_file.GetElementID(#x)
#define AT(x) int at_##x = xmb_file.GetAttributeID(#x)
EL(path);
EL(rotation);
EL(distortion);
EL(node);
EL(position);
EL(time);
AT(name);
AT(timescale);
AT(mode);
AT(style);
AT(growth);
AT(switch);
AT(x);
AT(y);
AT(z);
#undef EL
#undef AT
std::map pathList;
XERO_ITER_EL(parent, element)
{
int elementName = element.GetNodeName();
if ( elementName == el_path )
{
XMBAttributeList attrs = element.GetAttributes();
CStrW name(attrs.GetNamedItem(at_name).FromUTF8());
float timescale = attrs.GetNamedItem(at_timescale).ToFloat();
CCinemaData pathData;
pathData.m_Timescale = timescale;
TNSpline spline, backwardSpline;
XERO_ITER_EL(element, pathChild)
{
elementName = pathChild.GetNodeName();
attrs = pathChild.GetAttributes();
//Load distortion attributes
if ( elementName == el_distortion )
{
pathData.m_Mode = attrs.GetNamedItem(at_mode).ToInt();
pathData.m_Style = attrs.GetNamedItem(at_style).ToInt();
pathData.m_Growth = attrs.GetNamedItem(at_growth).ToInt();
pathData.m_Switch = attrs.GetNamedItem(at_switch).ToInt();
}
//Load node data used for spline
else if ( elementName == el_node )
{
SplineData data;
XERO_ITER_EL(pathChild, nodeChild)
{
elementName = nodeChild.GetNodeName();
attrs = nodeChild.GetAttributes();
//Fix?: assumes that time is last element
if ( elementName == el_position )
{
data.Position.X = attrs.GetNamedItem(at_x).ToFloat();
data.Position.Y = attrs.GetNamedItem(at_y).ToFloat();
data.Position.Z = attrs.GetNamedItem(at_z).ToFloat();
continue;
}
else if ( elementName == el_rotation )
{
data.Rotation.X = attrs.GetNamedItem(at_x).ToFloat();
data.Rotation.Y = attrs.GetNamedItem(at_y).ToFloat();
data.Rotation.Z = attrs.GetNamedItem(at_z).ToFloat();
continue;
}
else if ( elementName == el_time )
data.Distance = nodeChild.GetText().ToFloat();
else
debug_warn(L"Invalid cinematic element for node child");
backwardSpline.AddNode(data.Position, data.Rotation, data.Distance);
}
}
else
debug_warn(L"Invalid cinematic element for path child");
}
//Construct cinema path with data gathered
CCinemaPath temp(pathData, backwardSpline);
const std::vector& nodes = temp.GetAllNodes();
if ( nodes.empty() )
{
debug_warn(L"Failure loading cinematics");
return;
}
for ( std::vector::const_reverse_iterator it = nodes.rbegin();
it != nodes.rend(); ++it )
{
spline.AddNode(it->Position, it->Rotation, it->Distance);
}
CCinemaPath path(pathData, spline);
pathList[name] = path;
}
else
ENSURE("Invalid cinema child");
}
if (m_MapReader.pCinema)
m_MapReader.pCinema->SetAllPaths(pathList);
}
void CXMLReader::ReadTriggers(XMBElement UNUSED(parent))
{
}
int CXMLReader::ReadEntities(XMBElement parent, double end_time)
{
XMBElementList entities = parent.GetChildNodes();
ENSURE(m_MapReader.pSimulation2);
CSimulation2& sim = *m_MapReader.pSimulation2;
CmpPtr cmpPlayerManager(sim, SYSTEM_ENTITY);
while (entity_idx < entities.Count)
{
// all new state at this scope and below doesn't need to be
// wrapped, since we only yield after a complete iteration.
XMBElement entity = entities.Item(entity_idx++);
ENSURE(entity.GetNodeName() == el_entity);
XMBAttributeList attrs = entity.GetAttributes();
CStr uid = attrs.GetNamedItem(at_uid);
ENSURE(!uid.empty());
int EntityUid = uid.ToInt();
CStrW TemplateName;
int PlayerID = 0;
CFixedVector3D Position;
CFixedVector3D Orientation;
long Seed = -1;
// Obstruction control groups.
entity_id_t ControlGroup = INVALID_ENTITY;
entity_id_t ControlGroup2 = INVALID_ENTITY;
XERO_ITER_EL(entity, setting)
{
int element_name = setting.GetNodeName();
//
if (element_name == el_template)
{
TemplateName = setting.GetText().FromUTF8();
}
//
else if (element_name == el_player)
{
PlayerID = setting.GetText().ToInt();
}
//
else if (element_name == el_position)
{
XMBAttributeList attrs = setting.GetAttributes();
Position = CFixedVector3D(
fixed::FromString(attrs.GetNamedItem(at_x)),
fixed::FromString(attrs.GetNamedItem(at_y)),
fixed::FromString(attrs.GetNamedItem(at_z)));
}
//
else if (element_name == el_orientation)
{
XMBAttributeList attrs = setting.GetAttributes();
Orientation = CFixedVector3D(
fixed::FromString(attrs.GetNamedItem(at_x)),
fixed::FromString(attrs.GetNamedItem(at_y)),
fixed::FromString(attrs.GetNamedItem(at_z)));
// TODO: what happens if some attributes are missing?
}
//
else if (element_name == el_obstruction)
{
XMBAttributeList attrs = setting.GetAttributes();
ControlGroup = attrs.GetNamedItem(at_group).ToInt();
ControlGroup2 = attrs.GetNamedItem(at_group2).ToInt();
}
//
else if (element_name == el_actor)
{
XMBAttributeList attrs = setting.GetAttributes();
CStr seedStr = attrs.GetNamedItem(at_seed);
if (!seedStr.empty())
{
Seed = seedStr.ToLong();
ENSURE(Seed >= 0);
}
}
else
debug_warn(L"Invalid map XML data");
}
entity_id_t ent = sim.AddEntity(TemplateName, EntityUid);
entity_id_t player = cmpPlayerManager->GetPlayerByID(PlayerID);
if (ent == INVALID_ENTITY || player == INVALID_ENTITY)
{ // Don't add entities with invalid player IDs
LOGERROR(L"Failed to load entity template '%ls'", TemplateName.c_str());
}
else
{
CmpPtr cmpPosition(sim, ent);
if (cmpPosition)
{
cmpPosition->JumpTo(Position.X, Position.Z);
cmpPosition->SetYRotation(Orientation.Y);
// TODO: other parts of the position
}
CmpPtr cmpOwnership(sim, ent);
if (cmpOwnership)
cmpOwnership->SetOwner(PlayerID);
CmpPtr cmpObstruction(sim, ent);
if (cmpObstruction)
{
if (ControlGroup != INVALID_ENTITY)
cmpObstruction->SetControlGroup(ControlGroup);
if (ControlGroup2 != INVALID_ENTITY)
cmpObstruction->SetControlGroup2(ControlGroup2);
cmpObstruction->ResolveFoundationCollisions();
}
CmpPtr cmpVisual(sim, ent);
if (cmpVisual)
{
if (Seed != -1)
cmpVisual->SetActorSeed((u32)Seed);
// TODO: variation/selection strings
}
if (PlayerID == m_MapReader.m_PlayerID && (boost::algorithm::ends_with(TemplateName, L"civil_centre") || m_MapReader.m_StartingCameraTarget == INVALID_ENTITY))
{
// Focus on civil centre or first entity owned by player
m_MapReader.m_StartingCameraTarget = ent;
}
}
completed_jobs++;
LDR_CHECK_TIMEOUT(completed_jobs, total_jobs);
}
return 0;
}
int CXMLReader::ProgressiveRead()
{
// yield after this time is reached. balances increased progress bar
// smoothness vs. slowing down loading.
const double end_time = timer_Time() + 200e-3;
int ret;
while (node_idx < nodes.Count)
{
XMBElement node = nodes.Item(node_idx);
CStr name = xmb_file.GetElementString(node.GetNodeName());
if (name == "Terrain")
{
ReadTerrain(node);
}
else if (name == "Environment")
{
ReadEnvironment(node);
}
else if (name == "Camera")
{
ReadCamera(node);
}
else if (name == "ScriptSettings")
{
//Already loaded - this is to prevent an assertion
}
else if (name == "Entities")
{
if (!m_MapReader.m_SkipEntities)
{
ret = ReadEntities(node, end_time);
if (ret != 0) // error or timed out
return ret;
}
}
else if (name == "Paths")
{
ReadCinema(node);
}
else if (name == "Triggers")
{
ReadTriggers(node);
}
else if (name == "Script")
{
if (m_MapReader.pSimulation2)
m_MapReader.pSimulation2->SetStartupScript(node.GetText());
}
else
{
debug_printf(L"Invalid XML element in map file: %hs\n", name.c_str());
debug_warn(L"Invalid map XML data");
}
node_idx++;
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// load script settings from map
int CMapReader::LoadScriptSettings()
{
if (!xml_reader)
xml_reader = new CXMLReader(filename_xml, *this);
// parse the script settings
if (pSimulation2)
pSimulation2->SetMapSettings(xml_reader->ReadScriptSettings());
return 0;
}
// load player settings script
int CMapReader::LoadPlayerSettings()
{
if (pSimulation2)
pSimulation2->LoadPlayerSettings(true);
return 0;
}
// load map settings script
int CMapReader::LoadMapSettings()
{
if (pSimulation2)
pSimulation2->LoadMapSettings();
return 0;
}
// progressive
int CMapReader::ReadXML()
{
if (!xml_reader)
xml_reader = new CXMLReader(filename_xml, *this);
int ret = xml_reader->ProgressiveRead();
// finished or failed
if (ret <= 0)
{
SAFE_DELETE(xml_reader);
}
return ret;
}
int CMapReader::DelayLoadFinished()
{
// we were dynamically allocated by CWorld::Initialize
delete this;
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int CMapReader::LoadRMSettings()
{
// copy random map settings over to sim
ENSURE(pSimulation2);
pSimulation2->SetMapSettings(m_ScriptSettings);
return 0;
}
int CMapReader::GenerateMap()
{
if (!m_MapGen)
{
// Initialize map generator
m_MapGen = new CMapGenerator();
VfsPath scriptPath;
if (m_ScriptFile.length())
scriptPath = L"maps/random/"+m_ScriptFile;
// Stringify settings to pass across threads
std::string scriptSettings = pSimulation2->GetScriptInterface().StringifyJSON(m_ScriptSettings.get());
// Try to generate map
m_MapGen->GenerateMap(scriptPath, scriptSettings);
}
// Check status
int progress = m_MapGen->GetProgress();
if (progress < 0)
{
// RMS failed - return to main menu
throw PSERROR_Game_World_MapLoadFailed("Error generating random map.\nCheck application log for details.");
}
else if (progress == 0)
{
// Finished, get results as StructuredClone object, which must be read to obtain the JS val
shared_ptr results = m_MapGen->GetResults();
// Parse data into simulation context
CScriptValRooted data(pSimulation2->GetScriptInterface().GetContext(), pSimulation2->GetScriptInterface().ReadStructuredClone(results));
if (data.undefined())
{
// RMS failed - return to main menu
throw PSERROR_Game_World_MapLoadFailed("Error generating random map.\nCheck application log for details.");
}
else
{
m_MapData = data;
}
}
else
{
// Still working
// Sleep for a while, slowing down the rendering thread
// to allow more CPU for the map generator thread
SDL_Delay(100);
}
// return progress
return progress;
};
int CMapReader::ParseTerrain()
{
TIMER(L"ParseTerrain");
// parse terrain from map data
// an error here should stop the loading process
#define GET_TERRAIN_PROPERTY(val, prop, out)\
if (!pSimulation2->GetScriptInterface().GetProperty(val, #prop, out))\
{ LOGERROR(L"CMapReader::ParseTerrain() failed to get '%hs' property", #prop);\
throw PSERROR_Game_World_MapLoadFailed("Error parsing terrain data.\nCheck application log for details"); }
u32 size;
GET_TERRAIN_PROPERTY(m_MapData.get(), size, size)
m_PatchesPerSide = size / PATCH_SIZE;
// flat heightmap of u16 data
GET_TERRAIN_PROPERTY(m_MapData.get(), height, m_Heightmap)
// load textures
std::vector textureNames;
GET_TERRAIN_PROPERTY(m_MapData.get(), textureNames, textureNames)
num_terrain_tex = textureNames.size();
while (cur_terrain_tex < num_terrain_tex)
{
ENSURE(CTerrainTextureManager::IsInitialised()); // we need this for the terrain properties (even when graphics are disabled)
CTerrainTextureEntry* texentry = g_TexMan.FindTexture(textureNames[cur_terrain_tex]);
m_TerrainTextures.push_back(texentry);
cur_terrain_tex++;
}
// build tile data
m_Tiles.resize(SQR(size));
CScriptValRooted tileData;
GET_TERRAIN_PROPERTY(m_MapData.get(), tileData, tileData)
// parse tile data object into flat arrays
std::vector tileIndex;
std::vector tilePriority;
GET_TERRAIN_PROPERTY(tileData.get(), index, tileIndex);
GET_TERRAIN_PROPERTY(tileData.get(), priority, tilePriority);
ENSURE(SQR(size) == tileIndex.size() && SQR(size) == tilePriority.size());
// reorder by patches and store
for (size_t x = 0; x < size; ++x)
{
size_t patchX = x / PATCH_SIZE;
size_t offX = x % PATCH_SIZE;
for (size_t y = 0; y < size; ++y)
{
size_t patchY = y / PATCH_SIZE;
size_t offY = y % PATCH_SIZE;
STileDesc tile;
tile.m_Tex1Index = tileIndex[y*size + x];
tile.m_Tex2Index = 0xFFFF;
tile.m_Priority = tilePriority[y*size + x];
m_Tiles[(patchY * m_PatchesPerSide + patchX) * SQR(PATCH_SIZE) + (offY * PATCH_SIZE + offX)] = tile;
}
}
// reset generator state
cur_terrain_tex = 0;
#undef GET_TERRAIN_PROPERTY
return 0;
}
int CMapReader::ParseEntities()
{
TIMER(L"ParseEntities");
// parse entities from map data
std::vector entities;
if (!pSimulation2->GetScriptInterface().GetProperty(m_MapData.get(), "entities", entities))
LOGWARNING(L"CMapReader::ParseEntities() failed to get 'entities' property");
CSimulation2& sim = *pSimulation2;
CmpPtr cmpPlayerManager(sim, SYSTEM_ENTITY);
size_t entity_idx = 0;
size_t num_entities = entities.size();
Entity currEnt;
while (entity_idx < num_entities)
{
// Get current entity struct
currEnt = entities[entity_idx];
entity_id_t ent = pSimulation2->AddEntity(currEnt.templateName, currEnt.entityID);
entity_id_t player = cmpPlayerManager->GetPlayerByID(currEnt.playerID);
if (ent == INVALID_ENTITY || player == INVALID_ENTITY)
{ // Don't add entities with invalid player IDs
LOGERROR(L"Failed to load entity template '%ls'", currEnt.templateName.c_str());
}
else
{
CmpPtr cmpPosition(sim, ent);
if (cmpPosition)
{
cmpPosition->JumpTo(currEnt.position.X, currEnt.position.Z);
cmpPosition->SetYRotation(currEnt.rotation.Y);
// TODO: other parts of the position
}
CmpPtr cmpOwnership(sim, ent);
if (cmpOwnership)
cmpOwnership->SetOwner(currEnt.playerID);
// Detect and fix collisions between foundation-blocking entities.
// This presently serves to copy wall tower control groups to wall
// segments, allowing players to expand RMS-generated walls.
CmpPtr cmpObstruction(sim, ent);
if (cmpObstruction)
cmpObstruction->ResolveFoundationCollisions();
if (currEnt.playerID == m_PlayerID && (boost::algorithm::ends_with(currEnt.templateName, L"civil_centre") || m_StartingCameraTarget == INVALID_ENTITY))
{
// Focus on civil centre or first entity owned by player
m_StartingCameraTarget = currEnt.entityID;
}
}
entity_idx++;
}
return 0;
}
int CMapReader::ParseEnvironment()
{
// parse environment settings from map data
#define GET_ENVIRONMENT_PROPERTY(val, prop, out)\
if (!pSimulation2->GetScriptInterface().GetProperty(val, #prop, out))\
LOGWARNING(L"CMapReader::ParseEnvironment() failed to get '%hs' property", #prop);
CScriptValRooted envObj;
GET_ENVIRONMENT_PROPERTY(m_MapData.get(), Environment, envObj)
if (envObj.undefined())
{
LOGWARNING(L"CMapReader::ParseEnvironment(): Environment settings not found");
return 0;
}
//m_LightEnv.SetLightingModel("standard");
if (pPostproc)
pPostproc->SetPostEffect(L"default");
std::wstring skySet;
GET_ENVIRONMENT_PROPERTY(envObj.get(), SkySet, skySet)
if (pSkyMan)
pSkyMan->SetSkySet(skySet);
CColor sunColor;
GET_ENVIRONMENT_PROPERTY(envObj.get(), SunColour, sunColor)
m_LightEnv.m_SunColor = RGBColor(sunColor.r, sunColor.g, sunColor.b);
GET_ENVIRONMENT_PROPERTY(envObj.get(), SunElevation, m_LightEnv.m_Elevation)
GET_ENVIRONMENT_PROPERTY(envObj.get(), SunRotation, m_LightEnv.m_Rotation)
CColor terrainAmbientColor;
GET_ENVIRONMENT_PROPERTY(envObj.get(), TerrainAmbientColour, terrainAmbientColor)
m_LightEnv.m_TerrainAmbientColor = RGBColor(terrainAmbientColor.r, terrainAmbientColor.g, terrainAmbientColor.b);
CColor unitsAmbientColor;
GET_ENVIRONMENT_PROPERTY(envObj.get(), UnitsAmbientColour, unitsAmbientColor)
m_LightEnv.m_UnitsAmbientColor = RGBColor(unitsAmbientColor.r, unitsAmbientColor.g, unitsAmbientColor.b);
// Water properties
CScriptValRooted waterObj;
GET_ENVIRONMENT_PROPERTY(envObj.get(), Water, waterObj)
CScriptValRooted waterBodyObj;
GET_ENVIRONMENT_PROPERTY(waterObj.get(), WaterBody, waterBodyObj)
// Water level - necessary
float waterHeight;
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), Height, waterHeight)
CmpPtr cmpWaterManager(*pSimulation2, SYSTEM_ENTITY);
ENSURE(cmpWaterManager);
cmpWaterManager->SetWaterLevel(entity_pos_t::FromFloat(waterHeight));
// If we have graphics, get rest of settings
if (pWaterMan)
{
// TODO: Water type not implemented
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), Colour, pWaterMan->m_WaterColor)
- GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), Shininess, pWaterMan->m_Shininess)
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), Waviness, pWaterMan->m_Waviness)
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), Murkiness, pWaterMan->m_Murkiness)
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), Tint, pWaterMan->m_WaterTint)
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), ReflectionTint, pWaterMan->m_ReflectionTint)
GET_ENVIRONMENT_PROPERTY(waterBodyObj.get(), ReflectionTintStrength, pWaterMan->m_ReflectionTintStrength)
}
CScriptValRooted fogObject;
GET_ENVIRONMENT_PROPERTY(envObj.get(), Fog, fogObject);
GET_ENVIRONMENT_PROPERTY(fogObject.get(), FogFactor, m_LightEnv.m_FogFactor);
GET_ENVIRONMENT_PROPERTY(fogObject.get(), FogThickness, m_LightEnv.m_FogMax);
CColor fogColor;
GET_ENVIRONMENT_PROPERTY(fogObject.get(), FogColor, fogColor);
m_LightEnv.m_FogColor = RGBColor(fogColor.r, fogColor.g, fogColor.b);
CScriptValRooted postprocObject;
GET_ENVIRONMENT_PROPERTY(envObj.get(), Postproc, postprocObject);
std::wstring postProcEffect;
GET_ENVIRONMENT_PROPERTY(postprocObject.get(), PostprocEffect, postProcEffect);
if (pPostproc)
pPostproc->SetPostEffect(postProcEffect);
GET_ENVIRONMENT_PROPERTY(postprocObject.get(), Brightness, m_LightEnv.m_Brightness);
GET_ENVIRONMENT_PROPERTY(postprocObject.get(), Contrast, m_LightEnv.m_Contrast);
GET_ENVIRONMENT_PROPERTY(postprocObject.get(), Saturation, m_LightEnv.m_Saturation);
GET_ENVIRONMENT_PROPERTY(postprocObject.get(), Bloom, m_LightEnv.m_Bloom);
m_LightEnv.CalculateSunDirection();
#undef GET_ENVIRONMENT_PROPERTY
return 0;
}
int CMapReader::ParseCamera()
{
// parse camera settings from map data
// defaults if we don't find player starting camera
float declination = DEGTORAD(30.f), rotation = DEGTORAD(-45.f);
CVector3D translation = CVector3D(100, 150, -100);
#define GET_CAMERA_PROPERTY(val, prop, out)\
if (!pSimulation2->GetScriptInterface().GetProperty(val, #prop, out))\
LOGWARNING(L"CMapReader::ParseCamera() failed to get '%hs' property", #prop);
CScriptValRooted cameraObj;
GET_CAMERA_PROPERTY(m_MapData.get(), Camera, cameraObj)
if (!cameraObj.undefined())
{ // If camera property exists, read values
CFixedVector3D pos;
GET_CAMERA_PROPERTY(cameraObj.get(), Position, pos)
translation = pos;
GET_CAMERA_PROPERTY(cameraObj.get(), Rotation, rotation)
GET_CAMERA_PROPERTY(cameraObj.get(), Declination, declination)
}
#undef GET_CAMERA_PROPERTY
if (pGameView)
{
pGameView->GetCamera()->m_Orientation.SetXRotation(declination);
pGameView->GetCamera()->m_Orientation.RotateY(rotation);
pGameView->GetCamera()->m_Orientation.Translate(translation);
pGameView->GetCamera()->UpdateFrustum();
}
return 0;
}
CMapReader::~CMapReader()
{
// Cleaup objects
delete xml_reader;
delete m_MapGen;
}
Index: ps/trunk/source/graphics/MapWriter.cpp
===================================================================
--- ps/trunk/source/graphics/MapWriter.cpp (revision 14513)
+++ ps/trunk/source/graphics/MapWriter.cpp (revision 14514)
@@ -1,568 +1,567 @@
/* Copyright (C) 2013 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 .
*/
#include "precompiled.h"
#include "Camera.h"
#include "CinemaTrack.h"
#include "GameView.h"
#include "LightEnv.h"
#include "MapReader.h"
#include "MapWriter.h"
#include "Patch.h"
#include "Terrain.h"
#include "TerrainTextureEntry.h"
#include "TerrainTextureManager.h"
#include "maths/MathUtil.h"
#include "maths/NUSpline.h"
#include "ps/CLogger.h"
#include "ps/Loader.h"
#include "ps/Filesystem.h"
#include "ps/XML/XMLWriter.h"
#include "renderer/PostprocManager.h"
#include "renderer/SkyManager.h"
#include "renderer/WaterManager.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpObstruction.h"
#include "simulation2/components/ICmpOwnership.h"
#include "simulation2/components/ICmpPosition.h"
#include "simulation2/components/ICmpTemplateManager.h"
#include "simulation2/components/ICmpVisual.h"
#include "simulation2/components/ICmpWaterManager.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
// CMapWriter constructor: nothing to do at the minute
CMapWriter::CMapWriter()
{
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// SaveMap: try to save the current map to the given file
void CMapWriter::SaveMap(const VfsPath& pathname, CTerrain* pTerrain,
WaterManager* pWaterMan, SkyManager* pSkyMan,
CLightEnv* pLightEnv, CCamera* pCamera, CCinemaManager* pCinema,
CPostprocManager* pPostproc,
CSimulation2* pSimulation2)
{
CFilePacker packer(FILE_VERSION, "PSMP");
// build necessary data
PackMap(packer, pTerrain);
try
{
// write it out
packer.Write(pathname);
}
catch (PSERROR_File_WriteFailed&)
{
LOGERROR(L"Failed to write map '%ls'", pathname.string().c_str());
return;
}
VfsPath pathnameXML = pathname.ChangeExtension(L".xml");
WriteXML(pathnameXML, pWaterMan, pSkyMan, pLightEnv, pCamera, pCinema, pPostproc, pSimulation2);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// GetHandleIndex: return the index of the given handle in the given list; or 0xFFFF if
// handle isn't in list
static u16 GetEntryIndex(const CTerrainTextureEntry* entry, const std::vector& entries)
{
const size_t limit = std::min(entries.size(), size_t(0xFFFEu)); // paranoia
for (size_t i=0;i& textures,
std::vector& tiles)
{
// the list of all handles in use
std::vector entries;
// resize tile array to required size
tiles.resize(SQR(pTerrain->GetVerticesPerSide()-1));
STileDesc* tileptr=&tiles[0];
// now iterate through all the tiles
const ssize_t patchesPerSide=pTerrain->GetPatchesPerSide();
for (ssize_t j=0;jGetPatch(i,j)->m_MiniPatches[m][k]; // can't fail
u16 index=u16(GetEntryIndex(mp.GetTextureEntry(),entries));
if (index==0xFFFF) {
index=(u16)entries.size();
entries.push_back(mp.GetTextureEntry());
}
tileptr->m_Tex1Index=index;
tileptr->m_Tex2Index=0xFFFF;
tileptr->m_Priority=mp.GetPriority();
tileptr++;
}
}
}
}
// now find the texture names for each handle
for (size_t i=0;iGetTag();
}
textures.push_back(texturename);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// PackMap: pack the current world into a raw data stream
void CMapWriter::PackMap(CFilePacker& packer, CTerrain* pTerrain)
{
// now pack everything up
PackTerrain(packer, pTerrain);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// PackTerrain: pack the terrain onto the end of the output data stream
// - data: map size, heightmap, list of textures used by map, texture tile assignments
void CMapWriter::PackTerrain(CFilePacker& packer, CTerrain* pTerrain)
{
// pack map size
const ssize_t mapsize = pTerrain->GetPatchesPerSide();
packer.PackSize(mapsize);
// pack heightmap
packer.PackRaw(pTerrain->GetHeightMap(),sizeof(u16)*SQR(pTerrain->GetVerticesPerSide()));
// the list of textures used by map
std::vector terrainTextures;
// descriptions of each tile
std::vector tiles;
// build lists by scanning through the terrain
EnumTerrainTextures(pTerrain, terrainTextures, tiles);
// pack texture names
const size_t numTextures = terrainTextures.size();
packer.PackSize(numTextures);
for (size_t i=0;iGetStartupScript().empty())
{
XML_Element("Script");
XML_CDATA(pSimulation2->GetStartupScript().c_str());
}
{
XML_Element("Environment");
XML_Setting("SkySet", pSkyMan->GetSkySet());
{
XML_Element("SunColour");
XML_Attribute("r", pLightEnv->m_SunColor.X); // yes, it's X/Y/Z...
XML_Attribute("g", pLightEnv->m_SunColor.Y);
XML_Attribute("b", pLightEnv->m_SunColor.Z);
}
{
XML_Element("SunElevation");
XML_Attribute("angle", pLightEnv->m_Elevation);
}
{
XML_Element("SunRotation");
XML_Attribute("angle", pLightEnv->m_Rotation);
}
{
XML_Element("TerrainAmbientColour");
XML_Attribute("r", pLightEnv->m_TerrainAmbientColor.X);
XML_Attribute("g", pLightEnv->m_TerrainAmbientColor.Y);
XML_Attribute("b", pLightEnv->m_TerrainAmbientColor.Z);
}
{
XML_Element("UnitsAmbientColour");
XML_Attribute("r", pLightEnv->m_UnitsAmbientColor.X);
XML_Attribute("g", pLightEnv->m_UnitsAmbientColor.Y);
XML_Attribute("b", pLightEnv->m_UnitsAmbientColor.Z);
}
{
XML_Element("Fog");
XML_Setting("FogFactor", pLightEnv->m_FogFactor);
XML_Setting("FogThickness", pLightEnv->m_FogMax);
{
XML_Element("FogColour");
XML_Attribute("r", pLightEnv->m_FogColor.X);
XML_Attribute("g", pLightEnv->m_FogColor.Y);
XML_Attribute("b", pLightEnv->m_FogColor.Z);
}
}
{
XML_Element("Water");
{
XML_Element("WaterBody");
XML_Setting("Type", "default");
{
XML_Element("Colour");
XML_Attribute("r", pWaterMan->m_WaterColor.r);
XML_Attribute("g", pWaterMan->m_WaterColor.g);
XML_Attribute("b", pWaterMan->m_WaterColor.b);
}
CmpPtr cmpWaterManager(*pSimulation2, SYSTEM_ENTITY);
ENSURE(cmpWaterManager);
XML_Setting("Height", cmpWaterManager->GetExactWaterLevel(0, 0));
- XML_Setting("Shininess", pWaterMan->m_Shininess);
XML_Setting("Waviness", pWaterMan->m_Waviness);
XML_Setting("Murkiness", pWaterMan->m_Murkiness);
{
XML_Element("Tint");
XML_Attribute("r", pWaterMan->m_WaterTint.r);
XML_Attribute("g", pWaterMan->m_WaterTint.g);
XML_Attribute("b", pWaterMan->m_WaterTint.b);
}
{
XML_Element("ReflectionTint");
XML_Attribute("r", pWaterMan->m_ReflectionTint.r);
XML_Attribute("g", pWaterMan->m_ReflectionTint.g);
XML_Attribute("b", pWaterMan->m_ReflectionTint.b);
}
XML_Setting("ReflectionTintStrength", pWaterMan->m_ReflectionTintStrength);
}
}
{
XML_Element("Postproc");
{
XML_Setting("Brightness", pLightEnv->m_Brightness);
XML_Setting("Contrast", pLightEnv->m_Contrast);
XML_Setting("Saturation", pLightEnv->m_Saturation);
XML_Setting("Bloom", pLightEnv->m_Bloom);
XML_Setting("PostEffect", pPostproc->GetPostEffect());
}
}
}
{
XML_Element("Camera");
{
XML_Element("Position");
CVector3D pos = pCamera->m_Orientation.GetTranslation();
XML_Attribute("x", pos.X);
XML_Attribute("y", pos.Y);
XML_Attribute("z", pos.Z);
}
CVector3D in = pCamera->m_Orientation.GetIn();
// Convert to spherical coordinates
float rotation = atan2(in.X, in.Z);
float declination = atan2(sqrt(in.X*in.X + in.Z*in.Z), in.Y) - (float)M_PI/2;
{
XML_Element("Rotation");
XML_Attribute("angle", rotation);
}
{
XML_Element("Declination");
XML_Attribute("angle", declination);
}
}
if (pSimulation2)
{
std::string settings = pSimulation2->GetMapSettingsString();
if (!settings.empty())
{
XML_Element("ScriptSettings");
XML_CDATA(("\n" + settings + "\n").c_str());
}
}
{
XML_Element("Entities");
CSimulation2& sim = *pSimulation2;
CmpPtr cmpTemplateManager(sim, SYSTEM_ENTITY);
ENSURE(cmpTemplateManager);
// This will probably need to be changed in the future, but for now we'll
// just save all entities that have a position
CSimulation2::InterfaceList ents = sim.GetEntitiesWithInterface(IID_Position);
for (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it)
{
entity_id_t ent = it->first;
// Don't save local entities (placement previews etc)
if (ENTITY_IS_LOCAL(ent))
continue;
XML_Element("Entity");
XML_Attribute("uid", ent);
XML_Setting("Template", cmpTemplateManager->GetCurrentTemplateName(ent));
CmpPtr cmpOwnership(sim, ent);
if (cmpOwnership)
XML_Setting("Player", (int)cmpOwnership->GetOwner());
CmpPtr cmpPosition(sim, ent);
if (cmpPosition)
{
CFixedVector3D pos = cmpPosition->GetPosition();
CFixedVector3D rot = cmpPosition->GetRotation();
{
XML_Element("Position");
XML_Attribute("x", pos.X);
XML_Attribute("z", pos.Z);
// TODO: height offset etc
}
{
XML_Element("Orientation");
XML_Attribute("y", rot.Y);
// TODO: X, Z maybe
}
}
CmpPtr cmpObstruction(sim, ent);
if (cmpObstruction)
{
// TODO: Currently only necessary because Atlas
// does not set up control groups for its walls.
cmpObstruction->ResolveFoundationCollisions();
entity_id_t group = cmpObstruction->GetControlGroup();
entity_id_t group2 = cmpObstruction->GetControlGroup2();
// Don't waste space writing the default control groups.
if (group != ent || group2 != INVALID_ENTITY)
{
XML_Element("Obstruction");
if (group != ent)
XML_Attribute("group", group);
if (group2 != INVALID_ENTITY)
XML_Attribute("group2", group2);
}
}
CmpPtr cmpVisual(sim, ent);
if (cmpVisual)
{
u32 seed = cmpVisual->GetActorSeed();
if (seed != (u32)ent)
{
XML_Element("Actor");
XML_Attribute("seed", seed);
}
// TODO: variation/selection strings
}
}
}
const std::map& paths = pCinema->GetAllPaths();
std::map::const_iterator it = paths.begin();
{
XML_Element("Paths");
for ( ; it != paths.end(); ++it )
{
CStrW name = it->first;
float timescale = it->second.GetTimescale();
XML_Element("Path");
XML_Attribute("name", name);
XML_Attribute("timescale", timescale);
const std::vector& nodes = it->second.GetAllNodes();
const CCinemaData* data = it->second.GetData();
{
XML_Element("Distortion");
XML_Attribute("mode", data->m_Mode);
XML_Attribute("style", data->m_Style);
XML_Attribute("growth", data->m_Growth);
XML_Attribute("switch", data->m_Switch);
}
for ( ssize_t j=nodes.size()-1; j >= 0; --j )
{
XML_Element("Node");
{
XML_Element("Position");
XML_Attribute("x", nodes[j].Position.X);
XML_Attribute("y", nodes[j].Position.Y);
XML_Attribute("z", nodes[j].Position.Z);
}
{
XML_Element("Rotation");
XML_Attribute("x", nodes[j].Rotation.X);
XML_Attribute("y", nodes[j].Rotation.Y);
XML_Attribute("z", nodes[j].Rotation.Z);
}
XML_Setting("Time", nodes[j].Distance);
}
}
}
}
if (!XML_StoreVFS(g_VFS, filename))
LOGERROR(L"Failed to write map '%ls'", filename.string().c_str());
}
/*
void CMapWriter::WriteTriggerGroup(XMLWriter_File& xml_file_, const MapTriggerGroup& group, const std::list& groupList)
{
XML_Element("Group");
XML_Attribute("name", group.name);
for ( std::list::const_iterator it = group.childGroups.begin();
it != group.childGroups.end(); ++it )
{
//Not very efficient...
std::list::const_iterator it2 = std::find(groupList.begin(), groupList.end(), *it);
if ( it2 != groupList.end() )
WriteTriggerGroup(xml_file_, *it2, groupList);
else
debug_warn(L"Invalid trigger group ID while writing map");
}
for ( std::list::const_iterator it = group.triggers.begin(); it != group.triggers.end(); ++it )
{
WriteTrigger(xml_file_, *it);
}
}
void CMapWriter::WriteTrigger(XMLWriter_File& xml_file_, const MapTrigger& trigger)
{
XML_Element("Trigger");
XML_Attribute("name", trigger.name);
{
if ( trigger.active )
XML_Setting("Active", "true");
else
XML_Setting("Active", "false");
XML_Setting("MaxRunCount", trigger.maxRunCount);
XML_Setting("Delay", trigger.timeValue);
}
{
XML_Element("Conditions");
for ( std::list::const_iterator it2 = trigger.conditions.begin();
it2 != trigger.conditions.end(); ++it2 )
{
size_t distance = std::distance( trigger.conditions.begin(), it2 );
std::set::const_iterator logicIter;
if ( ( logicIter = trigger.logicBlocks.find(MapTriggerLogicBlock(distance)) )
!= trigger.logicBlocks.end() )
{
XML_Element("LogicBlock");
if ( logicIter->negated )
XML_Attribute("not", "true");
else
XML_Attribute("not", "false");
}
{
XML_Element("Condition");
XML_Attribute("name", it2->name);
XML_Attribute("function", it2->functionName);
XML_Attribute("display", it2->displayName);
if ( it2->negated )
XML_Attribute("not", "true");
else
XML_Attribute("not", "false");
for ( std::list::const_iterator paramIter = it2->parameters.begin();
paramIter != it2->parameters.end(); ++paramIter )
{
CStrW paramString(*paramIter);
//paramString.Replace(L"<", L"<");
//paramString.Replace(L">", L">");
XML_Setting("Parameter", paramString);
}
if ( it2->linkLogic == 1 )
XML_Setting("LinkLogic", "AND");
else if ( it2->linkLogic == 2 )
XML_Setting("LinkLogic", "OR");
if ( trigger.logicBlockEnds.find(distance) != trigger.logicBlockEnds.end() )
{
XML_Element("LogicBlockEnd");
}
}
} //Read all conditions
} //Conditions' scope
{
XML_Element("Effects");
for ( std::list::const_iterator it2 = trigger.effects.begin();
it2 != trigger.effects.end(); ++it2 )
{
XML_Element("Effect");
XML_Attribute("name", it2->name);
{
XML_Setting("function", it2->functionName);
XML_Setting("display", it2->displayName);
for ( std::list::const_iterator paramIter = it2->parameters.begin();
paramIter != it2->parameters.end(); ++paramIter )
{
XML_Setting("Parameter", *paramIter);
}
}
}
} //Effects' scope
}
*/
Index: ps/trunk/source/ps/CStrInternStatic.h
===================================================================
--- ps/trunk/source/ps/CStrInternStatic.h (revision 14513)
+++ ps/trunk/source/ps/CStrInternStatic.h (revision 14514)
@@ -1,149 +1,148 @@
/* Copyright (C) 2013 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(Foam)
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_FOAM)
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_SHADOWS)
X(USE_SHADOW_PCF)
X(USE_SHADOW_SAMPLER)
X(USE_WAVES)
X2(_emptystring, "")
X(a_skinJoints)
X(a_skinWeights)
X(a_tangent)
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(fogColor)
X(fogParams)
X(foreground_overlay)
X(gui_add)
X(gui_basic)
X(gui_grayscale)
X(gui_solid)
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(murkiness)
X(normalMap)
X(normalMap2)
X(objectColor)
X(particle)
X(particle_solid)
X(playerColor)
X(qualityLevel)
X(reflectionMap)
X(reflectionMatrix)
X(reflectionTint)
X(reflectionTintStrength)
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(shininess)
X(skinBlendMatrices)
X2(skinBlendMatrices_0, "skinBlendMatrices[0]")
X(skyCube)
X(sky_simple)
X(specularStrength)
X(sunColor)
X(sunDir)
X(tex)
X(texSize)
X(textureTransform)
X(time)
X(tint)
X(transform)
X(translation)
X(waterTex)
X(waveTex)
X(waviness)
X(width)
X(zFar)
X(zNear)
Index: ps/trunk/source/renderer/TerrainRenderer.cpp
===================================================================
--- ps/trunk/source/renderer/TerrainRenderer.cpp (revision 14513)
+++ ps/trunk/source/renderer/TerrainRenderer.cpp (revision 14514)
@@ -1,1021 +1,1021 @@
/* Copyright (C) 2013 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/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
/**
* 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;
std::vector filteredPatches;
/// Decals that were submitted for this frame
std::vector visibleDecals;
std::vector filteredDecals;
/// Fancy water shader
CShaderProgramPtr fancyWaterShader;
CShaderProgramPtr wavesShader;
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(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.push_back(data);
}
///////////////////////////////////////////////////////////////////
// Submit a decal for rendering
void TerrainRenderer::Submit(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.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);
m->visiblePatches.clear();
m->visibleDecals.clear();
m->phase = Phase_Submit;
}
///////////////////////////////////////////////////////////////////
// Culls patches and decals against a frustum.
bool TerrainRenderer::CullPatches(const CFrustum* frustum)
{
m->filteredPatches.clear();
for (std::vector::iterator it = m->visiblePatches.begin(); it != m->visiblePatches.end(); ++it)
{
if (frustum->IsBoxVisible(CVector3D(0, 0, 0), (*it)->GetPatch()->GetWorldBounds()))
m->filteredPatches.push_back(*it);
}
m->filteredDecals.clear();
for (std::vector::iterator it = m->visibleDecals.begin(); it != m->visibleDecals.end(); ++it)
{
if (frustum->IsBoxVisible(CVector3D(0, 0, 0), (*it)->GetDecal()->GetWorldBounds()))
m->filteredDecals.push_back(*it);
}
return !m->filteredPatches.empty() || !m->filteredDecals.empty();
}
///////////////////////////////////////////////////////////////////
// Full-featured terrain rendering with blending and everything
void TerrainRenderer::RenderTerrain(bool filtered)
{
#if CONFIG2_GLES
UNUSED2(filtered);
#else
ENSURE(m->phase == Phase_Render);
std::vector& visiblePatches = filtered ? m->filteredPatches : m->visiblePatches;
std::vector& visibleDecals = filtered ? m->filteredDecals : m->visibleDecals;
if (visiblePatches.empty() && visibleDecals.empty())
return;
CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("fixed:dummy", CShaderDefines());
dummyShader->Bind();
// 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 colours
// 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|STREAM_COLOR;
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(CMatrix3D& textureMatrix)
{
#if CONFIG2_GLES
#warning TODO: implement TerrainRenderer::RenderTerrainOverlayTexture for GLES
UNUSED2(textureMatrix);
#else
ENSURE(m->phase == Phase_Render);
std::vector& visiblePatches = m->visiblePatches;
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(0);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glMatrixMode(GL_TEXTURE);
glLoadMatrixf(&textureMatrix._11);
glMatrixMode(GL_MODELVIEW);
CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("fixed:dummy", CShaderDefines());
dummyShader->Bind();
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 < m->visiblePatches.size(); ++i)
{
CPatchRData* data = m->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);
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, ShadowMap* shadow, bool filtered)
{
ENSURE(m->phase == Phase_Render);
std::vector& visiblePatches = filtered ? m->filteredPatches : m->visiblePatches;
std::vector& visibleDecals = filtered ? m->filteredDecals : m->visibleDecals;
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(bool filtered)
{
ENSURE(m->phase == Phase_Render);
std::vector& visiblePatches = filtered ? m->filteredPatches : m->visiblePatches;
if (visiblePatches.empty())
return;
#if CONFIG2_GLES
#warning TODO: implement TerrainRenderer::RenderPatches for GLES
#else
CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("fixed:dummy", CShaderDefines());
dummyShader->Bind();
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(bool filtered)
{
ENSURE(m->phase == Phase_Render);
std::vector& visiblePatches = filtered ? m->filteredPatches : m->visiblePatches;
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(const CMatrix3D &viewproj)
{
CBoundingBoxAligned scissor;
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* data = m->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, ShadowMap* shadow)
{
PROFILE3_GPU("fancy water");
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
CShaderDefines defines = context;
WaterMgr->UpdateQuality();
// If we're using fancy water, make sure its shader is loaded
if (!m->fancyWaterShader || WaterMgr->m_NeedsReloading)
{
if (WaterMgr->m_WaterNormal)
defines.Add(str_USE_NORMALS, str_1);
if (WaterMgr->m_WaterRealDepth)
defines.Add(str_USE_REAL_DEPTH, str_1);
- if (WaterMgr->m_WaterFoam && !g_AtlasGameLoop->running)
+ if (WaterMgr->m_WaterFoam)
defines.Add(str_USE_FOAM, str_1);
if (WaterMgr->m_WaterCoastalWaves && !g_AtlasGameLoop->running)
defines.Add(str_USE_WAVES, 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, str_1);
m->wavesShader = g_Renderer.GetShaderManager().LoadProgram("glsl/waves", defines);
if (!m->wavesShader)
{
LOGERROR(L"Failed to load waves shader. Deactivating waves.\n");
g_Renderer.SetOptionBool(CRenderer::OPT_WATERCOASTALWAVES, false);
defines.Add(str_USE_WAVES, str_0);
}
// haven't updated the ARB shader yet so I'll always load the GLSL
/*if (!g_Renderer.m_Options.m_PreferGLSL && !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(L"Failed to load water shader. Falling back to non-fancy water.\n");
WaterMgr->m_RenderWater = false;
return false;
}
WaterMgr->m_NeedsReloading = false;
}
CLOSTexture& losTexture = g_Renderer.GetScene().GetLOSTexture();
GLuint depthTex;
// creating the real depth texture using the depth buffer.
if (WaterMgr->m_WaterRealDepth)
{
if (WaterMgr->m_depthTT == 0)
{
glGenTextures(1, (GLuint*)&depthTex);
WaterMgr->m_depthTT = depthTex;
glBindTexture(GL_TEXTURE_2D, WaterMgr->m_depthTT);
#if CONFIG2_GLES
GLenum format = GL_DEPTH_COMPONENT;
#else
GLenum format = GL_DEPTH_COMPONENT32;
#endif
// TODO: use POT texture
glTexImage2D(GL_TEXTURE_2D, 0, format, g_Renderer.GetWidth(), g_Renderer.GetHeight(),
0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE,NULL);
}
glBindTexture(GL_TEXTURE_2D, WaterMgr->m_depthTT);
#if !CONFIG2_GLES
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
#endif
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);
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();
}*/
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
double time = WaterMgr->m_WaterTexTimer;
double period = 8;
int curTex = (int)(time*60/period) % 60;
int nexTex = (curTex + 1) % 60;
GLuint FramebufferName = 0;
// rendering waves to a framebuffer
- if (WaterMgr->m_WaterCoastalWaves && WaterMgr->m_VBWaves && !g_AtlasGameLoop->running)
+ // TODO: reactivate this with something that looks good.
+ if (false && WaterMgr->m_WaterCoastalWaves && WaterMgr->m_VBWaves && !g_AtlasGameLoop->running)
{
// Save the post-processing framebuffer.
GLint fbo;
glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &fbo);
pglGenFramebuffersEXT(1, &FramebufferName);
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, FramebufferName);
GLuint renderedTexture;
if (WaterMgr->m_waveTT == 0)
{
glGenTextures(1, &renderedTexture);
WaterMgr->m_waveTT = renderedTexture;
glBindTexture(GL_TEXTURE_2D, WaterMgr->m_waveTT);
// TODO: use POT texture
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA, (float)g_Renderer.GetWidth(), (float)g_Renderer.GetHeight(), 0,GL_RGBA, GL_UNSIGNED_BYTE, 0);
}
glBindTexture(GL_TEXTURE_2D, WaterMgr->m_waveTT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, WaterMgr->m_waveTT, 0);
glClearColor(0.5f,0.5f,1.0f,0.0f);
glClear(GL_COLOR_BUFFER_BIT);
// rendering
m->wavesShader->Bind();
m->wavesShader->BindTexture(str_waveTex, WaterMgr->m_Wave);
m->wavesShader->Uniform(str_time, (float)time);
m->wavesShader->Uniform(str_waviness, WaterMgr->m_Waviness);
m->wavesShader->Uniform(str_mapSize, (float)(WaterMgr->m_TexSize));
SWavesVertex *base=(SWavesVertex *)WaterMgr->m_VBWaves->m_Owner->Bind();
GLsizei stride = sizeof(SWavesVertex);
m->wavesShader->VertexPointer(3, GL_FLOAT, stride, &base[WaterMgr->m_VBWaves->m_Index].m_Position);
m->wavesShader->TexCoordPointer(GL_TEXTURE0,2,GL_BYTE, stride,&base[WaterMgr->m_VBWaves->m_Index].m_UV);
m->wavesShader->AssertPointersBound();
u8* indexBase = WaterMgr->m_VBWavesIndices->m_Owner->Bind();
glDrawElements(GL_TRIANGLES, (GLsizei) WaterMgr->m_VBWavesIndices->m_Count, GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*(WaterMgr->m_VBWavesIndices->m_Index));
g_Renderer.m_Stats.m_DrawCalls++;
CVertexBuffer::Unbind();
m->wavesShader->Unbind();
// rebind post-processing frambuffer.
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
glBindTexture(GL_TEXTURE_2D, 0);
}
m->fancyWaterShader->Bind();
// Shift the texture coordinates by these amounts to make the water "flow"
float tx = -fmod(time, 81.0 / (WaterMgr->m_Waviness/20.0 + 0.8) )/(81.0/ (WaterMgr->m_Waviness/20.0 + 0.8) );
float ty = -fmod(time, 34.0 / (WaterMgr->m_Waviness/20.0 + 0.8) )/(34.0/ (WaterMgr->m_Waviness/20.0 + 0.8) );
float repeatPeriod = WaterMgr->m_RepeatPeriod;
const CCamera& camera = g_Renderer.GetViewCamera();
CVector3D camPos = camera.m_Orientation.GetTranslation();
m->fancyWaterShader->BindTexture(str_normalMap, WaterMgr->m_NormalMap[curTex]);
m->fancyWaterShader->BindTexture(str_normalMap2, WaterMgr->m_NormalMap[nexTex]);
if (WaterMgr->m_WaterFoam || WaterMgr->m_WaterCoastalWaves)
{
m->fancyWaterShader->BindTexture(str_Foam, WaterMgr->m_Foam);
m->fancyWaterShader->Uniform(str_mapSize, (float)(WaterMgr->m_TexSize));
}
if (WaterMgr->m_WaterRealDepth)
m->fancyWaterShader->BindTexture(str_depthTex, WaterMgr->m_depthTT);
if (WaterMgr->m_WaterCoastalWaves)
m->fancyWaterShader->BindTexture(str_waveTex, WaterMgr->m_waveTT);
if (WaterMgr->m_WaterReflection)
m->fancyWaterShader->BindTexture(str_reflectionMap, WaterMgr->m_ReflectionTexture);
if (WaterMgr->m_WaterRefraction)
m->fancyWaterShader->BindTexture(str_refractionMap, WaterMgr->m_RefractionTexture);
m->fancyWaterShader->BindTexture(str_losMap, losTexture.GetTextureSmooth());
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
// TODO: only bind what's really needed for that.
m->fancyWaterShader->Uniform(str_sunDir, lightEnv.GetSunDir());
m->fancyWaterShader->Uniform(str_sunColor, lightEnv.m_SunColor.X);
m->fancyWaterShader->Uniform(str_color, WaterMgr->m_WaterColor);
- m->fancyWaterShader->Uniform(str_shininess, WaterMgr->m_Shininess);
m->fancyWaterShader->Uniform(str_specularStrength, WaterMgr->m_SpecularStrength);
m->fancyWaterShader->Uniform(str_waviness, WaterMgr->m_Waviness);
m->fancyWaterShader->Uniform(str_murkiness, WaterMgr->m_Murkiness);
m->fancyWaterShader->Uniform(str_tint, WaterMgr->m_WaterTint);
m->fancyWaterShader->Uniform(str_reflectionTintStrength, WaterMgr->m_ReflectionTintStrength);
m->fancyWaterShader->Uniform(str_reflectionTint, WaterMgr->m_ReflectionTint);
m->fancyWaterShader->Uniform(str_translation, tx, ty);
m->fancyWaterShader->Uniform(str_repeatScale, 1.0f / repeatPeriod);
m->fancyWaterShader->Uniform(str_reflectionMatrix, WaterMgr->m_ReflectionMatrix);
m->fancyWaterShader->Uniform(str_refractionMatrix, WaterMgr->m_RefractionMatrix);
m->fancyWaterShader->Uniform(str_losMatrix, losTexture.GetTextureMatrix());
m->fancyWaterShader->Uniform(str_cameraPos, camPos);
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);
m->fancyWaterShader->BindTexture(str_skyCube, g_Renderer.GetSkyManager()->GetSkyCube());
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);
}
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* data = m->visiblePatches[i];
data->RenderWater(m->fancyWaterShader);
}
m->fancyWaterShader->Unbind();
pglActiveTextureARB(GL_TEXTURE0);
pglDeleteFramebuffersEXT(1, &FramebufferName);
glDisable(GL_BLEND);
return true;
}
void TerrainRenderer::RenderSimpleWater()
{
#if !CONFIG2_GLES
PROFILE3_GPU("simple water");
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
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 and use vertex alpha
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);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
// 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);
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);
CShaderProgramPtr dummyShader = g_Renderer.GetShaderManager().LoadProgram("fixed:dummy", CShaderDefines());
dummyShader->Bind();
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
{
CPatchRData* data = m->visiblePatches[i];
data->RenderWater(dummyShader);
}
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
dummyShader->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_BLEND);
glDisable(GL_TEXTURE_2D);
#endif
}
///////////////////////////////////////////////////////////////////
// Render water that is part of the terrain
void TerrainRenderer::RenderWater(const CShaderDefines& context, ShadowMap* shadow)
{
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
if (!WaterMgr->WillRenderFancyWater())
RenderSimpleWater();
else
RenderFancyWater(context, shadow);
}
void TerrainRenderer::RenderPriorities()
{
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);
for (size_t i = 0; i < m->visiblePatches.size(); ++i)
m->visiblePatches[i]->RenderPriorities(textRenderer);
textRenderer.Render();
tech->EndPass();
}
Index: ps/trunk/source/renderer/WaterManager.cpp
===================================================================
--- ps/trunk/source/renderer/WaterManager.cpp (revision 14513)
+++ ps/trunk/source/renderer/WaterManager.cpp (revision 14514)
@@ -1,670 +1,671 @@
/* Copyright (C) 2013 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 .
*/
/*
* Water settings (speed, height) and texture management
*/
#include "precompiled.h"
#include "graphics/Terrain.h"
#include "graphics/TextureManager.h"
#include "lib/bits.h"
#include "lib/timer.h"
#include "lib/tex/tex.h"
#include "lib/res/graphics/ogl_tex.h"
#include "maths/MathUtil.h"
#include "maths/Vector2D.h"
#include "ps/Game.h"
#include "ps/World.h"
#include "renderer/WaterManager.h"
#include "renderer/Renderer.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpWaterManager.h"
#include "simulation2/components/ICmpRangeManager.h"
///////////////////////////////////////////////////////////////////////////////////////////////
// WaterManager implementation
///////////////////////////////////////////////////////////////////
// Construction/Destruction
WaterManager::WaterManager()
{
// water
m_RenderWater = false; // disabled until textures are successfully loaded
m_WaterHeight = 5.0f;
m_WaterColor = CColor(0.3f, 0.35f, 0.7f, 1.0f);
m_WaterFullDepth = 5.0f;
m_WaterMaxAlpha = 1.0f;
m_WaterAlphaOffset = -0.05f;
m_SWaterTrans = 0;
m_TWaterTrans = 0;
m_SWaterSpeed = 0.0015f;
m_TWaterSpeed = 0.0015f;
m_SWaterScrollCounter = 0;
m_TWaterScrollCounter = 0;
m_WaterCurrentTex = 0;
m_ReflectionTexture = 0;
m_RefractionTexture = 0;
m_ReflectionTextureSize = 0;
m_RefractionTextureSize = 0;
m_WaterTexTimer = 0.0;
- m_Shininess = 150.0f;
m_SpecularStrength = 0.6f;
m_Waviness = 8.0f;
m_ReflectionTint = CColor(0.28f, 0.3f, 0.59f, 1.0f);
m_ReflectionTintStrength = 0.0f;
m_WaterTint = CColor(0.28f, 0.3f, 0.59f, 1.0f);
m_Murkiness = 0.45f;
m_RepeatPeriod = 16.0f;
m_WaveX = NULL;
m_WaveZ = NULL;
m_DistanceToShore = NULL;
m_FoamFactor = NULL;
m_WaterNormal = false;
m_WaterRealDepth = false;
m_WaterFoam = false;
m_WaterCoastalWaves = false;
m_WaterRefraction = false;
m_WaterReflection = false;
m_WaterShadows = false;
m_NeedsReloading = false;
m_NeedInfoUpdate = true;
m_VBWaves = NULL;
m_VBWavesIndices = NULL;
m_depthTT = 0;
m_waveTT = 0;
m_MapSize = 0;
m_updatei0 = 0;
m_updatej0 = 0;
m_updatei1 = 0;
m_updatej1 = 0;
}
WaterManager::~WaterManager()
{
// Cleanup if the caller messed up
UnloadWaterTextures();
delete[] m_WaveX;
delete[] m_WaveZ;
delete[] m_DistanceToShore;
delete[] m_FoamFactor;
glDeleteTextures(1, &m_depthTT);
glDeleteTextures(1, &m_waveTT);
if (m_VBWaves) g_VBMan.Release(m_VBWaves);
if (m_VBWavesIndices) g_VBMan.Release(m_VBWavesIndices);
}
///////////////////////////////////////////////////////////////////
// Progressive load of water textures
int WaterManager::LoadWaterTextures()
{
// TODO: this doesn't need to be progressive-loading any more
// (since texture loading is async now)
// TODO: add a member variable and setter for this. (can't make this
// a parameter because this function is called via delay-load code)
static const wchar_t* const water_type = L"default";
wchar_t pathname[PATH_MAX];
// Load diffuse grayscale images (for non-fancy water)
for (size_t i = 0; i < ARRAY_SIZE(m_WaterTexture); ++i)
{
swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/%ls/diffuse%02d.dds", water_type, (int)i+1);
CTextureProperties textureProps(pathname);
textureProps.SetWrap(GL_REPEAT);
CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
texture->Prefetch();
m_WaterTexture[i] = texture;
}
// Load normalmaps (for fancy water)
for (size_t i = 0; i < ARRAY_SIZE(m_NormalMap); ++i)
{
swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/%ls/normal%02d.dds", water_type, (int)i+1);
CTextureProperties textureProps(pathname);
textureProps.SetWrap(GL_REPEAT);
textureProps.SetMaxAnisotropy(4);
CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
texture->Prefetch();
m_NormalMap[i] = texture;
}
// Load foam (for fancy water)
{
CTextureProperties textureProps("art/textures/terrain/types/water/foam.png");
textureProps.SetWrap(GL_REPEAT);
CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
texture->Prefetch();
m_Foam = texture;
}
// Load waves (for fancy water)
{
CTextureProperties textureProps("art/textures/terrain/types/water/shore_wave.png");
textureProps.SetWrap(GL_REPEAT);
CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
texture->Prefetch();
m_Wave = texture;
}
// Set the size to the largest power of 2 that is <= to the window height, so
// the reflection/refraction images will fit within the window
// (alternative: use FBO's, which can have arbitrary size - but do we need
// the reflection/refraction textures to be that large?)
int size = (int)round_up_to_pow2((unsigned)g_Renderer.GetHeight());
if(size > g_Renderer.GetHeight()) size /= 2;
m_ReflectionTextureSize = size;
m_RefractionTextureSize = size;
// Create reflection texture
glGenTextures(1, &m_ReflectionTexture);
glBindTexture(GL_TEXTURE_2D, m_ReflectionTexture);
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB,
(GLsizei)m_ReflectionTextureSize, (GLsizei)m_ReflectionTextureSize,
0, GL_RGB, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
// Create refraction texture
glGenTextures(1, &m_RefractionTexture);
glBindTexture(GL_TEXTURE_2D, m_RefractionTexture);
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB,
(GLsizei)m_RefractionTextureSize, (GLsizei)m_RefractionTextureSize,
0, GL_RGB, GL_UNSIGNED_BYTE, 0);
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_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
// Enable rendering, now that we've succeeded this far
m_RenderWater = true;
return 0;
}
///////////////////////////////////////////////////////////////////
// Unload water textures
void WaterManager::UnloadWaterTextures()
{
for(size_t i = 0; i < ARRAY_SIZE(m_WaterTexture); i++)
{
m_WaterTexture[i].reset();
}
for(size_t i = 0; i < ARRAY_SIZE(m_NormalMap); i++)
{
m_NormalMap[i].reset();
}
}
///////////////////////////////////////////////////////////////////
// Create information about the terrain and wave vertices.
void WaterManager::CreateSuperfancyInfo(CSimulation2* simulation)
{
if (m_VBWaves)
{
g_VBMan.Release(m_VBWaves);
m_VBWaves = NULL;
}
if (m_VBWavesIndices)
{
g_VBMan.Release(m_VBWavesIndices);
m_VBWavesIndices = NULL;
}
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
CmpPtr cmpWaterManager(*simulation, SYSTEM_ENTITY);
if (!cmpWaterManager)
return; // REALLY shouldn't happen and will most likely crash.
// Using this to get some more optimization on circular maps
CmpPtr cmpRangeManager(*simulation, SYSTEM_ENTITY);
if (!cmpRangeManager)
return;
bool circular = cmpRangeManager->GetLosCircular();
float mSize = m_MapSize*m_MapSize;
float halfSize = (m_MapSize/2.0);
// Warning: this won't work with multiple water planes
m_WaterHeight = cmpWaterManager->GetExactWaterLevel(0,0);
// Get the square we want to work on.
- i32 Xstart = clamp(m_updatei0, 0, (i32)m_MapSize-1);
- i32 Xend = clamp(m_updatei1, 0, (i32)m_MapSize-1);
- i32 Zstart = clamp(m_updatej0, 0, (i32)m_MapSize-1);
- i32 Zend = clamp(m_updatej1, 0, (i32)m_MapSize-1);
-
+ ssize_t Xstart = m_updatei0 < 0 ? 0 : (m_updatei0 >= (ssize_t)m_MapSize ? (ssize_t)m_MapSize-1 : m_updatei0);
+ ssize_t Xend = m_updatei1 < 0 ? 0 : (m_updatei1 >= (ssize_t)m_MapSize ? (ssize_t)m_MapSize-1 : m_updatei1);
+ ssize_t Zstart = m_updatej0 < 0 ? 0 : (m_updatej0 >= (ssize_t)m_MapSize ? (ssize_t)m_MapSize-1 : m_updatej0);
+ ssize_t Zend = m_updatej1 < 0 ? 0 : (m_updatej1 >= (ssize_t)m_MapSize ? (ssize_t)m_MapSize-1 : m_updatej1);
+
+ if (!(Xend > Xstart && Zend > Zstart))
+ {
+ // it corrupts every now and then for reasons I don't get.
+ std::cout << m_updatei0 << " , " << Xstart << std::endl;
+ std::cout << m_MapSize << "," << (ssize_t)m_MapSize << std::endl;
+ }
+
if (m_WaveX == NULL)
{
m_WaveX = new float[m_MapSize*m_MapSize];
m_WaveZ = new float[m_MapSize*m_MapSize];
m_DistanceToShore = new float[m_MapSize*m_MapSize];
m_FoamFactor = new float[m_MapSize*m_MapSize];
}
u16* heightmap = terrain->GetHeightMap();
// some temporary stuff for wave intensity
// not really used too much right now.
//u8* waveForceHQ = new u8[mapSize*mapSize];
// used to cache terrain normals since otherwise we'd recalculate them a lot (I'm blurring the "normal" map).
// this might be updated to actually cache in the terrain manager but that's not for now.
CVector3D* normals = new CVector3D[m_MapSize*m_MapSize];
-
// taken out of the bottom loop, blurs the normal map
// To remove if below is reactivated
ssize_t blurZstart = Zstart-4 < 0 ? 0 : Zstart - 4;
ssize_t blurZend = Zend+4 >= (ssize_t)m_MapSize ? (ssize_t)m_MapSize-1 : Zend + 4;
ssize_t blurXstart = Xstart-4 < 0 ? 0 : Xstart - 4;
ssize_t blurXend = Xend+4 >= (ssize_t)m_MapSize ? (ssize_t)m_MapSize-1 : Xend + 4;
+
for (ssize_t j = blurZstart; j < blurZend; ++j)
{
for (ssize_t i = blurXstart; i < blurXend; ++i)
{
normals[j*m_MapSize + i] = terrain->CalcExactNormal(((float)i)*4.0f,((float)j)*4.0f);
}
}
// TODO: reactivate?
/*
// calculate wave force (not really used right now)
// and puts into "normals" the terrain normal at that point
// so as to avoid recalculating terrain normals too often.
for (ssize_t i = 0; i < mapSize; ++i)
{
for (ssize_t j = 0; j < mapSize; ++j)
{
normals[j*mapSize + i] = terrain->CalcExactNormal(((float)i)*4.0f,((float)j)*4.0f);
if (circular && (i-halfSize)*(i-halfSize)+(j-halfSize)*(j-halfSize) > mSize)
{
waveForceHQ[j*mapSize + i] = 255;
continue;
}
u8 color = 0;
for (int v = 0; v <= 18; v += 3){
if (j-v >= 0 && i-v >= 0 && heightmap[(j-v)*mapSize + i-v] > waterHeightInu16)
{
if (color == 0)
color = 5;
else
color++;
}
}
waveForceHQ[j*mapSize + i] = 255 - color * 40;
}
}
*/
// this creates information for waves and stores it in float arrays. PatchRData then puts it in the vertex info for speed.
for (ssize_t j = Zstart; j < Zend; ++j)
{
for (ssize_t i = Xstart; i < Xend; ++i)
{
+ ssize_t index = j*m_MapSize + i;
if (circular && (i-halfSize)*(i-halfSize)+(j-halfSize)*(j-halfSize) > mSize)
{
- m_WaveX[j*m_MapSize + i] = 0.0f;
- m_WaveZ[j*m_MapSize + i] = 0.0f;
- m_DistanceToShore[j*m_MapSize + i] = 100;
- m_FoamFactor[j*m_MapSize + i] = 0.0f;
+ m_WaveX[index] = 0.0f;
+ m_WaveZ[index] = 0.0f;
+ m_DistanceToShore[index] = 100;
+ m_FoamFactor[index] = 0.0f;
continue;
}
- float depth = m_WaterHeight - heightmap[j*m_MapSize + i]*HEIGHT_SCALE;
- int distanceToShore = 10000;
+ float depth = m_WaterHeight - heightmap[index]*HEIGHT_SCALE;
+ float distanceToShore = 10000;
// calculation of the distance to the shore.
- // TODO: this is fairly dumb, though it returns a good result
- // Could be sped up a fair bit.
- if (depth >= 0)
+ if (i > 0 && i < (ssize_t)m_MapSize-1 && j > 0 && j < (ssize_t)m_MapSize-1)
{
- // check in the square around.
- for (int yy = -5; yy <= 5; ++yy)
+ // search a 5x5 array with us in the center (do not search me)
+ // much faster since we spiral search and can just stop once we've found the shore.
+ // also everything is precomputed and we get exact results instead.
+ int offset[24] = { -1,1,-m_MapSize,+m_MapSize, -1-m_MapSize,+1-m_MapSize,-1+m_MapSize,1+m_MapSize,
+ -2,2,-2*m_MapSize,2*m_MapSize,-2-m_MapSize,-2+m_MapSize,2-m_MapSize,2+m_MapSize,
+ -1-2*m_MapSize,+1-2*m_MapSize,-1+2*m_MapSize,1+2*m_MapSize,
+ -2-2*m_MapSize,2+2*m_MapSize,-2+2*m_MapSize,2-2*m_MapSize };
+ float dist[24] = { 1.0f, 1.0f, 1.0f, 1.0f, 1.414f, 1.414f, 1.414f, 1.414f,
+ 2.0f, 2.0f, 2.0f, 2.0f, 2.236f, 2.236f, 2.236f, 2.236f,
+ 2.236f, 2.236f, 2.236f, 2.236f,
+ 2.828f, 2.828f, 2.828f, 2.828f };
+
+ int max = 8;
+ if (i > 1 && i < (ssize_t)m_MapSize-2 && j > 1 && j < (ssize_t)m_MapSize-2)
+ max = 24;
+
+ for(int lookupI = 0; lookupI < max;++lookupI)
{
- for (int xx = -5; xx <= 5; ++xx)
- {
- if (i+xx >= 0 && i + xx < (long)m_MapSize)
- if (j + yy >= 0 && j + yy < (long)m_MapSize)
- {
- float hereDepth = m_WaterHeight - heightmap[(j+yy)*m_MapSize + (i+xx)]*HEIGHT_SCALE;
- if (hereDepth < 0 && xx*xx + yy*yy < distanceToShore)
- distanceToShore = xx*xx + yy*yy;
- }
- }
+ float hereDepth = m_WaterHeight - heightmap[index+offset[lookupI]]*HEIGHT_SCALE;
+ distanceToShore = hereDepth <= 0 && depth >= 0 ? dist[lookupI] : (depth < 0 ? 1 : distanceToShore);
+ if (distanceToShore != 10000)
+ break;
}
- // refine the calculation if we're close enough
- if (distanceToShore < 9)
- {
- for (float yy = -2.5f; yy <= 2.5f; ++yy)
+ } else {
+ // revert to for and if-based because I can't be bothered to special case all that.
+ for (int xx = -1; xx <= 1;++xx)
+ for (int yy = -1; yy <= 1;++yy)
{
- for (float xx = -2.5f; xx <= 2.5f; ++xx)
+ if (i+xx >= 0 && i+xx < (ssize_t)m_MapSize && j+yy >= 0 && j+yy < (ssize_t)m_MapSize)
{
- float hereDepth = m_WaterHeight - terrain->GetExactGroundLevel( (i+xx)*4, (j+yy)*4 );
- if (hereDepth < 0 && xx*xx + yy*yy < distanceToShore)
- distanceToShore = xx*xx + yy*yy;
+ float hereDepth = m_WaterHeight - heightmap[index+xx+yy*m_MapSize]*HEIGHT_SCALE;
+ distanceToShore = (hereDepth < 0 && sqrt((double)xx*xx+yy*yy) < distanceToShore) ? sqrt((double)xx*xx+yy*yy) : distanceToShore;
}
}
- }
- }
- else
- {
- for (int yy = -2; yy <= 2; ++yy)
- {
- for (int xx = -2; xx <= 2; ++xx)
- {
- float hereDepth = m_WaterHeight - terrain->GetVertexGroundLevel(i+xx, j+yy);
- if (hereDepth > 0)
- distanceToShore = 0;
- }
- }
-
}
+
// speedup with default values for land squares
if (distanceToShore == 10000)
{
- m_WaveX[j*m_MapSize + i] = 0.0f;
- m_WaveZ[j*m_MapSize + i] = 0.0f;
- m_DistanceToShore[j*m_MapSize + i] = 100;
- m_FoamFactor[j*m_MapSize + i] = 0.0f;
+ m_WaveX[index] = 0.0f;
+ m_WaveZ[index] = 0.0f;
+ m_DistanceToShore[index] = 100.0f;
+ m_FoamFactor[index] = 0.0f;
continue;
}
+
// We'll compute the normals and the "water raise", to know about foam
// Normals are a pretty good calculation but it's slow since we normalize so much.
CVector3D normal;
int waterRaise = 0;
- for (int yy = -4; yy <= 4; yy += 2)
+ for (int yy = -3; yy <= 3; yy += 2)
{
- for (int xx = -4; xx <= 4; xx += 2) // every 2 tile is good enough.
+ for (int xx = -3; xx <= 3; xx += 2) // every 2 tile is good enough.
{
if (j+yy < (long)m_MapSize && i+xx < (long)m_MapSize && i+xx >= 0 && j+yy >= 0)
normal += normals[(j+yy)*m_MapSize + (i+xx)];
- if (terrain->GetVertexGroundLevel(i+xx,j+yy) < heightmap[j*m_MapSize + i]*HEIGHT_SCALE)
- waterRaise += heightmap[j*m_MapSize + i]*HEIGHT_SCALE - terrain->GetVertexGroundLevel(i+xx,j+yy);
+ waterRaise += heightmap[index]*HEIGHT_SCALE - terrain->GetVertexGroundLevel(i+xx,j+yy) > 0 ? heightmap[index]*HEIGHT_SCALE - terrain->GetVertexGroundLevel(i+xx,j+yy) : 0.0f;
}
}
// normalizes the terrain info to avoid foam moving at too different speeds.
- normal *= 0.012345679f;
+ normal *= 0.08f;
normal[1] = 0.1f;
normal = normal.Normalized();
- m_WaveX[j*m_MapSize + i] = normal[0];
- m_WaveZ[j*m_MapSize + i] = normal[2];
+ m_WaveX[index] = normal[0];
+ m_WaveZ[index] = normal[2];
// distance is /5.0 to be a [0,1] value.
- m_DistanceToShore[j*m_MapSize + i] = sqrtf(distanceToShore)/5.0f; // TODO: this can probably be cached as I'm integer here.
+ m_DistanceToShore[index] = distanceToShore;
// computing the amount of foam I want
-
depth = clamp(depth,0.0f,10.0f);
float foamAmount = (waterRaise/255.0f) * (1.0f - depth/10.0f) /** (waveForceHQ[j*m_MapSize+i]/255.0f)*/ * (m_Waviness/8.0f);
- foamAmount += clamp(m_Waviness/2.0f - distanceToShore,0.0f,m_Waviness/2.0f)/(m_Waviness/2.0f) * clamp(m_Waviness/9.0f,0.3f,1.0f);
- foamAmount = foamAmount > 1.0f ? 1.0f: foamAmount;
+ foamAmount += clamp(m_Waviness/2.0f,0.0f,m_Waviness/2.0f)/(m_Waviness/2.0f) * clamp(m_Waviness/9.0f,0.3f,1.0f);
+ foamAmount *= (m_Waviness/4.0f - distanceToShore);
+ foamAmount = foamAmount > 1.0f ? 1.0f: (foamAmount < 0.0f ? 0.0f : foamAmount);
- m_FoamFactor[j*m_MapSize + i] = foamAmount;
+ m_FoamFactor[index] = foamAmount;
}
}
delete[] normals;
//delete[] waveForceHQ;
- // TODO: The rest should be cleaned up
-
+ // TODO: reactivate this with something that looks good and is efficient.
+/*
// okay let's create the waves squares. i'll divide the map in arbitrary squares
// For each of these squares, check if waves are needed.
// If yes, look for the best positionning (in order to have a nice blending with the shore)
// Then clean-up: remove squares that are too close to each other
std::vector waveSquares;
int size = 8; // I think this is the size of the squares.
for (size_t j = 0; j < m_MapSize/size; ++j)
{
for (size_t i = 0; i < m_MapSize/size; ++i)
{
int landTexel = 0;
int waterTexel = 0;
CVector3D avnormal (0.0f,0.0f,0.0f);
CVector2D landPosition(0.0f,0.0f);
CVector2D waterPosition(0.0f,0.0f);
for (int yy = 0; yy < size; ++yy)
{
for (int xx = 0; xx < size; ++xx)
{
if (terrain->GetVertexGroundLevel(i*size+xx,j*size+yy) > m_WaterHeight)
{
landTexel++;
landPosition += CVector2D(i*size+xx,j*size+yy);
}
else
{
waterPosition += CVector2D(i*size+xx,j*size+yy);
waterTexel++;
avnormal += terrain->CalcExactNormal( (i*size+xx)*4.0f,(j*size+yy)*4.0f);
}
}
}
if (landTexel < size/2)
continue;
landPosition /= landTexel;
waterPosition /= waterTexel;
avnormal[1] = 1.0f;
avnormal.Normalize();
avnormal[1] = 0.0f;
// this should help ensure that the shore is pretty flat.
if (avnormal.Length() <= 0.2f)
continue;
// To get the best position for squares, I start at the mean "ocean" position
// And step by step go to the mean "land" position. I keep the position where I change from water to land.
// If this never happens, the square is scrapped.
if (terrain->GetExactGroundLevel(waterPosition.X*4.0f,waterPosition.Y*4.0f) > m_WaterHeight)
continue;
CVector2D squarePos(-1,-1);
for (u8 i = 0; i < 40; i++)
{
squarePos = landPosition * (i/40.0f) + waterPosition * (1.0f-(i/40.0f));
if (terrain->GetExactGroundLevel(squarePos.X*4.0f,squarePos.Y*4.0f) > m_WaterHeight)
break;
}
if (squarePos.X == -1)
continue;
u8 enter = 1;
// okaaaaaay. Got a square. Check for proximity.
for (unsigned long i = 0; i < waveSquares.size(); i++)
{
if ( CVector2D(waveSquares[i]-squarePos).LengthSquared() < 80) {
enter = 0;
break;
}
}
if (enter == 1)
waveSquares.push_back(squarePos);
}
}
// Actually create the waves' meshes.
std::vector waves_vertex_data;
std::vector waves_indices;
// loop through each square point. Look in the square around it, calculate the normal
// create the square.
for (unsigned long i = 0; i < waveSquares.size(); i++)
{
CVector2D pos(waveSquares[i]);
CVector3D avgnorm(0.0f,0.0f,0.0f);
for (int yy = -size/2; yy < size/2; ++yy)
{
for (int xx = -size/2; xx < size/2; ++xx)
{
avgnorm += terrain->CalcExactNormal((pos.X+xx)*4.0f,(pos.Y+yy)*4.0f);
}
}
avgnorm[1] = 0.1f;
// okay crank out a square.
// we have the direction of the square. We'll get the perpendicular vector too
CVector2D perp(-avgnorm[2],avgnorm[0]);
perp = perp.Normalized();
avgnorm = avgnorm.Normalized();
GLushort index[4];
SWavesVertex vertex[4];
vertex[0].m_Position = CVector3D(pos.X + perp.X*(size/2.2f) - avgnorm[0]*1.0f, 0.0f,pos.Y + perp.Y*(size/2.2f) - avgnorm[2]*1.0f);
vertex[0].m_Position *= 4.0f;
vertex[0].m_Position.Y = m_WaterHeight + 1.0f;
vertex[0].m_UV[1] = 1;
vertex[0].m_UV[0] = 0;
index[0] = waves_vertex_data.size();
waves_vertex_data.push_back(vertex[0]);
vertex[1].m_Position = CVector3D(pos.X - perp.X*(size/2.2f) - avgnorm[0]*1.0f, 0.0f,pos.Y - perp.Y*(size/2.2f) - avgnorm[2]*1.0f);
vertex[1].m_Position *= 4.0f;
vertex[1].m_Position.Y = m_WaterHeight + 1.0f;
vertex[1].m_UV[1] = 1;
vertex[1].m_UV[0] = 1;
index[1] = waves_vertex_data.size();
waves_vertex_data.push_back(vertex[1]);
vertex[3].m_Position = CVector3D(pos.X + perp.X*(size/2.2f) + avgnorm[0]*(size/1.5f), 0.0f,pos.Y + perp.Y*(size/2.2f) + avgnorm[2]*(size/1.5f));
vertex[3].m_Position *= 4.0f;
vertex[3].m_Position.Y = m_WaterHeight + 1.0f;
vertex[3].m_UV[1] = 0;
vertex[3].m_UV[0] = 0;
index[3] = waves_vertex_data.size();
waves_vertex_data.push_back(vertex[3]);
vertex[2].m_Position = CVector3D(pos.X - perp.X*(size/2.2f) + avgnorm[0]*(size/1.5f), 0.0f,pos.Y - perp.Y*(size/2.2f) + avgnorm[2]*(size/1.5f));
vertex[2].m_Position *= 4.0f;
vertex[2].m_Position.Y = m_WaterHeight + 1.0f;
vertex[2].m_UV[1] = 0;
vertex[2].m_UV[0] = 1;
index[2] = waves_vertex_data.size();
waves_vertex_data.push_back(vertex[2]);
waves_indices.push_back(index[0]);
waves_indices.push_back(index[1]);
waves_indices.push_back(index[2]);
waves_indices.push_back(index[2]);
waves_indices.push_back(index[3]);
waves_indices.push_back(index[0]);
}
// no vertex buffers if no data generated
if (waves_indices.empty())
return;
// waves
// allocate vertex buffer
m_VBWaves = g_VBMan.Allocate(sizeof(SWavesVertex), waves_vertex_data.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
m_VBWaves->m_Owner->UpdateChunkVertices(m_VBWaves, &waves_vertex_data[0]);
// Construct indices buffer
m_VBWavesIndices = g_VBMan.Allocate(sizeof(GLushort), waves_indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
m_VBWavesIndices->m_Owner->UpdateChunkVertices(m_VBWavesIndices, &waves_indices[0]);
+ */
}
////////////////////////////////////////////////////////////////////////
// This will always recalculate for now
void WaterManager::SetMapSize(size_t size)
{
// TODO: Im' blindly trusting the user here.
m_MapSize = size;
m_NeedInfoUpdate = true;
m_updatei0 = 0;
m_updatei1 = size;
m_updatej0 = 0;
m_updatej1 = size;
SAFE_ARRAY_DELETE(m_WaveX);
SAFE_ARRAY_DELETE(m_WaveZ);
SAFE_ARRAY_DELETE(m_DistanceToShore);
SAFE_ARRAY_DELETE(m_FoamFactor);
}
////////////////////////////////////////////////////////////////////////
// This will set the bools properly
void WaterManager::UpdateQuality()
{
if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERNORMAL) != m_WaterNormal) {
m_WaterNormal = g_Renderer.GetOptionBool(CRenderer::OPT_WATERNORMAL);
m_NeedsReloading = true;
}
if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERREALDEPTH) != m_WaterRealDepth) {
m_WaterRealDepth = g_Renderer.GetOptionBool(CRenderer::OPT_WATERREALDEPTH);
m_NeedsReloading = true;
}
if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERFOAM) != m_WaterFoam) {
m_WaterFoam = g_Renderer.GetOptionBool(CRenderer::OPT_WATERFOAM);
m_NeedsReloading = true;
m_NeedInfoUpdate = true;
}
if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERCOASTALWAVES) != m_WaterCoastalWaves) {
m_WaterCoastalWaves = g_Renderer.GetOptionBool(CRenderer::OPT_WATERCOASTALWAVES);
m_NeedsReloading = true;
m_NeedInfoUpdate = true;
}
if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERREFRACTION) != m_WaterRefraction) {
m_WaterRefraction = g_Renderer.GetOptionBool(CRenderer::OPT_WATERREFRACTION);
m_NeedsReloading = true;
}
if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERREFLECTION) != m_WaterReflection) {
m_WaterReflection = g_Renderer.GetOptionBool(CRenderer::OPT_WATERREFLECTION);
m_NeedsReloading = true;
}
if (g_Renderer.GetOptionBool(CRenderer::OPT_WATERSHADOW) != m_WaterShadows) {
m_WaterShadows = g_Renderer.GetOptionBool(CRenderer::OPT_WATERSHADOW);
m_NeedsReloading = true;
}
}
bool WaterManager::WillRenderFancyWater()
{
if (!g_Renderer.GetCapabilities().m_FragmentShader)
return false;
if (!m_RenderWater)
return false;
return true;
}
Index: ps/trunk/source/renderer/WaterManager.h
===================================================================
--- ps/trunk/source/renderer/WaterManager.h (revision 14513)
+++ ps/trunk/source/renderer/WaterManager.h (revision 14514)
@@ -1,172 +1,171 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* 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 .
*/
/*
* Water settings (speed, height) and texture management
*/
#ifndef INCLUDED_WATERMANAGER
#define INCLUDED_WATERMANAGER
#include "graphics/Texture.h"
#include "lib/ogl.h"
#include "maths/Matrix3D.h"
#include "ps/Overlay.h"
#include "renderer/VertexBufferManager.h"
class CSimulation2;
struct SWavesVertex {
// vertex position
CVector3D m_Position;
u8 m_UV[2];
};
cassert(sizeof(SWavesVertex) == 16);
/**
* Class WaterManager: Maintain water settings and textures.
*
* This could be extended to provide more advanced water rendering effects
* (refractive/reflective water) in the future.
*/
class WaterManager
{
public:
CTexturePtr m_WaterTexture[60];
CTexturePtr m_NormalMap[60];
CTexturePtr m_Foam;
CTexturePtr m_Wave;
float* m_WaveX;
float* m_WaveZ;
float* m_DistanceToShore;
float* m_FoamFactor;
size_t m_MapSize;
ssize_t m_TexSize;
GLuint m_depthTT;
GLuint m_waveTT;
// used to know what to update when updating parts of the terrain only.
i32 m_updatei0;
i32 m_updatej0;
i32 m_updatei1;
i32 m_updatej1;
int m_WaterCurrentTex;
CColor m_WaterColor;
bool m_RenderWater;
// Those variables register the current quality level. If there is a change, I have to recompile the shader.
bool m_WaterNormal;
bool m_WaterRealDepth;
bool m_WaterFoam;
bool m_WaterCoastalWaves;
bool m_WaterRefraction;
bool m_WaterReflection;
bool m_WaterShadows;
bool m_NeedsReloading;
// requires also recreating the super fancy information.
bool m_NeedInfoUpdate;
bool m_WaterScroll;
float m_WaterHeight;
float m_WaterMaxAlpha;
float m_WaterFullDepth;
float m_WaterAlphaOffset;
float m_SWaterSpeed;
float m_TWaterSpeed;
float m_SWaterTrans;
float m_TWaterTrans;
float m_SWaterScrollCounter;
float m_TWaterScrollCounter;
double m_WaterTexTimer;
// Reflection and refraction textures for fancy water
GLuint m_ReflectionTexture;
GLuint m_RefractionTexture;
size_t m_ReflectionTextureSize;
size_t m_RefractionTextureSize;
// Model-view-projection matrices for reflected & refracted cameras
// (used to let the vertex shader do projective texturing)
CMatrix3D m_ReflectionMatrix;
CMatrix3D m_RefractionMatrix;
// Shader parameters for fancy water
CColor m_WaterTint;
float m_RepeatPeriod;
- float m_Shininess;
float m_SpecularStrength;
float m_Waviness;
float m_Murkiness;
CColor m_ReflectionTint;
float m_ReflectionTintStrength;
// Waves
// see the struct above.
CVertexBuffer::VBChunk* m_VBWaves;
CVertexBuffer::VBChunk* m_VBWavesIndices;
public:
WaterManager();
~WaterManager();
/**
* LoadWaterTextures: Load water textures from within the
* progressive load framework.
*
* @return 0 if loading has completed, a value from 1 to 100 (in percent of completion)
* if more textures need to be loaded and a negative error value on failure.
*/
int LoadWaterTextures();
/**
* UnloadWaterTextures: Free any loaded water textures and reset the internal state
* so that another call to LoadWaterTextures will begin progressive loading.
*/
void UnloadWaterTextures();
/**
* CreateSuperfancyInfo: creates textures and wave vertices for superfancy water
*/
void CreateSuperfancyInfo(CSimulation2* simulation);
/**
* Updates the map size. Will trigger a complete recalculation of fancy water information the next turn.
*/
void SetMapSize(size_t size);
/**
* Updates the settings to the one from the renderer, and sets m_NeedsReloading.
*/
void UpdateQuality();
/**
* Returns true if fancy water shaders will be used (i.e. the hardware is capable
* and it hasn't been configured off)
*/
bool WillRenderFancyWater();
};
#endif // INCLUDED_WATERMANAGER
Index: ps/trunk/source/simulation2/components/CCmpWaterManager.cpp
===================================================================
--- ps/trunk/source/simulation2/components/CCmpWaterManager.cpp (revision 14513)
+++ ps/trunk/source/simulation2/components/CCmpWaterManager.cpp (revision 14514)
@@ -1,119 +1,136 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* 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 .
*/
#include "precompiled.h"
#include "simulation2/system/Component.h"
#include "ICmpWaterManager.h"
#include "graphics/RenderableObject.h"
#include "graphics/Terrain.h"
#include "renderer/Renderer.h"
#include "renderer/WaterManager.h"
#include "simulation2/MessageTypes.h"
+#include "tools/atlas/GameInterface/GameLoop.h"
+
class CCmpWaterManager : public ICmpWaterManager
{
public:
static void ClassInit(CComponentManager& componentManager)
{
componentManager.SubscribeToMessageType(MT_Interpolate);
componentManager.SubscribeToMessageType(MT_TerrainChanged);
}
DEFAULT_COMPONENT_ALLOCATOR(WaterManager)
// Dynamic state:
entity_pos_t m_WaterHeight;
static std::string GetSchema()
{
return "";
}
virtual void Init(const CParamNode& UNUSED(paramNode))
{
SetWaterLevel(entity_pos_t::FromInt(5));
}
virtual void Deinit()
{
}
virtual void Serialize(ISerializer& serialize)
{
serialize.NumberFixed_Unbounded("height", m_WaterHeight);
}
virtual void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize)
{
Init(paramNode);
deserialize.NumberFixed_Unbounded("height", m_WaterHeight);
}
virtual void HandleMessage(const CMessage& msg, bool UNUSED(global))
{
switch (msg.GetType())
{
case MT_Interpolate:
{
const CMessageInterpolate& msgData = static_cast (msg);
if (CRenderer::IsInitialised())
g_Renderer.GetWaterManager()->m_WaterTexTimer += msgData.deltaSimTime;
break;
}
case MT_TerrainChanged:
{
// Tell the renderer to redraw the map.
- if (CRenderer::IsInitialised())
+ // TODO: sometimes atlas glitches out.
+ // I've added a button to recompute on demand but that's not extremely nice.
+ if (CRenderer::IsInitialised() && !g_AtlasGameLoop->running)
{
const CMessageTerrainChanged& msgData = static_cast (msg);
g_Renderer.GetWaterManager()->m_NeedInfoUpdate = true;
g_Renderer.GetWaterManager()->m_updatei0 = msgData.i0;
g_Renderer.GetWaterManager()->m_updatej0 = msgData.j0;
g_Renderer.GetWaterManager()->m_updatei1 = msgData.i1;
g_Renderer.GetWaterManager()->m_updatej1 = msgData.j1;
GetSimContext().GetTerrain().MakeDirty(msgData.i0,msgData.j0,msgData.i1,msgData.j1,RENDERDATA_UPDATE_VERTICES);
}
break;
}
}
}
+ virtual void RecomputeWaterData()
+ {
+ ssize_t mapSize = GetSimContext().GetTerrain().GetVerticesPerSide();
+ g_Renderer.GetWaterManager()->m_NeedInfoUpdate = true;
+ g_Renderer.GetWaterManager()->m_updatei0 = 0;
+ g_Renderer.GetWaterManager()->m_updatej0 = 0;
+ g_Renderer.GetWaterManager()->m_updatei1 = mapSize-1;
+ g_Renderer.GetWaterManager()->m_updatej1 = mapSize-1;
+
+ // Tell the terrain it'll need to recompute its cached render data
+ GetSimContext().GetTerrain().MakeDirty(RENDERDATA_UPDATE_VERTICES);
+ }
+
virtual void SetWaterLevel(entity_pos_t h)
{
m_WaterHeight = h;
// Tell the terrain it'll need to recompute its cached render data
GetSimContext().GetTerrain().MakeDirty(RENDERDATA_UPDATE_VERTICES);
}
virtual entity_pos_t GetWaterLevel(entity_pos_t UNUSED(x), entity_pos_t UNUSED(z))
{
return m_WaterHeight;
}
virtual float GetExactWaterLevel(float UNUSED(x), float UNUSED(z))
{
return m_WaterHeight.ToFloat();
}
};
REGISTER_COMPONENT_TYPE(WaterManager)
Index: ps/trunk/source/simulation2/components/ICmpWaterManager.cpp
===================================================================
--- ps/trunk/source/simulation2/components/ICmpWaterManager.cpp (revision 14513)
+++ ps/trunk/source/simulation2/components/ICmpWaterManager.cpp (revision 14514)
@@ -1,27 +1,28 @@
/* Copyright (C) 2010 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 .
*/
#include "precompiled.h"
#include "ICmpWaterManager.h"
#include "simulation2/system/InterfaceScripted.h"
BEGIN_INTERFACE_WRAPPER(WaterManager)
+DEFINE_INTERFACE_METHOD_0("RecomputeWaterData", void, ICmpWaterManager, RecomputeWaterData)
DEFINE_INTERFACE_METHOD_1("SetWaterLevel", void, ICmpWaterManager, SetWaterLevel, entity_pos_t)
DEFINE_INTERFACE_METHOD_2("GetWaterLevel", entity_pos_t, ICmpWaterManager, GetWaterLevel, entity_pos_t, entity_pos_t)
END_INTERFACE_WRAPPER(WaterManager)
Index: ps/trunk/source/simulation2/components/ICmpWaterManager.h
===================================================================
--- ps/trunk/source/simulation2/components/ICmpWaterManager.h (revision 14513)
+++ ps/trunk/source/simulation2/components/ICmpWaterManager.h (revision 14514)
@@ -1,46 +1,51 @@
/* Copyright (C) 2010 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 .
*/
#ifndef INCLUDED_ICMPWATERMANAGER
#define INCLUDED_ICMPWATERMANAGER
#include "simulation2/system/Interface.h"
#include "simulation2/helpers/Position.h"
class ICmpWaterManager : public IComponent
{
public:
/**
+ * Recompute all the water information (foam…)
+ */
+ virtual void RecomputeWaterData() = 0;
+
+ /**
* Set the height of the water level, as a constant value across the whole map.
*/
virtual void SetWaterLevel(entity_pos_t h) = 0;
/**
* Get the current water level at the given point.
*/
virtual entity_pos_t GetWaterLevel(entity_pos_t x, entity_pos_t z) = 0;
/**
* Get the current water level at the given point.
*/
virtual float GetExactWaterLevel(float x, float z) = 0;
DECLARE_INTERFACE_TYPE(WaterManager)
};
#endif // INCLUDED_ICMPWATERMANAGER
Index: ps/trunk/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.cpp
===================================================================
--- ps/trunk/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.cpp (revision 14513)
+++ ps/trunk/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.cpp (revision 14514)
@@ -1,279 +1,291 @@
/* Copyright (C) 2013 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 .
*/
#include "precompiled.h"
#include "Environment.h"
#include "LightControl.h"
#include "GameInterface/Messages.h"
#include "ScenarioEditor/ScenarioEditor.h"
#include "General/Observable.h"
#include "CustomControls/ColourDialog/ColourDialog.h"
using AtlasMessage::Shareable;
static Observable g_EnvironmentSettings;
const float M_PIf = 3.14159265f;
//////////////////////////////////////////////////////////////////////////
class VariableSliderBox : public wxPanel
{
static const int range = 1024;
public:
VariableSliderBox(wxWindow* parent, const wxString& label, Shareable& var, float min, float max)
: wxPanel(parent),
m_Var(var), m_Min(min), m_Max(max)
{
m_Conn = g_EnvironmentSettings.RegisterObserver(0, &VariableSliderBox::OnSettingsChange, this);
m_Sizer = new wxStaticBoxSizer(wxVERTICAL, this, label);
SetSizer(m_Sizer);
m_Slider = new wxSlider(this, -1, 0, 0, range);
m_Sizer->Add(m_Slider, wxSizerFlags().Expand());
}
void OnSettingsChange(const AtlasMessage::sEnvironmentSettings& WXUNUSED(env))
{
m_Slider->SetValue((m_Var - m_Min) * (range / (m_Max - m_Min)));
}
void OnScroll(wxScrollEvent& evt)
{
m_Var = m_Min + (m_Max - m_Min)*(evt.GetInt() / (float)range);
g_EnvironmentSettings.NotifyObserversExcept(m_Conn);
}
private:
ObservableScopedConnection m_Conn;
wxStaticBoxSizer* m_Sizer;
wxSlider* m_Slider;
Shareable& m_Var;
float m_Min, m_Max;
DECLARE_EVENT_TABLE();
};
BEGIN_EVENT_TABLE(VariableSliderBox, wxPanel)
EVT_SCROLL(VariableSliderBox::OnScroll)
END_EVENT_TABLE()
//////////////////////////////////////////////////////////////////////////
class VariableListBox : public wxPanel
{
public:
VariableListBox(wxWindow* parent, const wxString& label, Shareable& var)
: wxPanel(parent),
m_Var(var)
{
m_Conn = g_EnvironmentSettings.RegisterObserver(0, &VariableListBox::OnSettingsChange, this);
m_Sizer = new wxStaticBoxSizer(wxVERTICAL, this, label);
SetSizer(m_Sizer);
m_Combo = new wxComboBox(this, -1, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxArrayString(), wxCB_READONLY),
m_Sizer->Add(m_Combo, wxSizerFlags().Expand());
}
void SetChoices(const std::vector& choices)
{
wxArrayString choices_arraystr;
for (size_t i = 0; i < choices.size(); ++i)
choices_arraystr.Add(choices[i].c_str());
m_Combo->Clear();
m_Combo->Append(choices_arraystr);
m_Combo->SetValue(m_Var.c_str());
}
void OnSettingsChange(const AtlasMessage::sEnvironmentSettings& WXUNUSED(env))
{
m_Combo->SetValue(m_Var.c_str());
}
void OnSelect(wxCommandEvent& WXUNUSED(evt))
{
m_Var = std::wstring(m_Combo->GetValue().c_str());
g_EnvironmentSettings.NotifyObserversExcept(m_Conn);
}
private:
ObservableScopedConnection m_Conn;
wxStaticBoxSizer* m_Sizer;
wxComboBox* m_Combo;
Shareable& m_Var;
DECLARE_EVENT_TABLE();
};
BEGIN_EVENT_TABLE(VariableListBox, wxPanel)
EVT_COMBOBOX(wxID_ANY, VariableListBox::OnSelect)
END_EVENT_TABLE()
//////////////////////////////////////////////////////////////////////////
class VariableColourBox : public wxPanel
{
public:
VariableColourBox(wxWindow* parent, const wxString& label, Shareable& colour)
: wxPanel(parent),
m_Colour(colour)
{
m_Conn = g_EnvironmentSettings.RegisterObserver(0, &VariableColourBox::OnSettingsChange, this);
m_Sizer = new wxStaticBoxSizer(wxVERTICAL, this, label);
SetSizer(m_Sizer);
m_Button = new wxButton(this, -1);
m_Sizer->Add(m_Button, wxSizerFlags().Expand());
}
void OnSettingsChange(const AtlasMessage::sEnvironmentSettings& WXUNUSED(env))
{
UpdateButton();
}
void OnClick(wxCommandEvent& WXUNUSED(evt))
{
ColourDialog dlg (this, _T("Scenario Editor/LightingColour"),
wxColour(m_Colour->r, m_Colour->g, m_Colour->b));
if (dlg.ShowModal() == wxID_OK)
{
wxColour& c = dlg.GetColourData().GetColour();
m_Colour = AtlasMessage::Colour(c.Red(), c.Green(), c.Blue());
UpdateButton();
g_EnvironmentSettings.NotifyObserversExcept(m_Conn);
}
}
void UpdateButton()
{
m_Button->SetBackgroundColour(wxColour(m_Colour->r, m_Colour->g, m_Colour->b));
m_Button->SetLabel(wxString::Format(_T("%02X %02X %02X"), m_Colour->r, m_Colour->g, m_Colour->b));
int y = 3*m_Colour->r + 6*m_Colour->g + 1*m_Colour->b;
if (y > 1280)
m_Button->SetForegroundColour(wxColour(0, 0, 0));
else
m_Button->SetForegroundColour(wxColour(255, 255, 255));
}
private:
ObservableScopedConnection m_Conn;
wxStaticBoxSizer* m_Sizer;
wxButton* m_Button;
Shareable& m_Colour;
DECLARE_EVENT_TABLE();
};
BEGIN_EVENT_TABLE(VariableColourBox, wxPanel)
EVT_BUTTON(wxID_ANY, VariableColourBox::OnClick)
END_EVENT_TABLE()
//////////////////////////////////////////////////////////////////////////
+enum {
+ ID_RecomputeWaterData
+};
static void SendToGame(const AtlasMessage::sEnvironmentSettings& settings)
{
POST_COMMAND(SetEnvironmentSettings, (settings));
}
EnvironmentSidebar::EnvironmentSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer)
: Sidebar(scenarioEditor, sidebarContainer, bottomBarContainer)
{
wxSizer* scrollSizer = new wxBoxSizer(wxVERTICAL);
wxScrolledWindow* scrolledWindow = new wxScrolledWindow(this);
scrolledWindow->SetScrollRate(10, 10);
scrolledWindow->SetSizer(scrollSizer);
m_MainSizer->Add(scrolledWindow, wxSizerFlags().Expand().Proportion(1));
wxSizer* waterSizer = new wxStaticBoxSizer(wxVERTICAL, scrolledWindow, _T("Water settings"));
scrollSizer->Add(waterSizer, wxSizerFlags().Expand());
-
+ waterSizer->Add(new wxButton(this, ID_RecomputeWaterData, _("Reset Water Data")), wxSizerFlags().Expand());
waterSizer->Add(new VariableSliderBox(scrolledWindow, _("Water height"), g_EnvironmentSettings.waterheight, 0.f, 1.2f), wxSizerFlags().Expand());
- waterSizer->Add(new VariableSliderBox(scrolledWindow, _("Water shininess"), g_EnvironmentSettings.watershininess, 0.f, 250.f), wxSizerFlags().Expand());
waterSizer->Add(new VariableSliderBox(scrolledWindow, _("Water waviness"), g_EnvironmentSettings.waterwaviness, 0.f, 10.f), wxSizerFlags().Expand());
waterSizer->Add(new VariableSliderBox(scrolledWindow, _("Water murkiness"), g_EnvironmentSettings.watermurkiness, 0.f, 1.f), wxSizerFlags().Expand());
waterSizer->Add(new VariableColourBox(scrolledWindow, _("Water colour"), g_EnvironmentSettings.watercolour), wxSizerFlags().Expand());
waterSizer->Add(new VariableColourBox(scrolledWindow, _("Water tint"), g_EnvironmentSettings.watertint), wxSizerFlags().Expand());
waterSizer->Add(new VariableColourBox(scrolledWindow, _("Reflection tint"), g_EnvironmentSettings.waterreflectiontint), wxSizerFlags().Expand());
waterSizer->Add(new VariableSliderBox(scrolledWindow, _("Reflection tint strength"), g_EnvironmentSettings.waterreflectiontintstrength, 0.f, 1.f), wxSizerFlags().Expand());
wxSizer* sunSizer = new wxStaticBoxSizer(wxVERTICAL, scrolledWindow, _T("Sun / lighting settings"));
scrollSizer->Add(sunSizer, wxSizerFlags().Expand().Border(wxTOP, 8));
sunSizer->Add(new VariableSliderBox(scrolledWindow, _("Sun rotation"), g_EnvironmentSettings.sunrotation, -M_PIf, M_PIf), wxSizerFlags().Expand());
sunSizer->Add(new VariableSliderBox(scrolledWindow, _("Sun elevation"), g_EnvironmentSettings.sunelevation, -M_PIf/2, M_PIf/2), wxSizerFlags().Expand());
sunSizer->Add(new VariableSliderBox(scrolledWindow, _("Sun overbrightness"), g_EnvironmentSettings.sunoverbrightness, 1.0f, 3.0f), wxSizerFlags().Expand());
sunSizer->Add(new LightControl(scrolledWindow, wxSize(150, 150), g_EnvironmentSettings));
sunSizer->Add(new VariableColourBox(scrolledWindow, _("Sun colour"), g_EnvironmentSettings.suncolour), wxSizerFlags().Expand());
sunSizer->Add(m_SkyList = new VariableListBox(scrolledWindow, _("Sky set"), g_EnvironmentSettings.skyset), wxSizerFlags().Expand());
sunSizer->Add(new VariableSliderBox(scrolledWindow, _("Fog Factor"), g_EnvironmentSettings.fogfactor, 0.0f, 0.01f), wxSizerFlags().Expand());
sunSizer->Add(new VariableSliderBox(scrolledWindow, _("Fog Thickness"), g_EnvironmentSettings.fogmax, 0.5f, 0.0f), wxSizerFlags().Expand());
sunSizer->Add(new VariableColourBox(scrolledWindow, _("Fog colour"), g_EnvironmentSettings.fogcolour), wxSizerFlags().Expand());
sunSizer->Add(new VariableColourBox(scrolledWindow, _("Terrain ambient colour"), g_EnvironmentSettings.terraincolour), wxSizerFlags().Expand());
sunSizer->Add(new VariableColourBox(scrolledWindow, _("Object ambient colour"), g_EnvironmentSettings.unitcolour), wxSizerFlags().Expand());
wxSizer* postProcSizer = new wxStaticBoxSizer(wxVERTICAL, scrolledWindow, _T("Post-processing settings"));
scrollSizer->Add(postProcSizer, wxSizerFlags().Expand().Border(wxTOP, 8));
postProcSizer->Add(m_PostEffectList = new VariableListBox(scrolledWindow, _("Post Effect"), g_EnvironmentSettings.posteffect), wxSizerFlags().Expand());
postProcSizer->Add(new VariableSliderBox(scrolledWindow, _("Brightness"), g_EnvironmentSettings.brightness, -0.5f, 0.5f), wxSizerFlags().Expand());
postProcSizer->Add(new VariableSliderBox(scrolledWindow, _("Contrast (HDR)"), g_EnvironmentSettings.contrast, 0.5f, 1.5f), wxSizerFlags().Expand());
postProcSizer->Add(new VariableSliderBox(scrolledWindow, _("Saturation"), g_EnvironmentSettings.saturation, 0.0f, 2.0f), wxSizerFlags().Expand());
postProcSizer->Add(new VariableSliderBox(scrolledWindow, _("Bloom"), g_EnvironmentSettings.bloom, 0.2f, 0.0f), wxSizerFlags().Expand());
m_Conn = g_EnvironmentSettings.RegisterObserver(0, &SendToGame);
}
void EnvironmentSidebar::OnFirstDisplay()
{
// Load the list of skies. (Can only be done now rather than in the constructor,
// after the game has been initialised.)
AtlasMessage::qGetSkySets qry_skysets;
qry_skysets.Post();
m_SkyList->SetChoices(*qry_skysets.skysets);
AtlasMessage::qGetPostEffects qry_effects;
qry_effects.Post();
m_PostEffectList->SetChoices(*qry_effects.posteffects);
AtlasMessage::qGetEnvironmentSettings qry_env;
qry_env.Post();
g_EnvironmentSettings = qry_env.settings;
g_EnvironmentSettings.NotifyObservers();
}
void EnvironmentSidebar::OnMapReload()
{
AtlasMessage::qGetEnvironmentSettings qry_env;
qry_env.Post();
g_EnvironmentSettings = qry_env.settings;
g_EnvironmentSettings.NotifyObservers();
}
+
+void EnvironmentSidebar::RecomputeWaterData(wxCommandEvent& evt)
+{
+ POST_COMMAND(RecalculateWaterData, (0.0f));
+}
+
+BEGIN_EVENT_TABLE(EnvironmentSidebar, Sidebar)
+ EVT_BUTTON(ID_RecomputeWaterData, EnvironmentSidebar::RecomputeWaterData)
+END_EVENT_TABLE();
+
Index: ps/trunk/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.h
===================================================================
--- ps/trunk/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.h (revision 14513)
+++ ps/trunk/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Environment/Environment.h (revision 14514)
@@ -1,38 +1,41 @@
/* Copyright (C) 2009 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 .
*/
#include "../Common/Sidebar.h"
#include "General/Observable.h"
class VariableListBox;
class EnvironmentSidebar : public Sidebar
{
public:
EnvironmentSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer);
virtual void OnMapReload();
+ virtual void RecomputeWaterData(wxCommandEvent& evt);
protected:
virtual void OnFirstDisplay();
private:
VariableListBox* m_PostEffectList;
VariableListBox* m_SkyList;
ObservableScopedConnection m_Conn;
+
+ DECLARE_EVENT_TABLE();
};
Index: ps/trunk/source/tools/atlas/GameInterface/Handlers/EnvironmentHandlers.cpp
===================================================================
--- ps/trunk/source/tools/atlas/GameInterface/Handlers/EnvironmentHandlers.cpp (revision 14513)
+++ ps/trunk/source/tools/atlas/GameInterface/Handlers/EnvironmentHandlers.cpp (revision 14514)
@@ -1,190 +1,212 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* 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 .
*/
#include "precompiled.h"
#include "MessageHandler.h"
#include "../CommandProc.h"
#include "graphics/LightEnv.h"
#include "graphics/Terrain.h"
#include "maths/MathUtil.h"
#include "ps/Game.h"
#include "ps/World.h"
#include "renderer/Renderer.h"
#include "renderer/SkyManager.h"
#include "renderer/WaterManager.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpWaterManager.h"
namespace AtlasMessage {
sEnvironmentSettings GetSettings()
{
sEnvironmentSettings s;
CmpPtr cmpWaterManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
ENSURE(cmpWaterManager);
s.waterheight = cmpWaterManager->GetExactWaterLevel(0, 0) / (65536.f * HEIGHT_SCALE);
WaterManager* wm = g_Renderer.GetWaterManager();
- s.watershininess = wm->m_Shininess;
s.waterwaviness = wm->m_Waviness;
s.watermurkiness = wm->m_Murkiness;
s.waterreflectiontintstrength = wm->m_ReflectionTintStrength;
// CColor colours
#define COLOUR(A, B) A = Colour((int)(B.r*255), (int)(B.g*255), (int)(B.b*255))
COLOUR(s.watercolour, wm->m_WaterColor);
COLOUR(s.watertint, wm->m_WaterTint);
COLOUR(s.waterreflectiontint, wm->m_ReflectionTint);
#undef COLOUR
float sunrotation = g_LightEnv.GetRotation();
if (sunrotation > (float)M_PI)
sunrotation -= (float)M_PI*2;
s.sunrotation = sunrotation;
s.sunelevation = g_LightEnv.GetElevation();
s.posteffect = g_Renderer.GetPostprocManager().GetPostEffect();
s.skyset = g_Renderer.GetSkyManager()->GetSkySet();
s.fogfactor = g_LightEnv.m_FogFactor;
s.fogmax = g_LightEnv.m_FogMax;
s.brightness = g_LightEnv.m_Brightness;
s.contrast = g_LightEnv.m_Contrast;
s.saturation = g_LightEnv.m_Saturation;
s.bloom = g_LightEnv.m_Bloom;
// RGBColor (CVector3D) colours
#define COLOUR(A, B) A = Colour((int)(B.X*255), (int)(B.Y*255), (int)(B.Z*255))
s.sunoverbrightness = MaxComponent(g_LightEnv.m_SunColor);
// clamp color to [0..1] before packing into u8 triplet
if(s.sunoverbrightness > 1.0f)
g_LightEnv.m_SunColor *= 1.0/s.sunoverbrightness; // (there's no operator/=)
// no component was above 1.0, so reset scale factor (don't want to darken)
else
s.sunoverbrightness = 1.0f;
COLOUR(s.suncolour, g_LightEnv.m_SunColor);
COLOUR(s.terraincolour, g_LightEnv.m_TerrainAmbientColor);
COLOUR(s.unitcolour, g_LightEnv.m_UnitsAmbientColor);
COLOUR(s.fogcolour, g_LightEnv.m_FogColor);
#undef COLOUR
return s;
}
void SetSettings(const sEnvironmentSettings& s)
{
CmpPtr cmpWaterManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
ENSURE(cmpWaterManager);
cmpWaterManager->SetWaterLevel(entity_pos_t::FromFloat(s.waterheight * (65536.f * HEIGHT_SCALE)));
WaterManager* wm = g_Renderer.GetWaterManager();
- wm->m_Shininess = s.watershininess;
wm->m_Waviness = s.waterwaviness;
wm->m_Murkiness = s.watermurkiness;
wm->m_ReflectionTintStrength = s.waterreflectiontintstrength;
#define COLOUR(A, B) B = CColor(A->r/255.f, A->g/255.f, A->b/255.f, 1.f)
COLOUR(s.watercolour, wm->m_WaterColor);
COLOUR(s.watertint, wm->m_WaterTint);
COLOUR(s.waterreflectiontint, wm->m_ReflectionTint);
#undef COLOUR
g_LightEnv.SetRotation(s.sunrotation);
g_LightEnv.SetElevation(s.sunelevation);
CStrW posteffect = *s.posteffect;
if (posteffect.length() == 0)
posteffect = L"default";
g_Renderer.GetPostprocManager().SetPostEffect(posteffect);
CStrW skySet = *s.skyset;
if (skySet.length() == 0)
skySet = L"default";
g_Renderer.GetSkyManager()->SetSkySet(skySet);
g_LightEnv.m_FogFactor = s.fogfactor;
g_LightEnv.m_FogMax = s.fogmax;
g_LightEnv.m_Brightness = s.brightness;
g_LightEnv.m_Contrast = s.contrast;
g_LightEnv.m_Saturation = s.saturation;
g_LightEnv.m_Bloom = s.bloom;
#define COLOUR(A, B) B = RGBColor(A->r/255.f, A->g/255.f, A->b/255.f)
COLOUR(s.suncolour, g_LightEnv.m_SunColor);
g_LightEnv.m_SunColor *= s.sunoverbrightness;
COLOUR(s.terraincolour, g_LightEnv.m_TerrainAmbientColor);
COLOUR(s.unitcolour, g_LightEnv.m_UnitsAmbientColor);
COLOUR(s.fogcolour, g_LightEnv.m_FogColor);
#undef COLOUR
}
BEGIN_COMMAND(SetEnvironmentSettings)
{
sEnvironmentSettings m_OldSettings, m_NewSettings;
void Do()
{
m_OldSettings = GetSettings();
m_NewSettings = msg->settings;
Redo();
}
void Redo()
{
SetSettings(m_NewSettings);
}
void Undo()
{
SetSettings(m_OldSettings);
}
void MergeIntoPrevious(cSetEnvironmentSettings* prev)
{
prev->m_NewSettings = m_NewSettings;
}
};
END_COMMAND(SetEnvironmentSettings)
+BEGIN_COMMAND(RecalculateWaterData)
+{
+ void Do()
+ {
+ Redo();
+ }
+
+ void Redo()
+ {
+ CmpPtr cmpWaterManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
+ ENSURE(cmpWaterManager);
+
+ cmpWaterManager->RecomputeWaterData();
+ }
+
+ void Undo()
+ {
+ Redo();
+ }
+
+};
+END_COMMAND(RecalculateWaterData)
+
+
QUERYHANDLER(GetEnvironmentSettings)
{
msg->settings = GetSettings();
}
QUERYHANDLER(GetSkySets)
{
std::vector skies = g_Renderer.GetSkyManager()->GetSkySets();
msg->skysets = std::vector(skies.begin(), skies.end());
}
QUERYHANDLER(GetPostEffects)
{
std::vector effects = g_Renderer.GetPostprocManager().GetPostEffects();
msg->posteffects = std::vector(effects.begin(), effects.end());
}
}
Index: ps/trunk/source/tools/atlas/GameInterface/Messages.h
===================================================================
--- ps/trunk/source/tools/atlas/GameInterface/Messages.h (revision 14513)
+++ ps/trunk/source/tools/atlas/GameInterface/Messages.h (revision 14514)
@@ -1,674 +1,675 @@
/* Copyright (C) 2013 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 .
*/
#ifndef INCLUDED_MESSAGES
#define INCLUDED_MESSAGES
#ifndef MESSAGES_SKIP_SETUP
#include "MessagesSetup.h"
#endif
#include
#include
// TODO: organisation, documentation, etc
//////////////////////////////////////////////////////////////////////////
// Initialise some engine code. Must be called before anything else.
MESSAGE(Init, );
// Initialise graphics-related code. Must be called after the first SetCanvas,
// and before much else.
MESSAGE(InitGraphics, );
// Shut down engine/graphics code.
MESSAGE(Shutdown, );
struct eRenderView { enum renderViews { NONE, GAME, ACTOR }; };
MESSAGE(RenderEnable,
((int, view)) // eRenderView
);
// SetViewParam: used for hints to the renderer, e.g. to set wireframe mode;
// unrecognised param names are ignored
MESSAGE(SetViewParamB,
((int, view)) // eRenderView
((std::wstring, name))
((bool, value))
);
MESSAGE(SetViewParamI,
((int, view)) // eRenderView
((std::wstring, name))
((int, value))
);
MESSAGE(SetViewParamC,
((int, view)) // eRenderView
((std::wstring, name))
((Colour, value))
);
MESSAGE(SetViewParamS,
((int, view)) // eRenderView
((std::wstring, name))
((std::wstring, value))
);
MESSAGE(JavaScript,
((std::wstring, command))
);
//////////////////////////////////////////////////////////////////////////
MESSAGE(GuiSwitchPage,
((std::wstring, page))
);
MESSAGE(GuiMouseButtonEvent,
((int, button))
((bool, pressed))
((Position, pos))
);
MESSAGE(GuiMouseMotionEvent,
((Position, pos))
);
MESSAGE(GuiKeyEvent,
((int, sdlkey)) // SDLKey code
((int, unichar)) // Unicode character
((bool, pressed))
);
MESSAGE(GuiCharEvent,
((int, sdlkey))
((int, unichar))
);
//////////////////////////////////////////////////////////////////////////
MESSAGE(SimStateSave,
((std::wstring, label)) // named slot to store saved data
);
MESSAGE(SimStateRestore,
((std::wstring, label)) // named slot to find saved data
);
QUERY(SimStateDebugDump,
((bool, binary))
,
((std::wstring, dump))
);
MESSAGE(SimPlay,
((float, speed)) // 0 for pause, 1 for normal speed
((bool, simTest)) // true if we're in simulation test mode, false otherwise
);
//////////////////////////////////////////////////////////////////////////
QUERY(Ping, , );
//////////////////////////////////////////////////////////////////////////
MESSAGE(SetCanvas,
((void*, canvas))
((int, width))
((int, height))
);
MESSAGE(ResizeScreen,
((int, width))
((int, height))
);
//////////////////////////////////////////////////////////////////////////
// Messages for map panel
QUERY(GenerateMap,
((std::wstring, filename)) // random map script filename
((std::string, settings)) // map settings as JSON string
,
((int, status))
);
MESSAGE(ImportHeightmap,
((std::wstring, filename))
);
MESSAGE(LoadMap,
((std::wstring, filename))
);
MESSAGE(SaveMap,
((std::wstring, filename))
);
QUERY(GetMapList,
,
((std::vector, scenarioFilenames))
((std::vector, skirmishFilenames))
);
QUERY(GetMapSettings,
,
((std::string, settings))
);
COMMAND(SetMapSettings, MERGE,
((std::string, settings))
);
MESSAGE(LoadPlayerSettings,
((bool, newplayers))
);
QUERY(GetMapSizes,
,
((std::string, sizes))
);
QUERY(GetRMSData,
,
((std::vector, data))
);
COMMAND(ResizeMap, NOMERGE,
((int, tiles))
);
QUERY(VFSFileExists,
((std::wstring, path))
,
((bool, exists))
);
//////////////////////////////////////////////////////////////////////////
// Messages for player panel
QUERY(GetCivData,
,
((std::vector, data))
);
QUERY(GetPlayerDefaults,
,
((std::string, defaults))
);
QUERY(GetAIData,
,
((std::string, data))
);
//////////////////////////////////////////////////////////////////////////
MESSAGE(RenderStyle,
((bool, wireframe))
);
MESSAGE(MessageTrace,
((bool, enable))
);
MESSAGE(Screenshot,
((bool, big))
((int, tiles)) // For big screenshots: the final image will be (640*tiles)x(480*tiles)
);
#ifndef MESSAGES_SKIP_STRUCTS
struct sCinemaRecordCB
{
unsigned char* buffer;
};
SHAREABLE_STRUCT(sCinemaRecordCB);
#endif
QUERY(CinemaRecord,
((std::wstring, path))
((int, framerate))
((float, duration))
((int, width))
((int, height))
((Callback, cb))
,
);
//////////////////////////////////////////////////////////////////////////
MESSAGE(Brush,
((int, width)) // number of vertices
((int, height))
((std::vector, data)) // width*height array
);
MESSAGE(BrushPreview,
((bool, enable))
((Position, pos)) // only used if enable==true
);
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
QUERY(GetTerrainGroups,
, // no inputs
((std::vector, groupNames))
);
#ifndef MESSAGES_SKIP_STRUCTS
struct sTerrainTexturePreview
{
Shareable name;
Shareable loaded;
Shareable imageWidth;
Shareable imageHeight;
Shareable > imageData; // RGB*width*height
};
SHAREABLE_STRUCT(sTerrainTexturePreview);
#endif
QUERY(GetTerrainGroupPreviews,
((std::wstring, groupName))
((int, imageWidth))
((int, imageHeight))
,
((std::vector, previews))
);
QUERY(GetTerrainPassabilityClasses,
, // no inputs
((std::vector, classNames))
);
QUERY(GetTerrainTexturePreview,
((std::wstring, name))
((int, imageWidth))
((int, imageHeight))
,
((sTerrainTexturePreview, preview))
);
//////////////////////////////////////////////////////////////////////////
#ifndef MESSAGES_SKIP_STRUCTS
struct sObjectsListItem
{
Shareable id;
Shareable name;
Shareable type; // 0 = entity, 1 = actor
};
SHAREABLE_STRUCT(sObjectsListItem);
#endif
QUERY(GetObjectsList,
, // no inputs
((std::vector, objects)) // sorted by .name
);
#ifndef MESSAGES_SKIP_STRUCTS
struct sObjectSettings
{
Shareable player;
Shareable > selections;
// Some settings are immutable and therefore are ignored (and should be left
// empty) when passed from the editor to the game:
Shareable > > variantGroups;
};
SHAREABLE_STRUCT(sObjectSettings);
#endif
// Preview object in the game world - creates a temporary unit at the given
// position, and removes it when the preview is next changed
MESSAGE(ObjectPreview,
((std::wstring, id)) // or empty string => disable
((sObjectSettings, settings))
((Position, pos))
((bool, usetarget)) // true => use 'target' for orientation; false => use 'angle'
((Position, target))
((float, angle))
((unsigned int, actorseed))
);
COMMAND(CreateObject, NOMERGE,
((std::wstring, id))
((sObjectSettings, settings))
((Position, pos))
((bool, usetarget)) // true => use 'target' for orientation; false => use 'angle'
((Position, target))
((float, angle))
((unsigned int, actorseed))
);
// Set an actor to be previewed on its own (i.e. without the game world).
// (Use RenderEnable to make it visible.)
MESSAGE(SetActorViewer,
((std::wstring, id))
((std::wstring, animation))
((int, playerID))
((float, speed))
((bool, flushcache)) // true => unload all actor files before starting the preview (because we don't have proper hotloading yet)
);
//////////////////////////////////////////////////////////////////////////
QUERY(Exit,,); // no inputs nor outputs
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
struct eScrollConstantDir { enum { FORWARDS, BACKWARDS, LEFT, RIGHT, CLOCKWISE, ANTICLOCKWISE }; };
MESSAGE(ScrollConstant, // set a constant scrolling(/rotation) rate
((int, view)) // eRenderView
((int, dir)) // eScrollConstantDir
((float, speed)) // set speed 0.0f to stop scrolling
);
struct eScrollType { enum { FROM, TO }; };
MESSAGE(Scroll, // for scrolling by dragging the mouse FROM somewhere TO elsewhere
((int, view)) // eRenderView
((int, type)) // eScrollType
((Position, pos))
);
MESSAGE(SmoothZoom,
((int, view)) // eRenderView
((float, amount))
);
struct eRotateAroundType { enum { FROM, TO }; };
MESSAGE(RotateAround,
((int, view)) // eRenderView
((int, type)) // eRotateAroundType
((Position, pos))
);
MESSAGE(LookAt,
((int, view)) // eRenderView
((Position, pos))
((Position, target))
);
MESSAGE(CameraReset, );
QUERY(GetView,
,
((sCameraInfo, info))
);
MESSAGE(SetView,
((sCameraInfo, info))
);
//////////////////////////////////////////////////////////////////////////
#ifndef MESSAGES_SKIP_STRUCTS
struct sEnvironmentSettings
{
Shareable waterheight; // range 0..1 corresponds to min..max terrain height; out-of-bounds values allowed
- Shareable watershininess; // range ???
Shareable waterwaviness; // range ???
Shareable watermurkiness; // range ???
Shareable watercolour;
Shareable watertint;
Shareable waterreflectiontint;
Shareable waterreflectiontintstrength; // range ???
Shareable sunrotation; // range -pi..+pi
Shareable sunelevation; // range -pi/2 .. +pi/2
// emulate 'HDR' by allowing overly bright suncolour. this is
// multiplied on to suncolour after converting to float
// (struct Colour stores as normal u8, 0..255)
Shareable sunoverbrightness; // range 1..3
// support different lighting models ("old" for the version compatible with old scenarios,
// "standard" for the new normal model that supports much brighter lighting)
Shareable posteffect;
Shareable skyset;
Shareable suncolour;
Shareable terraincolour;
Shareable unitcolour;
Shareable fogcolour;
Shareable fogfactor;
Shareable fogmax;
Shareable brightness;
Shareable contrast;
Shareable saturation;
Shareable bloom;
};
SHAREABLE_STRUCT(sEnvironmentSettings);
#endif
QUERY(GetEnvironmentSettings,
// no inputs
,
((sEnvironmentSettings, settings))
);
COMMAND(SetEnvironmentSettings, MERGE, // merge lots of small changes into one undoable command
((sEnvironmentSettings, settings))
);
+COMMAND(RecalculateWaterData, NOMERGE, ((float,unused)));
+
QUERY(GetSkySets,
// no inputs
,
((std::vector, skysets))
);
QUERY(GetPostEffects,
// no inputs
,
((std::vector, posteffects))
);
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
COMMAND(AlterElevation, MERGE,
((Position, pos))
((float, amount))
);
COMMAND(SmoothElevation, MERGE,
((Position, pos))
((float, amount))
);
COMMAND(FlattenElevation, MERGE,
((Position, pos))
((float, amount))
);
COMMAND(PikeElevation, MERGE,
((Position, pos))
((float, amount))
);
struct ePaintTerrainPriority { enum { HIGH, LOW }; };
COMMAND(PaintTerrain, MERGE,
((Position, pos))
((std::wstring, texture))
((int, priority)) // ePaintTerrainPriority
);
COMMAND(ReplaceTerrain, NOMERGE,
((Position, pos))
((std::wstring, texture))
);
COMMAND(FillTerrain, NOMERGE,
((Position, pos))
((std::wstring, texture))
);
QUERY(GetTerrainTexture,
((Position, pos))
,
((std::wstring, texture))
);
//////////////////////////////////////////////////////////////////////////
QUERY(PickObject,
((Position, pos))
((bool, selectActors))
,
((ObjectID, id))
((int, offsetx)) // offset of object centre from input position
((int, offsety)) //
);
QUERY(PickObjectsInRect,
((Position, start))
((Position, end))
((bool, selectActors))
,
((std::vector, ids))
);
QUERY(PickSimilarObjects,
((ObjectID, id))
,
((std::vector, ids))
);
COMMAND(MoveObjects, MERGE,
((std::vector, ids))
((ObjectID, pivot))
((Position, pos))
);
COMMAND(RotateObject, MERGE,
((ObjectID, id))
((bool, usetarget)) // true => use 'target' for orientation; false => use 'angle'
((Position, target))
((float, angle))
);
COMMAND(DeleteObjects, NOMERGE,
((std::vector, ids))
);
MESSAGE(SetSelectionPreview,
((std::vector, ids))
);
QUERY(GetObjectSettings,
((int, view)) // eRenderView
((ObjectID, id))
,
((sObjectSettings, settings))
);
COMMAND(SetObjectSettings, NOMERGE,
((int, view)) // eRenderView
((ObjectID, id))
((sObjectSettings, settings))
);
QUERY(GetPlayerObjects,
((int, player))
,
((std::vector, ids))
);
MESSAGE(SetBandbox,
((bool, show))
((int, sx0))
((int, sy0))
((int, sx1))
((int, sy1))
);
//////////////////////////////////////////////////////////////////////////
QUERY(GetCinemaPaths,
, // no inputs
((std::vector , paths))
);
QUERY(GetCameraInfo,
,
((AtlasMessage::sCameraInfo, info))
);
COMMAND(SetCinemaPaths, NOMERGE,
((std::vector, paths))
);
MESSAGE(CinemaEvent,
((std::wstring, path))
((int, mode))
((float, t))
((bool, drawCurrent))
((bool, lines))
);
//////////////////////////////////////////////////////////////////////////
enum eTriggerListType
{
CINEMA_LIST,
TRIGGER_LIST,
TRIG_GROUP_LIST //list of trigger groups
// [Eventually include things like entities and areas as the editor progresses...]
};
QUERY(GetTriggerData,
, //no inputs
((std::vector, groups))
((std::vector, conditions))
((std::vector, effects))
);
QUERY(GetTriggerChoices,
((std::wstring, name)),
((std::vector, choices))
((std::vector, translations))
);
COMMAND(SetAllTriggers, NOMERGE,
((std::vector, groups))
);
QUERY(GetWorldPosition,
((int, x))
((int, y)),
((Position, position))
);
MESSAGE(TriggerToggleSelector,
((bool, enable))
((Position, position))
);
#ifndef MESSAGES_SKIP_SETUP
#include "MessagesSetup.h"
#endif
#endif // INCLUDED_MESSAGES