diff --git a/.travis.yml b/.travis.yml index 983e7166d..6a0b46f4b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,9 +6,7 @@ language: cpp compiler: - gcc - #- clang -git: - submodules: false +# - clang #branches: # only: # - master @@ -16,10 +14,13 @@ before_install: # UPDATE REPOS - sudo apt-get update -qq # INSTALL DEPENDENCIES - - sudo apt-get install autoconf automake build-essential cmake libogg-dev libvorbis-dev libopenal-dev libxxf86vm-dev libgl1-mesa-dev libglu1-mesa-dev libcurl4-openssl-dev libfribidi-dev libbluetooth-dev + - sudo apt-get install build-essential cmake libogg-dev libvorbis-dev libopenal-dev libxxf86vm-dev libgl1-mesa-dev libglu1-mesa-dev libcurl4-openssl-dev libfribidi-dev libbluetooth-dev script: # BUILD COMMANDS - - ./tools/build-linux-travis.sh + - mkdir build + - cd build + - cmake .. -DCMAKE_BUILD_TYPE=Debug + - make VERBOSE=1 -j 4 notifications: irc: channels: diff --git a/CMakeLists.txt b/CMakeLists.txt index eb544bd66..0aa20f5f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ project(SuperTuxKart) set(PROJECT_VERSION "0.8.1") cmake_minimum_required(VERSION 2.8.1) -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/cmake) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake") include(BuildTypeSTKRelease) if (NOT CMAKE_BUILD_TYPE) diff --git a/data/CREDITS b/data/CREDITS index b358814c2..0ab04b4de 100644 Binary files a/data/CREDITS and b/data/CREDITS differ diff --git a/data/achievements.xml b/data/achievements.xml index feb017a98..119531085 100644 --- a/data/achievements.xml +++ b/data/achievements.xml @@ -1,6 +1,6 @@ - @@ -13,21 +13,35 @@ - - - - + - - + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/gui/down.png b/data/gui/down.png new file mode 100644 index 000000000..87eda19a3 Binary files /dev/null and b/data/gui/down.png differ diff --git a/data/gui/edit_track.stkgui b/data/gui/edit_track.stkgui new file mode 100644 index 000000000..41c8800b6 --- /dev/null +++ b/data/gui/edit_track.stkgui @@ -0,0 +1,54 @@ + + +
+ +
+ + + + + + + + + + + + + + +
+ + + +
+ + +
+ +
+ +
+ +
\ No newline at end of file diff --git a/data/gui/enter_gp_name_dialog.stkgui b/data/gui/enter_gp_name_dialog.stkgui new file mode 100644 index 000000000..0c69499b2 --- /dev/null +++ b/data/gui/enter_gp_name_dialog.stkgui @@ -0,0 +1,21 @@ + + +
+ +
+ +
\ No newline at end of file diff --git a/data/gui/gp_add_track.png b/data/gui/gp_add_track.png new file mode 100644 index 000000000..6e0898abd Binary files /dev/null and b/data/gui/gp_add_track.png differ diff --git a/data/gui/gp_copy.png b/data/gui/gp_copy.png new file mode 100644 index 000000000..4dd134dcd Binary files /dev/null and b/data/gui/gp_copy.png differ diff --git a/data/gui/gp_edit.png b/data/gui/gp_edit.png new file mode 100644 index 000000000..9de1f49b3 Binary files /dev/null and b/data/gui/gp_edit.png differ diff --git a/data/gui/gp_edit_track.png b/data/gui/gp_edit_track.png new file mode 100644 index 000000000..7081d7eeb Binary files /dev/null and b/data/gui/gp_edit_track.png differ diff --git a/data/gui/gp_new.png b/data/gui/gp_new.png new file mode 100644 index 000000000..a4aefdfab Binary files /dev/null and b/data/gui/gp_new.png differ diff --git a/data/gui/gp_remove.png b/data/gui/gp_remove.png new file mode 100644 index 000000000..504a08730 Binary files /dev/null and b/data/gui/gp_remove.png differ diff --git a/data/gui/gp_remove_track.png b/data/gui/gp_remove_track.png new file mode 100644 index 000000000..7e1156279 Binary files /dev/null and b/data/gui/gp_remove_track.png differ diff --git a/data/gui/gp_rename.png b/data/gui/gp_rename.png new file mode 100644 index 000000000..94f09fe59 Binary files /dev/null and b/data/gui/gp_rename.png differ diff --git a/data/gui/gp_save.png b/data/gui/gp_save.png new file mode 100644 index 000000000..7025c0b48 Binary files /dev/null and b/data/gui/gp_save.png differ diff --git a/data/gui/gpedit.stkgui b/data/gui/gpedit.stkgui new file mode 100644 index 000000000..890e5e300 --- /dev/null +++ b/data/gui/gpedit.stkgui @@ -0,0 +1,35 @@ + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
\ No newline at end of file diff --git a/data/gui/gpeditor.png b/data/gui/gpeditor.png new file mode 100644 index 000000000..0562fb8a2 Binary files /dev/null and b/data/gui/gpeditor.png differ diff --git a/data/gui/gpeditor.stkgui b/data/gui/gpeditor.stkgui new file mode 100644 index 000000000..c830525a5 --- /dev/null +++ b/data/gui/gpeditor.stkgui @@ -0,0 +1,45 @@ + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+ +
\ No newline at end of file diff --git a/data/gui/main.stkgui b/data/gui/main.stkgui index 7a297aee9..3d06834b0 100644 --- a/data/gui/main.stkgui +++ b/data/gui/main.stkgui @@ -1,9 +1,9 @@ - +
diff --git a/data/gui/up.png b/data/gui/up.png new file mode 100644 index 000000000..ec7f7d30f Binary files /dev/null and b/data/gui/up.png differ diff --git a/data/shaders/caustics.frag b/data/shaders/caustics.frag index 203bd2b40..2c87b03f2 100644 --- a/data/shaders/caustics.frag +++ b/data/shaders/caustics.frag @@ -1,9 +1,4 @@ uniform sampler2D Albedo; -uniform sampler2D DiffuseMap; -uniform sampler2D SpecularMap; -uniform sampler2D SSAO; -uniform vec2 screen; -uniform vec3 ambient; uniform sampler2D caustictex; uniform vec2 dir; uniform vec2 dir2; @@ -16,17 +11,14 @@ varying vec2 uv; #define FragColor gl_FragColor #endif +vec3 getLightFactor(float specMapValue); + void main() { - vec2 tc = gl_FragCoord.xy / screen; - vec3 DiffuseComponent = texture(DiffuseMap, tc).xyz; - vec3 SpecularComponent = texture(SpecularMap, tc).xyz; vec4 color = texture(Albedo, uv); - float ao = texture(SSAO, tc).x; - float caustic = texture(caustictex, uv + dir).x; float caustic2 = texture(caustictex, (uv.yx + dir2 * vec2(-0.6, 0.3)) * vec2(0.6)).x; - vec3 LightFactor = ao * ambient + DiffuseComponent + SpecularComponent + caustic * caustic2 * 10; + vec3 LightFactor = getLightFactor(1.) + caustic * caustic2 * 10; FragColor = vec4(color.xyz * LightFactor, 1.); } diff --git a/data/shaders/color_levels.frag b/data/shaders/color_levels.frag index c99d7d4d8..d49c91219 100644 --- a/data/shaders/color_levels.frag +++ b/data/shaders/color_levels.frag @@ -12,6 +12,208 @@ varying vec2 uv; #define FragColor gl_FragColor #endif +#define PI 3.14159265 + +/* ---------------------------------------------------------------------------------------------- */ +// TEST +/* ---------------------------------------------------------------------------------------------- */ + +float width = 1920; //texture width +float height = 1080; //texture height + +vec2 texel = vec2(1.0/width,1.0/height); + +//------------------------------------------ +//user variables + +int samples = 3; //samples on the first ring +int rings = 5; //ring count + +bool autofocus = false; //use autofocus in shader? disable if you use external focalDepth value +float focalDepth = 0.1; +vec2 focus = vec2(0.5,0.5); // autofocus point on screen (0.0,0.0 - left lower corner, 1.0,1.0 - upper right) +float range = 150.0; //focal range +float maxblur = 1.25; //clamp value of max blur + +float threshold = 0.9; //highlight threshold; +float gain = 10.0; //highlight gain; + +float bias = 0.4; //bokeh edge bias +float fringe = 0.5; //bokeh chromatic aberration/fringing + +bool noise = true; //use noise instead of pattern for sample dithering +float namount = 0.0001; //dither amount + +bool depthblur = false; //blur the depth buffer? +float dbsize = 2.0; //depthblursize + + + +/* ---------------------------------------------------------------------------------------------- */ +// Function +/* ---------------------------------------------------------------------------------------------- */ + +vec3 color(vec2 coords,float blur) //processing the sample +{ + vec3 col = vec3(0.0); + + col.r = texture2D(tex,coords + vec2(0.0,1.0)*texel*fringe*blur).r; + col.g = texture2D(tex,coords + vec2(-0.866,-0.5)*texel*fringe*blur).g; + col.b = texture2D(tex,coords + vec2(0.866,-0.5)*texel*fringe*blur).b; + + vec3 lumcoeff = vec3(0.299,0.587,0.114); + float lum = dot(col.rgb, lumcoeff); + float thresh = max((lum-threshold)*gain, 0.0); + return col+mix(vec3(0.0),col,thresh*blur); +} + +vec2 rand(in vec2 coord) //generating noise/pattern texture for dithering +{ + float noiseX = ((fract(1.0-coord.s*(width/2.0))*0.25)+(fract(coord.t*(height/2.0))*0.75))*2.0-1.0; + float noiseY = ((fract(1.0-coord.s*(width/2.0))*0.75)+(fract(coord.t*(height/2.0))*0.25))*2.0-1.0; + + if (noise) + { + noiseX = clamp(fract(sin(dot(coord ,vec2(12.9898,78.233))) * 43758.5453),0.0,1.0)*2.0-1.0; + noiseY = clamp(fract(sin(dot(coord ,vec2(12.9898,78.233)*2.0)) * 43758.5453),0.0,1.0)*2.0-1.0; + } + return vec2(noiseX,noiseY); +} + + + + + + +void main() +{ + + //float depth = texture2D(dtex,uv).x; + + float curdepth = texture(dtex, uv).x; + vec4 FragPos = invprojm * (2.0f * vec4(uv, curdepth, 1.0f) - 1.0f); + FragPos /= FragPos.w; + + float depth = clamp((FragPos.z/180), 0., 1.); + + + float blur = 0.0; + + + blur = clamp((abs(depth - focalDepth)/range)*100.0,-maxblur,maxblur); + + vec2 noise = rand(uv)*namount*blur; + + float w = (1.0/width)*blur+noise.x; + float h = (1.0/height)*blur+noise.y; + + vec3 col = texture2D(tex, uv).rgb; + vec3 colDof = col; + float s = 1.0; + + int ringsamples; + + for (int i = 1; i <= rings; i += 1) + { + ringsamples = i * samples; + + for (int j = 0 ; j < ringsamples ; j += 1) + { + float step = PI*2.0 / float(ringsamples); + float pw = (cos(float(j)*step)*float(i)); + float ph = (sin(float(j)*step)*float(i)); + float p = 1.0; + + colDof += color(uv + vec2(pw*w,ph*h),blur)*mix(1.0,(float(i))/(float(rings)),bias)*p; + s += 1.0*mix(1.0,(float(i))/(float(rings)),bias)*p; + } + } + colDof /= s; + + // get color correction values + float inBlack = inlevel.x; + float inWhite = inlevel.z; + float inGamma = inlevel.y; + + float outBlack = outlevel.x; + float outWhite = outlevel.y; + + vec3 colOut = (pow(((col.rgb * 255.0) - inBlack) / (inWhite - inBlack), + vec3(1.0 / inGamma)) * (outWhite - outBlack) + outBlack) / 255.0; + + depth = (1 - depth); + vec3 final = colOut * depth + colDof.rgb * (1 - depth); + + vec2 inTex = uv - 0.5; + float vignette = 1 - dot(inTex, inTex); + + vignette = clamp(pow(vignette, 0.8), 0., 1.) ; + vignette = vignette + vignette - 0.5; + final.rgb *= clamp(vignette, 0., 1.15); + + FragColor.rgb = final; + FragColor.a = 1.0; +} + + + +/* +void main() +{ + vec2 texc = uv; + //texc.y = 1.0 - texc.y; + + + vec4 col = texture(tex, texc); + float curdepth = texture(dtex, uv).x; + + vec2 inTex = uv - 0.5; + float vignette = 1 - dot(inTex, inTex); + + vec4 FragPos = invprojm * (2.0f * vec4(uv, curdepth, 1.0f) - 1.0f); + FragPos /= FragPos.w; + + //col = col / (1 - col); + + float inBlack = inlevel.x; + float inWhite = inlevel.z; + float inGamma = inlevel.y; + + float outBlack = outlevel.x; + float outWhite = outlevel.y; + + float depth1 = clamp((FragPos.z/180), 0., 1.); + + vec3 colOut = (pow(((col.rgb * 255.0) - inBlack) / (inWhite - inBlack), + vec3(1.0 / inGamma)) * (outWhite - outBlack) + outBlack) / 255.0; + + depth1 = (1 - depth1); + vec3 final = colOut * depth1 + col.rgb * (1 - depth1); + + vignette = clamp(pow(vignette, 0.8), 0., 1.) ; + vignette = vignette + vignette - 0.5; + final.rgb *= clamp(vignette, 0., 1.15); + FragColor = vec4(final, 1.0); + + +}*/ + + + +/*uniform sampler2D tex; +uniform sampler2D dtex; +uniform vec3 inlevel; +uniform vec2 outlevel; +uniform mat4 invprojm; + +#if __VERSION__ >= 130 +in vec2 uv; +out vec4 FragColor; +#else +varying vec2 uv; +#define FragColor gl_FragColor +#endif + void main() @@ -21,14 +223,14 @@ void main() float curdepth = texture(dtex, uv).x; vec4 FragPos = invprojm * (2.0 * vec4(uv, curdepth, 1.0f) - 1.0f); FragPos /= FragPos.w; - float depth = clamp(FragPos.z / 180, 0, 1); + float depth = clamp(FragPos.z / 180, 0., 1.); depth = (1 - depth); // Compute the vignette vec2 inside = uv - 0.5; float vignette = 1 - dot(inside, inside); - vignette = clamp(pow(vignette, 0.8), 0, 1); - vignette = clamp(vignette + vignette - 0.5, 0, 1.15); + vignette = clamp(pow(vignette, 0.8), 0., 1.); + vignette = clamp(vignette + vignette - 0.5, 0., 1.15); float inBlack = inlevel.x; float inWhite = inlevel.z; @@ -44,4 +246,4 @@ void main() FragColor = vec4(colFinal * vignette, 1.0); //FragColor = vec4(vec3(depth), 1.0); -} +}*/ diff --git a/data/shaders/detailledobject_pass2.frag b/data/shaders/detailledobject_pass2.frag index c0c9527d0..6d9a94c04 100644 --- a/data/shaders/detailledobject_pass2.frag +++ b/data/shaders/detailledobject_pass2.frag @@ -1,10 +1,5 @@ uniform sampler2D Albedo; uniform sampler2D Detail; -uniform sampler2D DiffuseMap; -uniform sampler2D SpecularMap; -uniform sampler2D SSAO; -uniform vec2 screen; -uniform vec3 ambient; #if __VERSION__ >= 130 in vec2 uv; @@ -16,17 +11,13 @@ varying vec2 uv_bis; #define FragColor gl_FragColor #endif - +vec3 getLightFactor(float specMapValue); void main(void) { - vec2 tc = gl_FragCoord.xy / screen; vec4 color = texture(Albedo, uv); - vec4 detail = texture(Detail, uv_bis); - color *= detail; - vec3 DiffuseComponent = texture(DiffuseMap, tc).xyz; - vec3 SpecularComponent = texture(SpecularMap, tc).xyz; - float ao = texture(SSAO, tc).x; - vec3 LightFactor = ao * ambient + DiffuseComponent + SpecularComponent * (1. - color.a); - FragColor = vec4(color.xyz * LightFactor * ao, 1.); + vec4 detail = texture(Detail, uv_bis); + color *= detail; + vec3 LightFactor = getLightFactor(1. - color.a); + FragColor = vec4(color.xyz * LightFactor, 1.); } diff --git a/data/shaders/diffuseenvmap.frag b/data/shaders/diffuseenvmap.frag index eb2822598..0c9f70777 100644 --- a/data/shaders/diffuseenvmap.frag +++ b/data/shaders/diffuseenvmap.frag @@ -14,12 +14,7 @@ varying vec2 uv; #define Spec gl_FragData[1] #endif -vec3 DecodeNormal(vec2 n) -{ - float z = dot(n, n) * 2. - 1.; - vec2 xy = normalize(n) * sqrt(1. - z * z); - return vec3(xy,z); -} +vec3 DecodeNormal(vec2 n); mat4 getMatrix(float L[9]) { diff --git a/data/shaders/normalmap.frag b/data/shaders/normalmap.frag index b07c307eb..125bd3aaa 100644 --- a/data/shaders/normalmap.frag +++ b/data/shaders/normalmap.frag @@ -13,11 +13,8 @@ varying vec2 uv; #endif -// from Crytek "a bit more deferred CryEngine" -vec2 EncodeNormal(vec3 n) -{ - return normalize(n.xy) * sqrt(n.z * 0.5 + 0.5); -} + +vec2 EncodeNormal(vec3 n); void main() { diff --git a/data/shaders/object_pass1.frag b/data/shaders/object_pass1.frag index e119e2e0a..e699e1593 100644 --- a/data/shaders/object_pass1.frag +++ b/data/shaders/object_pass1.frag @@ -6,13 +6,7 @@ varying vec3 nor; #define EncodedNormal gl_FragColor.xy #endif - - -// from Crytek "a bit more deferred CryEngine" -vec2 EncodeNormal(vec3 n) -{ - return normalize(n.xy) * sqrt(n.z * 0.5 + 0.5); -} +vec2 EncodeNormal(vec3 n); void main(void) { diff --git a/data/shaders/object_pass2.frag b/data/shaders/object_pass2.frag index 426a79ec4..727975e99 100644 --- a/data/shaders/object_pass2.frag +++ b/data/shaders/object_pass2.frag @@ -1,9 +1,4 @@ uniform sampler2D Albedo; -uniform sampler2D DiffuseMap; -uniform sampler2D SpecularMap; -uniform sampler2D SSAO; -uniform vec2 screen; -uniform vec3 ambient; #if __VERSION__ >= 130 in vec2 uv; @@ -13,15 +8,11 @@ varying vec2 uv; #define FragColor gl_FragColor #endif +vec3 getLightFactor(float specMapValue); void main(void) { - vec2 tc = gl_FragCoord.xy / screen; vec4 color = texture(Albedo, uv); - vec3 DiffuseComponent = texture(DiffuseMap, tc).xyz; - vec3 SpecularComponent = texture(SpecularMap, tc).xyz; - float ao = texture(SSAO, tc).x; - vec3 LightFactor = ao * ambient + DiffuseComponent + SpecularComponent * (1. - color.a); - FragColor = vec4(color.xyz * LightFactor * (0.4 + ao*0.6), 1.); - //FragColor = vec4(color.xyz * LightFactor, 1.); + vec3 LightFactor = getLightFactor(1. - color.a); + FragColor = vec4(color.xyz * LightFactor, 1.); } diff --git a/data/shaders/objectref_pass1.frag b/data/shaders/objectref_pass1.frag index d322f9a5b..b32d97f1d 100644 --- a/data/shaders/objectref_pass1.frag +++ b/data/shaders/objectref_pass1.frag @@ -10,12 +10,7 @@ varying vec2 uv; #define EncodedNormal gl_FragColor.xy #endif - -// from Crytek "a bit more deferred CryEngine" -vec2 EncodeNormal(vec3 n) -{ - return normalize(n.xy) * sqrt(n.z * 0.5 + 0.5); -} +vec2 EncodeNormal(vec3 n); void main() { vec4 col = texture(tex, uv); diff --git a/data/shaders/objectref_pass2.frag b/data/shaders/objectref_pass2.frag index 823d405d4..0d5e8f304 100644 --- a/data/shaders/objectref_pass2.frag +++ b/data/shaders/objectref_pass2.frag @@ -1,9 +1,4 @@ uniform sampler2D Albedo; -uniform sampler2D DiffuseMap; -uniform sampler2D SpecularMap; -uniform sampler2D SSAO; -uniform vec2 screen; -uniform vec3 ambient; #if __VERSION__ >= 130 in vec2 uv; @@ -13,15 +8,12 @@ varying vec2 uv; #define FragColor gl_FragColor #endif +vec3 getLightFactor(float specMapValue); void main(void) { vec4 color = texture(Albedo, uv); if (color.a < 0.5) discard; - vec2 tc = gl_FragCoord.xy / screen; - vec3 DiffuseComponent = texture(DiffuseMap, tc).xyz; - vec3 SpecularComponent = texture(SpecularMap, tc).xyz; - float ao = texture(SSAO, tc).x; - vec3 LightFactor = ao * ambient + DiffuseComponent + SpecularComponent; + vec3 LightFactor = getLightFactor(1.); FragColor = vec4(color.xyz * LightFactor, 1.); } diff --git a/data/shaders/pass.frag b/data/shaders/pass.frag index e5b1e7c69..d721a3d7c 100644 --- a/data/shaders/pass.frag +++ b/data/shaders/pass.frag @@ -1,4 +1,4 @@ -#version 330 +#version 140 uniform sampler2D tex; in vec2 uv; diff --git a/data/shaders/pass.vert b/data/shaders/pass.vert index 4c495e0a7..3f3a70263 100644 --- a/data/shaders/pass.vert +++ b/data/shaders/pass.vert @@ -1,5 +1,5 @@ // Passthrough shader for drawQuad() -#version 330 +#version 140 in vec3 Position; in vec2 Texcoord; diff --git a/data/shaders/pointlight.frag b/data/shaders/pointlight.frag index 0c2c9a1cf..cba9e2e49 100644 --- a/data/shaders/pointlight.frag +++ b/data/shaders/pointlight.frag @@ -12,12 +12,7 @@ flat in vec3 col; out vec4 Diffuse; out vec4 Specular; -vec3 DecodeNormal(vec2 n) -{ - float z = dot(n, n) * 2. - 1.; - vec2 xy = normalize(n) * sqrt(1. - z * z); - return vec3(xy,z); -} +vec3 DecodeNormal(vec2 n); void main() { vec2 texc = gl_FragCoord.xy / screen; diff --git a/data/shaders/ssao.frag b/data/shaders/ssao.frag index dacabddbc..e38e75067 100644 --- a/data/shaders/ssao.frag +++ b/data/shaders/ssao.frag @@ -25,12 +25,7 @@ vec3 rand(vec2 co) return texture(noise_texture, co*20.16).xyz; } -vec3 DecodeNormal(vec2 n) -{ - float z = dot(n, n) * 2. - 1.; - vec2 xy = normalize(n) * sqrt(1. - z * z); - return vec3(xy,z); -} +vec3 DecodeNormal(vec2 n); void main(void) { diff --git a/data/shaders/sunlight.frag b/data/shaders/sunlight.frag index fccf941d2..0f56d1671 100644 --- a/data/shaders/sunlight.frag +++ b/data/shaders/sunlight.frag @@ -19,12 +19,7 @@ varying vec2 uv; #endif -vec3 DecodeNormal(vec2 n) -{ - float z = dot(n, n) * 2. - 1.; - vec2 xy = normalize(n) * sqrt(1. - z * z); - return vec3(xy,z); -} +vec3 DecodeNormal(vec2 n); void main() { float z = texture(dtex, uv).x; diff --git a/data/shaders/sunlightshadow.frag b/data/shaders/sunlightshadow.frag index 69ffafd41..dc2db7199 100644 --- a/data/shaders/sunlightshadow.frag +++ b/data/shaders/sunlightshadow.frag @@ -23,18 +23,13 @@ varying vec2 uv; #endif -vec3 DecodeNormal(vec2 n) -{ - float z = dot(n, n) * 2. - 1.; - vec2 xy = normalize(n) * sqrt(1. - z * z); - return vec3(xy,z); -} +vec3 DecodeNormal(vec2 n); float getShadowFactor(vec3 pos, float bias, int index) { //float a[5] = float[](3.4, 4.2, 5.0, 5.2, 1.1); - const vec2 shadowoffset[4] = vec2[]( + vec2 shadowoffset[4] = vec2[]( vec2(-1., -1.), vec2(-1., 1.), vec2(1., -1.), diff --git a/data/shaders/transparent.frag b/data/shaders/transparent.frag index 4de578afd..f6585ef9d 100644 --- a/data/shaders/transparent.frag +++ b/data/shaders/transparent.frag @@ -2,6 +2,7 @@ uniform sampler2D tex; #if __VERSION__ >= 130 in vec2 uv; +in vec4 color; out vec4 FragColor; #else varying vec2 uv; @@ -11,5 +12,5 @@ varying vec2 uv; void main() { - FragColor = texture(tex, uv); + FragColor = texture(tex, uv) * color; } diff --git a/data/shaders/transparent.vert b/data/shaders/transparent.vert index 9692fc549..e6c6b13fe 100644 --- a/data/shaders/transparent.vert +++ b/data/shaders/transparent.vert @@ -4,7 +4,9 @@ uniform mat4 TextureMatrix; #if __VERSION__ >= 130 in vec3 Position; in vec2 Texcoord; +in vec4 Color; out vec2 uv; +out vec4 color; #else attribute vec3 Position; attribute vec2 Texcoord; @@ -16,4 +18,5 @@ void main() { uv = (TextureMatrix * vec4(Texcoord, 1., 1.)).xy; gl_Position = ModelViewProjectionMatrix * vec4(Position, 1.); + color = Color; } diff --git a/data/shaders/transparentfog.frag b/data/shaders/transparentfog.frag index 9e0e9ad0f..b5084f5a5 100644 --- a/data/shaders/transparentfog.frag +++ b/data/shaders/transparentfog.frag @@ -11,6 +11,7 @@ uniform vec2 screen; #if __VERSION__ >= 130 in vec2 uv; +in vec4 color; out vec4 FragColor; #else varying vec2 uv; @@ -20,7 +21,7 @@ varying vec2 uv; void main() { - vec4 color = texture(tex, uv); + vec4 diffusecolor = texture(tex, uv) * color; vec3 tmp = vec3(gl_FragCoord.xy / screen, gl_FragCoord.z); tmp = 2. * tmp - 1.; @@ -33,5 +34,5 @@ void main() fog = min(fog, fogmax); - FragColor = vec4(vec4(col, 0.) * fog + color *(1. - fog)); + FragColor = vec4(vec4(col, 0.) * fog + diffusecolor *(1. - fog)); } diff --git a/data/shaders/untextured_object.frag b/data/shaders/untextured_object.frag index d4e7852ee..05e8b2cd2 100644 --- a/data/shaders/untextured_object.frag +++ b/data/shaders/untextured_object.frag @@ -1,8 +1,3 @@ -uniform sampler2D DiffuseMap; -uniform sampler2D SpecularMap; -uniform sampler2D SSAO; -uniform vec2 screen; -uniform vec3 ambient; #if __VERSION__ >= 130 in vec4 color; @@ -12,14 +7,10 @@ varying vec4 color; #define FragColor gl_FragColor #endif +vec3 getLightFactor(float specMapValue); void main(void) { - vec2 tc = gl_FragCoord.xy / screen; - vec3 DiffuseComponent = texture(DiffuseMap, tc).xyz; - vec3 SpecularComponent = texture(SpecularMap, tc).xyz; - float ao = texture(SSAO, tc).x; - vec3 LightFactor = ao * ambient + DiffuseComponent + SpecularComponent * (1. - color.a); - - FragColor = vec4(color.xyz * LightFactor * ao, 1.); + vec3 LightFactor = getLightFactor(1.); + FragColor = vec4(color.xyz * LightFactor, 1.); } diff --git a/data/shaders/utils/decodeNormal.frag b/data/shaders/utils/decodeNormal.frag new file mode 100644 index 000000000..d3601509c --- /dev/null +++ b/data/shaders/utils/decodeNormal.frag @@ -0,0 +1,7 @@ + +vec3 DecodeNormal(vec2 n) +{ + float z = dot(n, n) * 2. - 1.; + vec2 xy = normalize(n) * sqrt(1. - z * z); + return vec3(xy,z); +} \ No newline at end of file diff --git a/data/shaders/utils/encode_normal.frag b/data/shaders/utils/encode_normal.frag new file mode 100644 index 000000000..ea0323588 --- /dev/null +++ b/data/shaders/utils/encode_normal.frag @@ -0,0 +1,5 @@ +// from Crytek "a bit more deferred CryEngine" +vec2 EncodeNormal(vec3 n) +{ + return normalize(n.xy) * sqrt(n.z * 0.5 + 0.5); +} \ No newline at end of file diff --git a/data/shaders/utils/getLightFactor.frag b/data/shaders/utils/getLightFactor.frag new file mode 100644 index 000000000..9132b6bc3 --- /dev/null +++ b/data/shaders/utils/getLightFactor.frag @@ -0,0 +1,15 @@ +uniform sampler2D DiffuseMap; +uniform sampler2D SpecularMap; +uniform sampler2D SSAO; +uniform vec2 screen; +uniform vec3 ambient; + +vec3 getLightFactor(float specMapValue) +{ + vec2 tc = gl_FragCoord.xy / screen; + vec3 DiffuseComponent = texture(DiffuseMap, tc).xyz; + vec3 SpecularComponent = texture(SpecularMap, tc).xyz; + float ao = texture(SSAO, tc).x; + vec3 tmp = ao * ambient + DiffuseComponent + SpecularComponent * specMapValue; + return tmp * (0.4 + ao*0.6); +} \ No newline at end of file diff --git a/lib/irrlicht/CMakeLists.txt b/lib/irrlicht/CMakeLists.txt index 1fed7ea4b..e1200309d 100644 --- a/lib/irrlicht/CMakeLists.txt +++ b/lib/irrlicht/CMakeLists.txt @@ -1,13 +1,13 @@ # CMakeLists.txt for Irrlicht in STK -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/ - ${CMAKE_CURRENT_SOURCE_DIR}/source/Irrlicht/jpeglib - ${CMAKE_CURRENT_SOURCE_DIR}/source/Irrlicht/libpng - ${CMAKE_CURRENT_SOURCE_DIR}/source/Irrlicht/zlib - ${CMAKE_CURRENT_SOURCE_DIR}/source/Irrlicht/bzip2) +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include/" + "${CMAKE_CURRENT_SOURCE_DIR}/source/Irrlicht/jpeglib" + "${CMAKE_CURRENT_SOURCE_DIR}/source/Irrlicht/libpng" + "${CMAKE_CURRENT_SOURCE_DIR}/source/Irrlicht/zlib" + "${CMAKE_CURRENT_SOURCE_DIR}/source/Irrlicht/bzip2") if(APPLE) - include_directories(${CMAKE_CURRENT_SOURCE_DIR}/source/Irrlicht/MacOSX ${CMAKE_CURRENT_SOURCE_DIR}/source/Irrlicht) + include_directories("${CMAKE_CURRENT_SOURCE_DIR}/source/Irrlicht/MacOSX" "${CMAKE_CURRENT_SOURCE_DIR}/source/Irrlicht") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -arch i386") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -arch i386 -F/Library/Frameworks") endif() diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp index 4829cf72c..e9877a3e3 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp @@ -368,6 +368,65 @@ void IrrPrintXGrabError(int grabResult, const c8 * grabCommand ) } #endif +static GLXContext getMeAGLContext(Display *display, GLXFBConfig glxFBConfig) +{ + GLXContext Context; + int compat33ctx[] = + { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 3, + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB, + None + }; + int core33ctx[] = + { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 3, + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, + GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB, + None + }; + int core31ctx[] = + { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 1, + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, + GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB, + None + }; + int legacyctx[] = + { + GLX_CONTEXT_MAJOR_VERSION_ARB, 2, + GLX_CONTEXT_MINOR_VERSION_ARB, 1, + None + }; + PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = 0; + glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) + glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" ); + + // create compat 3.3 context (for proprietary drivers) + Context = glXCreateContextAttribsARB(display, glxFBConfig, 0, True, compat33ctx); + if (!XErrorSignaled) + return Context; + + XErrorSignaled = false; + // create core 3.3 context (for mesa) + Context = glXCreateContextAttribsARB(display, glxFBConfig, 0, True, core33ctx); + if (!XErrorSignaled) + return Context; + + XErrorSignaled = false; + // create core 3.1 context (for older mesa) + Context = glXCreateContextAttribsARB(display, glxFBConfig, 0, True, core31ctx); + if (!XErrorSignaled) + return Context; + + XErrorSignaled = false; + // fall back to legacy context + Context = glXCreateContextAttribsARB(display, glxFBConfig, 0, True, legacyctx); + return Context; +} bool CIrrDeviceLinux::createWindow() { @@ -741,30 +800,7 @@ bool CIrrDeviceLinux::createWindow() glxWin=glXCreateWindow(display,glxFBConfig,window,NULL); if (glxWin) { - int context_attribs[] = - { - GLX_CONTEXT_MAJOR_VERSION_ARB, 3, - GLX_CONTEXT_MINOR_VERSION_ARB, 3, - GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, //GLX_CONTEXT_CORE_PROFILE_BIT_ARB - GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB, - None - }; - PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = 0; - glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) - glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" ); - // create glx context - Context = glXCreateContextAttribsARB(display, glxFBConfig, 0, True, context_attribs); - if (XErrorSignaled) - { - int context_attribs[] = - { - GLX_CONTEXT_MAJOR_VERSION_ARB, 2, - GLX_CONTEXT_MINOR_VERSION_ARB, 1, - None - }; - Context = glXCreateContextAttribsARB(display, glxFBConfig, 0, True, context_attribs); - XErrorSignaled = false; - } + Context = getMeAGLContext(display, glxFBConfig); if (Context) { if (!glXMakeContextCurrent(display, glxWin, glxWin, Context)) diff --git a/lib/irrlicht/source/Irrlicht/COpenGLTexture.cpp b/lib/irrlicht/source/Irrlicht/COpenGLTexture.cpp index 0bc4b612a..20b5d834a 100644 --- a/lib/irrlicht/source/Irrlicht/COpenGLTexture.cpp +++ b/lib/irrlicht/source/Irrlicht/COpenGLTexture.cpp @@ -868,16 +868,9 @@ bool COpenGLFBODepthTexture::attach(ITexture* renderTex) #ifdef GL_EXT_framebuffer_object if (UseStencil) { - // attach stencil texture to stencil buffer + // attach depth stencil texture to depth buffer Driver->extGlFramebufferTexture2D(GL_FRAMEBUFFER_EXT, - GL_STENCIL_ATTACHMENT_EXT, - GL_TEXTURE_2D, - StencilRenderBuffer, - 0); - - // attach depth texture to depth buffer - Driver->extGlFramebufferTexture2D(GL_FRAMEBUFFER_EXT, - GL_DEPTH_ATTACHMENT_EXT, + GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, DepthRenderBuffer, 0); diff --git a/lib/wiiuse/CMakeLists.txt b/lib/wiiuse/CMakeLists.txt index 04e5c1e92..eabc0836e 100644 --- a/lib/wiiuse/CMakeLists.txt +++ b/lib/wiiuse/CMakeLists.txt @@ -2,6 +2,16 @@ cmake_minimum_required(VERSION 2.8.1) +# libbluetooth is required on Unix platforms +if(UNIX) + include(FindPkgConfig) + pkg_check_modules(BLUETOOTH bluez) + if(NOT BLUETOOTH_FOUND) + message(FATAL_ERROR "Bluetooth library not found. " + "Either install libbluetooth or disable wiiuse support with -DUSE_WIIUSE=0") + endif() +endif() + set(WIIUSE_SOURCES classic.c dynamics.c diff --git a/sources.cmake b/sources.cmake index b4f5841a7..01767ef22 100644 --- a/sources.cmake +++ b/sources.cmake @@ -228,6 +228,7 @@ src/states_screens/dialogs/addons_loading.cpp src/states_screens/dialogs/change_password_dialog.cpp src/states_screens/dialogs/confirm_resolution_dialog.cpp src/states_screens/dialogs/custom_video_settings.cpp +src/states_screens/dialogs/enter_gp_name_dialog.cpp src/states_screens/dialogs/enter_player_name_dialog.cpp src/states_screens/dialogs/gp_info_dialog.cpp src/states_screens/dialogs/message_dialog.cpp @@ -244,7 +245,10 @@ src/states_screens/dialogs/tutorial_message_dialog.cpp src/states_screens/dialogs/user_info_dialog.cpp src/states_screens/dialogs/vote_dialog.cpp src/states_screens/easter_egg_screen.cpp +src/states_screens/edit_gp_screen.cpp +src/states_screens/edit_track_screen.cpp src/states_screens/feature_unlocked.cpp +src/states_screens/grand_prix_editor_screen.cpp src/states_screens/grand_prix_lose.cpp src/states_screens/grand_prix_win.cpp src/states_screens/guest_login_screen.cpp @@ -565,6 +569,7 @@ src/states_screens/dialogs/addons_loading.hpp src/states_screens/dialogs/change_password_dialog.hpp src/states_screens/dialogs/confirm_resolution_dialog.hpp src/states_screens/dialogs/custom_video_settings.hpp +src/states_screens/dialogs/enter_gp_name_dialog.hpp src/states_screens/dialogs/enter_player_name_dialog.hpp src/states_screens/dialogs/gp_info_dialog.hpp src/states_screens/dialogs/message_dialog.hpp @@ -581,7 +586,10 @@ src/states_screens/dialogs/tutorial_message_dialog.hpp src/states_screens/dialogs/user_info_dialog.hpp src/states_screens/dialogs/vote_dialog.hpp src/states_screens/easter_egg_screen.hpp +src/states_screens/edit_gp_screen.hpp +src/states_screens/edit_track_screen.hpp src/states_screens/feature_unlocked.hpp +src/states_screens/grand_prix_editor_screen.hpp src/states_screens/grand_prix_lose.hpp src/states_screens/grand_prix_win.hpp src/states_screens/guest_login_screen.hpp diff --git a/src/achievements/achievement.cpp b/src/achievements/achievement.cpp index 8e07dd60b..ff85fc138 100644 --- a/src/achievements/achievement.cpp +++ b/src/achievements/achievement.cpp @@ -179,6 +179,15 @@ void Achievement::onRaceEnd() reset(); } // onRaceEnd +// ---------------------------------------------------------------------------- +/** Called at the end of a lap to potentially reset values. +*/ +void Achievement::onLapEnd() +{ + if (m_achievement_info->needsResetAfterLap()) + reset(); +} // onLapEnd + // ---------------------------------------------------------------------------- /** Checks if this achievement has been achieved. */ diff --git a/src/achievements/achievement.hpp b/src/achievements/achievement.hpp index ec3790aad..9f7239d98 100644 --- a/src/achievements/achievement.hpp +++ b/src/achievements/achievement.hpp @@ -69,7 +69,8 @@ public: virtual void reset(); virtual irr::core::stringw getProgressAsString() const; - void onRaceEnd(); + void onRaceEnd(); + void onLapEnd(); // ------------------------------------------------------------------------ /** Returns the id of this achievement. */ uint32_t getID() const { return m_id; } diff --git a/src/achievements/achievement_info.cpp b/src/achievements/achievement_info.cpp index 98602cd32..692bff4bd 100644 --- a/src/achievements/achievement_info.cpp +++ b/src/achievements/achievement_info.cpp @@ -31,7 +31,7 @@ */ AchievementInfo::AchievementInfo(const XMLNode * input) { - m_reset_after_race = false; + m_reset_type = NEVER; m_id = 0; m_title = ""; m_description = ""; @@ -47,10 +47,20 @@ AchievementInfo::AchievementInfo(const XMLNode * input) "ID %d title '%s' description '%s'", m_id, m_title.c_str(), m_description.c_str()); } - input->get("reset-after-race", &m_reset_after_race); - m_check_type = AC_ALL_AT_LEAST; + // Load the reset-type std::string s; + input->get("reset-type", &s); + if (s == "race") + m_reset_type = AFTER_RACE; + else if (s == "lap") + m_reset_type = AFTER_LAP; + else if (s != "never") + Log::warn("AchievementInfo", "Achievement check type '%s' unknown.", + s.c_str()); + + // Load check-type + m_check_type = AC_ALL_AT_LEAST; input->get("check-type", &s); if (s == "all-at-least") m_check_type = AC_ALL_AT_LEAST; diff --git a/src/achievements/achievement_info.hpp b/src/achievements/achievement_info.hpp index 5e52989bf..5aaa5c0f9 100644 --- a/src/achievements/achievement_info.hpp +++ b/src/achievements/achievement_info.hpp @@ -33,31 +33,45 @@ class Achievement; -/** This is the base class for storing the definition of an achievement, e.g. +/** This is the base class for storing the definition of an achievement, e.g. * title, description (which is common for all achievements), but also how * to achieve this achievement. - * \ingroup achievements - */ + * \ingroup achievements + */ class AchievementInfo { public: /** Some handy names for the various achievements. */ - enum { ACHIEVE_COLUMBUS = 1, - ACHIEVE_FIRST = ACHIEVE_COLUMBUS, - ACHIEVE_STRIKE = 2, - ACHIEVE_ARCH_ENEMY = 3, - ACHIEVE_MARATHONER = 4, - ACHIEVE_SKIDDING = 5 + enum { ACHIEVE_COLUMBUS = 1, + ACHIEVE_FIRST = ACHIEVE_COLUMBUS, + ACHIEVE_STRIKE = 2, + ACHIEVE_ARCH_ENEMY = 3, + ACHIEVE_MARATHONER = 4, + ACHIEVE_SKIDDING = 5, + ACHIEVE_GOLD_DRIVER = 6, + ACHIEVE_POWERUP_LOVER = 7, + ACHIEVE_UNSTOPPABLE = 8 }; - /** Achievement check type: + /** Achievement check type: * ALL_AT_LEAST: All goal values must be reached (or exceeded). * ONE_AT_LEAST: At least one current value reaches or exceedes the goal. */ - enum AchievementCheckType + enum AchievementCheckType { AC_ALL_AT_LEAST, AC_ONE_AT_LEAST }; + /** Achievement reset type: + * AFTER_LAP: Achievement needs to be reset after each lap. + * AFTER_RACE: Achievement needs to be reset after each race. + * NEVER: Achievement does not need to be reset + */ + enum ResetType + { + AFTER_LAP = 1, + AFTER_RACE = 2, + NEVER = 3 + }; private: /** The id of this Achievement. */ @@ -75,8 +89,8 @@ private: /** The target values needed to be reached. */ std::map m_goal_values; - /** True if the achievement needs to be reset after each race. */ - bool m_reset_after_race; + /** Determines when the achievement needs to be reset */ + ResetType m_reset_type; public: AchievementInfo(const XMLNode * input); @@ -96,7 +110,9 @@ public: /** Returns the title of this achievement. */ irr::core::stringw getTitle() const { return m_title; } // ------------------------------------------------------------------------ - bool needsResetAfterRace() const { return m_reset_after_race; } + bool needsResetAfterRace() const { return m_reset_type == AFTER_RACE; } + // ------------------------------------------------------------------------ + bool needsResetAfterLap() const { return m_reset_type == AFTER_LAP; } // ------------------------------------------------------------------------ /** Returns the check type for this achievement. */ AchievementCheckType getCheckType() const { return m_check_type; } diff --git a/src/achievements/achievements_manager.cpp b/src/achievements/achievements_manager.cpp index e13af174e..ec59095cf 100644 --- a/src/achievements/achievements_manager.cpp +++ b/src/achievements/achievements_manager.cpp @@ -95,14 +95,6 @@ AchievementsStatus* return status; } // createAchievementStatus -// ---------------------------------------------------------------------------- -void AchievementsManager::onRaceEnd() -{ - //reset all values that need to be reset - PlayerManager::get()->getCurrentPlayer() - ->getAchievementsStatus()->onRaceEnd(); -} // onRaceEnd - // ---------------------------------------------------------------------------- AchievementInfo * AchievementsManager::getAchievementInfo(uint32_t id) const { diff --git a/src/achievements/achievements_manager.hpp b/src/achievements/achievements_manager.hpp index 67cebf888..d138cb151 100644 --- a/src/achievements/achievements_manager.hpp +++ b/src/achievements/achievements_manager.hpp @@ -71,7 +71,6 @@ public: } // destroy // ======================================================================== - void onRaceEnd(); AchievementInfo* getAchievementInfo(uint32_t id) const; AchievementsStatus* createAchievementsStatus(const XMLNode *node=NULL); // ------------------------------------------------------------------------ diff --git a/src/achievements/achievements_status.cpp b/src/achievements/achievements_status.cpp index 40aa3d34d..3643b9d52 100644 --- a/src/achievements/achievements_status.cpp +++ b/src/achievements/achievements_status.cpp @@ -132,3 +132,12 @@ void AchievementsStatus::onRaceEnd() iter->second->onRaceEnd(); } } // onRaceEnd + +void AchievementsStatus::onLapEnd() +{ + //reset all values that need to be reset + std::map::iterator iter; + for (iter = m_achievements.begin(); iter != m_achievements.end(); ++iter) { + iter->second->onLapEnd(); + } +} // onLapEnd \ No newline at end of file diff --git a/src/achievements/achievements_status.hpp b/src/achievements/achievements_status.hpp index e956dd99b..7aa8edefa 100644 --- a/src/achievements/achievements_status.hpp +++ b/src/achievements/achievements_status.hpp @@ -60,6 +60,7 @@ public : void add(Achievement *achievement); void sync(const std::vector & achieved_ids); void onRaceEnd(); + void onLapEnd(); // ------------------------------------------------------------------------ const std::map& getAllAchievements() { diff --git a/src/audio/sfx_manager.cpp b/src/audio/sfx_manager.cpp index 30eb98d5b..af00cb056 100644 --- a/src/audio/sfx_manager.cpp +++ b/src/audio/sfx_manager.cpp @@ -314,16 +314,6 @@ SFXBase* SFXManager::createSoundSource(SFXBuffer* buffer, return sfx; } // createSoundSource -//---------------------------------------------------------------------------- - -void SFXManager::dump() -{ - for(int n=0; n<(int)m_all_sfx.size(); n++) - { - Log::debug("SFXManager", "Sound %i : %s \n", n, m_all_sfx[n]->getBuffer()->getFileName().c_str()); - } -} - //---------------------------------------------------------------------------- SFXBase* SFXManager::createSoundSource(const std::string &name, const bool addToSFXList) diff --git a/src/graphics/glwrap.cpp b/src/graphics/glwrap.cpp index fe3c9b164..ebcb238d2 100644 --- a/src/graphics/glwrap.cpp +++ b/src/graphics/glwrap.cpp @@ -65,8 +65,10 @@ PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus; static bool is_gl_init = false; #ifdef DEBUG +#ifdef WIN32 #define ARB_DEBUG_OUTPUT #endif +#endif #ifdef ARB_DEBUG_OUTPUT static void @@ -202,15 +204,17 @@ void initGL() #endif #endif #ifdef ARB_DEBUG_OUTPUT - glDebugMessageCallbackARB((GLDEBUGPROCARB)debugCallback, NULL); + glDebugMessageCallbackARB((GLDEBUGPROCARB)debugCallback, NULL); #endif } // Mostly from shader tutorial -static GLuint LoadShader(const char * file, unsigned type) +GLuint LoadShader(const char * file, unsigned type) { GLuint Id = glCreateShader(type); - std::string Code = "#version 330\n"; + char versionString[20]; + sprintf(versionString, "#version %d\n", irr_driver->getGLSLVersion()); + std::string Code = versionString; std::ifstream Stream(file, std::ios::in); if (Stream.is_open()) { @@ -230,10 +234,11 @@ static GLuint LoadShader(const char * file, unsigned type) glGetShaderiv(Id, GL_COMPILE_STATUS, &Result); if (Result == GL_FALSE) { + Log::error("GLWrap", "Error in shader %s", file); glGetShaderiv(Id, GL_INFO_LOG_LENGTH, &InfoLogLength); char *ErrorMessage = new char[InfoLogLength]; glGetShaderInfoLog(Id, InfoLogLength, NULL, ErrorMessage); - printf(ErrorMessage); + Log::error("GLWrap", ErrorMessage); delete[] ErrorMessage; } @@ -246,80 +251,10 @@ static GLuint LoadShader(const char * file, unsigned type) return Id; } -GLuint LoadProgram(const char * vertex_file_path, const char * fragment_file_path) -{ - GLuint VertexShaderID = LoadShader(vertex_file_path, GL_VERTEX_SHADER); - GLuint FragmentShaderID = LoadShader(fragment_file_path, GL_FRAGMENT_SHADER); - - GLuint ProgramID = glCreateProgram(); - glAttachShader(ProgramID, VertexShaderID); - glAttachShader(ProgramID, FragmentShaderID); - glLinkProgram(ProgramID); - - GLint Result = GL_FALSE; - int InfoLogLength; - glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result); - if (Result == GL_FALSE) { - glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength); - char *ErrorMessage = new char[InfoLogLength]; - glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, ErrorMessage); - printf(ErrorMessage); - delete[] ErrorMessage; - } - - glDeleteShader(VertexShaderID); - glDeleteShader(FragmentShaderID); - - GLenum glErr = glGetError(); - if (glErr != GL_NO_ERROR) - { - Log::warn("IrrDriver", "GLWrap : OpenGL error %i\n", glErr); - } - - return ProgramID; -} - -GLuint LoadProgram(const char * vertex_file_path, const char * geometry_file_path, const char * fragment_file_path) -{ - GLuint VertexShaderID = LoadShader(vertex_file_path, GL_VERTEX_SHADER); - GLuint FragmentShaderID = LoadShader(fragment_file_path, GL_FRAGMENT_SHADER); - GLuint GeometryShaderID = LoadShader(geometry_file_path, GL_GEOMETRY_SHADER); - - GLuint ProgramID = glCreateProgram(); - glAttachShader(ProgramID, VertexShaderID); - glAttachShader(ProgramID, GeometryShaderID); - glAttachShader(ProgramID, FragmentShaderID); - glLinkProgram(ProgramID); - - GLint Result = GL_FALSE; - int InfoLogLength; - glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result); - if (Result == GL_FALSE) { - glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength); - char *ErrorMessage = new char[InfoLogLength]; - glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, ErrorMessage); - printf(ErrorMessage); - delete[] ErrorMessage; - } - - glDeleteShader(VertexShaderID); - glDeleteShader(GeometryShaderID); - glDeleteShader(FragmentShaderID); - - GLenum glErr = glGetError(); - if (glErr != GL_NO_ERROR) - { - Log::warn("IrrDriver", "GLWrap : OpenGL error %i\n", glErr); - } - - return ProgramID; -} - GLuint LoadTFBProgram(const char * vertex_file_path, const char **varyings, unsigned varyingscount) { - GLuint Shader = LoadShader(vertex_file_path, GL_VERTEX_SHADER); GLuint Program = glCreateProgram(); - glAttachShader(Program, Shader); + loadAndAttach(Program, GL_VERTEX_SHADER, vertex_file_path); glTransformFeedbackVaryings(Program, varyingscount, varyings, GL_INTERLEAVED_ATTRIBS); glLinkProgram(Program); @@ -334,7 +269,6 @@ GLuint LoadTFBProgram(const char * vertex_file_path, const char **varyings, unsi printf(ErrorMessage); delete[] ErrorMessage; } - glDeleteShader(Shader); GLenum glErr = glGetError(); if (glErr != GL_NO_ERROR) @@ -428,6 +362,16 @@ void drawTexQuad(const video::ITexture *texture, float width, float height, } } +void draw2DImage(const video::ITexture* texture, const core::rect& destRect, + const core::rect& sourceRect, const core::rect* clipRect, + const video::SColor &colors, bool useAlphaChannelOfTexture) +{ + video::SColor duplicatedArray[4] = { + colors, colors, colors, colors + }; + draw2DImage(texture, destRect, sourceRect, clipRect, duplicatedArray, useAlphaChannelOfTexture); +} + void draw2DImage(const video::ITexture* texture, const core::rect& destRect, const core::rect& sourceRect, const core::rect* clipRect, const video::SColor* const colors, bool useAlphaChannelOfTexture) diff --git a/src/graphics/glwrap.hpp b/src/graphics/glwrap.hpp index 2809c8bec..c26489f14 100644 --- a/src/graphics/glwrap.hpp +++ b/src/graphics/glwrap.hpp @@ -18,11 +18,7 @@ # include #endif -void initGL(); -GLuint LoadProgram(const char * vertex_file_path, const char * fragment_file_path); -GLuint LoadProgram(const char * vertex_file_path, const char * geometry_file_path, const char * fragment_file_path); -GLuint LoadTFBProgram(const char * vertex_file_path, const char **varyings, unsigned varyingscount); -void setTexture(unsigned TextureUnit, GLuint TextureId, GLenum MagFilter, GLenum MinFilter, bool allowAF = false); +#include "utils/log.hpp" // already includes glext.h, which defines useful GL constants. // COpenGLDriver has already loaded the extension GL functions we use (e.g glBeginQuery) @@ -87,6 +83,53 @@ extern PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackARB; #endif +void initGL(); +GLuint LoadTFBProgram(const char * vertex_file_path, const char **varyings, unsigned varyingscount); +void setTexture(unsigned TextureUnit, GLuint TextureId, GLenum MagFilter, GLenum MinFilter, bool allowAF = false); +GLuint LoadShader(const char * file, unsigned type); + +template +void loadAndAttach(GLint ProgramID) +{ + return; +} + +template +void loadAndAttach(GLint ProgramID, GLint ShaderType, const char *filepath, Types ... args) +{ + GLint ShaderID = LoadShader(filepath, ShaderType); + glAttachShader(ProgramID, ShaderID); + glDeleteShader(ShaderID); + loadAndAttach(ProgramID, args...); +} + +template +GLint LoadProgram(Types ... args) +{ + GLint ProgramID = glCreateProgram(); + loadAndAttach(ProgramID, args...); + glLinkProgram(ProgramID); + + GLint Result = GL_FALSE; + int InfoLogLength; + glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result); + if (Result == GL_FALSE) { + glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength); + char *ErrorMessage = new char[InfoLogLength]; + glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, ErrorMessage); + printf(ErrorMessage); + delete[] ErrorMessage; + } + + GLenum glErr = glGetError(); + if (glErr != GL_NO_ERROR) + { + Log::warn("IrrDriver", "GLWrap : OpenGL error %i\n", glErr); + } + + return ProgramID; +} + // core::rect needs these includes #include #include "utils/vec3.hpp" @@ -94,6 +137,10 @@ extern PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackARB; GLuint getTextureGLuint(irr::video::ITexture *tex); GLuint getDepthTexture(irr::video::ITexture *tex); +void draw2DImage(const irr::video::ITexture* texture, const irr::core::rect& destRect, + const irr::core::rect& sourceRect, const irr::core::rect* clipRect, + const irr::video::SColor &color, bool useAlphaChannelOfTexture); + void draw2DImage(const irr::video::ITexture* texture, const irr::core::rect& destRect, const irr::core::rect& sourceRect, const irr::core::rect* clipRect, const irr::video::SColor* const colors, bool useAlphaChannelOfTexture); diff --git a/src/graphics/gpuparticles.cpp b/src/graphics/gpuparticles.cpp index ed02ae7e3..d8cf1947a 100644 --- a/src/graphics/gpuparticles.cpp +++ b/src/graphics/gpuparticles.cpp @@ -47,10 +47,6 @@ ParticleSystemProxy::ParticleSystemProxy(bool createDefaultEmitter, heighmapbuffer = 0; heightmaptexture = 0; current_simulation_vao = 0; - non_currenthm__simulation_vao = 0; - current_hm_simulation_vao = 0; - current_rendering_flip_vao = 0; - non_current_rendering_flip_vao = 0; has_height_map = false; flip = false; track_x = 0; @@ -77,41 +73,17 @@ ParticleSystemProxy::~ParticleSystemProxy() { glDeleteBuffers(2, tfb_buffers); glDeleteBuffers(1, &initial_values_buffer); - glDeleteBuffers(1, &quaternionsbuffer); - if (has_height_map) - { + if (quaternionsbuffer) + glDeleteBuffers(1, &quaternionsbuffer); + if (heighmapbuffer) glDeleteBuffers(1, &heighmapbuffer); + if (heightmaptexture) glDeleteTextures(1, &heightmaptexture); - } - } void ParticleSystemProxy::setFlip() { flip = true; - float *quaternions = new float[4 * count]; - for (unsigned i = 0; i < count; i++) - { - core::vector3df rotationdir(0., 1., 0.); - - quaternions[4 * i] = rotationdir.X; - quaternions[4 * i + 1] = rotationdir.Y; - quaternions[4 * i + 2] = rotationdir.Z; - quaternions[4 * i + 3] = 3.14f * 3.f * (2.f * os::Randomizer::frand() - 1.f); // 3 half rotation during lifetime at max - } - glGenBuffers(1, &quaternionsbuffer); - glBindBuffer(GL_ARRAY_BUFFER, quaternionsbuffer); - glBufferData(GL_ARRAY_BUFFER, 4 * count * sizeof(float), quaternions, GL_STATIC_DRAW); - - glGenVertexArrays(1, ¤t_rendering_flip_vao); - glBindVertexArray(current_rendering_flip_vao); - FlipParticleVAOBind(tfb_buffers[0], quaternionsbuffer); - glGenVertexArrays(1, &non_current_rendering_flip_vao); - glBindVertexArray(non_current_rendering_flip_vao); - FlipParticleVAOBind(tfb_buffers[1], quaternionsbuffer); - glBindVertexArray(0); - - delete[] quaternions; } void ParticleSystemProxy::setHeightmap(const std::vector > &hm, @@ -138,16 +110,6 @@ void ParticleSystemProxy::setHeightmap(const std::vector > &h glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, heighmapbuffer); glBindBuffer(GL_TEXTURE_BUFFER, 0); - glGenVertexArrays(1, ¤t_hm_simulation_vao); - glBindVertexArray(current_hm_simulation_vao); - HeightmapSimulationBind(tfb_buffers[0], initial_values_buffer); - - glGenVertexArrays(1, &non_currenthm__simulation_vao); - glBindVertexArray(non_currenthm__simulation_vao); - HeightmapSimulationBind(tfb_buffers[1], initial_values_buffer); - - glBindVertexArray(0); - delete[] hm_array; } @@ -419,21 +381,6 @@ void ParticleSystemProxy::setEmitter(scene::IParticleEmitter* emitter) } glBindBuffer(GL_ARRAY_BUFFER, 0); - - glBindVertexArray(current_rendering_vao); - SimpleParticleVAOBind(tfb_buffers[0]); - - glBindVertexArray(non_current_rendering_vao); - SimpleParticleVAOBind(tfb_buffers[1]); - - glGenVertexArrays(1, ¤t_simulation_vao); - glBindVertexArray(current_simulation_vao); - SimpleSimulationBind(tfb_buffers[0], initial_values_buffer); - - glGenVertexArrays(1, &non_current_simulation_vao); - glBindVertexArray(non_current_simulation_vao); - SimpleSimulationBind(tfb_buffers[1], initial_values_buffer); - glBindVertexArray(0); texture = getTextureGLuint(getMaterial(0).getTexture(0)); @@ -459,7 +406,7 @@ void ParticleSystemProxy::simulateHeightmap() glUniform1f(ParticleShader::HeightmapSimulationShader::uniform_track_x_len, track_x_len); glUniform1f(ParticleShader::HeightmapSimulationShader::uniform_track_z_len, track_z_len); - glBindVertexArray(current_hm_simulation_vao); + glBindVertexArray(current_simulation_vao); glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tfb_buffers[1]); glBeginTransformFeedback(GL_POINTS); @@ -469,8 +416,8 @@ void ParticleSystemProxy::simulateHeightmap() glDisable(GL_RASTERIZER_DISCARD); std::swap(tfb_buffers[0], tfb_buffers[1]); - std::swap(current_rendering_flip_vao, non_current_rendering_flip_vao); - std::swap(current_hm_simulation_vao, non_currenthm__simulation_vao); + std::swap(current_rendering_vao, non_current_rendering_vao); + std::swap(current_simulation_vao, non_current_simulation_vao); } void ParticleSystemProxy::simulateNoHeightmap() @@ -523,7 +470,7 @@ void ParticleSystemProxy::drawFlip() ParticleShader::FlipParticleRender::setUniforms(irr_driver->getViewMatrix(), irr_driver->getProjMatrix(), irr_driver->getInvProjMatrix(), screen[0], screen[1], 0, 1); - glBindVertexArray(current_rendering_flip_vao); + glBindVertexArray(current_rendering_vao); glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, count); } @@ -558,12 +505,66 @@ void ParticleSystemProxy::draw() drawNotFlip(); } +void ParticleSystemProxy::generateVAOs() +{ + glGenVertexArrays(1, ¤t_rendering_vao); + glGenVertexArrays(1, &non_current_rendering_vao); + glGenVertexArrays(1, ¤t_simulation_vao); + glGenVertexArrays(1, &non_current_simulation_vao); + + glBindVertexArray(current_simulation_vao); + if (has_height_map) + HeightmapSimulationBind(tfb_buffers[0], initial_values_buffer); + else + SimpleSimulationBind(tfb_buffers[0], initial_values_buffer); + glBindVertexArray(non_current_simulation_vao); + if (has_height_map) + HeightmapSimulationBind(tfb_buffers[1], initial_values_buffer); + else + SimpleSimulationBind(tfb_buffers[1], initial_values_buffer); + + float *quaternions = new float[4 * count]; + glBindVertexArray(0); + if (flip) + { + for (unsigned i = 0; i < count; i++) + { + core::vector3df rotationdir(0., 1., 0.); + + quaternions[4 * i] = rotationdir.X; + quaternions[4 * i + 1] = rotationdir.Y; + quaternions[4 * i + 2] = rotationdir.Z; + quaternions[4 * i + 3] = 3.14f * 3.f * (2.f * os::Randomizer::frand() - 1.f); // 3 half rotation during lifetime at max + } + glGenBuffers(1, &quaternionsbuffer); + glBindBuffer(GL_ARRAY_BUFFER, quaternionsbuffer); + glBufferData(GL_ARRAY_BUFFER, 4 * count * sizeof(float), quaternions, GL_STATIC_DRAW); + } + + glBindVertexArray(current_rendering_vao); + if (flip) + FlipParticleVAOBind(tfb_buffers[0], quaternionsbuffer); + else + SimpleParticleVAOBind(tfb_buffers[0]); + + glBindVertexArray(non_current_rendering_vao); + if (flip) + FlipParticleVAOBind(tfb_buffers[1], quaternionsbuffer); + else + SimpleParticleVAOBind(tfb_buffers[1]); + glBindVertexArray(0); + + delete[] quaternions; +} + void ParticleSystemProxy::render() { if (!getEmitter() || !isGPUParticleType(getEmitter()->getType())) { CParticleSystemSceneNode::render(); return; } + if (!current_rendering_vao || !non_current_rendering_vao || !current_simulation_vao || !non_current_simulation_vao) + generateVAOs(); simulate(); draw(); } diff --git a/src/graphics/gpuparticles.hpp b/src/graphics/gpuparticles.hpp index 01040f629..d60724b71 100644 --- a/src/graphics/gpuparticles.hpp +++ b/src/graphics/gpuparticles.hpp @@ -14,9 +14,7 @@ class ParticleSystemProxy : public scene::CParticleSystemSceneNode protected: GLuint tfb_buffers[2], initial_values_buffer, heighmapbuffer, heightmaptexture, quaternionsbuffer; GLuint current_simulation_vao, non_current_simulation_vao; - GLuint current_hm_simulation_vao, non_currenthm__simulation_vao; GLuint current_rendering_vao, non_current_rendering_vao; - GLuint current_rendering_flip_vao, non_current_rendering_flip_vao; bool m_alpha_additive, has_height_map, flip; float size_increase_factor, track_x, track_z, track_x_len, track_z_len; float m_color_from[3]; @@ -31,6 +29,8 @@ protected: static void SimpleSimulationBind(GLuint PositionBuffer, GLuint InitialValuesBuffer); static void HeightmapSimulationBind(GLuint PositionBuffer, GLuint InitialValuesBuffer); + void generateVAOs(); + void simulateHeightmap(); void simulateNoHeightmap(); void drawFlip(); diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 97c104339..60cf5e9fb 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -422,12 +422,12 @@ void IrrDriver::initDevice() m_gui_env = m_device->getGUIEnvironment(); m_video_driver = m_device->getVideoDriver(); - int GLMajorVersion = 0, GLMinorVersion = 0; + GLMajorVersion = 2; + GLMinorVersion = 1; glGetIntegerv(GL_MAJOR_VERSION, &GLMajorVersion); glGetIntegerv(GL_MINOR_VERSION, &GLMinorVersion); Log::info("IrrDriver", "OPENGL VERSION IS %d.%d", GLMajorVersion, GLMinorVersion); - m_glsl = (GLMajorVersion > 3 || (GLMajorVersion == 3 && GLMinorVersion == 3)) && UserConfigParams::m_pixel_shaders; - + m_glsl = (GLMajorVersion > 3 || (GLMajorVersion == 3 && GLMinorVersion >= 1)) && UserConfigParams::m_pixel_shaders; // This remaps the window, so it has to be done before the clear to avoid flicker m_device->setResizable(false); diff --git a/src/graphics/irr_driver.hpp b/src/graphics/irr_driver.hpp index 9155451bf..08620da7f 100644 --- a/src/graphics/irr_driver.hpp +++ b/src/graphics/irr_driver.hpp @@ -82,6 +82,7 @@ enum STKRenderingPass class IrrDriver : public IEventReceiver, public NoCopy { private: + int GLMajorVersion, GLMinorVersion; /** The irrlicht device. */ IrrlichtDevice *m_device; /** Irrlicht scene manager. */ @@ -148,6 +149,16 @@ public: float power; }; + unsigned getGLSLVersion() const + { + if (GLMajorVersion > 3 || (GLMajorVersion == 3 && GLMinorVersion == 3)) + return GLMajorVersion * 100 + GLMinorVersion * 10; + else if (GLMajorVersion == 3) + return 100 + (GLMinorVersion + 3) * 10; + else + return 120; + } + private: std::vector m_modes; diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp index fcc8fec3d..4c8622329 100644 --- a/src/graphics/render.cpp +++ b/src/graphics/render.cpp @@ -755,50 +755,11 @@ void IrrDriver::renderGlow(video::SOverrideMaterial &overridemat, } // ---------------------------------------------------------------------------- -#define MAXLIGHT 16 // to be adjusted in pointlight.frag too +#define MAX2(a, b) ((a) > (b) ? (a) : (b)) +#define MIN2(a, b) ((a) > (b) ? (b) : (a)) +static LightShader::PointLightInfo PointLightsInfo[MAXLIGHT]; - -static GLuint pointlightvbo = 0; -static GLuint pointlightsvao = 0; - -struct PointLightInfo -{ - float posX; - float posY; - float posZ; - float energy; - float red; - float green; - float blue; - float padding; -}; - -void createPointLightVAO() -{ - glGenVertexArrays(1, &pointlightsvao); - glBindVertexArray(pointlightsvao); - - glBindBuffer(GL_ARRAY_BUFFER, SharedObject::billboardvbo); - glEnableVertexAttribArray(MeshShader::PointLightShader::attrib_Corner); - glVertexAttribPointer(MeshShader::PointLightShader::attrib_Corner, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0); - - glGenBuffers(1, &pointlightvbo); - glBindBuffer(GL_ARRAY_BUFFER, pointlightvbo); - glBufferData(GL_ARRAY_BUFFER, MAXLIGHT * sizeof(PointLightInfo), 0, GL_DYNAMIC_DRAW); - - glEnableVertexAttribArray(MeshShader::PointLightShader::attrib_Position); - glVertexAttribPointer(MeshShader::PointLightShader::attrib_Position, 3, GL_FLOAT, GL_FALSE, sizeof(PointLightInfo), 0); - glEnableVertexAttribArray(MeshShader::PointLightShader::attrib_Energy); - glVertexAttribPointer(MeshShader::PointLightShader::attrib_Energy, 1, GL_FLOAT, GL_FALSE, sizeof(PointLightInfo), (GLvoid*)(3 * sizeof(float))); - glEnableVertexAttribArray(MeshShader::PointLightShader::attrib_Color); - glVertexAttribPointer(MeshShader::PointLightShader::attrib_Color, 3, GL_FLOAT, GL_FALSE, sizeof(PointLightInfo), (GLvoid*)(4 * sizeof(float))); - - glVertexAttribDivisor(MeshShader::PointLightShader::attrib_Position, 1); - glVertexAttribDivisor(MeshShader::PointLightShader::attrib_Energy, 1); - glVertexAttribDivisor(MeshShader::PointLightShader::attrib_Color, 1); -} - -static void renderPointLights() +static void renderPointLights(unsigned count) { glEnable(GL_BLEND); glBlendEquation(GL_FUNC_ADD); @@ -806,23 +767,18 @@ static void renderPointLights() glEnable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); - glUseProgram(MeshShader::PointLightShader::Program); - glBindVertexArray(pointlightsvao); + glUseProgram(LightShader::PointLightShader::Program); + glBindVertexArray(LightShader::PointLightShader::vao); + glBindBuffer(GL_ARRAY_BUFFER, LightShader::PointLightShader::vbo); + glBufferSubData(GL_ARRAY_BUFFER, 0, count * sizeof(LightShader::PointLightInfo), PointLightsInfo); setTexture(0, getTextureGLuint(irr_driver->getRTT(RTT_NORMAL_AND_DEPTH)), GL_NEAREST, GL_NEAREST); setTexture(1, getDepthTexture(irr_driver->getRTT(RTT_NORMAL_AND_DEPTH)), GL_NEAREST, GL_NEAREST); - MeshShader::PointLightShader::setUniforms(irr_driver->getViewMatrix(), irr_driver->getProjMatrix(), irr_driver->getInvProjMatrix(), core::vector2df(UserConfigParams::m_width, UserConfigParams::m_height), 200, 0, 1); + LightShader::PointLightShader::setUniforms(irr_driver->getViewMatrix(), irr_driver->getProjMatrix(), irr_driver->getInvProjMatrix(), core::vector2df(UserConfigParams::m_width, UserConfigParams::m_height), 200, 0, 1); - glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, MAXLIGHT); - glBindVertexArray(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glEnable(GL_DEPTH_TEST); - glDisable(GL_BLEND); + glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, count); } -PointLightInfo PointLightsInfo[MAXLIGHT]; - void IrrDriver::renderLights(const core::aabbox3df& cambox, scene::ICameraSceneNode * const camnode, video::SOverrideMaterial &overridemat, @@ -860,7 +816,7 @@ void IrrDriver::renderLights(const core::aabbox3df& cambox, const core::vector3df &lightpos = (m_lights[i]->getAbsolutePosition() - campos); unsigned idx = (unsigned)(lightpos.getLength() / 10); if (idx > 14) - continue; + idx = 14; BucketedLN[idx].push_back(m_lights[i]); } @@ -907,17 +863,7 @@ void IrrDriver::renderLights(const core::aabbox3df& cambox, lightnum++; - // Fill lights - for (; lightnum < MAXLIGHT; lightnum++) { - PointLightsInfo[lightnum].energy = 0; - } - - if (!pointlightsvao) - createPointLightVAO(); - glBindVertexArray(pointlightsvao); - glBindBuffer(GL_ARRAY_BUFFER, pointlightvbo); - glBufferSubData(GL_ARRAY_BUFFER, 0, MAXLIGHT * sizeof(PointLightInfo), PointLightsInfo); - renderPointLights(); + renderPointLights(MIN2(lightnum, MAXLIGHT)); if (SkyboxCubeMap) m_post_processing->renderDiffuseEnvMap(blueSHCoeff, greenSHCoeff, redSHCoeff); // Handle SSAO @@ -936,77 +882,6 @@ void IrrDriver::renderLights(const core::aabbox3df& cambox, m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_COLOR), false, false); } - -static GLuint cubevao = 0; -static GLuint cubevbo; -static GLuint cubeidx; - -static void createcubevao() -{ - // From CSkyBoxSceneNode - float corners[] = - { - // top side - 1., 1., -1., - 1., 1., 1., - -1., 1., 1., - -1., 1., -1., - - // Bottom side - 1., -1., 1., - 1., -1., -1., - -1., -1., -1., - -1., -1., 1., - - // right side - 1., -1, -1, - 1., -1, 1, - 1., 1., 1., - 1., 1., -1., - - // left side - -1., -1., 1., - -1., -1., -1., - -1., 1., -1., - -1., 1., 1., - - // back side - -1., -1., -1., - 1., -1, -1., - 1, 1, -1., - -1, 1, -1., - - // front side - 1., -1., 1., - -1., -1., 1., - -1, 1., 1., - 1., 1., 1., - }; - int indices[] = { - 0, 1, 2, 2, 3, 0, - 4, 5, 6, 6, 7, 4, - 8, 9, 10, 10, 11, 8, - 12, 13, 14, 14, 15, 12, - 16, 17, 18, 18, 19, 16, - 20, 21, 22, 22, 23, 20 - }; - - glGenBuffers(1, &cubevbo); - glGenBuffers(1, &cubeidx); - glGenVertexArrays(1, &cubevao); - - glBindVertexArray(cubevao); - glBindBuffer(GL_ARRAY_BUFFER, cubevbo); - glBufferData(GL_ARRAY_BUFFER, 6 * 4 * 3 * sizeof(float), corners, GL_STATIC_DRAW); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cubeidx); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * 6 * sizeof(int), indices, GL_STATIC_DRAW); -} - -#define MAX2(a, b) ((a) > (b) ? (a) : (b)) -#define MIN2(a, b) ((a) > (b) ? (b) : (a)) - static void getXYZ(GLenum face, float i, float j, float &x, float &y, float &z) { switch (face) @@ -1399,11 +1274,9 @@ void IrrDriver::renderSkybox() if (SkyboxTextures.empty()) return; scene::ICameraSceneNode *camera = m_scene_manager->getActiveCamera(); - if (!cubevao) - createcubevao(); if (!SkyboxCubeMap) generateSkyboxCubemap(); - glBindVertexArray(cubevao); + glBindVertexArray(MeshShader::SkyboxShader::cubevao); glDisable(GL_CULL_FACE); assert(SkyboxTextures.size() == 6); core::matrix4 transform = irr_driver->getProjViewMatrix(); diff --git a/src/graphics/rtts.cpp b/src/graphics/rtts.cpp index d5927c483..c8ef946cd 100644 --- a/src/graphics/rtts.cpp +++ b/src/graphics/rtts.cpp @@ -137,6 +137,8 @@ RTT::RTT() drv->setRenderTarget(0, false, false); drv->endScene(); + if (irr_driver->getGLSLVersion() < 150) + return; glGenFramebuffers(1, &shadowFBO); glBindFramebuffer(GL_FRAMEBUFFER, shadowFBO); glGenTextures(1, &shadowColorTex); diff --git a/src/graphics/shaders.cpp b/src/graphics/shaders.cpp index 2458d385e..6199699d6 100644 --- a/src/graphics/shaders.cpp +++ b/src/graphics/shaders.cpp @@ -105,6 +105,68 @@ static void initBillboardVBO() glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), quad, GL_STATIC_DRAW); } +GLuint SharedObject::cubevbo = 0; +GLuint SharedObject::cubeindexes = 0; + +static void initCubeVBO() +{ + // From CSkyBoxSceneNode + float corners[] = + { + // top side + 1., 1., -1., + 1., 1., 1., + -1., 1., 1., + -1., 1., -1., + + // Bottom side + 1., -1., 1., + 1., -1., -1., + -1., -1., -1., + -1., -1., 1., + + // right side + 1., -1, -1, + 1., -1, 1, + 1., 1., 1., + 1., 1., -1., + + // left side + -1., -1., 1., + -1., -1., -1., + -1., 1., -1., + -1., 1., 1., + + // back side + -1., -1., -1., + 1., -1, -1., + 1, 1, -1., + -1, 1, -1., + + // front side + 1., -1., 1., + -1., -1., 1., + -1, 1., 1., + 1., 1., 1., + }; + int indices[] = { + 0, 1, 2, 2, 3, 0, + 4, 5, 6, 6, 7, 4, + 8, 9, 10, 10, 11, 8, + 12, 13, 14, 14, 15, 12, + 16, 17, 18, 18, 19, 16, + 20, 21, 22, 22, 23, 20 + }; + + glGenBuffers(1, &SharedObject::cubevbo); + glBindBuffer(GL_ARRAY_BUFFER, SharedObject::cubevbo); + glBufferData(GL_ARRAY_BUFFER, 6 * 4 * 3 * sizeof(float), corners, GL_STATIC_DRAW); + + glGenBuffers(1, &SharedObject::cubeindexes); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedObject::cubeindexes); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * 6 * sizeof(int), indices, GL_STATIC_DRAW); +} + void Shaders::loadShaders() { const std::string &dir = file_manager->getAsset(FileManager::SHADER, ""); @@ -232,6 +294,7 @@ void Shaders::loadShaders() initQuadVBO(); initQuadBuffer(); initBillboardVBO(); + initCubeVBO(); FullScreenShader::BloomBlendShader::init(); FullScreenShader::BloomShader::init(); FullScreenShader::ColorLevelShader::init(); @@ -272,7 +335,7 @@ void Shaders::loadShaders() MeshShader::TransparentShader::init(); MeshShader::TransparentFogShader::init(); MeshShader::BillboardShader::init(); - MeshShader::PointLightShader::init(); + LightShader::PointLightShader::init(); MeshShader::DisplaceShader::init(); MeshShader::DisplaceMaskShader::init(); MeshShader::ShadowShader::init(); @@ -326,7 +389,10 @@ namespace MeshShader void ObjectPass1Shader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/object_pass1.vert").c_str(), file_manager->getAsset("shaders/object_pass1.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/object_pass1.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/encode_normal.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/object_pass1.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); attrib_normal = glGetAttribLocation(Program, "Normal"); uniform_MVP = glGetUniformLocation(Program, "ModelViewProjectionMatrix"); @@ -350,7 +416,10 @@ namespace MeshShader void ObjectRefPass1Shader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/objectref_pass1.vert").c_str(), file_manager->getAsset("shaders/objectref_pass1.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/objectref_pass1.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/encode_normal.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/objectref_pass1.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); attrib_normal = glGetAttribLocation(Program, "Normal"); attrib_texcoord = glGetAttribLocation(Program, "Texcoord"); @@ -380,7 +449,10 @@ namespace MeshShader void GrassPass1Shader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/grass_pass1.vert").c_str(), file_manager->getAsset("shaders/objectref_pass1.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/grass_pass1.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/encode_normal.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/objectref_pass1.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); attrib_texcoord = glGetAttribLocation(Program, "Texcoord"); attrib_normal = glGetAttribLocation(Program, "Normal"); @@ -410,7 +482,10 @@ namespace MeshShader void NormalMapShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/normalmap.vert").c_str(), file_manager->getAsset("shaders/normalmap.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/normalmap.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/encode_normal.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/normalmap.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); attrib_texcoord = glGetAttribLocation(Program, "Texcoord"); attrib_tangent = glGetAttribLocation(Program, "Tangent"); @@ -436,7 +511,10 @@ namespace MeshShader void InstancedObjectPass1Shader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/instanced_object_pass1.vert").c_str(), file_manager->getAsset("shaders/object_pass1.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/instanced_object_pass1.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/encode_normal.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/object_pass1.frag").c_str()); attrib_origin = glGetAttribLocation(Program, "Origin"); attrib_position = glGetAttribLocation(Program, "Position"); attrib_normal = glGetAttribLocation(Program, "Normal"); @@ -463,7 +541,10 @@ namespace MeshShader void ObjectPass2Shader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/object_pass2.vert").c_str(), file_manager->getAsset("shaders/object_pass2.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/object_pass2.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getLightFactor.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/object_pass2.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); attrib_texcoord = glGetAttribLocation(Program, "Texcoord"); uniform_MVP = glGetUniformLocation(Program, "ModelViewProjectionMatrix"); @@ -505,7 +586,10 @@ namespace MeshShader void InstancedObjectPass2Shader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/instanced_object_pass2.vert").c_str(), file_manager->getAsset("shaders/object_pass2.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/instanced_object_pass2.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getLightFactor.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/object_pass2.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); attrib_texcoord = glGetAttribLocation(Program, "Texcoord"); attrib_origin = glGetAttribLocation(Program, "Origin"); @@ -548,7 +632,10 @@ namespace MeshShader void DetailledObjectPass2Shader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/object_pass2.vert").c_str(), file_manager->getAsset("shaders/detailledobject_pass2.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/object_pass2.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getLightFactor.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/detailledobject_pass2.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); attrib_texcoord = glGetAttribLocation(Program, "Texcoord"); attrib_second_texcoord = glGetAttribLocation(Program, "SecondTexcoord"); @@ -588,7 +675,9 @@ namespace MeshShader void ObjectUnlitShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/object_pass2.vert").c_str(), file_manager->getAsset("shaders/object_unlit.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/object_pass2.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/object_unlit.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); attrib_texcoord = glGetAttribLocation(Program, "Texcoord"); uniform_MVP = glGetUniformLocation(Program, "ModelViewProjectionMatrix"); @@ -618,7 +707,9 @@ namespace MeshShader void ObjectRimLimitShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/objectpass_rimlit.vert").c_str(), file_manager->getAsset("shaders/objectpass_rimlit.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/objectpass_rimlit.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/objectpass_rimlit.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); attrib_texcoord = glGetAttribLocation(Program, "Texcoord"); attrib_normal = glGetAttribLocation(Program, "Normal"); @@ -660,7 +751,10 @@ namespace MeshShader void UntexturedObjectShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/untextured_object.vert").c_str(), file_manager->getAsset("shaders/untextured_object.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/untextured_object.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getLightFactor.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/untextured_object.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); attrib_color = glGetAttribLocation(Program, "Color"); uniform_MVP = glGetUniformLocation(Program, "ModelViewProjectionMatrix"); @@ -698,7 +792,10 @@ namespace MeshShader void ObjectRefPass2Shader::init() { initGL(); - Program = LoadProgram(file_manager->getAsset("shaders/object_pass2.vert").c_str(), file_manager->getAsset("shaders/objectref_pass2.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/object_pass2.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getLightFactor.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/objectref_pass2.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); attrib_texcoord = glGetAttribLocation(Program, "Texcoord"); uniform_MVP = glGetUniformLocation(Program, "ModelViewProjectionMatrix"); @@ -740,7 +837,10 @@ namespace MeshShader void GrassPass2Shader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/grass_pass2.vert").c_str(), file_manager->getAsset("shaders/objectref_pass2.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/grass_pass2.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getLightFactor.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/objectref_pass2.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); attrib_texcoord = glGetAttribLocation(Program, "Texcoord"); attrib_color = glGetAttribLocation(Program, "Color"); @@ -783,7 +883,9 @@ namespace MeshShader void SphereMapShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/object_pass1.vert").c_str(), file_manager->getAsset("shaders/objectpass_spheremap.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/object_pass1.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/objectpass_spheremap.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); attrib_normal = glGetAttribLocation(Program, "Normal"); uniform_MVP = glGetUniformLocation(Program, "ModelViewProjectionMatrix"); @@ -823,7 +925,9 @@ namespace MeshShader void SplattingShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/splatting.vert").c_str(), file_manager->getAsset("shaders/splatting.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/splatting.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/splatting.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); attrib_texcoord = glGetAttribLocation(Program, "Texcoord"); attrib_second_texcoord = glGetAttribLocation(Program, "SecondTexcoord"); @@ -877,7 +981,10 @@ namespace MeshShader void CausticsShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/object_pass2.vert").c_str(), file_manager->getAsset("shaders/caustics.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/object_pass2.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getLightFactor.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/caustics.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); attrib_texcoord = glGetAttribLocation(Program, "Texcoord"); uniform_MVP = glGetUniformLocation(Program, "ModelViewProjectionMatrix"); @@ -922,7 +1029,9 @@ namespace MeshShader void BubbleShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/bubble.vert").c_str(), file_manager->getAsset("shaders/bubble.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/bubble.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/bubble.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); attrib_texcoord = glGetAttribLocation(Program, "Texcoord"); uniform_MVP = glGetUniformLocation(Program, "ModelViewProjectionMatrix"); @@ -941,15 +1050,19 @@ namespace MeshShader GLuint TransparentShader::Program; GLuint TransparentShader::attrib_position; GLuint TransparentShader::attrib_texcoord; + GLuint TransparentShader::attrib_color; GLuint TransparentShader::uniform_MVP; GLuint TransparentShader::uniform_TM; GLuint TransparentShader::uniform_tex; void TransparentShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/transparent.vert").c_str(), file_manager->getAsset("shaders/transparent.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/transparent.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/transparent.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); attrib_texcoord = glGetAttribLocation(Program, "Texcoord"); + attrib_color = glGetAttribLocation(Program, "Color"); uniform_MVP = glGetUniformLocation(Program, "ModelViewProjectionMatrix"); uniform_TM = glGetUniformLocation(Program, "TextureMatrix"); uniform_tex = glGetUniformLocation(Program, "tex"); @@ -965,6 +1078,7 @@ namespace MeshShader GLuint TransparentFogShader::Program; GLuint TransparentFogShader::attrib_position; GLuint TransparentFogShader::attrib_texcoord; + GLuint TransparentFogShader::attrib_color; GLuint TransparentFogShader::uniform_MVP; GLuint TransparentFogShader::uniform_TM; GLuint TransparentFogShader::uniform_tex; @@ -979,9 +1093,12 @@ namespace MeshShader void TransparentFogShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/transparent.vert").c_str(), file_manager->getAsset("shaders/transparentfog.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/transparent.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/transparentfog.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); attrib_texcoord = glGetAttribLocation(Program, "Texcoord"); + attrib_color = glGetAttribLocation(Program, "Color"); uniform_MVP = glGetUniformLocation(Program, "ModelViewProjectionMatrix"); uniform_TM = glGetUniformLocation(Program, "TextureMatrix"); uniform_tex = glGetUniformLocation(Program, "tex"); @@ -1009,47 +1126,6 @@ namespace MeshShader glUniformMatrix4fv(uniform_ipvmat, 1, GL_FALSE, ipvmat.pointer()); glUniform1i(uniform_tex, TU_tex); } - - GLuint PointLightShader::Program; - GLuint PointLightShader::attrib_Position; - GLuint PointLightShader::attrib_Color; - GLuint PointLightShader::attrib_Energy; - GLuint PointLightShader::attrib_Corner; - GLuint PointLightShader::uniform_ntex; - GLuint PointLightShader::uniform_dtex; - GLuint PointLightShader::uniform_spec; - GLuint PointLightShader::uniform_screen; - GLuint PointLightShader::uniform_invproj; - GLuint PointLightShader::uniform_VM; - GLuint PointLightShader::uniform_PM; - - void PointLightShader::init() - { - Program = LoadProgram(file_manager->getAsset("shaders/pointlight.vert").c_str(), file_manager->getAsset("shaders/pointlight.frag").c_str()); - attrib_Position = glGetAttribLocation(Program, "Position"); - attrib_Color = glGetAttribLocation(Program, "Color"); - attrib_Energy = glGetAttribLocation(Program, "Energy"); - attrib_Corner = glGetAttribLocation(Program, "Corner"); - uniform_ntex = glGetUniformLocation(Program, "ntex"); - uniform_dtex = glGetUniformLocation(Program, "dtex"); - uniform_spec = glGetUniformLocation(Program, "spec"); - uniform_invproj = glGetUniformLocation(Program, "invproj"); - uniform_screen = glGetUniformLocation(Program, "screen"); - uniform_VM = glGetUniformLocation(Program, "ViewMatrix"); - uniform_PM = glGetUniformLocation(Program, "ProjectionMatrix"); - } - - void PointLightShader::setUniforms(const core::matrix4 &ViewMatrix, const core::matrix4 &ProjMatrix, const core::matrix4 &InvProjMatrix, const core::vector2df &screen, unsigned spec, unsigned TU_ntex, unsigned TU_dtex) - { - glUniform1f(uniform_spec, 200); - glUniform2f(uniform_screen, screen.X, screen.Y); - glUniformMatrix4fv(uniform_invproj, 1, GL_FALSE, InvProjMatrix.pointer()); - glUniformMatrix4fv(uniform_VM, 1, GL_FALSE, ViewMatrix.pointer()); - glUniformMatrix4fv(uniform_PM, 1, GL_FALSE, ProjMatrix.pointer()); - - glUniform1i(uniform_ntex, TU_ntex); - glUniform1i(uniform_dtex, TU_dtex); - } GLuint BillboardShader::Program; GLuint BillboardShader::attrib_corner; @@ -1062,7 +1138,9 @@ namespace MeshShader void BillboardShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/billboard.vert").c_str(), file_manager->getAsset("shaders/billboard.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/billboard.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/billboard.frag").c_str()); attrib_corner = glGetAttribLocation(Program, "Corner"); attrib_texcoord = glGetAttribLocation(Program, "Texcoord"); uniform_MV = glGetUniformLocation(Program, "ModelViewMatrix"); @@ -1088,7 +1166,9 @@ namespace MeshShader void ColorizeShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/object_pass2.vert").c_str(), file_manager->getAsset("shaders/colorize.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/object_pass2.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/colorize.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); uniform_MVP = glGetUniformLocation(Program, "ModelViewProjectionMatrix"); uniform_col = glGetUniformLocation(Program, "col"); @@ -1106,7 +1186,16 @@ namespace MeshShader void ShadowShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/shadow.vert").c_str(), file_manager->getAsset("shaders/shadow.geom").c_str(), file_manager->getAsset("shaders/white.frag").c_str()); + // Geometry shader needed + if (irr_driver->getGLSLVersion() < 150) + { + attrib_position = -1; + return; + } + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/shadow.vert").c_str(), + GL_GEOMETRY_SHADER, file_manager->getAsset("shaders/shadow.geom").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/white.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); uniform_MVP = glGetUniformLocation(Program, "ModelViewProjectionMatrix"); } @@ -1130,7 +1219,17 @@ namespace MeshShader void RefShadowShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/shadow.vert").c_str(), file_manager->getAsset("shaders/shadow.geom").c_str(), file_manager->getAsset("shaders/object_unlit.frag").c_str()); + // Geometry shader needed + if (irr_driver->getGLSLVersion() < 150) + { + attrib_position = -1; + attrib_texcoord = -1; + return; + } + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/shadow.vert").c_str(), + GL_GEOMETRY_SHADER, file_manager->getAsset("shaders/shadow.geom").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/object_unlit.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); attrib_texcoord = glGetAttribLocation(Program, "Texcoord"); uniform_MVP = glGetUniformLocation(Program, "ModelViewProjectionMatrix"); @@ -1159,7 +1258,9 @@ namespace MeshShader void GrassShadowShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/grass_pass2.vert").c_str(), file_manager->getAsset("shaders/object_unlit.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/grass_pass2.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/object_unlit.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); attrib_texcoord = glGetAttribLocation(Program, "Texcoord"); uniform_MVP = glGetUniformLocation(Program, "ModelViewProjectionMatrix"); @@ -1181,7 +1282,9 @@ namespace MeshShader void DisplaceMaskShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/displace.vert").c_str(), file_manager->getAsset("shaders/white.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/displace.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/white.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); uniform_MVP = glGetUniformLocation(Program, "ModelViewProjectionMatrix"); } @@ -1206,7 +1309,9 @@ namespace MeshShader void DisplaceShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/displace.vert").c_str(), file_manager->getAsset("shaders/displace.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/displace.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/displace.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); attrib_texcoord = glGetAttribLocation(Program, "Texcoord"); attrib_second_texcoord = glGetAttribLocation(Program, "SecondTexcoord"); @@ -1238,15 +1343,26 @@ namespace MeshShader GLuint SkyboxShader::uniform_tex; GLuint SkyboxShader::uniform_screen; GLuint SkyboxShader::uniform_InvProjView; + GLuint SkyboxShader::cubevao; void SkyboxShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/object_pass2.vert").c_str(), file_manager->getAsset("shaders/sky.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/object_pass2.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/sky.frag").c_str()); attrib_position = glGetAttribLocation(Program, "Position"); uniform_MVP = glGetUniformLocation(Program, "ModelViewProjectionMatrix"); uniform_InvProjView = glGetUniformLocation(Program, "InvProjView"); uniform_tex = glGetUniformLocation(Program, "tex"); uniform_screen = glGetUniformLocation(Program, "screen"); + + glGenVertexArrays(1, &cubevao); + glBindVertexArray(cubevao); + glBindBuffer(GL_ARRAY_BUFFER, SharedObject::cubevbo); + glEnableVertexAttribArray(attrib_position); + glVertexAttribPointer(attrib_position, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedObject::cubeindexes); + glBindVertexArray(0); } void SkyboxShader::setUniforms(const core::matrix4 &ModelViewProjectionMatrix, const core::matrix4 &InvProjView, const core::vector2df &screen, unsigned TU_tex) @@ -1258,6 +1374,79 @@ namespace MeshShader } } +namespace LightShader +{ + + GLuint PointLightShader::Program; + GLuint PointLightShader::attrib_Position; + GLuint PointLightShader::attrib_Color; + GLuint PointLightShader::attrib_Energy; + GLuint PointLightShader::attrib_Corner; + GLuint PointLightShader::uniform_ntex; + GLuint PointLightShader::uniform_dtex; + GLuint PointLightShader::uniform_spec; + GLuint PointLightShader::uniform_screen; + GLuint PointLightShader::uniform_invproj; + GLuint PointLightShader::uniform_VM; + GLuint PointLightShader::uniform_PM; + GLuint PointLightShader::vbo; + GLuint PointLightShader::vao; + + void PointLightShader::init() + { + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/pointlight.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/decodeNormal.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/pointlight.frag").c_str()); + attrib_Position = glGetAttribLocation(Program, "Position"); + attrib_Color = glGetAttribLocation(Program, "Color"); + attrib_Energy = glGetAttribLocation(Program, "Energy"); + attrib_Corner = glGetAttribLocation(Program, "Corner"); + uniform_ntex = glGetUniformLocation(Program, "ntex"); + uniform_dtex = glGetUniformLocation(Program, "dtex"); + uniform_spec = glGetUniformLocation(Program, "spec"); + uniform_invproj = glGetUniformLocation(Program, "invproj"); + uniform_screen = glGetUniformLocation(Program, "screen"); + uniform_VM = glGetUniformLocation(Program, "ViewMatrix"); + uniform_PM = glGetUniformLocation(Program, "ProjectionMatrix"); + + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + glBindBuffer(GL_ARRAY_BUFFER, SharedObject::billboardvbo); + glEnableVertexAttribArray(attrib_Corner); + glVertexAttribPointer(attrib_Corner, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0); + + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, MAXLIGHT * sizeof(PointLightInfo), 0, GL_DYNAMIC_DRAW); + + glEnableVertexAttribArray(attrib_Position); + glVertexAttribPointer(attrib_Position, 3, GL_FLOAT, GL_FALSE, sizeof(PointLightInfo), 0); + glEnableVertexAttribArray(attrib_Energy); + glVertexAttribPointer(attrib_Energy, 1, GL_FLOAT, GL_FALSE, sizeof(PointLightInfo), (GLvoid*)(3 * sizeof(float))); + glEnableVertexAttribArray(attrib_Color); + glVertexAttribPointer(attrib_Color, 3, GL_FLOAT, GL_FALSE, sizeof(PointLightInfo), (GLvoid*)(4 * sizeof(float))); + + glVertexAttribDivisor(attrib_Position, 1); + glVertexAttribDivisor(attrib_Energy, 1); + glVertexAttribDivisor(attrib_Color, 1); + } + + void PointLightShader::setUniforms(const core::matrix4 &ViewMatrix, const core::matrix4 &ProjMatrix, const core::matrix4 &InvProjMatrix, const core::vector2df &screen, unsigned spec, unsigned TU_ntex, unsigned TU_dtex) + { + glUniform1f(uniform_spec, 200); + glUniform2f(uniform_screen, screen.X, screen.Y); + glUniformMatrix4fv(uniform_invproj, 1, GL_FALSE, InvProjMatrix.pointer()); + glUniformMatrix4fv(uniform_VM, 1, GL_FALSE, ViewMatrix.pointer()); + glUniformMatrix4fv(uniform_PM, 1, GL_FALSE, ProjMatrix.pointer()); + + glUniform1i(uniform_ntex, TU_ntex); + glUniform1i(uniform_dtex, TU_dtex); + } + +} + namespace ParticleShader { @@ -1367,7 +1556,9 @@ namespace ParticleShader void SimpleParticleRender::init() { - Program = LoadProgram(file_manager->getAsset("shaders/particle.vert").c_str(), file_manager->getAsset("shaders/particle.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/particle.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/particle.frag").c_str()); attrib_pos = glGetAttribLocation(Program, "position"); attrib_sz = glGetAttribLocation(Program, "size"); attrib_lf = glGetAttribLocation(Program, "lifetime"); @@ -1421,7 +1612,9 @@ namespace ParticleShader void FlipParticleRender::init() { - Program = LoadProgram(file_manager->getAsset("shaders/flipparticle.vert").c_str(), file_manager->getAsset("shaders/particle.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/flipparticle.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/particle.frag").c_str()); attrib_pos = glGetAttribLocation(Program, "position"); attrib_sz = glGetAttribLocation(Program, "size"); attrib_lf = glGetAttribLocation(Program, "lifetime"); @@ -1473,7 +1666,9 @@ namespace FullScreenShader GLuint BloomShader::vao; void BloomShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/screenquad.vert").c_str(), file_manager->getAsset("shaders/bloom.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/bloom.frag").c_str()); uniform_texture = glGetUniformLocation(Program, "tex"); uniform_low = glGetUniformLocation(Program, "low"); vao = createVAO(Program); @@ -1485,7 +1680,9 @@ namespace FullScreenShader GLuint BloomBlendShader::vao; void BloomBlendShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/screenquad.vert").c_str(), file_manager->getAsset("shaders/bloomblend.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/bloomblend.frag").c_str()); uniform_texture = glGetUniformLocation(Program, "tex"); vao = createVAO(Program); } @@ -1499,7 +1696,9 @@ namespace FullScreenShader GLuint ColorLevelShader::uniform_dtex; void ColorLevelShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/screenquad.vert").c_str(), file_manager->getAsset("shaders/color_levels.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/color_levels.frag").c_str()); uniform_tex = glGetUniformLocation(Program, "tex"); uniform_dtex = glGetUniformLocation(Program, "dtex"); uniform_inlevel = glGetUniformLocation(Program, "inlevel"); @@ -1518,7 +1717,10 @@ namespace FullScreenShader void SunLightShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/screenquad.vert").c_str(), file_manager->getAsset("shaders/sunlight.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/decodeNormal.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/sunlight.frag").c_str()); uniform_ntex = glGetUniformLocation(Program, "ntex"); uniform_dtex = glGetUniformLocation(Program, "dtex"); uniform_direction = glGetUniformLocation(Program, "direction"); @@ -1546,7 +1748,10 @@ namespace FullScreenShader void DiffuseEnvMapShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/screenquad.vert").c_str(), file_manager->getAsset("shaders/diffuseenvmap.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/decodeNormal.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/diffuseenvmap.frag").c_str()); uniform_ntex = glGetUniformLocation(Program, "ntex"); uniform_blueLmn = glGetUniformLocation(Program, "blueLmn[0]"); uniform_greenLmn = glGetUniformLocation(Program, "greenLmn[0]"); @@ -1576,7 +1781,10 @@ namespace FullScreenShader void ShadowedSunLightShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/screenquad.vert").c_str(), file_manager->getAsset("shaders/sunlightshadow.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/decodeNormal.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/sunlightshadow.frag").c_str()); uniform_ntex = glGetUniformLocation(Program, "ntex"); uniform_dtex = glGetUniformLocation(Program, "dtex"); uniform_shadowtex = glGetUniformLocation(Program, "shadowtex"); @@ -1611,7 +1819,9 @@ namespace FullScreenShader GLuint Gaussian6HBlurShader::vao; void Gaussian6HBlurShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/screenquad.vert").c_str(), file_manager->getAsset("shaders/gaussian6h.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/gaussian6h.frag").c_str()); uniform_tex = glGetUniformLocation(Program, "tex"); uniform_pixel = glGetUniformLocation(Program, "pixel"); vao = createVAO(Program); @@ -1623,7 +1833,9 @@ namespace FullScreenShader GLuint Gaussian3HBlurShader::vao; void Gaussian3HBlurShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/screenquad.vert").c_str(), file_manager->getAsset("shaders/gaussian3h.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/gaussian3h.frag").c_str()); uniform_tex = glGetUniformLocation(Program, "tex"); uniform_pixel = glGetUniformLocation(Program, "pixel"); vao = createVAO(Program); @@ -1635,7 +1847,9 @@ namespace FullScreenShader GLuint Gaussian6VBlurShader::vao; void Gaussian6VBlurShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/screenquad.vert").c_str(), file_manager->getAsset("shaders/gaussian6v.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/gaussian6v.frag").c_str()); uniform_tex = glGetUniformLocation(Program, "tex"); uniform_pixel = glGetUniformLocation(Program, "pixel"); vao = createVAO(Program); @@ -1647,7 +1861,9 @@ namespace FullScreenShader GLuint Gaussian3VBlurShader::vao; void Gaussian3VBlurShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/screenquad.vert").c_str(), file_manager->getAsset("shaders/gaussian3v.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/gaussian3v.frag").c_str()); uniform_tex = glGetUniformLocation(Program, "tex"); uniform_pixel = glGetUniformLocation(Program, "pixel"); vao = createVAO(Program); @@ -1659,7 +1875,9 @@ namespace FullScreenShader GLuint PenumbraHShader::vao; void PenumbraHShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/screenquad.vert").c_str(), file_manager->getAsset("shaders/penumbrah.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/penumbrah.frag").c_str()); uniform_tex = glGetUniformLocation(Program, "tex"); uniform_pixel = glGetUniformLocation(Program, "pixel"); vao = createVAO(Program); @@ -1677,7 +1895,9 @@ namespace FullScreenShader GLuint PenumbraVShader::vao; void PenumbraVShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/screenquad.vert").c_str(), file_manager->getAsset("shaders/penumbrav.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/penumbrav.frag").c_str()); uniform_tex = glGetUniformLocation(Program, "tex"); uniform_pixel = glGetUniformLocation(Program, "pixel"); vao = createVAO(Program); @@ -1696,7 +1916,9 @@ namespace FullScreenShader GLuint ShadowGenShader::vao; void ShadowGenShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/screenquad.vert").c_str(), file_manager->getAsset("shaders/shadowgen.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/shadowgen.frag").c_str()); uniform_halft = glGetUniformLocation(Program, "halft"); uniform_quarter = glGetUniformLocation(Program, "quarter"); uniform_height = glGetUniformLocation(Program, "height"); @@ -1715,7 +1937,9 @@ namespace FullScreenShader GLuint PassThroughShader::vao; void PassThroughShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/screenquad.vert").c_str(), file_manager->getAsset("shaders/texturedquad.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/texturedquad.frag").c_str()); uniform_texture = glGetUniformLocation(Program, "texture"); vao = createVAO(Program); } @@ -1725,7 +1949,9 @@ namespace FullScreenShader GLuint GlowShader::vao; void GlowShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/screenquad.vert").c_str(), file_manager->getAsset("shaders/glow.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/glow.frag").c_str()); uniform_tex = glGetUniformLocation(Program, "tex"); vao = createVAO(Program); } @@ -1742,7 +1968,10 @@ namespace FullScreenShader void SSAOShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/screenquad.vert").c_str(), file_manager->getAsset("shaders/ssao.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/decodeNormal.frag").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/ssao.frag").c_str()); uniform_ntex = glGetUniformLocation(Program, "ntex"); uniform_dtex = glGetUniformLocation(Program, "dtex"); uniform_noise_texture = glGetUniformLocation(Program, "noise_texture"); @@ -1889,7 +2118,9 @@ namespace FullScreenShader void FogShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/screenquad.vert").c_str(), file_manager->getAsset("shaders/fog.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/fog.frag").c_str()); uniform_tex = glGetUniformLocation(Program, "tex"); uniform_fogmax = glGetUniformLocation(Program, "fogmax"); uniform_startH = glGetUniformLocation(Program, "startH"); @@ -1924,7 +2155,9 @@ namespace FullScreenShader void MotionBlurShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/screenquad.vert").c_str(), file_manager->getAsset("shaders/motion_blur.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/motion_blur.frag").c_str()); uniform_boost_amount = glGetUniformLocation(Program, "boost_amount"); uniform_center = glGetUniformLocation(Program, "center"); uniform_color_buffer = glGetUniformLocation(Program, "color_buffer"); @@ -1951,7 +2184,9 @@ namespace FullScreenShader void GodFadeShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/screenquad.vert").c_str(), file_manager->getAsset("shaders/godfade.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/godfade.frag").c_str()); uniform_tex = glGetUniformLocation(Program, "tex"); uniform_col = glGetUniformLocation(Program, "col"); vao = createVAO(Program); @@ -1970,7 +2205,9 @@ namespace FullScreenShader void GodRayShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/screenquad.vert").c_str(), file_manager->getAsset("shaders/godray.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/godray.frag").c_str()); uniform_tex = glGetUniformLocation(Program, "tex"); uniform_sunpos = glGetUniformLocation(Program, "sunpos"); vao = createVAO(Program); @@ -1997,7 +2234,9 @@ namespace UIShader void TextureRectShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/texturedquad.vert").c_str(), file_manager->getAsset("shaders/texturedquad.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/texturedquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/texturedquad.frag").c_str()); attrib_position = glGetAttribLocation(Program, "position"); attrib_texcoord = glGetAttribLocation(Program, "texcoord"); @@ -2039,7 +2278,9 @@ namespace UIShader void ColoredTextureRectShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/colortexturedquad.vert").c_str(), file_manager->getAsset("shaders/colortexturedquad.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/colortexturedquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/colortexturedquad.frag").c_str()); attrib_position = glGetAttribLocation(Program, "position"); attrib_texcoord = glGetAttribLocation(Program, "texcoord"); @@ -2088,7 +2329,9 @@ namespace UIShader void ColoredRectShader::init() { - Program = LoadProgram(file_manager->getAsset("shaders/coloredquad.vert").c_str(), file_manager->getAsset("shaders/coloredquad.frag").c_str()); + Program = LoadProgram( + GL_VERTEX_SHADER, file_manager->getAsset("shaders/coloredquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/coloredquad.frag").c_str()); attrib_position = glGetAttribLocation(Program, "position"); uniform_color = glGetUniformLocation(Program, "color"); uniform_center = glGetUniformLocation(Program, "center"); diff --git a/src/graphics/shaders.hpp b/src/graphics/shaders.hpp index 9898ab567..e68d00848 100644 --- a/src/graphics/shaders.hpp +++ b/src/graphics/shaders.hpp @@ -29,6 +29,7 @@ class SharedObject { public: static GLuint billboardvbo; + static GLuint cubevbo, cubeindexes; }; namespace MeshShader { @@ -233,7 +234,7 @@ class TransparentShader { public: static GLuint Program; - static GLuint attrib_position, attrib_texcoord; + static GLuint attrib_position, attrib_texcoord, attrib_color; static GLuint uniform_MVP, uniform_TM, uniform_tex; static void init(); @@ -244,25 +245,13 @@ class TransparentFogShader { public: static GLuint Program; - static GLuint attrib_position, attrib_texcoord; + static GLuint attrib_position, attrib_texcoord, attrib_color; static GLuint uniform_MVP, uniform_TM, uniform_tex, uniform_fogmax, uniform_startH, uniform_endH, uniform_start, uniform_end, uniform_col, uniform_screen, uniform_ipvmat; static void init(); static void setUniforms(const core::matrix4 &ModelViewProjectionMatrix, const core::matrix4 &TextureMatrix, const core::matrix4 &ipvmat, float fogmax, float startH, float endH, float start, float end, const core::vector3df &col, const core::vector3df &campos, unsigned TU_tex); }; -class PointLightShader -{ -public: - static GLuint Program; - static GLuint attrib_Position, attrib_Energy, attrib_Color; - static GLuint attrib_Corner; - static GLuint uniform_ntex, uniform_dtex, uniform_spec, uniform_screen, uniform_invproj, uniform_VM, uniform_PM; - - static void init(); - static void setUniforms(const core::matrix4 &ViewMatrix, const core::matrix4 &ProjMatrix, const core::matrix4 &InvProjMatrix, const core::vector2df &screen, unsigned spec, unsigned TU_ntex, unsigned TU_dtex); -}; - class BillboardShader { public: @@ -347,6 +336,7 @@ public: static GLuint Program; static GLuint attrib_position; static GLuint uniform_MVP, uniform_InvProjView, uniform_tex, uniform_screen; + static GLuint cubevao; static void init(); static void setUniforms(const core::matrix4 &ModelViewProjectionMatrix, const core::matrix4 &InvProjView, const core::vector2df &screen, unsigned TU_tex); @@ -354,6 +344,38 @@ public: } +#define MAXLIGHT 16 + +namespace LightShader +{ + struct PointLightInfo + { + float posX; + float posY; + float posZ; + float energy; + float red; + float green; + float blue; + float padding; + }; + + + class PointLightShader + { + public: + static GLuint Program; + static GLuint attrib_Position, attrib_Energy, attrib_Color; + static GLuint attrib_Corner; + static GLuint uniform_ntex, uniform_dtex, uniform_spec, uniform_screen, uniform_invproj, uniform_VM, uniform_PM; + static GLuint vbo; + static GLuint vao; + + static void init(); + static void setUniforms(const core::matrix4 &ViewMatrix, const core::matrix4 &ProjMatrix, const core::matrix4 &InvProjMatrix, const core::vector2df &screen, unsigned spec, unsigned TU_ntex, unsigned TU_dtex); + }; +} + namespace ParticleShader { diff --git a/src/graphics/skid_marks.cpp b/src/graphics/skid_marks.cpp index e8f0eb40a..9d519ba99 100644 --- a/src/graphics/skid_marks.cpp +++ b/src/graphics/skid_marks.cpp @@ -25,6 +25,7 @@ #include "karts/abstract_kart.hpp" #include "karts/skidding.hpp" #include "physics/btKart.hpp" +#include "graphics/stkmeshscenenode.hpp" #include #include @@ -134,6 +135,8 @@ void SkidMarks::update(float dt, bool force_skid_marks, // (till these skid mark quads are deleted) m_left[m_current]->setHardwareMappingHint(scene::EHM_STATIC); m_right[m_current]->setHardwareMappingHint(scene::EHM_STATIC); + if (STKMeshSceneNode* stkm = dynamic_cast(m_nodes[m_current])) + stkm->setReloadEachFrame(false); return; } @@ -188,6 +191,8 @@ void SkidMarks::update(float dt, bool force_skid_marks, m_material, m_avoid_z_fighting, custom_color); new_mesh->addMeshBuffer(smq_right); scene::IMeshSceneNode *new_node = irr_driver->addMesh(new_mesh); + if (STKMeshSceneNode* stkm = dynamic_cast(new_node)) + stkm->setReloadEachFrame(true); #ifdef DEBUG std::string debug_name = m_kart.getIdent()+" (skid-mark)"; new_node->setName(debug_name.c_str()); diff --git a/src/graphics/stkanimatedmesh.cpp b/src/graphics/stkanimatedmesh.cpp index 8bb937dd1..ea67aa8e9 100644 --- a/src/graphics/stkanimatedmesh.cpp +++ b/src/graphics/stkanimatedmesh.cpp @@ -171,11 +171,13 @@ void STKAnimatedMesh::render() computeMVP(ModelViewProjectionMatrix); computeTIMV(TransposeInverseModelView); - glUseProgram(MeshShader::ObjectPass1Shader::Program); + if (!GeometricMesh[FPSM_DEFAULT].empty()) + glUseProgram(MeshShader::ObjectPass1Shader::Program); for (unsigned i = 0; i < GeometricMesh[FPSM_DEFAULT].size(); i++) drawSolidPass1(*GeometricMesh[FPSM_DEFAULT][i], FPSM_DEFAULT); - glUseProgram(MeshShader::ObjectRefPass1Shader::Program); + if (!GeometricMesh[FPSM_ALPHA_REF_TEXTURE].empty()) + glUseProgram(MeshShader::ObjectRefPass1Shader::Program); for (unsigned i = 0; i < GeometricMesh[FPSM_ALPHA_REF_TEXTURE].size(); i++) drawSolidPass1(*GeometricMesh[FPSM_ALPHA_REF_TEXTURE][i], FPSM_ALPHA_REF_TEXTURE); @@ -184,36 +186,43 @@ void STKAnimatedMesh::render() if (irr_driver->getPhase() == SOLID_LIT_PASS) { - glUseProgram(MeshShader::ObjectPass2Shader::Program); - for (unsigned i = 0; i < ShadedMesh[SM_DEFAULT].size(); i++) - drawSolidPass2(*ShadedMesh[SM_DEFAULT][i], SM_DEFAULT); + if (!ShadedMesh[SM_DEFAULT].empty()) + glUseProgram(MeshShader::ObjectPass2Shader::Program); + for (unsigned i = 0; i < ShadedMesh[SM_DEFAULT].size(); i++) + drawSolidPass2(*ShadedMesh[SM_DEFAULT][i], SM_DEFAULT); - glUseProgram(MeshShader::ObjectRefPass2Shader::Program); - for (unsigned i = 0; i < ShadedMesh[SM_ALPHA_REF_TEXTURE].size(); i++) - drawSolidPass2(*ShadedMesh[SM_ALPHA_REF_TEXTURE][i], SM_ALPHA_REF_TEXTURE); + if (!ShadedMesh[SM_ALPHA_REF_TEXTURE].empty()) + glUseProgram(MeshShader::ObjectRefPass2Shader::Program); + for (unsigned i = 0; i < ShadedMesh[SM_ALPHA_REF_TEXTURE].size(); i++) + drawSolidPass2(*ShadedMesh[SM_ALPHA_REF_TEXTURE][i], SM_ALPHA_REF_TEXTURE); - glUseProgram(MeshShader::ObjectRimLimitShader::Program); - for (unsigned i = 0; i < ShadedMesh[SM_RIMLIT].size(); i++) - drawSolidPass2(*ShadedMesh[SM_RIMLIT][i], SM_RIMLIT); + if (!ShadedMesh[SM_RIMLIT].empty()) + glUseProgram(MeshShader::ObjectRimLimitShader::Program); + for (unsigned i = 0; i < ShadedMesh[SM_RIMLIT].size(); i++) + drawSolidPass2(*ShadedMesh[SM_RIMLIT][i], SM_RIMLIT); - glUseProgram(MeshShader::ObjectUnlitShader::Program); - for (unsigned i = 0; i < ShadedMesh[SM_UNLIT].size(); i++) - drawSolidPass2(*ShadedMesh[SM_UNLIT][i], SM_UNLIT); + if (!ShadedMesh[SM_UNLIT].empty()) + glUseProgram(MeshShader::ObjectUnlitShader::Program); + for (unsigned i = 0; i < ShadedMesh[SM_UNLIT].size(); i++) + drawSolidPass2(*ShadedMesh[SM_UNLIT][i], SM_UNLIT); - glUseProgram(MeshShader::DetailledObjectPass2Shader::Program); - for (unsigned i = 0; i < ShadedMesh[SM_DETAILS].size(); i++) - drawSolidPass2(*ShadedMesh[SM_DETAILS][i], SM_DETAILS); + if (!ShadedMesh[SM_DETAILS].empty()) + glUseProgram(MeshShader::DetailledObjectPass2Shader::Program); + for (unsigned i = 0; i < ShadedMesh[SM_DETAILS].size(); i++) + drawSolidPass2(*ShadedMesh[SM_DETAILS][i], SM_DETAILS); - return; + return; } if (irr_driver->getPhase() == SHADOW_PASS) { - glUseProgram(MeshShader::ShadowShader::Program); + if (!GeometricMesh[FPSM_DEFAULT].empty()) + glUseProgram(MeshShader::ShadowShader::Program); for (unsigned i = 0; i < GeometricMesh[FPSM_DEFAULT].size(); i++) drawShadow(*GeometricMesh[FPSM_DEFAULT][i]); - glUseProgram(MeshShader::RefShadowShader::Program); + if (!GeometricMesh[FPSM_ALPHA_REF_TEXTURE].empty()) + glUseProgram(MeshShader::RefShadowShader::Program); for (unsigned i = 0; i < GeometricMesh[FPSM_ALPHA_REF_TEXTURE].size(); i++) drawShadowRef(*GeometricMesh[FPSM_ALPHA_REF_TEXTURE][i]); return; @@ -223,13 +232,25 @@ void STKAnimatedMesh::render() { computeMVP(ModelViewProjectionMatrix); - glUseProgram(MeshShader::BubbleShader::Program); + if (!TransparentMesh[TM_BUBBLE].empty()) + glUseProgram(MeshShader::BubbleShader::Program); for (unsigned i = 0; i < TransparentMesh[TM_BUBBLE].size(); i++) drawBubble(*TransparentMesh[TM_BUBBLE][i], ModelViewProjectionMatrix); - glUseProgram(MeshShader::TransparentShader::Program); - for (unsigned i = 0; i < TransparentMesh[TM_DEFAULT].size(); i++) - drawTransparentObject(*TransparentMesh[TM_DEFAULT][i], ModelViewProjectionMatrix, (*TransparentMesh[TM_DEFAULT][i]).TextureMatrix); + if (World::getWorld()->getTrack()->isFogEnabled()) + { + if (!TransparentMesh[TM_DEFAULT].empty()) + glUseProgram(MeshShader::TransparentFogShader::Program); + for (unsigned i = 0; i < TransparentMesh[TM_DEFAULT].size(); i++) + drawTransparentFogObject(*TransparentMesh[TM_DEFAULT][i], ModelViewProjectionMatrix, (*TransparentMesh[TM_DEFAULT][i]).TextureMatrix); + } + else + { + if (!TransparentMesh[TM_DEFAULT].empty()) + glUseProgram(MeshShader::TransparentShader::Program); + for (unsigned i = 0; i < TransparentMesh[TM_DEFAULT].size(); i++) + drawTransparentObject(*TransparentMesh[TM_DEFAULT][i], ModelViewProjectionMatrix, (*TransparentMesh[TM_DEFAULT][i]).TextureMatrix); + } return; } } diff --git a/src/graphics/stkbillboard.cpp b/src/graphics/stkbillboard.cpp index 2f4d5f578..1acfab27b 100644 --- a/src/graphics/stkbillboard.cpp +++ b/src/graphics/stkbillboard.cpp @@ -30,6 +30,8 @@ STKBillboard::STKBillboard(irr::scene::ISceneNode* parent, irr::scene::ISceneMan void STKBillboard::render() { + if (irr_driver->getPhase() != TRANSPARENT_PASS) + return; core::vector3df pos = getAbsolutePosition(); glBindVertexArray(billboardvao); GLuint texid = getTextureGLuint(Material.getTexture(0)); diff --git a/src/graphics/stkmesh.cpp b/src/graphics/stkmesh.cpp index 1f18ad4d3..27a0119ff 100644 --- a/src/graphics/stkmesh.cpp +++ b/src/graphics/stkmesh.cpp @@ -55,6 +55,8 @@ TransparentMaterial MaterialTypeToTransparentMaterial(video::E_MATERIAL_TYPE typ GLuint createVAO(GLuint vbo, GLuint idx, GLuint attrib_position, GLuint attrib_texcoord, GLuint attrib_second_texcoord, GLuint attrib_normal, GLuint attrib_tangent, GLuint attrib_bitangent, GLuint attrib_color, size_t stride) { + if (attrib_position == -1) + return 0; GLuint vao; glGenVertexArrays(1, &vao); glBindVertexArray(vao); @@ -401,6 +403,8 @@ void drawObjectRefPass2(const GLMesh &mesh, const core::matrix4 &ModelViewProjec glDrawElements(ptype, count, itype, 0); } +static video::ITexture *CausticTex = 0; + void drawCaustics(const GLMesh &mesh, const core::matrix4 & ModelViewProjectionMatrix, core::vector2df dir, core::vector2df dir2) { irr_driver->IncreaseObjectCount(); @@ -419,7 +423,9 @@ void drawCaustics(const GLMesh &mesh, const core::matrix4 & ModelViewProjectionM GLint swizzleMask[] = { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA }; glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask); } - setTexture(MeshShader::CausticsShader::TU_caustictex, getTextureGLuint(irr_driver->getTexture(file_manager->getAsset("textures/caustics.png").c_str())), GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, true); + if (!CausticTex) + CausticTex = irr_driver->getTexture(file_manager->getAsset("textures/caustics.png").c_str()); + setTexture(MeshShader::CausticsShader::TU_caustictex, getTextureGLuint(CausticTex), GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, true); MeshShader::CausticsShader::setUniforms(ModelViewProjectionMatrix, dir, dir2, core::vector2df(UserConfigParams::m_width, UserConfigParams::m_height)); @@ -814,20 +820,18 @@ void initvaostate(GLMesh &mesh, TransparentMaterial TranspMat) MeshShader::BubbleShader::attrib_position, MeshShader::BubbleShader::attrib_texcoord, -1, -1, -1, -1, -1, mesh.Stride); break; case TM_DEFAULT: - mesh.vao_first_pass = createVAO(mesh.vertex_buffer, mesh.index_buffer, - MeshShader::TransparentShader::attrib_position, MeshShader::TransparentShader::attrib_texcoord, -1, -1, -1, -1, -1, mesh.Stride); + if (World::getWorld()->getTrack()->isFogEnabled()) + mesh.vao_first_pass = createVAO(mesh.vertex_buffer, mesh.index_buffer, + MeshShader::TransparentFogShader::attrib_position, MeshShader::TransparentFogShader::attrib_texcoord, -1, -1, -1, -1, MeshShader::TransparentFogShader::attrib_color, mesh.Stride); + + else + mesh.vao_first_pass = createVAO(mesh.vertex_buffer, mesh.index_buffer, + MeshShader::TransparentShader::attrib_position, MeshShader::TransparentShader::attrib_texcoord, -1, -1, -1, -1, MeshShader::TransparentShader::attrib_color, mesh.Stride); break; } mesh.vao_glow_pass = createVAO(mesh.vertex_buffer, mesh.index_buffer, MeshShader::ColorizeShader::attrib_position, -1, -1, -1, -1, -1, -1, mesh.Stride); mesh.vao_displace_mask_pass = createVAO(mesh.vertex_buffer, mesh.index_buffer, MeshShader::DisplaceShader::attrib_position, -1, -1, -1, -1, -1, -1, mesh.Stride); if (mesh.Stride >= 44) mesh.vao_displace_pass = createVAO(mesh.vertex_buffer, mesh.index_buffer, MeshShader::DisplaceShader::attrib_position, MeshShader::DisplaceShader::attrib_texcoord, MeshShader::DisplaceShader::attrib_second_texcoord, -1, -1, -1, -1, mesh.Stride); - -/* -else if (World::getWorld()->getTrack()->isFogEnabled()) -{ -mesh.vao_first_pass = createVAO(mesh.vertex_buffer, mesh.index_buffer, -MeshShader::TransparentFogShader::attrib_position, MeshShader::TransparentFogShader::attrib_texcoord, -1, -1, -1, -1, -1, mesh.Stride); -}*/ } diff --git a/src/graphics/stkmeshscenenode.cpp b/src/graphics/stkmeshscenenode.cpp index bf67c4893..2b4301fd1 100644 --- a/src/graphics/stkmeshscenenode.cpp +++ b/src/graphics/stkmeshscenenode.cpp @@ -32,9 +32,15 @@ STKMeshSceneNode::STKMeshSceneNode(irr::scene::IMesh* mesh, ISceneNode* parent, const irr::core::vector3df& scale) : CMeshSceneNode(mesh, parent, mgr, id, position, rotation, scale) { + reload_each_frame = false; createGLMeshes(); } +void STKMeshSceneNode::setReloadEachFrame(bool val) +{ + reload_each_frame = val; +} + void STKMeshSceneNode::createGLMeshes() { for (u32 i = 0; igetMeshBufferCount(); ++i) @@ -137,6 +143,8 @@ void STKMeshSceneNode::drawGlow(const GLMesh &mesh) glDrawElements(ptype, count, itype, 0); } +static video::ITexture *displaceTex = 0; + void STKMeshSceneNode::drawDisplace(const GLMesh &mesh) { DisplaceProvider * const cb = (DisplaceProvider *)irr_driver->getCallback(ES_DISPLACE); @@ -161,8 +169,10 @@ void STKMeshSceneNode::drawDisplace(const GLMesh &mesh) glDrawElements(ptype, count, itype, 0); // Render the effect + if (!displaceTex) + displaceTex = irr_driver->getTexture(FileManager::TEXTURE, "displace.png"); irr_driver->getVideoDriver()->setRenderTarget(irr_driver->getRTT(RTT_DISPLACE), false, false); - setTexture(0, getTextureGLuint(irr_driver->getTexture(FileManager::TEXTURE, "displace.png")), GL_LINEAR, GL_LINEAR, true); + setTexture(0, getTextureGLuint(displaceTex), GL_LINEAR, GL_LINEAR, true); setTexture(1, getTextureGLuint(irr_driver->getRTT(RTT_TMP4)), GL_LINEAR, GL_LINEAR, true); setTexture(2, getTextureGLuint(irr_driver->getRTT(RTT_COLOR)), GL_LINEAR, GL_LINEAR, true); glUseProgram(MeshShader::DisplaceShader::Program); @@ -270,6 +280,41 @@ void STKMeshSceneNode::drawSolidPass2(const GLMesh &mesh, ShadedMaterial type) } } +void STKMeshSceneNode::updatevbo() +{ + for (unsigned i = 0; i < Mesh->getMeshBufferCount(); ++i) + { + scene::IMeshBuffer* mb = Mesh->getMeshBuffer(i); + if (!mb) + continue; + GLMesh &mesh = GLmeshes[i]; + glBindVertexArray(0); + + glBindBuffer(GL_ARRAY_BUFFER, mesh.vertex_buffer); + const void* vertices = mb->getVertices(); + const u32 vertexCount = mb->getVertexCount(); + const c8* vbuf = static_cast(vertices); + glBufferData(GL_ARRAY_BUFFER, vertexCount * mesh.Stride, vbuf, GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.index_buffer); + const void* indices = mb->getIndices(); + mesh.IndexCount = mb->getIndexCount(); + GLenum indexSize; + switch (mb->getIndexType()) + { + case irr::video::EIT_16BIT: + indexSize = sizeof(u16); + break; + case irr::video::EIT_32BIT: + indexSize = sizeof(u32); + break; + default: + assert(0 && "Wrong index size"); + } + glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.IndexCount * indexSize, indices, GL_STATIC_DRAW); + } +} + void STKMeshSceneNode::render() { irr::video::IVideoDriver* driver = irr_driver->getVideoDriver(); @@ -277,6 +322,9 @@ void STKMeshSceneNode::render() if (!Mesh || !driver) return; + if (reload_each_frame) + updatevbo(); + bool isTransparentPass = SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT; @@ -297,82 +345,113 @@ void STKMeshSceneNode::render() if (irr_driver->getPhase() == SOLID_NORMAL_AND_DEPTH_PASS) { + if (reload_each_frame) + glDisable(GL_CULL_FACE); computeMVP(ModelViewProjectionMatrix); computeTIMV(TransposeInverseModelView); - glUseProgram(MeshShader::ObjectPass1Shader::Program); + if (!GeometricMesh[FPSM_DEFAULT].empty()) + glUseProgram(MeshShader::ObjectPass1Shader::Program); for (unsigned i = 0; i < GeometricMesh[FPSM_DEFAULT].size(); i++) drawSolidPass1(*GeometricMesh[FPSM_DEFAULT][i], FPSM_DEFAULT); - glUseProgram(MeshShader::ObjectRefPass1Shader::Program); + if (!GeometricMesh[FPSM_ALPHA_REF_TEXTURE].empty()) + glUseProgram(MeshShader::ObjectRefPass1Shader::Program); for (unsigned i = 0; i < GeometricMesh[FPSM_ALPHA_REF_TEXTURE].size(); i++) drawSolidPass1(*GeometricMesh[FPSM_ALPHA_REF_TEXTURE][i], FPSM_ALPHA_REF_TEXTURE); - glUseProgram(MeshShader::NormalMapShader::Program); + if (!GeometricMesh[FPSM_NORMAL_MAP].empty()) + glUseProgram(MeshShader::NormalMapShader::Program); for (unsigned i = 0; i < GeometricMesh[FPSM_NORMAL_MAP].size(); i++) drawSolidPass1(*GeometricMesh[FPSM_NORMAL_MAP][i], FPSM_NORMAL_MAP); - glUseProgram(MeshShader::GrassPass1Shader::Program); + if (!GeometricMesh[FPSM_GRASS].empty()) + glUseProgram(MeshShader::GrassPass1Shader::Program); for (unsigned i = 0; i < GeometricMesh[FPSM_GRASS].size(); i++) drawSolidPass1(*GeometricMesh[FPSM_GRASS][i], FPSM_GRASS); + if (reload_each_frame) + glEnable(GL_CULL_FACE); return; } if (irr_driver->getPhase() == SOLID_LIT_PASS) { - glUseProgram(MeshShader::ObjectPass2Shader::Program); - for (unsigned i = 0; i < ShadedMesh[SM_DEFAULT].size(); i++) - drawSolidPass2(*ShadedMesh[SM_DEFAULT][i], SM_DEFAULT); + if (reload_each_frame) + glDisable(GL_CULL_FACE); - glUseProgram(MeshShader::ObjectRefPass2Shader::Program); - for (unsigned i = 0; i < ShadedMesh[SM_ALPHA_REF_TEXTURE].size(); i++) - drawSolidPass2(*ShadedMesh[SM_ALPHA_REF_TEXTURE][i], SM_ALPHA_REF_TEXTURE); + if (!ShadedMesh[SM_DEFAULT].empty()) + glUseProgram(MeshShader::ObjectPass2Shader::Program); + for (unsigned i = 0; i < ShadedMesh[SM_DEFAULT].size(); i++) + drawSolidPass2(*ShadedMesh[SM_DEFAULT][i], SM_DEFAULT); - glUseProgram(MeshShader::ObjectRimLimitShader::Program); - for (unsigned i = 0; i < ShadedMesh[SM_RIMLIT].size(); i++) - drawSolidPass2(*ShadedMesh[SM_RIMLIT][i], SM_RIMLIT); + if (!ShadedMesh[SM_ALPHA_REF_TEXTURE].empty()) + glUseProgram(MeshShader::ObjectRefPass2Shader::Program); + for (unsigned i = 0; i < ShadedMesh[SM_ALPHA_REF_TEXTURE].size(); i++) + drawSolidPass2(*ShadedMesh[SM_ALPHA_REF_TEXTURE][i], SM_ALPHA_REF_TEXTURE); - glUseProgram(MeshShader::SphereMapShader::Program); - for (unsigned i = 0; i < ShadedMesh[SM_SPHEREMAP].size(); i++) - drawSolidPass2(*ShadedMesh[SM_SPHEREMAP][i], SM_SPHEREMAP); + if (!ShadedMesh[SM_RIMLIT].empty()) + glUseProgram(MeshShader::ObjectRimLimitShader::Program); + for (unsigned i = 0; i < ShadedMesh[SM_RIMLIT].size(); i++) + drawSolidPass2(*ShadedMesh[SM_RIMLIT][i], SM_RIMLIT); - glUseProgram(MeshShader::SplattingShader::Program); - for (unsigned i = 0; i < ShadedMesh[SM_SPLATTING].size(); i++) - drawSolidPass2(*ShadedMesh[SM_SPLATTING][i], SM_SPLATTING); + if (!ShadedMesh[SM_SPHEREMAP].empty()) + glUseProgram(MeshShader::SphereMapShader::Program); + for (unsigned i = 0; i < ShadedMesh[SM_SPHEREMAP].size(); i++) + drawSolidPass2(*ShadedMesh[SM_SPHEREMAP][i], SM_SPHEREMAP); - glUseProgram(MeshShader::GrassPass2Shader::Program); - for (unsigned i = 0; i < ShadedMesh[SM_GRASS].size(); i++) - drawSolidPass2(*ShadedMesh[SM_GRASS][i], SM_GRASS); + if (!ShadedMesh[SM_SPLATTING].empty()) + glUseProgram(MeshShader::SplattingShader::Program); + for (unsigned i = 0; i < ShadedMesh[SM_SPLATTING].size(); i++) + drawSolidPass2(*ShadedMesh[SM_SPLATTING][i], SM_SPLATTING); - glUseProgram(MeshShader::ObjectUnlitShader::Program); - for (unsigned i = 0; i < ShadedMesh[SM_UNLIT].size(); i++) - drawSolidPass2(*ShadedMesh[SM_UNLIT][i], SM_UNLIT); + if (!ShadedMesh[SM_GRASS].empty()) + glUseProgram(MeshShader::GrassPass2Shader::Program); + for (unsigned i = 0; i < ShadedMesh[SM_GRASS].size(); i++) + drawSolidPass2(*ShadedMesh[SM_GRASS][i], SM_GRASS); - glUseProgram(MeshShader::CausticsShader::Program); - for (unsigned i = 0; i < ShadedMesh[SM_CAUSTICS].size(); i++) - drawSolidPass2(*ShadedMesh[SM_CAUSTICS][i], SM_CAUSTICS); + if (!ShadedMesh[SM_UNLIT].empty()) + glUseProgram(MeshShader::ObjectUnlitShader::Program); + for (unsigned i = 0; i < ShadedMesh[SM_UNLIT].size(); i++) + drawSolidPass2(*ShadedMesh[SM_UNLIT][i], SM_UNLIT); - glUseProgram(MeshShader::DetailledObjectPass2Shader::Program); - for (unsigned i = 0; i < ShadedMesh[SM_DETAILS].size(); i++) - drawSolidPass2(*ShadedMesh[SM_DETAILS][i], SM_DETAILS); + if (!ShadedMesh[SM_CAUSTICS].empty()) + glUseProgram(MeshShader::CausticsShader::Program); + for (unsigned i = 0; i < ShadedMesh[SM_CAUSTICS].size(); i++) + drawSolidPass2(*ShadedMesh[SM_CAUSTICS][i], SM_CAUSTICS); - glUseProgram(MeshShader::UntexturedObjectShader::Program); - for (unsigned i = 0; i < ShadedMesh[SM_UNTEXTURED].size(); i++) - drawSolidPass2(*ShadedMesh[SM_UNTEXTURED][i], SM_UNTEXTURED); + if (!ShadedMesh[SM_DETAILS].empty()) + glUseProgram(MeshShader::DetailledObjectPass2Shader::Program); + for (unsigned i = 0; i < ShadedMesh[SM_DETAILS].size(); i++) + drawSolidPass2(*ShadedMesh[SM_DETAILS][i], SM_DETAILS); - return; + if (!ShadedMesh[SM_UNTEXTURED].empty()) + glUseProgram(MeshShader::UntexturedObjectShader::Program); + for (unsigned i = 0; i < ShadedMesh[SM_UNTEXTURED].size(); i++) + drawSolidPass2(*ShadedMesh[SM_UNTEXTURED][i], SM_UNTEXTURED); + + if (reload_each_frame) + glEnable(GL_CULL_FACE); + return; } if (irr_driver->getPhase() == SHADOW_PASS) { - glUseProgram(MeshShader::ShadowShader::Program); + if (reload_each_frame) + glDisable(GL_CULL_FACE); + + if (!GeometricMesh[FPSM_DEFAULT].empty()) + glUseProgram(MeshShader::ShadowShader::Program); for (unsigned i = 0; i < GeometricMesh[FPSM_DEFAULT].size(); i++) drawShadow(*GeometricMesh[FPSM_DEFAULT][i]); - glUseProgram(MeshShader::RefShadowShader::Program); + if (!GeometricMesh[FPSM_ALPHA_REF_TEXTURE].empty()) + glUseProgram(MeshShader::RefShadowShader::Program); for (unsigned i = 0; i < GeometricMesh[FPSM_ALPHA_REF_TEXTURE].size(); i++) drawShadowRef(*GeometricMesh[FPSM_ALPHA_REF_TEXTURE][i]); + + if (reload_each_frame) + glEnable(GL_CULL_FACE); return; } @@ -392,13 +471,25 @@ void STKMeshSceneNode::render() { computeMVP(ModelViewProjectionMatrix); - glUseProgram(MeshShader::BubbleShader::Program); + if (!TransparentMesh[TM_BUBBLE].empty()) + glUseProgram(MeshShader::BubbleShader::Program); for (unsigned i = 0; i < TransparentMesh[TM_BUBBLE].size(); i++) drawBubble(*TransparentMesh[TM_BUBBLE][i], ModelViewProjectionMatrix); - glUseProgram(MeshShader::TransparentShader::Program); - for (unsigned i = 0; i < TransparentMesh[TM_DEFAULT].size(); i++) - drawTransparentObject(*TransparentMesh[TM_DEFAULT][i], ModelViewProjectionMatrix, (*TransparentMesh[TM_DEFAULT][i]).TextureMatrix); + if (World::getWorld()->getTrack()->isFogEnabled()) + { + if (!TransparentMesh[TM_DEFAULT].empty()) + glUseProgram(MeshShader::TransparentFogShader::Program); + for (unsigned i = 0; i < TransparentMesh[TM_DEFAULT].size(); i++) + drawTransparentFogObject(*TransparentMesh[TM_DEFAULT][i], ModelViewProjectionMatrix, (*TransparentMesh[TM_DEFAULT][i]).TextureMatrix); + } + else + { + if (!TransparentMesh[TM_DEFAULT].empty()) + glUseProgram(MeshShader::TransparentShader::Program); + for (unsigned i = 0; i < TransparentMesh[TM_DEFAULT].size(); i++) + drawTransparentObject(*TransparentMesh[TM_DEFAULT][i], ModelViewProjectionMatrix, (*TransparentMesh[TM_DEFAULT][i]).TextureMatrix); + } return; } diff --git a/src/graphics/stkmeshscenenode.hpp b/src/graphics/stkmeshscenenode.hpp index afc628335..fffd04bcf 100644 --- a/src/graphics/stkmeshscenenode.hpp +++ b/src/graphics/stkmeshscenenode.hpp @@ -23,15 +23,17 @@ protected: void createGLMeshes(); void cleanGLMeshes(); void setFirstTimeMaterial(); + void updatevbo(); bool isMaterialInitialized; + bool reload_each_frame; public: + void setReloadEachFrame(bool); STKMeshSceneNode(irr::scene::IMesh* mesh, ISceneNode* parent, irr::scene::ISceneManager* mgr, irr::s32 id, const irr::core::vector3df& position = irr::core::vector3df(0, 0, 0), const irr::core::vector3df& rotation = irr::core::vector3df(0, 0, 0), const irr::core::vector3df& scale = irr::core::vector3df(1.0f, 1.0f, 1.0f)); virtual void render(); virtual void setMesh(irr::scene::IMesh* mesh); - void MovingTexture(unsigned, unsigned); ~STKMeshSceneNode(); }; diff --git a/src/guiengine/CGUISpriteBank.cpp b/src/guiengine/CGUISpriteBank.cpp index a80ca4409..6b0970fa6 100644 --- a/src/guiengine/CGUISpriteBank.cpp +++ b/src/guiengine/CGUISpriteBank.cpp @@ -26,6 +26,7 @@ STKModifiedSpriteBank::STKModifiedSpriteBank(IGUIEnvironment* env) : #endif m_scale = 1.0f; + m_height = 0; if (Environment) { @@ -62,8 +63,8 @@ core::array< core::rect >& STKModifiedSpriteBank::getPositions() for (int n=0; n<(int)Rectangles.size(); n++) { - const int h = (int)(Rectangles[n].getHeight()*m_scale); - const int w = (int)(Rectangles[n].getWidth() *m_scale); + const int h = getScaledHeight(Rectangles[n].getHeight()); + const int w = getScaledWidth(Rectangles[n].getWidth()); copy.push_back( core::rect(Rectangles[n].UpperLeftCorner, core::dimension2d(w,h) ) ); @@ -203,8 +204,8 @@ void STKModifiedSpriteBank::draw2DSprite(u32 index, const core::dimension2d& dim = r.getSize(); core::rect dest( pos, - core::dimension2d((int)(dim.Width*m_scale), - (int)(dim.Height*m_scale)) ); + core::dimension2d(getScaledWidth(dim.Width), + getScaledHeight(dim.Height))); if (center) { dest -= dest.getSize() / 2; @@ -295,6 +296,30 @@ void STKModifiedSpriteBank::draw2DSpriteBatch(const core::array& indices, } } // draw2DSpriteBatch +// ---------------------------------------------------------------------------- +void STKModifiedSpriteBank::scaleToHeight(int height) +{ + m_height = height; +} + +// ---------------------------------------------------------------------------- +s32 STKModifiedSpriteBank::getScaledWidth(s32 width) const +{ + if (m_height == 0) + return (s32)((float)width * m_scale); + else + return m_height; +} + +// ---------------------------------------------------------------------------- +s32 STKModifiedSpriteBank::getScaledHeight(s32 height) const +{ + if (m_height == 0) + return (s32)((float)height * m_scale); + else + return m_height; +} + // ---------------------------------------------------------------------------- } // namespace gui } // namespace irr diff --git a/src/guiengine/CGUISpriteBank.h b/src/guiengine/CGUISpriteBank.h index c388d865b..28a87fc3d 100644 --- a/src/guiengine/CGUISpriteBank.h +++ b/src/guiengine/CGUISpriteBank.h @@ -67,12 +67,15 @@ public: m_scale = scale; } + void scaleToHeight(int height); + protected: // this object was getting access after being freed, I wanna see when/why unsigned int m_magic_number; float m_scale; + int m_height; struct SDrawBatch { @@ -91,6 +94,8 @@ protected: IGUIEnvironment* Environment; video::IVideoDriver* Driver; + s32 getScaledWidth(s32 width) const; + s32 getScaledHeight(s32 height) const; }; } // end namespace gui diff --git a/src/guiengine/engine.cpp b/src/guiengine/engine.cpp index 69ce7b712..f77cb55d1 100644 --- a/src/guiengine/engine.cpp +++ b/src/guiengine/engine.cpp @@ -486,6 +486,14 @@ namespace GUIEngine Used on divs, indicate by how many pixels to pad contents + \n + \subsection prop20 PROP_KEEP_SELECTION + Name in XML files: \c "keep_selection" + + Used on lists, indicates that the list should keep showing the selected item + even when it doesn't have the focus + + \n
\section code Using the engine in code @@ -723,20 +731,6 @@ namespace GUIEngine std::vector gui_messages; - // ------------------------------------------------------------------------ - Screen* getScreenNamed(const char* name) - { - const int screenCount = g_loaded_screens.size(); - for (int n=0; n dest(offsets[n] + core::position2di(0, y_shift), size); - video::SColor colors[] = {color, color, color, color}; - video::ITexture* texture = (fallback[n] ? m_fallback_font->SpriteBank->getTexture(texID) : SpriteBank->getTexture(texID) ); @@ -644,7 +642,6 @@ void ScalableFont::draw(const core::stringw& text, { // draw black border video::SColor black(color.getAlpha(),0,0,0); - video::SColor black_colors[] = {black, black, black, black}; for (int x_delta=-2; x_delta<=2; x_delta++) { @@ -652,10 +649,10 @@ void ScalableFont::draw(const core::stringw& text, { if (x_delta == 0 || y_delta == 0) continue; draw2DImage(texture, - dest + core::position2d(x_delta, y_delta), - source, - clip, - black_colors, true); + dest + core::position2d(x_delta, y_delta), + source, + clip, + black, true); } } } @@ -667,18 +664,18 @@ void ScalableFont::draw(const core::stringw& text, static video::SColor yellow(color.getAlpha(), 255, 220, 15); video::SColor title_colors[] = {yellow, orange, orange, yellow}; draw2DImage(texture, - dest, - source, - clip, - title_colors, true); + dest, + source, + clip, + title_colors, true); } else { draw2DImage(texture, - dest, - source, - clip, - colors, true); + dest, + source, + clip, + color, true); #ifdef FONT_DEBUG driver->draw2DLine(core::position2d(dest.UpperLeftCorner.X, dest.UpperLeftCorner.Y), diff --git a/src/guiengine/screen_loader.cpp b/src/guiengine/screen_loader.cpp index 62d426595..e6c5f0c2a 100644 --- a/src/guiengine/screen_loader.cpp +++ b/src/guiengine/screen_loader.cpp @@ -221,6 +221,7 @@ if(prop_name != NULL) widget.m_properties[prop_flag] = core::stringc(prop_name). READ_PROPERTY(max_rows, PROP_MAX_ROWS); READ_PROPERTY(wrap_around, PROP_WRAP_AROUND); READ_PROPERTY(padding, PROP_DIV_PADDING); + READ_PROPERTY(keep_selection, PROP_KEEP_SELECTION); #undef READ_PROPERTY const wchar_t* text = xml->getAttributeValue( L"text" ); diff --git a/src/guiengine/widget.hpp b/src/guiengine/widget.hpp index 874ca87cd..aa3dd5391 100644 --- a/src/guiengine/widget.hpp +++ b/src/guiengine/widget.hpp @@ -103,7 +103,8 @@ namespace GUIEngine PROP_LABELS_LOCATION, PROP_MAX_ROWS, PROP_WRAP_AROUND, - PROP_DIV_PADDING + PROP_DIV_PADDING, + PROP_KEEP_SELECTION, }; bool isWithinATextBox(); diff --git a/src/guiengine/widgets/list_widget.cpp b/src/guiengine/widgets/list_widget.cpp index 13cd2b7f4..d8ed4da81 100644 --- a/src/guiengine/widgets/list_widget.cpp +++ b/src/guiengine/widgets/list_widget.cpp @@ -299,7 +299,8 @@ void ListWidget::unfocused(const int playerID, Widget* new_focus) CGUISTKListBox* list = getIrrlichtElement(); // remove selection when leaving list - if (list != NULL) list->setSelected(-1); + if (list != NULL && m_properties[PROP_KEEP_SELECTION] != "true") + list->setSelected(-1); } // ----------------------------------------------------------------------------- diff --git a/src/io/file_manager.cpp b/src/io/file_manager.cpp index 2ff762ba2..4df7857ad 100644 --- a/src/io/file_manager.cpp +++ b/src/io/file_manager.cpp @@ -188,10 +188,11 @@ FileManager::FileManager() addRootDirs(root_dir+"../../stk-assets"); if ( getenv ( "SUPERTUXKART_ROOT_PATH" ) != NULL ) addRootDirs(getenv("SUPERTUXKART_ROOT_PATH")); - + checkAndCreateConfigDir(); checkAndCreateAddonsDir(); checkAndCreateScreenshotDir(); + checkAndCreateGPDir(); #ifdef WIN32 redirectOutput(); @@ -203,12 +204,14 @@ FileManager::FileManager() for(unsigned int i=0; i -// Copyright (C) 2008-2013 Steve Baker, Joerg Henrichs -// -// This program 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 3 -// of the License, or (at your option) any later version. -// -// This program 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 this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - -#include "io/file_manager.hpp" - -#include "config/user_config.hpp" -#include "graphics/irr_driver.hpp" -#include "graphics/material_manager.hpp" -#include "karts/kart_properties_manager.hpp" -#include "tracks/track_manager.hpp" -#include "utils/command_line.hpp" -#include "utils/log.hpp" -#include "utils/string_utils.hpp" - -#include - -#include -#include -#include -#include -#include -#include - -// For mkdir -#if !defined(WIN32) -# include -# include -# include -# include -#else -# include -# include -# include -# ifndef __CYGWIN__ - /*Needed by the remove directory function */ -# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) -# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) -# endif -#endif - - -std::vector FileManager::m_root_dirs; - -#ifdef __APPLE__ -// dynamic data path detection onmac -# include - -bool macSetBundlePathIfRelevant(std::string& data_dir) -{ - Log::debug("FileManager", "Checking whether we are using an app bundle... "); - // the following code will enable STK to find its data when placed in an - // app bundle on mac OS X. - // returns true if path is set, returns false if path was not set - char path[1024]; - CFBundleRef main_bundle = CFBundleGetMainBundle(); assert(main_bundle); - CFURLRef main_bundle_URL = CFBundleCopyBundleURL(main_bundle); - assert(main_bundle_URL); - CFStringRef cf_string_ref = CFURLCopyFileSystemPath(main_bundle_URL, - kCFURLPOSIXPathStyle); - assert(cf_string_ref); - CFStringGetCString(cf_string_ref, path, 1024, kCFStringEncodingASCII); - CFRelease(main_bundle_URL); - CFRelease(cf_string_ref); - - std::string contents = std::string(path) + std::string("/Contents"); - if(contents.find(".app") != std::string::npos) - { - Log::debug("FileManager", "yes\n"); - // executable is inside an app bundle, use app bundle-relative paths - data_dir = contents + std::string("/Resources/"); - return true; - } - else - { - Log::debug("FileManager", "no\n"); - return false; - } -} -#endif - -// ============================================================================ -FileManager* file_manager = 0; - -/** With irrlicht the constructor creates a NULL device. This is necessary to - * handle the Chicken/egg problem with irrlicht: access to the file system - * is given from the device, but we can't create the device before reading - * the user_config file (for resolution, fullscreen). So we create a dummy - * device here to begin with, which is then later (once the real device - * exists) changed in reInit(). - * - */ -FileManager::FileManager() -{ - m_subdir_name.resize(ASSET_COUNT); - m_subdir_name[CHALLENGE ] = "challenges"; - m_subdir_name[FONT ] = "fonts"; - m_subdir_name[GFX ] = "gfx"; - m_subdir_name[GRANDPRIX ] = "grandprix"; - m_subdir_name[GUI ] = "gui"; - m_subdir_name[MODEL ] = "models"; - m_subdir_name[MUSIC ] = "music"; - m_subdir_name[TRANSLATION] = "po"; - m_subdir_name[TEXTURE ] = "textures"; - m_subdir_name[SFX ] = "sfx"; - m_subdir_name[SKIN ] = "skins"; - m_subdir_name[SHADER ] = "shaders"; -#ifdef __APPLE__ - // irrLicht's createDevice method has a nasty habit of messing the CWD. - // since the code above may rely on it, save it to be able to restore - // it after. - char buffer[256]; - getcwd(buffer, 256); -#endif - -#ifdef __APPLE__ - chdir( buffer ); -#endif - - m_file_system = irr_driver->getDevice()->getFileSystem(); - m_file_system->grab(); - - irr::io::path exe_path; - - // Search for the root directory - // ============================= - - // Also check for data dirs relative to the path of the executable. - // This is esp. useful for Visual Studio, since it's not necessary - // to define the working directory when debugging, it works automatically. - std::string root_dir; - if(m_file_system->existFile(CommandLine::getExecName().c_str())) - exe_path = m_file_system->getFileDir(CommandLine::getExecName().c_str()); - if(exe_path.size()==0 || exe_path[exe_path.size()-1]!='/') - exe_path += "/"; - if ( getenv ( "SUPERTUXKART_DATADIR" ) != NULL ) - root_dir = std::string(getenv("SUPERTUXKART_DATADIR"))+"/" ; -#ifdef __APPLE__ - else if( macSetBundlePathIfRelevant( root_dir ) ) { root_dir = root_dir + "data/"; } -#endif - else if(m_file_system->existFile("data")) - root_dir = "data/" ; - else if(m_file_system->existFile("../data")) - root_dir = "../data/" ; - else if(m_file_system->existFile(exe_path+"data")) - root_dir = (exe_path+"data/").c_str(); - else if(m_file_system->existFile(exe_path+"/../data")) - { - root_dir = exe_path.c_str(); - root_dir += "/../data/"; - } - else - { -#ifdef SUPERTUXKART_DATADIR - root_dir = SUPERTUXKART_DATADIR; - if(root_dir.size()==0 || root_dir[root_dir.size()-1]!='/') - root_dir+='/'; - -#else - root_dir = "/usr/local/share/games/supertuxkart/"; -#endif - } - - addRootDirs(root_dir); - if( fileExists(root_dir+"../../stk-assets")) - addRootDirs(root_dir+"../../stk-assets"); - if ( getenv ( "SUPERTUXKART_ROOT_PATH" ) != NULL ) - addRootDirs(getenv("SUPERTUXKART_ROOT_PATH")); - - checkAndCreateConfigDir(); - checkAndCreateAddonsDir(); - checkAndCreateScreenshotDir(); - -#ifdef WIN32 - redirectOutput(); - -#endif - - // We can't use _() here, since translations will only be initalised - // after the filemanager (to get the path to the tranlsations from it) - for(unsigned int i=0; i dir_found; - dir_found.resize(ASSET_COUNT, false); - for(unsigned int i=0; idrop(); -} // dropFileSystem - -//----------------------------------------------------------------------------- -/** This function is used to re-initialise the file-manager after reading in - * the user configuration data. -*/ -void FileManager::reInit() -{ - m_file_system = irr_driver->getDevice()->getFileSystem(); - m_file_system->grab(); - - // Note that we can't push the texture search path in the constructor - // since this also adds a file archive to te file system - and - // m_file_system is deleted (in irr_driver) after - pushTextureSearchPath(m_subdir_name[TEXTURE]); - if(fileExists(m_subdir_name[TEXTURE]+"deprecated/")) - pushTextureSearchPath(m_subdir_name[TEXTURE]+"deprecated/"); - - pushTextureSearchPath(m_subdir_name[GUI]); - - - pushModelSearchPath (m_subdir_name[MODEL]); - pushMusicSearchPath (m_subdir_name[MUSIC]); - - // Add more paths from the STK_MUSIC_PATH environment variable - if(getenv("SUPERTUXKART_MUSIC_PATH")!=NULL) - { - std::string path=getenv("SUPERTUXKART_MUSIC_PATH"); - std::vector dirs = StringUtils::splitPath(path); - for(int i=0;i<(int)dirs.size(); i++) - pushMusicSearchPath(dirs[i]); - } -} // reInit - -//----------------------------------------------------------------------------- -FileManager::~FileManager() -{ - // Clean up left-over files in addons/tmp that are older than 24h - // ============================================================== - // (The 24h delay is useful when debugging a problem with a zip file) - std::set allfiles; - std::string tmp=getAddonsFile("tmp"); - listFiles(allfiles, tmp); - for(std::set::iterator i=allfiles.begin(); - i!=allfiles.end(); i++) - { - if((*i)=="." || (*i)=="..") continue; - // For now there should be only zip files or .part files - // (not fully downloaded files) in tmp. Warn about any - // other files. - std::string full_path=tmp+"/"+*i; - if(StringUtils::getExtension(*i)!="zip" && - StringUtils::getExtension(*i)!="part" ) - { - Log::warn("FileManager", "Unexpected tmp file '%s' found.", - full_path.c_str()); - continue; - } - if(isDirectory(full_path)) - { - // Gee, a .zip file which is a directory - stay away from it - Log::warn("FileManager", "'%s' is a directory and will not be deleted.", - full_path.c_str()); - continue; - } - struct stat mystat; - stat(full_path.c_str(), &mystat); - StkTime::TimeType current = StkTime::getTimeSinceEpoch(); - if(current - mystat.st_ctime <24*3600) - { - if(UserConfigParams::logAddons()) - Log::verbose("FileManager", "'%s' is less than 24h old " - "and will not be deleted.", - full_path.c_str()); - continue; - } - if(UserConfigParams::logAddons()) - Log::verbose("FileManager", "Deleting tmp file'%s'.",full_path.c_str()); - removeFile(full_path); - - } // for i in all files in tmp - - // Clean up rest of file manager - // ============================= - popMusicSearchPath(); - popModelSearchPath(); - popTextureSearchPath(); - popTextureSearchPath(); - m_file_system->drop(); - m_file_system = NULL; -} // ~FileManager - -//----------------------------------------------------------------------------- -/** Adds paths to the list of stk root directories. - * \param roots A ":" separated string of directories to add. - */ -void FileManager::addRootDirs(const std::string &roots) -{ - std::vector all = StringUtils::split(roots, ':'); - for(unsigned int i=0; icreateXMLReader(filename.c_str()); -} // getXMLReader -//----------------------------------------------------------------------------- -/** Reads in a XML file and converts it into a XMLNode tree. - * \param filename Name of the XML file to read. - */ -XMLNode *FileManager::createXMLTree(const std::string &filename) -{ - try - { - XMLNode* node = new XMLNode(filename); - return node; - } - catch (std::runtime_error& e) - { - if (UserConfigParams::logMisc()) - { - Log::error("FileManager", "createXMLTree: %s\n", e.what()); - } - return NULL; - } -} // createXMLTree - -//----------------------------------------------------------------------------- -/** Reads in XML from a string and converts it into a XMLNode tree. - * \param content the string containing the XML content. - */ -XMLNode *FileManager::createXMLTreeFromString(const std::string & content) -{ - try - { - char *b = new char[content.size()]; - memcpy(b, content.c_str(), content.size()); - io::IReadFile * ireadfile = m_file_system->createMemoryReadFile(b, strlen(b), "tempfile", true); - io::IXMLReader * reader = m_file_system->createXMLReader(ireadfile); - XMLNode* node = new XMLNode(reader); - reader->drop(); - return node; - } - catch (std::runtime_error& e) - { - if (UserConfigParams::logMisc()) - { - Log::error("FileManager", "createXMLTreeFromString: %s\n", e.what()); - } - return NULL; - } -} // createXMLTreeFromString - -//----------------------------------------------------------------------------- -/** In order to add and later remove paths we have to specify the absolute - * filename (and replace '\' with '/' on windows). - */ -io::path FileManager::createAbsoluteFilename(const std::string &f) -{ - io::path abs_path=m_file_system->getAbsolutePath(f.c_str()); - abs_path=m_file_system->flattenFilename(abs_path); - return abs_path; -} // createAbsoluteFilename - -//----------------------------------------------------------------------------- -/** Adds a model search path to the list of model search paths. - * This path will be searched before any other existing paths. - */ -void FileManager::pushModelSearchPath(const std::string& path) -{ - m_model_search_path.push_back(path); - const int n=m_file_system->getFileArchiveCount(); - m_file_system->addFileArchive(createAbsoluteFilename(path), - /*ignoreCase*/false, - /*ignorePaths*/false, - io::EFAT_FOLDER); - // A later added file archive should be searched first (so that - // track specific models are found before models in data/models). - // This is not necessary if this is the first member, or if the - // addFileArchive call did not add this file systems (this can - // happen if the file archive has been added prevously, which - // commonly happens since each kart/track specific path is added - // twice: once for textures and once for models). - if(n>0 && (int)m_file_system->getFileArchiveCount()>n) - { - // In this case move the just added file archive - // (which has index n) to position 0 (by -n positions): - m_file_system->moveFileArchive(n, -n); - } -} // pushModelSearchPath - -//----------------------------------------------------------------------------- -/** Adds a texture search path to the list of texture search paths. - * This path will be searched before any other existing paths. - */ -void FileManager::pushTextureSearchPath(const std::string& path) -{ - m_texture_search_path.push_back(path); - const int n=m_file_system->getFileArchiveCount(); - m_file_system->addFileArchive(createAbsoluteFilename(path), - /*ignoreCase*/false, - /*ignorePaths*/false, - io::EFAT_FOLDER); - // A later added file archive should be searched first (so that - // e.g. track specific textures are found before textures in - // data/textures). - // This is not necessary if this is the first member, or if the - // addFileArchive call did not add this file systems (this can - // happen if the file archive has been added previously, which - // commonly happens since each kart/track specific path is added - // twice: once for textures and once for models). - if(n>0 && (int)m_file_system->getFileArchiveCount()>n) - { - // In this case move the just added file archive - // (which has index n) to position 0 (by -n positions): - m_file_system->moveFileArchive(n, -n); - } -} // pushTextureSearchPath - -//----------------------------------------------------------------------------- -/** Removes the last added texture search path from the list of paths. - */ -void FileManager::popTextureSearchPath() -{ - std::string dir = m_texture_search_path.back(); - m_texture_search_path.pop_back(); - m_file_system->removeFileArchive(createAbsoluteFilename(dir)); -} // popTextureSearchPath - -//----------------------------------------------------------------------------- -/** Removes the last added model search path from the list of paths. - */ -void FileManager::popModelSearchPath() -{ - std::string dir = m_model_search_path.back(); - m_model_search_path.pop_back(); - m_file_system->removeFileArchive(createAbsoluteFilename(dir)); -} // popModelSearchPath - -//----------------------------------------------------------------------------- -/** Tries to find the specified file in any of the given search paths. - * \param full_path On return contains the full path of the file, or - * "" if the file is not found. - * \param file_name The name of the file to look for. - * \param search_path The list of paths to search for the file. - * \return True if the file is found, false otherwise. - */ -bool FileManager::findFile(std::string& full_path, - const std::string& file_name, - const std::vector& search_path) const -{ - for(std::vector::const_reverse_iterator - i = search_path.rbegin(); - i != search_path.rend(); ++i) - { - full_path = *i + file_name; - if(m_file_system->existFile(full_path.c_str())) return true; - } - full_path=""; - return false; -} // findFile - -//----------------------------------------------------------------------------- -std::string FileManager::getAssetChecked(FileManager::AssetType type, - const std::string& name, - bool abort_on_error) const -{ - std::string path = m_subdir_name[type]+name; - if(fileExists(path)) - return path; - - if(abort_on_error) - { - Log::fatal("FileManager", "Can not find file '%s' in '%s'", - name.c_str(), m_subdir_name[type].c_str()); - } - return ""; -} // getAssetChecked - -//----------------------------------------------------------------------------- -/** Returns the full path of a file of the given asset class. It is not - * checked if the file actually exists (use getAssetChecked() instead if - * checking is needed). - * \param type Type of the asset class. - * \param name Name of the file to search. - * \return Full path to the file. - */ -std::string FileManager::getAsset(FileManager::AssetType type, - const std::string &name) const -{ - return m_subdir_name[type]+name; -} // getAsset - -//----------------------------------------------------------------------------- -/** Searches in all root directories for the specified file. - * \param name Name of the file to find. - * \return Full path of the file, or "" if not found. - */ -std::string FileManager::getAsset(const std::string &name) const -{ - std::string path; - findFile(path, name, m_root_dirs); - return path; -} // getAsset - -//----------------------------------------------------------------------------- -/** Returns the directory in which screenshots should be stored. - */ -std::string FileManager::getScreenshotDir() const -{ - return m_screenshot_dir; -} // getScreenshotDir - -//----------------------------------------------------------------------------- -/** Returns the full path of a texture file name by searching in all - * directories currently in the texture search path. The difference to - * a call getAsset(TEXTURE,...) is that the latter will only return - * textures from .../textures, while the searchTexture will also - * search e.g. in kart or track directories (depending on what is currently - * being loaded). - * \param file_name Name of the texture file to search. - * \return The full path for the texture, or "" if the texture was not found. - */ -std::string FileManager::searchTexture(const std::string& file_name) const -{ - std::string path; - findFile(path, file_name, m_texture_search_path); - return path; -} // searchTexture - -//----------------------------------------------------------------------------- -/** Returns the list of all directories in which music files are searched. - */ -std::vector FileManager::getMusicDirs() const -{ - return m_music_search_path; -} // getMusicDirs - -//----------------------------------------------------------------------------- -/** If the directory specified in path does not exist, it is created. This - * function does not support recursive operations, so if a directory "a/b" - * is tested, and "a" does not exist, this function will fail. - * \params path Directory to test. - * \return True if the directory exists or could be created, - * false otherwise. - */ -bool FileManager::checkAndCreateDirectory(const std::string &path) -{ - // irrlicht apparently returns true for files and directory - // (using access/_access internally): - if(m_file_system->existFile(io::path(path.c_str()))) - return true; - - Log::info("FileManager", "Creating directory '%s'.", path.c_str()); - - // Otherwise try to create the directory: -#if defined(WIN32) && !defined(__CYGWIN__) - bool error = _mkdir(path.c_str()) != 0; -#else - bool error = mkdir(path.c_str(), 0755) != 0; -#endif - return !error; -} // checkAndCreateDirectory - -//----------------------------------------------------------------------------- -/** If the directory specified in path does not exist, it is created - * recursively (mkdir -p style). - * \params path Directory to test. - * \return True if the directory exists or could be created, false otherwise. - */ -bool FileManager::checkAndCreateDirectoryP(const std::string &path) -{ - // irrlicht apparently returns true for files and directory - // (using access/_access internally): - if(m_file_system->existFile(io::path(path.c_str()))) - return true; - - std::cout << "[FileManager] Creating directory(ies) '" << path << "'.\n"; - - std::vector split = StringUtils::split(path,'/'); - std::string current_path = ""; - for (unsigned int i=0; iexistFile(io::path(current_path.c_str()))) - { - if (!checkAndCreateDirectory(current_path)) - { - Log::error("FileManager", "Can't create dir '%s'", - current_path.c_str()); - break; - } - } - } - bool error = checkAndCreateDirectory(path); - - return error; -} // checkAndCreateDirectory - -//----------------------------------------------------------------------------- -/** Checks if the config directory exists, and it not, tries to create it. - * It will set m_user_config_dir to the path to which user-specific config - * files are stored. - */ -void FileManager::checkAndCreateConfigDir() -{ - if(getenv("SUPERTUXKART_SAVEDIR") && - checkAndCreateDirectory(getenv("SUPERTUXKART_SAVEDIR")) ) - { - m_user_config_dir = getenv("SUPERTUXKART_SAVEDIR"); - } - else - { - -#if defined(WIN32) || defined(__CYGWIN__) - - // Try to use the APPDATA directory to store config files and highscore - // lists. If not defined, used the current directory. - if(getenv("APPDATA")!=NULL) - { - m_user_config_dir = getenv("APPDATA"); - if(!checkAndCreateDirectory(m_user_config_dir)) - { - std::cerr << "[FileManager] Can't create config dir '" - << m_user_config_dir << "', falling back to '.'.\n"; - m_user_config_dir = "."; - } - } - else - m_user_config_dir = "."; - - m_user_config_dir += "/supertuxkart"; - -#elif defined(__APPLE__) - - if (getenv("HOME")!=NULL) - { - m_user_config_dir = getenv("HOME"); - } - else - { - std::cerr << - "[FileManager] No home directory, this should NOT happen!\n"; - // Fall back to system-wide app data (rather than - // user-specific data), but should not happen anyway. - m_user_config_dir = ""; - } - m_user_config_dir += "/Library/Application Support/"; - const std::string CONFIGDIR("SuperTuxKart"); - m_user_config_dir += CONFIGDIR; - -#else - - // Remaining unix variants. Use the new standards for config directory - // i.e. either XDG_CONFIG_HOME or $HOME/.config - if (getenv("XDG_CONFIG_HOME")!=NULL){ - m_user_config_dir = getenv("XDG_CONFIG_HOME"); - } - else if (!getenv("HOME")) - { - std::cerr - << "[FileManager] No home directory, this should NOT happen " - << "- trying '.' for config files!\n"; - m_user_config_dir = "."; - } - else - { - m_user_config_dir = getenv("HOME"); - m_user_config_dir += "/.config"; - if(!checkAndCreateDirectory(m_user_config_dir)) - { - // If $HOME/.config can not be created: - std::cerr << "[FileManager] Cannot create directory '" - << m_user_config_dir <<"', falling back to use '" - << getenv("HOME")<< "'.\n"; - m_user_config_dir = getenv("HOME"); - } - } - m_user_config_dir += "/supertuxkart"; - -#endif - - } // if(getenv("SUPERTUXKART_SAVEDIR") && checkAndCreateDirectory(...)) - - if(m_user_config_dir.size()>0 && *m_user_config_dir.rbegin()!='/') - m_user_config_dir += "/"; - - if(!checkAndCreateDirectory(m_user_config_dir)) - { - Log::warn("FileManager", "Can not create config dir '%s', " - "falling back to '.'.", m_user_config_dir.c_str()); - m_user_config_dir = "./"; - } - return; -} // checkAndCreateConfigDir - -// ---------------------------------------------------------------------------- -/** Creates the directories for the addons data. This will set m_addons_dir - * with the appropriate path, and also create the subdirectories in this - * directory. - */ -void FileManager::checkAndCreateAddonsDir() -{ -#if defined(WIN32) || defined(__CYGWIN__) - m_addons_dir = m_user_config_dir+"addons/"; -#elif defined(__APPLE__) - m_addons_dir = getenv("HOME"); - m_addons_dir += "/Library/Application Support/SuperTuxKart/Addons/"; -#else - m_addons_dir = checkAndCreateLinuxDir("XDG_DATA_HOME", "supertuxkart", - ".local/share", ".stkaddons"); - m_addons_dir += "addons/"; -#endif - - if(!checkAndCreateDirectory(m_addons_dir)) - { - Log::error("FileManager", "Can not create add-ons dir '%s', " - "falling back to '.'.", m_addons_dir.c_str()); - m_addons_dir = "./"; - } - - if (!checkAndCreateDirectory(m_addons_dir + "icons/")) - { - Log::error("FileManager", "Failed to create add-ons icon dir at '%s'.", - (m_addons_dir + "icons/").c_str()); - } - if (!checkAndCreateDirectory(m_addons_dir + "tmp/")) - { - Log::error("FileManager", "Failed to create add-ons tmp dir at '%s'.", - (m_addons_dir + "tmp/").c_str()); - } - -} // checkAndCreateAddonsDir - -// ---------------------------------------------------------------------------- -/** Creates the directories for screenshots. This will set m_screenshot_dir - * with the appropriate path. - */ -void FileManager::checkAndCreateScreenshotDir() -{ -#if defined(WIN32) || defined(__CYGWIN__) - m_screenshot_dir = m_user_config_dir+"screenshots/"; -#elif defined(__APPLE__) - m_screenshot_dir = getenv("HOME"); - m_screenshot_dir += "/Library/Application Support/SuperTuxKart/Screenshots/"; -#else - m_screenshot_dir = checkAndCreateLinuxDir("XDG_CACHE_HOME", "supertuxkart", ".cache/", "."); - m_screenshot_dir += "screenshots/"; -#endif - - if(!checkAndCreateDirectory(m_screenshot_dir)) - { - Log::error("FileManager", "Can not create screenshot directory '%s', " - "falling back to '.'.", m_screenshot_dir.c_str()); - m_screenshot_dir = "."; - } - -} // checkAndCreateScreenshotDir - -// ---------------------------------------------------------------------------- -#if !defined(WIN32) && !defined(__CYGWIN__) && !defined(__APPLE__) - -/** Find a directory to use for remaining unix variants. Use the new standards - * for config directory based on XDG_* environment variables, or a - * subdirectory under $HOME, trying two different fallbacks. It will also - * check if the directory 'dirname' can be created (to avoid problems that - * e.g. $env_name is '/', which exists, but can not be written to. - * \param env_name Name of the environment variable to test first. - * \param dir_name Name of the directory to create - * \param fallback1 Subdirectory under $HOME to use if the environment - * variable is not defined or can not be created. - * \param fallback2 Subdirectory under $HOME to use if the environment - * variable and fallback1 are not defined or can not be created. - */ -std::string FileManager::checkAndCreateLinuxDir(const char *env_name, - const char *dir_name, - const char *fallback1, - const char *fallback2) -{ - bool dir_ok = false; - std::string dir; - - if (getenv(env_name)!=NULL) - { - dir = getenv(env_name); - dir_ok = checkAndCreateDirectory(dir); - if(!dir_ok) - Log::warn("FileManager", "Cannot create $%s.", env_name); - - if(dir[dir.size()-1]!='/') dir += "/"; - // Do an additional test here, e.g. in case that XDG_DATA_HOME is '/' - // and since dir_ok is set, it would not test any of the other options - // like $HOME/.local/share - dir_ok = checkAndCreateDirectory(dir+dir_name); - if(!dir_ok) - Log::warn("FileManager", "Cannot create $%s/%s.", dir.c_str(), - dir_name); - } - - if(!dir_ok && getenv("HOME")) - { - // Use ~/.local/share : - dir = getenv("HOME"); - if(dir.size()>0 && dir[dir.size()-1]!='/') dir += "/"; - dir += fallback1; - // This will create each individual subdirectory if - // dir_name contains "/". - dir_ok = checkAndCreateDirectoryP(dir); - if(!dir_ok) - Log::warn("FileManager", "Cannot create $HOME/%s.", - fallback1); - } - if(!dir_ok && fallback2 && getenv("HOME")) - { - dir = getenv("HOME"); - if(dir.size()>0 && dir[dir.size()-1]!='/') dir += "/"; - dir += fallback2; - dir_ok = checkAndCreateDirectory(dir); - if(!dir_ok) - Log::warn("FileManager", "Cannot create $HOME/%s.", - fallback2); - } - - if(!dir_ok) - { - Log::warn("FileManager", "Falling back to use '.'."); - dir = "./"; - } - - if(dir.size()>0 && dir[dir.size()-1]!='/') dir += "/"; - dir += dir_name; - dir_ok = checkAndCreateDirectory(dir); - if(!dir_ok) - { - // If the directory can not be created - Log::error("FileManager", "Cannot create directory '%s', " - "falling back to use '.'.", dir.c_str()); - dir="./"; - } - if(dir.size()>0 && dir[dir.size()-1]!='/') dir += "/"; - return dir; -} // checkAndCreateLinuxDir -#endif - -//----------------------------------------------------------------------------- -/** Redirects output to go into files in the user's config directory - * instead of to the console. - */ -void FileManager::redirectOutput() -{ - //Enable logging of stdout and stderr to logfile - std::string logoutfile = getUserConfigFile("stdout.log"); - Log::verbose("main", "Error messages and other text output will " - "be logged to %s.", logoutfile.c_str()); - Log::openOutputFiles(logoutfile); -} // redirectOutput - -//----------------------------------------------------------------------------- -/** Returns the directory for addon files. */ -const std::string &FileManager::getAddonsDir() const -{ - return m_addons_dir; -} // getAddonsDir - -//----------------------------------------------------------------------------- -/** Returns the full path of a file in the addons directory. - * \param name Name of the file. - */ -std::string FileManager::getAddonsFile(const std::string &name) -{ - return getAddonsDir()+name; -} // getAddonsFile - -//----------------------------------------------------------------------------- -/** Returns the full path of the config directory. - */ -std::string FileManager::getUserConfigFile(const std::string &fname) const -{ - return m_user_config_dir+fname; -} // getUserConfigFile - -//----------------------------------------------------------------------------- -/** Returns the full path of a music file by searching all music search paths. - * It throws an exception if the file is not found. - * \param file_name File name to search for. - */ -std::string FileManager::searchMusic(const std::string& file_name) const -{ - std::string path; - bool success = findFile(path, file_name, m_music_search_path); - if(!success) - { - // If a music file is not found in any of the music search paths - // check all root dirs. This is used by stk_config to load the - // title music before any music search path is defined) - path = getAsset(MUSIC, file_name); - success = fileExists(path); - } - if (!success) - { - throw std::runtime_error( - "[FileManager::getMusicFile] Cannot find music file '" - +file_name+"'."); - } - return path; -} // searchMusic - -//----------------------------------------------------------------------------- -/** Returns true if the given name is a directory. - * \param path File name to test. - */ -bool FileManager::isDirectory(const std::string &path) const -{ - struct stat mystat; - std::string s(path); - // At least on windows stat returns an error if there is - // a '/' at the end of the path. - if(s[s.size()-1]=='/') - s.erase(s.end()-1, s.end()); - if(stat(s.c_str(), &mystat) < 0) return false; - return S_ISDIR(mystat.st_mode); -} // isDirectory - -//----------------------------------------------------------------------------- -/** Returns a list of files in a given directory. - * \param result A reference to a std::vector which will - * hold all files in a directory. The vector will be cleared. - * \param dir The director for which to get the directory listing. - * \param make_full_path If set to true, all listed files will be full paths. - */ -void FileManager::listFiles(std::set& result, - const std::string& dir, - bool make_full_path) const -{ - result.clear(); - -#ifndef ANDROID - if(!isDirectory(dir)) - return; -#endif - - io::path previous_cwd = m_file_system->getWorkingDirectory(); - - if(!m_file_system->changeWorkingDirectoryTo( dir.c_str() )) - { - Log::error("FileManager", "listFiles : Could not change CWD!\n"); - return; - } - irr::io::IFileList* files = m_file_system->createFileList(); - - for(int n=0; n<(int)files->getFileCount(); n++) - { - result.insert(make_full_path ? dir+"/"+ files->getFileName(n).c_str() - : files->getFileName(n).c_str() ); - } - - m_file_system->changeWorkingDirectoryTo( previous_cwd ); - files->drop(); -} // listFiles - -//----------------------------------------------------------------------------- -/** Creates a directory for an addon. - * \param addons_name Name of the directory to create. - * \param addons_type The type, which is used as a subdirectory. E.g.: - * 'karts' (m_addons_dir/karts/name will be created). - */ -void FileManager::checkAndCreateDirForAddons(const std::string &dir) -{ - // Tries to create directory recursively - bool success = checkAndCreateDirectoryP(dir); - if(!success) - { - Log::warn("FileManager", "There is a problem with the addons dir."); - return; - } -} // checkAndCreateDirForAddons - -// ---------------------------------------------------------------------------- -/** Removes the specified file, returns true if successful, or false - * if the file is not a regular file or can not be removed. - */ -bool FileManager::removeFile(const std::string &name) const -{ - struct stat mystat; - if(stat(name.c_str(), &mystat) < 0) return false; - if( S_ISREG(mystat.st_mode)) - return remove(name.c_str())==0; - return false; -} // removeFile - -// ---------------------------------------------------------------------------- -/** Removes a directory (including all files contained). The function could - * easily recursively delete further subdirectories, but this is commented - * out atm (to limit the amount of damage in case of a bug). - * \param name Directory name to remove. - * \param return True if removal was successful. - */ -bool FileManager::removeDirectory(const std::string &name) const -{ - std::set files; - listFiles(files, name, /*is full path*/ true); - for(std::set::iterator i=files.begin(); i!=files.end(); i++) - { - if((*i)=="." || (*i)=="..") continue; - if(UserConfigParams::logMisc()) - Log::verbose("FileManager", "Deleting directory '%s'.", - (*i).c_str()); - if(isDirectory(*i)) - { - // This should not be necessary (since this function is only - // used to remove addons), and it limits the damage in case - // of any bugs - i.e. if name should be "/" or so. - // removeDirectory(full_path); - } - else - { - removeFile(*i); - } - } -#if defined(WIN32) - return RemoveDirectory(name.c_str())==TRUE; -#else - return remove(name.c_str())==0; -#endif -} // remove directory - diff --git a/src/io/file_manager.hpp b/src/io/file_manager.hpp index 4037cb464..abeba8084 100644 --- a/src/io/file_manager.hpp +++ b/src/io/file_manager.hpp @@ -46,10 +46,10 @@ class FileManager : public NoCopy public: /** The various asset types (and directories) STK might request. * The last entry ASSET_COUNT specifies the number of entries. */ - enum AssetType {ASSET_MIN, - CHALLENGE=ASSET_MIN, + enum AssetType {ASSET_MIN, + CHALLENGE=ASSET_MIN, FONT, GFX, GRANDPRIX, GUI, MODEL, MUSIC, - SFX, SHADER, SKIN, TEXTURE, TRANSLATION, + SFX, SHADER, SKIN, TEXTURE, TRANSLATION, ASSET_MAX = TRANSLATION, ASSET_COUNT}; private: @@ -72,6 +72,9 @@ private: /** Directory to store screenshots in. */ std::string m_screenshot_dir; + /** Directory where user-defined grand prix are stored. */ + std::string m_gp_dir; + std::vector m_texture_search_path, m_model_search_path, @@ -88,6 +91,7 @@ private: bool isDirectory(const std::string &path) const; void checkAndCreateAddonsDir(); void checkAndCreateScreenshotDir(); + void checkAndCreateGPDir(); #if !defined(WIN32) && !defined(__CYGWIN__) && !defined(__APPLE__) std::string checkAndCreateLinuxDir(const char *env_name, const char *dir_name, @@ -106,6 +110,7 @@ public: XMLNode *createXMLTreeFromString(const std::string & content); std::string getScreenshotDir() const; + std::string getGPDir() const; bool checkAndCreateDirectoryP(const std::string &path); const std::string &getAddonsDir() const; std::string getAddonsFile(const std::string &name); diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index c86cc85cd..201047dcd 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -18,6 +18,9 @@ #include "items/powerup.hpp" +#include "achievements/achievement_info.hpp" +#include "config/player_manager.hpp" + #include "audio/sfx_base.hpp" #include "audio/sfx_manager.hpp" #include "config/stk_config.hpp" @@ -170,6 +173,14 @@ void Powerup::adjustSound() */ void Powerup::use() { + // The player gets an achievement point for using a powerup + StateManager::ActivePlayer * player = m_owner->getController()->getPlayer(); + if (m_type != PowerupManager::POWERUP_NOTHING && + player != NULL && player->getConstProfile() == PlayerManager::get()->getCurrentPlayer()) + { + PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_POWERUP_LOVER, "poweruplover"); + } + // Play custom kart sound when collectible is used //TODO: what about the bubble gum? if (m_type != PowerupManager::POWERUP_NOTHING && m_type != PowerupManager::POWERUP_SWATTER && @@ -237,7 +248,7 @@ void Powerup::use() m_sound_use->play(); pos.setY(hit_point.getY()-0.05f); - + ItemManager::get()->newItem(Item::ITEM_BUBBLEGUM, pos, normal, m_owner); } else // if the kart is looking forward, use the bubblegum as a shield diff --git a/src/items/rubber_band.cpp b/src/items/rubber_band.cpp index fc4a9beec..d978c3926 100644 --- a/src/items/rubber_band.cpp +++ b/src/items/rubber_band.cpp @@ -22,6 +22,7 @@ #include "graphics/irr_driver.hpp" #include "graphics/material_manager.hpp" +#include "graphics/stkmeshscenenode.hpp" #include "items/plunger.hpp" #include "items/projectile_manager.hpp" #include "karts/abstract_kart.hpp" @@ -71,6 +72,8 @@ RubberBand::RubberBand(Plunger *plunger, AbstractKart *kart) updatePosition(); m_node = irr_driver->addMesh(m_mesh); irr_driver->applyObjectPassShader(m_node); + if (STKMeshSceneNode *stkm = dynamic_cast(m_node)) + stkm->setReloadEachFrame(true); #ifdef DEBUG std::string debug_name = m_owner->getIdent()+" (rubber-band)"; m_node->setName(debug_name.c_str()); diff --git a/src/karts/kart_properties.cpp b/src/karts/kart_properties.cpp index 8cd676fe1..670d10617 100644 --- a/src/karts/kart_properties.cpp +++ b/src/karts/kart_properties.cpp @@ -560,9 +560,17 @@ void KartProperties::getAllData(const XMLNode * root) else if (s == "small") m_engine_sfx_type = "engine_small"; else { - Log::warn("[KartProperties]", "Kart '%s' has invalid engine '%s'.", - m_name.c_str(), s.c_str()); - m_engine_sfx_type = "engine_small"; + if (sfx_manager->soundExist(s)) + { + m_engine_sfx_type = s; + } + else + { + Log::error("[KartProperties]", + "Kart '%s' has an invalid engine '%s'.", + m_name.c_str(), s.c_str()); + m_engine_sfx_type = "engine_small"; + } } #ifdef WILL_BE_ENABLED_ONCE_DONE_PROPERLY diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index 865e995be..af33e6a6e 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -17,6 +17,8 @@ #include "modes/linear_world.hpp" +#include "achievements/achievements_manager.hpp" +#include "config/player_manager.hpp" #include "audio/music_manager.hpp" #include "audio/sfx_base.hpp" #include "audio/sfx_manager.hpp" @@ -236,6 +238,14 @@ void LinearWorld::newLap(unsigned int kart_index) KartInfo &kart_info = m_kart_info[kart_index]; AbstractKart *kart = m_karts[kart_index]; + // Reset reset-after-lap achievements + StateManager::ActivePlayer *c = kart->getController()->getPlayer(); + PlayerProfile *p = PlayerManager::get()->getCurrentPlayer(); + if (c && c->getConstProfile() == p) + { + p->getAchievementsStatus()->onLapEnd(); + } + // Only update the kart controller if a kart that has already finished // the race crosses the start line again. This avoids 'fastest lap' // messages if the end controller does a fastest lap, but especially diff --git a/src/modes/world.cpp b/src/modes/world.cpp index e90b89dbb..d6db91624 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -445,6 +445,7 @@ void World::terminateRace() &best_player); } + // Check achievements PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_COLUMBUS, getTrack()->getIdent(), 1); if (raceHasLaps()) @@ -452,6 +453,64 @@ void World::terminateRace() PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_MARATHONER, "laps", race_manager->getNumLaps()); } + + Achievement *achiev = PlayerManager::getCurrentAchievementsStatus()->getAchievement(AchievementInfo::ACHIEVE_GOLD_DRIVER); + if (achiev) + { + std::string mode_name = getIdent(); // Get the race mode name + int winner_position = 1; + int opponents = achiev->getInfo()->getGoalValue("opponents"); // Get the required opponents number + if (mode_name == IDENT_FTL) + { + winner_position = 2; + opponents++; + } + for(unsigned int i = 0; i < kart_amount; i++) + { + // Retrieve the current player + StateManager::ActivePlayer* p = m_karts[i]->getController()->getPlayer(); + if (p && p->getConstProfile() == PlayerManager::get()->getCurrentPlayer()) + { + // Check if the player has won + if (m_karts[i]->getPosition() == winner_position && kart_amount > opponents ) + { + // Update the achievement + mode_name = StringUtils::toLowerCase(mode_name); + if (achiev->getValue("opponents") <= 0) + PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_GOLD_DRIVER, + "opponents", opponents); + PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_GOLD_DRIVER, + mode_name, 1); + } + } + } // for i < kart_amount + } // if (achiev) + + Achievement *win = PlayerManager::getCurrentAchievementsStatus()->getAchievement(AchievementInfo::ACHIEVE_UNSTOPPABLE); + //if achivement has been unlocked + if (win->getValue("wins") < 5 ) + { + for(unsigned int i = 0; i < kart_amount; i++) + { + // Retrieve the current player + StateManager::ActivePlayer* p = m_karts[i]->getController()->getPlayer(); + if (p && p->getConstProfile() == PlayerManager::get()->getCurrentPlayer()) + { + // Check if the player has won + if (m_karts[i]->getPosition() == 1 ) + { + // Increase number of consecutive wins + PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_UNSTOPPABLE, + "wins", 1); + } + else + { + //Set number of consecutive wins to 0 + win->reset(); + } + } + } + } PlayerManager::get()->getCurrentPlayer()->raceFinished(); if (m_race_gui) m_race_gui->clearAllMessages(); diff --git a/src/race/grand_prix_data.cpp b/src/race/grand_prix_data.cpp index 2a62811eb..73d9d9cfe 100644 --- a/src/race/grand_prix_data.cpp +++ b/src/race/grand_prix_data.cpp @@ -23,37 +23,62 @@ #include "challenges/unlock_manager.hpp" #include "config/player_manager.hpp" #include "io/file_manager.hpp" +#include "io/utf_writer.hpp" #include "tracks/track_manager.hpp" #include "tracks/track.hpp" #include "utils/string_utils.hpp" -#include "utils/translation.hpp" #include +#include +#include #include -GrandPrixData::GrandPrixData(const std::string filename) -{ - load_from_file(file_manager->getAsset(FileManager::GRANDPRIX, filename), - filename); -} + // ---------------------------------------------------------------------------- -GrandPrixData::GrandPrixData(const std::string dir, const std::string filename) -{ - assert(dir[dir.size() - 1] == '/'); - load_from_file(dir + filename, filename); -} -// ---------------------------------------------------------------------------- -void GrandPrixData::load_from_file(const std::string fullpath, - const std::string filename) +GrandPrixData::GrandPrixData(const std::string& filename) throw(std::logic_error) { m_filename = filename; m_id = StringUtils::getBasename(StringUtils::removeExtension(filename)); + m_editable = (filename.find(file_manager->getGPDir(), 0) == 0); + reload(); +} - XMLNode * root = file_manager->createXMLTree(fullpath); - if (!root) +// ---------------------------------------------------------------------------- +void GrandPrixData::setId(const std::string& id) +{ + m_id = id; +} + +// ---------------------------------------------------------------------------- +void GrandPrixData::setName(const irr::core::stringw& name) +{ + m_name = name; +} + +// ---------------------------------------------------------------------------- +void GrandPrixData::setFilename(const std::string& filename) +{ + m_filename = filename; +} + +// ---------------------------------------------------------------------------- +void GrandPrixData::setEditable(const bool editable) +{ + m_editable = editable; +} + +// ---------------------------------------------------------------------------- +void GrandPrixData::reload() +{ + m_tracks.clear(); + m_laps.clear(); + m_reversed.clear(); + + std::auto_ptr root(file_manager->createXMLTree(m_filename)); + if (root.get() == NULL) { - Log::error("GrandPrixData","Error while trying to read grandprix file " - "'%s'", fullpath.c_str()); + Log::error("GrandPrixData","Error while trying to read grandprix file '%s'", + m_filename.c_str()); throw std::logic_error("File not found"); } @@ -61,24 +86,18 @@ void GrandPrixData::load_from_file(const std::string fullpath, if (root->getName() == "supertuxkart_grand_prix") { - std::string temp_name; - if (root->get("name", &temp_name) == 0) + if (root->get("name", &m_name) == 0) { - Log::error("GrandPrixData", "Error while trying to read grandprix " - "file '%s' : missing 'name' attribute\n", - fullpath.c_str()); - delete root; + Log::error("GrandPrixData", "Error while trying to read grandprix file '%s' : " + "missing 'name' attribute\n", m_filename.c_str()); throw std::logic_error("File contents are incomplete or corrupt"); } - m_name = temp_name.c_str(); foundName = true; } else { - Log::error("GrandPrixData", "Error while trying to read grandprix file " - "'%s' : Root node has an unexpected name\n", - fullpath.c_str()); - delete root; + Log::error("GrandPrixData", "Error while trying to read grandprix file '%s' : " + "Root node has an unexpected name\n", m_filename.c_str()); throw std::logic_error("File contents are incomplete or corrupt"); } @@ -95,20 +114,17 @@ void GrandPrixData::load_from_file(const std::string fullpath, int numLaps; bool reversed = false; - const int idFound = node->get("id", &trackID); - const int lapFound = node->get("laps", &numLaps); + const int idFound = node->get("id", &trackID ); + const int lapFound = node->get("laps", &numLaps ); // Will stay false if not found node->get("reverse", &reversed ); if (!idFound || !lapFound) { - Log::error("GrandPrixData", "Error while trying to read " - "grandprix file '%s' : tag does not have " - "idi and laps reverse attributes. \n", - fullpath.c_str()); - delete root; - throw std::logic_error("File contents are incomplete or " - "corrupt"); + Log::error("GrandPrixData", "Error while trying to read grandprix file '%s' : " + " tag does not have idi and laps reverse attributes. \n", + m_filename.c_str()); + throw std::logic_error("File contents are incomplete or corrupt"); } // Make sure the track really is reversible @@ -127,27 +143,58 @@ void GrandPrixData::load_from_file(const std::string fullpath, } else { - Log::error("Unknown node in Grand Prix XML file: %s/n", - node->getName().c_str()); - delete root; + std::cerr << "Unknown node in Grand Prix XML file : " << node->getName().c_str() << std::endl; throw std::runtime_error("Unknown node in sfx XML file"); } - }// end for - - delete root; + }// nend for // sanity checks if (!foundName) { - Log::error("GrandPrixData", "Error while trying to read grandprix file " - "'%s' : missing 'name' attribute\n", fullpath.c_str()); + Log::error("GrandPrixData", "Error while trying to read grandprix file '%s' : " + "missing 'name' attribute\n", m_filename.c_str()); throw std::logic_error("File contents are incomplete or corrupt"); } } + +// ---------------------------------------------------------------------------- +bool GrandPrixData::writeToFile() +{ + try + { + UTFWriter file(m_filename.c_str()); + if (file.is_open()) + { + file << L"\n\n\n"; + for (unsigned int i = 0; i < m_tracks.size(); i++) + { + file << + L"\t\n"; + } + file << L"\n\n"; + + file.close(); + + return true; + } + + return false; + } + catch (std::runtime_error& e) + { + Log::error("GrandPrixData", "Failed to write '%s'; cause: %s\n", + m_filename.c_str(), e.what()); + return false; + } +} + // ---------------------------------------------------------------------------- bool GrandPrixData::checkConsistency(bool chatty) const { - for(unsigned int i=0; igetTrack(m_tracks[i]); @@ -166,6 +213,7 @@ bool GrandPrixData::checkConsistency(bool chatty) const return true; } // checkConsistency + // ---------------------------------------------------------------------------- /** Returns true if the track is available. This is used to test if Fort Magma * is available (this way FortMagma is not used in the last Grand Prix in @@ -182,7 +230,7 @@ bool GrandPrixData::isTrackAvailable(const std::string &id) const void GrandPrixData::getLaps(std::vector *laps) const { laps->clear(); - for(unsigned int i=0; i< m_tracks.size(); i++) + for (unsigned int i = 0; i< m_tracks.size(); i++) if(isTrackAvailable(m_tracks[i])) laps->push_back(m_laps[i]); } // getLaps @@ -191,16 +239,129 @@ void GrandPrixData::getLaps(std::vector *laps) const void GrandPrixData::getReverse(std::vector *reverse) const { reverse->clear(); - for(unsigned int i=0; i< m_tracks.size(); i++) + for (unsigned int i = 0; i< m_tracks.size(); i++) if(isTrackAvailable(m_tracks[i])) reverse->push_back(m_reversed[i]); } // getReverse +// ---------------------------------------------------------------------------- +bool GrandPrixData::isEditable() const +{ + return m_editable; +} // isEditable + +// ---------------------------------------------------------------------------- +unsigned int GrandPrixData::getNumberOfTracks() const +{ + return m_tracks.size(); +} + +// ---------------------------------------------------------------------------- +irr::core::stringw GrandPrixData::getTrackName(const unsigned int track) const +{ + assert(track < getNumberOfTracks()); + Track* t = track_manager->getTrack(m_tracks[track]); + assert(t != NULL); + return t->getName(); +} + +// ---------------------------------------------------------------------------- +const std::string& GrandPrixData::getTrackId(const unsigned int track) const +{ + assert(track < getNumberOfTracks()); + return m_tracks[track]; +} + +// ---------------------------------------------------------------------------- +unsigned int GrandPrixData::getLaps(const unsigned int track) const +{ + assert(track < getNumberOfTracks()); + return m_laps[track]; +} + +// ---------------------------------------------------------------------------- +bool GrandPrixData::getReverse(const unsigned int track) const +{ + assert(track < getNumberOfTracks()); + return m_reversed[track]; +} + +// ---------------------------------------------------------------------------- +void GrandPrixData::moveUp(const unsigned int track) +{ + assert (track > 0 && track < getNumberOfTracks()); + + std::swap(m_tracks[track], m_tracks[track - 1]); + std::swap(m_laps[track], m_laps[track - 1]); + m_reversed.swap(m_reversed[track], m_reversed[track - 1]); +} + +// ---------------------------------------------------------------------------- +void GrandPrixData::moveDown(const unsigned int track) +{ + assert (track < (getNumberOfTracks() - 1)); + + std::swap(m_tracks[track], m_tracks[track + 1]); + std::swap(m_laps[track], m_laps[track + 1]); + m_reversed.swap(m_reversed[track], m_reversed[track + 1]); +} + +// ---------------------------------------------------------------------------- +void GrandPrixData::addTrack(Track* track, unsigned int laps, bool reverse, + int position) +{ + int n; + + n = getNumberOfTracks(); + assert (track != NULL); + assert (laps > 0); + assert (position >= -1 && position < n); + + if (position < 0 || position == (n - 1) || m_tracks.empty()) + { + //Append new track to the end of the list + m_tracks.push_back(track->getIdent()); + m_laps.push_back(laps); + m_reversed.push_back(reverse); + } + else + { + //Insert new track right after the specified position. Caution: + //std::vector inserts elements _before_ the specified position + m_tracks.insert(m_tracks.begin() + position + 1, track->getIdent()); + m_laps.insert(m_laps.begin() + position + 1, laps); + m_reversed.insert(m_reversed.begin() + position + 1, reverse); + } +} + +// ---------------------------------------------------------------------------- +void GrandPrixData::editTrack(unsigned int t, Track* track, + unsigned int laps, bool reverse) +{ + assert (t < getNumberOfTracks()); + assert (track != NULL); + assert (laps > 0); + + m_tracks[t] = track->getIdent(); + m_laps[t] = laps; + m_reversed[t] = reverse; +} + +// ---------------------------------------------------------------------------- +void GrandPrixData::remove(const unsigned int track) +{ + assert (track < getNumberOfTracks()); + + m_tracks.erase(m_tracks.begin() + track); + m_laps.erase(m_laps.begin() + track); + m_reversed.erase(m_reversed.begin() + track); +} + // ---------------------------------------------------------------------------- const std::vector& GrandPrixData::getTrackNames() const { m_really_available_tracks.clear(); - for(unsigned int i=0; i< m_tracks.size(); i++) + for (unsigned int i = 0; i < m_tracks.size(); i++) { if(isTrackAvailable(m_tracks[i])) m_really_available_tracks.push_back(m_tracks[i]); diff --git a/src/race/grand_prix_data.hpp b/src/race/grand_prix_data.hpp index 8061d88c9..6ecff2e98 100644 --- a/src/race/grand_prix_data.hpp +++ b/src/race/grand_prix_data.hpp @@ -27,7 +27,8 @@ #include #include "utils/translation.hpp" -#include "io/file_manager.hpp" + +class Track; /** Simple class that hold the data relevant to a 'grand_prix', aka. a number * of races that has to be completed one after the other @@ -65,28 +66,49 @@ private: /** Whether the track in question should be done in reverse mode */ std::vector m_reversed; - void load_from_file(const std::string fullpath, const std::string filename); + /** Wether the user can edit this grand prix or not */ + bool m_editable; + bool isTrackAvailable(const std::string &id) const; + public: /** Load the GrandPrixData from the given filename */ #if (defined(WIN32) || defined(_WIN32)) && !defined(__MINGW32__) #pragma warning(disable:4290) #endif - GrandPrixData () {}; // empty for initialising - GrandPrixData(const std::string filename); - GrandPrixData (const std::string dir, const std::string filename); + GrandPrixData (const std::string& filename) throw(std::logic_error); + GrandPrixData () {}; // empty for initialising + void setId(const std::string& id); + void setName(const irr::core::stringw& name); + void setFilename(const std::string& filename); + void setEditable(const bool editable); + void reload(); + bool writeToFile(); - bool checkConsistency(bool chatty=true) const; + bool checkConsistency(bool chatty=true) const; const std::vector& getTrackNames() const; - void getLaps(std::vector *laps) const; - void getReverse(std::vector *reverse) const; + void getLaps(std::vector *laps) const; + void getReverse(std::vector *reverse) const; + bool isEditable() const; + unsigned int getNumberOfTracks() const; + const std::string& getTrackId(const unsigned int track) const; + irr::core::stringw getTrackName(const unsigned int track) const; + unsigned int getLaps(const unsigned int track) const; + bool getReverse(const unsigned int track) const; + void moveUp(const unsigned int track); + void moveDown(const unsigned int track); + void addTrack(Track* track, unsigned int laps, + bool reverse, int position=-1); + void editTrack(unsigned int t, Track* track, + unsigned int laps, bool reverse); + void remove(const unsigned int track); // ------------------------------------------------------------------------ /** @return the (potentially translated) user-visible name of the Grand * Prix (apply fribidi as needed) */ - const irr::core::stringw getName() const { return _LTR(m_name.c_str()); } + irr::core::stringw getName() const { return _LTR(m_name.c_str()); } // ------------------------------------------------------------------------ /** @return the internal name identifier of the Grand Prix (not translated) */ diff --git a/src/race/grand_prix_manager.cpp b/src/race/grand_prix_manager.cpp index 508c35eae..653933224 100644 --- a/src/race/grand_prix_manager.cpp +++ b/src/race/grand_prix_manager.cpp @@ -18,66 +18,126 @@ #include "race/grand_prix_manager.hpp" -#include +#include "config/user_config.hpp" +#include "grand_prix_data.hpp" #include "io/file_manager.hpp" #include "utils/string_utils.hpp" -#include "config/user_config.hpp" + +#include +#include +#include GrandPrixManager *grand_prix_manager = NULL; +const char* GrandPrixManager::SUFFIX = ".grandprix"; + +// ---------------------------------------------------------------------------- +void GrandPrixManager::loadFiles() +{ + std::set dirs; + + //Add all the directories to a set to avoid duplicates + dirs.insert(file_manager->getAsset(FileManager::GRANDPRIX, "")); + dirs.insert(file_manager->getGPDir()); + dirs.insert(UserConfigParams::m_additional_gp_directory); + + for (std::set::const_iterator it = dirs.begin(); it != dirs.end(); ++it) + { + std::string dir = *it; + if (!dir.empty() && dir[dir.size() - 1] == '/') + loadDir(dir); + } +} + +// ---------------------------------------------------------------------------- +void GrandPrixManager::loadDir(const std::string& dir) +{ + Log::info("GrandPrixManager", "Loading Grand Prix files from %s", dir.c_str()); + assert(!dir.empty() && dir[dir.size() - 1] == '/'); + + // Findout which grand prixs are available and load them + std::set result; + file_manager->listFiles(result, dir); + for(std::set::iterator i = result.begin(); i != result.end(); i++) + { + if (StringUtils::hasSuffix(*i, SUFFIX)) + load(dir + *i); + } // for i +} // loadDir + +// ---------------------------------------------------------------------------- +void GrandPrixManager::load(const std::string& filename) +{ + GrandPrixData* gp; + + try + { + gp = new GrandPrixData(filename); + m_gp_data.push_back(gp); + Log::debug("GrandPrixManager", "Grand Prix '%s' loaded from %s", + gp->getId().c_str(), filename.c_str()); + } + catch (std::logic_error& er) + { + Log::error("GrandPrixManager", "Ignoring GP %s (%s)\n", + filename.c_str(), er.what()); + } +} // load + +// ---------------------------------------------------------------------------- +void GrandPrixManager::reload() +{ + for(unsigned int i=0; igetId() == id); + + return exists; +} + +// ---------------------------------------------------------------------------- +bool GrandPrixManager::existsName(const irr::core::stringw& name) const +{ + bool exists; + + exists = false; + for (unsigned int i = 0; !exists && i < m_gp_data.size(); i++) + exists = (m_gp_data[i]->getName() == name); + + return exists; +} + +// ---------------------------------------------------------------------------- GrandPrixManager::GrandPrixManager() { - // Findout which grand prixs are available and load them - // Grand Prix in the standart directory - std::set result; - std::string gp_dir = file_manager->getAsset(FileManager::GRANDPRIX, ""); - file_manager->listFiles(result, gp_dir); - for(std::set::iterator i = result.begin(); - i != result.end() ; i++) - { - if (StringUtils::hasSuffix(*i, ".grandprix")) - { - try - { - m_gp_data.push_back(new GrandPrixData(*i)); - Log::debug("GrandPrixManager", "Grand Prix %s loaded.", - i->c_str()); - } - catch (std::logic_error& e) - { - Log::error("GrandPrixManager", "Ignoring GP %s ( %s ) \n", - i->c_str(), e.what()); - } - } - } - - // Load additional Grand Prix - const std::string dir = UserConfigParams::m_additional_gp_directory; - if(dir != "") { - Log::info("GrandPrixManager", "Loading additional Grand Prix from " - "%s ...", dir.c_str()); - file_manager->listFiles(result, dir); - for(std::set::iterator i = result.begin(); - i != result.end() ; i++) - { - if (StringUtils::hasSuffix(*i, ".grandprix")) - { - try - { - m_gp_data.push_back(new GrandPrixData(dir, *i)); - Log::debug("GrandPrixManager", "Grand Prix %s loaded from " - "%s", i->c_str(), - dir.c_str()); - } - catch (std::logic_error& e) - { - Log::error("GrandPrixManager", "Ignoring GP %s ( %s ) \n", - i->c_str(), e.what()); - } - } - } // end for - } // end if + loadFiles(); } // GrandPrixManager + // ---------------------------------------------------------------------------- GrandPrixManager::~GrandPrixManager() { @@ -87,15 +147,21 @@ GrandPrixManager::~GrandPrixManager() } // for i } // ~GrandPrixManager + // ---------------------------------------------------------------------------- const GrandPrixData* GrandPrixManager::getGrandPrix(const std::string& s) const { - for(unsigned int i=0; igetId() == s) - return m_gp_data[i]; - - return NULL; + return editGrandPrix(s); } // getGrandPrix + +// ---------------------------------------------------------------------------- +GrandPrixData* GrandPrixManager::editGrandPrix(const std::string& s) const +{ + for(unsigned int i=0; igetId()==s) return m_gp_data[i]; + return NULL; +} + // ---------------------------------------------------------------------------- void GrandPrixManager::checkConsistency() { @@ -104,9 +170,64 @@ void GrandPrixManager::checkConsistency() if(!m_gp_data[i]->checkConsistency()) { // delete this GP, since a track is missing - m_gp_data.erase(m_gp_data.begin()+i); + delete *(m_gp_data.erase(m_gp_data.begin()+i)); i--; } } } // checkConsistency + // ---------------------------------------------------------------------------- +GrandPrixData* GrandPrixManager::createNew(const irr::core::stringw& newName) +{ + if (existsName(newName)) + return NULL; + + std::string newID = generateId(); + + GrandPrixData* gp = new GrandPrixData; + gp->setId(newID); + gp->setName(newName); + gp->setFilename(file_manager->getGPDir() + newID + SUFFIX); + gp->setEditable(true); + gp->writeToFile(); + m_gp_data.push_back(gp); + + return gp; +} + +// ---------------------------------------------------------------------------- +GrandPrixData* GrandPrixManager::copy(const std::string& id, + const irr::core::stringw& newName) +{ + if (existsName(newName)) + return NULL; + + std::string newID = generateId(); + + GrandPrixData* gp = new GrandPrixData(*getGrandPrix(id)); + gp->setId(newID); + gp->setName(newName); + gp->setFilename(file_manager->getGPDir() + newID + SUFFIX); + gp->setEditable(true); + gp->writeToFile(); + m_gp_data.push_back(gp); + + return gp; +} + +// ---------------------------------------------------------------------------- +void GrandPrixManager::remove(const std::string& id) +{ + const GrandPrixData* gp = getGrandPrix(id); + assert(gp != NULL); + + if (gp->isEditable()) + { + file_manager->removeFile(gp->getFilename()); + reload(); + } + else + { + Log::warn("GrandPrixManager", "Grand Prix '%s' cannot be removed\n", gp->getId().c_str()); + } +} diff --git a/src/race/grand_prix_manager.hpp b/src/race/grand_prix_manager.hpp index 2afb979b6..8238fc6de 100644 --- a/src/race/grand_prix_manager.hpp +++ b/src/race/grand_prix_manager.hpp @@ -30,16 +30,31 @@ class GrandPrixManager { private: + static const char* SUFFIX; + + void loadFiles(); + void loadDir(const std::string& dir); + void load(const std::string &filename); + + std::string generateId(); + + bool existsId(const std::string& id) const; + bool existsName(const irr::core::stringw& name) const; + std::vector m_gp_data; public: GrandPrixManager(); ~GrandPrixManager(); - void load(const std::string &filename); - const GrandPrixData* getGrandPrix(int i) const { return m_gp_data[i]; } + void reload(); + const GrandPrixData* getGrandPrix(const int i) const { return m_gp_data[i]; } const GrandPrixData* getGrandPrix(const std::string& s) const; + GrandPrixData* editGrandPrix(const std::string& s) const; unsigned int getNumberOfGrandPrix() const { return m_gp_data.size(); } void checkConsistency(); + GrandPrixData* createNew(const irr::core::stringw& newName); + GrandPrixData* copy(const std::string& id, const irr::core::stringw& newName); + void remove(const std::string& id); }; // GrandPrixManager extern GrandPrixManager *grand_prix_manager; diff --git a/src/states_screens/dialogs/enter_gp_name_dialog.cpp b/src/states_screens/dialogs/enter_gp_name_dialog.cpp new file mode 100644 index 000000000..b8b9d566f --- /dev/null +++ b/src/states_screens/dialogs/enter_gp_name_dialog.cpp @@ -0,0 +1,137 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2014 Marc Coll +// +// This program 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 3 +// of the License, or (at your option) any later version. +// +// This program 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 this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "states_screens/dialogs/enter_gp_name_dialog.hpp" + +#include "audio/sfx_manager.hpp" +#include "guiengine/engine.hpp" +#include "guiengine/widgets/button_widget.hpp" +#include "guiengine/widgets/label_widget.hpp" +#include "guiengine/widgets/text_box_widget.hpp" +#include "race/grand_prix_manager.hpp" +#include "states_screens/state_manager.hpp" +#include "utils/translation.hpp" + +#include + + +using namespace GUIEngine; +using namespace irr::core; + +// ----------------------------------------------------------------------------- +EnterGPNameDialog::EnterGPNameDialog(INewGPListener* listener, + const float w, const float h) + : ModalDialog(w, h), m_listener(listener), m_self_destroy(false) +{ + assert(listener != NULL); + loadFromFile("enter_gp_name_dialog.stkgui"); + + TextBoxWidget* textCtrl = getWidget("textfield"); + assert(textCtrl != NULL); + textCtrl->setFocusForPlayer(PLAYER_ID_GAME_MASTER); +} + +// ----------------------------------------------------------------------------- +EnterGPNameDialog::~EnterGPNameDialog() +{ + // FIXME: what is this code for? + TextBoxWidget* textCtrl = getWidget("textfield"); + textCtrl->getIrrlichtElement()->remove(); + textCtrl->clearListeners(); +} + +// ----------------------------------------------------------------------------- +GUIEngine::EventPropagation EnterGPNameDialog::processEvent(const std::string& eventSource) +{ + if (eventSource == "cancel") + { + dismiss(); + return GUIEngine::EVENT_BLOCK; + } + return GUIEngine::EVENT_LET; +} + +// ----------------------------------------------------------------------------- +void EnterGPNameDialog::onEnterPressedInternal() +{ + //Cancel button pressed + ButtonWidget* cancelButton = getWidget("cancel"); + if (GUIEngine::isFocusedForPlayer(cancelButton, PLAYER_ID_GAME_MASTER)) + { + std::string fakeEvent = "cancel"; + processEvent(fakeEvent); + return; + } + + //Otherwise, see if we can accept the new name + TextBoxWidget* textCtrl = getWidget("textfield"); + assert(textCtrl != NULL); + stringw name = textCtrl->getText().trim(); + if (name.size() > 0) + { + // check for duplicate names + for (int i = 0; i < grand_prix_manager->getNumberOfGrandPrix(); i++) + { + const GrandPrixData* gp = grand_prix_manager->getGrandPrix(i); + if (gp->getName() == name) + { + LabelWidget* label = getWidget("title"); + assert(label != NULL); + label->setText(_("Another grand prix with this name already exists."), false); + sfx_manager->quickSound("anvil"); + return; + } + } + + // It's unsafe to delete from inside the event handler so we do it + // in onUpdate (which checks for m_self_destroy) + m_self_destroy = true; + } + else + { + LabelWidget* label = getWidget("title"); + assert(label != NULL); + label->setText(_("Cannot add a grand prix with this name"), false); + sfx_manager->quickSound("anvil"); + } +} + +// ----------------------------------------------------------------------------- +void EnterGPNameDialog::onUpdate(float dt) +{ + // It's unsafe to delete from inside the event handler so we do it here + if (m_self_destroy) + { + TextBoxWidget* textCtrl = getWidget("textfield"); + stringw name = textCtrl->getText().trim(); + + // irrLicht is too stupid to remove focus from deleted widgets + // so do it by hand + GUIEngine::getGUIEnv()->removeFocus( textCtrl->getIrrlichtElement() ); + GUIEngine::getGUIEnv()->removeFocus( m_irrlicht_window ); + + // we will destroy the dialog before notifying the listener to be safer. + // but in order not to crash we must make a local copy of the listern + // otherwise we will crash + INewGPListener* listener = m_listener; + + ModalDialog::dismiss(); + + if (listener != NULL) + listener->onNewGPWithName(name); + } +} diff --git a/src/states_screens/dialogs/enter_gp_name_dialog.hpp b/src/states_screens/dialogs/enter_gp_name_dialog.hpp new file mode 100644 index 000000000..a9a5cc42d --- /dev/null +++ b/src/states_screens/dialogs/enter_gp_name_dialog.hpp @@ -0,0 +1,70 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2014 Marc Coll +// +// This program 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 3 +// of the License, or (at your option) any later version. +// +// This program 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 this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + +#ifndef HEADER_ENTER_GP_NAME_DIALOG_HPP +#define HEADER_ENTER_GP_NAME_DIALOG_HPP + +#include "guiengine/modaldialog.hpp" + +#include + + +namespace GUIEngine +{ + class TextBoxWidget; + class ButtonWidget; + class LabelWidget; +} + +/** + * \brief Dialog that allows the player to enter the name for a new grand prix + * \ingroup states_screens + */ +class EnterGPNameDialog : public GUIEngine::ModalDialog +{ + +public: + + class INewGPListener + { + public: + virtual void onNewGPWithName(const irr::core::stringw& newName) = 0; + virtual ~INewGPListener(){} + }; + +private: + + INewGPListener* m_listener; + bool m_self_destroy; + +public: + + /** + * Creates a modal dialog with given percentage of screen width and height + */ + EnterGPNameDialog(INewGPListener* listener, const float percentWidth, + const float percentHeight); + ~EnterGPNameDialog(); + + void onEnterPressedInternal(); + GUIEngine::EventPropagation processEvent(const std::string& eventSource); + + virtual void onUpdate(float dt); +}; + +#endif diff --git a/src/states_screens/dialogs/user_info_dialog.cpp b/src/states_screens/dialogs/user_info_dialog.cpp index bd06dcde1..b89aee79d 100644 --- a/src/states_screens/dialogs/user_info_dialog.cpp +++ b/src/states_screens/dialogs/user_info_dialog.cpp @@ -278,6 +278,24 @@ void UserInfoDialog::declineFriendRequest() } // declineFriendRequest +// ----------------------------------------------------------------------------- +/** Removes an existing friend. + */ +void UserInfoDialog::removeExistingFriend() +{ + CurrentUser::get()->requestRemoveFriend(m_profile->getID()); + +} // removeExistingFriend + +// ----------------------------------------------------------------------------- +/** Removes a pending friend request. + */ +void UserInfoDialog::removePendingFriend() +{ + CurrentUser::get()->requestCancelFriend(m_profile->getID()); + +} // removePendingFriend + // ----------------------------------------------------------------------------- GUIEngine::EventPropagation UserInfoDialog::processEvent(const std::string& eventSource) { @@ -304,10 +322,12 @@ GUIEngine::EventPropagation UserInfoDialog::processEvent(const std::string& even } else if(selection == m_remove_widget->m_properties[PROP_ID]) { - if(m_profile->getRelationInfo()->isPending()) - CurrentUser::get()->requestCancelFriend(m_profile->getID()); + if (m_profile->getRelationInfo() && + m_profile->getRelationInfo()->isPending() ) + removePendingFriend(); else - CurrentUser::get()->requestRemoveFriend(m_profile->getID()); + removeExistingFriend(); + m_processing = true; m_options_widget->setDeactivated(); return GUIEngine::EVENT_BLOCK; diff --git a/src/states_screens/dialogs/user_info_dialog.hpp b/src/states_screens/dialogs/user_info_dialog.hpp index ca6c23c78..ed5c01bd8 100644 --- a/src/states_screens/dialogs/user_info_dialog.hpp +++ b/src/states_screens/dialogs/user_info_dialog.hpp @@ -63,6 +63,8 @@ private: void sendFriendRequest(); void acceptFriendRequest(); void declineFriendRequest(); + void removeExistingFriend(); + void removePendingFriend(); public: UserInfoDialog(uint32_t showing_id, const core::stringw info = "", bool error = false, bool from_queue = false); diff --git a/src/states_screens/edit_gp_screen.cpp b/src/states_screens/edit_gp_screen.cpp new file mode 100644 index 000000000..d692eb7e4 --- /dev/null +++ b/src/states_screens/edit_gp_screen.cpp @@ -0,0 +1,334 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2014 Marc Coll +// +// This program 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 3 +// of the License, or (at your option) any later version. +// +// This program 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 this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "states_screens/edit_gp_screen.hpp" + +#include "graphics/irr_driver.hpp" +#include "guiengine/CGUISpriteBank.h" +#include "guiengine/widgets/dynamic_ribbon_widget.hpp" +#include "guiengine/widgets/icon_button_widget.hpp" +#include "guiengine/widgets/label_widget.hpp" +#include "guiengine/widgets/list_widget.hpp" +#include "race/grand_prix_data.hpp" +#include "states_screens/edit_track_screen.hpp" +#include "states_screens/state_manager.hpp" +#include "tracks/track.hpp" +#include "tracks/track_manager.hpp" + + +using namespace GUIEngine; + +DEFINE_SCREEN_SINGLETON( EditGPScreen ); + +// ----------------------------------------------------------------------------- +EditGPScreen::EditGPScreen() + : Screen("gpedit.stkgui"), m_gp(NULL), m_list(NULL), m_icon_bank(NULL), + m_selected(-1), m_modified(false) +{ + +} + +// ----------------------------------------------------------------------------- +EditGPScreen::~EditGPScreen() +{ + delete m_icon_bank; +} + +// ----------------------------------------------------------------------------- +void EditGPScreen::setSelectedGP(GrandPrixData* gp) +{ + assert(gp != NULL); + m_gp = gp; +} + +// ----------------------------------------------------------------------------- +void EditGPScreen::loadedFromFile() +{ + if (m_icon_bank == NULL) + m_icon_bank = new irr::gui::STKModifiedSpriteBank(GUIEngine::getGUIEnv()); + + m_list = getWidget("tracks"); + assert(m_list != NULL); + m_list->addColumn(_("Track"), 3); + m_list->addColumn(_("Laps"), 1); + m_list->addColumn(_("Reversed"), 1); +} + +// ----------------------------------------------------------------------------- +void EditGPScreen::eventCallback(GUIEngine::Widget* widget, const std::string& name, + const int playerID) +{ + setSelected(m_list->getSelectionID()); + + if (name == "tracks") + { + m_action = "edit"; + edit(); + } + else if (name == "menu") + { + RibbonWidget* menu = getWidget("menu"); + assert(menu != NULL); + m_action = menu->getSelectionIDString(PLAYER_ID_GAME_MASTER); + + if (m_action == "up") + { + if (canMoveUp()) + { + m_gp->moveUp(m_selected--); + loadList(m_selected); + setModified(true); + } + } + else if (m_action == "down") + { + if (canMoveDown()) + { + m_gp->moveDown(m_selected++); + loadList(m_selected); + setModified(true); + } + } + else if (m_action == "add" || m_action == "edit") + { + if (m_action == "edit") + { + edit(); + } + else + { + EditTrackScreen* edit = EditTrackScreen::getInstance(); + assert(edit != NULL); + //By default, 3 laps and no reversing + edit->setSelection(NULL, 3, false); + StateManager::get()->pushScreen(edit); + } + } + else if (m_action == "remove") + { + if (m_selected >= 0 && m_selected < m_list->getItemCount()) + { + new MessageDialog( + _("Are you sure you want to remove '%s'?", + m_gp->getTrackName(m_selected).c_str()), + MessageDialog::MESSAGE_DIALOG_CONFIRM, + this, false); + } + } + else if (m_action == "save") + { + save(); + } + } + else if (name == "back") + { + if (m_modified) + { + m_action = "back"; + new MessageDialog( + _("Do you want to save your changes?"), + MessageDialog::MESSAGE_DIALOG_CONFIRM, + this, false); + } + else + { + back(); + } + } +} + +// ----------------------------------------------------------------------------- +void EditGPScreen::init() +{ + if (m_action.empty()) + { + LabelWidget* header = getWidget("title"); + assert(header != NULL); + header->setText(m_gp->getName(), true); + + IconButtonWidget* button = getWidget("save"); + assert(button != NULL); + button->setDeactivated(); + + loadList(0); + setModified(false); + } + else + { + EditTrackScreen* edit = EditTrackScreen::getInstance(); + assert(edit != NULL); + + if (edit->getResult()) + { + if (m_action == "add") + { + m_gp->addTrack(edit->getTrack(), edit->getLaps(), edit->getReverse(), + m_selected); + setSelected(m_selected + 1); + } + else if (m_action == "edit") + { + m_gp->editTrack(m_selected, edit->getTrack(), edit->getLaps(), + edit->getReverse()); + } + setModified(true); + } + loadList(m_selected); + m_action.clear(); + } +} + +// ----------------------------------------------------------------------------- +void EditGPScreen::onConfirm() +{ + ModalDialog::dismiss(); + if (m_action == "remove") + { + m_gp->remove(m_selected); + setSelected(m_selected >= m_gp->getNumberOfTracks() ? + m_gp->getNumberOfTracks() - 1 : m_selected); + loadList(m_selected); + setModified(true); + } + else if (m_action == "back") + { + save(); + back(); + } +} + +// ----------------------------------------------------------------------------- +void EditGPScreen::onCancel() +{ + ModalDialog::dismiss(); + if (m_action == "back") + back(); +} + +// ----------------------------------------------------------------------------- +void EditGPScreen::loadList(const int selected) +{ + m_list->clear(); + m_icons.clear(); + m_icon_bank->clear(); + m_icon_bank->scaleToHeight (64); + m_list->setIcons(m_icon_bank, 64); + + for (unsigned int i = 0; i < m_gp->getNumberOfTracks(); i++) + { + std::vector row; + + Track* t = track_manager->getTrack(m_gp->getTrackId(i)); + assert(t != NULL); + video::ITexture* screenShot = irr_driver->getTexture(t->getScreenshotFile()); + assert(screenShot != NULL); + m_icons.push_back(m_icon_bank->addTextureAsSprite(screenShot)); + + row.push_back(GUIEngine::ListWidget::ListCell( + _LTR(m_gp->getTrackName(i).c_str()), m_icons[i], 3, false)); + row.push_back(GUIEngine::ListWidget::ListCell( + StringUtils::toWString(m_gp->getLaps(i)), -1, 1, true)); + row.push_back(GUIEngine::ListWidget::ListCell( + m_gp->getReverse(i) ? _("Yes") : _("No"), -1, 1, true)); + + m_list->addItem(m_gp->getId(), row); + } + m_list->setIcons(m_icon_bank); + + if (selected < m_list->getItemCount()) + { + m_list->setSelectionID(selected); + setSelected(selected); + } +} + +// ----------------------------------------------------------------------------- +void EditGPScreen::setModified(const bool modified) +{ + m_modified = modified; + + IconButtonWidget* save_button = getWidget("save"); + assert(save_button != NULL); + if (modified) + save_button->setActivated(); + else + save_button->setDeactivated(); +} + +// ----------------------------------------------------------------------------- +void EditGPScreen::setSelected(const int selected) +{ + IconButtonWidget* button_up = getWidget("up"); + assert(button_up != NULL); + IconButtonWidget* button_down = getWidget("down"); + assert(button_down != NULL); + + m_selected = selected; +} + +// ----------------------------------------------------------------------------- +void EditGPScreen::edit() +{ + EditTrackScreen* edit_screen = EditTrackScreen::getInstance(); + assert(edit_screen != NULL); + + if (m_selected >= 0 && m_selected < m_list->getItemCount()) + { + edit_screen->setSelection(track_manager->getTrack( + m_gp->getTrackId(m_selected)), + m_gp->getLaps(m_selected), + m_gp->getReverse(m_selected)); + StateManager::get()->pushScreen(edit_screen); + } +} + +// ----------------------------------------------------------------------------- +bool EditGPScreen::save() +{ + if (m_gp->writeToFile()) + { + setModified(false); + return true; + } + else + { + new MessageDialog( + _("An error occurred while trying to save your grand prix"), + MessageDialog::MESSAGE_DIALOG_OK, NULL, false); + return false; + } +} + +// ----------------------------------------------------------------------------- +void EditGPScreen::back () +{ + m_action.clear(); + m_modified = false; + StateManager::get()->popMenu(); +} + +// ----------------------------------------------------------------------------- +bool EditGPScreen::canMoveUp() const +{ + return (m_selected > 0 && m_selected < m_list->getItemCount()); +} + +// ----------------------------------------------------------------------------- +bool EditGPScreen::canMoveDown() const +{ + return (m_selected >= 0 && m_selected < (m_list->getItemCount() - 1)); +} diff --git a/src/states_screens/edit_gp_screen.hpp b/src/states_screens/edit_gp_screen.hpp new file mode 100644 index 000000000..62b260b1e --- /dev/null +++ b/src/states_screens/edit_gp_screen.hpp @@ -0,0 +1,85 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2014 Marc Coll +// +// This program 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 3 +// of the License, or (at your option) any later version. +// +// This program 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 this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_EDIT_GP_SCREEN_HPP +#define HEADER_EDIT_GP_SCREEN_HPP + +#include "guiengine/screen.hpp" +#include "guiengine/widgets/list_widget.hpp" +#include "states_screens/dialogs/message_dialog.hpp" + +#include + + +namespace GUIEngine { class Widget; } +namespace irr { namespace gui { class STKModifiedSpriteBank; } } + +class GrandPrixData; + +/** + * \brief screen where the user can edit a grand prix + * \ingroup states_screens + */ +class EditGPScreen : + public GUIEngine::Screen, + public GUIEngine::ScreenSingleton, + public MessageDialog::IConfirmDialogListener +{ + friend class GUIEngine::ScreenSingleton; + + EditGPScreen(); + + void onConfirm(); + void onCancel(); + + void loadList(const int selected); + void setModified(const bool modified); + void setSelected(const int selected); + void edit(); + bool save(); + void back(); + + bool canMoveUp() const; + bool canMoveDown() const; + + GrandPrixData* m_gp; + GUIEngine::ListWidget* m_list; + irr::gui::STKModifiedSpriteBank* m_icon_bank; + std::vector m_icons; + int m_selected; + bool m_modified; + + std::string m_action; + +public: + + ~EditGPScreen(); + + void setSelectedGP(GrandPrixData* gp); + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void loadedFromFile() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, + const int playerID) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void init() OVERRIDE; +}; + +#endif diff --git a/src/states_screens/edit_track_screen.cpp b/src/states_screens/edit_track_screen.cpp new file mode 100644 index 000000000..7259a5623 --- /dev/null +++ b/src/states_screens/edit_track_screen.cpp @@ -0,0 +1,240 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2014 Marc Coll +// +// This program 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 3 +// of the License, or (at your option) any later version. +// +// This program 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 this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "states_screens/edit_track_screen.hpp" + +#include "guiengine/widgets/button_widget.hpp" +#include "guiengine/widgets/check_box_widget.hpp" +#include "guiengine/widgets/dynamic_ribbon_widget.hpp" +#include "guiengine/widgets/label_widget.hpp" +#include "guiengine/widgets/ribbon_widget.hpp" +#include "guiengine/widgets/spinner_widget.hpp" +#include "states_screens/state_manager.hpp" +#include "tracks/track.hpp" +#include "tracks/track_manager.hpp" + + +using namespace GUIEngine; +using namespace irr::core; + +const char* EditTrackScreen::ALL_TRACKS_GROUP_ID = "all"; + +DEFINE_SCREEN_SINGLETON( EditTrackScreen ); + +// ----------------------------------------------------------------------------- +EditTrackScreen::EditTrackScreen() + : Screen("edit_track.stkgui"), m_track_group(ALL_TRACKS_GROUP_ID), + m_track(NULL), m_laps(0), m_reverse(false), m_result(false) +{ + +} + +// ----------------------------------------------------------------------------- +EditTrackScreen::~EditTrackScreen() +{ + +} + +// ----------------------------------------------------------------------------- +void EditTrackScreen::setSelection(Track* track, unsigned int laps, bool reverse) +{ + assert(laps > 0); + m_track = track; + m_laps = laps; + m_reverse = reverse; +} + +// ----------------------------------------------------------------------------- +Track* EditTrackScreen::getTrack() const +{ + return m_track; +} + +// ----------------------------------------------------------------------------- +unsigned int EditTrackScreen::getLaps() const +{ + return m_laps; +} + +// ----------------------------------------------------------------------------- +bool EditTrackScreen::getReverse() const +{ + return m_reverse; +} + +// ----------------------------------------------------------------------------- +bool EditTrackScreen::getResult() const +{ + return m_result; +} + +// ----------------------------------------------------------------------------- +void EditTrackScreen::loadedFromFile() +{ + +} + +// ----------------------------------------------------------------------------- +void EditTrackScreen::eventCallback(GUIEngine::Widget* widget, const std::string& name, + const int playerID) +{ + if (name == "ok") + { + m_result = true; + StateManager::get()->popMenu(); + } + else if (name == "cancel") + { + m_result = false; + StateManager::get()->popMenu(); + } + else if (name == "tracks") + { + DynamicRibbonWidget* tracks = getWidget("tracks"); + assert(tracks != NULL); + selectTrack(tracks->getSelectionIDString(PLAYER_ID_GAME_MASTER)); + } + else if (name == "trackgroups") + { + RibbonWidget* tabs = getWidget("trackgroups"); + assert(tabs != NULL); + m_track_group = tabs->getSelectionIDString(PLAYER_ID_GAME_MASTER); + loadTrackList(); + } + else if (name == "laps") + { + SpinnerWidget* laps = getWidget("laps"); + assert(laps != NULL); + m_laps = laps->getValue(); + } + else if (name == "reverse") + { + CheckBoxWidget* reverse = getWidget("reverse"); + assert(reverse != NULL); + m_reverse = reverse->getState(); + } +} + +// ----------------------------------------------------------------------------- +void EditTrackScreen::beforeAddingWidget() +{ + RibbonWidget* tabs = getWidget("trackgroups"); + assert (tabs != NULL); + + tabs->clearAllChildren(); + + const std::vector& groups = track_manager->getAllTrackGroups(); + if (groups.size() > 1) + { + tabs->addTextChild(_("All"), ALL_TRACKS_GROUP_ID); + for (unsigned int i = 0; i < groups.size(); i++) + tabs->addTextChild(_(groups[i].c_str()), groups[i]); + } +} + +// ----------------------------------------------------------------------------- +void EditTrackScreen::init() +{ + RibbonWidget* tabs = getWidget("trackgroups"); + assert (tabs != NULL); + SpinnerWidget* laps = getWidget("laps"); + assert(laps != NULL); + CheckBoxWidget* reverse = getWidget("reverse"); + assert(reverse != NULL); + + if (m_track_group.empty()) + tabs->select (ALL_TRACKS_GROUP_ID, PLAYER_ID_GAME_MASTER); + else + tabs->select (m_track_group, PLAYER_ID_GAME_MASTER); + laps->setValue(m_laps); + reverse->setState(m_reverse); + + loadTrackList(); + if (m_track == NULL) + selectTrack(""); + else + selectTrack(m_track->getIdent()); +} + +// ----------------------------------------------------------------------------- +void EditTrackScreen::loadTrackList() +{ + bool belongsToGroup; + + DynamicRibbonWidget* tracks_widget = getWidget("tracks"); + assert(tracks_widget != NULL); + + tracks_widget->clearItems(); + + for (unsigned int i = 0; i < track_manager->getNumberOfTracks(); i++) + { + Track* t = track_manager->getTrack(i); + const std::vector& groups = t->getGroups(); + belongsToGroup = (m_track_group.empty() || m_track_group == ALL_TRACKS_GROUP_ID || + std::find(groups.begin(), groups.end(), m_track_group) != groups.end()); + if (!t->isArena() && !t->isSoccer() && !t->isInternal() && belongsToGroup) + { + tracks_widget->addItem(translations->fribidize(t->getName()), t->getIdent(), + t->getScreenshotFile(), 0, IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE ); + } + } + + tracks_widget->updateItemDisplay(); +} + +// ----------------------------------------------------------------------------- +void EditTrackScreen::selectTrack(const std::string& id) +{ + DynamicRibbonWidget* tracks = getWidget("tracks"); + assert(tracks != NULL); + LabelWidget* selected_track = getWidget("selected_track"); + assert(selected_track != NULL); + SpinnerWidget* laps = getWidget("laps"); + assert(laps != NULL); + LabelWidget* label_reverse = getWidget("reverse_label"); + assert(label_reverse != NULL); + CheckBoxWidget* reverse = getWidget("reverse"); + assert(reverse != NULL); + ButtonWidget* ok_button = getWidget("ok"); + assert(ok_button != NULL); + + m_track = track_manager->getTrack(id); + if (m_track != NULL) + { + tracks->setSelection(m_track->getIdent(), PLAYER_ID_GAME_MASTER, true); + selected_track->setText(m_track->getName(), true); + + laps->setValue(m_laps); + + reverse->setVisible(m_track->reverseAvailable()); + label_reverse->setVisible(m_track->reverseAvailable()); + + ok_button->setActivated(); + } + else + { + tracks->setSelection("", PLAYER_ID_GAME_MASTER, true); + selected_track->setText(_("Select a track"), true); + + laps->setValue(3); + + reverse->setVisible(true); + reverse->setState(false); + + ok_button->setDeactivated(); + } +} diff --git a/src/states_screens/edit_track_screen.hpp b/src/states_screens/edit_track_screen.hpp new file mode 100644 index 000000000..a5edb982b --- /dev/null +++ b/src/states_screens/edit_track_screen.hpp @@ -0,0 +1,78 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2014 Marc Coll +// +// This program 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 3 +// of the License, or (at your option) any later version. +// +// This program 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 this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_EDIT_TRACK_SCREEN_HPP +#define HEADER_EDIT_TRACK_SCREEN_HPP + +#include "guiengine/screen.hpp" + + +namespace GUIEngine { class Widget; } + +namespace irr { namespace gui { class STKModifiedSpriteBank; } } + +class Track; + +/** + * \brief screen where the user can edit the details of a track inside a grand prix + * \ingroup states_screens + */ +class EditTrackScreen : + public GUIEngine::Screen, + public GUIEngine::ScreenSingleton +{ + friend class GUIEngine::ScreenSingleton; + + static const char* ALL_TRACKS_GROUP_ID; + + EditTrackScreen(); + + void loadTrackList(); + void selectTrack(const std::string& id); + + std::string m_track_group; + + Track* m_track; + unsigned int m_laps; + bool m_reverse; + bool m_result; + +public: + + ~EditTrackScreen(); + + void setSelection(Track* track, unsigned int laps, bool reverse); + Track* getTrack() const; + unsigned int getLaps() const; + bool getReverse() const; + bool getResult() const; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void beforeAddingWidget() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void loadedFromFile() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, + const int playerID) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void init() OVERRIDE; +}; + +#endif diff --git a/src/states_screens/grand_prix_editor_screen.cpp b/src/states_screens/grand_prix_editor_screen.cpp new file mode 100644 index 000000000..238c5e94b --- /dev/null +++ b/src/states_screens/grand_prix_editor_screen.cpp @@ -0,0 +1,286 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2014 Marc Coll +// +// This program 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 3 +// of the License, or (at your option) any later version. +// +// This program 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 this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "states_screens/grand_prix_editor_screen.hpp" + +#include "graphics/irr_driver.hpp" +#include "guiengine/widget.hpp" +#include "guiengine/widgets/label_widget.hpp" +#include "guiengine/widgets/dynamic_ribbon_widget.hpp" +#include "guiengine/widgets/icon_button_widget.hpp" +#include "io/file_manager.hpp" +#include "race/grand_prix_data.hpp" +#include "race/grand_prix_manager.hpp" +#include "states_screens/state_manager.hpp" +#include "states_screens/edit_gp_screen.hpp" +#include "states_screens/dialogs/enter_gp_name_dialog.hpp" +#include "states_screens/dialogs/gp_info_dialog.hpp" +#include "states_screens/dialogs/track_info_dialog.hpp" +#include "tracks/track.hpp" +#include "tracks/track_manager.hpp" +#include "utils/translation.hpp" + + +using namespace GUIEngine; +using namespace irr::core; + +DEFINE_SCREEN_SINGLETON( GrandPrixEditorScreen ); + +// ----------------------------------------------------------------------------- +GrandPrixEditorScreen::GrandPrixEditorScreen() + : Screen("gpeditor.stkgui"), m_selection(NULL) +{ +} + +// ----------------------------------------------------------------------------- +void GrandPrixEditorScreen::loadedFromFile() +{ + +} + +// ----------------------------------------------------------------------------- +void GrandPrixEditorScreen::eventCallback(Widget* widget, const std::string& name, const int playerID) +{ + DynamicRibbonWidget* gplist_widget = getWidget("gplist"); + assert (gplist_widget != NULL); + std::string selected = gplist_widget->getSelectionIDString(PLAYER_ID_GAME_MASTER); + if (!selected.empty()) + setSelection (grand_prix_manager->getGrandPrix(selected)); + + if (name == "menu") + { + RibbonWidget* menu = getWidget("menu"); + assert(menu != NULL); + m_action = menu->getSelectionIDString(PLAYER_ID_GAME_MASTER); + + if (m_action == "new" || m_action == "copy") + { + new EnterGPNameDialog(this, 0.5f, 0.4f); + } + else if (m_action == "edit") + { + if (m_selection->isEditable()) + { + showEditScreen(m_selection); + } + else + { + new MessageDialog( + _("You can't edit the '%s' grand prix.\nYou might want to copy it first", + m_selection->getName().c_str()), + MessageDialog::MESSAGE_DIALOG_OK, NULL, false); + } + } + else if (m_action == "remove") + { + if (m_selection->isEditable()) + { + new MessageDialog( + _("Are you sure you want to remove '%s'?", m_selection->getName().c_str()), + MessageDialog::MESSAGE_DIALOG_CONFIRM, + this, false); + } + else + { + new MessageDialog( + _("You can't remove '%s'.", m_selection->getName().c_str()), + MessageDialog::MESSAGE_DIALOG_OK, NULL, false); + } + } + else if (m_action == "rename") + { + if (m_selection->isEditable()) + { + new EnterGPNameDialog(this, 0.5f, 0.4f); + } + else + { + new MessageDialog( + _("You can't rename '%s'.", m_selection->getName().c_str()), + MessageDialog::MESSAGE_DIALOG_OK, NULL, false); + } + } + } + else if (name == "back") + { + StateManager::get()->escapePressed(); + } +} + +// ----------------------------------------------------------------------------- +void GrandPrixEditorScreen::init() +{ + if (grand_prix_manager->getNumberOfGrandPrix() > 0) + { + if (m_selection == NULL) + { + loadGPList(); + setSelection (grand_prix_manager->getGrandPrix(0)); + } + else + { + std::string id = m_selection->getId(); + grand_prix_manager->reload(); + loadGPList(); + m_selection = grand_prix_manager->editGrandPrix(id); + m_selection->reload(); + setSelection (m_selection); + } + } + else + { + loadGPList(); + } +} + +// ----------------------------------------------------------------------------- +void GrandPrixEditorScreen::setSelection (const GrandPrixData* gpdata) +{ + LabelWidget* gpname_widget = getWidget("gpname"); + assert(gpname_widget != NULL); + DynamicRibbonWidget* gplist_widget = getWidget("gplist"); + assert (gplist_widget != NULL); + + m_selection = grand_prix_manager->editGrandPrix(gpdata->getId()); + gpname_widget->setText (gpdata->getName(), true); + gplist_widget->setSelection(m_selection->getId(), PLAYER_ID_GAME_MASTER, true); + loadTrackList (gpdata->getId()); +} + +// ----------------------------------------------------------------------------- +void GrandPrixEditorScreen::loadTrackList (const std::string& gpname) +{ + if (gpname.empty()) + return; + + DynamicRibbonWidget* tracks_widget = getWidget("tracks"); + assert(tracks_widget != NULL); + + const GrandPrixData* gp = grand_prix_manager->getGrandPrix(gpname); + const std::vector& tracks = gp->getTrackNames(); + + tracks_widget->clearItems(); + tracks_widget->setItemCountHint(tracks.size()); + for (unsigned int t = 0; t < tracks.size(); t++) + { + Track* curr = track_manager->getTrack(tracks[t]); + if (curr == NULL) + { + Log::warn("GrandPrixEditor", + "Grand Prix '%s' refers to track '%s', which does not exist\n", + gp->getId().c_str(), tracks[t].c_str()); + } + else + { + tracks_widget->addItem( + StringUtils::toWString(t + 1) + ". " + translations->fribidize(curr->getName()), + curr->getIdent(), curr->getScreenshotFile(), 0, + IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE ); + } + } + + tracks_widget->updateItemDisplay(); +} + +// ----------------------------------------------------------------------------- +void GrandPrixEditorScreen::loadGPList() +{ + DynamicRibbonWidget* gplist_widget = getWidget("gplist"); + assert(gplist_widget != NULL); + + // Reset GP list everytime (accounts for locking changes, etc.) + gplist_widget->clearItems(); + + // Build GP list + for (unsigned int i = 0; i < grand_prix_manager->getNumberOfGrandPrix(); i++) + { + const GrandPrixData* gp = grand_prix_manager->getGrandPrix(i); + const std::vector& tracks = gp->getTrackNames(); + + std::vector sshot_files; + for (unsigned int t=0; tgetTrack(tracks[t]); + if (track == NULL) + { + Log::warn("GrandPrixEditor", + "Grand Prix '%s' refers to track '%s', which does not exist\n", + gp->getId().c_str(), tracks[t].c_str()); + } + else + { + sshot_files.push_back(track->getScreenshotFile()); + } + } + if (sshot_files.size() == 0) + { + Log::warn("GrandPrixEditor", + "Grand Prix '%s' does not contain any valid track\n", + gp->getId().c_str()); + sshot_files.push_back("gui/main_help.png"); + } + + gplist_widget->addAnimatedItem(translations->fribidize(gp->getName()), gp->getId(), + sshot_files, 2.0f, 0, IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE ); + } + + gplist_widget->updateItemDisplay(); +} + +// ----------------------------------------------------------------------------- +void GrandPrixEditorScreen::showEditScreen(GrandPrixData* gp) +{ + assert(gp != NULL); + EditGPScreen* edit = EditGPScreen::getInstance(); + edit->setSelectedGP(gp); + StateManager::get()->pushScreen(edit); +} + +// ----------------------------------------------------------------------------- +void GrandPrixEditorScreen::onNewGPWithName(const stringw& newName) +{ + if (m_action == "copy") + { + setSelection(grand_prix_manager->copy(m_selection->getId(), newName)); + } + else if (m_action == "rename") + { + m_selection->setName(newName); + m_selection->writeToFile(); + } + else if (m_action == "new") + { + setSelection(grand_prix_manager->createNew(newName)); + } + + loadGPList(); + if (m_action != "rename") + showEditScreen(m_selection); +} + +// ----------------------------------------------------------------------------- +void GrandPrixEditorScreen::onConfirm() +{ + if (m_action == "remove") + { + grand_prix_manager->remove(m_selection->getId()); + loadGPList(); + if (grand_prix_manager->getNumberOfGrandPrix() > 0) + setSelection (grand_prix_manager->getGrandPrix(0)); + } + ModalDialog::dismiss(); +} diff --git a/src/states_screens/grand_prix_editor_screen.hpp b/src/states_screens/grand_prix_editor_screen.hpp new file mode 100644 index 000000000..4d763846b --- /dev/null +++ b/src/states_screens/grand_prix_editor_screen.hpp @@ -0,0 +1,68 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2014 Marc Coll +// +// This program 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 3 +// of the License, or (at your option) any later version. +// +// This program 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 this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_GRAND_PRIX_EDITOR_SCREEN_HPP +#define HEADER_GRAND_PRIX_EDITOR_SCREEN_HPP + +#include "dialogs/enter_gp_name_dialog.hpp" +#include "guiengine/screen.hpp" +#include "states_screens/dialogs/message_dialog.hpp" + + +namespace GUIEngine { class Widget; } + +class GrandPrixData; + +/** + * \brief screen where the user can edit his own grand prix + * \ingroup states_screens + */ +class GrandPrixEditorScreen : + public GUIEngine::Screen, + public GUIEngine::ScreenSingleton, + public EnterGPNameDialog::INewGPListener, + public MessageDialog::IConfirmDialogListener +{ + friend class GUIEngine::ScreenSingleton; + + GrandPrixEditorScreen(); + + void setSelection(const GrandPrixData* gpdata); + void loadGPList(); + void loadTrackList(const std::string& gpname); + void showEditScreen(GrandPrixData* gp); + + void onNewGPWithName(const irr::core::stringw& newName); + void onConfirm(); + + GrandPrixData* m_selection; + std::string m_action; + +public: + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void loadedFromFile() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, + const int playerID) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void init() OVERRIDE; +}; + +#endif diff --git a/src/states_screens/main_menu_screen.cpp b/src/states_screens/main_menu_screen.cpp index bbe0a215c..0bfaeab7b 100644 --- a/src/states_screens/main_menu_screen.cpp +++ b/src/states_screens/main_menu_screen.cpp @@ -39,6 +39,7 @@ #include "online/request_manager.hpp" #include "states_screens/addons_screen.hpp" #include "states_screens/credits.hpp" +#include "states_screens/grand_prix_editor_screen.hpp" #include "states_screens/help_screen_1.hpp" #include "states_screens/login_screen.hpp" #include "states_screens/offline_kart_selection.hpp" @@ -158,7 +159,7 @@ void MainMenuScreen::onUpdate(float delta) } else // now must be either logging in or logging out m_online->setDeactivated(); - + m_online->setLabel(CurrentUser::get()->getID() ? _("Online") : _("Login" ) ); IconButtonWidget* addons_icon = getWidget("addons"); @@ -413,7 +414,7 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, { // Don't go to addons if there is no internet, unless some addons are // already installed (so that you can delete addons without being online). - if(UserConfigParams::m_internet_status!=RequestManager::IPERM_ALLOWED && + if(UserConfigParams::m_internet_status!=RequestManager::IPERM_ALLOWED && !addons_manager->anyAddonsInstalled()) { new MessageDialog(_("You can not download addons without internet access. " @@ -424,6 +425,10 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, } StateManager::get()->pushScreen(AddonsScreen::getInstance()); } + else if (selection == "gpEditor") + { + StateManager::get()->pushScreen(GrandPrixEditorScreen::getInstance()); + } } // eventCallback // ---------------------------------------------------------------------------- diff --git a/src/tracks/track_object_presentation.cpp b/src/tracks/track_object_presentation.cpp index 38e4eac9e..5c0aabf73 100644 --- a/src/tracks/track_object_presentation.cpp +++ b/src/tracks/track_object_presentation.cpp @@ -747,6 +747,16 @@ void TrackObjectPresentationActionTrigger::onTriggerItemApproached(Item* who) new TutorialMessageDialog(_("Collect gift boxes, and fire the weapon with <%s> to blow away these boxes!", fire), true); } + else if (m_action == "tutorial_backgiftboxes") + { + m_action_active = false; + InputDevice* device = input_manager->getDeviceList()->getLatestUsedDevice(); + DeviceConfig* config = device->getConfiguration(); + irr::core::stringw fire = config->getBindingAsString(PA_FIRE); + + new TutorialMessageDialog(_("Press to look behind, to fire the weapon with <%s> while pressing to to fire behind!", fire), + true); + } else if (m_action == "tutorial_nitro_collect") { m_action_active = false;